From 68c9bef38fe3a6b1fd37cc438764b679e9e2f990 Mon Sep 17 00:00:00 2001 From: Kjell Tore Guttormsen Date: Fri, 26 Jun 2026 17:14:03 +0200 Subject: [PATCH] test(observability): close 2 MAJOR test gaps from SKAL-2 review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit F1 cache-analyzer regression guard (SC5): pin percentile (wall_time_ms_p50/p90) and time-range (oldest/newest_event_iso) — 2 of 3 'unchanged' categories were previously un-asserted on the mixed-input fixture. F2 lastMainChainModel: add direct coverage — last-wins across 2 distinct main-chain models, sidechain exclusion (even when the sidechain is the last record), and model-absent → null propagating to deriveCost refuse-to-estimate. 804 -> 807 tests (805 pass / 0 fail / 2 skipped). No test pins the test count. --- tests/lib/cache-analyzer.test.mjs | 6 +++++ tests/lib/token-usage.test.mjs | 37 +++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/tests/lib/cache-analyzer.test.mjs b/tests/lib/cache-analyzer.test.mjs index 17b4292..474f2c7 100644 --- a/tests/lib/cache-analyzer.test.mjs +++ b/tests/lib/cache-analyzer.test.mjs @@ -58,8 +58,14 @@ test('summarize — existing duration/event-name fields still correct on mixed i // Pre-existing behavior (regression guard) assert.equal(s.total_events, 3); assert.equal(s.events_with_duration, 2); + // Percentiles: durations [100,200] → floor(2·0.5)=floor(2·0.9)=idx 1 → both 200. + assert.equal(s.wall_time_ms_p50, 200); + assert.equal(s.wall_time_ms_p90, 200); assert.equal(s.wall_time_ms_max, 200); assert.deepEqual(s.unique_event_names, ['main-merge-approved', 'main-merge-gate']); + // Time range spans all ts-bearing lines (incl. the token-only record at 12:00). + assert.equal(s.oldest_event_iso, '2026-06-26T10:00:00.000Z'); + assert.equal(s.newest_event_iso, '2026-06-26T12:00:00.000Z'); // New behavior coexists assert.equal(s.total_tokens_input, 1000); assert.equal(s.sessions_with_tokens, 1); diff --git a/tests/lib/token-usage.test.mjs b/tests/lib/token-usage.test.mjs index 36e7f8d..bccdfe7 100644 --- a/tests/lib/token-usage.test.mjs +++ b/tests/lib/token-usage.test.mjs @@ -16,6 +16,7 @@ import { deriveCost, buildRecord, upsertSessionRecord, + lastMainChainModel, PRICE_TABLE, PRICE_TABLE_VERSION, } from '../../lib/stats/token-usage.mjs'; @@ -140,3 +141,39 @@ test('upsertSessionRecord — empty file → single record, trailing newline', ( 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); +});