import assert from "node:assert/strict"; import { mkdtemp, mkdir, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { test } from "node:test"; import type { ReviewComment } from "./comments.js"; import type { AgentResponse } from "./conversations.js"; import { reconcileReviewReport } from "./review-report.js"; import { emptyReviewState, loadReviewStateFrom, saveReviewStateTo, statePathFor, type ReviewState, } from "./review-state.js"; const sampleComment: ReviewComment = { id: "u1", author: "user", sha: "abc1234", file: "src/a.ts", side: "new", line: 11, body: "fix this", status: "resolved", }; const sampleResponse: AgentResponse = { id: "response-u1-1", commentId: "u1", summary: "Updated the implementation.", changedLocations: [{ file: "src/a.ts", line: 11, before: "old", after: "new" }], checks: [{ command: "npm test", status: "passed", summary: "all passed" }], createdAt: "2026-07-30T12:00:00.000Z", }; test("statePathFor nests under /code-eye", () => { assert.equal(statePathFor("/repo/.git"), join("/repo/.git", "code-eye", "state.json")); }); test("save/load round-trips comments and lastReviewedHead", async () => { const dir = await mkdtemp(join(tmpdir(), "code-eye-state-")); const path = statePathFor(dir); const lastReport = reconcileReviewReport( { mode: "quick", summary: "No issues.", findings: [], checks: [{ command: "npm test", status: "passed", summary: "all passed" }], coverage: { reviewedFiles: ["src/a.ts"], skipped: [] }, }, [], "2026-07-26T10:00:00.000Z", ); const state: ReviewState = { version: 3, comments: [sampleComment], agentResponses: [sampleResponse], lastReviewedHead: "deadbeef42", lastReport, }; await saveReviewStateTo(path, state); const loaded = await loadReviewStateFrom(path); assert.deepEqual(loaded, state); }); test("load tolerates missing file and corrupt JSON", async () => { const dir = await mkdtemp(join(tmpdir(), "code-eye-state-")); assert.deepEqual(await loadReviewStateFrom(join(dir, "nope.json")), emptyReviewState()); const bad = join(dir, "bad.json"); await writeFile(bad, "{ not json", "utf8"); assert.deepEqual(await loadReviewStateFrom(bad), emptyReviewState()); const wrongShape = join(dir, "wrong.json"); await writeFile(wrongShape, JSON.stringify({ comments: "nope", lastReviewedHead: 42 }), "utf8"); assert.deepEqual(await loadReviewStateFrom(wrongShape), emptyReviewState()); const brokenReport = join(dir, "broken-report.json"); await writeFile( brokenReport, JSON.stringify({ version: 2, comments: [], lastReport: { findings: "nope" } }), "utf8", ); assert.deepEqual(await loadReviewStateFrom(brokenReport), emptyReviewState()); }); test("load migrates version 1 and 2 state to the current review ledger", async () => { const dir = await mkdtemp(join(tmpdir(), "code-eye-state-")); for (const version of [1, 2]) { const path = join(dir, `v${version}.json`); await writeFile( path, JSON.stringify({ version, comments: [sampleComment], lastReviewedHead: "deadbeef42" }), "utf8", ); const loaded = await loadReviewStateFrom(path); assert.equal(Number(loaded.version), 3); assert.deepEqual(loaded.comments, [sampleComment]); assert.deepEqual(loaded.agentResponses, []); assert.equal(loaded.lastReviewedHead, "deadbeef42"); assert.equal(loaded.lastReport, undefined); } }); test("load rejects agent response locations with non-positive line numbers", async () => { const dir = await mkdtemp(join(tmpdir(), "code-eye-state-")); const path = join(dir, "state.json"); await writeFile( path, JSON.stringify({ version: 3, comments: [sampleComment], agentResponses: [ { ...sampleResponse, changedLocations: [{ file: "src/a.ts", line: 0, after: "const fixed = true;" }], }, ], }), "utf8", ); assert.deepEqual((await loadReviewStateFrom(path)).agentResponses, []); }); test("save creates the code-eye directory and replaces state atomically", async () => { const dir = await mkdtemp(join(tmpdir(), "code-eye-state-")); const path = statePathFor(join(dir, "nested", "gitdir")); await saveReviewStateTo(path, { version: 3, comments: [], agentResponses: [] }); await saveReviewStateTo(path, { version: 3, comments: [sampleComment], agentResponses: [sampleResponse] }); const loaded = await loadReviewStateFrom(path); assert.equal(loaded.comments.length, 1); assert.equal(loaded.agentResponses.length, 1); assert.equal(loaded.lastReviewedHead, undefined); });