Luigit
repositories / pi-ext

pi-ext

bugabingas pi extensions

owned by admin

extensions/strata/__tests__/implementation.test.ts

Raw
import { appendFile, writeFile } from "node:fs/promises";
import path from "node:path";
import { aroundEach, describe, expect, it, vi } from "vitest";
import { assertGitHubCheckout } from "../github.js";
import { collectGitHubSnapshot, formatFeedback } from "../implementation.js";
import type { Feedback, ForgeReview, Snapshot } from "../types.js";
import {
	git,
	nativeOperation,
	nativeTest,
	openFixture,
	testSignal,
	withFakeGh,
} from "./fixture.js";

let fixture: Awaited<ReturnType<typeof openFixture>> | undefined;

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

async function connect(url: string) {
	const response = await fetch(url, {
		redirect: "manual",
		signal: testSignal(),
	});
	const cookie = response.headers.get("set-cookie")?.split(";")[0] ?? "";
	const origin = new URL(url).origin;
	let version = await (
		await fetch(`${origin}/api/state`, {
			headers: { Cookie: cookie },
			signal: testSignal(),
		})
	).json();
	return (route: string, body?: unknown) =>
		nativeOperation(async () => {
			const response = await fetch(`${origin}/api/${route}`, {
				signal: testSignal(),
				method: body === undefined ? "GET" : "POST",
				headers: {
					Cookie: cookie,
					Origin: origin,
					"Content-Type": "application/json",
					"If-Match": String(version.revision),
					"X-Strata-Snapshot": version.snapshotId,
				},
				body: body === undefined ? undefined : JSON.stringify(body),
			});
			const payload = await response.clone().json();
			if (payload.revision !== undefined) version = payload;
			return response;
		});
}

function ghProgram(metadata: Record<string, unknown>): string {
	return `
if (args[0] === "pr") process.stdout.write(${JSON.stringify(JSON.stringify(metadata))});
else process.stdout.write("[]");
`;
}

async function excludeTestHome(root: string): Promise<void> {
	await appendFile(path.join(root, ".git", "info", "exclude"), ".test-home/\n");
}

describe("forge feedback handoff", () => {
	it("carries exact snapshot, checkout, PR, anchor, and source discussion identities without treating imports as findings", () => {
		const snapshot: Snapshot = {
			id: "snapshot-1",
			repoRoot: "/work/project",
			source: { kind: "base", ref: "b".repeat(40) },
			base: "a".repeat(40),
			head: "c".repeat(40),
			hunks: [
				{
					id: "h-one",
					path: "src/change.ts",
					header: "@@ -1 +1 @@",
					lines: [{ kind: "add", text: "next", newLine: 1 }],
				},
			],
			skipped: [],
		};
		const forge: ForgeReview = {
			provider: "github",
			providerLabel: "GitHub",
			repository: "acme/project",
			number: 7,
			url: "https://github.com/acme/project/pull/7",
			title: "Change",
			body: "Description",
			author: "author",
			baseRef: "main",
			baseSha: "b".repeat(40),
			headRef: "feature",
			headSha: "c".repeat(40),
			checkoutBranch: "strata/pr-7-feature",
			comments: [
				{
					id: "inline:91",
					url: "https://github.com/acme/project/pull/7#discussion_r91",
					author: "reviewer",
					body: "Imported prose must not become a finding",
					kind: "inline",
					path: "src/change.ts",
					side: "new",
					line: 1,
					commit: "c".repeat(40),
					outdated: false,
				},
			],
		};
		const feedback: Feedback = {
			snapshotId: snapshot.id,
			reviewed: ["h-one"],
			findings: [
				{
					id: "finding-1",
					hunkId: "h-one",
					side: "new",
					line: 1,
					severity: "major",
					text: "Human finding",
				},
			],
			notes: "Human note",
		};

		const result = formatFeedback(snapshot, feedback, forge);
		expect(result).toContain("GitHub acme/project PR #7");
		expect(result).toContain("Checkout branch: strata/pr-7-feature");
		expect(result).toContain(`PR base: main ${"b".repeat(40)}`);
		expect(result).toContain(`PR head: feature ${"c".repeat(40)}`);
		expect(result).toContain("inline:91 [inline] src/change.ts:1 (new)");
		expect(result).not.toContain("Imported prose must not become a finding");
		expect(result).toContain(
			"[major] src/change.ts:1 (new, h-one)\nHuman finding",
		);
		expect(result).toContain("Ordinary local inspection and edits may proceed");
		expect(result).toContain("requires separate user approval");
		expect(result).toContain("is not approval to publish it");
	});
});

