82 lines
2.7 KiB
Go
82 lines
2.7 KiB
Go
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")
|
|
}
|
|
}
|