Files
Jyotisha/frontend/tests/rectification-step-answer.test.ts
T
Jesse_Chen 68f0759eef
Independent Staging Quality Gate / validate (push) Successful in 13m56s
Independent Staging Quality Gate / publish (push) Successful in 16m46s
fix(rectification): persist the next interview after a choice tap
Closing a discriminator used to leave GET without a card after refresh.
Write the next dated question in the same request, skip childhood career
and move probes, and do not continue a read-only turn when that question
is already persisted.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 16:30:09 +08:00

128 lines
5.1 KiB
TypeScript

import assert from "node:assert/strict";
import test from "node:test";
import {
applyStepAnswerChunk,
createStepAnswerState,
flushStepAnswerOnStreamFinish,
shouldPublishStepText,
} from "../src/lib/rectification-agentic/v9/step-answer.ts";
import { mapStreamChunkToPhase, mapStreamChunkToThinking } from "../src/lib/rectification-agentic/v9/stream-mapping.ts";
import { PUBLIC_RECTIFICATION_TOOLS } from "../src/lib/rectification-agentic/v9/public-receipt.ts";
function isPublicTool(name: string): boolean {
return (PUBLIC_RECTIFICATION_TOOLS as readonly string[]).includes(name);
}
function chunk(type: string, payload?: Record<string, unknown>) {
return { type, payload };
}
test("does not publish intermediate tool-step text as answer.delta", () => {
const state = createStepAnswerState();
applyStepAnswerChunk(state, chunk("text-delta", { text: "Let me set" }), isPublicTool);
applyStepAnswerChunk(state, chunk("tool-call", { toolName: "rectification-record-evidence-batch" }), isPublicTool);
const afterTool = applyStepAnswerChunk(
state,
chunk("tool-result", { toolName: "rectification-record-evidence-batch" }),
isPublicTool,
);
assert.equal(afterTool.kind, "discard");
const live = applyStepAnswerChunk(
state,
chunk("text-delta", { text: "记下了,2020年4月开始实习。" }),
isPublicTool,
);
assert.deepEqual(live, { kind: "live", text: "记下了,2020年4月开始实习。" });
const finish = applyStepAnswerChunk(state, chunk("step-finish", { reason: "stop" }), isPublicTool);
assert.equal(finish.kind, "none");
});
test("never publishes reasoning-delta to the browser", () => {
assert.equal(mapStreamChunkToPhase(chunk("reasoning-delta", { text: "Let me" }) as never), null);
assert.equal(mapStreamChunkToPhase(chunk("text-delta", { text: "Let me" }) as never), null);
assert.ok(mapStreamChunkToThinking(chunk("reasoning-delta", { text: "先核对经历。" }) as never));
});
test("publishes only the terminal no-tool step as assistant text", () => {
const state = createStepAnswerState();
applyStepAnswerChunk(state, chunk("step-start"), isPublicTool);
applyStepAnswerChunk(state, chunk("text-delta", { text: "_probe" }), isPublicTool);
applyStepAnswerChunk(state, chunk("tool-call", { toolName: "rectification-compare-candidates" }), isPublicTool);
applyStepAnswerChunk(state, chunk("step-finish", { stepResult: { reason: "tool-calls" } }), isPublicTool);
assert.equal(shouldPublishStepText({ calledTool: true, text: "_probe" }, "tool-calls"), false);
applyStepAnswerChunk(state, chunk("step-start"), isPublicTool);
const live = applyStepAnswerChunk(state, chunk("text-delta", { text: "已经记下实习。" }), isPublicTool);
assert.deepEqual(live, { kind: "live", text: "已经记下实习。" });
const finish = flushStepAnswerOnStreamFinish(state, "stop");
assert.equal(finish.kind, "none");
});
test("streams terminal Chinese tokens live after compare", () => {
const state = createStepAnswerState();
applyStepAnswerChunk(
state,
chunk("tool-result", { toolName: "rectification-compare-candidates" }),
isPublicTool,
);
const first = applyStepAnswerChunk(state, chunk("text-delta", { text: "记下了," }), isPublicTool);
const second = applyStepAnswerChunk(
state,
chunk("text-delta", { text: "2020年4月开始实习。" }),
isPublicTool,
);
assert.deepEqual(first, { kind: "live", text: "记下了," });
assert.deepEqual(second, { kind: "live", text: "2020年4月开始实习。" });
assert.equal(applyStepAnswerChunk(state, chunk("step-finish", { reason: "stop" }), isPublicTool).kind, "none");
});
test("retracts a live spoken prefix if that step later calls a public tool", () => {
const state = createStepAnswerState();
applyStepAnswerChunk(
state,
chunk("tool-result", { toolName: "rectification-compare-candidates" }),
isPublicTool,
);
assert.deepEqual(
applyStepAnswerChunk(state, chunk("text-delta", { text: "记下了," }), isPublicTool),
{ kind: "live", text: "记下了," },
);
const retracted = applyStepAnswerChunk(
state,
chunk("tool-call", { toolName: "rectification-read-diagnostics" }),
isPublicTool,
);
assert.equal(retracted.kind, "retract");
applyStepAnswerChunk(
state,
chunk("tool-result", { toolName: "rectification-read-diagnostics" }),
isPublicTool,
);
const spoken = applyStepAnswerChunk(
state,
chunk("text-delta", { text: "诊断已经看过。接下来确认入职年份。" }),
isPublicTool,
);
assert.deepEqual(spoken, { kind: "live", text: "诊断已经看过。接下来确认入职年份。" });
});
test("does not live-publish English planning before a tool-call", () => {
const state = createStepAnswerState();
assert.equal(
applyStepAnswerChunk(state, chunk("text-delta", { text: "Let me set" }), isPublicTool).kind,
"none",
);
assert.equal(
applyStepAnswerChunk(state, chunk("text-delta", { text: " _probe" }), isPublicTool).kind,
"none",
);
assert.equal(
applyStepAnswerChunk(
state,
chunk("tool-call", { toolName: "rectification-record-evidence-batch" }),
isPublicTool,
).kind,
"none",
);
});