P4: budget battery — DBBudget (rolling 7-day) over run.Budget
Second Tier-2 battery, plugging into run.Ports.Budget: - budget.go: skillexec's BudgetTracker / NoOpBudget / DBBudget moved clean (stdlib only). Check/Commit match run.Budget exactly (compile-time proof in run.go: NoOpBudget and *DBBudget are run.Budget). - storage.go: the BudgetStorage seam + SkillBudget domain, split out of mort's GORM file (the GORM impl stays in mort). - memory.go: NewMemory() — zero-dependency in-process BudgetStorage with the 7-day rolling-window rollover in Add. Tests: per-user cap enforced, window rolls over after 7 days, NoOp always allows. CI invariant: core imports ZERO from the budget battery. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user