84 lines
2.6 KiB
Go
84 lines
2.6 KiB
Go
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)
|
|
}
|
|
}
|