package cost import "testing" func TestParseUsageOpenAI(t *testing.T) { payload := []byte(`{ "usage": { "prompt_tokens": 1200, "completion_tokens": 340, "total_tokens": 1540, "prompt_tokens_details": {"cached_tokens": 800}, "completion_tokens_details": {"reasoning_tokens": 50} } }`) u := ParseUsage(payload) if u.PromptTokens != 1200 || u.CompletionTokens != 340 { t.Fatalf("unexpected tokens: %+v", u) } if u.CachedInputTokens != 800 || u.ReasoningTokens != 50 { t.Fatalf("unexpected details: %+v", u) } } func TestParseUsageFallback(t *testing.T) { u := ParseUsage([]byte(`{"choices":[]}`)) if u.TotalTokens != 1 { t.Fatalf("expected fallback 1, got %d", u.TotalTokens) } } func TestComputeCostMicroEUR(t *testing.T) { price := ModelPrice{ InputMicroEURPerMTok: 1_000_000, OutputMicroEURPerMTok: 2_000_000, } usage := UsageDetail{ PromptTokens: 1000, CompletionTokens: 500, CachedInputTokens: 200, TotalTokens: 1500, } cost, estimated := ComputeCostMicroEUR(usage, price, true) if estimated { t.Fatal("should not be estimated when price found") } // uncached 800 * 1 + cached 200 * 0.5 + output 500 * 2 = 800+100+1000 = 1900 micro (cached uses half input) if cost < 1800 || cost > 2000 { t.Fatalf("unexpected cost %d", cost) } } func TestComputeCostUnknownModel(t *testing.T) { usage := UsageDetail{TotalTokens: 1_000_000} cost, estimated := ComputeCostMicroEUR(usage, ModelPrice{}, false) if !estimated { t.Fatal("expected estimated") } if cost != fallbackInputMicroEURPerMTok { t.Fatalf("expected fallback cost %d, got %d", fallbackInputMicroEURPerMTok, cost) } } func TestMergeStreamUsage(t *testing.T) { acc := UsageDetail{} chunk1 := []byte(`{"usage":{"prompt_tokens":10,"completion_tokens":0,"total_tokens":10}}`) chunk2 := []byte(`{"usage":{"prompt_tokens":100,"completion_tokens":40,"total_tokens":140}}`) acc = MergeStreamUsage(acc, chunk1) acc = MergeStreamUsage(acc, chunk2) if acc.TotalTokens != 140 { t.Fatalf("expected 140, got %d", acc.TotalTokens) } }