package aiaudit import ( "testing" ) // gpt_response nests the usage block differently across pipeline versions, so the // walker looks for any object carrying "usage" rather than one fixed path. A rigid // path would silently recover nothing from older rows. func TestExtractUsageCalls_findsNestedUsage(t *testing.T) { t.Parallel() raw := []byte(`{ "steps": [ {"step": "normalize", "raw": {"keys": 21}}, {"step": "categorize", "raw": {"model": "gpt-5.6-luna", "usage": {"prompt_tokens": 700, "completion_tokens": 5, "total_tokens": 705}}}, {"step": "ai_enhance", "raw": {"languages": [ {"language": "sl", "raw": {"raw": {"model": "gpt-5.6-luna", "status": 200, "usage": {"prompt_tokens": 4000, "completion_tokens": 800, "total_tokens": 4800}}}} ]}} ] }`) calls := extractUsageCalls(raw) if len(calls) != 2 { t.Fatalf("expected 2 recovered calls, got %d: %+v", len(calls), calls) } byRole := map[string]recoveredCall{} for _, c := range calls { byRole[c.Role] = c } cat, ok := byRole[RoleCategorize] if !ok { t.Fatalf("categorize call not recovered: %+v", calls) } if cat.PromptTokens != 700 || cat.Model != "gpt-5.6-luna" { t.Fatalf("categorize call wrong: %+v", cat) } enh, ok := byRole[RoleProcessing] if !ok { t.Fatalf("enhance call not recovered: %+v", calls) } if enh.PromptTokens != 4000 || enh.CompletionTokens != 800 { t.Fatalf("enhance call wrong: %+v", enh) } } func TestExtractUsageCalls_ignoresRowsWithoutUsage(t *testing.T) { t.Parallel() for name, raw := range map[string]string{ "empty": `{}`, "steps only": `{"steps":[{"step":"normalize","raw":{"keys":3}}]}`, "zero tokens": `{"raw":{"usage":{"prompt_tokens":0,"completion_tokens":0}}}`, "not json": `not json at all`, "unchanged skip": `{"steps":[{"step":"ai_enhance","raw":{"languages":[{"raw":{"status":"unchanged"}}]}}]}`, } { t.Run(name, func(t *testing.T) { if got := extractUsageCalls([]byte(raw)); len(got) != 0 { t.Fatalf("expected no recovered calls, got %+v", got) } }) } } // Backfilled history has no cached-token detail, so input is priced as fully // fresh. That must over-estimate, never under-estimate. func TestBackfillPricing_treatsInputAsUncached(t *testing.T) { t.Parallel() luna, _ := DefaultModelPrice("gpt-5.6-luna") backfilled := CostMicros(luna, 10_000, 0, 1_000) ifHalfCached := CostMicros(luna, 10_000, 5_000, 1_000) if backfilled <= ifHalfCached { t.Fatalf("backfill cost %d should exceed the cached-aware cost %d", backfilled, ifHalfCached) } } func TestBackfillSourcesAreDistinct(t *testing.T) { t.Parallel() if SourceLive == SourceBackfill { t.Fatal("live and backfill rows must be distinguishable") } }