849 lines · 35.9 KB · TypeScript
RawDownload
// @effect-diagnostics nodeBuiltinImport:off - the executed suite runs the generated install script through a real POSIX shell.import { describe, it } from "@effect/vitest";import { afterAll, expect } from "vite-plus/test";import * as NodeChildProcess from "node:child_process";import * as Duration from "effect/Duration";import * as Effect from "effect/Effect";import * as Fiber from "effect/Fiber";import * as Layer from "effect/Layer";import * as Sink from "effect/Sink";import * as Stream from "effect/Stream";import * as TestClock from "effect/testing/TestClock";import { ChildProcessSpawner } from "effect/unstable/process"; import {  buildWslRuntimeInstallScript,  buildWslRuntimeInvalidateScript,  buildWslRuntimePruneScript,  DesktopWslDistroListError,  formatMissingToolsReason,  parseNodePath,  parseNodeVersion,  parseResolvedPath,  parseToolchainReport,  parseWslRuntimeRoot,  probeWslDistros,} from "./DesktopWslEnvironment.ts"; const encoder = new TextEncoder(); // The install script only fails the way this file cares about when a real shell// runs it, so find one that has the tools it needs: bash directly on Linux, and// the WSL distro on a Windows dev box, where Git Bash ships no flock. Anywhere// else the executed suite skips and the generated-text assertions stand alone.const REQUIRED_SHELL_TOOLS = ["flock", "sha256sum", "tar", "mktemp"] as const; const posixShellRunner = (() => {  // Candidates rather than a platform switch: wsl.exe simply fails to spawn  // where it does not exist, which is the same answer as a shell missing flock.  const candidates = [    { file: "bash", args: [] as ReadonlyArray<string> },    { file: "wsl.exe", args: ["-e", "bash"] as ReadonlyArray<string> },  ];  const probe = [    "[ -d /proc/1 ] || exit 1",    ...REQUIRED_SHELL_TOOLS.map((tool) => `command -v ${tool} >/dev/null || exit 1`),  ].join("\n");  return (    candidates.find((candidate) => {      const result = NodeChildProcess.spawnSync(candidate.file, [...candidate.args, "-c", probe], {        encoding: "utf8",      });      return result.status === 0;    }) ?? null  );})(); const runShell = (script: string) => {  if (posixShellRunner === null) throw new Error("no POSIX shell runner available");  // The install script arrives on stdin in production too, which is what lets  // its own /proc scan not match itself.  const result = NodeChildProcess.spawnSync(    posixShellRunner.file,    [...posixShellRunner.args, "-s"],    { input: script, encoding: "utf8" },  );  return { status: result.status, stdout: result.stdout ?? "", stderr: result.stderr ?? "" };}; const sh = (value: string) => `'${value.replaceAll("'", "'\\''")}'`; const readField = (stdout: string, field: string) => {  const line = stdout.split("\n").find((candidate) => candidate.startsWith(`${field}:`));  if (line === undefined) throw new Error(`missing ${field} in fixture output: ${stdout}`);  return line.slice(field.length + 1).trim();}; const SERVER_ENTRY_SOURCE = 'console.log("t3code wsl runtime test server");'; const makeDistroListSpawner = (result: { readonly stdout?: string; readonly exitCode?: number }) =>  ChildProcessSpawner.make(() =>    Effect.succeed(      ChildProcessSpawner.makeHandle({        pid: ChildProcessSpawner.ProcessId(1),        exitCode:          result.exitCode === undefined            ? Effect.never            : Effect.succeed(ChildProcessSpawner.ExitCode(result.exitCode)),        isRunning: Effect.succeed(result.exitCode === undefined),        kill: () => Effect.void,        unref: Effect.succeed(Effect.void),        stdin: Sink.drain,        stdout: Stream.make(encoder.encode(result.stdout ?? "")),        stderr: Stream.empty,        all: Stream.empty,        getInputFd: () => Sink.drain,        getOutputFd: () => Stream.empty,      }),    ),  ); describe("probeWslDistros", () => {  it.effect("preserves a successful empty distro list", () =>    Effect.gen(function* () {      const distros = yield* probeWslDistros;      expect(distros).toEqual([]);    }).pipe(      Effect.provideService(        ChildProcessSpawner.ChildProcessSpawner,        makeDistroListSpawner({ stdout: "", exitCode: 0 }),      ),    ),  );   it.effect("fails when the distro-list command exits unsuccessfully", () =>    Effect.gen(function* () {      const error = yield* probeWslDistros.pipe(Effect.flip);      expect(error).toBeInstanceOf(DesktopWslDistroListError);      expect(error.message).toContain("exited with code 1");    }).pipe(      Effect.provideService(        ChildProcessSpawner.ChildProcessSpawner,        makeDistroListSpawner({ exitCode: 1 }),      ),    ),  );   it.effect("fails when the distro-list command times out", () => {    const layer = Layer.merge(      TestClock.layer(),      Layer.succeed(ChildProcessSpawner.ChildProcessSpawner, makeDistroListSpawner({})),    );    return Effect.gen(function* () {      const fiber = yield* probeWslDistros.pipe(Effect.flip, Effect.forkScoped);      yield* Effect.yieldNow;      yield* TestClock.adjust(Duration.seconds(8));      const error = yield* Fiber.join(fiber);      expect(error).toBeInstanceOf(DesktopWslDistroListError);      expect(error.message).toContain("timed out");    }).pipe(Effect.provide(layer));  });}); describe("WSL runtime cache", () => {  it.each([    [      "install",      (id: string) => buildWslRuntimeInstallScript("/runtime.tar.gz", id, "b".repeat(64)),    ],    ["prune", buildWslRuntimePruneScript],    ["invalidate", buildWslRuntimeInvalidateScript],  ] as const)("sanitizes cache ids in the %s script", (_, buildScript) => {    const runtimeId = "1.2.3/x64; touch /tmp/nope";    const script = buildScript(runtimeId);    expect(script).toContain("/1.2.3_x64__touch__tmp_nope");    expect(script).not.toContain(runtimeId);  });   it("installs through a temporary directory and only reuses valid completed caches", () => {    const script = buildWslRuntimeInstallScript(      "/mnt/c/Program Files/T3 Code/wsl-runtime.tar.gz",      "1.2.3-x64",      "b".repeat(64),    );     expect(script).toContain('runtime_parent="$HOME/.t3/wsl-runtime"');    expect(script).toContain('  [ -f "$ready_marker" ] &&');    expect(script).toContain('  [ -f "$runtime_root/apps/server/dist/bin.mjs" ] &&');    expect(script).toContain('  [ -f "$runtime_root/node_modules/node-pty/package.json" ] &&');    expect(script).toContain('    node_pty_payload_present "$runtime_root"');    expect(script).toContain("if runtime_is_ready; then");    expect(script).toContain("trap 'exit 1' HUP INT TERM");    expect(script).toContain('exec 9> "$runtime_lock"');    expect(script).toContain("flock -x 9");    expect(script).not.toContain('rm -rf "$runtime_lock"');    expect(script).toContain('mv -T "$runtime_root" "$runtime_stale"');    expect(script).toContain('mktemp -d "$runtime_parent/.1.2.3-x64.tmp.XXXXXX"');    expect(script).toContain(      "tar -xzf '/mnt/c/Program Files/T3 Code/wsl-runtime.tar.gz' -C \"$runtime_tmp\"",    );    expect(script).toContain('test -f "$runtime_tmp/apps/server/dist/bin.mjs"');    expect(script).toContain('test -f "$runtime_tmp/node_modules/node-pty/package.json"');    expect(script).toContain('mv -T "$runtime_tmp" "$runtime_root"');    expect(script).not.toContain('rm -rf "$runtime_root"');     const lockAcquired = script.indexOf("flock -x 9");    const readinessAfterLock = script.indexOf("if runtime_is_ready; then", lockAcquired + 1);    const existingRuntimeMoved = script.indexOf('mv -T "$runtime_root" "$runtime_stale"');    expect(lockAcquired).toBeGreaterThan(-1);    expect(readinessAfterLock).toBeGreaterThan(lockAcquired);    expect(existingRuntimeMoved).toBeGreaterThan(readinessAfterLock);  });   it("verifies the archive digest before extracting, and only on a cache miss", () => {    const script = buildWslRuntimeInstallScript(      "/mnt/c/Program Files/T3 Code/wsl-runtime.tar.gz",      "1.2.3-x64",      "b".repeat(64),    );     const expected = "b".repeat(64);    expect(script).toContain(      "archive_sha=$(sha256sum '/mnt/c/Program Files/T3 Code/wsl-runtime.tar.gz' | cut -d ' ' -f 1)",    );    expect(script).toContain(`if [ "$archive_sha" != '${expected}' ]; then`);     // A warm cache exits before the hash, so reuse never pays for it, and the    // mismatch check runs before anything mutates the cache.    const readyShortCircuit = script.indexOf("if runtime_is_ready; then");    const digestChecked = script.indexOf("archive_sha=$(sha256sum");    const existingRuntimeMoved = script.indexOf('mv -T "$runtime_root" "$runtime_stale"');    const extracted = script.indexOf("tar -xzf");    expect(digestChecked).toBeGreaterThan(readyShortCircuit);    expect(existingRuntimeMoved).toBeGreaterThan(digestChecked);    expect(extracted).toBeGreaterThan(digestChecked);  });   // Invalidation revokes the ready marker without stopping the backend that  // failed the probe, so the next install can find an unready tree that a live  // process is still running out of. Deleting it there unlinks node_modules  // under that process; the pruner already refuses to touch in-use caches, and  // the install path has to refuse too.  it("moves an in-use runtime aside instead of deleting it under a live backend", () => {    const script = buildWslRuntimeInstallScript(      "/mnt/c/Program Files/T3 Code/wsl-runtime.tar.gz",      "sha256-" + "c".repeat(64),      "b".repeat(64),    );     expect(script).toContain('grep -qF -- "$1/" /proc/[0-9]*/cmdline 2>/dev/null');    // No /proc means no way to tell, and guessing wrong costs a backend its    // runtime, so an unknowable answer has to count as in use.    expect(script).toContain("  [ -d /proc/1 ] || return 0");    expect(script).toContain('  if runtime_in_use "$runtime_root"; then');     // A process's cmdline keeps the pre-rename path, so the question is only    // answerable before the move.    const inUseChecked = script.indexOf('if runtime_in_use "$runtime_root"; then');    const moved = script.indexOf('mv -T "$runtime_root" "$runtime_stale"');    expect(inUseChecked).toBeGreaterThan(-1);    expect(inUseChecked).toBeLessThan(moved);     // In use: keep the tree and restart the sweep's clock, because renaming    // preserves the directory's mtime and a long-installed tree would otherwise    // already be past the age gate. Idle: delete it now, as before.    const kept = script.indexOf('touch "$runtime_stale"');    const deleted = script.indexOf('rm -rf "$runtime_stale"');    expect(kept).toBeGreaterThan(moved);    expect(deleted).toBeGreaterThan(kept);  });   it("treats a runtime whose native payload went missing as a cache miss", () => {    const script = buildWslRuntimeInstallScript(      "/mnt/c/Program Files/T3 Code/wsl-runtime.tar.gz",      "1.2.3-x64",      "b".repeat(64),    );     // A glob, not a mapped `uname -m`: this is a presence check, and the later    // native probe is what judges arch and loadability.    expect(script).toContain(      '  for candidate in "$1"/node_modules/node-pty/prebuilds/linux-*/pty.node; do',    );    // The marker the probe reads must sit beside the binary, or the runtime is    // just as unusable as one missing pty.node outright.    expect(script).toContain('    [ -f "${candidate%/*}/t3code-wsl-node-pty.json" ] || continue');     // Readiness gates the short-circuit, so a cache missing the payload    // reinstalls from the archive instead of being reused forever.    const payloadCheckDefined = script.indexOf("node_pty_payload_present() {");    const readinessDefined = script.indexOf("runtime_is_ready() {");    const readyShortCircuit = script.indexOf("if runtime_is_ready; then");    expect(payloadCheckDefined).toBeGreaterThan(-1);    expect(payloadCheckDefined).toBeLessThan(readinessDefined);    expect(readinessDefined).toBeLessThan(readyShortCircuit);  });   // A truncated or half-written bin.mjs passes every presence check the cache  // had: the file exists, node-pty still loads, and launch then picks a server  // that exits before it becomes ready — forever, because nothing ever  // reinstalls. The digest the install records is what turns that into a miss.  it("re-hashes the server entry against the digest the install recorded", () => {    const script = buildWslRuntimeInstallScript(      "/mnt/c/Program Files/T3 Code/wsl-runtime.tar.gz",      "1.2.3-x64",      "b".repeat(64),    );     expect(script).toContain(      `  sha256sum "$1/apps/server/dist/bin.mjs" 2>/dev/null | cut -d ' ' -f 1`,    );    expect(script).toContain(      '    [ "$recorded_entry_digest" = "$(runtime_server_entry_digest "$runtime_root")" ]',    );    // A runtime installed before the marker carried a digest reads as empty,    // which has to be a miss rather than a pass.    expect(script).toContain('    [ -n "$recorded_entry_digest" ] &&');    expect(script).toContain(      `printf '%s\\n' "$installed_entry_digest" > "$runtime_tmp/.t3code-wsl-runtime-ready"`,    );     // The digest is recorded after extraction and before promotion.    const extracted = script.indexOf("tar -xzf");    const digestRecorded = script.indexOf(      'installed_entry_digest=$(runtime_server_entry_digest "$runtime_tmp")',    );    const markerWritten = script.indexOf('> "$runtime_tmp/.t3code-wsl-runtime-ready"');    const promoted = script.indexOf('mv -T "$runtime_tmp" "$runtime_root"');    expect(digestRecorded).toBeGreaterThan(extracted);    expect(markerWritten).toBeGreaterThan(digestRecorded);    expect(promoted).toBeGreaterThan(markerWritten);  });   it("refuses to mark an archive without a native payload as ready", () => {    const script = buildWslRuntimeInstallScript(      "/mnt/c/Program Files/T3 Code/wsl-runtime.tar.gz",      "1.2.3-x64",      "b".repeat(64),    );     expect(script).toContain('if ! node_pty_payload_present "$runtime_tmp"; then');     // The extracted tree is rejected before the ready marker is written, so a    // defective archive falls back to the mounted tree instead of caching.    const payloadValidated = script.indexOf('node_pty_payload_present "$runtime_tmp"');    const markerWritten = script.indexOf('> "$runtime_tmp/.t3code-wsl-runtime-ready"');    const promoted = script.indexOf('mv -T "$runtime_tmp" "$runtime_root"');    expect(payloadValidated).toBeGreaterThan(-1);    expect(markerWritten).toBeGreaterThan(payloadValidated);    expect(promoted).toBeGreaterThan(payloadValidated);  });   it("parses only absolute Linux runtime paths", () => {    expect(parseWslRuntimeRoot("runtimeRoot:/home/josh/.t3/wsl-runtime/1.2.3-x64\n")).toBe(      "/home/josh/.t3/wsl-runtime/1.2.3-x64",    );    expect(parseWslRuntimeRoot("runtimeRoot:relative/path\n")).toBeNull();    expect(parseWslRuntimeRoot("noise\n")).toBeNull();  });   it("prunes completed runtimes except the current and newest previous cache", () => {    const script = buildWslRuntimePruneScript("1.2.3/x64");     expect(script).toContain('current_runtime="$runtime_parent/1.2.3_x64"');    expect(script).toContain('[ "$candidate" -nt "$previous_runtime" ]');    expect(script).toContain('[ "$candidate" != "$current_runtime" ] || continue');    expect(script).toContain('[ "$candidate" != "$previous_runtime" ] || continue');    expect(script).toContain('[ -f "$candidate/.t3code-wsl-runtime-ready" ] || continue');    expect(script).toContain('rm -rf -- "$candidate"');  });   it("never deletes a runtime another backend is running from", () => {    const script = buildWslRuntimePruneScript("1.2.3/x64");     // The running backend's argv holds `<runtime>/apps/server/dist/bin.mjs`, so    // the process itself is the lease and exiting releases it. Nothing has to be    // registered up front, which is what makes this cover backends already    // running from an older version that knows nothing about pruning.    expect(script).toContain('  grep -qF -- "$1/" /proc/[0-9]*/cmdline 2>/dev/null');    expect(script).toContain('  ! runtime_in_use "$candidate" || continue');     // Without visible processes the retention rules cannot tell a live cache    // from an abandoned one, so the sweep is skipped rather than guessed at.    expect(script).toContain("[ -d /proc/1 ] || exit 0");     // The guard has to gate the delete, not just exist.    const inUseChecked = script.indexOf('! runtime_in_use "$candidate"');    const removed = script.indexOf('rm -rf -- "$candidate"');    expect(inUseChecked).toBeGreaterThan(-1);    expect(removed).toBeGreaterThan(inUseChecked);  });   it("sweeps orphaned install scratch directories the ready-marker loops cannot see", () => {    const script = buildWslRuntimePruneScript("1.2.3/x64");     // Dot-prefixed, so `"$runtime_parent"/*` never matches them, and they carry    // no ready marker either; without this pass a killed install leaks forever.    expect(script).toContain(      'for scratch in "$runtime_parent"/.*.tmp.* "$runtime_parent"/.*.stale.*; do',    );    // Age guard: a scratch directory younger than this belongs to a live install.    expect(script).toContain('find "$scratch" -maxdepth 0 -mmin +120');  });   it("invalidates a cache by dropping its ready marker, not the tree", () => {    const script = buildWslRuntimeInvalidateScript("1.2.3/x64");     // Readiness is a presence check, so a tree whose pty.node is present but    // unloadable stays ready forever unless the probe can revoke the marker.    expect(script).toContain('rm -f "$HOME/.t3/wsl-runtime/1.2.3_x64/.t3code-wsl-runtime-ready"');    // Deleting the tree here would pull it out from under any backend still    // running from it; the next install moves an unready root aside instead.    expect(script).not.toContain("rm -rf");  });}); // Reading the generated script proves what it says, not what it does. A cache// whose bin.mjs was truncated satisfied every assertion above and still got// reused, so these run the real script against a real archive in a throwaway// HOME and check the outcome.describe.skipIf(posixShellRunner === null)("WSL runtime install script (executed)", () => {  const fixtures: Array<string> = [];   afterAll(() => {    for (const work of fixtures) runShell(`set -eu\nrm -rf ${sh(work)}`);    fixtures.length = 0;  });   const createFixture = () => {    const result = runShell(      [        "set -eu",        "work=$(mktemp -d)",        'stage="$work/stage"',        'mkdir -p "$stage/apps/server/dist" "$stage/node_modules/node-pty/prebuilds/linux-x64" "$work/home"',        `printf '%s' ${sh(SERVER_ENTRY_SOURCE)} > "$stage/apps/server/dist/bin.mjs"`,        `printf '%s' '{"name":"node-pty","version":"0.0.0-test"}' > "$stage/node_modules/node-pty/package.json"`,        `printf '%s' 'pty-native-payload' > "$stage/node_modules/node-pty/prebuilds/linux-x64/pty.node"`,        `printf '%s' '{"arch":"x64"}' > "$stage/node_modules/node-pty/prebuilds/linux-x64/t3code-wsl-node-pty.json"`,        `tar -czf "$work/wsl-runtime.tar.gz" -C "$stage" apps/server/dist node_modules`,        `printf 'work:%s\\n' "$work"`,        `printf 'archiveSha:%s\\n' "$(sha256sum "$work/wsl-runtime.tar.gz" | cut -d ' ' -f 1)"`,      ].join("\n"),    );    expect(result.status, result.stderr).toBe(0);     const work = readField(result.stdout, "work");    fixtures.push(work);    const archivePath = `${work}/wsl-runtime.tar.gz`;    const archiveSha = readField(result.stdout, "archiveSha");    const runtimeId = `sha256-${archiveSha}`;    // The script reads $HOME, and WSL does not inherit the parent process's    // environment, so the home override rides in the script itself.    const installScript = (archive = archivePath, sha = archiveSha) =>      [        `HOME=${sh(`${work}/home`)}`,        "export HOME",        buildWslRuntimeInstallScript(archive, runtimeId, sha),      ].join("\n");    return {      work,      archivePath,      archiveSha,      runtimeId,      runtimeParent: `${work}/home/.t3/wsl-runtime`,      runtimeRoot: `${work}/home/.t3/wsl-runtime/${runtimeId}`,      serverEntry: `${work}/home/.t3/wsl-runtime/${runtimeId}/apps/server/dist/bin.mjs`,      installScript,      install: (archive?: string, sha?: string) => runShell(installScript(archive, sha)),    };  };   it("reuses a warm cache without touching the archive", () => {    const fixture = createFixture();    expect(fixture.install().status).toBe(0);    // Deleting the archive is how the test tells reuse apart from a silent    // reinstall: only the warm path can succeed without it.    expect(runShell(`set -eu\nrm ${sh(fixture.archivePath)}`).status).toBe(0);     const warm = fixture.install();     expect(warm.status, warm.stderr).toBe(0);    expect(parseWslRuntimeRoot(warm.stdout)).toBe(fixture.runtimeRoot);  });   it("reinstalls a cache whose server entry was truncated", () => {    const fixture = createFixture();    expect(fixture.install().status).toBe(0);    expect(runShell(`set -eu\n: > ${sh(fixture.serverEntry)}`).status).toBe(0);     const repaired = fixture.install();     expect(repaired.status, repaired.stderr).toBe(0);    expect(parseWslRuntimeRoot(repaired.stdout)).toBe(fixture.runtimeRoot);    const restored = runShell(`set -eu\ncat ${sh(fixture.serverEntry)}`);    expect(restored.stdout).toBe(SERVER_ENTRY_SOURCE);  });   it("falls back instead of launching a corrupted cache it cannot reinstall", () => {    const fixture = createFixture();    expect(fixture.install().status).toBe(0);    expect(runShell(`set -eu\n: > ${sh(fixture.serverEntry)}`).status).toBe(0);    expect(runShell(`set -eu\nrm ${sh(fixture.archivePath)}`).status).toBe(0);     const broken = fixture.install();     // Non-zero with no runtimeRoot is what sends the backend to the mounted    // server tree. Exiting 0 here is the bug: launch would pick the zero-byte    // server, fail to become ready, and do it again on every restart.    expect(broken.status).not.toBe(0);    expect(parseWslRuntimeRoot(broken.stdout)).toBeNull();  });   it("extracts once when two installs race for the same cache", () => {    const fixture = createFixture();    // A tar shim counts extractions and holds the critical section open long    // enough that the second install is certain to arrive while the first is    // still inside it. One extraction is the answer either way the runs    // interleave: whoever waits for the lock re-checks readiness before    // spending an extract, so a broken lock shows up as two.    const raced = runShell(

Lines 1–500 of 849.