feat: add pr diff and checkout (task 0014)
All checks were successful
CI / test (pull_request) Successful in 42s
All checks were successful
CI / test (pull_request) Successful in 42s
Add the two PR commands that touch content and the local worktree:
- `pr diff <n>` fetches the raw diff from the `.diff` endpoint (forcing a
text response, which the JSON-defaulting client would otherwise discard),
truncates at 4000 chars with separate `truncated`/`original_length` fields
and a prepended `--full` suggestion; `--full` returns the raw diff.
- `pr checkout <n>` reads the PR head branch from the PR fetch and fetches
`refs/pull/{n}/head` from origin (uniform for same-repo and fork PRs, ADR
0011), three-cased on local branch state so re-checkout is idempotent and
divergent local commits fail with `GIT_ERROR` rather than being discarded.
Introduces the `GIT_ERROR` mapping (`runGit`) carrying git's first stderr
line plus a remediation help line, and widens the PR 404 classifier so a
`.diff` path still resolves to `PR_NOT_FOUND`.
This commit is contained in:
116
test/pr-diff.test.ts
Normal file
116
test/pr-diff.test.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { startFixtureServer, type FixtureServer } from "./fixture-server.js";
|
||||
import { runCliTest, testModeEnv } from "./harness.js";
|
||||
|
||||
const DIFF_PATH = "/api/v1/repos/testowner/testrepo/pulls/7.diff";
|
||||
|
||||
let server: FixtureServer;
|
||||
|
||||
afterEach(async () => {
|
||||
await server.close();
|
||||
});
|
||||
|
||||
describe("pr diff", () => {
|
||||
it("outputs the raw diff verbatim when under the limit, with no truncation fields", async () => {
|
||||
const diff = [
|
||||
"diff --git a/README.md b/README.md",
|
||||
"index 1234567..89abcde 100644",
|
||||
"--- a/README.md",
|
||||
"+++ b/README.md",
|
||||
"@@ -1,3 +1,4 @@",
|
||||
" # Project",
|
||||
" ",
|
||||
"-Old tagline",
|
||||
"+New tagline",
|
||||
"+An extra line",
|
||||
"",
|
||||
].join("\n");
|
||||
|
||||
server = await startFixtureServer([
|
||||
{ method: "GET", path: DIFF_PATH, raw: diff },
|
||||
]);
|
||||
|
||||
const { stdout, exitCode } = await runCliTest(["pr", "diff", "7"], {
|
||||
env: testModeEnv(server.url),
|
||||
});
|
||||
|
||||
expect(exitCode).toBe(0);
|
||||
expect(stdout).toContain("pr_diff:");
|
||||
expect(stdout).toContain("number: 7");
|
||||
expect(stdout).toContain("+New tagline");
|
||||
expect(stdout).not.toContain("truncated:");
|
||||
expect(stdout).not.toContain("original_length:");
|
||||
});
|
||||
|
||||
it("truncates a diff over 4000 chars to the first 4000 and reports the original length", async () => {
|
||||
const diff = "x".repeat(4100);
|
||||
|
||||
server = await startFixtureServer([
|
||||
{ method: "GET", path: DIFF_PATH, raw: diff },
|
||||
]);
|
||||
|
||||
const { stdout, exitCode } = await runCliTest(["pr", "diff", "7"], {
|
||||
env: testModeEnv(server.url),
|
||||
});
|
||||
|
||||
expect(exitCode).toBe(0);
|
||||
expect(stdout).toContain("truncated: true");
|
||||
expect(stdout).toContain("original_length: 4100");
|
||||
expect(stdout).toContain("Run `gitea-axi pr diff 7");
|
||||
expect(stdout).toContain("--full");
|
||||
expect(stdout).toContain("to see the complete diff");
|
||||
expect(stdout).toContain("x".repeat(4000));
|
||||
expect(stdout).not.toContain("x".repeat(4001));
|
||||
});
|
||||
|
||||
it("passes a diff of exactly 4000 chars through untouched", async () => {
|
||||
const diff = "y".repeat(4000);
|
||||
|
||||
server = await startFixtureServer([
|
||||
{ method: "GET", path: DIFF_PATH, raw: diff },
|
||||
]);
|
||||
|
||||
const { stdout, exitCode } = await runCliTest(["pr", "diff", "7"], {
|
||||
env: testModeEnv(server.url),
|
||||
});
|
||||
|
||||
expect(exitCode).toBe(0);
|
||||
expect(stdout).not.toContain("truncated:");
|
||||
expect(stdout).not.toContain("original_length:");
|
||||
});
|
||||
|
||||
it("returns the complete diff and suppresses truncation with --full", async () => {
|
||||
const diff = "z".repeat(4100);
|
||||
|
||||
server = await startFixtureServer([
|
||||
{ method: "GET", path: DIFF_PATH, raw: diff },
|
||||
]);
|
||||
|
||||
const { stdout, exitCode } = await runCliTest(["pr", "diff", "7", "--full"], {
|
||||
env: testModeEnv(server.url),
|
||||
});
|
||||
|
||||
expect(exitCode).toBe(0);
|
||||
expect(stdout).toContain("z".repeat(4100));
|
||||
expect(stdout).not.toContain("truncated:");
|
||||
expect(stdout).not.toContain("original_length:");
|
||||
});
|
||||
|
||||
it("reports a nonexistent pull request as PR_NOT_FOUND with exit 1", async () => {
|
||||
server = await startFixtureServer([
|
||||
{
|
||||
method: "GET",
|
||||
path: "/api/v1/repos/testowner/testrepo/pulls/999.diff",
|
||||
status: 404,
|
||||
body: { message: "Not Found" },
|
||||
},
|
||||
]);
|
||||
|
||||
const { stdout, exitCode } = await runCliTest(["pr", "diff", "999"], {
|
||||
env: testModeEnv(server.url),
|
||||
});
|
||||
|
||||
expect(exitCode).toBe(1);
|
||||
expect(stdout).toContain("code: PR_NOT_FOUND");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user