package aiaudit import "testing" // Luna list price, 2026-07-30: $0.20 / $0.02 cached / $1.20 per 1M tokens. func TestCostMicros_lunaListPrice(t *testing.T) { t.Parallel() luna, ok := DefaultModelPrice("gpt-5.6-luna") if !ok { t.Fatal("gpt-5.6-luna must have a shipped price") } if luna.InputPerMTok != 200_000 || luna.CachedInputPerMTok != 20_000 || luna.OutputPerMTok != 1_200_000 { t.Fatalf("unexpected Luna price: %+v", luna) } // 1M input + 1M output = $0.20 + $1.20 = $1.40 = 1_400_000 micros. if got := CostMicros(luna, 1_000_000, 0, 1_000_000); got != 1_400_000 { t.Fatalf("got %d micros want 1400000", got) } // A realistic enhance call: 4k prompt, 800 completion. // 4000*0.2 + 800*1.2 per 1M = 800 + 960 = 1760 micros ($0.00176). if got := CostMicros(luna, 4000, 0, 800); got != 1760 { t.Fatalf("got %d micros want 1760", got) } } // Cached tokens are a SUBSET of prompt tokens, not extra ones. Billing them on top // of full input would overstate every repeated system prompt. func TestCostMicros_cachedTokensAreDiscountedNotAdded(t *testing.T) { t.Parallel() luna, _ := DefaultModelPrice("gpt-5.6-luna") full := CostMicros(luna, 1_000_000, 0, 0) // 1M fresh input = 200000 allCached := CostMicros(luna, 1_000_000, 1_000_000, 0) // 1M cached = 20000 if full != 200_000 { t.Fatalf("fresh input = %d want 200000", full) } if allCached != 20_000 { t.Fatalf("fully cached input = %d want 20000", allCached) } if allCached >= full { t.Fatal("cached input must be cheaper than fresh input") } // Half cached: 500k*0.2 + 500k*0.02 per 1M = 100000 + 10000. if got := CostMicros(luna, 1_000_000, 500_000, 0); got != 110_000 { t.Fatalf("half cached = %d want 110000", got) } // A provider reporting more cached than prompt tokens must not go negative. if got := CostMicros(luna, 100, 900, 0); got < 0 { t.Fatalf("cached > prompt produced negative cost %d", got) } } // Small calls must not round to zero, or per-call spend vanishes at scale. func TestCostMicros_smallCallsStillCost(t *testing.T) { t.Parallel() luna, _ := DefaultModelPrice("gpt-5.6-luna") if got := CostMicros(luna, 500, 0, 100); got <= 0 { t.Fatalf("small call cost %d, expected > 0", got) } } func TestCostMicros_unknownModelIsFree(t *testing.T) { t.Parallel() if _, ok := DefaultModelPrice("some-local-model"); ok { t.Fatal("unknown model should not have a shipped price") } // Zero price rather than a guessed rate: better to under-report than invent spend. if got := CostMicros(ModelPrice{}, 1_000_000, 0, 1_000_000); got != 0 { t.Fatalf("unpriced model cost %d want 0", got) } } func TestNormalizeModel(t *testing.T) { t.Parallel() cases := map[string]string{ "gpt-5.6-luna": "gpt-5.6-luna", "GPT-5.6-Luna": "gpt-5.6-luna", "openai/gpt-5.6-luna": "gpt-5.6-luna", "gpt-5.6-luna-2026-07-30": "gpt-5.6-luna", " openai/GPT-5.6-Terra ": "gpt-5.6-terra", "mock-llm": "mock-llm", } for in, want := range cases { if got := NormalizeModel(in); got != want { t.Fatalf("NormalizeModel(%q) = %q want %q", in, got, want) } } // A dated snapshot must still price like its base model. if _, ok := DefaultModelPrice("gpt-5.6-luna-2026-07-30"); !ok { t.Fatal("dated snapshot should resolve to the base model price") } } // Three years, so quarter- and year-scale cost questions still have data. func TestUsageRetentionCoversThreeYears(t *testing.T) { t.Parallel() if UsageRetentionDays < 3*365 { t.Fatalf("usage retention %d days is under 3 years", UsageRetentionDays) } if RetentionDays >= UsageRetentionDays { t.Fatal("prompt bodies must expire long before the cost rollup") } }