describe.sequential("GitHub snapshot ownership", () => {
	it.each([
		{
			scope: "PR number",
			requests: [
				{ host: "github.com", number: 7 },
				{ host: "github.com", number: 8 },
			],
		},
		{
			scope: "forge host",
			requests: [
				{ host: "github.com", number: 8 },
				{ host: "github.example", number: 8 },
			],
		},
	])("isolates saved plans and drafts by $scope", async ({ requests }) => {
		fixture = await openFixture();
		await excludeTestHome(fixture.root);
		await git(fixture.root, "add", "contract.ts", "consumer.ts");
		await git(fixture.root, "commit", "--quiet", "-m", "feature");
		const branch = await git(fixture.root, "branch", "--show-current");
		const head = await git(fixture.root, "rev-parse", "HEAD");
		const base = await git(fixture.root, "rev-parse", "HEAD^");
		await git(
			fixture.root,
			"remote",
			"add",
			"origin",
			"https://github.com/acme/project.git",
		);
		await git(
			fixture.root,
			"remote",
			"add",
			"enterprise",
			"https://github.example/acme/project.git",
		);
		const current = fixture;
		for (const [index, request] of requests.entries()) {
			const url = `https://${request.host}/acme/project/pull/${request.number}`;
			await withFakeGh(
				ghProgram({
					number: request.number,
					url,
					title: "Scoped review",
					body: "",
					author: { login: "author" },
					baseRefName: "main",
					baseRefOid: base,
					headRefName: branch,
					headRefOid: head,
					headRepository: { name: "project", nameWithOwner: "acme/project" },
					headRepositoryOwner: { login: "acme" },
				}),
				async () => {
					await current.run(`--pr ${url}`);
					const api = await connect(current.url);
					const state = await (await api("state")).json();
					expect(state.review.forge.url).toBe(url);
					expect(state.review.draft).toEqual({
						reviewed: [],
						findings: [],
						notes: "",
					});
					expect(current.calls).toBe(index + 1);
					expect(
						(
							await api("draft", {
								reviewed: [state.review.snapshot.hunks[0].id],
								findings: [],
								notes: `Notes only for ${url}`,
							})
						).status,
					).toBe(200);
					await current.run("cancel");
				},
			);
		}
	});

	it("detects a same-commit branch switch across snapshot collection", async () => {
		fixture = await openFixture();
		await excludeTestHome(fixture.root);
		await git(fixture.root, "add", "contract.ts", "consumer.ts");
		await git(fixture.root, "commit", "--quiet", "-m", "feature");
		expect(await git(fixture.root, "status", "--porcelain")).toBe("");
		const branch = await git(fixture.root, "branch", "--show-current");
		const head = await git(fixture.root, "rev-parse", "HEAD");
		const forge: ForgeReview = {
			provider: "github",
			providerLabel: "GitHub",
			repository: "acme/project",
			number: 7,
			url: "https://github.com/acme/project/pull/7",
			title: "Pinned capture",
			body: "",
			author: "author",
			baseRef: branch,
			baseSha: head,
			headRef: branch,
			headSha: head,
			checkoutBranch: branch,
			comments: [],
		};
		let assertions = 0;
		const github = {
			assertGitHubCheckout: async (
				cwd: string,
				review: ForgeReview,
				signal: AbortSignal,
			) => {
				assertions++;
				await assertGitHubCheckout(cwd, review, signal);
				if (assertions === 1) {
					await git(cwd, "branch", "same-sha", head);
					await git(cwd, "checkout", "--quiet", "same-sha");
				}
			},
		};

		await expect(
			collectGitHubSnapshot(
				fixture.root,
				{ kind: "base", ref: head },
				forge,
				github,
				testSignal(),
			),
		).rejects.toThrow(/checkout branch, commit, or working tree changed/);
		expect(assertions).toBe(2);
	});

	it("rejects a captured HEAD that differs from the pinned PR head", async () => {
		fixture = await openFixture();
		await excludeTestHome(fixture.root);
		await git(fixture.root, "add", "contract.ts", "consumer.ts");
		await git(fixture.root, "commit", "--quiet", "-m", "feature");
		const branch = await git(fixture.root, "branch", "--show-current");
		const head = await git(fixture.root, "rev-parse", "HEAD");
		const forge: ForgeReview = {
			provider: "github",
			providerLabel: "GitHub",
			repository: "acme/project",
			number: 7,
			url: "https://github.com/acme/project/pull/7",
			title: "Pinned capture",
			body: "",
			author: "author",
			baseRef: branch,
			baseSha: head,
			headRef: branch,
			headSha: "f".repeat(40),
			checkoutBranch: branch,
			comments: [],
		};
		const assertCheckout = vi.fn(async () => {});

		await expect(
			collectGitHubSnapshot(
				fixture.root,
				{ kind: "base", ref: head },
				forge,
				{ assertGitHubCheckout: assertCheckout },
				testSignal(),
			),
		).rejects.toThrow(
			/captured Git HEAD does not match the pinned GitHub PR head/,
		);
		expect(assertCheckout).toHaveBeenCalledOnce();
	});

	it("keeps the active forge snapshot after a failed staged refresh", async () => {
		fixture = await openFixture();
		await excludeTestHome(fixture.root);
		const baseBranch = await git(fixture.root, "branch", "--show-current");
		const base = await git(fixture.root, "rev-parse", "HEAD");
		await git(fixture.root, "checkout", "--quiet", "-b", "feature");
		await git(fixture.root, "add", "contract.ts", "consumer.ts");
		await git(fixture.root, "commit", "--quiet", "-m", "feature");
		expect(await git(fixture.root, "status", "--porcelain")).toBe("");
		const head = await git(fixture.root, "rev-parse", "HEAD");
		await git(
			fixture.root,
			"checkout",
			"--quiet",
			"-b",
			"alternate-base",
			base,
		);
		await writeFile(path.join(fixture.root, "alternate.ts"), "export {};\n");
		await git(fixture.root, "add", "alternate.ts");
		await git(fixture.root, "commit", "--quiet", "-m", "alternate base");
		const alternateBase = await git(fixture.root, "rev-parse", "HEAD");
		await git(fixture.root, "checkout", "--quiet", "feature");
		await git(
			fixture.root,
			"remote",
			"add",
			"origin",
			"https://github.com/acme/project.git",
		);
		const metadata = {
			number: 7,
			url: "https://github.com/acme/project/pull/7",
			title: "Stable review",
			body: "",
			author: { login: "author" },
			baseRefName: baseBranch,
			baseRefOid: base,
			headRefName: "feature",
			headRefOid: head,
			headRepository: { name: "project", nameWithOwner: "acme/project" },
			headRepositoryOwner: { login: "acme" },
		};
		await withFakeGh(ghProgram(metadata), async () => {
			await fixture?.run("--pr");
		});
		const api = await connect(fixture.url);
		const initial = await (await api("state")).json();
		const changedMetadata = {
			...metadata,
			baseRefName: "alternate-base",
			baseRefOid: alternateBase,
		};
		vi.spyOn(fixture.ctx.modelRegistry, "complete").mockRejectedValueOnce(
			new Error("refresh planning failed"),
		);
		await withFakeGh(ghProgram(changedMetadata), async () => {
			expect((await api("refresh", { confirm: true })).status).toBe(500);
		});
		const afterFailure = await (await api("state")).json();
		expect(afterFailure.review).toEqual(initial.review);
		const send = vi
			.spyOn(fixture.t.session, "prompt")
			.mockResolvedValue(undefined);
		await withFakeGh(ghProgram(metadata), async () => {
			expect(
				(
					await api("submit", {
						draft: {
							reviewed: [],
							findings: [],
							notes: "Keep the original refresh state.",
						},
					})
				).status,
			).toBe(200);
		});
		await vi.waitFor(() => expect(send).toHaveBeenCalled());
		expect(send.mock.calls[0]?.[0]).toContain(`PR base: ${baseBranch} ${base}`);
		expect(send.mock.calls[0]?.[0]).not.toContain(alternateBase);
	});
});