返回 DeepSeek-Reasonix
session-lifecycle-fences.test.ts
根目录 / desktop / frontend / src / __tests__ / session-lifecycle-fences.test.ts
1 import assert from "node:assert/strict";
2 import { test } from "node:test";
3 import { createSessionLifecycleFences, sessionLifecycleFences } from "../lib/sessionLifecycleFences";
4 import { createProjectTreeRuntimeProjection } from "../lib/projectTreeRuntime";
5 import { projectSessionIdentity } from "../lib/projectSessionIdentity";
6 import type { ProjectNode, ProjectRuntimeTopic } from "../lib/types";
7
8 function session(id: string, lifecycleGeneration = 10): ProjectNode {
9 return {
10 key: id, kind: "topic", topicId: "shared-topic", label: id, root: "/repo",
11 session: { hostId: "local", sessionId: id }, lifecycleGeneration, children: [],
12 };
13 }
14 function catalog(...rows: ProjectNode[]): ProjectNode[] {
15 return [{ key: "project", kind: "project", root: "/repo", label: "Project", children: rows }];
16 }
17 function runtime(node: ProjectNode): ProjectRuntimeTopic[] {
18 return [{ scope: "project", workspaceRoot: "/repo", node: { ...node, running: true, runtimeOnly: true } }];
19 }
20
21 test("the application fence survives sidebar projection disposal and remount", () => {
22 const archived = session("remount-archived");
23 const sibling = session("remount-sibling");
24 sessionLifecycleFences.archive(archived, { lifecycleGeneration: 10, operationId: "archive-remount" });
25 try {
26 const firstMount = createProjectTreeRuntimeProjection().apply(catalog(sibling, archived), runtime(archived), sessionLifecycleFences.keys());
27 assert.deepEqual(firstMount[0]?.children?.map(node => node.session?.sessionId), ["remount-sibling"]);
28 const secondMount = createProjectTreeRuntimeProjection().apply(catalog(sibling, archived), runtime(archived), sessionLifecycleFences.keys());
29 assert.deepEqual(secondMount[0]?.children?.map(node => node.session?.sessionId), ["remount-sibling"]);
30 assert.equal(sessionLifecycleFences.keys().has(projectSessionIdentity(archived)), true);
31 } finally {
32 sessionLifecycleFences.observeDirectory([session("remount-archived", 11)]);
33 }
34 });
35
36 test("old directory rows and runtime-only rows cannot release a committed archive fence", () => {
37 const fences = createSessionLifecycleFences();
38 const archived = session("b", 20);
39 const alias = "source\x00local\x00legacy-b";
40 fences.archive(archived, { lifecycleGeneration: 20, operationId: "archive-b", identityAliases: [alias] });
41 fences.observeDirectory(catalog(session("b", 19)));
42 fences.observeDirectory(catalog(session("b", 20)));
43 fences.observeDirectory(catalog({ ...session("b", 999), runtimeOnly: true }));
44 assert.equal(fences.keys().has(projectSessionIdentity(archived)), true);
45 assert.equal(fences.keys().has(alias), true);
46 const projected = createProjectTreeRuntimeProjection().apply(catalog(), runtime(session("b", 999)), fences.keys());
47 assert.deepEqual(projected[0]?.children, []);
48 });
49
50 test("a newer restored directory row releases only its exact session and source aliases", () => {
51 const fences = createSessionLifecycleFences();
52 const a = session("a");
53 const b = session("b");
54 const aliasA = "source\x00local\x00legacy-a";
55 const aliasB = "source\x00local\x00legacy-b";
56 fences.archive(a, { lifecycleGeneration: 10, operationId: "archive-a", identityAliases: [aliasA] });
57 fences.archive(b, { lifecycleGeneration: 10, operationId: "archive-b", identityAliases: [aliasB] });
58 const restored = session("a", 11);
59 fences.observeDirectory(catalog(restored, b));
60 assert.equal(fences.keys().has(projectSessionIdentity(a)), false);
61 assert.equal(fences.keys().has(aliasA), false);
62 assert.equal(fences.keys().has(projectSessionIdentity(b)), true);
63 assert.equal(fences.keys().has(aliasB), true);
64 const projected = createProjectTreeRuntimeProjection().apply(catalog(restored, b), runtime(b), fences.keys());
65 assert.deepEqual(projected[0]?.children?.map(node => node.session?.sessionId), ["a"]);
66 });
67
68 test("a late archive receipt cannot weaken a newer fence for the same session", () => {
69 const fences = createSessionLifecycleFences();
70 const b = session("b");
71 fences.archive(b, { lifecycleGeneration: 30, operationId: "new-archive" });
72 fences.archive(b, { lifecycleGeneration: 10, operationId: "old-archive" });
73 fences.observeDirectory([session("b", 20)]);
74 assert.equal(fences.keys().has(projectSessionIdentity(b)), true);
75 fences.observeDirectory([session("b", 31)]);
76 assert.equal(fences.keys().has(projectSessionIdentity(b)), false);
77 });
78
78 lines TYPESCRIPT