| 1 | import assert from "node:assert/strict"; |
| 2 | import { JSDOM } from "jsdom"; |
| 3 | import { app } from "../lib/bridge"; |
| 4 | |
| 5 | const dom = new JSDOM("", { url: "http://localhost/?mock=bench" }); |
| 6 | Object.assign(globalThis, { window: dom.window, document: dom.window.document, localStorage: dom.window.localStorage }); |
| 7 | try { |
| 8 | const local = (await app.ListTabs())[0]; |
| 9 | const remote = await app.OpenRemoteProjectTab("demo", "~/app", { sessionName: "intro" }); |
| 10 | assert.equal(remote.sessionPath, "~/app/sessions/intro.jsonl"); |
| 11 | assert.ok((await app.ListTabs()).some(tab => tab.id === remote.id), "remote open and ListTabs share the backend catalog"); |
| 12 | await app.SetActiveTab(remote.id); |
| 13 | assert.deepEqual((await app.ListTabs()).filter(tab => tab.active).map(tab => tab.id), [remote.id]); |
| 14 | await app.SetRemoteTabModel(remote.id, "fixture/model"); |
| 15 | assert.equal((await app.ListTabs()).find(tab => tab.id === remote.id)?.label, "fixture/model"); |
| 16 | const renewed = await app.OpenRemoteProjectTab("demo", "~/app", { newSession: true }); |
| 17 | assert.equal(renewed.id, remote.id, "remote new session reuses its workspace surface"); |
| 18 | assert.ok(renewed.sessionPath && renewed.sessionPath !== remote.sessionPath, "new session replaces the durable session identity"); |
| 19 | assert.equal((await app.ListTabs()).filter(tab => tab.id === remote.id).length, 1); |
| 20 | assert.equal((await app.ListTabs()).find(tab => tab.id === remote.id)?.topicTitle, "New session"); |
| 21 | const status = await app.RemoteTabStatus(remote.id) as Record<string, unknown>; |
| 22 | assert.equal(status.sessionPath, renewed.sessionPath); |
| 23 | assert.equal(status.plan, false); |
| 24 | assert.equal(status.toolApprovalMode, "workspace-write"); |
| 25 | assert.equal(status.goal, ""); |
| 26 | assert.deepEqual((await app.RemoteTabSnapshot(remote.id)).status, status, "snapshot and status share the authoritative composer profile"); |
| 27 | // A canonical row carries only its immutable session id: opening it must |
| 28 | // bind that identity on the tab (the Serve publishes TabMeta.sessionId from |
| 29 | // opts.SessionID) instead of falling back to a synthesized basename route. |
| 30 | const canonical = await app.OpenRemoteProjectTab("demo", "~/app", { sessionId: "canonical-session-1" }); |
| 31 | assert.equal(canonical.id, remote.id, "a canonical session reuses its workspace surface"); |
| 32 | assert.equal(canonical.sessionId, "canonical-session-1", "opening by session id binds the canonical identity"); |
| 33 | assert.ok(!canonical.sessionPath?.includes("intro"), "a canonical open does not inherit the previous legacy route"); |
| 34 | const legacy = await app.OpenRemoteProjectTab("demo", "~/app", { sessionName: "intro" }); |
| 35 | assert.equal(legacy.sessionPath, "~/app/sessions/intro.jsonl", "legacy rows keep their basename route"); |
| 36 | assert.ok((await app.RemoteProjectSessions("demo", "~/app")).find(row => row.name === "intro")?.current, |
| 37 | "the opened legacy session becomes the current row"); |
| 38 | await app.SetActiveTab(local.id); |
| 39 | assert.deepEqual((await app.ListTabs()).filter(tab => tab.active).map(tab => tab.id), [local.id]); |
| 40 | await app.CloseRemoteTab(remote.id); |
| 41 | assert.ok(!(await app.ListTabs()).some(tab => tab.id === remote.id)); |
| 42 | await assert.rejects(app.SetActiveTab(remote.id), /not found/); |
| 43 | console.log("mock remote catalog: open, refresh, model, new session, selection and close agree"); |
| 44 | } finally { dom.window.close(); } |
| 45 |