test: cover Codex usage-limit dynamic fallback (#3088)

This commit is contained in:
Carlos Florêncio
2026-08-27 12:55:14 +00:00
committed by GitHub
parent 5c5b3c6d90
commit d851ff7ca1
3 changed files with 72 additions and 19 deletions
@@ -93,6 +93,42 @@ func TestEngineAppliesCandidateErrorActions(t *testing.T) {
}
}
// Contract coverage: Codex's complete usage-limit envelope must reach the
// existing hard-error policy without adding a provider branch to the engine.
func TestEngineRoutesCodexUsageLimitThroughHardPolicy(t *testing.T) {
failure := routingerr.Classify(routingerr.Input{
Phase: routingerr.PhasePromptSend,
ProviderID: "codex-acp",
Stderr: `{"code":-32603,"message":"Internal error","data":{"codexErrorInfo":"usageLimitExceeded","message":"You've hit your usage limit. Visit https://chatgpt.com/codex/settings/usage to purchase more credits or try again at Sep 1st, 2026 3:14 PM."}}`,
})
if failure.Code != routingerr.CodeQuotaLimited || failure.Class != routingerr.ClassHard {
t.Fatalf("classification = %+v, want hard quota_limited", failure)
}
profile := Profile{
ID: "dynamic-1", Version: 1,
Candidates: []Candidate{
{ID: "codex", Enabled: true, BindingKey: "codex", Policies: routingpolicy.DefaultDocument()},
{ID: "fallback", Enabled: true, BindingKey: "fallback", Policies: routingpolicy.DefaultDocument()},
},
}
engine := NewEngine()
initial, err := engine.Select("session-1", profile, 0, "")
if err != nil {
t.Fatalf("Select: %v", err)
}
decision, err := engine.ApplyFailure(
"session-1", profile, initial.Generation, initial.ExecutionProfileID, failure,
)
if err != nil {
t.Fatalf("ApplyFailure: %v", err)
}
if decision.ExecutionProfileID != "fallback" || decision.Reason != "policy_skip" {
t.Fatalf("decision = %#v, want fallback selected by hard policy", decision)
}
}
func TestEngineRetrySameKeepsTheFailedCandidate(t *testing.T) {
profile := Profile{
ID: "dynamic-1", Version: 1,
@@ -59,7 +59,12 @@ func TestClassify_ProviderRules(t *testing.T) {
{"claude auth", "claude-acp", "you are not authenticated", CodeAuthRequired},
{"claude model", "claude-acp", "model claude-foo not found here", CodeModelUnavailable},
{"codex quota", "codex-acp", "insufficient_quota for project", CodeQuotaLimited},
{"codex usage limit", "codex-acp", `{"codexErrorInfo":"usageLimitExceeded"}`, CodeQuotaLimited},
{
"codex usage limit",
"codex-acp",
`{"code":-32603,"message":"Internal error","data":{"codexErrorInfo":"usageLimitExceeded","message":"You've hit your usage limit. Visit https://chatgpt.com/codex/settings/usage to purchase more credits or try again at Sep 1st, 2026 3:14 PM."}}`,
CodeQuotaLimited,
},
{"codex rate", "codex-acp", "rate_limit_exceeded", CodeRateLimited},
{"codex apikey", "codex-acp", "invalid api key provided", CodeMissingCredentials},
{"opencode auth", "opencode-acp", "Unauthorized request", CodeAuthRequired},
@@ -6,29 +6,41 @@ import (
"github.com/kandev/kandev/internal/orchestrator/watcher"
)
// Contract coverage: a recognized provider failure still cannot override the
// dynamic route's pre-result and effect-safety gate.
func TestDynamicPreResultRequiresExplicitKnownEvidence(t *testing.T) {
if dynamicPreResultSafe(watcher.AgentEventData{DynamicRouteAttempt: true}) {
t.Fatal("unknown dynamic attempt was treated as pre-result safe")
}
if !dynamicPreResultSafe(watcher.AgentEventData{
usageLimitFailure := watcher.AgentEventData{
AgentID: "codex-acp",
ErrorMessage: `{"code":-32603,"message":"Internal error","data":{"codexErrorInfo":"usageLimitExceeded","message":"You've hit your usage limit. Visit https://chatgpt.com/codex/settings/usage to purchase more credits or try again at Sep 1st, 2026 3:14 PM."}}`,
DynamicRouteAttempt: true,
EvidenceKnown: true,
}) {
t.Fatal("known no-output/no-effect attempt was not pre-result safe")
}
if dynamicPreResultSafe(watcher.AgentEventData{
DynamicRouteAttempt: true,
EvidenceKnown: true,
OutputObserved: true,
}) {
t.Fatal("output-producing attempt was treated as pre-result safe")
if !dynamicPreResultSafe(usageLimitFailure) {
t.Fatal("pre-result usage-limit failure was not safe to route")
}
if dynamicPreResultSafe(watcher.AgentEventData{
DynamicRouteAttempt: true,
EvidenceKnown: true,
EffectObserved: true,
}) {
t.Fatal("effect-producing attempt was treated as pre-result safe")
unsafeCases := []struct {
name string
data watcher.AgentEventData
}{
{name: "unknown evidence", data: watcher.AgentEventData{DynamicRouteAttempt: true}},
{name: "assistant output", data: func() watcher.AgentEventData {
data := usageLimitFailure
data.OutputObserved = true
return data
}()},
{name: "tool effect", data: func() watcher.AgentEventData {
data := usageLimitFailure
data.EffectObserved = true
return data
}()},
}
for _, test := range unsafeCases {
t.Run(test.name, func(t *testing.T) {
if dynamicPreResultSafe(test.data) {
t.Fatalf("case %q was incorrectly treated as pre-result safe", test.name)
}
})
}
}