///
import { describe, it, expect, beforeEach } from "bun:test";
import * as fs from "node:fs";
import * as os from "node:os";
import * as path from "node:path";
import { ProgressTracker } from "../src/progress";
/**
* Regression test: two concurrent loops (different PRDs) each run their own
* ProgressTracker. Each instance snapshots the whole state at construction;
* a save() that writes that stale snapshot verbatim would revert the OTHER
* loop's task status changes — tasks wrongly back to "pending" while their
* worktrees carry real work, stranding it on the next resume.
*/
let root: string;
beforeEach(() => {
root = fs.mkdtempSync(path.join(os.tmpdir(), "ralpi-prog-test-"));
});
function prdA(projectDir: string): ProgressTracker {
return new ProgressTracker(
projectDir,
path.join(projectDir, "tasks/a/README.md"),
);
}
function prdB(projectDir: string): ProgressTracker {
return new ProgressTracker(
projectDir,
path.join(projectDir, "tasks/b/README.md"),
);
}
/** Read the on-disk progress state; the file is written by the tracker, so
* a parse failure is a test bug worth surfacing. */
function readState(): Record {
const raw = fs.readFileSync(
path.join(root, ".ralpi", "progress.json"),
"utf-8",
);
try {
return JSON.parse(raw) as Record;
} catch {
throw new Error(`malformed progress.json:\n${raw.slice(0, 200)}`);
}
}
describe("ProgressTracker multi-PRD save isolation", () => {
it("does not clobber another PRD's task status on save", () => {
const a = prdA(root);
const b = prdB(root);
expect(a.getKey()).not.toBe(b.getKey());
// Loop A marks its task in_progress.
a.markInProgress("01");
expect(a.getTaskStatus("01")).toBe("in_progress");
// Loop B (stale snapshot from before A's update) marks ITS task.
b.markInProgress("02");
// The on-disk state must show BOTH updates.
const raw = readState();
expect(raw.prds[a.getKey()].tasks["01"].status).toBe("in_progress");
expect(raw.prds[b.getKey()].tasks["02"].status).toBe("in_progress");
});
it("preserves other PRD completions when this PRD saves", () => {
const a = prdA(root);
const b = prdB(root);
a.markCompleted("01", 1000);
b.markInProgress("02");
// A completes another task later — A's save must not revert B.
a.markCompleted("03", 500);
const raw = readState();
expect(raw.prds[a.getKey()].tasks["01"].status).toBe("completed");
expect(raw.prds[a.getKey()].tasks["03"].status).toBe("completed");
expect(raw.prds[b.getKey()].tasks["02"].status).toBe("in_progress");
});
});