CodeIssuesDiscussionsWikiPull RequestsProjectsCommitsActionsReleasesContributorsPulse● GatesSecuritySettingsDeploymentsPipelineInsightsAgents✨ Explain✨ Ask AI✨ Workspace✨ Spec✨ Tests▓ Debt Map✨ NL Search🏛 Archaeology
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | /**
* Tests for src/lib/ai-review.ts.
*
* The Anthropic-calling path (reviewDiff) and the DB-touching paths
* (triggerAiReview's idempotency probe + insert) require external
* services we don't stand up in unit tests. We therefore cover the
* pure invariants:
*
* - The marker constant is stable so future searches keep working.
* - isAiReviewEnabled is a boolean reflecting config.anthropicApiKey.
* - triggerAiReview is a no-op (resolves cleanly) when the API key
* is absent — this is the documented graceful-degrade contract.
* - triggerAiReview never throws even when called with garbage.
* - The internal __test helpers are exported and shaped correctly.
*/
import { describe, it, expect } from "bun:test";
import {
AI_REVIEW_MARKER,
isAiReviewEnabled,
triggerAiReview,
__test,
} from "../lib/ai-review";
describe("AI_REVIEW_MARKER", () => {
it("is the documented stable string", () => {
// Important: any change to this string is a breaking change for
// idempotency. New version → write a migration to back-fill old
// markers so older AI summaries still suppress duplicates.
expect(AI_REVIEW_MARKER).toBe("<!-- gluecron-ai-review:summary -->");
});
it("is an HTML comment so it doesn't render in markdown", () => {
expect(AI_REVIEW_MARKER.startsWith("<!--")).toBe(true);
expect(AI_REVIEW_MARKER.endsWith("-->")).toBe(true);
});
});
describe("isAiReviewEnabled", () => {
it("returns a boolean", () => {
const v = isAiReviewEnabled();
expect(typeof v).toBe("boolean");
});
});
describe("triggerAiReview — graceful degrade + crash-free", () => {
// Note: in the test sandbox ANTHROPIC_API_KEY is unset, so the
// function should resolve immediately without touching the DB. We
// also pass garbage repo names + nonexistent PR ids to confirm the
// overall try/catch holds.
it("resolves without throwing when API key is absent", async () => {
const before = process.env.ANTHROPIC_API_KEY;
delete process.env.ANTHROPIC_API_KEY;
try {
await triggerAiReview(
"alice",
"demo",
"00000000-0000-0000-0000-000000000000",
"Test PR",
"Body",
"main",
"feature"
);
} finally {
if (before) process.env.ANTHROPIC_API_KEY = before;
}
expect(true).toBe(true);
});
it("never throws even with invalid inputs", async () => {
let threw = false;
try {
await triggerAiReview(
"",
"",
"not-a-uuid",
"",
"",
"",
""
);
} catch {
threw = true;
}
expect(threw).toBe(false);
});
it("survives an unknown branch combination without throwing", async () => {
let threw = false;
try {
await triggerAiReview(
"definitely-not-a-real-owner",
"definitely-not-a-real-repo",
"00000000-0000-0000-0000-000000000000",
"Title",
"Body",
"definitely-not-a-real-base",
"definitely-not-a-real-head"
);
} catch {
threw = true;
}
expect(threw).toBe(false);
});
});
describe("__test internals", () => {
it("exports diffBetweenBranches and alreadyReviewed", () => {
expect(typeof __test.diffBetweenBranches).toBe("function");
expect(typeof __test.alreadyReviewed).toBe("function");
});
it("diffBetweenBranches returns '' for a nonexistent repo", async () => {
const out = await __test.diffBetweenBranches(
"definitely-not-a-real-owner",
"definitely-not-a-real-repo",
"main",
"feature"
);
expect(out).toBe("");
});
it("alreadyReviewed returns false for an unknown PR id (fail-open)", async () => {
const out = await __test.alreadyReviewed("00000000-0000-0000-0000-000000000000");
expect(out).toBe(false);
});
});
|