Files
Jyotisha/frontend/tests/rectification-session-composer-guard.test.ts
T
jesse-uxandGrok (agent loop) a0e223e20c 发问准备可以并行,三个开关先保持关闭(BUG-1337~1339)
开流前的注销检查可以和读会话、解析模型同时进行;审核改到扣点前等待,不再挡住第一行;模型版本可以短缓存;两个领域可以一起算。本机没有数据库中位数,也没有带星盘网络库的镜像,所以默认仍是关闭、缓存 0、领域逐个算。

自测:tsc 0 错;lint 0 error;相关测试失败 0、取消 0。test:db 未跑。

Co-Authored-By: Grok (agent loop)
2026-10-14 07:23:58 +08:00

148 lines
6.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 {
composerLocksAsRectification,
consultSendBlockedByRectificationSession,
dropRectificationStoredPending,
fallbackSessionId,
missingActiveSessionSendAction,
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 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", () => {
const head = readFileSync(new URL("../src/lib/consult/turn-prep.ts", import.meta.url), "utf8");
// 原值: consult route 里 if (!chatSession) 返回 404,类型不对返回 session_not_consultation。
// 新值: 同样两句在 turn-prep.ts,route 把读会话的结果交给它。
// 原因: 开流前的会话判断收进准备函数,文案和状态码没变。
assert.match(head, /if \(!session\.data\) return \{ ok: false, response: sessionMissing\(\) \}/);
assert.match(head, /function sessionMissing\(\)[\s\S]*咨询会话不存在[\s\S]*status: 404/);
assert.match(head, /function notConsultation\(\)[\s\S]*session_not_consultation[\s\S]*status: 409/);
assert.match(head, /session_type !== "consultation"/);
assert.match(head, /这是生时校正会话/);
assert.match(head, /请在生时校正里继续。/);
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, /fallbackSessionId\(listed\.filter\(\(session\) => sessionMatchesSubject\(session, readCurrentSubjectId\(\)\)\)\)/);
// 原值:首页第二条回退只排除校正会话。
// 新值:同一条还要 sessionMatchesSubject(当前人物)。
// 原因:当前会话对不上时,不能改打开另一个人的普通对话。
assert.match(page, /sessions\.find\(\(session\) => session\.sessionType !== "birth_time_rectification" && sessionMatchesSubject\(session, readCurrentSubjectId\(\)\)\)/);
});
test("an empty active session still locks the composer when the list is only rectification", () => {
assert.equal(
composerLocksAsRectification(undefined, [{ sessionType: "birth_time_rectification" }]),
true,
);
assert.equal(
composerLocksAsRectification(undefined, [{ sessionType: "consultation" }]),
false,
);
assert.equal(
composerLocksAsRectification({ sessionType: "consultation" }, [{ sessionType: "birth_time_rectification" }]),
false,
);
assert.deepEqual(
missingActiveSessionSendAction([{ id: "r1", sessionType: "birth_time_rectification" }]),
{ openSessionId: "r1" },
);
assert.deepEqual(
missingActiveSessionSendAction([]),
{ notice: "请先选一条对话,或点上方「新建对话」。" },
);
assert.match(pageSource, /composerLocksAsRectification\(activeSession, sessions\)/);
assert.match(sendFn, /missingActiveSessionSendAction\(sessions\)/);
assert.match(sendFn, /openRectificationSession\(action\.openSessionId\)/);
assert.match(sendFn, /setComposerNotice\(action\.notice\)/);
assert.doesNotMatch(
sendFn,
/if \(!question \|\| !currentSession \|\| !modelCatalog \|\| !account\) return false;/,
);
});