130 lines
3.1 KiB
Go
130 lines
3.1 KiB
Go
package proxy
|
|
|
|
import (
|
|
"sync"
|
|
"sync/atomic"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestUniqueKeyIncludesCredentialVersionButNotPassword(t *testing.T) {
|
|
p := Proxy{
|
|
Scheme: SchemeHTTP,
|
|
Host: "EXAMPLE.COM",
|
|
Port: 8080,
|
|
Username: "alice",
|
|
CredentialVersion: "v2",
|
|
SecretRef: "secret-password",
|
|
}
|
|
|
|
got := p.UniqueKey()
|
|
want := "http|example.com|8080|alice|v2"
|
|
if got != want {
|
|
t.Fatalf("UniqueKey() = %q, want %q", got, want)
|
|
}
|
|
if contains(got, p.SecretRef) {
|
|
t.Fatal("unique key leaked the proxy password reference")
|
|
}
|
|
}
|
|
|
|
func TestEffectiveExpiryPrecedence(t *testing.T) {
|
|
now := time.Date(2026, 7, 28, 10, 0, 0, 0, time.UTC)
|
|
explicit := now.Add(90 * time.Second)
|
|
|
|
tests := []struct {
|
|
name string
|
|
expiresAt *time.Time
|
|
response time.Duration
|
|
configured time.Duration
|
|
want *time.Time
|
|
}{
|
|
{name: "explicit timestamp", expiresAt: &explicit, response: 2 * time.Minute, configured: 3 * time.Minute, want: &explicit},
|
|
{name: "response ttl", response: 2 * time.Minute, configured: 3 * time.Minute, want: timePtr(now.Add(2 * time.Minute))},
|
|
{name: "configured ttl", configured: 3 * time.Minute, want: timePtr(now.Add(3 * time.Minute))},
|
|
{name: "non expiring"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got := EffectiveExpiry(now, tt.expiresAt, tt.response, tt.configured)
|
|
if !equalTimePtr(got, tt.want) {
|
|
t.Fatalf("EffectiveExpiry() = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestStateMachineRejectsIllegalTransition(t *testing.T) {
|
|
p := Proxy{State: StateFetched}
|
|
if err := p.Transition(StateChecking); err != nil {
|
|
t.Fatalf("FETCHED -> CHECKING: %v", err)
|
|
}
|
|
if err := p.Transition(StateAvailable); err != nil {
|
|
t.Fatalf("CHECKING -> AVAILABLE: %v", err)
|
|
}
|
|
if err := p.Transition(StateFetched); err == nil {
|
|
t.Fatal("AVAILABLE -> FETCHED must be rejected")
|
|
}
|
|
}
|
|
|
|
func TestCapacityNeverOversubscribes(t *testing.T) {
|
|
capacity := NewCapacity(8)
|
|
var acquired atomic.Int64
|
|
var peak atomic.Int64
|
|
var wg sync.WaitGroup
|
|
|
|
for range 1000 {
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
reservation, ok := capacity.Reserve()
|
|
if !ok {
|
|
return
|
|
}
|
|
active := acquired.Add(1)
|
|
for {
|
|
old := peak.Load()
|
|
if active <= old || peak.CompareAndSwap(old, active) {
|
|
break
|
|
}
|
|
}
|
|
if err := reservation.Commit(); err != nil {
|
|
t.Errorf("Commit(): %v", err)
|
|
}
|
|
acquired.Add(-1)
|
|
if err := reservation.Release(); err != nil {
|
|
t.Errorf("Release(): %v", err)
|
|
}
|
|
}()
|
|
}
|
|
wg.Wait()
|
|
|
|
if peak.Load() > 8 {
|
|
t.Fatalf("peak reservations = %d, exceeds 8", peak.Load())
|
|
}
|
|
if got := capacity.Active(); got != 0 {
|
|
t.Fatalf("active = %d, want 0", got)
|
|
}
|
|
if got := capacity.Reserved(); got != 0 {
|
|
t.Fatalf("reserved = %d, want 0", got)
|
|
}
|
|
}
|
|
|
|
func contains(s, part string) bool {
|
|
for i := 0; i+len(part) <= len(s); i++ {
|
|
if s[i:i+len(part)] == part {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func timePtr(value time.Time) *time.Time { return &value }
|
|
|
|
func equalTimePtr(a, b *time.Time) bool {
|
|
if a == nil || b == nil {
|
|
return a == b
|
|
}
|
|
return a.Equal(*b)
|
|
}
|