import { mkdtempSync, rmSync, writeFileSync } from "node:fs"; import os from "node:os"; import path from "node:path"; import { afterEach, describe, expect, it } from "vitest"; import { readPersistedDevServerStatus, toDevServerHealthStatus } from "../dev-server-status.js"; const tempDirs = []; function createTempStatusFile(payload: unknown) { const dir = mkdtempSync(path.join(os.tmpdir(), "paperclip-dev-status-")); tempDirs.push(dir); const filePath = path.join(dir, "dev-server-status.json"); writeFileSync(filePath, `${JSON.stringify(payload)}\n`, "utf8"); return filePath; } afterEach(() => { for (const dir of tempDirs.splice(0)) { rmSync(dir, { recursive: true, force: true }); } }); describe("dev server status helpers", () => { it("reads and normalizes persisted supervisor state", () => { const filePath = createTempStatusFile({ dirty: true, lastChangedAt: "2026-03-20T12:00:00.000Z", changedPathCount: 4, changedPathsSample: ["server/src/app.ts", "packages/shared/src/index.ts"], pendingMigrations: ["0040_restart_banner.sql"], lastRestartAt: "2026-03-20T11:30:00.000Z", }); expect(readPersistedDevServerStatus({ PAPERCLIP_DEV_SERVER_STATUS_FILE: filePath })).toEqual({ dirty: true, lastChangedAt: "2026-03-20T12:00:00.000Z", changedPathCount: 4, changedPathsSample: ["server/src/app.ts", "packages/shared/src/index.ts"], pendingMigrations: ["0040_restart_banner.sql"], lastRestartAt: "2026-03-20T11:30:00.000Z", }); }); it("derives waiting-for-idle health state", () => { const health = toDevServerHealthStatus( { dirty: true, lastChangedAt: "2026-03-20T12:00:00.000Z", changedPathCount: 2, changedPathsSample: ["server/src/app.ts"], pendingMigrations: [], lastRestartAt: "2026-03-20T11:30:00.000Z", }, { autoRestartEnabled: true, activeRunCount: 3 }, ); expect(health).toMatchObject({ enabled: true, restartRequired: true, reason: "backend_changes", autoRestartEnabled: true, activeRunCount: 3, waitingForIdle: true, }); }); });