import fs from "node:fs"; import path from "node:path"; import test from "node:test"; import assert from "node:assert/strict"; import { collectReviewContext, resolveReviewTarget } from "../plugins/codex/scripts/lib/git.mjs"; import { initGitRepo, makeTempDir, run } from "./helpers.mjs"; test("resolveReviewTarget prefers working tree when repo is dirty", () => { const cwd = makeTempDir(); initGitRepo(cwd); fs.writeFileSync(path.join(cwd, "app.js"), "console.log('v1');\n"); run("git", ["add", "app.js"], { cwd }); run("git", ["commit", "-m", "init"], { cwd }); fs.writeFileSync(path.join(cwd, "app.js"), "console.log('v2');\n"); const target = resolveReviewTarget(cwd, {}); assert.equal(target.mode, "working-tree"); }); test("resolveReviewTarget falls back to branch diff when repo is clean", () => { const cwd = makeTempDir(); initGitRepo(cwd); fs.writeFileSync(path.join(cwd, "app.js"), "console.log('v1');\n"); run("git", ["add", "app.js"], { cwd }); run("git", ["commit", "-m", "init"], { cwd }); run("git", ["checkout", "-b", "feature/test"], { cwd }); fs.writeFileSync(path.join(cwd, "app.js"), "console.log('v2');\n"); run("git", ["add", "app.js"], { cwd }); run("git", ["commit", "-m", "change"], { cwd }); const target = resolveReviewTarget(cwd, {}); const context = collectReviewContext(cwd, target); assert.equal(target.mode, "branch"); assert.match(target.label, /main/); assert.match(context.content, /Branch Diff/); }); test("resolveReviewTarget honors explicit base overrides", () => { const cwd = makeTempDir(); initGitRepo(cwd); fs.writeFileSync(path.join(cwd, "app.js"), "console.log('v1');\n"); run("git", ["add", "app.js"], { cwd }); run("git", ["commit", "-m", "init"], { cwd }); run("git", ["checkout", "-b", "feature/test"], { cwd }); fs.writeFileSync(path.join(cwd, "app.js"), "console.log('v2');\n"); run("git", ["add", "app.js"], { cwd }); run("git", ["commit", "-m", "change"], { cwd }); const target = resolveReviewTarget(cwd, { base: "main" }); assert.equal(target.mode, "branch"); assert.equal(target.baseRef, "main"); }); test("resolveReviewTarget requires an explicit base when no default branch can be inferred", () => { const cwd = makeTempDir(); initGitRepo(cwd); fs.writeFileSync(path.join(cwd, "app.js"), "console.log('v1');\n"); run("git", ["add", "app.js"], { cwd }); run("git", ["commit", "-m", "init"], { cwd }); run("git", ["branch", "-m", "feature-only"], { cwd }); assert.throws( () => resolveReviewTarget(cwd, {}), /Unable to detect the repository default branch\. Pass --base or use --scope working-tree\./ ); }); test("collectReviewContext skips untracked directories in working tree review", () => { const cwd = makeTempDir(); initGitRepo(cwd); fs.writeFileSync(path.join(cwd, "app.js"), "console.log('v1');\n"); run("git", ["add", "app.js"], { cwd }); run("git", ["commit", "-m", "init"], { cwd }); const nestedRepoDir = path.join(cwd, ".claude", "worktrees", "agent-test"); fs.mkdirSync(nestedRepoDir, { recursive: true }); initGitRepo(nestedRepoDir); const target = resolveReviewTarget(cwd, { scope: "working-tree" }); const context = collectReviewContext(cwd, target); assert.match(context.content, /### \.claude\/worktrees\/agent-test\/\n\(skipped: directory\)/); }); test("collectReviewContext skips broken untracked symlinks instead of crashing", () => { const cwd = makeTempDir(); initGitRepo(cwd); fs.writeFileSync(path.join(cwd, "app.js"), "console.log('v1');\n"); run("git", ["add", "app.js"], { cwd }); run("git", ["commit", "-m", "init"], { cwd }); fs.symlinkSync("missing-target", path.join(cwd, "broken-link")); const target = resolveReviewTarget(cwd, {}); const context = collectReviewContext(cwd, target); assert.equal(target.mode, "working-tree"); assert.match(context.content, /### broken-link/); assert.match(context.content, /skipped: broken symlink or unreadable file/i); });