import assert from "node:assert/strict"; import { spawnSync } from "node:child_process"; import { accessSync, chmodSync, constants } from "node:fs"; import { mkdir, mkdtemp, rm, symlink, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; import test from "node:test"; const LIB_SH = fileURLToPath(new URL("../scripts/lib.sh", import.meta.url)); const SYSTEM_KITTY = "/Applications/kitty.app/Contents/MacOS/kitty"; function systemKittyPath(): string | undefined { try { accessSync(SYSTEM_KITTY, constants.X_OK); return SYSTEM_KITTY; } catch { return undefined; } } function runDetectKitty(home: string, pathDir: string) { return spawnSync( "sh", ["-c", `. "$1"; detect_kitty; status=$?; printf '%s' "$kitty_bin"; exit "$status"`, "sh", LIB_SH], { encoding: "utf8", env: { ...process.env, HOME: home, PATH: `${pathDir}:${process.env.PATH}` }, }, ); } test("detect_kitty prefers an official install over a PATH kitty", async () => { const home = await mkdtemp(join(tmpdir(), "my-pi-libsh-")); try { const official = join(home, ".local", "kitty.app", "bin", "kitty"); const pathDir = join(home, "pathbin"); await mkdir(dirname(official), { recursive: true }); await mkdir(pathDir, { recursive: true }); await writeFile(official, "#!/bin/sh\n"); chmodSync(official, 0o755); await symlink(official, join(pathDir, "kitty")); const result = runDetectKitty(home, pathDir); assert.equal(result.status, 0, result.stderr); // A real /Applications kitty also wins over $HOME/.local and PATH. assert.equal(result.stdout, systemKittyPath() ?? official); } finally { await rm(home, { recursive: true, force: true }); } }); test("detect_kitty falls back to a PATH kitty when no official install exists", async () => { const home = await mkdtemp(join(tmpdir(), "my-pi-libsh-")); try { const pathDir = join(home, "pathbin"); const pathKitty = join(pathDir, "kitty"); await mkdir(pathDir, { recursive: true }); await writeFile(pathKitty, "#!/bin/sh\n"); chmodSync(pathKitty, 0o755); const result = runDetectKitty(home, pathDir); assert.equal(result.status, 0, result.stderr); // The isolated HOME has no ~/.local install; only a machine-level // /Applications kitty (when present) still takes precedence. assert.equal(result.stdout, systemKittyPath() ?? pathKitty); } finally { await rm(home, { recursive: true, force: true }); } });