[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:
161
electron/bridges/terminalBridge.windowsAppsAlias.test.cjs
Normal file
161
electron/bridges/terminalBridge.windowsAppsAlias.test.cjs
Normal file
@@ -0,0 +1,161 @@
|
||||
const test = require("node:test");
|
||||
const assert = require("node:assert/strict");
|
||||
const fs = require("node:fs");
|
||||
const os = require("node:os");
|
||||
const path = require("node:path");
|
||||
const Module = require("node:module");
|
||||
const childProcess = require("node:child_process");
|
||||
|
||||
function loadBridgeWithFakePty() {
|
||||
const bridgePath = require.resolve("./terminalBridge.cjs");
|
||||
delete require.cache[bridgePath];
|
||||
const originalLoad = Module._load;
|
||||
Module._load = function patchedLoad(request) {
|
||||
if (request === "node-pty") {
|
||||
return {
|
||||
spawn() {
|
||||
throw new Error("node-pty spawn is not exercised by this suite");
|
||||
},
|
||||
};
|
||||
}
|
||||
return originalLoad.apply(this, arguments);
|
||||
};
|
||||
try {
|
||||
return require("./terminalBridge.cjs");
|
||||
} finally {
|
||||
Module._load = originalLoad;
|
||||
}
|
||||
}
|
||||
|
||||
const bridge = loadBridgeWithFakePty();
|
||||
|
||||
function withWin32Platform(fn) {
|
||||
const platformDescriptor = Object.getOwnPropertyDescriptor(process, "platform");
|
||||
Object.defineProperty(process, "platform", { ...platformDescriptor, value: "win32" });
|
||||
try {
|
||||
return fn();
|
||||
} finally {
|
||||
Object.defineProperty(process, "platform", platformDescriptor);
|
||||
}
|
||||
}
|
||||
|
||||
// findExecutable() re-requires child_process inside the call, so patching the
|
||||
// module object's execFileSync at runtime intercepts its `where.exe` probes.
|
||||
function withWhereFake(resultsByname, fn) {
|
||||
const realExecFileSync = childProcess.execFileSync;
|
||||
childProcess.execFileSync = function fakeExecFileSync(file, args) {
|
||||
const argv0 = String(file).toLowerCase();
|
||||
const isWhere =
|
||||
argv0 === "where.exe" || argv0.endsWith("\\where.exe") || argv0.endsWith("/where.exe");
|
||||
if (isWhere) {
|
||||
const lines = resultsByname.get(args[0]);
|
||||
if (lines !== undefined) {
|
||||
if (lines === null) {
|
||||
throw new Error("not found");
|
||||
}
|
||||
return lines.join("\r\n");
|
||||
}
|
||||
}
|
||||
throw new Error(`unexpected execFileSync call: ${file} ${JSON.stringify(args)}`);
|
||||
};
|
||||
try {
|
||||
return fn();
|
||||
} finally {
|
||||
childProcess.execFileSync = realExecFileSync;
|
||||
}
|
||||
}
|
||||
|
||||
// Hide machine-specific fallback paths (e.g. an MSI pwsh under Program Files)
|
||||
// so the tests behave identically regardless of what the host has installed.
|
||||
function withExistsSyncAllowlist(allowed, fn) {
|
||||
const realExistsSync = fs.existsSync;
|
||||
fs.existsSync = (p) => allowed.has(String(p));
|
||||
try {
|
||||
return fn();
|
||||
} finally {
|
||||
fs.existsSync = realExistsSync;
|
||||
}
|
||||
}
|
||||
|
||||
function makeHarness(t) {
|
||||
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "netcatty-local-shell-"));
|
||||
t.after(() => fs.rmSync(tmp, { recursive: true, force: true }));
|
||||
|
||||
const previousLocalAppData = process.env.LOCALAPPDATA;
|
||||
process.env.LOCALAPPDATA = tmp;
|
||||
t.after(() => {
|
||||
if (previousLocalAppData === undefined) {
|
||||
delete process.env.LOCALAPPDATA;
|
||||
} else {
|
||||
process.env.LOCALAPPDATA = previousLocalAppData;
|
||||
}
|
||||
});
|
||||
|
||||
const aliasPath = path.join(tmp, "Microsoft", "WindowsApps", "pwsh.exe");
|
||||
// Intentionally not created on disk: real App Execution Aliases fail
|
||||
// fs.statSync() with EACCES, so existsSync() is false for them anyway.
|
||||
|
||||
const realPwshPath = path.join(tmp, "PowerShell", "7", "pwsh.exe");
|
||||
fs.mkdirSync(path.dirname(realPwshPath), { recursive: true });
|
||||
fs.writeFileSync(realPwshPath, "");
|
||||
|
||||
const windowsPowerShellPath = path.join(tmp, "WindowsPowerShell", "v1.0", "powershell.exe");
|
||||
fs.mkdirSync(path.dirname(windowsPowerShellPath), { recursive: true });
|
||||
fs.writeFileSync(windowsPowerShellPath, "");
|
||||
|
||||
return { tmp, aliasPath, realPwshPath, windowsPowerShellPath };
|
||||
}
|
||||
|
||||
test("getDefaultLocalShell resolves an MSIX pwsh execution alias", (t) => {
|
||||
const { aliasPath } = makeHarness(t);
|
||||
|
||||
withWin32Platform(() =>
|
||||
withWhereFake(new Map([["pwsh", [aliasPath]]]), () => {
|
||||
assert.equal(bridge.getDefaultLocalShell(), aliasPath);
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
test("findExecutable prefers a regular executable listed after an execution alias", (t) => {
|
||||
const { aliasPath, realPwshPath } = makeHarness(t);
|
||||
|
||||
withWin32Platform(() =>
|
||||
withWhereFake(new Map([["pwsh", [aliasPath, realPwshPath]]]), () => {
|
||||
assert.equal(bridge.findExecutable("pwsh"), realPwshPath);
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
test("getDefaultLocalShell still falls back to Windows PowerShell when no pwsh exists", (t) => {
|
||||
const { windowsPowerShellPath } = makeHarness(t);
|
||||
|
||||
withWin32Platform(() =>
|
||||
withWhereFake(
|
||||
new Map([
|
||||
["pwsh", null],
|
||||
["powershell", [windowsPowerShellPath]],
|
||||
]),
|
||||
() =>
|
||||
withExistsSyncAllowlist(new Set([windowsPowerShellPath]), () => {
|
||||
assert.equal(bridge.getDefaultLocalShell(), windowsPowerShellPath);
|
||||
}),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
test("getDefaultLocalShell keeps the powershell.exe last-resort fallback", (t) => {
|
||||
makeHarness(t);
|
||||
|
||||
withWin32Platform(() =>
|
||||
withWhereFake(
|
||||
new Map([
|
||||
["pwsh", null],
|
||||
["powershell", null],
|
||||
]),
|
||||
() =>
|
||||
withExistsSyncAllowlist(new Set(), () => {
|
||||
assert.equal(bridge.getDefaultLocalShell(), "powershell.exe");
|
||||
}),
|
||||
),
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user