postgresai 0.14.0-dev.7 → 0.14.0-dev.71
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +161 -61
- package/bin/postgres-ai.ts +1982 -404
- package/bun.lock +258 -0
- package/bunfig.toml +20 -0
- package/dist/bin/postgres-ai.js +29395 -1576
- package/dist/sql/01.role.sql +16 -0
- package/dist/sql/02.permissions.sql +37 -0
- package/dist/sql/03.optional_rds.sql +6 -0
- package/dist/sql/04.optional_self_managed.sql +8 -0
- package/dist/sql/05.helpers.sql +439 -0
- package/dist/sql/sql/01.role.sql +16 -0
- package/dist/sql/sql/02.permissions.sql +37 -0
- package/dist/sql/sql/03.optional_rds.sql +6 -0
- package/dist/sql/sql/04.optional_self_managed.sql +8 -0
- package/dist/sql/sql/05.helpers.sql +439 -0
- package/lib/auth-server.ts +124 -106
- package/lib/checkup-api.ts +386 -0
- package/lib/checkup.ts +1396 -0
- package/lib/config.ts +6 -3
- package/lib/init.ts +568 -155
- package/lib/issues.ts +400 -191
- package/lib/mcp-server.ts +213 -90
- package/lib/metrics-embedded.ts +79 -0
- package/lib/metrics-loader.ts +127 -0
- package/lib/supabase.ts +769 -0
- package/lib/util.ts +61 -0
- package/package.json +20 -10
- package/packages/postgres-ai/README.md +26 -0
- package/packages/postgres-ai/bin/postgres-ai.js +27 -0
- package/packages/postgres-ai/package.json +27 -0
- package/scripts/embed-metrics.ts +154 -0
- package/sql/01.role.sql +16 -0
- package/sql/02.permissions.sql +37 -0
- package/sql/03.optional_rds.sql +6 -0
- package/sql/04.optional_self_managed.sql +8 -0
- package/sql/05.helpers.sql +439 -0
- package/test/auth.test.ts +258 -0
- package/test/checkup.integration.test.ts +321 -0
- package/test/checkup.test.ts +1117 -0
- package/test/config-consistency.test.ts +36 -0
- package/test/init.integration.test.ts +500 -0
- package/test/init.test.ts +682 -0
- package/test/issues.cli.test.ts +314 -0
- package/test/issues.test.ts +456 -0
- package/test/mcp-server.test.ts +988 -0
- package/test/schema-validation.test.ts +81 -0
- package/test/supabase.test.ts +568 -0
- package/test/test-utils.ts +128 -0
- package/tsconfig.json +12 -20
- package/dist/bin/postgres-ai.d.ts +0 -3
- package/dist/bin/postgres-ai.d.ts.map +0 -1
- package/dist/bin/postgres-ai.js.map +0 -1
- package/dist/lib/auth-server.d.ts +0 -31
- package/dist/lib/auth-server.d.ts.map +0 -1
- package/dist/lib/auth-server.js +0 -263
- package/dist/lib/auth-server.js.map +0 -1
- package/dist/lib/config.d.ts +0 -45
- package/dist/lib/config.d.ts.map +0 -1
- package/dist/lib/config.js +0 -181
- package/dist/lib/config.js.map +0 -1
- package/dist/lib/init.d.ts +0 -61
- package/dist/lib/init.d.ts.map +0 -1
- package/dist/lib/init.js +0 -359
- package/dist/lib/init.js.map +0 -1
- package/dist/lib/issues.d.ts +0 -75
- package/dist/lib/issues.d.ts.map +0 -1
- package/dist/lib/issues.js +0 -336
- package/dist/lib/issues.js.map +0 -1
- package/dist/lib/mcp-server.d.ts +0 -9
- package/dist/lib/mcp-server.d.ts.map +0 -1
- package/dist/lib/mcp-server.js +0 -168
- package/dist/lib/mcp-server.js.map +0 -1
- package/dist/lib/pkce.d.ts +0 -32
- package/dist/lib/pkce.d.ts.map +0 -1
- package/dist/lib/pkce.js +0 -101
- package/dist/lib/pkce.js.map +0 -1
- package/dist/lib/util.d.ts +0 -27
- package/dist/lib/util.d.ts.map +0 -1
- package/dist/lib/util.js +0 -46
- package/dist/lib/util.js.map +0 -1
- package/dist/package.json +0 -46
- package/test/init.integration.test.cjs +0 -269
- package/test/init.test.cjs +0 -69
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
import { describe, test, expect, mock, beforeEach, afterEach } from "bun:test";
|
|
2
|
+
import { createIssue, updateIssue, updateIssueComment } from "../lib/issues";
|
|
3
|
+
|
|
4
|
+
// Mock fetch globally
|
|
5
|
+
const originalFetch = globalThis.fetch;
|
|
6
|
+
|
|
7
|
+
describe("createIssue", () => {
|
|
8
|
+
afterEach(() => {
|
|
9
|
+
globalThis.fetch = originalFetch;
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
test("throws when apiKey is missing", async () => {
|
|
13
|
+
await expect(
|
|
14
|
+
createIssue({
|
|
15
|
+
apiKey: "",
|
|
16
|
+
apiBaseUrl: "https://api.example.com",
|
|
17
|
+
title: "Test Issue",
|
|
18
|
+
orgId: 1,
|
|
19
|
+
})
|
|
20
|
+
).rejects.toThrow("API key is required");
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test("throws when title is missing", async () => {
|
|
24
|
+
await expect(
|
|
25
|
+
createIssue({
|
|
26
|
+
apiKey: "test-key",
|
|
27
|
+
apiBaseUrl: "https://api.example.com",
|
|
28
|
+
title: "",
|
|
29
|
+
orgId: 1,
|
|
30
|
+
})
|
|
31
|
+
).rejects.toThrow("title is required");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("throws when orgId is not a number", async () => {
|
|
35
|
+
await expect(
|
|
36
|
+
createIssue({
|
|
37
|
+
apiKey: "test-key",
|
|
38
|
+
apiBaseUrl: "https://api.example.com",
|
|
39
|
+
title: "Test Issue",
|
|
40
|
+
orgId: undefined as unknown as number,
|
|
41
|
+
})
|
|
42
|
+
).rejects.toThrow("orgId is required");
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test("accepts orgId=0 as valid", async () => {
|
|
46
|
+
const mockResponse = {
|
|
47
|
+
id: "test-id",
|
|
48
|
+
title: "Test Issue",
|
|
49
|
+
description: null,
|
|
50
|
+
created_at: "2025-01-01T00:00:00Z",
|
|
51
|
+
status: 0,
|
|
52
|
+
project_id: null,
|
|
53
|
+
labels: null,
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
globalThis.fetch = mock(() =>
|
|
57
|
+
Promise.resolve(
|
|
58
|
+
new Response(JSON.stringify(mockResponse), {
|
|
59
|
+
status: 200,
|
|
60
|
+
headers: { "Content-Type": "application/json" },
|
|
61
|
+
})
|
|
62
|
+
)
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const result = await createIssue({
|
|
66
|
+
apiKey: "test-key",
|
|
67
|
+
apiBaseUrl: "https://api.example.com",
|
|
68
|
+
title: "Test Issue",
|
|
69
|
+
orgId: 0,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
expect(result.id).toBe("test-id");
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("makes correct API call with all parameters", async () => {
|
|
76
|
+
const mockResponse = {
|
|
77
|
+
id: "test-id",
|
|
78
|
+
title: "Test Issue",
|
|
79
|
+
description: "Test description",
|
|
80
|
+
created_at: "2025-01-01T00:00:00Z",
|
|
81
|
+
status: 0,
|
|
82
|
+
project_id: 123,
|
|
83
|
+
labels: ["bug", "urgent"],
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
let capturedRequest: { url: string; options: RequestInit } | null = null;
|
|
87
|
+
|
|
88
|
+
globalThis.fetch = mock((url: string, options: RequestInit) => {
|
|
89
|
+
capturedRequest = { url, options };
|
|
90
|
+
return Promise.resolve(
|
|
91
|
+
new Response(JSON.stringify(mockResponse), {
|
|
92
|
+
status: 200,
|
|
93
|
+
headers: { "Content-Type": "application/json" },
|
|
94
|
+
})
|
|
95
|
+
);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const result = await createIssue({
|
|
99
|
+
apiKey: "test-key",
|
|
100
|
+
apiBaseUrl: "https://api.example.com",
|
|
101
|
+
title: "Test Issue",
|
|
102
|
+
orgId: 1,
|
|
103
|
+
description: "Test description",
|
|
104
|
+
projectId: 123,
|
|
105
|
+
labels: ["bug", "urgent"],
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
expect(capturedRequest).not.toBeNull();
|
|
109
|
+
expect(capturedRequest!.url).toBe("https://api.example.com/rpc/issue_create");
|
|
110
|
+
expect(capturedRequest!.options.method).toBe("POST");
|
|
111
|
+
|
|
112
|
+
const body = JSON.parse(capturedRequest!.options.body as string);
|
|
113
|
+
expect(body.title).toBe("Test Issue");
|
|
114
|
+
expect(body.org_id).toBe(1);
|
|
115
|
+
expect(body.description).toBe("Test description");
|
|
116
|
+
expect(body.project_id).toBe(123);
|
|
117
|
+
expect(body.labels).toEqual(["bug", "urgent"]);
|
|
118
|
+
|
|
119
|
+
expect(result).toEqual(mockResponse);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test("handles API error response", async () => {
|
|
123
|
+
globalThis.fetch = mock(() =>
|
|
124
|
+
Promise.resolve(
|
|
125
|
+
new Response('{"message": "Unauthorized"}', {
|
|
126
|
+
status: 401,
|
|
127
|
+
headers: { "Content-Type": "application/json" },
|
|
128
|
+
})
|
|
129
|
+
)
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
await expect(
|
|
133
|
+
createIssue({
|
|
134
|
+
apiKey: "invalid-key",
|
|
135
|
+
apiBaseUrl: "https://api.example.com",
|
|
136
|
+
title: "Test Issue",
|
|
137
|
+
orgId: 1,
|
|
138
|
+
})
|
|
139
|
+
).rejects.toThrow(/Failed to create issue/);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
describe("updateIssue", () => {
|
|
144
|
+
afterEach(() => {
|
|
145
|
+
globalThis.fetch = originalFetch;
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test("throws when apiKey is missing", async () => {
|
|
149
|
+
await expect(
|
|
150
|
+
updateIssue({
|
|
151
|
+
apiKey: "",
|
|
152
|
+
apiBaseUrl: "https://api.example.com",
|
|
153
|
+
issueId: "test-id",
|
|
154
|
+
title: "Updated Title",
|
|
155
|
+
})
|
|
156
|
+
).rejects.toThrow("API key is required");
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test("throws when issueId is missing", async () => {
|
|
160
|
+
await expect(
|
|
161
|
+
updateIssue({
|
|
162
|
+
apiKey: "test-key",
|
|
163
|
+
apiBaseUrl: "https://api.example.com",
|
|
164
|
+
issueId: "",
|
|
165
|
+
title: "Updated Title",
|
|
166
|
+
})
|
|
167
|
+
).rejects.toThrow("issueId is required");
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
test("throws when no update fields are provided", async () => {
|
|
171
|
+
await expect(
|
|
172
|
+
updateIssue({
|
|
173
|
+
apiKey: "test-key",
|
|
174
|
+
apiBaseUrl: "https://api.example.com",
|
|
175
|
+
issueId: "test-id",
|
|
176
|
+
})
|
|
177
|
+
).rejects.toThrow("At least one field to update is required");
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test("accepts update with only title", async () => {
|
|
181
|
+
const mockResponse = {
|
|
182
|
+
id: "test-id",
|
|
183
|
+
title: "Updated Title",
|
|
184
|
+
description: null,
|
|
185
|
+
status: 0,
|
|
186
|
+
updated_at: "2025-01-01T00:00:00Z",
|
|
187
|
+
labels: null,
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
globalThis.fetch = mock(() =>
|
|
191
|
+
Promise.resolve(
|
|
192
|
+
new Response(JSON.stringify(mockResponse), {
|
|
193
|
+
status: 200,
|
|
194
|
+
headers: { "Content-Type": "application/json" },
|
|
195
|
+
})
|
|
196
|
+
)
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
const result = await updateIssue({
|
|
200
|
+
apiKey: "test-key",
|
|
201
|
+
apiBaseUrl: "https://api.example.com",
|
|
202
|
+
issueId: "test-id",
|
|
203
|
+
title: "Updated Title",
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
expect(result.title).toBe("Updated Title");
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
test("accepts update with only description", async () => {
|
|
210
|
+
const mockResponse = {
|
|
211
|
+
id: "test-id",
|
|
212
|
+
title: "Original Title",
|
|
213
|
+
description: "New description",
|
|
214
|
+
status: 0,
|
|
215
|
+
updated_at: "2025-01-01T00:00:00Z",
|
|
216
|
+
labels: null,
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
globalThis.fetch = mock(() =>
|
|
220
|
+
Promise.resolve(
|
|
221
|
+
new Response(JSON.stringify(mockResponse), {
|
|
222
|
+
status: 200,
|
|
223
|
+
headers: { "Content-Type": "application/json" },
|
|
224
|
+
})
|
|
225
|
+
)
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
const result = await updateIssue({
|
|
229
|
+
apiKey: "test-key",
|
|
230
|
+
apiBaseUrl: "https://api.example.com",
|
|
231
|
+
issueId: "test-id",
|
|
232
|
+
description: "New description",
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
expect(result.description).toBe("New description");
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
test("accepts update with only status", async () => {
|
|
239
|
+
const mockResponse = {
|
|
240
|
+
id: "test-id",
|
|
241
|
+
title: "Title",
|
|
242
|
+
description: null,
|
|
243
|
+
status: 1,
|
|
244
|
+
updated_at: "2025-01-01T00:00:00Z",
|
|
245
|
+
labels: null,
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
globalThis.fetch = mock(() =>
|
|
249
|
+
Promise.resolve(
|
|
250
|
+
new Response(JSON.stringify(mockResponse), {
|
|
251
|
+
status: 200,
|
|
252
|
+
headers: { "Content-Type": "application/json" },
|
|
253
|
+
})
|
|
254
|
+
)
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
const result = await updateIssue({
|
|
258
|
+
apiKey: "test-key",
|
|
259
|
+
apiBaseUrl: "https://api.example.com",
|
|
260
|
+
issueId: "test-id",
|
|
261
|
+
status: 1,
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
expect(result.status).toBe(1);
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
test("accepts update with only labels", async () => {
|
|
268
|
+
const mockResponse = {
|
|
269
|
+
id: "test-id",
|
|
270
|
+
title: "Title",
|
|
271
|
+
description: null,
|
|
272
|
+
status: 0,
|
|
273
|
+
updated_at: "2025-01-01T00:00:00Z",
|
|
274
|
+
labels: ["new-label"],
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
globalThis.fetch = mock(() =>
|
|
278
|
+
Promise.resolve(
|
|
279
|
+
new Response(JSON.stringify(mockResponse), {
|
|
280
|
+
status: 200,
|
|
281
|
+
headers: { "Content-Type": "application/json" },
|
|
282
|
+
})
|
|
283
|
+
)
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
const result = await updateIssue({
|
|
287
|
+
apiKey: "test-key",
|
|
288
|
+
apiBaseUrl: "https://api.example.com",
|
|
289
|
+
issueId: "test-id",
|
|
290
|
+
labels: ["new-label"],
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
expect(result.labels).toEqual(["new-label"]);
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
test("makes correct API call with all parameters", async () => {
|
|
297
|
+
const mockResponse = {
|
|
298
|
+
id: "test-id",
|
|
299
|
+
title: "Updated Title",
|
|
300
|
+
description: "Updated description",
|
|
301
|
+
status: 1,
|
|
302
|
+
updated_at: "2025-01-01T00:00:00Z",
|
|
303
|
+
labels: ["bug"],
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
let capturedRequest: { url: string; options: RequestInit } | null = null;
|
|
307
|
+
|
|
308
|
+
globalThis.fetch = mock((url: string, options: RequestInit) => {
|
|
309
|
+
capturedRequest = { url, options };
|
|
310
|
+
return Promise.resolve(
|
|
311
|
+
new Response(JSON.stringify(mockResponse), {
|
|
312
|
+
status: 200,
|
|
313
|
+
headers: { "Content-Type": "application/json" },
|
|
314
|
+
})
|
|
315
|
+
);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
await updateIssue({
|
|
319
|
+
apiKey: "test-key",
|
|
320
|
+
apiBaseUrl: "https://api.example.com",
|
|
321
|
+
issueId: "test-id",
|
|
322
|
+
title: "Updated Title",
|
|
323
|
+
description: "Updated description",
|
|
324
|
+
status: 1,
|
|
325
|
+
labels: ["bug"],
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
expect(capturedRequest).not.toBeNull();
|
|
329
|
+
expect(capturedRequest!.url).toBe("https://api.example.com/rpc/issue_update");
|
|
330
|
+
expect(capturedRequest!.options.method).toBe("POST");
|
|
331
|
+
|
|
332
|
+
const body = JSON.parse(capturedRequest!.options.body as string);
|
|
333
|
+
expect(body.p_id).toBe("test-id");
|
|
334
|
+
expect(body.p_title).toBe("Updated Title");
|
|
335
|
+
expect(body.p_description).toBe("Updated description");
|
|
336
|
+
expect(body.p_status).toBe(1);
|
|
337
|
+
expect(body.p_labels).toEqual(["bug"]);
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
test("handles API error response", async () => {
|
|
341
|
+
globalThis.fetch = mock(() =>
|
|
342
|
+
Promise.resolve(
|
|
343
|
+
new Response('{"message": "Not found"}', {
|
|
344
|
+
status: 404,
|
|
345
|
+
headers: { "Content-Type": "application/json" },
|
|
346
|
+
})
|
|
347
|
+
)
|
|
348
|
+
);
|
|
349
|
+
|
|
350
|
+
await expect(
|
|
351
|
+
updateIssue({
|
|
352
|
+
apiKey: "test-key",
|
|
353
|
+
apiBaseUrl: "https://api.example.com",
|
|
354
|
+
issueId: "nonexistent-id",
|
|
355
|
+
title: "Updated Title",
|
|
356
|
+
})
|
|
357
|
+
).rejects.toThrow(/Failed to update issue/);
|
|
358
|
+
});
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
describe("updateIssueComment", () => {
|
|
362
|
+
afterEach(() => {
|
|
363
|
+
globalThis.fetch = originalFetch;
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
test("throws when apiKey is missing", async () => {
|
|
367
|
+
await expect(
|
|
368
|
+
updateIssueComment({
|
|
369
|
+
apiKey: "",
|
|
370
|
+
apiBaseUrl: "https://api.example.com",
|
|
371
|
+
commentId: "test-id",
|
|
372
|
+
content: "Updated content",
|
|
373
|
+
})
|
|
374
|
+
).rejects.toThrow("API key is required");
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
test("throws when commentId is missing", async () => {
|
|
378
|
+
await expect(
|
|
379
|
+
updateIssueComment({
|
|
380
|
+
apiKey: "test-key",
|
|
381
|
+
apiBaseUrl: "https://api.example.com",
|
|
382
|
+
commentId: "",
|
|
383
|
+
content: "Updated content",
|
|
384
|
+
})
|
|
385
|
+
).rejects.toThrow("commentId is required");
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
test("throws when content is missing", async () => {
|
|
389
|
+
await expect(
|
|
390
|
+
updateIssueComment({
|
|
391
|
+
apiKey: "test-key",
|
|
392
|
+
apiBaseUrl: "https://api.example.com",
|
|
393
|
+
commentId: "test-id",
|
|
394
|
+
content: "",
|
|
395
|
+
})
|
|
396
|
+
).rejects.toThrow("content is required");
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
test("makes correct API call", async () => {
|
|
400
|
+
const mockResponse = {
|
|
401
|
+
id: "test-id",
|
|
402
|
+
issue_id: "issue-id",
|
|
403
|
+
content: "Updated content",
|
|
404
|
+
updated_at: "2025-01-01T00:00:00Z",
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
let capturedRequest: { url: string; options: RequestInit } | null = null;
|
|
408
|
+
|
|
409
|
+
globalThis.fetch = mock((url: string, options: RequestInit) => {
|
|
410
|
+
capturedRequest = { url, options };
|
|
411
|
+
return Promise.resolve(
|
|
412
|
+
new Response(JSON.stringify(mockResponse), {
|
|
413
|
+
status: 200,
|
|
414
|
+
headers: { "Content-Type": "application/json" },
|
|
415
|
+
})
|
|
416
|
+
);
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
const result = await updateIssueComment({
|
|
420
|
+
apiKey: "test-key",
|
|
421
|
+
apiBaseUrl: "https://api.example.com",
|
|
422
|
+
commentId: "test-id",
|
|
423
|
+
content: "Updated content",
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
expect(capturedRequest).not.toBeNull();
|
|
427
|
+
expect(capturedRequest!.url).toBe("https://api.example.com/rpc/issue_comment_update");
|
|
428
|
+
expect(capturedRequest!.options.method).toBe("POST");
|
|
429
|
+
|
|
430
|
+
const body = JSON.parse(capturedRequest!.options.body as string);
|
|
431
|
+
expect(body.p_id).toBe("test-id");
|
|
432
|
+
expect(body.p_content).toBe("Updated content");
|
|
433
|
+
|
|
434
|
+
expect(result).toEqual(mockResponse);
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
test("handles API error response", async () => {
|
|
438
|
+
globalThis.fetch = mock(() =>
|
|
439
|
+
Promise.resolve(
|
|
440
|
+
new Response('{"message": "Not found"}', {
|
|
441
|
+
status: 404,
|
|
442
|
+
headers: { "Content-Type": "application/json" },
|
|
443
|
+
})
|
|
444
|
+
)
|
|
445
|
+
);
|
|
446
|
+
|
|
447
|
+
await expect(
|
|
448
|
+
updateIssueComment({
|
|
449
|
+
apiKey: "test-key",
|
|
450
|
+
apiBaseUrl: "https://api.example.com",
|
|
451
|
+
commentId: "nonexistent-id",
|
|
452
|
+
content: "Updated content",
|
|
453
|
+
})
|
|
454
|
+
).rejects.toThrow(/Failed to update issue comment/);
|
|
455
|
+
});
|
|
456
|
+
});
|