Files
CosmicClash/server/allocator/budget_test.go
T
2026-09-01 18:25:25 +01:00

73 lines
1.7 KiB
Go

package allocator
import (
"errors"
"sync"
"testing"
"time"
)
func TestFixedWindowBudgetLimitsEachRegionAndResets(t *testing.T) {
now := time.Unix(1000, 0).UTC()
budget, err := NewFixedWindowBudget(2, time.Minute)
if err != nil {
t.Fatal(err)
}
for i := 0; i < 2; i++ {
if err := budget.Allow("EU", now); err != nil {
t.Fatalf("EU attempt %d: %v", i, err)
}
}
if err := budget.Allow("EU", now); !errors.Is(err, ErrAllocationBudgetExceeded) {
t.Fatalf("third EU attempt = %v, want budget error", err)
}
if err := budget.Allow("NA", now); err != nil {
t.Fatalf("NA should have an independent budget: %v", err)
}
if err := budget.Allow("EU", now.Add(time.Minute)); err != nil {
t.Fatalf("EU after window: %v", err)
}
}
func TestFixedWindowBudgetIsAtomicUnderConcurrentAttempts(t *testing.T) {
budget, err := NewFixedWindowBudget(7, time.Minute)
if err != nil {
t.Fatal(err)
}
now := time.Unix(1000, 0).UTC()
var wg sync.WaitGroup
var mu sync.Mutex
allowed := 0
for i := 0; i < 64; i++ {
wg.Add(1)
go func() {
defer wg.Done()
if budget.Allow("EU", now) == nil {
mu.Lock()
allowed++
mu.Unlock()
}
}()
}
wg.Wait()
if allowed != 7 {
t.Fatalf("allowed=%d, want exactly 7", allowed)
}
}
func TestFixedWindowBudgetRejectsInvalidConfigurationAndInput(t *testing.T) {
if _, err := NewFixedWindowBudget(0, time.Minute); err == nil {
t.Fatal("zero limit accepted")
}
if _, err := NewFixedWindowBudget(1, 0); err == nil {
t.Fatal("zero window accepted")
}
budget, _ := NewFixedWindowBudget(1, time.Minute)
if err := budget.Allow("APAC", time.Unix(1000, 0)); err == nil {
t.Fatal("unknown region accepted")
}
if err := budget.Allow("EU", time.Time{}); err == nil {
t.Fatal("zero time accepted")
}
}