proxy-pool/internal/controller/provider/fleet_test.go

102 lines
3.2 KiB
Go

package provider
import (
"context"
"errors"
"testing"
"time"
"proxy-pool/internal/controller/pool"
"proxy-pool/internal/domain/activitypool"
proxyDomain "proxy-pool/internal/domain/proxy"
)
func TestFleetRunsAllUpstreamsAndCancelsSiblingsOnFailure(t *testing.T) {
started := make(chan string, 2)
siblingCancelled := make(chan struct{}, 1)
first := newFleetRuntime(t, "provider-a", coordinatorFunc(func(
context.Context, string, CoordinationLimits, func(context.Context, LeaderSession) error,
) error {
started <- "provider-a"
return errors.New("provider-a stopped")
}))
second := newFleetRuntime(t, "provider-b", coordinatorFunc(func(
ctx context.Context, _ string, _ CoordinationLimits, _ func(context.Context, LeaderSession) error,
) error {
started <- "provider-b"
<-ctx.Done()
siblingCancelled <- struct{}{}
return ctx.Err()
}))
fleet, err := NewFleet(first, second)
if err != nil {
t.Fatalf("NewFleet(): %v", err)
}
err = fleet.Run(context.Background())
if err == nil || err.Error() != "provider-a stopped" {
t.Fatalf("Run() error = %v, want provider-a failure", err)
}
seen := map[string]bool{<-started: true, <-started: true}
if !seen["provider-a"] || !seen["provider-b"] {
t.Fatalf("started upstreams = %v", seen)
}
select {
case <-siblingCancelled:
case <-time.After(time.Second):
t.Fatal("sibling runtime was not cancelled")
}
}
func TestNewFleetRejectsEmptyNilAndDuplicateUpstreams(t *testing.T) {
valid := newFleetRuntime(t, "provider-a", coordinatorFunc(func(
ctx context.Context, _ string, _ CoordinationLimits, _ func(context.Context, LeaderSession) error,
) error {
return ctx.Err()
}))
tests := []struct {
name string
runtimes []*UpstreamRuntime
}{
{name: "empty"},
{name: "nil", runtimes: []*UpstreamRuntime{nil}},
{name: "duplicate", runtimes: []*UpstreamRuntime{valid, valid}},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
fleet, err := NewFleet(test.runtimes...)
if err == nil || fleet != nil {
t.Fatalf("NewFleet() = (%v, %v), want invalid fleet", fleet, err)
}
})
}
}
func newFleetRuntime(t *testing.T, upstreamID string, coordinator Coordinator) *UpstreamRuntime {
t.Helper()
runtime, err := NewUpstreamRuntime(UpstreamRuntimeConfig{
Provider: runtimeProviderConfig(upstreamID),
ReconcilePolicy: runtimeReconcilePolicy(),
ReconcileInterval: time.Second,
}, UpstreamRuntimeDependencies{
Coordinator: coordinator,
Inventory: inventoryReaderFunc(func(context.Context, string, time.Duration) (pool.InventorySnapshot, error) {
return pool.InventorySnapshot{}, nil
}),
Adapter: adapterFunc(func(context.Context) (FetchResponse, error) {
return FetchResponse{Body: []byte("fixture")}, nil
}),
Parser: parserFunc(func(context.Context, []byte) ([]proxyDomain.Proxy, error) {
return []proxyDomain.Proxy{{ID: "proxy-1"}}, nil
}),
Activity: activitySinkFunc(func(context.Context, string, activitypool.FetchedBatch) (activitypool.UpsertResult, error) {
return activitypool.UpsertResult{Accepted: 1, Inserted: 1}, nil
}),
Results: resultRecorderFunc(func(Result) {}),
})
if err != nil {
t.Fatalf("NewUpstreamRuntime(%s): %v", upstreamID, err)
}
return runtime
}