import assert from "node:assert/strict"; import { readFileSync } from "node:fs"; import test from "node:test"; import { homeSurface as page } from "./home-surface.ts"; import { consultSendBlockedByRectificationSession, dropRectificationStoredPending, fallbackSessionId, RECTIFICATION_OPENING_COMPOSER_PLACEHOLDER, RECTIFICATION_REOPEN_LABEL, rectificationOrdinaryComposerReopen, SESSION_NOT_CONSULTATION_CODE, } from "../src/lib/rectification-session-composer-guard.ts"; const read = (relativePath: string) => readFileSync(new URL(relativePath, import.meta.url), "utf8"); const run = read("../src/hooks/use-consultation-run.ts"); const sessions = read("../src/hooks/use-session-management.ts"); const consultRoute = read("../src/app/api/consult/route.ts"); const pageSource = read("../src/app/(app)/page.tsx"); const sendFn = run.slice(run.indexOf("async function send("), run.indexOf('await fetch("/api/consult"')); test("a rectification session blocks consult send and keeps the draft", () => { assert.equal(consultSendBlockedByRectificationSession({ sessionType: "birth_time_rectification" }), true); assert.equal(consultSendBlockedByRectificationSession({ sessionType: "consultation" }), false); assert.match(sendFn, /consultSendBlockedByRectificationSession\(currentSession\)/); assert.match(sendFn, /setDraft\(originalQuestion\)/); assert.match(sendFn, /openRectificationSession\(currentSession\.id\)/); assert.doesNotMatch(sendFn, /fetch\("\/api\/consult"/); }); test("pending consult replay pointing at a rectification session is dropped", () => { assert.equal( dropRectificationStoredPending( { sessionId: "r1" }, [{ id: "r1", sessionType: "birth_time_rectification" }], ), null, ); assert.deepEqual( dropRectificationStoredPending( { sessionId: "c1" }, [{ id: "c1", sessionType: "consultation" }], ), { sessionId: "c1" }, ); assert.match(run, /if \(consultSendBlockedByRectificationSession\(session\)\) \{[\s\S]*sessionStorage\.removeItem\(pendingConsultationStorageKey\)/); }); test("consult POST splits missing session from wrong session type", () => { assert.match(consultRoute, /if \(!chatSession\) \{[\s\S]*咨询会话不存在[\s\S]*status: 404/); assert.match( consultRoute, /if \(chatSession\.session_type !== "consultation"\) \{[\s\S]*session_not_consultation[\s\S]*status: 409/, ); assert.match(consultRoute, /这是生时校正会话/); assert.match(consultRoute, /请在生时校正里继续。/); assert.equal(SESSION_NOT_CONSULTATION_CODE, "session_not_consultation"); assert.match(run, /caught\.code === SESSION_NOT_CONSULTATION_CODE/); }); test("ordinary composer is locked on a rectification session until the surface opens", () => { assert.equal(RECTIFICATION_OPENING_COMPOSER_PLACEHOLDER, "正在打开生时校正…"); assert.equal(RECTIFICATION_REOPEN_LABEL, "重新打开生时校正"); assert.match(pageSource, /inputDisabled=\{[^}]*activeRectificationSession/); assert.match(pageSource, /submitBlocked=\{[^}]*activeRectificationSession/); assert.match(pageSource, /ordinaryComposerPlaceholder\(/); assert.match(pageSource, /RectificationComposerReopen/); assert.equal( rectificationOrdinaryComposerReopen({ activeRectificationSession: true, error: "生时校正会话暂时无法打开,请稍后重试。", errorSessionId: "s1", sessionId: "s1", }), true, ); assert.equal( rectificationOrdinaryComposerReopen({ activeRectificationSession: true, error: "", errorSessionId: null, sessionId: "s1", }), false, ); }); test("delete and popstate fallbacks do not setActiveSessionId onto a rectification session", () => { assert.equal( fallbackSessionId([ { id: "r1", sessionType: "birth_time_rectification" }, { id: "c1", sessionType: "consultation" }, ]), "c1", ); assert.equal( fallbackSessionId([{ id: "r1", sessionType: "birth_time_rectification" }]), "r1", ); assert.match(sessions, /function activateFallbackSession/); assert.match(sessions, /if \(fallback\?\.sessionType === "birth_time_rectification"\) \{\s*selectSession\(fallbackId\);/); assert.match(sessions, /if \(activeSessionId === session.id\) \{\s*activateFallbackSession\(nextSessions\);/); assert.match(sessions, /const fallbackId = fallbackSessionId\(listed\);/); assert.match(page, /sessions.find\(\(session\) => session.sessionType !== "birth_time_rectification"\)/); });