Files
NetMesh/domain/sessionRestore.test.ts
zhaolei 3c72efcb7f
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
[Init] Initial commit - NetMesh terminal manager
2026-09-13 18:24:01 +08:00

865 lines
28 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
import {
buildSessionRestorePayload,
isRestoredDisconnectedSession,
quoteRestoreCwdForShell,
resolveRestoredActiveTabId,
resolveInheritedCwdIntent,
resolveInteractiveTerminalCdIntent,
resolveRestoreCwdIntent,
sanitizeSessionRestorePayload,
shouldAttemptRestoreCwd,
} from "./sessionRestore.ts";
import type { TerminalSession, Workspace } from "./models.ts";
const session = (id: string, workspaceId?: string): TerminalSession => ({
id,
hostId: `host-${id}`,
hostLabel: `Host ${id}`,
hostname: `${id}.example.test`,
username: "root",
status: "connected",
protocol: "ssh",
workspaceId,
});
test("buildSessionRestorePayload stores restored sessions as disconnected and drops reuse pointers", () => {
const payload = buildSessionRestorePayload({
sessions: [{ ...session("s1"), reuseConnectionFromSessionId: "source" }],
workspaces: [],
tabOrder: ["s1"],
activeTabId: "s1",
now: 123,
});
assert.equal(payload.version, 1);
assert.equal(payload.savedAt, 123);
assert.equal(payload.sessions[0].status, "disconnected");
assert.equal(payload.sessions[0].reuseConnectionFromSessionId, undefined);
assert.equal(payload.sessions[0].restoreState, "restored-disconnected");
});
test("restore keeps the duplicate-session fresh-connection marker and sanitizes it on load", () => {
const payload = buildSessionRestorePayload({
sessions: [{ ...session("s1"), requireFreshConnection: true }],
workspaces: [],
tabOrder: ["s1"],
activeTabId: "s1",
now: 123,
});
assert.equal(payload.sessions[0].requireFreshConnection, true);
const restored = sanitizeSessionRestorePayload({
version: 1,
savedAt: 123,
activeTabId: "s1",
tabOrder: ["s1"],
sessions: [payload.sessions[0] as unknown as Record<string, unknown>],
});
assert.equal(restored.sessions[0].requireFreshConnection, true);
const corrupted = sanitizeSessionRestorePayload({
version: 1,
savedAt: 123,
activeTabId: "s1",
tabOrder: ["s1"],
sessions: [{ ...payload.sessions[0], requireFreshConnection: "yes" } as unknown as Record<string, unknown>],
});
assert.equal(corrupted.sessions[0].requireFreshConnection, undefined);
});
test("buildSessionRestorePayload excludes ephemeral-host sessions and their tabs", () => {
const payload = buildSessionRestorePayload({
sessions: [
session("s1"),
{ ...session("s2"), ephemeralHost: true },
],
workspaces: [],
tabOrder: ["s1", "s2"],
activeTabId: "s2",
now: 123,
});
assert.deepEqual(payload.sessions.map((entry) => entry.id), ["s1"]);
assert.deepEqual(payload.tabOrder, ["s1"]);
assert.notEqual(payload.activeTabId, "s2");
});
test("buildSessionRestorePayload excludes silent MCP sessions and their tabs", () => {
const payload = buildSessionRestorePayload({
sessions: [
session("s1"),
{ ...session("s2"), hiddenFromTabs: true },
],
workspaces: [],
tabOrder: ["s1", "s2"],
activeTabId: "s2",
now: 123,
});
assert.deepEqual(payload.sessions.map((entry) => entry.id), ["s1"]);
assert.deepEqual(payload.tabOrder, ["s1"]);
assert.notEqual(payload.activeTabId, "s2");
});
test("buildSessionRestorePayload only stores allowlisted session fields", () => {
const payload = buildSessionRestorePayload({
sessions: [{
...session("s1"),
terminalData: "do-not-store",
reuseConnectionFromSessionId: "source",
} as TerminalSession & { terminalData: string }],
workspaces: [],
tabOrder: ["s1"],
activeTabId: "s1",
now: 123,
});
assert.deepEqual(Object.keys(payload.sessions[0]).sort(), [
"hostId",
"hostLabel",
"hostname",
"id",
"protocol",
"restoreState",
"status",
"username",
].sort());
});
test("buildSessionRestorePayload preserves local terminal start directory", () => {
const payload = buildSessionRestorePayload({
sessions: [{
...session("s1"),
protocol: "local",
localStartDir: "/Users/alice/project",
}],
workspaces: [],
tabOrder: ["s1"],
activeTabId: "s1",
now: 123,
});
assert.equal(payload.sessions[0].localStartDir, "/Users/alice/project");
});
test("buildSessionRestorePayload preserves a bounded plugin connection snapshot", () => {
const providerId = "com.example.transport.connection";
const payload = buildSessionRestorePayload({
sessions: [{
...session("plugin"),
protocol: `plugin:${providerId}`,
pluginConnection: {
providerId,
configuration: { endpoint: "saved.example", nested: { mode: "safe" } },
authenticationProviderId: "com.example.transport.authentication",
credentialId: "credential-reference-1234",
},
}],
workspaces: [],
tabOrder: ["plugin"],
activeTabId: "plugin",
now: 123,
});
assert.equal(payload.sessions[0].protocol, `plugin:${providerId}`);
assert.deepEqual(payload.sessions[0].pluginConnection, {
providerId,
configuration: { endpoint: "saved.example", nested: { mode: "safe" } },
authenticationProviderId: "com.example.transport.authentication",
credentialId: "credential-reference-1234",
});
});
test("buildSessionRestorePayload deeply allowlists serial config fields", () => {
const payload = buildSessionRestorePayload({
sessions: [{
...session("s1"),
protocol: "serial",
serialConfig: {
path: "/dev/tty.usbserial",
baudRate: 115200,
dataBits: 8,
stopBits: 1,
parity: "none",
flowControl: "none",
localEcho: true,
lineMode: true,
backspaceBehavior: "ctrl-h",
password: "do-not-store",
},
} as TerminalSession & { serialConfig: TerminalSession["serialConfig"] & { password: string } }],
workspaces: [],
tabOrder: ["s1"],
activeTabId: "s1",
now: 123,
});
assert.deepEqual(payload.sessions[0].serialConfig, {
path: "/dev/tty.usbserial",
baudRate: 115200,
dataBits: 8,
stopBits: 1,
parity: "none",
flowControl: "none",
localEcho: true,
lineMode: true,
backspaceBehavior: "ctrl-h",
});
});
test("buildSessionRestorePayload preserves serial sessions with empty usernames", () => {
const payload = buildSessionRestorePayload({
sessions: [{
...session("s1"),
username: "",
protocol: "serial",
serialConfig: {
path: "/dev/tty.usbserial",
baudRate: 115200,
},
}],
workspaces: [],
tabOrder: ["s1"],
activeTabId: "s1",
now: 123,
});
assert.equal(payload.sessions.length, 1);
assert.equal(payload.sessions[0].username, "");
assert.equal(payload.sessions[0].protocol, "serial");
});
test("buildSessionRestorePayload preserves missing Backspace behavior on legacy serial sessions", () => {
const payload = buildSessionRestorePayload({
sessions: [{
...session("s1"),
username: "",
protocol: "serial",
serialConfig: {
path: "/dev/tty.usbserial",
baudRate: 115200,
},
}],
workspaces: [],
tabOrder: ["s1"],
activeTabId: "s1",
now: 123,
});
assert.equal(payload.sessions[0].serialConfig?.backspaceBehavior, undefined);
});
test("buildSessionRestorePayload preserves serial-only workspaces", () => {
const payload = buildSessionRestorePayload({
sessions: [{
...session("s1", "ws-1"),
username: "",
protocol: "serial",
serialConfig: {
path: "/dev/tty.usbserial",
baudRate: 115200,
},
}],
workspaces: [{
id: "ws-1",
title: "Serial workspace",
root: { id: "pane-1", type: "pane", sessionId: "s1" },
}],
tabOrder: ["ws-1"],
activeTabId: "ws-1",
now: 123,
});
assert.equal(payload.sessions.length, 1);
assert.equal(payload.workspaces.length, 1);
assert.equal(payload.activeTabId, "ws-1");
});
test("buildSessionRestorePayload drops startup commands from restored sessions", () => {
const payload = buildSessionRestorePayload({
sessions: [{
...session("s1"),
startupCommand: "rm -rf /tmp/example",
noAutoRun: false,
}],
workspaces: [],
tabOrder: ["s1"],
activeTabId: "s1",
now: 123,
});
assert.equal(payload.sessions[0].startupCommand, undefined);
assert.equal(payload.sessions[0].noAutoRun, undefined);
});
test("sanitizeSessionRestorePayload prunes invalid workspace panes and drops empty workspaces", () => {
const workspace: Workspace = {
id: "ws-1",
title: "Workspace",
root: {
id: "split-1",
type: "split",
direction: "vertical",
children: [
{ id: "pane-1", type: "pane", sessionId: "s1" },
{ id: "pane-2", type: "pane", sessionId: "missing" },
],
sizes: [0.25, 0.75],
},
focusedSessionId: "missing",
focusSessionOrder: ["missing", "s1"],
};
const sanitized = sanitizeSessionRestorePayload({
version: 1,
savedAt: 1,
activeTabId: "missing",
tabOrder: ["missing", "ws-1", "s1"],
sessions: [session("s1", "ws-1")],
workspaces: [workspace],
});
assert.equal(sanitized.sessions.length, 1);
assert.equal(sanitized.workspaces.length, 1);
assert.equal(sanitized.workspaces[0].root.type, "pane");
assert.equal(sanitized.workspaces[0].focusedSessionId, "s1");
assert.deepEqual(sanitized.workspaces[0].focusSessionOrder, ["s1"]);
assert.deepEqual(sanitized.tabOrder, ["ws-1"]);
assert.equal(sanitized.activeTabId, "ws-1");
});
test("sanitizeSessionRestorePayload preserves explicit workspace autoTitle across restore", () => {
const workspace: Workspace = {
id: "ws-1",
title: "Workspace",
// User explicitly named this workspace "Workspace"; must not be relabeled.
autoTitle: false,
root: { id: "pane-1", type: "pane", sessionId: "s1" },
focusedSessionId: "s1",
focusSessionOrder: ["s1"],
};
const sanitized = sanitizeSessionRestorePayload({
version: 1,
savedAt: 1,
activeTabId: "ws-1",
tabOrder: ["ws-1"],
sessions: [session("s1", "ws-1")],
workspaces: [workspace],
});
assert.equal(sanitized.workspaces.length, 1);
assert.equal(sanitized.workspaces[0].autoTitle, false);
});
test("generated workspace titles survive JSON restore only with a valid boolean flag", () => {
const workspace: Workspace = {
id: "ws-1",
title: "01/02",
autoTitle: false,
generatedTitle: true,
root: {
id: "split-1",
type: "split",
direction: "vertical",
children: [
{ id: "pane-1", type: "pane", sessionId: "s1" },
{ id: "pane-2", type: "pane", sessionId: "s2" },
],
},
focusedSessionId: "s1",
focusSessionOrder: ["s1", "s2"],
};
const payload = buildSessionRestorePayload({
sessions: [session("s1", "ws-1"), session("s2", "ws-1")],
workspaces: [workspace],
tabOrder: ["ws-1"],
activeTabId: "ws-1",
now: 123,
});
const restored = sanitizeSessionRestorePayload(JSON.parse(JSON.stringify(payload)));
assert.equal(restored.workspaces[0].title, "01/02");
assert.equal(restored.workspaces[0].autoTitle, false);
assert.equal(restored.workspaces[0].generatedTitle, true);
const malformed = sanitizeSessionRestorePayload({
...payload,
workspaces: [{ ...payload.workspaces[0], generatedTitle: "true" }],
});
assert.equal(malformed.workspaces[0].generatedTitle, undefined);
});
test("sanitizeSessionRestorePayload leaves legacy workspaces without an autoTitle flag", () => {
const workspace: Workspace = {
id: "ws-1",
title: "Workspace",
root: { id: "pane-1", type: "pane", sessionId: "s1" },
focusedSessionId: "s1",
focusSessionOrder: ["s1"],
};
const sanitized = sanitizeSessionRestorePayload({
version: 1,
savedAt: 1,
activeTabId: "ws-1",
tabOrder: ["ws-1"],
sessions: [session("s1", "ws-1")],
workspaces: [workspace],
});
assert.equal(sanitized.workspaces[0].autoTitle, undefined);
});
test("sanitizeSessionRestorePayload drops malformed session and workspace records", () => {
const sanitized = sanitizeSessionRestorePayload({
version: 1,
savedAt: 1,
activeTabId: "ws-1",
tabOrder: ["ws-1", "s1"],
sessions: [null, session("s1")],
workspaces: [{ id: "ws-1" }],
} as unknown);
assert.deepEqual(sanitized.sessions.map((item) => item.id), ["s1"]);
assert.deepEqual(sanitized.workspaces, []);
assert.deepEqual(sanitized.tabOrder, ["s1"]);
assert.equal(sanitized.activeTabId, "s1");
});
test("sanitizeSessionRestorePayload preserves local terminal start directory", () => {
const sanitized = sanitizeSessionRestorePayload({
version: 1,
savedAt: 1,
activeTabId: "s1",
tabOrder: ["s1"],
sessions: [{
...session("s1"),
protocol: "local",
localStartDir: "/Users/alice/project",
}],
workspaces: [],
});
assert.equal(sanitized.sessions[0].localStartDir, "/Users/alice/project");
});
test("sanitizeSessionRestorePayload enforces workspace session ownership", () => {
const workspace: Workspace = {
id: "ws-1",
title: "Workspace",
root: {
id: "split-1",
type: "split",
direction: "horizontal",
children: [
{ id: "pane-1", type: "pane", sessionId: "owned" },
{ id: "pane-2", type: "pane", sessionId: "standalone" },
{ id: "pane-3", type: "pane", sessionId: "other-workspace" },
],
sizes: [0.2, 0.3, 0.5],
},
focusedSessionId: "standalone",
focusSessionOrder: ["standalone", "owned", "other-workspace"],
};
const sanitized = sanitizeSessionRestorePayload({
version: 1,
savedAt: 1,
activeTabId: "ws-1",
tabOrder: ["ws-1", "standalone", "ws-2"],
sessions: [
session("owned", "ws-1"),
session("standalone"),
session("other-workspace", "ws-2"),
],
workspaces: [
workspace,
{
id: "ws-2",
title: "Other",
root: { id: "pane-4", type: "pane", sessionId: "other-workspace" },
},
],
});
assert.equal(sanitized.workspaces[0].root.type, "pane");
assert.equal(sanitized.workspaces[0].root.sessionId, "owned");
assert.equal(sanitized.workspaces[0].focusedSessionId, "owned");
assert.deepEqual(sanitized.workspaces[0].focusSessionOrder, ["owned"]);
assert.deepEqual(sanitized.tabOrder, ["ws-1", "standalone", "ws-2"]);
});
test("sanitizeSessionRestorePayload drops workspace sessions missing from the restored root", () => {
const sanitized = sanitizeSessionRestorePayload({
version: 1,
savedAt: 1,
activeTabId: "ws-1",
tabOrder: ["ws-1"],
sessions: [
session("s1", "ws-1"),
session("s2", "ws-1"),
],
workspaces: [{
id: "ws-1",
title: "Workspace",
root: { id: "pane-1", type: "pane", sessionId: "s1" },
focusedSessionId: "s2",
focusSessionOrder: ["s2", "s1"],
}],
});
assert.deepEqual(sanitized.sessions.map((entry) => entry.id), ["s1"]);
assert.equal(sanitized.workspaces[0].focusedSessionId, "s1");
assert.deepEqual(sanitized.workspaces[0].focusSessionOrder, ["s1"]);
assert.deepEqual(sanitized.tabOrder, ["ws-1"]);
});
test("sanitizeSessionRestorePayload allowlists workspace node fields", () => {
const sanitized = sanitizeSessionRestorePayload({
version: 1,
savedAt: 1,
activeTabId: "ws-1",
tabOrder: ["ws-1"],
sessions: [session("s1", "ws-1"), session("s2", "ws-1")],
workspaces: [{
id: "ws-1",
title: "Workspace",
root: {
id: "split-1",
type: "split",
direction: "horizontal",
terminalData: "do-not-store",
children: [
{ id: "pane-1", type: "pane", sessionId: "s1", terminalData: "do-not-store" },
{ id: "pane-2", type: "pane", sessionId: "s2", secret: "do-not-store" },
],
sizes: [0.4, 0.6],
},
}],
});
const root = sanitized.workspaces[0].root;
assert.equal("terminalData" in root, false);
assert.equal(root.type, "split");
assert.equal("terminalData" in root.children[0], false);
assert.equal("secret" in root.children[1], false);
});
test("sanitizeSessionRestorePayload deeply allowlists unknown serial config fields", () => {
const sanitized = sanitizeSessionRestorePayload({
version: 1,
savedAt: 1,
activeTabId: "s1",
tabOrder: ["s1"],
sessions: [{
...session("s1"),
protocol: "serial",
serialConfig: {
path: "/dev/tty.usbserial",
baudRate: 115200,
dataBits: 8,
stopBits: 1,
parity: "none",
flowControl: "none",
localEcho: true,
lineMode: true,
backspaceBehavior: "default",
secret: "do-not-store",
},
}],
workspaces: [],
});
assert.deepEqual(sanitized.sessions[0].serialConfig, {
path: "/dev/tty.usbserial",
baudRate: 115200,
dataBits: 8,
stopBits: 1,
parity: "none",
flowControl: "none",
localEcho: true,
lineMode: true,
backspaceBehavior: "default",
});
});
test("sanitizeSessionRestorePayload drops invalid protocol and shell type enum values", () => {
const sanitized = sanitizeSessionRestorePayload({
version: 1,
savedAt: 1,
activeTabId: "s1",
tabOrder: ["s1"],
sessions: [{
...session("s1"),
protocol: "ftp",
shellType: "bash",
}],
workspaces: [],
});
assert.equal(sanitized.sessions[0].protocol, undefined);
assert.equal(sanitized.sessions[0].shellType, undefined);
});
test("sanitizeSessionRestorePayload falls back to equal split sizes when saved sizes are non-positive", () => {
const sanitized = sanitizeSessionRestorePayload({
version: 1,
savedAt: 1,
activeTabId: "ws-1",
tabOrder: ["ws-1"],
sessions: [session("s1", "ws-1"), session("s2", "ws-1")],
workspaces: [{
id: "ws-1",
title: "Workspace",
root: {
id: "split-1",
type: "split",
direction: "horizontal",
children: [
{ id: "pane-1", type: "pane", sessionId: "s1" },
{ id: "pane-2", type: "pane", sessionId: "s2" },
],
sizes: [-1, 2],
},
}],
});
assert.equal(sanitized.workspaces[0].root.type, "split");
if (sanitized.workspaces[0].root.type === "split") {
assert.deepEqual(sanitized.workspaces[0].root.sizes, [0.5, 0.5]);
}
});
test("sanitizeSessionRestorePayload preserves split sizes when nested splits collapse", () => {
const sanitized = sanitizeSessionRestorePayload({
version: 1,
savedAt: 1,
activeTabId: "ws-1",
tabOrder: ["ws-1"],
sessions: [session("s1", "ws-1"), session("s3", "ws-1")],
workspaces: [{
id: "ws-1",
title: "Workspace",
root: {
id: "root-split",
type: "split",
direction: "horizontal",
sizes: [0.7, 0.3],
children: [
{
id: "left-split",
type: "split",
direction: "vertical",
sizes: [0.4, 0.6],
children: [
{ id: "pane-1", type: "pane", sessionId: "s1" },
{ id: "pane-missing", type: "pane", sessionId: "missing" },
],
},
{ id: "pane-3", type: "pane", sessionId: "s3" },
],
},
}],
});
const root = sanitized.workspaces[0].root;
assert.equal(root.type, "split");
if (root.type === "split") {
assert.deepEqual(root.children.map((child) => child.type === "pane" ? child.sessionId : "split"), ["s1", "s3"]);
assert.deepEqual(root.sizes, [0.7, 0.3]);
assert.ok(root.sizes?.every((size) => size > 0));
}
});
test("resolveRestoredActiveTabId falls back to vault when no restored tab is valid", () => {
assert.equal(resolveRestoredActiveTabId("missing", [], [], []), "vault");
});
test("resolveRestoredActiveTabId does not restore sftp as an active startup tab", () => {
assert.equal(resolveRestoredActiveTabId("sftp", ["sftp"], [], []), "vault");
});
test("isRestoredDisconnectedSession detects only explicit restored sessions", () => {
assert.equal(
isRestoredDisconnectedSession({
...session("s1"),
status: "disconnected",
restoreState: "restored-disconnected",
}),
true,
);
assert.equal(isRestoredDisconnectedSession({ ...session("s1"), status: "disconnected" }), false);
});
test("shouldAttemptRestoreCwd allows restored local and unix ssh sessions only when enabled", () => {
assert.equal(shouldAttemptRestoreCwd({
enabled: true,
session: { ...session("s1"), status: "disconnected", restoreState: "restored-disconnected", lastCwd: "/srv/app" },
isNetworkDevice: false,
}), true);
assert.equal(shouldAttemptRestoreCwd({
enabled: false,
session: { ...session("s1"), status: "disconnected", restoreState: "restored-disconnected", lastCwd: "/srv/app" },
isNetworkDevice: false,
}), false);
});
test("restored fresh SSH duplicates retain metadata without injecting the previous target directory", () => {
for (const protocol of ["ssh", undefined, "local"] as const) {
const payload = buildSessionRestorePayload({
sessions: [{ ...session("s1"), protocol, requireFreshConnection: true, lastCwd: "/srv/old-target" }],
workspaces: [], tabOrder: ["s1"], activeTabId: "s1",
});
const restored = sanitizeSessionRestorePayload(JSON.parse(JSON.stringify(payload))).sessions[0];
assert.equal(restored.requireFreshConnection, true);
assert.equal(restored.lastCwd, "/srv/old-target");
const intent = resolveRestoreCwdIntent({ enabled: true, session: restored, isNetworkDevice: false });
if (protocol === "local") assert.equal(intent?.command, "cd -- '/srv/old-target'");
else assert.equal(intent, null, "a fresh bastion login must await target selection");
}
});
test("shouldAttemptRestoreCwd skips ineligible protocols and network devices", () => {
const base = { ...session("s1"), status: "disconnected" as const, restoreState: "restored-disconnected" as const, lastCwd: "/srv/app" };
assert.equal(shouldAttemptRestoreCwd({ enabled: true, session: { ...base, protocol: "telnet" }, isNetworkDevice: false }), false);
assert.equal(shouldAttemptRestoreCwd({ enabled: true, session: { ...base, protocol: "serial" }, isNetworkDevice: false }), false);
assert.equal(shouldAttemptRestoreCwd({ enabled: true, session: { ...base, protocol: "ssh" }, isNetworkDevice: true }), false);
assert.equal(shouldAttemptRestoreCwd({ enabled: true, session: { ...base, protocol: "ssh", moshEnabled: true }, isNetworkDevice: false }), false);
assert.equal(shouldAttemptRestoreCwd({ enabled: true, session: { ...base, protocol: "ssh", etEnabled: true }, isNetworkDevice: false }), false);
});
test("shouldAttemptRestoreCwd skips local Windows-like shell types", () => {
const base = {
...session("s1"),
status: "disconnected" as const,
restoreState: "restored-disconnected" as const,
protocol: "local" as const,
lastCwd: "/srv/app",
};
assert.equal(shouldAttemptRestoreCwd({ enabled: true, session: { ...base, shellType: "posix" }, isNetworkDevice: false }), true);
assert.equal(shouldAttemptRestoreCwd({ enabled: true, session: { ...base, shellType: "fish" }, isNetworkDevice: false }), true);
assert.equal(shouldAttemptRestoreCwd({ enabled: true, session: { ...base, shellType: "powershell" }, isNetworkDevice: false }), false);
assert.equal(shouldAttemptRestoreCwd({ enabled: true, session: { ...base, shellType: "cmd" }, isNetworkDevice: false }), false);
});
test("shouldAttemptRestoreCwd skips missing and windows-like cwd values", () => {
const base = { ...session("s1"), status: "disconnected" as const, restoreState: "restored-disconnected" as const };
assert.equal(shouldAttemptRestoreCwd({ enabled: true, session: { ...base }, isNetworkDevice: false }), false);
assert.equal(shouldAttemptRestoreCwd({ enabled: true, session: { ...base, lastCwd: "C:\\Users\\alice" }, isNetworkDevice: false }), false);
assert.equal(shouldAttemptRestoreCwd({ enabled: true, session: { ...base, lastCwd: "relative/path" }, isNetworkDevice: false }), false);
assert.equal(shouldAttemptRestoreCwd({ enabled: true, session: { ...base, lastCwd: "~alice/project" }, isNetworkDevice: false }), false);
});
test("quoteRestoreCwdForShell treats cwd as shell data", () => {
assert.equal(quoteRestoreCwdForShell("/srv/app"), "'/srv/app'");
assert.equal(quoteRestoreCwdForShell("/srv/app dir"), "'/srv/app dir'");
assert.equal(quoteRestoreCwdForShell("/tmp/it's ok; $(rm -rf ~)"), "'/tmp/it'\\''s ok; $(rm -rf ~)'");
});
test("resolveInteractiveTerminalCdIntent quotes path-only cd without session transport checks", () => {
assert.deepEqual(resolveInteractiveTerminalCdIntent("/srv/app dir"), {
cwd: "/srv/app dir",
command: "cd -- '/srv/app dir'",
});
assert.deepEqual(resolveInteractiveTerminalCdIntent("/srv/project "), {
cwd: "/srv/project ",
command: "cd -- '/srv/project '",
});
assert.equal(resolveInteractiveTerminalCdIntent("C:\\Users\\alice"), null);
});
test("resolveInteractiveTerminalCdIntent rejects control bytes that readline would interpret", () => {
// Tab / ESC / Ctrl-U / newline / DEL must not be typed into an interactive PTY.
assert.equal(resolveInteractiveTerminalCdIntent("/srv/app\tdir"), null);
assert.equal(resolveInteractiveTerminalCdIntent("/srv/app\u001bdir"), null);
assert.equal(resolveInteractiveTerminalCdIntent("/srv/app\u0015dir"), null);
assert.equal(resolveInteractiveTerminalCdIntent("/srv/app\ndir"), null);
assert.equal(resolveInteractiveTerminalCdIntent("/srv/app\u007fdir"), null);
// Trailing spaces remain valid POSIX names and must still quote exactly.
assert.deepEqual(resolveInteractiveTerminalCdIntent("/srv/project "), {
cwd: "/srv/project ",
command: "cd -- '/srv/project '",
});
});
test("resolveRestoreCwdIntent captures a one-shot restore command", () => {
assert.deepEqual(resolveRestoreCwdIntent({
enabled: true,
session: { ...session("s1"), status: "disconnected", restoreState: "restored-disconnected", lastCwd: "/srv/app dir" },
isNetworkDevice: false,
}), {
cwd: "/srv/app dir",
command: "cd -- '/srv/app dir'",
});
});
test("resolveRestoreCwdIntent does not emit POSIX cd for local Windows-like shells", () => {
assert.equal(resolveRestoreCwdIntent({
enabled: true,
session: {
...session("s1"),
status: "disconnected",
restoreState: "restored-disconnected",
protocol: "local",
shellType: "powershell",
lastCwd: "/srv/app",
},
isNetworkDevice: false,
}), null);
});
test("resolveRestoreCwdIntent keeps home-relative cwd expandable", () => {
assert.deepEqual(resolveRestoreCwdIntent({
enabled: true,
session: { ...session("s1"), status: "disconnected", restoreState: "restored-disconnected", lastCwd: "~/project dir" },
isNetworkDevice: false,
}), {
cwd: "~/project dir",
command: "cd -- ~/'project dir'",
});
assert.deepEqual(resolveRestoreCwdIntent({
enabled: true,
session: { ...session("s1"), status: "disconnected", restoreState: "restored-disconnected", lastCwd: "~" },
isNetworkDevice: false,
}), {
cwd: "~",
command: "cd -- ~",
});
});
test("resolveInheritedCwdIntent: ssh clone gets a cd command", () => {
const intent = resolveInheritedCwdIntent({
session: { protocol: "ssh", shellType: "posix", cwd: "/var/log" },
isNetworkDevice: false,
});
assert.deepEqual(intent, { cwd: "/var/log", command: "cd -- '/var/log'" });
});
test("resolveInheritedCwdIntent: fires without restoreState or enabled flag", () => {
const intent = resolveInheritedCwdIntent({
session: { protocol: "ssh", cwd: "/home/me/proj" },
isNetworkDevice: false,
});
assert.equal(intent?.command, "cd -- '/home/me/proj'");
});
test("resolveInheritedCwdIntent: skips mosh/et and network devices and bad paths", () => {
assert.equal(resolveInheritedCwdIntent({ session: { protocol: "ssh", etEnabled: true, cwd: "/x" }, isNetworkDevice: false }), null);
assert.equal(resolveInheritedCwdIntent({ session: { protocol: "ssh", cwd: "/x" }, isNetworkDevice: true }), null);
assert.equal(resolveInheritedCwdIntent({ session: { protocol: "ssh", cwd: "C:\\Users" }, isNetworkDevice: false }), null);
assert.equal(resolveInheritedCwdIntent({ session: { protocol: "ssh", cwd: " " }, isNetworkDevice: false }), null);
});
test("resolveInheritedCwdIntent: skips windows local shells", () => {
assert.equal(resolveInheritedCwdIntent({ session: { protocol: "local", shellType: "powershell", cwd: "/x" }, isNetworkDevice: false }), null);
});