import { describe, it, expect } from 'vitest'; import { estimateCostUsd, PRICING } from './pricing'; import { HAIKU, SONNET, OPUS } from './models'; describe('PRICING', () => { it('has rows for all three analyzer models', () => { expect(PRICING[HAIKU]).toBeDefined(); expect(PRICING[SONNET]).toBeDefined(); expect(PRICING[OPUS]).toBeDefined(); }); it('output is more expensive than input on every model', () => { for (const m of [HAIKU, SONNET, OPUS] as const) { expect(PRICING[m].output).toBeGreaterThan(PRICING[m].input); } }); it('cache_read is much cheaper than uncached input', () => { for (const m of [HAIKU, SONNET, OPUS] as const) { expect(PRICING[m].cacheRead).toBeLessThan(PRICING[m].input); } }); }); describe('estimateCostUsd', () => { it('charges Haiku $1/1M input + $5/1M output', () => { // 1,000,000 input + 1,000,000 output = $1 + $5 = $6 expect( estimateCostUsd(HAIKU, { input_tokens: 1_000_000, output_tokens: 1_000_000 }) ).toBe(6); }); it('charges Sonnet $3/1M input + $15/1M output', () => { expect( estimateCostUsd(SONNET, { input_tokens: 1_000_000, output_tokens: 1_000_000 }) ).toBe(18); }); it('charges Opus $5/1M input + $25/1M output', () => { expect( estimateCostUsd(OPUS, { input_tokens: 1_000_000, output_tokens: 1_000_000 }) ).toBe(30); }); it('charges cache_read at the discounted rate', () => { // 1M cache_read at Haiku's $0.10 = $0.10 const cost = estimateCostUsd(HAIKU, { input_tokens: 0, output_tokens: 0, cache_read_input_tokens: 1_000_000, }); expect(cost).toBeCloseTo(0.1, 4); }); it('charges cache_creation at the 1.25x premium', () => { // 1M cache_write at Haiku's $1.25 = $1.25 const cost = estimateCostUsd(HAIKU, { input_tokens: 0, output_tokens: 0, cache_creation_input_tokens: 1_000_000, }); expect(cost).toBeCloseTo(1.25, 4); }); it('rounds to 4 decimals (matches DB column)', () => { const cost = estimateCostUsd(HAIKU, { input_tokens: 1, output_tokens: 1 }); // 1 input @ $1/M = $0.000001 ≈ rounds to 0.0000 expect(cost.toString().split('.')[1]?.length ?? 0).toBeLessThanOrEqual(4); }); it('returns 0 for zero usage', () => { expect( estimateCostUsd(OPUS, { input_tokens: 0, output_tokens: 0 }) ).toBe(0); }); it('produces a realistic ticket-analysis cost for a typical Stage 1 call', () => { // ~10K input tokens, ~500 output tokens on Haiku ≈ $0.013 const cost = estimateCostUsd(HAIKU, { input_tokens: 10_000, output_tokens: 500, }); expect(cost).toBeCloseTo(0.0125, 4); }); });