#!/usr/bin/env node import { chmodSync, mkdirSync, mkdtempSync, readFileSync, rmSync, statSync, symlinkSync, writeFileSync } from "node:fs"; import os from "node:os"; import path from "node:path"; import { canonicalProjectPath, defaultMachineDataBaseDir, machineDataBaseDir, machineDataFile, machineProjectDataDir, machineProjectKey, machineSettingsFile, readMachineSettings, writeMachineSettings, } from "../lib/storage.ts"; import { pythonCandidates } from "../lib/python.ts"; function assert(condition, message) { if (!condition) throw new Error(message); } const previousDataDir = process.env.MACHINE_DATA_DIR; const tmp = mkdtempSync(path.join(os.tmpdir(), "machine-storage-")); const cwd = path.join(tmp, "Example Project"); const otherCwd = path.join(tmp, "other", "Example Project"); const dataBase = path.join(tmp, "machine-state"); mkdirSync(cwd, { recursive: true }); mkdirSync(otherCwd, { recursive: true }); process.env.MACHINE_DATA_DIR = dataBase; try { assert(machineDataBaseDir(cwd) === dataBase, "MACHINE_DATA_DIR should select the data base directory"); assert(defaultMachineDataBaseDir("darwin", {}, "/Users/example") === "/Users/example/.machine", "all platforms should use ~/.machine"); assert(defaultMachineDataBaseDir("linux", { XDG_DATA_HOME: "/xdg" }, "/home/example") === "/home/example/.machine", "Linux should use ~/.machine even when XDG_DATA_HOME is set"); assert(defaultMachineDataBaseDir("linux", {}, "/home/example") === "/home/example/.machine", "Linux should use ~/.machine"); assert(defaultMachineDataBaseDir("win32", { LOCALAPPDATA: "C:\\Local" }, "C:\\Users\\example") === path.join("C:\\Users\\example", ".machine"), "Windows should use ~/.machine"); assert(pythonCandidates("/project", "/package", "linux")[0] === "/project/.venv/bin/python", "POSIX should use .venv/bin/python"); assert(pythonCandidates("C:\\project", "C:\\package", "win32")[0] === "C:\\project\\.venv\\Scripts\\python.exe", "Windows should use .venv/Scripts/python.exe"); assert(machineProjectDataDir(cwd).startsWith(`${dataBase}${path.sep}`), "project data should live under the configured base"); assert(!machineProjectDataDir(cwd).startsWith(`${cwd}${path.sep}`), "project data should stay outside the working tree by default"); assert(machineProjectKey(cwd) !== machineProjectKey(otherCwd), "same-name projects should have distinct keys"); assert(machineProjectKey(canonicalProjectPath(cwd)) === machineProjectKey(cwd), "canonicalizing twice should not change the project key"); const linked = path.join(tmp, "linked-to-project"); try { symlinkSync(cwd, linked); assert(machineProjectKey(linked) === machineProjectKey(cwd), "a symlinked path to the project should share the same store"); assert(machineDataFile(linked, "train") === machineDataFile(cwd, "train"), "symlinked paths should resolve to the same data files"); } catch { // Symlink creation can require elevated privileges on Windows; skip. } finally { rmSync(linked, { force: true }); } assert(machineDataFile(cwd, "observed").endsWith(`${path.sep}observed.jsonl`), "observed rows need a distinct file"); assert(readMachineSettings(cwd).autoTrace === false, "automatic observation should default off"); mkdirSync(machineProjectDataDir(cwd), { recursive: true, mode: 0o755 }); if (process.platform !== "win32") chmodSync(machineProjectDataDir(cwd), 0o755); writeMachineSettings(cwd, { autoTrace: true }); assert(readMachineSettings(cwd).autoTrace === true, "automatic observation setting should persist"); assert(JSON.parse(readFileSync(machineSettingsFile(cwd), "utf8")).autoTrace === true, "settings file should contain the persisted value"); if (process.platform !== "win32") { assert((statSync(machineProjectDataDir(cwd)).mode & 0o777) === 0o700, "project data directory should be owner-only"); assert((statSync(machineSettingsFile(cwd)).mode & 0o777) === 0o600, "settings file should be owner-only"); } writeFileSync(machineSettingsFile(cwd), "not json\n"); assert(readMachineSettings(cwd).autoTrace === false, "malformed settings should fail closed"); } finally { if (previousDataDir === undefined) delete process.env.MACHINE_DATA_DIR; else process.env.MACHINE_DATA_DIR = previousDataDir; } console.log("storage test passed");