package quota import ( "errors" "testing" "hunjing/shenshi/internal/store" ) func TestReserveRejectsWhenBudgetExhausted(t *testing.T) { st := store.NewMem() l := New(st, 1000) if _, err := l.Reserve("u1", 600); err != nil { t.Fatal(err) } if _, err := l.Reserve("u1", 600); err == nil { t.Fatal("累计超预算应拒绝(HJ-2004)") } else if !errors.Is(err, ErrInsufficient) { t.Fatalf("错误类型错误: %v", err) } } func TestSettleReleasesAndAllowsReuse(t *testing.T) { st := store.NewMem() l := New(st, 1000) r, _ := l.Reserve("u1", 600) if err := l.Settle("u1", r, 100); err != nil { t.Fatal(err) } // 已用 100,剩 900 可用 if _, err := l.Reserve("u1", 900); err != nil { t.Fatalf("结算释放差值后应可继续使用: %v", err) } } func TestRefundIdempotent(t *testing.T) { st := store.NewMem() l := New(st, 1000) r, _ := l.Reserve("u1", 500) _ = l.Refund("u1", r) _ = l.Refund("u1", r) // 幂等重放 budget, used, reserved, _ := l.Budget("u1") if reserved != 0 || used != 0 || budget != 1000 { t.Fatalf("重复退款后账目异常: budget=%d used=%d reserved=%d", budget, used, reserved) } } func TestClampCeiling(t *testing.T) { if got := clamp(1 << 30); got != 1<<20 { t.Fatalf("超大预扣应钳位到 1M,实际 %d", got) } if got := clamp(-5); got != 0 { t.Fatalf("负值应截断为 0,实际 %d", got) } } func TestZeroBudgetMeansUnlimited(t *testing.T) { st := store.NewMem() l := New(st, 0) for i := 0; i < 3; i++ { if _, err := l.Reserve("u1", 1<<20); err != nil { t.Fatalf("budget=0 不应裁决: %v", err) } } }