test(observability): close 2 MAJOR test gaps from SKAL-2 review

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.
This commit is contained in:
Kjell Tore Guttormsen 2026-06-26 17:14:03 +02:00
commit 68c9bef38f
2 changed files with 43 additions and 0 deletions

View file

@ -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);

View file

@ -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);
});