返回 DeepSeek-Reasonix
project-tree-shell-race.test.ts
根目录 / desktop / frontend / src / __tests__ / project-tree-shell-race.test.ts
1 // Run: npx tsx src/__tests__/project-tree-shell-race.test.ts
2 import assert from "node:assert/strict";
3 import { readFileSync } from "node:fs";
4 import { dirname, join } from "node:path";
5 import { fileURLToPath } from "node:url";
6
7 const root = join(dirname(fileURLToPath(import.meta.url)), "..");
8 const topic = readFileSync(join(root, "lib/projectTreeTopic.ts"), "utf8");
9 const runtime = readFileSync(join(root, "lib/projectTreeRuntime.ts"), "utf8");
10 const runtimeHook = readFileSync(join(root, "lib/useProjectTreeRuntimeProjection.ts"), "utf8");
11 const bridge = readFileSync(join(root, "lib/bridge.ts"), "utf8");
12 const panel = readFileSync(join(root, "components/ProjectTree.tsx"), "utf8");
13
14 assert.match(topic, /projectTreeShouldApplyShellSnapshot/, "shell race helper exported");
15 assert.match(topic, /treeEmpty/, "empty-tree shells bypass catalog revision watermark");
16 assert.match(panel, /projectTreeShouldApplyShellSnapshot/, "ProjectTree uses shell race helper");
17 assert.match(panel, /treeRef\.current\.length === 0/, "v2 event re-fetches shell when tree is empty");
18 assert.match(panel, /void refresh\(\)/, "empty-tree event path calls refresh");
19 assert.match(
20 panel,
21 /projectTreeTopicPageIsFresh\(topicRevisionRef\.current, listKey, page\.revision\)/,
22 "topic pages compare revisions within their own project/group list",
23 );
24 assert.doesNotMatch(
25 panel,
26 /projectTreeRevisionIsFresh\(latestRevisionRef\.current, page\.revision\)/,
27 "an unrelated project's revision cannot discard a valid slower topic page",
28 );
29 assert.match(
30 panel,
31 /onProjectTreeChangedV2[\s\S]*projectTreeRevisionIsFresh\(latestRevisionRef\.current, event\.revision\)/,
32 "equal-revision catalog events use the shared freshness contract",
33 );
34 assert.match(runtime, /onProjectTreeRuntimeChanged/, "runtime projection has a dedicated bridge subscription");
35 assert.match(runtimeHook, /bindProjectTreeRuntime/, "ProjectTree binds the runtime projection after mount");
36 assert.match(runtimeHook, /GetProjectTreeRuntimeSnapshot/, "runtime subscription reconciles with a post-subscribe snapshot");
37 assert.match(bridge, /reason !== "runtime"/, "current frontend ignores tagged legacy runtime invalidations");
38 assert.match(bridge, /reason !== "catalog-v2"/, "current frontend ignores catalog v2 compatibility invalidations");
39 assert.doesNotMatch(
40 runtime,
41 /ListProjectTopics/,
42 "runtime events never reload catalog topic pages",
43 );
44 assert.doesNotMatch(
45 panel,
46 /event\.revision\s*<=\s*latestRevisionRef\.current/,
47 "equal-revision tombstone overlays are not discarded",
48 );
49 assert.doesNotMatch(panel, /setIndexingDone\(/, "topic refresh no longer waits for a global all-directories gate");
50 assert.match(panel, /projectTreeEventAffectsFolder\(project, affected\)/, "directory revisions refresh only affected expanded projects");
51 assert.match(
52 panel,
53 /projectTreeShellSignature/,
54 "debounced reload observes project arrivals through a shell signature",
55 );
56 assert.doesNotMatch(
57 panel,
58 /\[\s*expanded,\s*loadProjectTopics,\s*query,\s*tree\s*\]/,
59 "debounced reload depends on the shell signature, not tree (topic loads would re-arm it forever)",
60 );
61 assert.doesNotMatch(panel, /timeFilter/, "ProjectTree no longer owns a sidebar time filter");
62
63 console.log(" PASS project tree shell race contract");
64
64 lines TYPESCRIPT