test: add comprehensive test harness across all packages (156 tests)

Characterization tests capturing current behavior before refactoring.
Covers auth, config, logging, proxy, ratelimit, server, and telemetry
packages with race-safe concurrent access tests.
This commit is contained in:
Alexander
2026-04-15 10:40:43 +02:00
parent d3fbfe8b42
commit 9150f466e5
13 changed files with 4325 additions and 0 deletions
+318
View File
@@ -0,0 +1,318 @@
package auth
import (
"testing"
"time"
)
func TestNewPool(t *testing.T) {
creds := []*Credential{
{ID: "a", AccessToken: "tok-a"},
{ID: "b", AccessToken: "tok-b"},
}
p := NewPool(creds)
if p == nil {
t.Fatal("NewPool returned nil")
}
if len(p.creds) != 2 {
t.Errorf("pool has %d creds, want 2", len(p.creds))
}
if p.cursor != 0 {
t.Errorf("initial cursor = %d, want 0", p.cursor)
}
}
func TestPool_Pick_EmptyPool(t *testing.T) {
p := NewPool(nil)
_, err := p.Pick()
if err == nil {
t.Fatal("expected error from empty pool, got nil")
}
want := "no credentials available"
if err.Error() != want {
t.Errorf("error = %q, want %q", err.Error(), want)
}
}
func TestPool_Pick_SingleCredential(t *testing.T) {
cred := &Credential{ID: "only", AccessToken: "tok-only"}
p := NewPool([]*Credential{cred})
got, err := p.Pick()
if err != nil {
t.Fatalf("Pick() error = %v", err)
}
if got.ID != "only" {
t.Errorf("Pick() returned cred ID %q, want %q", got.ID, "only")
}
// Picking again should return the same credential
got2, err := p.Pick()
if err != nil {
t.Fatalf("second Pick() error = %v", err)
}
if got2.ID != "only" {
t.Errorf("second Pick() returned cred ID %q, want %q", got2.ID, "only")
}
}
func TestPool_Pick_RoundRobin(t *testing.T) {
creds := []*Credential{
{ID: "a"},
{ID: "b"},
{ID: "c"},
}
p := NewPool(creds)
// Should cycle through a, b, c, a, b, c
expected := []string{"a", "b", "c", "a", "b", "c"}
for i, want := range expected {
got, err := p.Pick()
if err != nil {
t.Fatalf("Pick() #%d error = %v", i, err)
}
if got.ID != want {
t.Errorf("Pick() #%d = %q, want %q", i, got.ID, want)
}
}
}
func TestPool_Pick_SkipsCooldown(t *testing.T) {
creds := []*Credential{
{ID: "a"},
{ID: "b", CooldownUntil: time.Now().Add(1 * time.Hour)},
{ID: "c"},
}
p := NewPool(creds)
// First pick: "a" (index 0, not on cooldown)
got, err := p.Pick()
if err != nil {
t.Fatalf("Pick() #1 error = %v", err)
}
if got.ID != "a" {
t.Errorf("Pick() #1 = %q, want %q", got.ID, "a")
}
// Second pick: cursor at 1, but "b" is on cooldown → skip to "c"
got, err = p.Pick()
if err != nil {
t.Fatalf("Pick() #2 error = %v", err)
}
if got.ID != "c" {
t.Errorf("Pick() #2 = %q, want %q", got.ID, "c")
}
// Third pick: cursor advanced past "c" to 0 → "a"
got, err = p.Pick()
if err != nil {
t.Fatalf("Pick() #3 error = %v", err)
}
if got.ID != "a" {
t.Errorf("Pick() #3 = %q, want %q", got.ID, "a")
}
}
func TestPool_Pick_AllOnCooldown(t *testing.T) {
future := time.Now().Add(1 * time.Hour)
creds := []*Credential{
{ID: "a", CooldownUntil: future},
{ID: "b", CooldownUntil: future},
}
p := NewPool(creds)
_, err := p.Pick()
if err == nil {
t.Fatal("expected error when all on cooldown, got nil")
}
want := "all 2 credentials are on cooldown"
if err.Error() != want {
t.Errorf("error = %q, want %q", err.Error(), want)
}
}
func TestPool_MarkFailure(t *testing.T) {
tests := []struct {
name string
statusCode int
expectCooldown bool
expectedDur time.Duration
}{
{
name: "429 sets 30s cooldown",
statusCode: 429,
expectCooldown: true,
expectedDur: 30 * time.Second,
},
{
name: "500 sets 5s cooldown",
statusCode: 500,
expectCooldown: true,
expectedDur: 5 * time.Second,
},
{
name: "502 sets 5s cooldown",
statusCode: 502,
expectCooldown: true,
expectedDur: 5 * time.Second,
},
{
name: "503 sets 5s cooldown",
statusCode: 503,
expectCooldown: true,
expectedDur: 5 * time.Second,
},
{
name: "400 does NOT set cooldown",
statusCode: 400,
expectCooldown: false,
},
{
name: "401 does NOT set cooldown",
statusCode: 401,
expectCooldown: false,
},
{
name: "403 does NOT set cooldown",
statusCode: 403,
expectCooldown: false,
},
{
name: "404 does NOT set cooldown",
statusCode: 404,
expectCooldown: false,
},
{
name: "422 does NOT set cooldown",
statusCode: 422,
expectCooldown: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cred := &Credential{ID: "test"}
p := NewPool([]*Credential{cred})
before := time.Now()
p.MarkFailure(cred, tt.statusCode)
if tt.expectCooldown {
if !cred.IsOnCooldown() {
t.Errorf("expected cooldown after status %d", tt.statusCode)
}
// Verify approximate duration
cred.mu.Lock()
cooldownEnd := cred.CooldownUntil
cred.mu.Unlock()
lower := before.Add(tt.expectedDur)
upper := time.Now().Add(tt.expectedDur)
if cooldownEnd.Before(lower) || cooldownEnd.After(upper) {
t.Errorf("CooldownUntil %v not in expected range [%v, %v]", cooldownEnd, lower, upper)
}
} else {
if cred.IsOnCooldown() {
t.Errorf("did not expect cooldown after status %d", tt.statusCode)
}
}
})
}
}
func TestPool_MarkSuccess(t *testing.T) {
cred := &Credential{
ID: "test",
CooldownUntil: time.Now().Add(1 * time.Hour),
}
p := NewPool([]*Credential{cred})
if !cred.IsOnCooldown() {
t.Fatal("precondition: expected credential to be on cooldown")
}
p.MarkSuccess(cred)
if cred.IsOnCooldown() {
t.Error("expected cooldown to be cleared after MarkSuccess")
}
}
func TestPool_RoundRobinCursorAdvancement(t *testing.T) {
creds := []*Credential{
{ID: "0"},
{ID: "1"},
{ID: "2"},
}
p := NewPool(creds)
// Verify cursor starts at 0
if p.cursor != 0 {
t.Fatalf("initial cursor = %d, want 0", p.cursor)
}
// Pick cred[0], cursor should advance to 1
got, _ := p.Pick()
if got.ID != "0" {
t.Errorf("first pick = %q, want %q", got.ID, "0")
}
if p.cursor != 1 {
t.Errorf("cursor after first pick = %d, want 1", p.cursor)
}
// Pick cred[1], cursor should advance to 2
got, _ = p.Pick()
if got.ID != "1" {
t.Errorf("second pick = %q, want %q", got.ID, "1")
}
if p.cursor != 2 {
t.Errorf("cursor after second pick = %d, want 2", p.cursor)
}
// Pick cred[2], cursor should wrap to 0
got, _ = p.Pick()
if got.ID != "2" {
t.Errorf("third pick = %q, want %q", got.ID, "2")
}
if p.cursor != 0 {
t.Errorf("cursor after third pick = %d, want 0 (wrap)", p.cursor)
}
}
func TestPool_RoundRobinWithCooldownSkip(t *testing.T) {
creds := []*Credential{
{ID: "0"},
{ID: "1", CooldownUntil: time.Now().Add(1 * time.Hour)},
{ID: "2"},
}
p := NewPool(creds)
// First pick: cred[0]
got, _ := p.Pick()
if got.ID != "0" {
t.Errorf("first pick = %q, want %q", got.ID, "0")
}
// Cursor should be at 1
if p.cursor != 1 {
t.Errorf("cursor after first pick = %d, want 1", p.cursor)
}
// Second pick: cursor at 1, but cred[1] on cooldown → skip to cred[2]
got, _ = p.Pick()
if got.ID != "2" {
t.Errorf("second pick = %q, want %q", got.ID, "2")
}
// Cursor should advance past cred[2] to 0
if p.cursor != 0 {
t.Errorf("cursor after second pick (skip) = %d, want 0", p.cursor)
}
// Third pick: cursor at 0, cred[0] available
got, _ = p.Pick()
if got.ID != "0" {
t.Errorf("third pick = %q, want %q", got.ID, "0")
}
if p.cursor != 1 {
t.Errorf("cursor after third pick = %d, want 1", p.cursor)
}
}
+167
View File
@@ -0,0 +1,167 @@
package auth
import (
"sync"
"testing"
"time"
)
func TestCredential_IsOnCooldown(t *testing.T) {
tests := []struct {
name string
cooldownUntil time.Time
want bool
}{
{
name: "zero time — not on cooldown",
cooldownUntil: time.Time{},
want: false,
},
{
name: "future time — on cooldown",
cooldownUntil: time.Now().Add(1 * time.Hour),
want: true,
},
{
name: "past time — expired cooldown",
cooldownUntil: time.Now().Add(-1 * time.Hour),
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &Credential{CooldownUntil: tt.cooldownUntil}
got := c.IsOnCooldown()
if got != tt.want {
t.Errorf("IsOnCooldown() = %v, want %v", got, tt.want)
}
})
}
}
func TestCredential_SetCooldown(t *testing.T) {
tests := []struct {
name string
duration time.Duration
}{
{name: "30 second cooldown", duration: 30 * time.Second},
{name: "5 second cooldown", duration: 5 * time.Second},
{name: "1 minute cooldown", duration: 1 * time.Minute},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &Credential{}
before := time.Now()
c.SetCooldown(tt.duration)
after := time.Now()
// CooldownUntil should be between before+duration and after+duration
if c.CooldownUntil.Before(before.Add(tt.duration)) {
t.Errorf("CooldownUntil %v is before expected lower bound %v", c.CooldownUntil, before.Add(tt.duration))
}
if c.CooldownUntil.After(after.Add(tt.duration)) {
t.Errorf("CooldownUntil %v is after expected upper bound %v", c.CooldownUntil, after.Add(tt.duration))
}
// Should now be on cooldown
if !c.IsOnCooldown() {
t.Error("expected credential to be on cooldown after SetCooldown")
}
})
}
}
func TestCredential_ClearCooldown(t *testing.T) {
t.Run("clears active cooldown", func(t *testing.T) {
c := &Credential{CooldownUntil: time.Now().Add(1 * time.Hour)}
if !c.IsOnCooldown() {
t.Fatal("precondition: expected credential to be on cooldown")
}
c.ClearCooldown()
if c.IsOnCooldown() {
t.Error("expected credential to not be on cooldown after ClearCooldown")
}
if !c.CooldownUntil.IsZero() {
t.Errorf("expected CooldownUntil to be zero time, got %v", c.CooldownUntil)
}
})
t.Run("clearing when not on cooldown is no-op", func(t *testing.T) {
c := &Credential{}
c.ClearCooldown()
if c.IsOnCooldown() {
t.Error("expected credential to not be on cooldown")
}
if !c.CooldownUntil.IsZero() {
t.Errorf("expected CooldownUntil to be zero time, got %v", c.CooldownUntil)
}
})
}
func TestCredential_Token(t *testing.T) {
tests := []struct {
name string
token string
}{
{name: "returns access token", token: "sk-ant-abc123"},
{name: "empty token", token: ""},
{name: "long token", token: "sk-ant-" + string(make([]byte, 200))},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &Credential{AccessToken: tt.token}
got := c.Token()
if got != tt.token {
t.Errorf("Token() = %q, want %q", got, tt.token)
}
})
}
}
func TestCredential_ConcurrentAccess(t *testing.T) {
c := &Credential{
AccessToken: "initial-token",
}
var wg sync.WaitGroup
const goroutines = 50
// Spawn goroutines that concurrently read and write
for i := 0; i < goroutines; i++ {
wg.Add(3)
go func() {
defer wg.Done()
_ = c.Token()
}()
go func() {
defer wg.Done()
c.SetCooldown(1 * time.Second)
}()
go func() {
defer wg.Done()
_ = c.IsOnCooldown()
}()
}
// Also mix in ClearCooldown calls
for i := 0; i < goroutines/2; i++ {
wg.Add(1)
go func() {
defer wg.Done()
c.ClearCooldown()
}()
}
wg.Wait()
// If we get here without -race detecting issues, mutex is working
}