package store_test import ( "context" "fmt" "slices" "testing" "time" "git.avsdev.uk/Go/job-search/internal/search" "git.avsdev.uk/Go/job-search/internal/store" "git.avsdev.uk/Go/job-search/internal/types" ) // searchStore returns a store holding five opportunities, s1 to s5, released // on days 1 to 5, for the search tests. func searchStore(t *testing.T) *store.Store { t.Helper() s := openStore(t) const software = "Programming services of packaged software products" cpv := func(code, description string) types.ClassificationCode { return types.ClassificationCode{ IsCPV: true, Code: code, Classification: description, } } rows := []struct { title, description, buyer, buyerID string classifications []types.ClassificationCode }{ { title: "Cloud migration services", description: "Moving servers to the cloud", buyer: "Leeds City Council", buyerID: "GB-LEEDS", classifications: []types.ClassificationCode{ cpv("72100000", "Hardware consultancy services"), cpv("72210000", software), }, }, { title: "Road repairs", description: "Pothole repairs in Bradford", buyer: "Bradford Council", buyerID: "GB-BRAD", classifications: []types.ClassificationCode{ cpv("45233141", "Road-maintenance works"), }, }, { title: "School meals", description: "Catering and cloud-based ordering", buyer: "Leeds Schools Trust", buyerID: "GB-LST", classifications: []types.ClassificationCode{ cpv("55524000", "School catering services"), cpv("15800000", "Miscellaneous food products"), }, }, { title: "Software licences", description: "Licences", buyer: "York Council", buyerID: "GB-YORK", classifications: []types.ClassificationCode{ cpv("72210000", software), }, }, { title: "Office cleaning", description: "Cleaning", buyer: "Hull Council", buyerID: "GB-HULL", }, } for i, row := range rows { o := opportunity(fmt.Sprintf("s%d", i+1), row.title, i+1, "r1") o.Description = row.description o.BuyerName = row.buyer o.BuyerID = row.buyerID o.Classifications = row.classifications mustSave(t, s, o) } return s } func TestSearchOpportunities(t *testing.T) { s := searchStore(t) all := []string{"s1", "s2", "s3", "s4", "s5"} cases := []struct { query string want []string }{ {"", all}, {"title: !!! \"\"", all}, {"cloud", []string{"s1", "s3"}}, {"CLOUD", []string{"s1", "s3"}}, {"migr", []string{"s1"}}, {"migrating", []string{"s1"}}, {"cloud servers", []string{"s1"}}, {"leeds", []string{}}, {"bradford", []string{"s2"}}, {`"road repairs"`, []string{"s2"}}, {`"repairs road"`, []string{}}, {"title:cloud", []string{"s1"}}, {"title:pothole", []string{}}, {"title:cloud title:road", []string{"s1", "s2"}}, {"buyer:leeds", []string{"s1", "s3"}}, {"buyer:leeds buyer:bradford", []string{"s1", "s2", "s3"}}, {`buyer:"leeds city"`, []string{"s1"}}, {"cloud buyer:leeds", []string{"s1", "s3"}}, {"title:cloud buyer:leeds", []string{"s1"}}, {`cloud !buyer:"leeds city"`, []string{"s3"}}, {"!cloud", []string{"s2", "s4", "s5"}}, {"!cloud !road", []string{"s4", "s5"}}, {"buyer_id:gb-leeds", []string{"s1"}}, {"buyer_id:GB-LEEDS buyer_id:gb-york", []string{"s1", "s4"}}, {"buyer_id:GB", []string{}}, {"!buyer_id:GB-LEEDS", []string{"s2", "s3", "s4", "s5"}}, {"cpv:72", []string{"s1", "s4"}}, {"cpv:72210000-5", []string{"s1", "s4"}}, {"cpv:72 !cpv:7221", []string{"s1"}}, {"!cpv:7221", []string{"s2", "s3", "s5"}}, {"cpv:food", []string{"s3"}}, {"!cpv:food", []string{"s1", "s2", "s4", "s5"}}, {"cpv:catering cpv:road", []string{"s2", "s3"}}, {"cpv:software !cpv:hardware", []string{"s1", "s4"}}, {"cpv:72 buyer:york", []string{"s4"}}, {"foo:cloud", all}, {`cloud" OR title:road*`, []string{}}, } for _, tc := range cases { got, err := s.SearchOpportunities( context.Background(), search.Parse(tc.query), time.Time{}, ) if err != nil { t.Fatalf("%q: %v", tc.query, err) } slices.Sort(got) if !slices.Equal(got, tc.want) { t.Errorf("%q: got %v, want %v", tc.query, got, tc.want) } } } // released_from bounds the search as it bounds ListOpportunities, and // changing a searched field updates the index. func TestSearchOpportunitiesReleasedFromAndUpdates(t *testing.T) { ctx := context.Background() s := searchStore(t) got, err := s.SearchOpportunities(ctx, search.Parse("buyer:leeds"), day(3)) if err != nil { t.Fatal(err) } if !slices.Equal(got, []string{"s3"}) { t.Errorf("released from day 3: got %v, want [s3]", got) } o := opportunity("s5", "Office cleaning", 6, "r1", "r2") o.Description = "Window washing" o.BuyerName = "Wakefield Council" mustSave(t, s, o) for query, want := range map[string][]string{ "washing": {"s5"}, "buyer:wakefield": {"s5"}, "cleaning": {"s5"}, "buyer:hull": {}, "cpv:agricultural": {"s5"}, } { got, err := s.SearchOpportunities(ctx, search.Parse(query), time.Time{}) if err != nil { t.Fatal(err) } if !slices.Equal(got, want) { t.Errorf("%q after update: got %v, want %v", query, got, want) } } }