| 1 | import assert from "node:assert/strict"; |
| 2 | import { managementDom } from "../test-support/managementDom"; |
| 3 | import { installDesktopHostStub } from "./desktopHostStub"; |
| 4 | |
| 5 | const dom = managementDom(); |
| 6 | const { default: React, act } = await import("react"); |
| 7 | const { createRoot } = await import("react-dom/client"); |
| 8 | const { LocaleProvider } = await import("../lib/i18n"); |
| 9 | const { ArchivedSessionsList } = await import("../components/ArchivedSessionsList"); |
| 10 | |
| 11 | const requests: Array<{ targets: Array<{ workspaceId?: string; recoveryEntryId?: string; ref?: unknown }> }> = []; |
| 12 | const opened: string[] = []; |
| 13 | let restored = false; |
| 14 | let cleanupRetries = 0; |
| 15 | const cleanupStatus = { |
| 16 | version: 1, batchId: "batch", state: "pending", removed: 1, pending: 2, |
| 17 | busy: 1, unknown: 1, protected: 0, hasContent: 0, items: [], |
| 18 | }; |
| 19 | const placeholder = { |
| 20 | id: "topic:legacy-placeholder", |
| 21 | recoveryEntryId: "legacy-cleanup:topic:legacy-placeholder", |
| 22 | title: "New session", |
| 23 | workspaceId: "workspace-a", |
| 24 | workspaceTitle: "Project A", |
| 25 | archivedAt: 42, |
| 26 | health: "ready", |
| 27 | canRestore: true, |
| 28 | canPreview: false, |
| 29 | canPurge: false, |
| 30 | cleanupBatchId: "batch", |
| 31 | cleanupKind: "topic_placeholder", |
| 32 | }; |
| 33 | const host = installDesktopHostStub({ |
| 34 | ListTrashEntries: async () => ({ items: restored ? [] : [placeholder], generation: 7 }), |
| 35 | GetLegacyEmptySessionCleanupStatus: async () => cleanupStatus, |
| 36 | RetryLegacyEmptySessionCleanup: async () => { cleanupRetries++; return cleanupStatus; }, |
| 37 | ApplySessionLifecycle: async (request: { targets: Array<{ workspaceId?: string; recoveryEntryId?: string; ref?: unknown }> }) => { |
| 38 | requests.push(request); |
| 39 | restored = true; |
| 40 | return { |
| 41 | committed: true, |
| 42 | generation: 8, |
| 43 | items: request.targets.map(target => ({ target, workspaceId: target.workspaceId, committed: true, retryable: false })), |
| 44 | }; |
| 45 | }, |
| 46 | }); |
| 47 | |
| 48 | const root = createRoot(document.getElementById("root")!); |
| 49 | await act(async () => root.render( |
| 50 | <LocaleProvider> |
| 51 | <ArchivedSessionsList active={true} onOpenSession={async ref => { opened.push(ref.sessionId); }} /> |
| 52 | </LocaleProvider>, |
| 53 | )); |
| 54 | |
| 55 | const row = document.querySelector(".archived-sessions__row")!; |
| 56 | assert.ok(row.textContent?.includes("Legacy session placeholder")); |
| 57 | assert.equal((row.querySelector(".archived-sessions__open") as HTMLButtonElement).disabled, true, "placeholder has no transcript preview"); |
| 58 | assert.ok(document.body.textContent?.includes("2 legacy sessions still need verification.")); |
| 59 | |
| 60 | await act(async () => (Array.from(document.querySelectorAll("button")).find(button => button.textContent === "Recheck") as HTMLButtonElement).click()); |
| 61 | assert.equal(cleanupRetries, 1, "pending cleanup can be explicitly rechecked"); |
| 62 | |
| 63 | await act(async () => (row.querySelector('[aria-label="Restore session"]') as HTMLButtonElement).click()); |
| 64 | assert.equal(requests.length, 1); |
| 65 | assert.deepEqual(requests[0].targets, [{ workspaceId: "workspace-a", recoveryEntryId: "legacy-cleanup:topic:legacy-placeholder" }]); |
| 66 | assert.deepEqual(opened, [], "restoring a placeholder must not open or create a formal session"); |
| 67 | assert.equal(document.querySelectorAll(".archived-sessions__row").length, 0); |
| 68 | |
| 69 | await act(async () => root.unmount()); |
| 70 | host.uninstall(); |
| 71 | dom.window.close(); |
| 72 | console.log("PASS legacy cleanup placeholder restore keeps session identity absent"); |
| 73 |