import { mkdtempSync, readFileSync, rmSync, statSync, writeFileSync, } from "node:fs"; import { tmpdir } from "node:os"; import path from "node:path"; import { afterEach, describe, expect, it } from "vitest"; import { loadRoutingState, routingStatePath, saveRoutingState, } from "../src/state.ts"; let root: string | undefined; afterEach(() => { if (root) rmSync(root, { recursive: true, force: true }); root = undefined; }); describe("gratis routing state file", () => { it("lives under the user state dir, honoring XDG_STATE_HOME", () => { expect(routingStatePath("linux", {}, "/home/u")).toBe( "/home/u/.local/state/pi/gratis/routing.json", ); expect(routingStatePath("linux", { XDG_STATE_HOME: "/x" }, "/home/u")).toBe( "/x/pi/gratis/routing.json", ); expect(routingStatePath("darwin", {}, "/Users/u")).toBe( "/Users/u/Library/Application Support/pi/gratis/routing.json", ); expect( routingStatePath("win32", { LOCALAPPDATA: "C:\\Local" }, "C:\\Users\\u"), ).toBe("C:\\Local\\pi\\gratis\\routing.json"); }); it("saves atomically with owner-only permissions and loads it back", async () => { root = mkdtempSync(path.join(tmpdir(), "gratis-state-")); const file = path.join(root, "pi", "gratis", "routing.json"); await saveRoutingState(file, { version: 1, models: {}, backends: {} }); expect(await loadRoutingState(file)).toEqual({ version: 1, models: {}, backends: {}, }); expect(JSON.parse(readFileSync(file, "utf8")).version).toBe(1); if (process.platform !== "win32") { expect(statSync(file).mode & 0o777).toBe(0o600); expect(statSync(path.dirname(file)).mode & 0o777).toBe(0o700); } }); it("treats a missing or corrupt file as no state", async () => { root = mkdtempSync(path.join(tmpdir(), "gratis-state-")); const file = path.join(root, "routing.json"); expect(await loadRoutingState(file)).toBeUndefined(); writeFileSync(file, "{ not json"); expect(await loadRoutingState(file)).toBeUndefined(); }); });