package adapters import ( "context" "errors" "testing" oppintel "github.com/missionworkforce/missionmatch/internal/modules/opportunity_intelligence" "github.com/missionworkforce/missionmatch/internal/platform/marketintel" ) // fakeProvider serves fixed pages and can simulate a throttle on a given page, so the adapter paging // and graceful stop are tested without any network. type fakeProvider struct { pages [][]marketintel.Award throttleAtPage int // 1 based; 0 means never calls int } func (f *fakeProvider) Name() string { return "USASpending" } func (f *fakeProvider) FetchAwards(_ context.Context, q marketintel.AwardQuery) ([]marketintel.Award, bool, error) { f.calls++ if f.throttleAtPage != 0 && q.Page == f.throttleAtPage { return nil, false, marketintel.ErrThrottled } idx := q.Page - 1 if idx < 0 || idx >= len(f.pages) { return nil, false, nil } return f.pages[idx], idx < len(f.pages)-1, nil } func TestUSASpendingSourceFetchPaginates(t *testing.T) { fp := &fakeProvider{pages: [][]marketintel.Award{ {{GeneratedInternalID: "A1", AwardID: "P1"}, {GeneratedInternalID: "A2", AwardID: "P2"}}, {{GeneratedInternalID: "A3", AwardID: "P3"}}, }} src := NewUSASpendingSource(fp, marketintel.AwardQuery{}, 5) var got []oppintel.OpportunityRecord err := src.Fetch(context.Background(), func(r oppintel.OpportunityRecord) error { got = append(got, r) return nil }) if err != nil { t.Fatalf("Fetch: %v", err) } if len(got) != 3 { t.Fatalf("want 3 records across pages, got %d", len(got)) } for _, r := range got { if r.Kind != oppintel.KindContractAward || r.SourceName != "USASpending" { t.Fatalf("record wrong: %+v", r) } } } func TestUSASpendingSourceThrottleSoftStop(t *testing.T) { fp := &fakeProvider{ pages: [][]marketintel.Award{{{GeneratedInternalID: "A1", AwardID: "P1"}}, {{GeneratedInternalID: "A2"}}}, throttleAtPage: 2, } src := NewUSASpendingSource(fp, marketintel.AwardQuery{}, 5) var got int err := src.Fetch(context.Background(), func(oppintel.OpportunityRecord) error { got++ return nil }) if !errors.Is(err, oppintel.ErrThrottled) { t.Fatalf("want ErrThrottled, got %v", err) } if got != 1 { t.Fatalf("want 1 record before the throttle, got %d", got) } } func TestUSASpendingSourceYieldErrorStops(t *testing.T) { fp := &fakeProvider{pages: [][]marketintel.Award{{{GeneratedInternalID: "A1"}, {GeneratedInternalID: "A2"}}}} src := NewUSASpendingSource(fp, marketintel.AwardQuery{}, 5) boom := errors.New("boom") err := src.Fetch(context.Background(), func(oppintel.OpportunityRecord) error { return boom }) if !errors.Is(err, boom) { t.Fatalf("want the yield error, got %v", err) } }