Files
NetMesh/application/state/terminalConnectionReuse.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

227 lines
9.0 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
import type { TerminalSession } from "../../domain/models";
import {
canReuseTerminalConnection,
createCopiedTerminalSessionClone,
createSplitTerminalSessionClone,
isTerminalSessionEligibleForSftpReuse,
resolveSftpReuseSourceSessionId,
} from "./terminalConnectionReuse";
const session = (overrides: Partial<TerminalSession> = {}): TerminalSession => ({
id: "session-1",
hostId: "host-1",
hostLabel: "Host",
hostname: "example.com",
username: "alice",
status: "connected",
protocol: "ssh",
...overrides,
});
test("connected SSH sessions can reuse their authenticated connection", () => {
assert.equal(canReuseTerminalConnection(session()), true);
assert.equal(canReuseTerminalConnection(session({ protocol: undefined })), true);
});
test("SSH sessions stay SFTP-linkable while reconnecting", () => {
assert.equal(isTerminalSessionEligibleForSftpReuse(session({ status: "connecting" })), true);
assert.equal(isTerminalSessionEligibleForSftpReuse(session({ status: "disconnected" })), true);
assert.equal(isTerminalSessionEligibleForSftpReuse(session({ protocol: "local" })), false);
assert.equal(isTerminalSessionEligibleForSftpReuse(session({ moshEnabled: true })), false);
assert.equal(isTerminalSessionEligibleForSftpReuse(session({ etEnabled: true })), false);
});
test("Mosh and ET drops keep their origin but do not request SSH session reuse", () => {
assert.equal(resolveSftpReuseSourceSessionId(session(), "session-1"), "session-1");
assert.equal(
resolveSftpReuseSourceSessionId(session({ moshEnabled: true }), "session-1"),
undefined,
);
assert.equal(
resolveSftpReuseSourceSessionId(session({ etEnabled: true }), "session-1"),
undefined,
);
});
test("non-SSH or unavailable sessions do not reuse a connection", () => {
assert.equal(canReuseTerminalConnection(session({ status: "connecting" })), false);
assert.equal(canReuseTerminalConnection(session({ status: "disconnected" })), false);
assert.equal(canReuseTerminalConnection(session({ protocol: "local" })), false);
assert.equal(canReuseTerminalConnection(session({ protocol: "serial" })), false);
assert.equal(canReuseTerminalConnection(session({ protocol: "telnet" })), false);
assert.equal(canReuseTerminalConnection(session({ moshEnabled: true })), false);
assert.equal(canReuseTerminalConnection(session({ etEnabled: true })), false);
});
test("split session clones reuse only connected SSH sources", () => {
assert.equal(
createSplitTerminalSessionClone(session(), { id: "split-1", workspaceId: "workspace-1" }).reuseConnectionFromSessionId,
"session-1",
);
assert.equal(
createSplitTerminalSessionClone(session({ etEnabled: true }), { id: "split-2" }).reuseConnectionFromSessionId,
undefined,
);
assert.equal(
createSplitTerminalSessionClone(session({ moshEnabled: true }), { id: "split-3" }).reuseConnectionFromSessionId,
undefined,
);
});
test("session clones preserve the ephemeral-host marker", () => {
assert.equal(
createSplitTerminalSessionClone(session({ ephemeralHost: true }), { id: "split-1" }).ephemeralHost,
true,
);
assert.equal(
createCopiedTerminalSessionClone(session({ ephemeralHost: true }), { id: "copy-1" }).ephemeralHost,
true,
);
assert.equal(
createSplitTerminalSessionClone(session(), { id: "split-2" }).ephemeralHost,
undefined,
);
});
test("copy session clones reuse SSH sources and preserve serial config", () => {
const copied = createCopiedTerminalSessionClone(
session({
serialConfig: { path: "/dev/tty.usbserial", baudRate: 115200 },
}),
{ id: "copy-1" },
);
assert.equal(copied.reuseConnectionFromSessionId, "session-1");
assert.deepEqual(copied.serialConfig, { path: "/dev/tty.usbserial", baudRate: 115200 });
});
test("copy session clones open a fresh connection when reuseConnection is false", () => {
const fresh = createCopiedTerminalSessionClone(session(), { id: "copy-fresh", reuseConnection: false });
assert.equal(fresh.reuseConnectionFromSessionId, undefined);
// The explicit fresh flag must survive: without it the bridge treats the
// clone as eligible for general endpoint reuse and can still borrow the
// source's live authenticated transport.
assert.equal(fresh.requireFreshConnection, true);
// Local sources never had reuse to drop — behavior is unchanged.
assert.equal(
createCopiedTerminalSessionClone(session({ protocol: "local" }), { id: "copy-local-fresh", reuseConnection: false }).reuseConnectionFromSessionId,
undefined,
);
});
test("copy and split clones default to no fresh-connection requirement", () => {
assert.equal(createCopiedTerminalSessionClone(session(), { id: "copy-default" }).requireFreshConnection, undefined);
assert.equal(createSplitTerminalSessionClone(session(), { id: "split-default" }).requireFreshConnection, undefined);
assert.equal(
createCopiedTerminalSessionClone(session(), { id: "copy-reuse", reuseConnection: true }).requireFreshConnection,
undefined,
);
});
test("fresh SSH clones discard inherited directories even when a caller supplies one", () => {
for (const protocol of ["ssh", undefined] as const) {
const clone = createCopiedTerminalSessionClone(session({ protocol }), {
id: "fresh-no-cwd", reuseConnection: false, inheritedCwd: "/srv/old-target",
});
assert.equal(clone.pendingInitialCwd, undefined);
assert.equal(clone.requireFreshConnection, true);
}
const localClone = createCopiedTerminalSessionClone(session({ protocol: "local" }), {
id: "local-cwd", reuseConnection: false, inheritedCwd: "/home/alice/project",
});
assert.equal(localClone.localStartDir, "/home/alice/project");
});
test("copy session clones default to connection reuse when reuseConnection is unset", () => {
assert.equal(
createCopiedTerminalSessionClone(session(), { id: "copy-default" }).reuseConnectionFromSessionId,
"session-1",
);
});
test("split and copy session clones preserve local start directory", () => {
const source = session({
protocol: "local",
localStartDir: "/Users/alice/project with spaces ",
});
assert.equal(
createSplitTerminalSessionClone(source, { id: "split-local" }).localStartDir,
"/Users/alice/project with spaces ",
);
assert.equal(
createCopiedTerminalSessionClone(source, { id: "copy-local" }).localStartDir,
"/Users/alice/project with spaces ",
);
});
test("split clone applies remote inheritedCwd as pendingInitialCwd without touching localStartDir", () => {
const clone = createSplitTerminalSessionClone(session({ protocol: "ssh" }), {
id: "split-remote-cwd",
inheritedCwd: "/var/log",
});
assert.equal(clone.pendingInitialCwd, "/var/log");
assert.equal(clone.localStartDir, undefined);
});
test("split clone applies local inheritedCwd as localStartDir without pendingInitialCwd", () => {
const clone = createSplitTerminalSessionClone(
session({ protocol: "local", localStartDir: "/home/u", status: "connecting" }),
{ id: "split-local-cwd", localShellType: "posix", inheritedCwd: "/tmp/work" },
);
assert.equal(clone.localStartDir, "/tmp/work");
assert.equal(clone.pendingInitialCwd, undefined);
});
test("clone does not set pendingInitialCwd for protocols that never inject a cd", () => {
for (const overrides of [
{ protocol: "telnet" as const },
{ protocol: "serial" as const },
{ protocol: "ssh" as const, moshEnabled: true },
{ protocol: "ssh" as const, etEnabled: true },
]) {
const clone = createSplitTerminalSessionClone(session(overrides), {
id: "split-no-inject",
inheritedCwd: "/var/log",
});
assert.equal(clone.pendingInitialCwd, undefined, `${JSON.stringify(overrides)} should not set pendingInitialCwd`);
}
});
test("copy clone without inheritedCwd keeps localStartDir and sets no pendingInitialCwd", () => {
const clone = createCopiedTerminalSessionClone(
session({ protocol: "local", localStartDir: "/home/u", status: "connecting" }),
{ id: "copy-no-cwd", localShellType: "posix" },
);
assert.equal(clone.localStartDir, "/home/u");
assert.equal(clone.pendingInitialCwd, undefined);
});
test("split and copy session clones preserve isolated plugin connection snapshots", () => {
const pluginConnection = {
providerId: "com.example.transport.connection",
configuration: { endpoint: "gateway.example", options: ["fast"] },
credentialId: "credential-reference-1234",
};
const source = session({
protocol: `plugin:${pluginConnection.providerId}`,
pluginConnection,
});
const split = createSplitTerminalSessionClone(source, { id: "split-plugin" });
const copied = createCopiedTerminalSessionClone(source, { id: "copy-plugin" });
assert.deepEqual(split.pluginConnection, pluginConnection);
assert.deepEqual(copied.pluginConnection, pluginConnection);
assert.notEqual(split.pluginConnection, pluginConnection);
assert.notEqual(copied.pluginConnection, pluginConnection);
assert.equal(split.reuseConnectionFromSessionId, undefined);
assert.equal(copied.reuseConnectionFromSessionId, undefined);
});