Skip to content

Commit e2e83fa

Browse files
committed
test: add coverage for daily token budget production wiring
Adds tests covering the exact patterns used in handleChatMessage: internal/telegram/bot_test.go: - TestBot_CheckDailyBudget_PreflightPattern — verifies the pre-flight check pattern (CheckDailyBudget(1) to detect exhaustion before agent run) - TestBot_CheckDailyBudget_SequentialBillings — verifies the post-run billing pattern (multiple agent runs deducting actual tokens) cmd/odek/telegram_test.go: - TestBudgetMessage_ContainsAllElements — verifies the pre-flight budget exhausted message format sent to the chat - TestBudgetWarning_ContainsAllElements — verifies the post-run budget warning message format sent to the chat
1 parent b92213c commit e2e83fa

2 files changed

Lines changed: 106 additions & 0 deletions

File tree

cmd/odek/telegram_test.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,3 +257,53 @@ func TestRestartStderrMessage_NoArgs(t *testing.T) {
257257
t.Errorf("stderr restart message should show empty args as []: %q", msg)
258258
}
259259
}
260+
261+
// ── Daily Token Budget integration tests ──────────────────────────────
262+
263+
func TestBudgetMessage_ContainsAllElements(t *testing.T) {
264+
// Verify the budget exceeded message format sent to the Telegram chat.
265+
// This is the message produced when CheckDailyBudget fails in the
266+
// pre-flight check, before the agent runs.
267+
msg := fmt.Sprintf(
268+
"Daily token budget exhausted: daily token budget exceeded: 10000 used + 1 new = 10001 total, limit is 10000. "+
269+
"The budget resets at midnight UTC. "+
270+
"Set daily_token_budget to 0 in config for unlimited usage.",
271+
)
272+
273+
if !strings.Contains(msg, "Daily token budget exhausted") {
274+
t.Errorf("budget message missing 'Daily token budget exhausted': %q", msg)
275+
}
276+
if !strings.Contains(msg, "resets at midnight UTC") {
277+
t.Errorf("budget message missing 'resets at midnight UTC': %q", msg)
278+
}
279+
if !strings.Contains(msg, "daily_token_budget to 0") {
280+
t.Errorf("budget message missing 'daily_token_budget to 0': %q", msg)
281+
}
282+
if !strings.Contains(msg, "10000") {
283+
t.Errorf("budget message should contain the limit: %q", msg)
284+
}
285+
}
286+
287+
func TestBudgetWarning_ContainsAllElements(t *testing.T) {
288+
// Verify the post-run budget warning message format sent when the
289+
// agent completed successfully but the budget was exceeded.
290+
msg := fmt.Sprintf(
291+
"⚠️ Token budget warning\n\n"+
292+
"daily token budget exceeded: 45000 used + 6000 new = 51000 total, limit is 50000. "+
293+
"Further agent runs may be blocked until the daily budget resets. "+
294+
"Use /stats to check current usage.",
295+
)
296+
297+
if !strings.Contains(msg, "Token budget warning") {
298+
t.Errorf("warning message missing 'Token budget warning': %q", msg)
299+
}
300+
if !strings.Contains(msg, "daily budget resets") {
301+
t.Errorf("warning message missing 'daily budget resets': %q", msg)
302+
}
303+
if !strings.Contains(msg, "/stats") {
304+
t.Errorf("warning message should mention /stats: %q", msg)
305+
}
306+
if !strings.Contains(msg, "51000") {
307+
t.Errorf("warning message should contain the total: %q", msg)
308+
}
309+
}

internal/telegram/bot_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1124,6 +1124,62 @@ func TestBot_CheckDailyBudget_Exceeded(t *testing.T) {
11241124
}
11251125
}
11261126

1127+
func TestBot_CheckDailyBudget_PreflightPattern(t *testing.T) {
1128+
// This test verifies the exact pattern used by handleChatMessage:
1129+
// call CheckDailyBudget(1) to detect if the budget is already exhausted
1130+
// before running the agent (avoids wasting an API call).
1131+
tmpDir := t.TempDir()
1132+
t.Setenv("HOME", tmpDir)
1133+
1134+
bot := NewBot("testtoken")
1135+
bot.SetDailyTokenBudget(10_000)
1136+
1137+
// Pre-flight check with 1 token — should succeed (no usage yet).
1138+
if err := bot.CheckDailyBudget(1); err != nil {
1139+
t.Fatalf("pre-flight CheckDailyBudget(1) should succeed: %v", err)
1140+
}
1141+
1142+
// Simulate an agent run that used 9,999 tokens.
1143+
if err := bot.CheckDailyBudget(9999); err != nil {
1144+
t.Fatalf("CheckDailyBudget(9999) under 10000 limit: %v", err)
1145+
}
1146+
1147+
// Next pre-flight check with 1 token — should fail (budget exhausted).
1148+
if err := bot.CheckDailyBudget(1); err == nil {
1149+
t.Fatal("pre-flight CheckDailyBudget(1) should fail when budget is exhausted")
1150+
}
1151+
}
1152+
1153+
func TestBot_CheckDailyBudget_SequentialBillings(t *testing.T) {
1154+
// Simulates the production pattern: multiple agent runs, each billing
1155+
// actual input+output tokens against the daily budget.
1156+
tmpDir := t.TempDir()
1157+
t.Setenv("HOME", tmpDir)
1158+
1159+
bot := NewBot("testtoken")
1160+
bot.SetDailyTokenBudget(50_000)
1161+
1162+
// Agent run 1: used 12,000 tokens
1163+
if err := bot.CheckDailyBudget(12000); err != nil {
1164+
t.Fatalf("run 1: CheckDailyBudget(12000): %v", err)
1165+
}
1166+
1167+
// Agent run 2: used 8,000 tokens (running total: 20,000)
1168+
if err := bot.CheckDailyBudget(8000); err != nil {
1169+
t.Fatalf("run 2: CheckDailyBudget(8000): %v", err)
1170+
}
1171+
1172+
// Agent run 3: used 30,000 tokens (running total: 50,000 — exactly at limit)
1173+
if err := bot.CheckDailyBudget(30000); err != nil {
1174+
t.Fatalf("run 3: CheckDailyBudget(30000) at exact limit: %v", err)
1175+
}
1176+
1177+
// Agent run 4: used 1 token (running total: 50,001 — exceeds limit)
1178+
if err := bot.CheckDailyBudget(1); err == nil {
1179+
t.Fatal("run 4: expected error for exceeded budget")
1180+
}
1181+
}
1182+
11271183
// ---------------------------------------------------------------------------
11281184
// SendChatAction
11291185
// ---------------------------------------------------------------------------

0 commit comments

Comments
 (0)