[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:
156
electron/mainDeepLinkDelivery.test.cjs
Normal file
156
electron/mainDeepLinkDelivery.test.cjs
Normal file
@@ -0,0 +1,156 @@
|
||||
const assert = require("node:assert/strict");
|
||||
const { readFileSync } = require("node:fs");
|
||||
const test = require("node:test");
|
||||
const vm = require("node:vm");
|
||||
const deepLink = require("./deepLink.cjs");
|
||||
|
||||
const source = readFileSync(require.resolve("./main.cjs"), "utf8");
|
||||
|
||||
function section(start, end) {
|
||||
const from = source.indexOf(start);
|
||||
const to = source.indexOf(end, from);
|
||||
assert.ok(from >= 0 && to > from);
|
||||
return source.slice(from, to);
|
||||
}
|
||||
|
||||
// Execute the main process's actual queue, preference handler, and delivery
|
||||
// functions. Only desktop services and renderer readiness are controlled here.
|
||||
function createHarness() {
|
||||
const handlers = new Map();
|
||||
const deliveries = [];
|
||||
const waiting = [];
|
||||
const timers = [];
|
||||
const sandbox = {
|
||||
...deepLink,
|
||||
process: { argv: ["netcatty"] },
|
||||
app: { isReady: () => false },
|
||||
ipcMain: { handle: (name, handler) => handlers.set(name, handler) },
|
||||
isDev: false,
|
||||
readSshDeepLinkEnabledPreference: () => true,
|
||||
readJmsDeepLinkEnabledPreference: () => false,
|
||||
resolveOpenTerminalPathsFromArgs: () => [],
|
||||
resolveExplorerContextMenuEnabled: () => ({ enabled: false }),
|
||||
applySshProtocolClientPreference: () => true,
|
||||
writeSshDeepLinkEnabledPreference: () => true,
|
||||
createAndShowMainWindow: async () => ({}),
|
||||
focusMainWindow: () => {},
|
||||
getWindowManager: () => ({
|
||||
sendWhenRendererReady: (_win, channel, payload, options) => new Promise((resolve) => {
|
||||
waiting.push({ channel, payload, options, resolve });
|
||||
}),
|
||||
}),
|
||||
setTimeout: (callback) => timers.push(callback),
|
||||
console: { warn: () => {} },
|
||||
};
|
||||
const context = vm.createContext(sandbox);
|
||||
vm.runInContext([
|
||||
section("let sshDeepLinkEnabled =", 'ipcMain?.handle?.("netcatty:deepLink:jms:setEnabled"'),
|
||||
section("async function deliverSshDeepLink(", "async function deliverOpenTerminalPath("),
|
||||
`globalThis.launch = {
|
||||
ssh: { queue: queueSshDeepLink, flush: flushPendingSshDeepLinks, pending: pendingSshDeepLinkUrls },
|
||||
telnet: { queue: queueTelnetDeepLink, flush: flushPendingTelnetDeepLinks, pending: pendingTelnetDeepLinkUrls },
|
||||
};`,
|
||||
].join("\n"), context);
|
||||
|
||||
return {
|
||||
launch: context.launch,
|
||||
deliveries,
|
||||
timers,
|
||||
async setEnabled(enabled) {
|
||||
await handlers.get("netcatty:deepLink:ssh:setEnabled")(null, { enabled });
|
||||
},
|
||||
async nextDelivery() {
|
||||
for (let attempt = 0; attempt < 20 && waiting.length === 0; attempt++) await Promise.resolve();
|
||||
assert.ok(waiting.length > 0, "delivery must reach renderer readiness");
|
||||
const item = waiting.shift();
|
||||
return {
|
||||
finish(result) {
|
||||
if (result) return item.resolve(result);
|
||||
if (!item.options.shouldSend()) {
|
||||
return item.resolve({ success: false, reason: item.options.cancelReason });
|
||||
}
|
||||
deliveries.push({ channel: item.channel, url: item.payload.url });
|
||||
item.resolve({ success: true });
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
for (const protocol of ["ssh", "telnet"]) {
|
||||
test(`${protocol}: disable/re-enable permanently cancels an in-flight scheme request`, async () => {
|
||||
const h = createHarness();
|
||||
const queue = h.launch[protocol];
|
||||
queue.queue(`${protocol}://old.example`);
|
||||
const flushing = queue.flush();
|
||||
const old = await h.nextDelivery();
|
||||
await h.setEnabled(false);
|
||||
await h.setEnabled(true);
|
||||
old.finish();
|
||||
await flushing;
|
||||
assert.deepEqual(h.deliveries, []);
|
||||
assert.equal(queue.pending.length, 0);
|
||||
assert.equal(h.timers.length, 0);
|
||||
});
|
||||
|
||||
test(`${protocol}: cancelled schemes cannot return through a failed-delivery retry`, async () => {
|
||||
const h = createHarness();
|
||||
const queue = h.launch[protocol];
|
||||
queue.queue(`${protocol}://old.example`);
|
||||
const flushing = queue.flush();
|
||||
const old = await h.nextDelivery();
|
||||
await h.setEnabled(false);
|
||||
await h.setEnabled(true);
|
||||
old.finish({ success: false, reason: "window-destroyed" });
|
||||
await flushing;
|
||||
assert.equal(queue.pending.length, 0, "a cancelled request must not be requeued");
|
||||
assert.equal(h.timers.length, 0);
|
||||
});
|
||||
|
||||
test(`${protocol}: disabling removes queued schemes but preserves queued CLI and new requests`, async () => {
|
||||
const h = createHarness();
|
||||
const queue = h.launch[protocol];
|
||||
queue.queue(`${protocol}://old.example`);
|
||||
queue.queue(`${protocol}://queued-old.example`);
|
||||
queue.queue(`${protocol}://cli.example`, { viaCommandLine: true });
|
||||
const flushing = queue.flush();
|
||||
const old = await h.nextDelivery();
|
||||
await h.setEnabled(false);
|
||||
await h.setEnabled(true);
|
||||
queue.queue(`${protocol}://new.example`);
|
||||
old.finish();
|
||||
(await h.nextDelivery()).finish();
|
||||
(await h.nextDelivery()).finish();
|
||||
await flushing;
|
||||
assert.deepEqual(h.deliveries.map((item) => item.url), [
|
||||
`${protocol}://cli.example`, `${protocol}://new.example`,
|
||||
]);
|
||||
});
|
||||
|
||||
test(`${protocol}: an in-flight CLI request survives disabling and re-enabling schemes`, async () => {
|
||||
const h = createHarness();
|
||||
const queue = h.launch[protocol];
|
||||
queue.queue(`${protocol}://cli.example`, { viaCommandLine: true });
|
||||
const flushing = queue.flush();
|
||||
const cli = await h.nextDelivery();
|
||||
await h.setEnabled(false);
|
||||
await h.setEnabled(true);
|
||||
cli.finish();
|
||||
await flushing;
|
||||
assert.deepEqual(h.deliveries.map((item) => item.url), [`${protocol}://cli.example`]);
|
||||
});
|
||||
|
||||
test(`${protocol}: CLI retries remain queued when schemes are disabled`, async () => {
|
||||
const h = createHarness();
|
||||
const queue = h.launch[protocol];
|
||||
queue.queue(`${protocol}://cli.example`, { viaCommandLine: true });
|
||||
const flushing = queue.flush();
|
||||
const cli = await h.nextDelivery();
|
||||
await h.setEnabled(false);
|
||||
cli.finish({ success: false, reason: "window-destroyed" });
|
||||
await flushing;
|
||||
assert.equal(queue.pending.length, 1);
|
||||
assert.equal(h.timers.length, 1);
|
||||
assert.equal(queue.pending[0].rawUrl, `${protocol}://cli.example`);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user