// tests/lib/token-usage.test.mjs // SKAL-2 Step 1 — pure token-usage parser + cache-aware cost derivation. // // Hermetic: no LLM, network, time, or randomness. Fixture transcript exercises // the dedup-by-requestId (streaming-placeholder) and isSidechain-exclusion // paths that were verified against a real local transcript at execute time. // Idiom: tests/lib/coordinator-contract.test.mjs (pure-module node:test style). import { test } from 'node:test'; import { strict as assert } from 'node:assert'; import { readFileSync } from 'node:fs'; import { join, dirname } from 'node:path'; import { fileURLToPath } from 'node:url'; import { parseTranscriptUsage, deriveCost, buildRecord, upsertSessionRecord, lastMainChainModel, PRICE_TABLE, PRICE_TABLE_VERSION, } from '../../lib/stats/token-usage.mjs'; const HERE = dirname(fileURLToPath(import.meta.url)); const FIXTURE = join(HERE, '..', 'fixtures', 'token-usage', 'sample-transcript.jsonl'); // ---- parseTranscriptUsage -------------------------------------------------- test('parseTranscriptUsage — sums main-chain final-per-requestId; excludes sidechain + placeholder dup', () => { const text = readFileSync(FIXTURE, 'utf-8'); const totals = parseTranscriptUsage(text); // req_A: placeholder {1,1,0,0} then real {1000,200,400,5000} → LAST wins. // req_B: appears twice identically → dedup → counted once {2000,300,600,1000}. // req_SIDE (isSidechain:true, all 9999) → EXCLUDED. user/system/malformed → skipped. assert.deepEqual(totals, { tokens_input: 3000, // 1000 + 2000 (NOT 1 — placeholder dropped) tokens_output: 500, // 200 + 300 tokens_cache_creation: 1000, // 400 + 600 tokens_cache_read: 6000, // 5000 + 1000 }); // Sidechain 9999s must not leak into any field. assert.ok(totals.tokens_input < 9999, 'sidechain tokens leaked into total'); }); test('parseTranscriptUsage — empty / nullish input → zeroed totals', () => { const zero = { tokens_input: 0, tokens_output: 0, tokens_cache_creation: 0, tokens_cache_read: 0 }; assert.deepEqual(parseTranscriptUsage(''), zero); assert.deepEqual(parseTranscriptUsage(null), zero); assert.deepEqual(parseTranscriptUsage(undefined), zero); }); test('parseTranscriptUsage — single-pass O(n) shape on a 500-line transcript', () => { // 500 distinct-requestId main-chain assistant records, each input_tokens:10. const lines = []; for (let i = 0; i < 500; i++) { lines.push(JSON.stringify({ type: 'assistant', isSidechain: false, requestId: `req_${i}`, message: { model: 'claude-opus-4-8', usage: { input_tokens: 10, output_tokens: 0, cache_creation_input_tokens: 0, cache_read_input_tokens: 0 } }, })); } const totals = parseTranscriptUsage(lines.join('\n')); assert.equal(totals.tokens_input, 5000); // 500 × 10 }); // ---- deriveCost ------------------------------------------------------------ test('deriveCost — hand-computed value for a known model (cache-aware)', () => { const totals = { tokens_input: 3000, tokens_output: 500, tokens_cache_creation: 1000, tokens_cache_read: 6000 }; const { cost_usd, is_estimate } = deriveCost(totals, 'claude-opus-4-8'); // (3000×5 + 500×25 + 1000×6.25 + 6000×0.5) / 1e6 // = (15000 + 12500 + 6250 + 3000) / 1e6 = 36750 / 1e6 = 0.03675 assert.equal(cost_usd, 0.03675); assert.equal(is_estimate, false); }); test('deriveCost — refuse-to-estimate for an unknown model', () => { const totals = { tokens_input: 3000, tokens_output: 500, tokens_cache_creation: 1000, tokens_cache_read: 6000 }; const out = deriveCost(totals, 'claude-unknown-9'); assert.equal(out.cost_usd, null); assert.equal(out.is_estimate, true); }); test('PRICE_TABLE — frozen, opus-4-8 carries all five rate fields', () => { assert.ok(Object.isFrozen(PRICE_TABLE), 'PRICE_TABLE must be frozen (drift-pin)'); const p = PRICE_TABLE['claude-opus-4-8']; for (const k of ['input', 'output', 'cache_read', 'cache_write_5m', 'cache_write_1h']) { assert.equal(typeof p[k], 'number', `missing rate field ${k}`); } }); // ---- buildRecord ----------------------------------------------------------- test('buildRecord — stamps scope:main-context + price_table_version + flat numerics', () => { const totals = { tokens_input: 3000, tokens_output: 500, tokens_cache_creation: 1000, tokens_cache_read: 6000 }; const rec = buildRecord({ sessionId: 'S1', model: 'claude-opus-4-8', totals, now: '2026-06-26T12:00:00Z' }); assert.equal(rec.scope, 'main-context'); assert.equal(rec.price_table_version, PRICE_TABLE_VERSION); assert.equal(rec.ts, '2026-06-26T12:00:00Z'); assert.equal(rec.session_id, 'S1'); assert.equal(rec.model, 'claude-opus-4-8'); assert.equal(rec.tokens_input, 3000); assert.equal(rec.cost_usd, 0.03675); assert.equal(rec.is_estimate, false); }); test('buildRecord — unknown model → cost_usd null + is_estimate true (honesty contract)', () => { const rec = buildRecord({ sessionId: 'S9', model: 'mystery', totals: { tokens_input: 1 }, now: 't' }); assert.equal(rec.cost_usd, null); assert.equal(rec.is_estimate, true); }); // ---- upsertSessionRecord --------------------------------------------------- test('upsertSessionRecord — REPLACES a same-session line (not append)', () => { const existing = JSON.stringify({ session_id: 'S1', tokens_input: 1, scope: 'main-context' }) + '\n' + JSON.stringify({ session_id: 'S2', tokens_input: 2, scope: 'main-context' }) + '\n'; const rec = buildRecord({ sessionId: 'S1', model: 'claude-opus-4-8', totals: { tokens_input: 999 }, now: 't' }); const out = upsertSessionRecord(existing, rec); const recs = out.trim().split('\n').map(JSON.parse); assert.equal(recs.length, 2, 'replace must NOT add a line'); const s1 = recs.find(r => r.session_id === 'S1'); assert.equal(s1.tokens_input, 999, 'S1 must carry the new totals'); assert.ok(recs.some(r => r.session_id === 'S2'), 'S2 must be preserved'); }); test('upsertSessionRecord — APPENDS a new-session line', () => { const existing = JSON.stringify({ session_id: 'S1', tokens_input: 1 }) + '\n'; const rec = buildRecord({ sessionId: 'S3', model: 'claude-opus-4-8', totals: { tokens_input: 7 }, now: 't' }); const out = upsertSessionRecord(existing, rec); const recs = out.trim().split('\n').map(JSON.parse); assert.equal(recs.length, 2, 'new session must append'); assert.ok(recs.some(r => r.session_id === 'S3' && r.tokens_input === 7)); }); test('upsertSessionRecord — empty file → single record, trailing newline', () => { const rec = buildRecord({ sessionId: 'S1', model: 'claude-opus-4-8', totals: { tokens_input: 5 }, now: 't' }); const out = upsertSessionRecord('', rec); assert.ok(out.endsWith('\n')); assert.deepEqual(out.trim().split('\n').map(JSON.parse).length, 1); }); // ---- lastMainChainModel ---------------------------------------------------- test('lastMainChainModel — last main-chain model wins; sidechain excluded even when last', () => { // Two distinct main-chain models (opus then sonnet) → sonnet (last) wins. // A sidechain record sits AFTER sonnet: if exclusion broke it would win; // if last-wins broke, opus (first) would win. Correct answer pins both. const text = [ JSON.stringify({ type: 'assistant', isSidechain: false, message: { model: 'claude-opus-4-8', usage: {} } }), JSON.stringify({ type: 'assistant', isSidechain: false, message: { model: 'claude-sonnet-4-6', usage: {} } }), JSON.stringify({ type: 'assistant', isSidechain: true, message: { model: 'claude-sidechain-ZZZ', usage: {} } }), JSON.stringify({ type: 'user', message: { content: 'ignored' } }), ].join('\n'); assert.equal(lastMainChainModel(text), 'claude-sonnet-4-6'); }); test('lastMainChainModel — no main-chain model → null → deriveCost refuses to estimate', () => { // Only a sidechain model, a user line, and a malformed line: no main-chain model. const text = [ JSON.stringify({ type: 'assistant', isSidechain: true, message: { model: 'claude-opus-4-8', usage: {} } }), JSON.stringify({ type: 'user', message: { content: 'hi' } }), 'not json', ].join('\n'); const model = lastMainChainModel(text); assert.equal(model, null); // null model must propagate to refuse-to-estimate (honesty contract). const { cost_usd, is_estimate } = deriveCost({ tokens_input: 1000 }, model); assert.equal(cost_usd, null); assert.equal(is_estimate, true); }); test('lastMainChainModel — empty / nullish input → null', () => { assert.equal(lastMainChainModel(''), null); assert.equal(lastMainChainModel(null), null); assert.equal(lastMainChainModel(undefined), null); });