package budget import ( "context" "errors" "testing" "time" ) func TestDBBudgetRollingWindow(t *testing.T) { ctx := context.Background() mem := NewMemory() now := time.Now() clock := func() time.Time { return now } b := NewDBBudget(mem, func() float64 { return 100 }, nil, clock) // Under cap: allowed. if err := b.Check(ctx, "u"); err != nil { t.Fatalf("fresh caller should pass: %v", err) } b.Commit(ctx, "u", 60) if err := b.Check(ctx, "u"); err != nil { t.Fatalf("60/100 should pass: %v", err) } // Over cap: rejected. b.Commit(ctx, "u", 50) // 110 total if err := b.Check(ctx, "u"); !errors.Is(err, ErrBudgetExceeded) { t.Fatalf("110/100 should be ErrBudgetExceeded, got %v", err) } // Window rolls over after 7 days: allowed again. now = now.Add(8 * 24 * time.Hour) b.Commit(ctx, "u", 1) // triggers rollover inside Add if err := b.Check(ctx, "u"); err != nil { t.Fatalf("after window rollover should pass: %v", err) } } func TestNoOpBudgetAlwaysAllows(t *testing.T) { b := NewNoOpBudget() if err := b.Check(context.Background(), "anyone"); err != nil { t.Fatalf("NoOp must always allow: %v", err) } b.Commit(context.Background(), "anyone", 1e9) // no-op }