Luigit
repositories / pi-ext

pi-ext

bugabingas pi extensions

owned by admin

extensions/strata/__tests__/github.test.ts

Raw
import { spawn } from "node:child_process";
import { existsSync, watch } from "node:fs";
import { appendFile, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import path from "node:path";
import { aroundEach, describe, expect, it, vi } from "vitest";
import { cleanGitEnvironment } from "../git.js";
import {
	isGitHubReviewCurrent as isCurrent,
	openGitHubReview as openReview,
	refreshGitHubReview as refreshReview,
} from "../github.js";
import {
	cleanupNative,
	git,
	nativeOperation,
	nativeTest,
	testSignal,
	withFakeExecutable,
	withFakeGh,
} from "./fixture.js";

// Always exercise the native adapter, with cancellation bounded by the test.
const openGitHubReview = (
	...[cwd, id, confirm, signal]: Parameters<typeof openReview>
) => nativeOperation(() => openReview(cwd, id, confirm, testSignal(signal)));
const isGitHubReviewCurrent = (
	...[cwd, review, signal]: Parameters<typeof isCurrent>
) => nativeOperation(() => isCurrent(cwd, review, testSignal(signal)));
const refreshGitHubReview = (
	...[cwd, review, signal]: Parameters<typeof refreshReview>
) => nativeOperation(() => refreshReview(cwd, review, testSignal(signal)));

aroundEach(async (run, context) => {
	try {
		await nativeTest(run, context);
	} finally {
		vi.restoreAllMocks();
	}
});

async function cancelAfterMarker(
	marker: string,
	message: string,
	start: (signal: AbortSignal) => Promise<unknown>,
): Promise<void> {
	const controller = new AbortController();
	const watcher = watch(path.dirname(marker));
	const started = new Promise<void>((resolve, reject) => {
		watcher.on("change", () => {
			if (existsSync(marker)) resolve();
		});
		watcher.on("error", reject);
	});
	const opening = start(controller.signal);
	try {
		await Promise.race([
			started,
			opening.then(() => {
				throw new Error("PR opening completed before the cancellation signal");
			}),
		]);
		expect(existsSync(marker)).toBe(true);
		const pid = Number(await readFile(marker, "utf8"));
		expect(Number.isInteger(pid) && pid > 0).toBe(true);
		controller.abort(new Error(message));
		await expect(opening).rejects.toThrow(message);
		expect(() => process.kill(pid, 0)).toThrow(/ESRCH/);
	} finally {
		controller.abort();
		watcher.close();
		await opening.catch(() => {});
	}
}

function gitInput(
	cwd: string,
	input: string,
	...args: string[]
): Promise<string> {
	return nativeOperation(
		() =>
			new Promise((resolve, reject) => {
				const signal = testSignal();
				signal.throwIfAborted();
				const child = spawn("git", args, {
					cwd,
					env: cleanGitEnvironment(),
					stdio: ["pipe", "pipe", "pipe"],
				});
				const abort = () => {
					child.kill();
				};
				signal.addEventListener("abort", abort, { once: true });
				const stdout: Buffer[] = [];
				const stderr: Buffer[] = [];
				child.stdout.on("data", (chunk: Buffer) => stdout.push(chunk));
				child.stderr.on("data", (chunk: Buffer) => stderr.push(chunk));
				let failure: Error | undefined;
				child.once("error", (error) => {
					failure = error;
				});
				child.stdin.once("error", (error) => {
					failure = error;
				});
				child.once("close", (code) => {
					signal.removeEventListener("abort", abort);
					if (signal.aborted) reject(signal.reason);
					else if (failure) reject(failure);
					else if (code === 0)
						resolve(Buffer.concat(stdout).toString("utf8").trim());
					else reject(new Error(Buffer.concat(stderr).toString("utf8").trim()));
				});
				child.stdin.end(input);
			}),
	);
}

async function repository() {
	return nativeOperation(async () => {
		testSignal().throwIfAborted();
		const root = await mkdtemp(path.join(tmpdir(), "strata-github-"));
		cleanupNative(() => rm(root, { recursive: true, force: true }));
		await git(root, "init", "--quiet", "--initial-branch=main");
		await appendFile(
			path.join(root, ".git", "config"),
			"[user]\n\tname = Strata Test\n\temail = strata@example.invalid\n",
		);
		await writeFile(path.join(root, "change.ts"), "export const value = 1;\n");
		await git(root, "add", "change.ts");
		await git(root, "commit", "--quiet", "-m", "base");
		const base = await git(root, "rev-parse", "HEAD");
		await git(root, "checkout", "--quiet", "-b", "feature");
		await writeFile(path.join(root, "change.ts"), "export const value = 2;\n");
		await git(root, "commit", "--quiet", "-am", "feature");
		const head = await git(root, "rev-parse", "HEAD");
		await git(root, "checkout", "--quiet", "main");
		await git(
			root,
			"remote",
			"add",
			"origin",
			"https://github.com/acme/project.git",
		);
		return { root, base, head };
	});
}

function fakeGh(
	metadata: Record<string, unknown>,
	pages: Record<string, unknown[]> = {},
) {
	return `
const metadata = ${JSON.stringify(metadata)};
const pages = ${JSON.stringify(pages)};
if (args[0] === "pr" && args[1] === "view") process.stdout.write(JSON.stringify(metadata));
else if (args[0] === "api") {
  if (!args.includes("GET") || args.some((arg) => ["POST", "PATCH", "PUT", "DELETE"].includes(arg))) process.exitCode = 91;
  else {
    const endpoint = args.at(-1);
    const key = Object.keys(pages).find((candidate) => endpoint.startsWith(candidate));
    process.stdout.write(JSON.stringify(key ? pages[key] : []));
  }
} else process.exitCode = 92;
`;
}

function metadata(
	base: string,
	head: string,
	overrides: Record<string, unknown> = {},
) {
	return {
		number: 7,
		url: "https://github.com/acme/project/pull/7",
		title: "Bound retries",
		body: "Keep failures visible.",
		author: { login: "review-author" },
		baseRefName: "main",
		baseRefOid: base,
		headRefName: "feature",
		headRefOid: head,
		headRepository: { name: "project", nameWithOwner: "acme/project" },
		headRepositoryOwner: { login: "acme" },
		...overrides,
	};
}

describe.sequential("GitHub forge adapter", () => {
	it("loads all review comment kinds through read-only API calls and checks out the pinned existing branch", async () => {
		const { root, base, head } = await repository();
		const pages = {
			"repos/acme/project/issues/7/comments": [
				{
					id: 11,
					html_url: "https://github.com/acme/project/pull/7#issuecomment-11",
					user: { login: "discussant" },
					body: "Discussion",
				},
			],
			"repos/acme/project/pulls/7/reviews": [
				{
					id: 12,
					html_url:
						"https://github.com/acme/project/pull/7#pullrequestreview-12",
					user: { login: "reviewer" },
					body: "Review summary",
				},
			],
			"repos/acme/project/pulls/7/comments": [
				{
					id: 13,
					html_url: "https://github.com/acme/project/pull/7#discussion_r13",
					user: { login: "inline-reviewer" },
					body: "Current line",
					path: "change.ts",
					side: "RIGHT",
					line: 1,
					commit_id: head,
					position: 1,
				},
				{
					id: 14,
					html_url: "https://github.com/acme/project/pull/7#discussion_r14",
					user: { login: "inline-reviewer" },
					body: "Old line",
					path: "change.ts",
					original_side: "LEFT",
					original_line: 1,
					original_commit_id: base,
					position: null,
					in_reply_to_id: 13,
				},
			],
		};
		await withFakeGh(fakeGh(metadata(base, head), pages), async () => {
			const confirm = vi.fn(async () => true);
			const review = await openGitHubReview(
				root,
				"7",
				confirm,
				new AbortController().signal,
			);
			expect(review).toMatchObject({
				provider: "github",
				providerLabel: "GitHub",
				repository: "acme/project",
				number: 7,
				baseSha: base,
				headSha: head,
				checkoutBranch: "feature",
			});
			expect(review.comments).toEqual([
				expect.objectContaining({
					id: "discussion:11",
					kind: "discussion",
					outdated: false,
				}),
				expect.objectContaining({
					id: "review:12",
					kind: "review",
					outdated: false,
				}),
				expect.objectContaining({
					id: "inline:13",
					kind: "inline",
					path: "change.ts",
					side: "new",
					line: 1,
					commit: head,
					outdated: false,
				}),
				expect.objectContaining({
					id: "inline:14",
					replyTo: "inline:13",
					outdated: true,
				}),
			]);
			expect(confirm).toHaveBeenCalledWith(
				"Switch to GitHub PR checkout?",
				expect.stringContaining(
					"Repository: acme/project\nPR: #7\nCurrent branch: main\nTarget branch: feature",
				),
				expect.any(AbortSignal),
			);
			expect(await git(root, "branch", "--show-current")).toBe("feature");
			expect(await git(root, "rev-parse", "HEAD")).toBe(head);
		});
	});

	it("creates a non-colliding PR branch for a fork without overwriting existing branches", async () => {
		const { root, base, head } = await repository();
		await git(root, "branch", "-D", "feature");
		await git(root, "branch", "strata/pr-8-fork-work", "main");
		const fork = metadata(base, head, {
			number: 8,
			url: "https://github.com/acme/project/pull/8",
			headRefName: "fork/work",
			headRepository: { name: "project", nameWithOwner: "contributor/project" },
			headRepositoryOwner: { login: "contributor" },
		});
		await withFakeGh(fakeGh(fork), async () => {
			const review = await openGitHubReview(
				root,
				"https://github.com/acme/project/pull/8",
				async () => true,
				new AbortController().signal,
			);
			expect(review.checkoutBranch).toBe("strata/pr-8-fork-work-2");
			expect(await git(root, "rev-parse", review.checkoutBranch)).toBe(head);
			expect(await git(root, "rev-parse", "strata/pr-8-fork-work")).toBe(base);
		});
	});

	it("refuses a dirty checkout before mutation", async () => {
		const first = await repository();
		await writeFile(path.join(first.root, "untracked.txt"), "dirty\n");
		await withFakeGh(fakeGh(metadata(first.base, first.head)), async () => {
			await expect(
				openGitHubReview(
					first.root,
					"7",
					async () => true,
					new AbortController().signal,
				),
			).rejects.toThrow(/clean worktree/);
			expect(await git(first.root, "branch", "--show-current")).toBe("main");
		});
	});

	it("refuses a denied checkout before mutation", async () => {
		const denied = await repository();
		await withFakeGh(fakeGh(metadata(denied.base, denied.head)), async () => {
			await expect(
				openGitHubReview(
					denied.root,
					"7",
					async () => false,
					new AbortController().signal,
				),
			).rejects.toThrow(/not confirmed/);
			expect(await git(denied.root, "branch", "--show-current")).toBe("main");
		});
	});

	it("refuses a checkout without UI before mutation", async () => {
		const denied = await repository();
		await withFakeGh(fakeGh(metadata(denied.base, denied.head)), async () => {
			await expect(
				openGitHubReview(
					denied.root,
					"7",
					undefined,
					new AbortController().signal,
				),
			).rejects.toThrow(/no interactive confirmation/);
			expect(await git(denied.root, "branch", "--show-current")).toBe("main");
		});
	});

	it("refuses a divergent checkout before mutation", async () => {
		const divergent = await repository();
		await git(divergent.root, "checkout", "--quiet", "feature");
		await writeFile(path.join(divergent.root, "other.ts"), "export {};\n");
		await git(divergent.root, "add", "other.ts");
		await git(divergent.root, "commit", "--quiet", "-m", "local commit");
		await git(divergent.root, "checkout", "--quiet", "main");
		await withFakeGh(
			fakeGh(metadata(divergent.base, divergent.head)),
			async () => {
				await expect(
					openGitHubReview(
						divergent.root,
						"7",
						async () => true,
						new AbortController().signal,
					),
				).rejects.toThrow(/diverges/);
			},
		);
	});

	it("refuses protected checkout paths before mutation", async () => {
		const protectedChange = await repository();
		const blob = await git(
			protectedChange.root,
			"rev-parse",
			`${protectedChange.head}:change.ts`,
		);
		const tree = await gitInput(
			protectedChange.root,
			`100644 blob ${blob}\tchange.ts\n100644 blob ${blob}\t.env\n`,
			"mktree",
		);
		const protectedHead = await git(
			protectedChange.root,
			"commit-tree",
			tree,
			"-p",
			protectedChange.base,
			"-m",
			"protected metadata fixture",
		);
		const protectedMetadata = metadata(protectedChange.base, protectedHead, {
			headRefName: "protected-fixture",
			headRepository: {
				name: "project",
				nameWithOwner: "contributor/project",
			},
			headRepositoryOwner: { login: "contributor" },
		});
		await withFakeGh(fakeGh(protectedMetadata), async () => {
			const confirm = vi.fn(async () => true);
			await expect(
				openGitHubReview(
					protectedChange.root,
					"7",
					confirm,
					new AbortController().signal,
				),
			).rejects.toThrow(/protected path \.env/);
			expect(confirm).toHaveBeenCalledOnce();
			expect(await git(protectedChange.root, "branch", "--show-current")).toBe(
				"main",
			);
		});
	});

	it("disables configured fsmonitor hooks on the real status route", async () => {
		const { root, base, head } = await repository();
		const marker = path.join(root, "fsmonitor-ran");
		await git(root, "config", "core.fsmonitor", "fake-fsmonitor");
		await withFakeExecutable(
			"fake-fsmonitor",
			`require("node:fs").writeFileSync(${JSON.stringify(marker)}, "ran"); process.stdout.write("2\\n");`,
			async () => {
				await withFakeGh(fakeGh(metadata(base, head)), async () => {
					const review = await openGitHubReview(
						root,
						"7",
						async () => true,
						new AbortController().signal,
					);
					expect(review.checkoutBranch).toBe("feature");
					expect(existsSync(marker)).toBe(false);
				});
			},
		);
	});

	it("rejects external checkout filters configured during confirmation without executing them", async () => {
		const fixture = await repository();
		await git(fixture.root, "checkout", "--quiet", "feature");
		await writeFile(
			path.join(fixture.root, ".gitattributes"),
			"change.ts filter=guard\n",
		);
		await git(fixture.root, "add", ".gitattributes");
		await git(fixture.root, "commit", "--quiet", "--amend", "--no-edit");
		fixture.head = await git(fixture.root, "rev-parse", "HEAD");
		await git(fixture.root, "checkout", "--quiet", "main");
		const marker = path.join(fixture.root, "filter-ran");
		await withFakeExecutable(
			"fake-filter",
			`require("node:fs").writeFileSync(${JSON.stringify(marker)}, "ran");`,
			async () => {
				await withFakeGh(
					fakeGh(metadata(fixture.base, fixture.head)),
					async () => {
						await expect(
							openGitHubReview(
								fixture.root,
								"7",
								async () => {
									await git(
										fixture.root,
										"config",
										"filter.guard.clean",
										"fake-filter",
									);
									await git(
										fixture.root,
										"config",
										"filter.guard.smudge",
										"fake-filter",
									);
									await git(
										fixture.root,
										"config",
										"filter.guard.process",
										"fake-filter",
									);
									return true;
								},
								new AbortController().signal,
							),
						).rejects.toThrow(/external clean, smudge, or process filters/);
						expect(existsSync(marker)).toBe(false);
						expect(await git(fixture.root, "branch", "--show-current")).toBe(
							"main",
						);
					},
				);
			},
		);
	});

	it("rejects repository credential helpers before fetch", async () => {
		const credential = await repository();
		const missing = "1".repeat(40);
		const credentialMarker = path.join(credential.root, "credential-ran");
		await git(
			credential.root,
			"config",
			"credential.helper",
			"!fake-credential",
		);
		await withFakeExecutable(
			"fake-credential",
			`require("node:fs").writeFileSync(${JSON.stringify(credentialMarker)}, "ran");`,
			async () => {
				await withFakeGh(
					fakeGh(metadata(missing, credential.head)),
					async () => {
						await expect(
							openGitHubReview(
								credential.root,
								"7",
								async () => true,
								new AbortController().signal,
							),
						).rejects.toThrow(/repository credential helpers/);
						expect(existsSync(credentialMarker)).toBe(false);
					},
				);
			},
		);
	});

	it("rejects repository URL rewrites before fetch", async () => {
		const missing = "1".repeat(40);
		const rewrite = await repository();
		const rewriteMarker = path.join(rewrite.root, "rewrite-ran");
		await git(
			rewrite.root,
			"config",
			"url.unsafe://redirect/.insteadOf",
			"https://github.com/",
		);
		await withFakeExecutable(
			"git-remote-unsafe",
			`require("node:fs").writeFileSync(${JSON.stringify(rewriteMarker)}, "ran");`,
			async () => {
				await withFakeGh(fakeGh(metadata(missing, rewrite.head)), async () => {
					await expect(
						openGitHubReview(
							rewrite.root,
							"7",
							async () => true,
							new AbortController().signal,
						),
					).rejects.toThrow(/repository URL rewrites/);
					expect(existsSync(rewriteMarker)).toBe(false);
				});
			},
		);
	});

	it("rejects repository include config without following it", async () => {
		const fixture = await repository();
		const included = path.join(fixture.root, "included.config");
		await writeFile(included, '[filter "included"]\n\tclean = fake-filter\n');
		await git(fixture.root, "config", "include.path", included);
		await withFakeGh(fakeGh(metadata(fixture.base, fixture.head)), async () => {
			await expect(
				openGitHubReview(
					fixture.root,
					"7",
					async () => true,
					new AbortController().signal,
				),
			).rejects.toThrow(/repository include config/);
		});
	});

	it("uses gh as Git's fixed credential helper for a private HTTPS fetch", async () => {
		const fixture = await repository();
		const missing = "2".repeat(40);
		const marker = path.join(fixture.root, "gh-credential-ran");
		const program = `
const metadata = ${JSON.stringify(metadata(missing, fixture.head))};
if (args[0] === "auth" && args[1] === "git-credential") {
  require("node:fs").writeFileSync(${JSON.stringify(marker)}, "ran");
  process.stdout.write("username=strata-test\\npassword=controlled-test-value\\n");
} else if (args[0] === "pr" && args[1] === "view") process.stdout.write(JSON.stringify(metadata));
else if (args[0] === "api") process.stdout.write("[]");
else process.exitCode = 92;
`;
		await withFakeExecutable(
			"git-remote-https",
			`const result = require("node:child_process").spawnSync("git", ["credential", "fill"], { input: "protocol=https\\nhost=github.com\\n\\n", encoding: "utf8" }); if (result.status !== 0) process.exitCode = 90; else process.exitCode = 1;`,
			async () => {
				await withFakeGh(program, async () => {
					await expect(
						openGitHubReview(
							fixture.root,
							"7",
							async () => true,
							new AbortController().signal,
						),
					).rejects.toThrow(/could not fetch pinned commit/);
					expect(existsSync(marker)).toBe(true);
				});
			},
		);
	});

	it("paginates discussion through read-only API calls", async () => {
		const paginated = await repository();
		const discussions = Array.from({ length: 101 }, (_, index) => ({
			id: index + 1,
			html_url: `https://github.com/acme/project/pull/7#issuecomment-${index + 1}`,
			user: { login: `author-${index + 1}` },
			body: `Discussion ${index + 1}`,
		}));
		const paginatedGh = `
const metadata = ${JSON.stringify(metadata(paginated.base, paginated.head))};
const discussions = ${JSON.stringify(discussions)};
if (args[0] === "pr" && args[1] === "view") process.stdout.write(JSON.stringify(metadata));
else if (args[0] === "api") {
  if (!args.includes("GET") || args.some((arg) => ["POST", "PATCH", "PUT", "DELETE"].includes(arg))) process.exitCode = 91;
  else {
    const endpoint = args.at(-1);
    const page = Number(/[?&]page=(\\d+)/.exec(endpoint)?.[1] ?? "1");
    const values = endpoint.startsWith("repos/acme/project/issues/7/comments")
      ? discussions.slice((page - 1) * 100, page * 100)
      : [];
    process.stdout.write(JSON.stringify(values));
  }
} else process.exitCode = 92;
`;
		await withFakeGh(paginatedGh, async () => {
			const review = await openGitHubReview(
				paginated.root,
				"7",
				async () => true,
				new AbortController().signal,
			);
			expect(review.comments).toHaveLength(101);
			expect(review.comments.at(-1)?.id).toBe("discussion:101");
		});
	});

	it("rejects malformed forge metadata", async () => {
		const malformed = await repository();
		await withFakeGh(
			fakeGh(
				metadata(malformed.base, malformed.head, { baseRefOid: "not-an-oid" }),
			),
			async () => {
				await expect(
					openGitHubReview(
						malformed.root,
						"7",
						async () => true,
						new AbortController().signal,
					),
				).rejects.toThrow(/invalid PR base commit/);
			},
		);
	});

	it("rejects foreign PR URLs", async () => {
		const { root, base, head } = await repository();
		await withFakeGh(fakeGh(metadata(base, head)), async () => {
			await expect(
				openGitHubReview(
					root,
					"https://github.com/other/project/pull/7",
					async () => true,
					new AbortController().signal,
				),
			).rejects.toThrow(/does not belong/);
		});
	});

	it("rejects unsafe imported links", async () => {
		const { root, base, head } = await repository();
		const pages = {
			"repos/acme/project/issues/7/comments": [
				{
					id: 1,
					html_url: "javascript:alert(1)",
					user: { login: "attacker" },
					body: "<img src=x onerror=alert(1)>",
				},
			],
		};
		await withFakeGh(fakeGh(metadata(base, head), pages), async () => {
			await expect(
				openGitHubReview(
					root,
					"7",
					async () => true,
					new AbortController().signal,
				),
			).rejects.toThrow(/unsafe discussion comment url/);
		});
	});

	it.each(["same-commit branch switch", "dirty worktree"])(
		"rejects freshness after a %s",
		async (change) => {
			const { root, base, head } = await repository();
			await git(root, "checkout", "--quiet", "feature");
			await withFakeGh(fakeGh(metadata(base, head)), async () => {
				const review = await openGitHubReview(
					root,
					undefined,
					async () => true,
					new AbortController().signal,
				);
				expect(
					await isGitHubReviewCurrent(
						root,
						review,
						new AbortController().signal,
					),
				).toBe(true);
				if (change === "same-commit branch switch") {
					await git(root, "branch", "same-sha", head);
					await git(root, "checkout", "--quiet", "same-sha");
				} else {
					await writeFile(path.join(root, "local.txt"), "dirty\n");
				}
				expect(
					await isGitHubReviewCurrent(
						root,
						review,
						new AbortController().signal,
					),
				).toBe(false);
			});
		},
	);

	it("requires reopen for a new PR head", async () => {
		const changed = await repository();
		await withFakeGh(fakeGh(metadata(changed.base, changed.head)), async () => {
			const review = await openGitHubReview(
				changed.root,
				"7",
				async () => true,
				new AbortController().signal,
			);
			await git(
				changed.root,
				"checkout",
				"--quiet",
				"-b",
				"remote-new",
				changed.head,
			);
			await writeFile(
				path.join(changed.root, "next.ts"),
				"export const next = true;\n",
			);
			await git(changed.root, "add", "next.ts");
			await git(
				changed.root,
				"commit",
				"--quiet",
				"-m",
				"new remote head fixture",
			);
			const nextHead = await git(changed.root, "rev-parse", "HEAD");
			await git(changed.root, "checkout", "--quiet", "feature");
			await withFakeGh(fakeGh(metadata(changed.base, nextHead)), async () => {
				await expect(
					refreshGitHubReview(
						changed.root,
						review,
						new AbortController().signal,
					),
				).rejects.toThrow(/new head commit.*reopen/s);
			});
		});
	});

	it("validates discussion before confirmation and leaves the original branch on failure", async () => {
		const { root, base, head } = await repository();
		const program = `
const metadata = ${JSON.stringify(metadata(base, head))};
if (args[0] === "pr") process.stdout.write(JSON.stringify(metadata));
else if (args.at(-1).startsWith("repos/acme/project/issues/7/comments")) process.stdout.write("{}");
else process.stdout.write("[]");
`;
		await withFakeGh(program, async () => {
			const confirm = vi.fn(async () => true);
			await expect(
				openGitHubReview(root, "7", confirm, new AbortController().signal),
			).rejects.toThrow(/discussion page must be an array/);
			expect(confirm).not.toHaveBeenCalled();
			expect(await git(root, "branch", "--show-current")).toBe("main");
		});
	});

	it("leaves the original branch when comment loading is cancelled", async () => {
		const { root, base, head } = await repository();
		const marker = path.join(root, "comment-started");
		const program = `
if (args[0] === "pr") process.stdout.write(${JSON.stringify(JSON.stringify(metadata(base, head)))});
else if (args.at(-1).startsWith("repos/acme/project/issues/7/comments")) {
  require("node:fs").writeFileSync(${JSON.stringify(`${marker}.pending`)}, String(process.pid));
  require("node:fs").renameSync(${JSON.stringify(`${marker}.pending`)}, ${JSON.stringify(marker)});
  Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 10000);
} else process.stdout.write("[]");
`;
		await withFakeGh(program, async () => {
			await cancelAfterMarker(
				marker,
				"cancelled while loading comments",
				(signal) => openGitHubReview(root, "7", async () => true, signal),
			);
			expect(await git(root, "branch", "--show-current")).toBe("main");
		});
	});

	it("rejects metadata changed after confirmation without switching", async () => {
		const { root, base, head } = await repository();
		const marker = path.join(root, ".git", "metadata-loaded");
		const first = metadata(base, head);
		const changed = metadata(base, head, {
			title: "Changed during confirmation",
		});
		const program = `
if (args[0] === "pr") {
  const fs = require("node:fs");
  const value = fs.existsSync(${JSON.stringify(marker)}) ? ${JSON.stringify(changed)} : ${JSON.stringify(first)};
  fs.writeFileSync(${JSON.stringify(marker)}, "loaded");
  process.stdout.write(JSON.stringify(value));
} else process.stdout.write("[]");
`;
		await withFakeGh(program, async () => {
			await expect(
				openGitHubReview(
					root,
					"7",
					async () => true,
					new AbortController().signal,
				),
			).rejects.toThrow(/metadata changed after confirmation/);
			expect(await git(root, "branch", "--show-current")).toBe("main");
		});
	});

	it("rejects an existing target branch changed during confirmation", async () => {
		const { root, base, head } = await repository();
		await withFakeGh(fakeGh(metadata(base, head)), async () => {
			await expect(
				openGitHubReview(
					root,
					"7",
					async () => {
						await git(root, "branch", "-f", "feature", base);
						return true;
					},
					new AbortController().signal,
				),
			).rejects.toThrow(/target branch feature changed/);
			expect(await git(root, "branch", "--show-current")).toBe("main");
		});
	});

	it("rejects a target branch created during confirmation", async () => {
		const { root, base, head } = await repository();
		await git(root, "branch", "-D", "feature");
		const fork = metadata(base, head, {
			number: 8,
			url: "https://github.com/acme/project/pull/8",
			headRefName: "fork/work",
			headRepository: {
				name: "project",
				nameWithOwner: "contributor/project",
			},
			headRepositoryOwner: { login: "contributor" },
		});
		await withFakeGh(fakeGh(fork), async () => {
			await expect(
				openGitHubReview(
					root,
					"8",
					async () => {
						await git(root, "branch", "strata/pr-8-fork-work", base);
						return true;
					},
					new AbortController().signal,
				),
			).rejects.toThrow(/target branch strata\/pr-8-fork-work changed/);
			expect(await git(root, "branch", "--show-current")).toBe("main");
		});
	});

	it("reuses the current branch for bare fork PR resolution without confirmation", async () => {
		const { root, base, head } = await repository();
		await git(root, "checkout", "--quiet", "feature");
		const fork = metadata(base, head, {
			headRepository: {
				name: "project",
				nameWithOwner: "contributor/project",
			},
			headRepositoryOwner: { login: "contributor" },
		});
		await withFakeGh(fakeGh(fork), async () => {
			const confirm = vi.fn(async () => true);
			const review = await openGitHubReview(
				root,
				undefined,
				confirm,
				new AbortController().signal,
			);
			expect(review.checkoutBranch).toBe("feature");
			expect(confirm).not.toHaveBeenCalled();
			expect(await git(root, "branch", "--show-current")).toBe("feature");
		});
	});

	it("rejects an unpushed current branch for bare PR resolution", async () => {
		const { root, base, head } = await repository();
		await git(root, "checkout", "--quiet", "feature");
		await writeFile(path.join(root, "local.ts"), "export {};\n");
		await git(root, "add", "local.ts");
		await git(root, "commit", "--quiet", "-m", "unpushed");
		await withFakeGh(fakeGh(metadata(base, head)), async () => {
			await expect(
				openGitHubReview(
					root,
					undefined,
					undefined,
					new AbortController().signal,
				),
			).rejects.toThrow(/unpushed commits/);
			expect(await git(root, "branch", "--show-current")).toBe("feature");
		});
	});

	it("validates an explicit requested PR against the returned identity", async () => {
		const { root, base, head } = await repository();
		await withFakeGh(fakeGh(metadata(base, head)), async () => {
			await expect(
				openGitHubReview(
					root,
					"8",
					async () => true,
					new AbortController().signal,
				),
			).rejects.toThrow(/different PR than the one requested/);
			expect(await git(root, "branch", "--show-current")).toBe("main");
		});
	});

	it("validates an explicit requested PR URL against the returned identity", async () => {
		const { root, base, head } = await repository();
		await withFakeGh(fakeGh(metadata(base, head)), async () => {
			await expect(
				openGitHubReview(
					root,
					"https://github.com/acme/project/pull/8",
					async () => true,
					new AbortController().signal,
				),
			).rejects.toThrow(/different PR than the one requested/);
			expect(await git(root, "branch", "--show-current")).toBe("main");
		});
	});

	it("keeps a currently mapped inline comment current when authored on an older commit", async () => {
		const { root, base, head } = await repository();
		const pages = {
			"repos/acme/project/pulls/7/comments": [
				{
					id: 21,
					html_url: "https://github.com/acme/project/pull/7#discussion_r21",
					user: { login: "reviewer" },
					body: "Still mapped",
					path: "change.ts",
					side: "RIGHT",
					line: 1,
					original_commit_id: base,
					position: 1,
				},
			],
		};
		await withFakeGh(fakeGh(metadata(base, head), pages), async () => {
			const review = await openGitHubReview(
				root,
				"7",
				async () => true,
				new AbortController().signal,
			);
			expect(review.comments[0]).toMatchObject({
				commit: base,
				line: 1,
				side: "new",
				outdated: false,
			});
		});
	});

	it("rechecks the local branch after the is-current network call", async () => {
		const { root, base, head } = await repository();
		let review!: Awaited<ReturnType<typeof openGitHubReview>>;
		await withFakeGh(fakeGh(metadata(base, head)), async () => {
			review = await openGitHubReview(
				root,
				"7",
				async () => true,
				new AbortController().signal,
			);
		});
		await git(root, "branch", "same-sha", head);
		const program = `
if (args[0] === "pr") {
  require("node:child_process").execFileSync("git", ["checkout", "--quiet", "same-sha"]);
  process.stdout.write(${JSON.stringify(JSON.stringify(metadata(base, head)))});
} else process.stdout.write("[]");
`;
		const sentinel = await repository();
		try {
			vi.stubEnv("GIT_DIR", path.join(sentinel.root, ".git"));
			vi.stubEnv("GIT_WORK_TREE", sentinel.root);
			vi.stubEnv("GIT_INDEX_FILE", path.join(sentinel.root, ".git", "index"));
			await withFakeGh(program, async () => {
				expect(
					await isGitHubReviewCurrent(
						root,
						review,
						new AbortController().signal,
					),
				).toBe(false);
			});
		} finally {
			vi.unstubAllEnvs();
		}
		expect(await git(root, "branch", "--show-current")).toBe("same-sha");
		expect(await git(sentinel.root, "branch", "--show-current")).toBe("main");
		expect(await git(sentinel.root, "rev-parse", "HEAD")).toBe(sentinel.base);
	});

	it("rechecks a clean checkout after all refresh network calls", async () => {
		const { root, base, head } = await repository();
		let review!: Awaited<ReturnType<typeof openGitHubReview>>;
		await withFakeGh(fakeGh(metadata(base, head)), async () => {
			review = await openGitHubReview(
				root,
				"7",
				async () => true,
				new AbortController().signal,
			);
		});
		const dirtyPath = path.join(root, "network-race.txt");
		const program = `
if (args[0] === "pr") process.stdout.write(${JSON.stringify(JSON.stringify(metadata(base, head)))});
else {
  if (args.at(-1).startsWith("repos/acme/project/pulls/7/comments")) require("node:fs").writeFileSync(${JSON.stringify(dirtyPath)}, "changed");
  process.stdout.write("[]");
}
`;
		await withFakeGh(program, async () => {
			await expect(
				refreshGitHubReview(root, review, new AbortController().signal),
			).rejects.toThrow(/checkout branch, commit, or working tree changed/);
		});
	});

	it("honors cancellation before gh acquisition", async () => {
		const controller = new AbortController();
		controller.abort(new Error("cancelled by test"));
		await expect(
			openGitHubReview("unused", "7", undefined, controller.signal),
		).rejects.toThrow("cancelled by test");
	});

	it("honors cancellation during gh acquisition", async () => {
		const active = await repository();
		const marker = path.join(active.root, ".gh-started");
		const blockingGh = `
if (args[0] === "pr" && args[1] === "view") {
  require("node:fs").writeFileSync(${JSON.stringify(`${marker}.pending`)}, String(process.pid));
  require("node:fs").renameSync(${JSON.stringify(`${marker}.pending`)}, ${JSON.stringify(marker)});
  Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 10000);
} else process.exitCode = 92;
`;
		await withFakeGh(blockingGh, async () => {
			await cancelAfterMarker(marker, "cancelled in flight", (signal) =>
				openGitHubReview(active.root, "7", async () => true, signal),
			);
			expect(await git(active.root, "branch", "--show-current")).toBe("main");
		});
	});
});