import assert from "node:assert/strict"; import { readFileSync } from "node:fs"; import test from "node:test"; import { createElement } from "react"; import { renderToStaticMarkup } from "react-dom/server"; import { deliveryTurnNarration, postAdoptVerifyDoneCopy, RECTIFICATION_USER_COPY, } from "../src/lib/rectification-agentic/user-copy.ts"; import { alreadyDelivered, markDeliveryTurn, resetDeliveryTurnGuardForTests, } from "../src/lib/rectification-agentic/v9/delivery-turn-guard.ts"; import { RectificationVerificationReport } from "../src/components/rectification-verification-report.tsx"; test("delivery body is three sentences and omits the eight-method report", () => { const text = deliveryTurnNarration({ credibleRange: ["04:51", "04:59"], representativeTime: "04:53", eventCount: 7, fitPercent: 80, }); assert.equal(text, "这次给出的范围 04:51–04:59。对照了 7 件经历,事件吻合率 80%。这只是代表性候选,不是已确认的唯一出生分钟。"); assert.doesNotMatch(text, /方法1/); assert.doesNotMatch(text, /Technique Audit/); assert.doesNotMatch(text, /排盘用/); }); test("skipping the last reverse-verify keeps the original post-adopt sentence", () => { assert.equal( postAdoptVerifyDoneCopy("05:07", true), RECTIFICATION_USER_COPY.postAdoptVerifyDone, ); assert.equal( postAdoptVerifyDoneCopy("05:07", false), "已采用 05:07,新建对话即按这个时间排盘。", ); }); test("verification report fold is closed and renders a real table", () => { const html = renderToStaticMarkup(createElement(RectificationVerificationReport, { markdown: "| 方法 | 结果 |\n| --- | --- |\n| 1 | 吻合 |", })); assert.match(html, /
]*\sopen/); assert.match(html, /查看验证报告/); assert.match(html, / { resetDeliveryTurnGuardForTests(); const caseId = "11111111-1111-4111-8111-111111111111"; const resultId = "22222222-2222-4222-8222-222222222222"; assert.equal(alreadyDelivered({ caseId, resultId, hasUserMessage: false, now: 1_000 }), false); markDeliveryTurn({ caseId, resultId, now: 1_000 }); assert.equal(alreadyDelivered({ caseId, resultId, hasUserMessage: false, now: 1_500 }), true); assert.equal(alreadyDelivered({ caseId, resultId, hasUserMessage: true, now: 1_500 }), false); assert.equal(alreadyDelivered({ caseId, resultId: "33333333-3333-4333-8333-333333333333", hasUserMessage: false, now: 1_500, }), false); assert.equal(alreadyDelivered({ caseId, resultId, hasUserMessage: false, now: 62_000 }), false); resetDeliveryTurnGuardForTests(); }); test("agent-run returns already_delivered before billing and the route skips a second turn", () => { const agentRun = readFileSync(new URL("../src/lib/rectification-agentic/v9/agent-run.ts", import.meta.url), "utf8"); const route = readFileSync(new URL("../src/app/api/rectification/agent/route.ts", import.meta.url), "utf8"); const exit = readFileSync(new URL("../src/lib/rectification-agentic/v9/turn-exit.ts", import.meta.url), "utf8"); const regenerate = readFileSync( new URL("../src/app/api/rectification/cases/[caseId]/turns/[turnId]/regenerate/route.ts", import.meta.url), "utf8", ); const guardStart = agentRun.indexOf("alreadyDelivered({"); const reserve = agentRun.indexOf("const reserve = await billing.reserve()"); assert.ok(guardStart >= 0 && guardStart < reserve); assert.match(agentRun, /errorCode: "already_delivered"/); assert.match(route, /result\.errorCode === "already_delivered"/); assert.match(route, /trigger: "opening"/); assert.match(exit, /trigger: "finalizeSuccessfulTurnExit"/); assert.match(regenerate, /trigger: "regenerate"/); assert.match(route, /logRectificationDeliveryTurn/); }); test("composer wrap no longer hosts 先这样 or the adopt status bar", () => { const chat = readFileSync(new URL("../src/components/rectification-agentic-chat.tsx", import.meta.url), "utf8"); const wrap = chat.slice(chat.indexOf("className=\"composer-wrap\""), chat.indexOf("className=\"composer-footer\"")); assert.doesNotMatch(wrap, /CHOICE_STOP_LABEL/); assert.doesNotMatch(wrap, /rectification-collect-stop/); assert.doesNotMatch(wrap, /rectification-composer-meta/); assert.doesNotMatch(wrap, /rectification-adopt-status/); assert.match(chat, /CHOICE_STOP_LABEL/); assert.match(chat, /postAdoptVerifyDoneCopy/); });