package snapshot import ( "errors" "fmt" "reflect" "testing" "time" proxyDomain "proxy-pool/internal/domain/proxy" ) func TestStoreAppliesCompleteSnapshotsInOrder(t *testing.T) { store := NewStore("cluster-a", "worker-a") first := Envelope{ ClusterID: "cluster-a", WorkerID: "worker-a", Epoch: 1, Version: 1, Full: true, Proxies: []proxyDomain.Proxy{{ ID: "p1", Scheme: proxyDomain.SchemeHTTP, Host: "127.0.0.1", Port: 18080, MaxConcurrency: 2, State: proxyDomain.StateAvailable, }}, } first.Checksum = Checksum(first.Proxies) if err := store.Apply(first); err != nil { t.Fatalf("Apply(first): %v", err) } view := store.Current() if view == nil || view.Version != 1 || len(view.Entries) != 1 { t.Fatalf("Current() = %+v", view) } if view.Entries[0].Runtime == nil { t.Fatal("snapshot entry has no local runtime capacity") } second := first second.Version = 2 second.Proxies = append([]proxyDomain.Proxy(nil), first.Proxies...) second.Proxies[0].Host = "localhost" second.Checksum = Checksum(second.Proxies) if err := store.Apply(second); err != nil { t.Fatalf("Apply(second): %v", err) } if got := store.Current().Entries[0].Proxy.Host; got != "localhost" { t.Fatalf("host = %q, want localhost", got) } } func TestStoreRejectsWrongWorkerVersionGapAndChecksum(t *testing.T) { store := NewStore("cluster-a", "worker-a") base := Envelope{ClusterID: "cluster-a", WorkerID: "worker-a", Epoch: 1, Version: 1, Full: true} base.Checksum = Checksum(base.Proxies) if err := store.Apply(base); err != nil { t.Fatalf("Apply(base): %v", err) } wrongWorker := base wrongWorker.Version = 2 wrongWorker.WorkerID = "worker-b" if err := store.Apply(wrongWorker); !errors.Is(err, ErrWrongTarget) { t.Fatalf("wrong worker error = %v, want ErrWrongTarget", err) } gap := base gap.Version = 3 if err := store.Apply(gap); !errors.Is(err, ErrResyncRequired) { t.Fatalf("version gap error = %v, want ErrResyncRequired", err) } badChecksum := base badChecksum.Version = 2 badChecksum.Checksum = "bad" if err := store.Apply(badChecksum); !errors.Is(err, ErrChecksumMismatch) { t.Fatalf("checksum error = %v, want ErrChecksumMismatch", err) } } func TestViewSelectFiltersBySchemeUpstreamTagAndExclude(t *testing.T) { now := time.Date(2026, 7, 28, 12, 0, 0, 0, time.UTC) expiresSoon := now.Add(5 * time.Second) expiresLater := now.Add(time.Minute) store := NewStore("cluster-a", "worker-a") proxies := []proxyDomain.Proxy{ {ID: "http-a-cn", Scheme: proxyDomain.SchemeHTTP, SourceUpstream: "a", State: proxyDomain.StateAvailable, MaxConcurrency: 1, ExpiresAt: &expiresLater, Tags: map[string]string{"region": "cn-east", "tier": "gold"}}, {ID: "http-b-cn", Scheme: proxyDomain.SchemeHTTP, SourceUpstream: "b", State: proxyDomain.StateAvailable, MaxConcurrency: 1, ExpiresAt: &expiresLater, Tags: map[string]string{"region": "cn-east"}}, {ID: "socks-a-cn", Scheme: proxyDomain.SchemeSOCKS5, SourceUpstream: "a", State: proxyDomain.StateAvailable, MaxConcurrency: 1, ExpiresAt: &expiresLater, Tags: map[string]string{"region": "cn-east"}}, {ID: "http-a-us", Scheme: proxyDomain.SchemeHTTP, SourceUpstream: "a", State: proxyDomain.StateAvailable, MaxConcurrency: 1, ExpiresAt: &expiresLater, Tags: map[string]string{"region": "us-west"}}, {ID: "expiring", Scheme: proxyDomain.SchemeHTTP, SourceUpstream: "a", State: proxyDomain.StateAvailable, MaxConcurrency: 1, ExpiresAt: &expiresSoon, Tags: map[string]string{"region": "cn-east"}}, } envelope := Envelope{ClusterID: "cluster-a", WorkerID: "worker-a", Epoch: 1, Version: 1, Full: true, Proxies: proxies} envelope.Checksum = Checksum(envelope.Proxies) if err := store.Apply(envelope); err != nil { t.Fatalf("Apply(): %v", err) } view := store.Current() if view == nil { t.Fatal("Current() returned nil view") } selection := view.Select(Query{ Now: now, Scheme: proxyDomain.SchemeHTTP, Upstreams: []string{"a", "c"}, RequiredTags: map[string]string{"region": "cn-east"}, Exclude: map[string]struct{}{"expiring": {}}, SafetyMargin: 10 * time.Second, }) if got, want := collectSelectionIDs(selection), []string{"http-a-cn"}; !reflect.DeepEqual(got, want) { t.Fatalf("selection ids = %v, want %v", got, want) } } func TestViewSelectRejectsProxyAfterProviderUsableDeadline(t *testing.T) { now := time.Date(2026, 7, 29, 12, 0, 0, 0, time.UTC) expiresAt := now.Add(30 * time.Second) usableUntil := now.Add(27 * time.Second) proxy := proxyDomain.Proxy{ ID: "short-lived", Scheme: proxyDomain.SchemeHTTP, State: proxyDomain.StateAvailable, MaxConcurrency: 1, ExpiresAt: &expiresAt, UsableUntil: &usableUntil, } store := NewStore("cluster-a", "worker-a") envelope := Envelope{ ClusterID: "cluster-a", WorkerID: "worker-a", Epoch: 1, Version: 1, Full: true, Proxies: []proxyDomain.Proxy{proxy}, } envelope.Checksum = Checksum(envelope.Proxies) if err := store.Apply(envelope); err != nil { t.Fatalf("Apply(): %v", err) } before := store.Current().Select(Query{Now: now.Add(26 * time.Second)}) if _, ok := before.EntryAt(0); !ok { t.Fatal("EntryAt(before usable deadline) = false, want true") } after := store.Current().Select(Query{Now: now.Add(27 * time.Second)}) if _, ok := after.EntryAt(0); ok { t.Fatal("EntryAt(at usable deadline) = true, want false") } } func TestStoreApplyReusesCapacityAcrossVersionsAndEpochs(t *testing.T) { store := NewStore("cluster-a", "worker-a") firstProxies := []proxyDomain.Proxy{{ ID: "stable", Scheme: proxyDomain.SchemeHTTP, Host: "127.0.0.1", Port: 18080, MaxConcurrency: 2, State: proxyDomain.StateAvailable, }} first := Envelope{ ClusterID: "cluster-a", WorkerID: "worker-a", Epoch: 1, Version: 1, Full: true, Proxies: firstProxies, } first.Checksum = Checksum(first.Proxies) if err := store.Apply(first); err != nil { t.Fatalf("Apply(first): %v", err) } initialView := store.Current() initialRuntime := initialView.Entries[0].Runtime reservation, ok := initialRuntime.Reserve() if !ok { t.Fatal("Reserve() = false, want true") } if err := reservation.Commit(); err != nil { t.Fatalf("Commit(): %v", err) } second := first second.Version = 2 second.Proxies = []proxyDomain.Proxy{{ ID: "stable", Scheme: proxyDomain.SchemeHTTP, Host: "localhost", Port: 18080, MaxConcurrency: 5, State: proxyDomain.StateAvailable, }} second.Checksum = Checksum(second.Proxies) if err := store.Apply(second); err != nil { t.Fatalf("Apply(second): %v", err) } third := second third.Epoch = 2 third.Version = 1 third.Checksum = Checksum(third.Proxies) if err := store.Apply(third); err != nil { t.Fatalf("Apply(third): %v", err) } current := store.Current() if got := current.Entries[0].Runtime; got != initialRuntime { t.Fatal("runtime pointer was replaced, want capacity reuse") } if got := current.Entries[0].Runtime.Max(); got != 5 { t.Fatalf("runtime max = %d, want 5", got) } if got := current.Entries[0].Runtime.Active(); got != 1 { t.Fatalf("runtime active = %d, want 1", got) } if err := reservation.Release(); err != nil { t.Fatalf("Release(): %v", err) } } func TestStoreReusesRuntimeWhenProxyDisappearsAndReappears(t *testing.T) { store := NewStore("cluster-a", "worker-a") proxy := proxyDomain.Proxy{ ID: "p1", Scheme: proxyDomain.SchemeHTTP, State: proxyDomain.StateAvailable, MaxConcurrency: 1, } first := Envelope{ ClusterID: "cluster-a", WorkerID: "worker-a", Epoch: 1, Version: 1, Full: true, Proxies: []proxyDomain.Proxy{proxy}, } first.Checksum = Checksum(first.Proxies) if err := store.Apply(first); err != nil { t.Fatalf("Apply(first): %v", err) } runtime := store.Current().Entries[0].Runtime reservation, ok := runtime.Reserve() if !ok { t.Fatal("Reserve() = false, want true") } if err := reservation.Commit(); err != nil { t.Fatalf("Commit(): %v", err) } removed := Envelope{ClusterID: "cluster-a", WorkerID: "worker-a", Epoch: 1, Version: 2, Full: true} removed.Checksum = Checksum(removed.Proxies) if err := store.Apply(removed); err != nil { t.Fatalf("Apply(removed): %v", err) } reappeared := Envelope{ ClusterID: "cluster-a", WorkerID: "worker-a", Epoch: 1, Version: 3, Full: true, Proxies: []proxyDomain.Proxy{proxy}, } reappeared.Checksum = Checksum(reappeared.Proxies) if err := store.Apply(reappeared); err != nil { t.Fatalf("Apply(reappeared): %v", err) } reused := store.Current().Entries[0].Runtime if reused != runtime || reused.Active() != 1 { t.Fatalf("reappeared runtime = %p active=%d, want %p active=1", reused, reused.Active(), runtime) } if _, ok := reused.Reserve(); ok { t.Fatal("Reserve() succeeded despite inherited active capacity") } if err := reservation.Release(); err != nil { t.Fatalf("Release(): %v", err) } } func collectSelectionIDs(selection Selection) []string { ids := make([]string, 0, selection.Len()) for index := 0; index < selection.Len(); index++ { entry, ok := selection.EntryAt(index) if !ok { continue } ids = append(ids, entry.Proxy.ID) } return ids } func BenchmarkStoreApply100k(b *testing.B) { store := NewStore("cluster-a", "worker-a") proxies := makeBenchmarkProxies(100_000) b.ReportAllocs() for index := 0; index < b.N; index++ { envelope := Envelope{ ClusterID: "cluster-a", WorkerID: "worker-a", Epoch: 1, Version: uint64(index + 1), Full: true, Proxies: proxies, } envelope.Checksum = Checksum(envelope.Proxies) if err := store.Apply(envelope); err != nil { b.Fatalf("Apply(): %v", err) } } } func makeBenchmarkProxies(count int) []proxyDomain.Proxy { now := time.Date(2026, 7, 28, 12, 0, 0, 0, time.UTC) expiresAt := now.Add(30 * time.Minute) proxies := make([]proxyDomain.Proxy, 0, count) for index := range count { proxies = append(proxies, proxyDomain.Proxy{ ID: fmt.Sprintf("proxy-%06d", index), Scheme: schemeForBenchmarkIndex(index), Host: "127.0.0.1", Port: uint16(20000 + index%1000), SourceUpstream: upstreamForBenchmarkIndex(index), State: proxyDomain.StateAvailable, MaxConcurrency: 8, ExpiresAt: &expiresAt, Tags: map[string]string{ "region": regionForBenchmarkIndex(index), "tier": tierForBenchmarkIndex(index), }, }) } return proxies } func schemeForBenchmarkIndex(index int) proxyDomain.Scheme { switch index % 3 { case 0: return proxyDomain.SchemeHTTP case 1: return proxyDomain.SchemeHTTPS default: return proxyDomain.SchemeSOCKS5 } } func upstreamForBenchmarkIndex(index int) string { if index%2 == 0 { return "upstream-a" } return "upstream-b" } func regionForBenchmarkIndex(index int) string { switch index % 4 { case 0: return "cn-east" case 1: return "us-west" case 2: return "eu-central" default: return "ap-south" } } func tierForBenchmarkIndex(index int) string { if index%5 == 0 { return "gold" } return "silver" }