aboutsummaryrefslogtreecommitdiff
path: root/test/util/daemon-socket-test.ts
diff options
context:
space:
mode:
Diffstat (limited to 'test/util/daemon-socket-test.ts')
-rw-r--r--test/util/daemon-socket-test.ts81
1 files changed, 79 insertions, 2 deletions
diff --git a/test/util/daemon-socket-test.ts b/test/util/daemon-socket-test.ts
index 80a03e4..2f9ce8d 100644
--- a/test/util/daemon-socket-test.ts
+++ b/test/util/daemon-socket-test.ts
@@ -1,11 +1,18 @@
import { test, describe } from "node:test";
import assert from "node:assert/strict";
import { fileURLToPath } from "node:url";
-import { existsSync } from "node:fs";
+import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
+import { tmpdir } from "node:os";
+import { dirname, join } from "node:path";
import { Type } from "typebox";
import type { ExtensionAPI, ToolDefinition } from "@mariozechner/pi-coding-agent";
import type { TSchema } from "typebox";
-import { daemonSocketMayExist, daemonSpawnCommand } from "../../src/daemon/spawn";
+import {
+ daemonNodeExecutable,
+ daemonSocketMayExist,
+ daemonSpawnCommand,
+ resolveTsxCliFromFilesystem,
+} from "../../src/daemon/spawn";
import { type AnyBrowserToolDefinition, registerBrowserTool } from "../../src/util/tool";
import type { BrowserClient } from "../../src/client";
import { ok } from "../../src/util/result";
@@ -55,6 +62,76 @@ describe("daemon spawn command", () => {
});
});
+describe("daemon node executable", () => {
+ const nodeVersions = { node: "22.0.0" };
+ const bunVersions = { node: "22.0.0", bun: "1.3.14" };
+
+ test("a plain Node host reuses its own executable", () => {
+ assert.equal(daemonNodeExecutable({}, nodeVersions, "linux", "/usr/bin/node"), "/usr/bin/node");
+ assert.equal(daemonNodeExecutable({}, nodeVersions, "win32", "C:\\node\\node.exe"), "C:\\node\\node.exe");
+ });
+
+ test("a Bun-compiled host never spawns itself as node", () => {
+ assert.equal(daemonNodeExecutable({}, bunVersions, "linux", "/opt/pi/pi"), "node");
+ assert.equal(daemonNodeExecutable({}, bunVersions, "darwin", "/opt/pi/pi"), "node");
+ assert.equal(daemonNodeExecutable({}, bunVersions, "win32", "C:\\pi\\pi.exe"), "node.exe");
+ });
+
+ test("PI_BROWSER_NODE overrides on every runtime", () => {
+ const env = { PI_BROWSER_NODE: "/custom/node" };
+ assert.equal(daemonNodeExecutable(env, nodeVersions, "linux", "/usr/bin/node"), "/custom/node");
+ assert.equal(daemonNodeExecutable(env, bunVersions, "win32", "C:\\pi\\pi.exe"), "/custom/node");
+ });
+
+ test("an empty PI_BROWSER_NODE is ignored", () => {
+ assert.equal(daemonNodeExecutable({ PI_BROWSER_NODE: "" }, bunVersions, "linux", "/opt/pi/pi"), "node");
+ });
+});
+
+const withTree = (prefix: string, fn: (root: string) => void): void => {
+ const root = mkdtempSync(join(tmpdir(), prefix));
+ try {
+ fn(root);
+ } finally {
+ rmSync(root, { recursive: true, force: true });
+ }
+};
+
+const touch = (file: string): void => {
+ mkdirSync(dirname(file), { recursive: true });
+ writeFileSync(file, "");
+};
+
+describe("tsx CLI filesystem fallback", () => {
+ // pi installs extensions into one flat tree, so tsx is a sibling of the package, not inside it.
+ test("finds a tsx hoisted beside the package, walking up from src/daemon", () => {
+ withTree("pbh-hoist-", (root) => {
+ const cli = join(root, "node_modules", "tsx", "dist", "cli.mjs");
+ touch(cli);
+ const start = join(root, "node_modules", "pi-browser-harness", "src", "daemon");
+ mkdirSync(start, { recursive: true });
+ assert.equal(resolveTsxCliFromFilesystem(start), cli);
+ });
+ });
+
+ test("prefers the package's own node_modules over a hoisted copy", () => {
+ withTree("pbh-local-", (root) => {
+ const hoisted = join(root, "node_modules", "tsx", "dist", "cli.mjs");
+ const local = join(root, "node_modules", "pi-browser-harness", "node_modules", "tsx", "dist", "cli.mjs");
+ touch(hoisted);
+ touch(local);
+ const start = join(root, "node_modules", "pi-browser-harness", "src", "daemon");
+ mkdirSync(start, { recursive: true });
+ assert.equal(resolveTsxCliFromFilesystem(start), local);
+ });
+ });
+
+ test("the fallback agrees with Node's resolver in this checkout", () => {
+ const viaResolver = daemonSpawnCommand()?.args[0];
+ assert.equal(resolveTsxCliFromFilesystem(), viaResolver);
+ });
+});
+
type GateProbe = { readonly ran: ReadonlyArray<string>; readonly text: string };
const runGate = async (ensureAlive: boolean | undefined): Promise<GateProbe> => {