Files
NetMesh/domain/sftpConnectedHosts.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

381 lines
12 KiB
TypeScript

import assert from "node:assert/strict";
import test from "node:test";
import type { Host, TerminalSession } from "./models";
import {
listSftpConnectedHosts,
resolveSftpTransferSourceSessionId,
sftpHostEndpointsEqual,
sftpPickerSessionsEqual,
sftpSourceSessionIdForHost,
} from "./sftpConnectedHosts";
const host = (overrides: Partial<Host> & Pick<Host, "id" | "label">): Host => ({
hostname: `${overrides.id}.example.test`,
username: "alice",
port: 22,
protocol: "ssh",
tags: [],
os: "linux",
...overrides,
});
const session = (
overrides: Partial<TerminalSession> & Pick<TerminalSession, "id" | "hostId" | "status">,
): TerminalSession => ({
hostLabel: overrides.hostId,
username: "alice",
hostname: `${overrides.hostId}.example.test`,
protocol: "ssh",
...overrides,
});
test("listSftpConnectedHosts returns connected SSH hosts sorted by label", () => {
const hosts = [
host({ id: "b", label: "Bravo" }),
host({ id: "a", label: "Alpha" }),
];
const hostsById = new Map(hosts.map((h) => [h.id, h]));
const sessions = [
session({ id: "s-b", hostId: "b", status: "connected" }),
session({ id: "s-a", hostId: "a", status: "connected" }),
];
const result = listSftpConnectedHosts(sessions, hostsById);
assert.deepEqual(
result.map((entry) => [entry.host.id, entry.sessionId, entry.status]),
[
["a", "s-a", "connected"],
["b", "s-b", "connected"],
],
);
});
test("listSftpConnectedHosts prefers the later reusable session for a host", () => {
const hostsById = new Map([["a", host({ id: "a", label: "Alpha" })]]);
const sessions = [
session({ id: "s-old", hostId: "a", status: "connected" }),
session({ id: "s-new", hostId: "a", status: "connected" }),
];
const result = listSftpConnectedHosts(sessions, hostsById);
assert.equal(result.length, 1);
assert.equal(result[0]?.sessionId, "s-new");
});
test("listSftpConnectedHosts skips connecting sessions", () => {
const hostsById = new Map([["a", host({ id: "a", label: "Alpha" })]]);
const sessions = [
session({ id: "s-connecting", hostId: "a", status: "connecting" }),
];
assert.deepEqual(listSftpConnectedHosts(sessions, hostsById), []);
});
test("listSftpConnectedHosts skips mosh and et sessions", () => {
const hosts = [
host({ id: "ssh", label: "SSH" }),
host({ id: "mosh", label: "Mosh" }),
host({ id: "et", label: "ET" }),
];
const hostsById = new Map(hosts.map((h) => [h.id, h]));
const sessions = [
session({ id: "s-ssh", hostId: "ssh", status: "connected" }),
session({ id: "s-mosh", hostId: "mosh", status: "connected", moshEnabled: true }),
session({ id: "s-et", hostId: "et", status: "connected", etEnabled: true }),
];
const result = listSftpConnectedHosts(sessions, hostsById);
assert.deepEqual(
result.map((entry) => entry.sessionId),
["s-ssh"],
);
});
test("listSftpConnectedHosts keeps plain SSH sessions even when vault host defaults to mosh/et", () => {
const hostsById = new Map([
["a", host({ id: "a", label: "Alpha", moshEnabled: true, etEnabled: true })],
]);
const sessions = [
session({ id: "s-ssh-deeplink", hostId: "a", status: "connected", moshEnabled: false, etEnabled: false }),
];
const result = listSftpConnectedHosts(sessions, hostsById);
assert.equal(result.length, 1);
assert.equal(result[0]?.sessionId, "s-ssh-deeplink");
});
test("listSftpConnectedHosts includes hosts with SFTP sudo for picker display", () => {
const hostsById = new Map([
["a", host({ id: "a", label: "Alpha", sftpSudo: true })],
]);
const sessions = [
session({ id: "s-sudo", hostId: "a", status: "connected" }),
];
const result = listSftpConnectedHosts(sessions, hostsById);
assert.equal(result.length, 1);
assert.equal(result[0]?.sessionId, "s-sudo");
assert.equal(result[0]?.host.sftpSudo, true);
// Sudo SFTP still opens a new subsystem/channel, but it should borrow the
// already-authenticated SSH transport before falling back to fresh auth.
assert.equal(
resolveSftpTransferSourceSessionId(sessions, hostsById, "a", result[0]?.host),
"s-sudo",
);
assert.equal(sftpSourceSessionIdForHost(result[0]?.host, result[0]?.sessionId), "s-sudo");
});
test("sftpSourceSessionIdForHost keeps sudo and non-sudo reuse hints", () => {
assert.equal(
sftpSourceSessionIdForHost(host({ id: "a", label: "Alpha" }), "s1"),
"s1",
);
assert.equal(
sftpSourceSessionIdForHost(host({ id: "a", label: "Alpha", sftpSudo: true }), "s1"),
"s1",
);
assert.equal(sftpSourceSessionIdForHost(undefined, "s1"), "s1");
assert.equal(sftpSourceSessionIdForHost(host({ id: "a", label: "Alpha" }), undefined), undefined);
});
test("listSftpConnectedHosts uses the live session endpoint when the vault host was edited", () => {
const hostsById = new Map([
[
"a",
host({
id: "a",
label: "Alpha",
hostname: "edited.example.test",
username: "bob",
port: 2222,
}),
],
]);
const sessions = [
session({
id: "s-live",
hostId: "a",
status: "connected",
hostname: "a.example.test",
username: "alice",
port: 22,
}),
];
const result = listSftpConnectedHosts(sessions, hostsById);
assert.equal(result.length, 1);
assert.equal(result[0]?.sessionId, "s-live");
assert.equal(result[0]?.host.label, "Alpha");
assert.equal(result[0]?.host.hostname, "a.example.test");
assert.equal(result[0]?.host.username, "alice");
assert.equal(result[0]?.host.port, 22);
});
test("listSftpConnectedHosts defaults missing session.port to 22, not the edited vault port", () => {
const hostsById = new Map([
["a", host({ id: "a", label: "Alpha", port: 2222 })],
]);
const sessions = [
session({
id: "s-live",
hostId: "a",
status: "connected",
hostname: "a.example.test",
username: "alice",
port: undefined,
}),
];
const result = listSftpConnectedHosts(sessions, hostsById);
assert.equal(result[0]?.host.port, 22);
});
test("listSftpConnectedHosts skips serial, local, telnet, plugin, and disconnected sessions", () => {
const hosts = [
host({ id: "ssh", label: "SSH" }),
host({ id: "serial", label: "Serial", protocol: "serial" }),
host({ id: "telnet", label: "Telnet", protocol: "telnet" }),
host({
id: "plugin",
label: "Plugin",
protocol: "plugin:com.example.transport.connection",
pluginConnection: { providerId: "com.example.transport.connection", configuration: {} },
}),
];
const hostsById = new Map(hosts.map((h) => [h.id, h]));
const sessions = [
session({ id: "s-ssh", hostId: "ssh", status: "connected" }),
session({ id: "s-serial", hostId: "serial", status: "connected", protocol: "serial" }),
session({ id: "s-local", hostId: "ssh", status: "connected", protocol: "local" }),
session({ id: "s-telnet", hostId: "telnet", status: "connected", protocol: "telnet" }),
session({
id: "s-plugin",
hostId: "plugin",
status: "connected",
protocol: "plugin:com.example.transport.connection",
}),
session({ id: "s-dead", hostId: "ssh", status: "disconnected" }),
];
const result = listSftpConnectedHosts(sessions, hostsById);
assert.deepEqual(
result.map((entry) => entry.sessionId),
["s-ssh"],
);
});
test("listSftpConnectedHosts includes ephemeral hosts present in hostsById", () => {
const ephemeral = host({ id: "ephemeral-1", label: "Deep Link", ephemeral: true });
const hostsById = new Map([[ephemeral.id, ephemeral]]);
const sessions = [
session({
id: "s-ephemeral",
hostId: ephemeral.id,
status: "connected",
ephemeralHost: true,
}),
];
const result = listSftpConnectedHosts(sessions, hostsById);
assert.equal(result.length, 1);
assert.equal(result[0]?.host.ephemeral, true);
assert.equal(result[0]?.sessionId, "s-ephemeral");
});
test("listSftpConnectedHosts skips sessions whose host is missing from the map", () => {
const result = listSftpConnectedHosts(
[session({ id: "orphan", hostId: "missing", status: "connected" })],
new Map(),
);
assert.deepEqual(result, []);
});
test("sftpHostEndpointsEqual compares hostname, username, and port", () => {
const base = host({ id: "a", label: "Alpha", hostname: "a.example.test", username: "alice", port: 22 });
assert.equal(
sftpHostEndpointsEqual(base, { hostname: "a.example.test", username: "alice", port: undefined }),
true,
);
assert.equal(
sftpHostEndpointsEqual(base, { hostname: "a.example.test", username: "bob", port: 22 }),
false,
);
assert.equal(
sftpHostEndpointsEqual(base, { hostname: "other.example.test", username: "alice", port: 22 }),
false,
);
});
test("resolveSftpTransferSourceSessionId matches among multi-tab same hostId endpoints", () => {
const vault = host({ id: "h1", label: "Prod", hostname: "vault.example.test", username: "vault", port: 22 });
const hostsById = new Map([["h1", vault]]);
const sessions = [
session({
id: "s-old",
hostId: "h1",
status: "connected",
hostname: "old.example.test",
username: "alice",
port: 22,
}),
session({
id: "s-new",
hostId: "h1",
status: "connected",
hostname: "new.example.test",
username: "bob",
port: 2222,
}),
];
// Picker collapses to one entry; transfer source must still find the older endpoint.
assert.equal(
listSftpConnectedHosts(sessions, hostsById).length,
1,
);
assert.equal(
resolveSftpTransferSourceSessionId(sessions, hostsById, "h1", {
hostname: "old.example.test",
username: "alice",
port: 22,
}),
"s-old",
);
assert.equal(
resolveSftpTransferSourceSessionId(sessions, hostsById, "h1", {
hostname: "new.example.test",
username: "bob",
port: 2222,
}),
"s-new",
);
// Without endpoint preference, last reusable session for hostId wins.
assert.equal(resolveSftpTransferSourceSessionId(sessions, hostsById, "h1"), "s-new");
});
test("resolveSftpTransferSourceSessionId ignores SFTP browse connection ids", () => {
const vault = host({ id: "h1", label: "Prod", hostname: "prod.example.test", username: "alice", port: 22 });
const hostsById = new Map([["h1", vault]]);
const sessions = [
session({
id: "terminal-ssh-1",
hostId: "h1",
status: "connected",
hostname: "prod.example.test",
username: "alice",
port: 22,
}),
session({
id: "sftp-left-accidental-browse-id",
hostId: "h1",
status: "connected",
hostname: "prod.example.test",
username: "alice",
port: 22,
}),
];
assert.equal(
resolveSftpTransferSourceSessionId(sessions, hostsById, "h1", vault),
"terminal-ssh-1",
);
});
test("sftpPickerSessionsEqual ignores title-only changes", () => {
const prev = [session({ id: "s1", hostId: "a", status: "connected", dynamicTitle: "old" })];
const next = [session({ id: "s1", hostId: "a", status: "connected", dynamicTitle: "new" })];
assert.equal(sftpPickerSessionsEqual(prev, next), true);
});
test("sftpPickerSessionsEqual detects session reorder for same-host preference", () => {
const a = session({ id: "s-a", hostId: "h", status: "connected" });
const b = session({ id: "s-b", hostId: "h", status: "connected" });
assert.equal(sftpPickerSessionsEqual([a, b], [b, a]), false);
});
test("sftpPickerSessionsEqual detects status, hostId, transport, and endpoint changes", () => {
const base = session({ id: "s1", hostId: "a", status: "connected" });
assert.equal(
sftpPickerSessionsEqual([base], [session({ id: "s1", hostId: "a", status: "connecting" })]),
false,
);
assert.equal(
sftpPickerSessionsEqual([base], [session({ id: "s1", hostId: "b", status: "connected" })]),
false,
);
assert.equal(
sftpPickerSessionsEqual(
[base],
[session({ id: "s1", hostId: "a", status: "connected", moshEnabled: true })],
),
false,
);
assert.equal(
sftpPickerSessionsEqual(
[base],
[session({ id: "s1", hostId: "a", status: "connected", hostname: "other.example.test" })],
),
false,
);
});