import { describe, it, expect } from 'vitest'; import { estimateCost, MODEL_COSTS_PER_MILLION } from './costs.js'; describe('estimateCost', () => { it('returns 0 for local/unknown models', () => { expect(estimateCost(1000, 1000)).toBe(0); expect(estimateCost(1000, 1000, 'some-local-model')).toBe(0); }); it('uses default costs when model name is undefined', () => { const cost = estimateCost(1_000_000, 1_000_000); expect(cost).toBe(0); }); it('calculates correctly for known Anthropic models', () => { // claude-sonnet-4: $3/M input, $15/M output const cost = estimateCost(1_000_000, 1_000_000, 'claude-sonnet-4-20250514'); expect(cost).toBe(3 + 15); }); it('calculates correctly for claude-opus', () => { // claude-opus-4: $15/M input, $75/M output const cost = estimateCost(1_000_000, 500_000, 'claude-opus-4-20250514'); expect(cost).toBe(15 + 37.5); }); it('calculates correctly for OpenAI models', () => { // gpt-4o: $2.50/M input, $10/M output const cost = estimateCost(2_000_000, 1_000_000, 'gpt-4o'); expect(cost).toBe(5 + 10); }); it('handles small token counts', () => { // 1000 tokens of claude-sonnet input: 1000 * 3 / 1_000_000 = 0.003 const cost = estimateCost(1000, 0, 'claude-sonnet-4-20250514'); expect(cost).toBeCloseTo(0.003); }); it('handles zero tokens', () => { const cost = estimateCost(0, 0, 'claude-sonnet-4-20250514'); expect(cost).toBe(0); }); }); describe('MODEL_COSTS_PER_MILLION', () => { it('has a default entry', () => { expect(MODEL_COSTS_PER_MILLION['default']).toEqual({ input: 0, output: 0 }); }); it('has entries for all expected models', () => { expect(MODEL_COSTS_PER_MILLION['claude-sonnet-4-20250514']).toBeDefined(); expect(MODEL_COSTS_PER_MILLION['claude-3-5-haiku-20241022']).toBeDefined(); expect(MODEL_COSTS_PER_MILLION['claude-opus-4-20250514']).toBeDefined(); expect(MODEL_COSTS_PER_MILLION['gpt-4o']).toBeDefined(); expect(MODEL_COSTS_PER_MILLION['gpt-4o-mini']).toBeDefined(); }); });