Validate on Linux Node 22 and PostgreSQL 17: 3894 frontend tests and 64 database tests pass, with no removed test names or new failures. Preserve static Home, bounded gzip, assertion-change records and manual acceptance gaps. Co-Authored-By: Claude Code <noreply@anthropic.com>
59 lines
3.5 KiB
TypeScript
59 lines
3.5 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { readFileSync } from "node:fs";
|
|
import test from "node:test";
|
|
|
|
const provider = readFileSync(new URL("../src/lib/session-list-context.tsx", import.meta.url), "utf8");
|
|
const layout = readFileSync(new URL("../src/app/(app)/layout.tsx", import.meta.url), "utf8");
|
|
const page = readFileSync(new URL("../src/app/(app)/page.tsx", import.meta.url), "utf8");
|
|
const sessionHook = readFileSync(new URL("../src/hooks/use-session-management.ts", import.meta.url), "utf8");
|
|
|
|
test("the app layout owns one SessionListProvider and one SidebarProvider", () => {
|
|
assert.match(layout, /<SessionListProvider>/);
|
|
assert.equal(layout.match(/<SessionListProvider>/g)?.length, 1);
|
|
assert.match(layout, /<SidebarProvider escapeBlocked=\{registration\?\.escapeBlocked \?\? false\}>/);
|
|
assert.equal(layout.match(/<SidebarProvider/g)?.length, 1);
|
|
assert.match(layout, /<AppSidebar/);
|
|
assert.doesNotMatch(page, /<SidebarProvider/);
|
|
assert.doesNotMatch(page, /<AppSidebar/);
|
|
});
|
|
|
|
test("the provider fetches the session list once and home does not fetch it again", () => {
|
|
assert.match(provider, /fetch\(`\/api\/sessions\?limit=\$\{SESSION_PAGE_SIZE\}&subject=\$\{encodeURIComponent\(subjectId\)\}`/);
|
|
assert.match(provider, /fetch\("\/api\/account"/);
|
|
// 原值: page.tsx 里 `await sessionListReady;`
|
|
// 新值: page.tsx 把 sessionListReady 交给 runHomeBootstrap,函数里 `await deps.sessionListReady`。
|
|
// 原因: 启动流程搬出首页后,会话列表仍只由 provider 拉一次。
|
|
const bootstrapRun = readFileSync(new URL("../src/lib/home-bootstrap-run.ts", import.meta.url), "utf8");
|
|
assert.match(page, /sessionListReady,/);
|
|
// 原值 / 新值 / 原因:固定 ready promise / 当前 reload gate / 切人期间回 Home 必须等新绑定,不能读空 boot 误登出。
|
|
assert.match(bootstrapRun, /await \(deps\.waitForSessionList\?\.\(\) \?\? deps\.sessionListReady\);/);
|
|
assert.match(page, /waitForSessionList,/);
|
|
assert.doesNotMatch(page, /fetchSessions\(/);
|
|
assert.doesNotMatch(page, /fetchAccount\(/);
|
|
assert.match(page, /useHomeShellRegistration\(/);
|
|
});
|
|
|
|
test("a 401 clears the list and marks signedOut", () => {
|
|
// 原值 / 新值 / 原因:并行 OR 401 / 先账户再会话各自 401 / 先完成账户绑定,禁止串账户请求。
|
|
assert.match(provider, /accountResponse\.status === 401/);
|
|
assert.match(provider, /sessionResponse\.status === 401/);
|
|
assert.match(provider, /signedOut: true/);
|
|
assert.doesNotMatch(provider, /localStorage|sessionStorage|document\.cookie|indexedDB/i);
|
|
});
|
|
|
|
test("session writes no longer invalidate a second cache", () => {
|
|
assert.doesNotMatch(sessionHook, /invalidateSidebarCache/);
|
|
});
|
|
|
|
test("only the shell consumes registration and it passes Home children through unchanged", () => {
|
|
assert.match(layout, /const registration = useShellRegistration\(\)/);
|
|
assert.match(layout, /sessionListSidebarModel\(list, registration\)/);
|
|
assert.doesNotMatch(layout, /list\.registration|cloneElement/);
|
|
assert.match(layout, /<SidebarInset[\s\S]*>\s*\{children\}\s*<\/SidebarInset>/);
|
|
assert.doesNotMatch(page, /useShellRegistration/);
|
|
const dataValue = provider.slice(provider.indexOf("const value = useMemo"), provider.indexOf("return (", provider.indexOf("const value = useMemo")));
|
|
assert.doesNotMatch(dataValue, /\bregistration\b/);
|
|
assert.match(dataValue, /registerShellControls/);
|
|
assert.match(provider, /<ShellRegistrationContext\.Provider value=\{registration\}>\s*\{children\}/);
|
|
});
|