/** * @file pty-run.test.js * @description Unit tests for the tmux-backed run lifecycle. Injects a fake * tmux exec implementation (via tmux.js's test seam) so no real tmux binary * is invoked. * @author Nguyễn Ngọc Trí Vĩ */ const { describe, it, beforeEach, before, after } = require("node:test"); const assert = require("node:assert/strict"); const path = require("node:path"); const fs = require("node:fs"); const os = require("node:os"); const TMP = fs.mkdtempSync(path.join(os.tmpdir(), "pty-run-test-")); process.env.DASHBOARD_DB_PATH = path.join(TMP, "dashboard.db"); const tmux = require("../lib/tmux"); const pty = require("../lib/pty-run"); describe("pty-run", () => { after(() => { try { fs.rmSync(TMP, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 }); } catch { /* best-effort */ } }); beforeEach(() => { tmux.__reset(); }); it("spawnRun creates a new tmux session named ccam-lane- when none exists", () => { const calls = []; tmux.__setExecImpl((args) => { calls.push(args); if (args[0] === "has-session") { const e = new Error("no such session"); e.status = 1; throw e; } return ""; }); const handle = pty.spawnRun({ laneId: 42, cwd: "/tmp/repo", model: "opus" }); assert.equal(handle.id, "ccam-lane-42"); const newSessionCall = calls.find((c) => c[0] === "new-session"); assert.ok(newSessionCall, "expected a new-session call"); assert.deepEqual(newSessionCall.slice(0, 6), [ "new-session", "-d", "-s", "ccam-lane-42", "-c", "/tmp/repo", ]); assert.ok(newSessionCall.includes("claude")); assert.ok(newSessionCall.includes("--model")); assert.ok(newSessionCall.includes("opus")); }); it("spawnRun is a no-op (adopts) when the tmux session already exists", () => { const calls = []; tmux.__setExecImpl((args) => { calls.push(args); return ""; // has-session succeeds → already running }); const handle = pty.spawnRun({ laneId: 7, cwd: "/tmp/repo" }); assert.equal(handle.id, "ccam-lane-7"); assert.ok(!calls.some((c) => c[0] === "new-session"), "must not create a duplicate session"); }); it("spawnRun with resumeSessionId passes --resume in argv", () => { let newSessionArgv = null; tmux.__setExecImpl((args) => { if (args[0] === "has-session") { const e = new Error("gone"); e.status = 1; throw e; } if (args[0] === "new-session") newSessionArgv = args; return ""; }); pty.spawnRun({ laneId: 1, cwd: "/tmp/repo", resumeSessionId: "abc12345" }); assert.ok(newSessionArgv.includes("--resume")); assert.ok(newSessionArgv.includes("abc12345")); }); it("spawnRun appends a positional initial prompt after argv flags", () => { let newSessionArgv = null; tmux.__setExecImpl((args) => { if (args[0] === "has-session") { const e = new Error("gone"); e.status = 1; throw e; } if (args[0] === "new-session") newSessionArgv = args; return ""; }); pty.spawnRun({ laneId: 3, cwd: "/tmp/repo", initialPrompt: "fix the bug" }); assert.equal(newSessionArgv[newSessionArgv.length - 1], "fix the bug"); }); it("killRun calls tmux kill-session with the run id", () => { const calls = []; tmux.__setExecImpl((args) => { calls.push(args); return ""; }); assert.equal(pty.killRun("ccam-lane-5"), true); assert.ok(calls.some((c) => c[0] === "kill-session" && c[2] === "ccam-lane-5")); }); it("listRuns reflects live tmux-session state, not cached memory", () => { tmux.__setExecImpl((args) => { if (args[0] === "list-sessions") return "ccam-lane-1\nccam-lane-2\n"; return ""; }); const first = pty.listRuns(); assert.deepEqual(first.map((r) => r.id).sort(), ["ccam-lane-1", "ccam-lane-2"]); // Session killed out-of-band (not through killRun) — next list() call // must self-correct, proving state is computed, not stored. tmux.__setExecImpl((args) => { if (args[0] === "list-sessions") return "ccam-lane-1\n"; return ""; }); const second = pty.listRuns(); assert.deepEqual( second.map((r) => r.id), ["ccam-lane-1"] ); }); it("laneIdFromRunId parses the numeric lane id back out", () => { assert.equal(pty.laneIdFromRunId("ccam-lane-42"), 42); assert.equal(pty.laneIdFromRunId("not-a-run-id"), null); }); it("getRun returns the recorded prompt for a live run", () => { let sessionExists = false; tmux.__setExecImpl((args) => { if (args[0] === "has-session") { if (sessionExists) { return ""; // session exists } // Session doesn't exist yet const e = new Error("no such session"); e.status = 1; throw e; } if (args[0] === "new-session") { sessionExists = true; // Mark session as created } return ""; }); const handle = pty.spawnRun({ laneId: 99, cwd: "/tmp/test", initialPrompt: "the live prompt", }); const retrieved = pty.getRun(handle.id); assert.equal(retrieved.id, "ccam-lane-99"); assert.equal(retrieved.promptPreview, "the live prompt"); assert.equal(retrieved.status, "running"); }); });