Skip to content

Commit 10306ca

Browse files
michaelmcneesclaude
andcommitted
test(budget): add fail-open error behavior and ResetDay validation tests
- Document intentional fail-open: DB errors in budget checks don't block AI steps - Test rolling mode rejects ResetDay outside 1-28 - Test calendar mode silently clamps invalid ResetDay to 1 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 096bdce commit 10306ca

2 files changed

Lines changed: 74 additions & 0 deletions

File tree

internal/budget/budget_test.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package budget_test
22

33
import (
44
"context"
5+
"fmt"
56
"testing"
67
"time"
78

@@ -105,6 +106,50 @@ func TestChecker_Check_AllPass(t *testing.T) {
105106
assert.False(t, result.Warning)
106107
}
107108

109+
func TestChecker_Check_FailOpenOnGetTotalUsageError(t *testing.T) {
110+
checker := &budget.Checker{
111+
GlobalMonthlyTokenLimit: 1000,
112+
ResetMode: "calendar",
113+
ResetDay: 1,
114+
GetTotalUsage: func(ctx context.Context, period time.Time) (int64, error) {
115+
return 0, fmt.Errorf("db connection lost")
116+
},
117+
}
118+
119+
result := checker.Check(context.Background(), budget.CheckInput{
120+
TeamID: "team-1",
121+
Provider: "openai",
122+
})
123+
124+
// Intentional fail-open: DB errors should not block AI steps.
125+
assert.False(t, result.Blocked)
126+
assert.False(t, result.Warning)
127+
}
128+
129+
func TestChecker_Check_FailOpenOnGetTeamBudgetError(t *testing.T) {
130+
checker := &budget.Checker{
131+
ResetMode: "calendar",
132+
ResetDay: 1,
133+
GetTotalUsage: func(ctx context.Context, period time.Time) (int64, error) {
134+
return 0, nil
135+
},
136+
GetProviderUsage: func(ctx context.Context, teamID, provider string, period time.Time) (int64, error) {
137+
return 0, nil
138+
},
139+
GetTeamBudget: func(ctx context.Context, teamID, provider string) (*budget.TeamBudget, error) {
140+
return nil, fmt.Errorf("db timeout")
141+
},
142+
}
143+
144+
result := checker.Check(context.Background(), budget.CheckInput{
145+
TeamID: "team-1",
146+
Provider: "openai",
147+
})
148+
149+
assert.False(t, result.Blocked)
150+
assert.False(t, result.Warning)
151+
}
152+
108153
func TestChecker_CheckExecutionBudget(t *testing.T) {
109154
result := budget.CheckExecutionBudget(50000, 50001)
110155
assert.True(t, result.Blocked)

internal/config/config_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,3 +206,32 @@ func TestLoad_BudgetDefaults(t *testing.T) {
206206
assert.Equal(t, int64(0), cfg.Engine.Budget.GlobalMonthlyTokenLimit)
207207
assert.Equal(t, int64(0), cfg.Engine.Budget.DefaultTeamMonthlyTokenLimit)
208208
}
209+
210+
func TestLoad_BudgetResetDay_RollingInvalid(t *testing.T) {
211+
// ResetDay 0 with rolling mode should error
212+
t.Setenv("MANTLE_ENGINE_BUDGET_RESET_MODE", "rolling")
213+
t.Setenv("MANTLE_ENGINE_BUDGET_RESET_DAY", "0")
214+
215+
cmd := newTestCommand()
216+
_, err := Load(cmd)
217+
assert.Error(t, err)
218+
assert.Contains(t, err.Error(), "reset_day must be between 1 and 28")
219+
220+
// ResetDay 29 with rolling mode should error
221+
t.Setenv("MANTLE_ENGINE_BUDGET_RESET_DAY", "29")
222+
cmd = newTestCommand()
223+
_, err = Load(cmd)
224+
assert.Error(t, err)
225+
assert.Contains(t, err.Error(), "reset_day must be between 1 and 28")
226+
}
227+
228+
func TestLoad_BudgetResetDay_CalendarClamps(t *testing.T) {
229+
// Invalid ResetDay with calendar mode should silently clamp to 1
230+
t.Setenv("MANTLE_ENGINE_BUDGET_RESET_MODE", "calendar")
231+
t.Setenv("MANTLE_ENGINE_BUDGET_RESET_DAY", "0")
232+
233+
cmd := newTestCommand()
234+
cfg, err := Load(cmd)
235+
require.NoError(t, err)
236+
assert.Equal(t, 1, cfg.Engine.Budget.ResetDay)
237+
}

0 commit comments

Comments
 (0)