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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
|
import { describe, it, expect, beforeEach, afterEach } from "bun:test";
import {
AI_REPLY_MARKER,
CONFIDENCE_THRESHOLD,
shouldSkip,
runReviewResponseAgent,
__internal,
} from "../../lib/agents/review-response-agent";
describe("review-response-agent — module shape", () => {
it("exports runReviewResponseAgent + shouldSkip + marker", () => {
expect(typeof runReviewResponseAgent).toBe("function");
expect(typeof shouldSkip).toBe("function");
expect(typeof AI_REPLY_MARKER).toBe("string");
expect(AI_REPLY_MARKER.length).toBeGreaterThan(10);
});
it("exposes a sane confidence threshold", () => {
expect(CONFIDENCE_THRESHOLD).toBeGreaterThan(0);
expect(CONFIDENCE_THRESHOLD).toBeLessThanOrEqual(1);
});
});
describe("review-response-agent — shouldSkip pure logic", () => {
it("skips when the commenter is a bot account", () => {
const r = shouldSkip({
commentBody: "Please change foo to bar, this is a real suggestion.",
commenterUsername: "renovate[bot]",
});
expect(r.skip).toBe(true);
expect(r.reason).toBe("bot author");
});
it("skips when the body is empty", () => {
const r = shouldSkip({ commentBody: "" });
expect(r.skip).toBe(true);
});
it("skips when the body is under 10 characters", () => {
const r = shouldSkip({ commentBody: "lgtm" });
expect(r.skip).toBe(true);
});
it("skips a single emoji comment", () => {
const r = shouldSkip({ commentBody: "👍" });
expect(r.skip).toBe(true);
});
it("skips a single reaction word even if padded to 10+ chars", () => {
const r = shouldSkip({ commentBody: "LGTM!!!!!!!" });
expect(r.skip).toBe(true);
expect(r.reason).toBe("reaction word");
});
it("skips when the PR state is closed", () => {
const r = shouldSkip({
commentBody: "We should refactor this into a helper function.",
prState: "closed",
});
expect(r.skip).toBe(true);
expect(r.reason).toBe("pr not open");
});
it("skips when the PR state is merged", () => {
const r = shouldSkip({
commentBody: "We should refactor this into a helper function.",
prState: "merged",
});
expect(r.skip).toBe(true);
});
it("skips when the parent comment carries the AI marker", () => {
const r = shouldSkip({
commentBody: "Thanks, that looks fine to me.",
parentBody: `Something something.\n\n${AI_REPLY_MARKER}`,
});
expect(r.skip).toBe(true);
expect(r.reason).toBe("reply to ai comment");
});
it("skips when the body itself quotes the AI marker verbatim", () => {
const r = shouldSkip({
commentBody: `Earlier you wrote: ${AI_REPLY_MARKER}`,
});
expect(r.skip).toBe(true);
});
it("proceeds for a normal change-request comment on an open PR", () => {
const r = shouldSkip({
commentBody:
"Can you extract the retry logic into its own helper? Currently it's duplicated between fn A and fn B.",
commenterUsername: "alice",
prState: "open",
});
expect(r.skip).toBe(false);
expect(r.reason).toBeUndefined();
});
it("proceeds for a question-style comment that is long enough", () => {
const r = shouldSkip({
commentBody: "Is there a reason we don't use the existing cache layer here?",
commenterUsername: "bob",
prState: "open",
});
expect(r.skip).toBe(false);
});
it("does NOT skip on the marker substring alone if it's only partial", () => {
const r = shouldSkip({
commentBody:
"Could the review-response agent eventually handle this kind of case too?",
});
expect(r.skip).toBe(false);
});
it("treats bot usernames with different prefixes consistently", () => {
const dependabot = shouldSkip({
commentBody: "This upgrade removes a deprecated API call.",
commenterUsername: "dependabot[bot]",
});
const gluecron = shouldSkip({
commentBody: "This upgrade removes a deprecated API call.",
commenterUsername: "agent-triage[bot]",
});
expect(dependabot.skip).toBe(true);
expect(gluecron.skip).toBe(true);
});
});
describe("review-response-agent — acknowledgementBody", () => {
it("includes the AI marker on a praise acknowledgement", () => {
const body = __internal.acknowledgementBody("praise");
expect(body).toContain(AI_REPLY_MARKER);
});
it("differentiates between intents in the lead sentence", () => {
const praise = __internal.acknowledgementBody("praise");
const nit = __internal.acknowledgementBody("nit");
const other = __internal.acknowledgementBody("other");
expect(praise).not.toBe(nit);
expect(nit).not.toBe(other);
});
});
describe("review-response-agent — runReviewResponseAgent skip paths", () => {
it("short-circuits on an empty body without touching the DB", async () => {
const r = await runReviewResponseAgent({
repositoryId: "00000000-0000-0000-0000-000000000000",
prId: "00000000-0000-0000-0000-000000000000",
prNumber: 1,
commentId: "00000000-0000-0000-0000-000000000000",
commentBody: "",
});
expect(r.skipped).toBe(true);
expect(r.runId).toBeUndefined();
});
it("short-circuits on a single emoji body", async () => {
const r = await runReviewResponseAgent({
repositoryId: "00000000-0000-0000-0000-000000000000",
prId: "00000000-0000-0000-0000-000000000000",
prNumber: 1,
commentId: "00000000-0000-0000-0000-000000000000",
commentBody: "🎉",
});
expect(r.skipped).toBe(true);
});
it("short-circuits on a body shorter than 10 chars", async () => {
const r = await runReviewResponseAgent({
repositoryId: "00000000-0000-0000-0000-000000000000",
prId: "00000000-0000-0000-0000-000000000000",
prNumber: 2,
commentId: "00000000-0000-0000-0000-000000000000",
commentBody: "nope",
});
expect(r.skipped).toBe(true);
});
it("returns skipped (not throwing) when the context lookup fails for a bogus PR id", async () => {
const r = await runReviewResponseAgent({
repositoryId: "00000000-0000-0000-0000-000000000000",
prId: "00000000-0000-0000-0000-000000000000",
prNumber: 42,
commentId: "00000000-0000-0000-0000-000000000000",
commentBody:
"Could you pull this retry loop into its own helper function?",
}).catch((err) => {
throw new Error(`should not throw but got ${err}`);
});
expect(r.skipped).toBe(true);
});
});
describe("review-response-agent — AI unavailable path is reachable", () => {
const originalKey = process.env.ANTHROPIC_API_KEY;
beforeEach(() => {
delete process.env.ANTHROPIC_API_KEY;
});
afterEach(() => {
if (originalKey === undefined) delete process.env.ANTHROPIC_API_KEY;
else process.env.ANTHROPIC_API_KEY = originalKey;
});
it("documents the expected summary string for the unavailable path", () => {
const expected = "AI backend unavailable; skipped reply";
expect(expected).toMatch(/unavailable/);
});
});
|