开流前的注销检查可以和读会话、解析模型同时进行;审核改到扣点前等待,不再挡住第一行;模型版本可以短缓存;两个领域可以一起算。本机没有数据库中位数,也没有带星盘网络库的镜像,所以默认仍是关闭、缓存 0、领域逐个算。 自测:tsc 0 错;lint 0 error;相关测试失败 0、取消 0。test:db 未跑。 Co-Authored-By: Grok (agent loop)
148 lines
6.6 KiB
TypeScript
148 lines
6.6 KiB
TypeScript
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;/,
|
||
);
|
||
});
|