proxy-pool/internal/config/store_test.go
2026-07-30 14:15:48 +08:00

96 lines
2.8 KiB
Go

package config
import (
"errors"
"sync"
"testing"
"time"
)
func TestStorePublishesAndReturnsDetachedConfigurations(t *testing.T) {
t.Parallel()
initial := storeTestConfig("provider-a")
store, err := NewStore(initial)
if err != nil {
t.Fatalf("NewStore() error = %v", err)
}
initial.Upstreams["provider-a"] = Upstream{}
current := store.Current()
if current == nil || !current.Upstreams["provider-a"].Enabled {
t.Fatalf("Current() was changed through constructor input: %+v", current)
}
current.Routing[0].Upstreams[0] = "mutated"
if got := store.Current().Routing[0].Upstreams[0]; got != "provider-a" {
t.Fatalf("Current() shared mutable state: %q", got)
}
next := storeTestConfig("provider-b")
store.Publish(next)
next.Routing[0].Upstreams[0] = "mutated"
if got := store.Current().Routing[0].Upstreams[0]; got != "provider-b" {
t.Fatalf("Publish() retained caller state: %q", got)
}
}
func TestStoreRejectsInvalidInitialConfiguration(t *testing.T) {
t.Parallel()
for _, configuration := range []*Config{nil, {}} {
if _, err := NewStore(configuration); !errors.Is(err, ErrInvalidStore) {
t.Fatalf("NewStore(%v) error = %v, want %v", configuration, err, ErrInvalidStore)
}
}
}
func TestStoreSupportsConcurrentReadersAndPublishers(t *testing.T) {
store, err := NewStore(storeTestConfig("provider-a"))
if err != nil {
t.Fatalf("NewStore() error = %v", err)
}
var wait sync.WaitGroup
for index := 0; index < 100; index++ {
wait.Add(2)
go func(index int) {
defer wait.Done()
name := "provider-a"
if index%2 == 1 {
name = "provider-b"
}
store.Publish(storeTestConfig(name))
}(index)
go func() {
defer wait.Done()
current := store.Current()
if current == nil || len(current.Routing) != 1 || len(current.Routing[0].Upstreams) != 1 {
t.Errorf("Current() returned partial configuration: %+v", current)
}
}()
}
wait.Wait()
}
func storeTestConfig(upstreamName string) *Config {
return &Config{
Version: 1,
Upstreams: map[string]Upstream{
upstreamName: {
Enabled: true, Exposure: []string{"gateway"},
API: ProviderAPI{Auth: ProviderAuth{Type: "none"}},
ProxyAuth: ProxyAuth{Type: "response"},
Pool: Pool{MaxSize: 10}, Capacity: Capacity{MaxConcurrencyPerProxy: 1},
Refill: Refill{
ReconcileInterval: Duration(time.Second), MinimumAvailableSlots: 1, TargetAvailableSlots: 2,
},
Lifecycle: Lifecycle{TTL: Duration(60_000_000_000), AllocationSafetyMargin: Duration(10_000_000_000)},
Fetch: Fetch{
EstimatedIPsPerCall: 1, Timeout: Duration(time.Second), MaxAttempts: 1, MaxInFlight: 1,
},
},
},
Routing: []Routing{{
Name: "default", Enabled: true, Purpose: "gateway", Upstreams: []string{upstreamName},
Strategy: Strategy{Type: "random"}, OnUnavailable: OnUnavailable{Action: "reject"},
}},
}
}