Files
Netcatty/electron/preloadDataBacklog.test.cjs
T
2026-06-26 15:22:52 +08:00

188 lines
5.6 KiB
JavaScript

const assert = require("node:assert/strict");
const test = require("node:test");
const { createPreloadApi } = require("./preload/api.cjs");
const {
clearTerminalDataSession,
createTerminalDataBacklog,
createTerminalDataDispatcher,
} = require("./preload/terminalDataBacklog.cjs");
test("stores early terminal data until the listener is registered", () => {
const backlog = createTerminalDataBacklog();
backlog.append("session-1", "Linux banner\r\n");
backlog.append("session-1", "root@host:~# ");
assert.equal(backlog.take("session-1"), "Linux banner\r\nroot@host:~# ");
assert.equal(backlog.take("session-1"), "");
});
test("keeps each session backlog isolated", () => {
const backlog = createTerminalDataBacklog();
backlog.append("session-1", "one");
backlog.append("session-2", "two");
assert.equal(backlog.take("session-2"), "two");
assert.equal(backlog.take("session-1"), "one");
});
test("trims old data when the per-session limit is exceeded", () => {
const backlog = createTerminalDataBacklog({ maxBytesPerSession: 5 });
backlog.append("session-1", "hello");
backlog.append("session-1", " world");
assert.equal(backlog.take("session-1"), "world");
});
test("clear drops pending data for a closed session", () => {
const backlog = createTerminalDataBacklog();
backlog.append("session-1", "pending");
backlog.clear("session-1");
assert.equal(backlog.size("session-1"), 0);
assert.equal(backlog.take("session-1"), "");
});
test("onSessionData flushes pending terminal data on subscribe", () => {
const dataListeners = new Map();
const displayDataListeners = new Map();
const terminalDataBacklog = createTerminalDataBacklog();
terminalDataBacklog.append("session-1", "early MOTD\r\n");
const api = createPreloadApi({
ipcRenderer: {
invoke() {},
send() {},
on() {},
removeListener() {},
},
os: {
release: () => "10.0.19045",
},
dataListeners,
displayDataListeners,
terminalDataBacklog,
});
const received = [];
const unsubscribe = api.onSessionData("session-1", (chunk) => {
received.push(chunk);
}, { replayBacklog: true });
assert.deepEqual(received, ["early MOTD\r\n"]);
assert.equal(terminalDataBacklog.size("session-1"), 0);
assert.equal(displayDataListeners.get("session-1").size, 1);
unsubscribe();
assert.equal(dataListeners.has("session-1"), false);
assert.equal(displayDataListeners.has("session-1"), false);
});
test("non-display listeners do not drain pending terminal data", () => {
const dataListeners = new Map();
const displayDataListeners = new Map();
const terminalDataBacklog = createTerminalDataBacklog();
terminalDataBacklog.append("session-1", "early prompt");
const api = createPreloadApi({
ipcRenderer: {
invoke() {},
send() {},
on() {},
removeListener() {},
},
os: {
release: () => "10.0.19045",
},
dataListeners,
displayDataListeners,
terminalDataBacklog,
});
const observerReceived = [];
const displayReceived = [];
api.onSessionData("session-1", (chunk) => {
observerReceived.push(chunk);
});
assert.deepEqual(observerReceived, []);
assert.equal(terminalDataBacklog.size("session-1"), "early prompt".length);
api.onSessionData("session-1", (chunk) => {
displayReceived.push(chunk);
}, { replayBacklog: true });
assert.deepEqual(observerReceived, []);
assert.deepEqual(displayReceived, ["early prompt"]);
assert.equal(terminalDataBacklog.size("session-1"), 0);
});
test("keeps early data for display replay while only observer listeners exist", () => {
const observed = [];
const dataListeners = new Map([
["session-1", new Set([(chunk) => observed.push(chunk)])],
]);
const displayDataListeners = new Map();
const terminalDataBacklog = createTerminalDataBacklog();
const deliverToListeners = createTerminalDataDispatcher({
dataListeners,
displayDataListeners,
terminalDataBacklog,
});
deliverToListeners("session-1", "Linux banner\r\n");
assert.deepEqual(observed, ["Linux banner\r\n"]);
assert.equal(terminalDataBacklog.take("session-1"), "Linux banner\r\n");
});
test("does not backlog data once the display listener is registered", () => {
const observed = [];
const displayed = [];
const displayListener = (chunk) => displayed.push(chunk);
const dataListeners = new Map([
["session-1", new Set([(chunk) => observed.push(chunk), displayListener])],
]);
const displayDataListeners = new Map([
["session-1", new Set([displayListener])],
]);
const terminalDataBacklog = createTerminalDataBacklog();
const deliverToListeners = createTerminalDataDispatcher({
dataListeners,
displayDataListeners,
terminalDataBacklog,
});
deliverToListeners("session-1", "live output");
assert.deepEqual(observed, ["live output"]);
assert.deepEqual(displayed, ["live output"]);
assert.equal(terminalDataBacklog.take("session-1"), "");
});
test("clearTerminalDataSession drops listener and backlog state together", () => {
const listener = () => {};
const dataListeners = new Map([
["session-1", new Set([listener])],
]);
const displayDataListeners = new Map([
["session-1", new Set([listener])],
]);
const terminalDataBacklog = createTerminalDataBacklog();
terminalDataBacklog.append("session-1", "pending");
clearTerminalDataSession({
dataListeners,
displayDataListeners,
terminalDataBacklog,
}, "session-1");
assert.equal(dataListeners.has("session-1"), false);
assert.equal(displayDataListeners.has("session-1"), false);
assert.equal(terminalDataBacklog.take("session-1"), "");
});