Files
Jyotisha/frontend/tests/consultation-context-cache-contract.test.ts
T
Jesse_Chen bf8ad0d1ff fix(web): keep consultation conclusions across turns and surface cache hits (BUG-555, BUG-556)
Session history was silently clipped to the first 4000 characters of the last 12 messages, so follow-ups could not see timing or audit tables. Keep an append-only tail plus a checkpoint summary, retry overflow in the same request, and expose cache hit rate in admin usage.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-06 15:16:18 +08:00

61 lines
3.2 KiB
TypeScript

import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import test from "node:test";
const consultRoute = readFileSync(new URL("../src/app/api/consult/route.ts", import.meta.url), "utf8");
const history = readFileSync(new URL("../src/lib/consultation-session-history.ts", import.meta.url), "utf8");
const migration = readFileSync(
new URL("../supabase/migrations/20260906010000_chat_session_context_summary.sql", import.meta.url),
"utf8",
);
const generation = readFileSync(new URL("../src/lib/agent-generation-settings.ts", import.meta.url), "utf8");
test("consult history uses the checkpoint tail and puts the summary after the time line", () => {
assert.match(consultRoute, /consultationHistoryWindow\(chatSession\.messages, contextSummary/);
assert.match(consultRoute, /SESSION_CONTEXT_SUMMARY_HEADING|consultationUserTurnContent/);
assert.match(consultRoute, /summaryText: historyWindow\.summaryText/);
assert.match(history, /【会话摘要(服务端维护)】/);
const helper = history.slice(history.indexOf("export function consultationUserTurnContent"));
const timeLine = helper.indexOf("input.currentTime");
const summaryLine = helper.indexOf("SESSION_CONTEXT_SUMMARY_HEADING");
const questionLine = helper.indexOf("input.question");
assert.ok(timeLine >= 0 && summaryLine > timeLine && questionLine > summaryLine);
});
test("consult retries once on context overflow with summary plus the last pair", () => {
assert.match(consultRoute, /isContextOverflowError/);
assert.match(consultRoute, /streamWithOverflowRetry/);
assert.match(consultRoute, /consultationBaseMessages\(true\)/);
assert.match(consultRoute, /lastConsultationPair\(history\)/);
assert.match(history, /context_length_exceeded/);
});
test("consult checkpoints the session summary after a successful completion", () => {
assert.match(consultRoute, /void checkpointConsultationContext\(\)/);
assert.match(consultRoute, /checkpointSessionContextSummary/);
assert.match(consultRoute, /context_summary ->>updatedAt|context_summary->>updatedAt/);
assert.match(consultRoute, /console\.warn\("session context summary failed"/);
assert.doesNotMatch(consultRoute, /AbortSignal\.timeout\(\s*15/);
});
test("the context summary migration only adds one jsonb column", () => {
assert.match(migration, /add column if not exists context_summary jsonb null/);
assert.match(migration, /jsonb_typeof\(context_summary\) = 'object'/);
assert.match(migration, /grant update \(context_summary\)/);
assert.doesNotMatch(migration, /create index|backfill|update public\.chat_sessions set/);
});
test("Anthropic history cache is applied only to the last history message", () => {
assert.match(consultRoute, /cachedHistoryMessage/);
assert.match(generation, /export function cachedHistoryMessage/);
const helper = consultRoute.slice(consultRoute.indexOf("const consultationBaseMessages"));
assert.match(helper, /mapped\.length > 0/);
assert.match(helper, /cachedHistoryMessage\(mapped\[mapped\.length - 1\]/);
});
test("the published model catalog reads context_window", () => {
const catalog = readFileSync(new URL("../src/lib/model-catalog.ts", import.meta.url), "utf8");
assert.match(catalog, /v\.context_window/);
assert.match(catalog, /contextWindow:/);
});