package mock import ( "math/rand" "os" "strings" "testing" ) // loadTestCatalog parses the small fixture catalog used by the tests. func loadTestCatalog(t *testing.T) *Catalog { t.Helper() b, err := os.ReadFile("testdata/catalog.json") if err != nil { t.Fatal(err) } cat, err := parseCatalog(b, "testdata/catalog.json") if err != nil { t.Fatal(err) } return cat } // The catalog compiled into the build must load: a bad data/catalog.json // fails here rather than when the mock starts. func TestEmbeddedCatalogIsValid(t *testing.T) { if len(embeddedCatalog) == 0 { t.Fatal("data/catalog.json was not embedded") } cat, err := LoadCatalog() if err != nil { t.Fatal(err) } if len(cat.Buyers) == 0 || len(cat.Suppliers) == 0 || len(cat.Subjects) == 0 || len(cat.Methods) == 0 { t.Fatalf( "embedded catalog has an empty pool: %d buyers, %d suppliers, %d subjects, %d methods", len(cat.Buyers), len(cat.Suppliers), len(cat.Subjects), len(cat.Methods), ) } } func TestParseCatalog(t *testing.T) { cat := loadTestCatalog(t) if len(cat.Buyers) != 2 || len(cat.Suppliers) != 1 || len(cat.Subjects) != 2 || len(cat.Methods) != 2 { t.Fatalf( "got %d buyers, %d suppliers, %d subjects, %d methods", len(cat.Buyers), len(cat.Suppliers), len(cat.Subjects), len(cat.Methods), ) } if d := cat.Buyers[0].Details; d == nil || len(d.Classifications) != 1 || d.Classifications[0].Scheme != "UK_CA_TYPE" { t.Fatalf("buyer details.classifications not read: %+v", d) } s := cat.Subjects[0] if s.Classification != "CPV-77314000" || len(s.Items) != 1 { t.Fatalf("subject classification key/items not read: %+v", s) } if ac := s.Items[0].AdditionalClassifications; len(ac) != 1 || ac[0] != "CPV-77310000" { t.Fatalf("items.additionalClassifications keys not read: %+v", ac) } if v := s.Lots[0].Value; v == nil || v.AmountGross == nil || *v.AmountGross != 120000 { t.Fatalf("lot value not read: %+v", v) } } // Picking a subject resolves its classification keys to full classifications. func TestSubjectResolvesClassifications(t *testing.T) { cat := loadTestCatalog(t) //nolint:gosec // G404: seeded math/rand keeps the test deterministic r := rand.New(rand.NewSource(1)) for i := 0; i < 20; i++ { tender := cat.subject(r) if tender.Title != "Grounds maintenance services" { continue } if cl := tender.Classification; cl == nil || cl.ID != "77314000" || cl.Description != "Grounds maintenance services" { t.Fatalf("main classification = %+v", cl) } ac := tender.Items[0].AdditionalClassifications if len(ac) != 1 || ac[0].Description != "Planting and maintenance services of green areas" { t.Fatalf("item classifications = %+v", ac) } // The catalog itself still holds keys, untouched by picking. if cat.Subjects[0].Classification != "CPV-77314000" { t.Fatal("picking modified the catalog") } return } t.Fatal("never picked the grounds maintenance subject") } func TestParseCatalogRejectsInvalidCatalogs(t *testing.T) { cases := map[string]struct { doc string want []string }{ "empty pools": { doc: `{"buyers": [], "subjects": [{"title": "T"}], "methods": [], "classifications": {}}`, want: []string{"no buyers", "no suppliers", "no methods"}, }, "unknown classification keys": { doc: `{"buyers": [{"name": "B"}], "suppliers": [{"name": "S"}], "methods": [{"procurementMethod": "open"}], "subjects": [{"title": "T", "classification": "CPV-1", "items": [{"id": "1", "additionalClassifications": ["CPV-2"]}]}], "classifications": {}}`, want: []string{"2 classification key(s) not found", "CPV-1", "CPV-2"}, }, "not JSON": {doc: `{`, want: []string{"decoding"}}, } for name, tc := range cases { t.Run(name, func(t *testing.T) { _, err := parseCatalog([]byte(tc.doc), "test catalog") if err == nil { t.Fatal("expected an error") } for _, w := range tc.want { if !strings.Contains(err.Error(), w) { t.Errorf("error %q does not mention %q", err, w) } } }) } } func TestResolveOneUnknownKey(t *testing.T) { cat := loadTestCatalog(t) if cl := cat.resolveOne("CPV-00000000"); cl != nil { t.Errorf("unknown key resolved to %+v", cl) } if cl := cat.resolveOne(""); cl != nil { t.Errorf("empty key resolved to %+v", cl) } if got := cat.resolve([]string{"CPV-00000000"}); got != nil { t.Errorf("resolve of unknown keys = %+v", got) } }