151 lines
4.9 KiB
Go
151 lines
4.9 KiB
Go
package redisprovider
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/redis/go-redis/v9"
|
|
|
|
controllerProvider "proxy-pool/internal/controller/provider"
|
|
)
|
|
|
|
func TestNewRejectsInvalidDependenciesAndOptions(t *testing.T) {
|
|
t.Parallel()
|
|
client := redis.NewClient(&redis.Options{Addr: "127.0.0.1:0"})
|
|
t.Cleanup(func() { _ = client.Close() })
|
|
valid := Options{
|
|
Namespace: "controller", HolderID: "controller-a", LeaseTTL: 3 * time.Second,
|
|
RenewEvery: time.Second, RetryInterval: 50 * time.Millisecond, PermitGrace: time.Second,
|
|
}
|
|
var typedNil *redis.Client
|
|
tests := []struct {
|
|
name string
|
|
client redis.Scripter
|
|
options Options
|
|
}{
|
|
{name: "nil client", options: valid},
|
|
{name: "typed nil client", client: typedNil, options: valid},
|
|
{name: "empty namespace", client: client, options: withNamespace(valid, "")},
|
|
{name: "unsafe namespace", client: client, options: withNamespace(valid, "bad:value")},
|
|
{name: "empty holder", client: client, options: withHolder(valid, "")},
|
|
{name: "zero lease", client: client, options: withLeaseTTL(valid, 0)},
|
|
{name: "renew exceeds third", client: client, options: withRenewEvery(valid, 2*time.Second)},
|
|
{name: "zero retry", client: client, options: withRetryInterval(valid, 0)},
|
|
{name: "negative grace", client: client, options: withPermitGrace(valid, -1)},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
adapter, err := New(test.client, test.options)
|
|
if err == nil || adapter != nil {
|
|
t.Fatalf("New() = (%v, %v), want nil adapter and error", adapter, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNewBuildsPerUpstreamClusterSafeKeys(t *testing.T) {
|
|
t.Parallel()
|
|
client := redis.NewClient(&redis.Options{Addr: "127.0.0.1:0"})
|
|
t.Cleanup(func() { _ = client.Close() })
|
|
adapter, err := New(client, Options{
|
|
Namespace: " controller ", HolderID: "controller-a", LeaseTTL: 3 * time.Second,
|
|
RenewEvery: time.Second, RetryInterval: 50 * time.Millisecond, PermitGrace: time.Second,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("New(): %v", err)
|
|
}
|
|
keys, err := adapter.keys.forUpstream("provider:{unsafe}")
|
|
if err != nil {
|
|
t.Fatalf("forUpstream(): %v", err)
|
|
}
|
|
all := keys.all()
|
|
for _, key := range all {
|
|
if strings.Contains(key, "provider:{unsafe}") || strings.Count(key, "{") != 1 ||
|
|
strings.Count(key, "}") != 1 || !strings.Contains(key, "{provider:") {
|
|
t.Fatalf("unsafe provider coordination key %q", key)
|
|
}
|
|
}
|
|
if strings.Split(all[0], "}")[0] != strings.Split(all[len(all)-1], "}")[0] {
|
|
t.Fatalf("keys do not share one upstream hash tag: %v", all)
|
|
}
|
|
if _, err := adapter.keys.forUpstream(""); !errors.Is(err, controllerProvider.ErrInvalidCoordination) {
|
|
t.Fatalf("forUpstream(empty) error = %v", err)
|
|
}
|
|
}
|
|
|
|
func TestRunLeaderRejectsInvalidCalls(t *testing.T) {
|
|
t.Parallel()
|
|
var adapter *Adapter
|
|
limits := controllerProvider.CoordinationLimits{
|
|
RequestInterval: time.Second, MaxInFlight: 1, MaxAttemptDuration: time.Second,
|
|
}
|
|
work := func(context.Context, controllerProvider.LeaderSession) error { return nil }
|
|
if err := adapter.RunLeader(context.Background(), "provider-a", limits, work); !errors.Is(err, controllerProvider.ErrInvalidCoordination) {
|
|
t.Fatalf("nil adapter error = %v", err)
|
|
}
|
|
}
|
|
|
|
func TestRunLeaderRejectsNegativeFetchQuota(t *testing.T) {
|
|
t.Parallel()
|
|
client := redis.NewClient(&redis.Options{Addr: "127.0.0.1:0"})
|
|
t.Cleanup(func() { _ = client.Close() })
|
|
adapter, err := New(client, Options{
|
|
Namespace: "controller", HolderID: "controller-a", LeaseTTL: 3 * time.Second,
|
|
RenewEvery: time.Second, RetryInterval: 50 * time.Millisecond,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("New(): %v", err)
|
|
}
|
|
limits := controllerProvider.CoordinationLimits{
|
|
MaxInFlight: 1, MaxAttemptDuration: time.Second, MaxTotal: -1,
|
|
}
|
|
err = adapter.RunLeader(context.Background(), "provider-a", limits,
|
|
func(context.Context, controllerProvider.LeaderSession) error { return nil })
|
|
if !errors.Is(err, controllerProvider.ErrInvalidCoordination) {
|
|
t.Fatalf("RunLeader() error = %v, want ErrInvalidCoordination", err)
|
|
}
|
|
}
|
|
|
|
func TestLeaderWorkResultPrefersParentCancellation(t *testing.T) {
|
|
t.Parallel()
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
cancel()
|
|
if err := leaderWorkResult(ctx, nil); !errors.Is(err, context.Canceled) {
|
|
t.Fatalf("leaderWorkResult() error = %v, want context cancellation", err)
|
|
}
|
|
}
|
|
|
|
func withNamespace(options Options, value string) Options {
|
|
options.Namespace = value
|
|
return options
|
|
}
|
|
|
|
func withHolder(options Options, value string) Options {
|
|
options.HolderID = value
|
|
return options
|
|
}
|
|
|
|
func withLeaseTTL(options Options, value time.Duration) Options {
|
|
options.LeaseTTL = value
|
|
return options
|
|
}
|
|
|
|
func withRenewEvery(options Options, value time.Duration) Options {
|
|
options.RenewEvery = value
|
|
return options
|
|
}
|
|
|
|
func withRetryInterval(options Options, value time.Duration) Options {
|
|
options.RetryInterval = value
|
|
return options
|
|
}
|
|
|
|
func withPermitGrace(options Options, value time.Duration) Options {
|
|
options.PermitGrace = value
|
|
return options
|
|
}
|