[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:
195
domain/connectionLog.test.ts
Normal file
195
domain/connectionLog.test.ts
Normal file
@@ -0,0 +1,195 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import type { ConnectionLog } from "./models.ts";
|
||||
import { selectConnectionLogForTerminalDataCapture } from "./connectionLog.ts";
|
||||
import {
|
||||
MAX_PERSISTED_UNSAVED_TERMINAL_DATA_ENTRIES,
|
||||
mergeConnectionLogsFromStorage,
|
||||
mergeTerminalDataIntoLogs,
|
||||
mergeTerminalDataMapsForStorage,
|
||||
pruneTerminalDataMapForStorage,
|
||||
} from "./connectionLogTerminalData.ts";
|
||||
|
||||
const baseLog: ConnectionLog = {
|
||||
id: "log-base",
|
||||
sessionId: "session-1",
|
||||
hostId: "host-1",
|
||||
hostLabel: "Example",
|
||||
hostname: "example.com",
|
||||
username: "user",
|
||||
protocol: "ssh",
|
||||
startTime: 1000,
|
||||
localUsername: "local",
|
||||
localHostname: "machine",
|
||||
saved: false,
|
||||
};
|
||||
|
||||
test("selectConnectionLogForTerminalDataCapture picks the active log for a normal session exit", () => {
|
||||
const matchingLog = { ...baseLog, id: "active", startTime: 2000 };
|
||||
const staleLog = {
|
||||
...baseLog,
|
||||
id: "stale",
|
||||
sessionId: "session-2",
|
||||
startTime: 3000,
|
||||
};
|
||||
|
||||
assert.equal(
|
||||
selectConnectionLogForTerminalDataCapture(
|
||||
[staleLog, matchingLog],
|
||||
{ sessionId: "session-1", hostname: "example.com" },
|
||||
)?.id,
|
||||
"active",
|
||||
);
|
||||
});
|
||||
|
||||
test("selectConnectionLogForTerminalDataCapture reuses the latest log for repeated captures after reconnect", () => {
|
||||
const firstCapture = {
|
||||
...baseLog,
|
||||
id: "first-capture",
|
||||
startTime: 2000,
|
||||
endTime: 2500,
|
||||
terminalData: "first disconnect",
|
||||
};
|
||||
const olderSameSession = {
|
||||
...baseLog,
|
||||
id: "older-same-session",
|
||||
startTime: 1500,
|
||||
endTime: 1800,
|
||||
terminalData: "older data",
|
||||
};
|
||||
const otherSession = {
|
||||
...baseLog,
|
||||
id: "other-session",
|
||||
sessionId: "session-2",
|
||||
startTime: 3000,
|
||||
};
|
||||
|
||||
assert.equal(
|
||||
selectConnectionLogForTerminalDataCapture(
|
||||
[otherSession, olderSameSession, firstCapture],
|
||||
{ sessionId: "session-1", hostname: "example.com" },
|
||||
)?.id,
|
||||
"first-capture",
|
||||
);
|
||||
});
|
||||
|
||||
test("selectConnectionLogForTerminalDataCapture does not cross-match localhost logs without sessionId", () => {
|
||||
const openLocalWithoutSession = {
|
||||
...baseLog,
|
||||
id: "open-local",
|
||||
sessionId: undefined,
|
||||
hostname: "localhost",
|
||||
protocol: "local",
|
||||
startTime: 3000,
|
||||
};
|
||||
const targetLocal = {
|
||||
...baseLog,
|
||||
id: "target-local",
|
||||
sessionId: "session-local-a",
|
||||
hostname: "localhost",
|
||||
protocol: "local",
|
||||
startTime: 2000,
|
||||
};
|
||||
|
||||
assert.equal(
|
||||
selectConnectionLogForTerminalDataCapture(
|
||||
[openLocalWithoutSession, targetLocal],
|
||||
{ sessionId: "session-local-b", hostname: "localhost" },
|
||||
),
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
|
||||
test("mergeConnectionLogsFromStorage keeps in-memory terminal replay data", () => {
|
||||
const memoryLog = {
|
||||
...baseLog,
|
||||
id: "memory",
|
||||
terminalData: "captured output",
|
||||
};
|
||||
const storedLog = {
|
||||
...baseLog,
|
||||
id: "memory",
|
||||
endTime: 2000,
|
||||
};
|
||||
|
||||
const merged = mergeConnectionLogsFromStorage(
|
||||
[memoryLog],
|
||||
[storedLog],
|
||||
{},
|
||||
);
|
||||
|
||||
assert.equal(merged[0]?.terminalData, "captured output");
|
||||
});
|
||||
|
||||
test("mergeTerminalDataIntoLogs hydrates unsaved logs from side storage", () => {
|
||||
const storedLog = { ...baseLog, id: "hydrate" };
|
||||
const hydrated = mergeTerminalDataIntoLogs([storedLog], {
|
||||
hydrate: "side-store output",
|
||||
});
|
||||
|
||||
assert.equal(hydrated[0]?.terminalData, "side-store output");
|
||||
});
|
||||
|
||||
test("pruneTerminalDataMapForStorage caps unsaved replay buffers", () => {
|
||||
const logs: ConnectionLog[] = Array.from({ length: 60 }, (_, index) => ({
|
||||
...baseLog,
|
||||
id: `log-${index}`,
|
||||
startTime: index,
|
||||
saved: false,
|
||||
}));
|
||||
|
||||
const map = Object.fromEntries(
|
||||
logs.map((log) => [log.id, `data-${log.id}`]),
|
||||
);
|
||||
|
||||
const pruned = pruneTerminalDataMapForStorage(logs, map);
|
||||
assert.equal(Object.keys(pruned).length, MAX_PERSISTED_UNSAVED_TERMINAL_DATA_ENTRIES);
|
||||
assert.equal(pruned["log-59"], "data-log-59");
|
||||
assert.equal(pruned["log-0"], undefined);
|
||||
});
|
||||
|
||||
test("mergeTerminalDataMapsForStorage keeps replay data from other windows", () => {
|
||||
const logs: ConnectionLog[] = [
|
||||
{ ...baseLog, id: "local", startTime: 2000 },
|
||||
{ ...baseLog, id: "remote", startTime: 1000 },
|
||||
];
|
||||
|
||||
const merged = mergeTerminalDataMapsForStorage(
|
||||
logs,
|
||||
{ remote: "remote-window output", other: "other-window only" },
|
||||
[{ local: "local-window output" }],
|
||||
new Set(["local", "remote", "other"]),
|
||||
);
|
||||
|
||||
assert.equal(merged.remote, "remote-window output");
|
||||
assert.equal(merged.local, "local-window output");
|
||||
assert.equal(merged.other, "other-window only");
|
||||
});
|
||||
|
||||
test("mergeTerminalDataMapsForStorage prefers fresher local replay data for orphans", () => {
|
||||
const logs: ConnectionLog[] = [{ ...baseLog, id: "local", startTime: 2000 }];
|
||||
|
||||
const merged = mergeTerminalDataMapsForStorage(
|
||||
logs,
|
||||
{ other: "stale snapshot" },
|
||||
[{ other: "fresh local" }],
|
||||
new Set(["local", "other"]),
|
||||
);
|
||||
|
||||
assert.equal(merged.other, "fresh local");
|
||||
});
|
||||
|
||||
test("mergeTerminalDataMapsForStorage drops deleted log replay buffers", () => {
|
||||
const logs: ConnectionLog[] = [{ ...baseLog, id: "local", startTime: 2000 }];
|
||||
|
||||
const merged = mergeTerminalDataMapsForStorage(
|
||||
logs,
|
||||
{ local: "keep", deleted: "drop me" },
|
||||
[],
|
||||
new Set(["local"]),
|
||||
);
|
||||
|
||||
assert.equal(merged.local, "keep");
|
||||
assert.equal(merged.deleted, undefined);
|
||||
});
|
||||
Reference in New Issue
Block a user