[Init] Initial commit - NetMesh terminal manager
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
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
This commit is contained in:
490
domain/terminalHostResolution.test.ts
Normal file
490
domain/terminalHostResolution.test.ts
Normal file
@@ -0,0 +1,490 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
import type { GroupConfig, Host, ProxyProfile, TerminalSession } from "./models";
|
||||
import {
|
||||
resolveTerminalChainHosts,
|
||||
resolveTerminalSessionHost,
|
||||
} from "./terminalHostResolution";
|
||||
|
||||
const baseSession: TerminalSession = {
|
||||
id: "session-1",
|
||||
hostId: "target",
|
||||
hostLabel: "Target",
|
||||
hostname: "target.example.test",
|
||||
username: "alice",
|
||||
port: 22,
|
||||
protocol: "ssh",
|
||||
status: "connected",
|
||||
createdAt: 1,
|
||||
};
|
||||
|
||||
const proxyProfiles: ProxyProfile[] = [{
|
||||
id: "proxy-1",
|
||||
label: "Office proxy",
|
||||
config: {
|
||||
type: "http",
|
||||
host: "proxy.example.test",
|
||||
port: 3128,
|
||||
username: "proxy-user",
|
||||
},
|
||||
createdAt: 1,
|
||||
}];
|
||||
|
||||
test("resolveTerminalSessionHost materializes a saved proxy profile for popup terminals", () => {
|
||||
const host: Host = {
|
||||
id: "target",
|
||||
label: "Target",
|
||||
hostname: "target.example.test",
|
||||
username: "alice",
|
||||
port: 22,
|
||||
protocol: "ssh",
|
||||
tags: [],
|
||||
os: "linux",
|
||||
proxyProfileId: "proxy-1",
|
||||
};
|
||||
|
||||
const resolved = resolveTerminalSessionHost({
|
||||
session: baseSession,
|
||||
hosts: [host],
|
||||
groupConfigs: [],
|
||||
proxyProfiles,
|
||||
localOs: "linux",
|
||||
});
|
||||
|
||||
assert.equal(resolved.proxyProfileId, "proxy-1");
|
||||
assert.deepEqual(resolved.proxyConfig, proxyProfiles[0].config);
|
||||
});
|
||||
|
||||
test("resolveTerminalSessionHost applies group default proxy profiles before opening popup terminals", () => {
|
||||
const host: Host = {
|
||||
id: "target",
|
||||
label: "Target",
|
||||
hostname: "target.example.test",
|
||||
username: "alice",
|
||||
port: 22,
|
||||
protocol: "ssh",
|
||||
tags: [],
|
||||
os: "linux",
|
||||
group: "prod/web",
|
||||
};
|
||||
const groupConfigs: GroupConfig[] = [
|
||||
{ path: "prod", proxyProfileId: "proxy-1" },
|
||||
];
|
||||
|
||||
const resolved = resolveTerminalSessionHost({
|
||||
session: baseSession,
|
||||
hosts: [host],
|
||||
groupConfigs,
|
||||
proxyProfiles,
|
||||
localOs: "linux",
|
||||
});
|
||||
|
||||
assert.equal(resolved.proxyProfileId, "proxy-1");
|
||||
assert.deepEqual(resolved.proxyConfig, proxyProfiles[0].config);
|
||||
});
|
||||
|
||||
test("resolveTerminalSessionHost defaults missing saved remote sessions to SSH", () => {
|
||||
const resolved = resolveTerminalSessionHost({
|
||||
session: {
|
||||
...baseSession,
|
||||
protocol: undefined,
|
||||
},
|
||||
hosts: [],
|
||||
groupConfigs: [],
|
||||
proxyProfiles,
|
||||
localOs: "macos",
|
||||
});
|
||||
|
||||
assert.equal(resolved.protocol, "ssh");
|
||||
assert.equal(resolved.hostname, "target.example.test");
|
||||
assert.equal(resolved.username, "alice");
|
||||
assert.equal(resolved.os, "linux");
|
||||
});
|
||||
|
||||
test("resolveTerminalSessionHost keeps explicit missing local sessions local", () => {
|
||||
const resolved = resolveTerminalSessionHost({
|
||||
session: {
|
||||
...baseSession,
|
||||
protocol: "local",
|
||||
hostname: "localhost",
|
||||
username: "local",
|
||||
},
|
||||
hosts: [],
|
||||
groupConfigs: [],
|
||||
proxyProfiles,
|
||||
localOs: "macos",
|
||||
});
|
||||
|
||||
assert.equal(resolved.protocol, "local");
|
||||
assert.equal(resolved.os, "macos");
|
||||
});
|
||||
|
||||
test("resolveTerminalSessionHost restores a missing plugin host from the session snapshot", () => {
|
||||
const providerId = "com.example.transport.connection";
|
||||
const pluginConnection = {
|
||||
providerId,
|
||||
configuration: { endpoint: "saved.example", mode: "opaque" },
|
||||
};
|
||||
const resolved = resolveTerminalSessionHost({
|
||||
session: {
|
||||
...baseSession,
|
||||
hostId: "missing-plugin-host",
|
||||
protocol: `plugin:${providerId}`,
|
||||
pluginConnection,
|
||||
},
|
||||
hosts: [],
|
||||
groupConfigs: [],
|
||||
proxyProfiles,
|
||||
localOs: "macos",
|
||||
});
|
||||
|
||||
assert.equal(resolved.protocol, `plugin:${providerId}`);
|
||||
assert.deepEqual(resolved.pluginConnection, pluginConnection);
|
||||
});
|
||||
|
||||
test("resolveTerminalSessionHost keeps the session plugin configuration when the Vault host changes", () => {
|
||||
const providerId = "com.example.transport.connection";
|
||||
const protocol = `plugin:${providerId}` as const;
|
||||
const resolved = resolveTerminalSessionHost({
|
||||
session: {
|
||||
...baseSession,
|
||||
protocol,
|
||||
pluginConnection: { providerId, configuration: { endpoint: "session.example" } },
|
||||
},
|
||||
hosts: [{
|
||||
id: "target",
|
||||
label: "Target",
|
||||
hostname: providerId,
|
||||
username: "",
|
||||
tags: [],
|
||||
os: "linux",
|
||||
protocol,
|
||||
pluginConnection: { providerId, configuration: { endpoint: "vault.example" } },
|
||||
}],
|
||||
groupConfigs: [],
|
||||
proxyProfiles,
|
||||
localOs: "linux",
|
||||
});
|
||||
|
||||
assert.deepEqual(resolved.pluginConnection, {
|
||||
providerId,
|
||||
configuration: { endpoint: "session.example" },
|
||||
});
|
||||
});
|
||||
|
||||
test("resolveTerminalSessionHost carries local session start directory into fallback host", () => {
|
||||
const resolved = resolveTerminalSessionHost({
|
||||
session: {
|
||||
...baseSession,
|
||||
protocol: "local",
|
||||
hostname: "localhost",
|
||||
username: "local",
|
||||
localStartDir: "/Users/alice/project",
|
||||
},
|
||||
hosts: [],
|
||||
groupConfigs: [],
|
||||
proxyProfiles,
|
||||
localOs: "macos",
|
||||
});
|
||||
|
||||
assert.equal(resolved.protocol, "local");
|
||||
assert.equal(resolved.localStartDir, "/Users/alice/project");
|
||||
});
|
||||
|
||||
test("resolveTerminalSessionHost carries serial Ctrl-H backspace behavior into quick sessions", () => {
|
||||
const resolved = resolveTerminalSessionHost({
|
||||
session: {
|
||||
...baseSession,
|
||||
hostId: "serial-session-1",
|
||||
hostLabel: "Serial: COM3",
|
||||
hostname: "COM3",
|
||||
username: "",
|
||||
protocol: "serial",
|
||||
serialConfig: {
|
||||
path: "COM3",
|
||||
baudRate: 115200,
|
||||
backspaceBehavior: "ctrl-h",
|
||||
},
|
||||
},
|
||||
hosts: [],
|
||||
groupConfigs: [],
|
||||
proxyProfiles,
|
||||
localOs: "windows",
|
||||
});
|
||||
|
||||
assert.equal(resolved.protocol, "serial");
|
||||
assert.equal(resolved.backspaceBehavior, "ctrl-h");
|
||||
});
|
||||
|
||||
test("resolveTerminalSessionHost applies serial Ctrl-H backspace behavior from saved hosts", () => {
|
||||
const host: Host = {
|
||||
id: "target",
|
||||
label: "Serial: COM3",
|
||||
hostname: "COM3",
|
||||
username: "",
|
||||
port: 115200,
|
||||
protocol: "serial",
|
||||
tags: [],
|
||||
os: "linux",
|
||||
serialConfig: {
|
||||
path: "COM3",
|
||||
baudRate: 115200,
|
||||
backspaceBehavior: "ctrl-h",
|
||||
},
|
||||
};
|
||||
|
||||
const resolved = resolveTerminalSessionHost({
|
||||
session: {
|
||||
...baseSession,
|
||||
hostLabel: "Serial: COM3",
|
||||
hostname: "COM3",
|
||||
username: "",
|
||||
port: undefined,
|
||||
protocol: "serial",
|
||||
},
|
||||
hosts: [host],
|
||||
groupConfigs: [],
|
||||
proxyProfiles,
|
||||
localOs: "windows",
|
||||
});
|
||||
|
||||
assert.equal(resolved.protocol, "serial");
|
||||
assert.equal(resolved.backspaceBehavior, "ctrl-h");
|
||||
});
|
||||
|
||||
test("resolveTerminalSessionHost preserves inherited Ctrl-H for legacy restored serial sessions", () => {
|
||||
const host: Host = {
|
||||
id: "target",
|
||||
label: "Serial: COM3",
|
||||
hostname: "COM3",
|
||||
username: "",
|
||||
port: 115200,
|
||||
protocol: "serial",
|
||||
tags: [],
|
||||
os: "linux",
|
||||
group: "serial-devices",
|
||||
serialConfig: {
|
||||
path: "COM3",
|
||||
baudRate: 115200,
|
||||
},
|
||||
};
|
||||
|
||||
const resolved = resolveTerminalSessionHost({
|
||||
session: {
|
||||
...baseSession,
|
||||
hostLabel: "Serial: COM3",
|
||||
hostname: "COM3",
|
||||
username: "",
|
||||
port: undefined,
|
||||
protocol: "serial",
|
||||
serialConfig: {
|
||||
path: "COM3",
|
||||
baudRate: 115200,
|
||||
},
|
||||
},
|
||||
hosts: [host],
|
||||
groupConfigs: [{ path: "serial-devices", backspaceBehavior: "ctrl-h" }],
|
||||
proxyProfiles,
|
||||
localOs: "windows",
|
||||
});
|
||||
|
||||
assert.equal(resolved.backspaceBehavior, "ctrl-h");
|
||||
});
|
||||
|
||||
test("resolveTerminalSessionHost keeps the serial Backspace behavior captured by the session", () => {
|
||||
const host: Host = {
|
||||
id: "target",
|
||||
label: "Serial: COM3",
|
||||
hostname: "COM3",
|
||||
username: "",
|
||||
port: 115200,
|
||||
protocol: "serial",
|
||||
tags: [],
|
||||
os: "linux",
|
||||
serialConfig: {
|
||||
path: "COM3",
|
||||
baudRate: 115200,
|
||||
backspaceBehavior: "default",
|
||||
},
|
||||
};
|
||||
|
||||
const ctrlHSession = resolveTerminalSessionHost({
|
||||
session: {
|
||||
...baseSession,
|
||||
hostname: "COM3",
|
||||
username: "",
|
||||
protocol: "serial",
|
||||
serialConfig: {
|
||||
path: "COM3",
|
||||
baudRate: 115200,
|
||||
backspaceBehavior: "ctrl-h",
|
||||
},
|
||||
},
|
||||
hosts: [host],
|
||||
groupConfigs: [],
|
||||
proxyProfiles,
|
||||
localOs: "windows",
|
||||
});
|
||||
|
||||
const defaultSession = resolveTerminalSessionHost({
|
||||
session: {
|
||||
...baseSession,
|
||||
hostname: "COM3",
|
||||
username: "",
|
||||
protocol: "serial",
|
||||
serialConfig: {
|
||||
path: "COM3",
|
||||
baudRate: 115200,
|
||||
backspaceBehavior: "default",
|
||||
},
|
||||
},
|
||||
hosts: [{
|
||||
...host,
|
||||
serialConfig: {
|
||||
...host.serialConfig!,
|
||||
backspaceBehavior: "ctrl-h",
|
||||
},
|
||||
}],
|
||||
groupConfigs: [],
|
||||
proxyProfiles,
|
||||
localOs: "windows",
|
||||
});
|
||||
|
||||
assert.equal(ctrlHSession.backspaceBehavior, "ctrl-h");
|
||||
assert.equal(defaultSession.backspaceBehavior, undefined);
|
||||
});
|
||||
|
||||
test("resolveTerminalSessionHost suppresses inherited network device mode for Mosh sessions", () => {
|
||||
const host: Host = {
|
||||
id: "target",
|
||||
label: "Target",
|
||||
hostname: "target.example.test",
|
||||
username: "alice",
|
||||
port: 22,
|
||||
protocol: "ssh",
|
||||
tags: [],
|
||||
os: "linux",
|
||||
group: "prod/web",
|
||||
};
|
||||
const groupConfigs: GroupConfig[] = [
|
||||
{ path: "prod", deviceType: "network" },
|
||||
{ path: "prod/web", moshEnabled: true },
|
||||
];
|
||||
|
||||
const resolved = resolveTerminalSessionHost({
|
||||
session: baseSession,
|
||||
hosts: [host],
|
||||
groupConfigs,
|
||||
proxyProfiles,
|
||||
localOs: "linux",
|
||||
});
|
||||
|
||||
assert.equal(resolved.moshEnabled, true);
|
||||
assert.equal(resolved.deviceType, undefined);
|
||||
});
|
||||
|
||||
test("resolveTerminalSessionHost suppresses explicit network device mode for ET session overrides", () => {
|
||||
const host: Host = {
|
||||
id: "target",
|
||||
label: "Target",
|
||||
hostname: "target.example.test",
|
||||
username: "alice",
|
||||
port: 22,
|
||||
protocol: "ssh",
|
||||
tags: [],
|
||||
os: "linux",
|
||||
deviceType: "network",
|
||||
};
|
||||
|
||||
const resolved = resolveTerminalSessionHost({
|
||||
session: {
|
||||
...baseSession,
|
||||
etEnabled: true,
|
||||
},
|
||||
hosts: [host],
|
||||
groupConfigs: [],
|
||||
proxyProfiles,
|
||||
localOs: "linux",
|
||||
});
|
||||
|
||||
assert.equal(resolved.etEnabled, true);
|
||||
assert.equal(resolved.deviceType, undefined);
|
||||
assert.equal(host.deviceType, "network");
|
||||
});
|
||||
|
||||
test("resolveTerminalChainHosts materializes proxy profiles on jump hosts", () => {
|
||||
const target: Host = {
|
||||
id: "target",
|
||||
label: "Target",
|
||||
hostname: "target.example.test",
|
||||
username: "alice",
|
||||
port: 22,
|
||||
protocol: "ssh",
|
||||
tags: [],
|
||||
os: "linux",
|
||||
hostChain: { hostIds: ["jump-1"] },
|
||||
};
|
||||
const jumpHost: Host = {
|
||||
id: "jump-1",
|
||||
label: "Jump",
|
||||
hostname: "jump.example.test",
|
||||
username: "jump",
|
||||
port: 22,
|
||||
protocol: "ssh",
|
||||
tags: [],
|
||||
os: "linux",
|
||||
proxyProfileId: "proxy-1",
|
||||
};
|
||||
|
||||
const resolved = resolveTerminalChainHosts({
|
||||
host: target,
|
||||
hosts: [target, jumpHost],
|
||||
groupConfigs: [],
|
||||
proxyProfiles,
|
||||
});
|
||||
|
||||
assert.equal(resolved.length, 1);
|
||||
assert.equal(resolved[0]?.id, "jump-1");
|
||||
assert.deepEqual(resolved[0]?.proxyConfig, proxyProfiles[0].config);
|
||||
});
|
||||
|
||||
test("resolveTerminalChainHosts applies group default proxy profiles to jump hosts", () => {
|
||||
const target: Host = {
|
||||
id: "target",
|
||||
label: "Target",
|
||||
hostname: "target.example.test",
|
||||
username: "alice",
|
||||
port: 22,
|
||||
protocol: "ssh",
|
||||
tags: [],
|
||||
os: "linux",
|
||||
hostChain: { hostIds: ["jump-1"] },
|
||||
};
|
||||
const jumpHost: Host = {
|
||||
id: "jump-1",
|
||||
label: "Jump",
|
||||
hostname: "jump.example.test",
|
||||
username: "jump",
|
||||
port: 22,
|
||||
protocol: "ssh",
|
||||
tags: [],
|
||||
os: "linux",
|
||||
group: "bastion",
|
||||
};
|
||||
const groupConfigs: GroupConfig[] = [
|
||||
{ path: "bastion", proxyProfileId: "proxy-1" },
|
||||
];
|
||||
|
||||
const resolved = resolveTerminalChainHosts({
|
||||
host: target,
|
||||
hosts: [target, jumpHost],
|
||||
groupConfigs,
|
||||
proxyProfiles,
|
||||
});
|
||||
|
||||
assert.equal(resolved.length, 1);
|
||||
assert.equal(resolved[0]?.id, "jump-1");
|
||||
assert.equal(resolved[0]?.proxyProfileId, "proxy-1");
|
||||
assert.deepEqual(resolved[0]?.proxyConfig, proxyProfiles[0].config);
|
||||
});
|
||||
Reference in New Issue
Block a user