CodeIssuesDiscussionsWikiPull RequestsProjectsCommitsActionsReleasesContributorsPulse● GatesSecuritySettingsDeploymentsPipelineInsightsAgents✨ Explain✨ Ask AI✨ Workspace✨ Spec✨ Tests▓ Debt Map✨ NL Search🏛 Archaeology
Blame · Line-by-line history
pulls-ai-rereview.test.ts
Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.
| c3e0c07 | 1 | /** |
| 2 | * Smoke tests for the new on-demand AI re-review endpoint: | |
| 3 | * POST /:owner/:repo/pulls/:number/ai-rereview | |
| 4 | * | |
| 5 | * Write-access only. Verifies auth-guard contracts. | |
| 6 | */ | |
| 7 | ||
| 8 | import { describe, it, expect } from "bun:test"; | |
| 9 | import app from "../app"; | |
| 10 | ||
| 11 | describe("POST /:owner/:repo/pulls/:number/ai-rereview — auth guard", () => { | |
| 12 | it("redirects to /login when unauthenticated", async () => { | |
| 13 | const res = await app.request( | |
| 14 | "/alice/demo/pulls/1/ai-rereview", | |
| 15 | { | |
| 16 | method: "POST", | |
| 17 | headers: { "content-type": "application/x-www-form-urlencoded" }, | |
| 18 | body: "", | |
| 19 | redirect: "manual", | |
| 20 | } | |
| 21 | ); | |
| 22 | expect([301, 302, 303, 307, 401, 403, 404, 503]).toContain(res.status); | |
| 23 | if (res.status === 302 || res.status === 303 || res.status === 307) { | |
| 24 | const loc = res.headers.get("location") || ""; | |
| 25 | expect(loc).toContain("/login"); | |
| 26 | } | |
| 27 | }); | |
| 28 | ||
| 29 | it("rejects bogus bearer tokens", async () => { | |
| 30 | const res = await app.request( | |
| 31 | "/alice/demo/pulls/1/ai-rereview", | |
| 32 | { | |
| 33 | method: "POST", | |
| 34 | headers: { | |
| 35 | "content-type": "application/x-www-form-urlencoded", | |
| 36 | authorization: "Bearer glct_definitely-not-valid", | |
| 37 | }, | |
| 38 | body: "", | |
| 39 | } | |
| 40 | ); | |
| 41 | expect([401, 403, 404, 503]).toContain(res.status); | |
| 42 | }); | |
| 43 | }); | |
| 44 | ||
| 45 | describe("triggerAiReview — force option (idempotency bypass)", () => { | |
| 46 | it("accepts and propagates the force flag without throwing", async () => { | |
| 47 | const { triggerAiReview } = await import("../lib/ai-review"); | |
| 48 | let threw = false; | |
| 49 | try { | |
| 50 | // No API key in test env → should bail at isAiReviewEnabled check. | |
| 51 | // The force flag is just a parameter pass-through; we verify it | |
| 52 | // doesn't change the never-throw contract. | |
| 53 | await triggerAiReview( | |
| 54 | "alice", | |
| 55 | "demo", | |
| 56 | "00000000-0000-0000-0000-000000000000", | |
| 57 | "Title", | |
| 58 | "Body", | |
| 59 | "main", | |
| 60 | "feature", | |
| 61 | { force: true } | |
| 62 | ); | |
| 63 | } catch { | |
| 64 | threw = true; | |
| 65 | } | |
| 66 | expect(threw).toBe(false); | |
| 67 | }); | |
| 68 | ||
| 69 | it("default options (no force) still works", async () => { | |
| 70 | const { triggerAiReview } = await import("../lib/ai-review"); | |
| 71 | let threw = false; | |
| 72 | try { | |
| 73 | await triggerAiReview( | |
| 74 | "alice", | |
| 75 | "demo", | |
| 76 | "00000000-0000-0000-0000-000000000000", | |
| 77 | "Title", | |
| 78 | "Body", | |
| 79 | "main", | |
| 80 | "feature" | |
| 81 | ); | |
| 82 | } catch { | |
| 83 | threw = true; | |
| 84 | } | |
| 85 | expect(threw).toBe(false); | |
| 86 | }); | |
| 87 | }); |