package provider import ( "sync" "testing" "proxy-pool/internal/domain/upstream" ) func TestStatsRecorderTracksEmptyResetAndErrors(t *testing.T) { recorder, err := NewStatsRecorder(2) if err != nil { t.Fatalf("NewStatsRecorder(): %v", err) } for _, class := range []upstream.FetchClass{ upstream.FetchEmpty, upstream.FetchEmpty, upstream.FetchError, } { recorder.Record(Result{UpstreamID: "provider-a", Class: class}) } stats := recorder.ReadProviderStats([]string{"provider-a"})[0] if stats.ConsecutiveEmptyFetch != 2 || stats.FetchErrorCount != 1 { t.Fatalf("stats = %+v, want empty=2 errors=1", stats) } recorder.Record(Result{UpstreamID: "provider-a", Class: upstream.FetchDuplicateOnly}) if got := recorder.ReadProviderStats([]string{"provider-a"})[0].ConsecutiveEmptyFetch; got != 0 { t.Fatalf("consecutive empty after duplicate = %d, want 0", got) } } func TestStatsRecorderIsBoundedAndConcurrent(t *testing.T) { recorder, err := NewStatsRecorder(1) if err != nil { t.Fatalf("NewStatsRecorder(): %v", err) } const workers = 100 var wait sync.WaitGroup for range workers { wait.Add(1) go func() { defer wait.Done() recorder.Record(Result{UpstreamID: "provider-a", Class: upstream.FetchError}) }() } wait.Wait() recorder.Record(Result{UpstreamID: "provider-b", Class: upstream.FetchError}) stats := recorder.ReadProviderStats([]string{"provider-a", "provider-b"}) if stats[0].FetchErrorCount != workers || stats[1].FetchErrorCount != 0 { t.Fatalf("stats = %+v, want bounded provider-a errors", stats) } } func TestStatsRecorderRetainsConfiguredProvidersAndReusesCapacity(t *testing.T) { recorder, err := NewStatsRecorder(2) if err != nil { t.Fatalf("NewStatsRecorder(): %v", err) } recorder.Record(Result{UpstreamID: "removed-a", Class: upstream.FetchError}) recorder.Record(Result{UpstreamID: "provider-b", Class: upstream.FetchError}) const workers = 100 var wait sync.WaitGroup for range workers { wait.Add(2) go func() { defer wait.Done() recorder.Record(Result{UpstreamID: "provider-b", Class: upstream.FetchError}) }() go func() { defer wait.Done() recorder.RetainProviderStats([]string{"provider-a", "provider-b"}) }() } wait.Wait() recorder.RetainProviderStats([]string{"provider-a", "provider-b"}) recorder.Record(Result{UpstreamID: "provider-a", Class: upstream.FetchError}) stats := recorder.ReadProviderStats([]string{"removed-a", "provider-a", "provider-b"}) if stats[0].FetchErrorCount != 0 || stats[1].FetchErrorCount != 1 || stats[2].FetchErrorCount == 0 { t.Fatalf("stats after retention = %+v", stats) } }