@bbearai/mcp-server 0.3.3 → 0.5.0
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/dist/index-api.js +47 -2
- package/dist/index.js +145 -8
- package/package.json +1 -1
- package/src/index-api.ts +48 -3
- package/src/index.ts +160 -6
package/dist/index-api.js
CHANGED
|
@@ -115,7 +115,7 @@ const tools = [
|
|
|
115
115
|
enum: ['new', 'reviewed', 'in_progress', 'resolved', 'closed'],
|
|
116
116
|
description: 'New status',
|
|
117
117
|
},
|
|
118
|
-
|
|
118
|
+
resolution_notes: {
|
|
119
119
|
type: 'string',
|
|
120
120
|
description: 'Resolution notes (optional)',
|
|
121
121
|
},
|
|
@@ -227,6 +227,33 @@ const tools = [
|
|
|
227
227
|
required: ['test_key', 'title', 'steps', 'expected_result'],
|
|
228
228
|
},
|
|
229
229
|
},
|
|
230
|
+
{
|
|
231
|
+
name: 'delete_test_cases',
|
|
232
|
+
description: 'Delete one or more test cases. Supports single delete by ID or test_key, or bulk delete with arrays (max 50). WARNING: cascade-deletes associated test_assignments, test_feedback, and ai_test_runs.',
|
|
233
|
+
inputSchema: {
|
|
234
|
+
type: 'object',
|
|
235
|
+
properties: {
|
|
236
|
+
test_case_id: {
|
|
237
|
+
type: 'string',
|
|
238
|
+
description: 'UUID of a single test case to delete',
|
|
239
|
+
},
|
|
240
|
+
test_key: {
|
|
241
|
+
type: 'string',
|
|
242
|
+
description: 'Delete a single test case by test_key (e.g., TC-001)',
|
|
243
|
+
},
|
|
244
|
+
test_case_ids: {
|
|
245
|
+
type: 'array',
|
|
246
|
+
items: { type: 'string' },
|
|
247
|
+
description: 'Array of test case UUIDs to bulk delete (max 50)',
|
|
248
|
+
},
|
|
249
|
+
test_keys: {
|
|
250
|
+
type: 'array',
|
|
251
|
+
items: { type: 'string' },
|
|
252
|
+
description: 'Array of test_keys to bulk delete (max 50)',
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
},
|
|
230
257
|
{
|
|
231
258
|
name: 'get_qa_tracks',
|
|
232
259
|
description: 'Get QA tracks for the project',
|
|
@@ -267,7 +294,7 @@ async function handleTool(name, args) {
|
|
|
267
294
|
case 'update_report_status':
|
|
268
295
|
result = await apiRequest(`/reports/${args.report_id}`, 'PATCH', {
|
|
269
296
|
status: args.status,
|
|
270
|
-
|
|
297
|
+
resolution_notes: args.resolution_notes,
|
|
271
298
|
});
|
|
272
299
|
break;
|
|
273
300
|
case 'create_bug_report': {
|
|
@@ -319,6 +346,24 @@ async function handleTool(name, args) {
|
|
|
319
346
|
priority: args.priority || 'P2',
|
|
320
347
|
});
|
|
321
348
|
break;
|
|
349
|
+
case 'delete_test_cases': {
|
|
350
|
+
const ids = args.test_case_ids || (args.test_case_id ? [args.test_case_id] : undefined);
|
|
351
|
+
const keys = args.test_keys || (args.test_key ? [args.test_key] : undefined);
|
|
352
|
+
if (ids) {
|
|
353
|
+
const params = new URLSearchParams();
|
|
354
|
+
ids.forEach((id) => params.append('ids', id));
|
|
355
|
+
result = await apiRequest(`/test-cases?${params.toString()}`, 'DELETE');
|
|
356
|
+
}
|
|
357
|
+
else if (keys) {
|
|
358
|
+
const params = new URLSearchParams();
|
|
359
|
+
keys.forEach((k) => params.append('keys', k));
|
|
360
|
+
result = await apiRequest(`/test-cases?${params.toString()}`, 'DELETE');
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
result = { error: 'Must provide test_case_id, test_key, test_case_ids, or test_keys' };
|
|
364
|
+
}
|
|
365
|
+
break;
|
|
366
|
+
}
|
|
322
367
|
case 'get_qa_tracks':
|
|
323
368
|
result = await apiRequest('/qa-tracks');
|
|
324
369
|
break;
|
package/dist/index.js
CHANGED
|
@@ -127,7 +127,7 @@ const tools = [
|
|
|
127
127
|
enum: ['new', 'triaging', 'confirmed', 'in_progress', 'fixed', 'resolved', 'verified', 'wont_fix', 'duplicate'],
|
|
128
128
|
description: 'The new status for the report',
|
|
129
129
|
},
|
|
130
|
-
|
|
130
|
+
resolution_notes: {
|
|
131
131
|
type: 'string',
|
|
132
132
|
description: 'Optional resolution notes when marking as resolved',
|
|
133
133
|
},
|
|
@@ -275,6 +275,33 @@ const tools = [
|
|
|
275
275
|
},
|
|
276
276
|
},
|
|
277
277
|
},
|
|
278
|
+
{
|
|
279
|
+
name: 'delete_test_cases',
|
|
280
|
+
description: 'Delete one or more test cases. Supports single delete by ID or test_key, or bulk delete with arrays (max 50). WARNING: cascade-deletes associated test_assignments, test_feedback, and ai_test_runs. Reports/qa_findings keep their records but test_case_id becomes null.',
|
|
281
|
+
inputSchema: {
|
|
282
|
+
type: 'object',
|
|
283
|
+
properties: {
|
|
284
|
+
test_case_id: {
|
|
285
|
+
type: 'string',
|
|
286
|
+
description: 'UUID of a single test case to delete',
|
|
287
|
+
},
|
|
288
|
+
test_key: {
|
|
289
|
+
type: 'string',
|
|
290
|
+
description: 'Delete a single test case by test_key (e.g., TC-001)',
|
|
291
|
+
},
|
|
292
|
+
test_case_ids: {
|
|
293
|
+
type: 'array',
|
|
294
|
+
items: { type: 'string' },
|
|
295
|
+
description: 'Array of test case UUIDs to bulk delete (max 50)',
|
|
296
|
+
},
|
|
297
|
+
test_keys: {
|
|
298
|
+
type: 'array',
|
|
299
|
+
items: { type: 'string' },
|
|
300
|
+
description: 'Array of test_keys to bulk delete (max 50)',
|
|
301
|
+
},
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
},
|
|
278
305
|
{
|
|
279
306
|
name: 'list_test_cases',
|
|
280
307
|
description: 'List all test cases in the project. Returns test_key, title, target_route, and other metadata. Use this to see existing tests before updating them.',
|
|
@@ -880,7 +907,7 @@ const tools = [
|
|
|
880
907
|
async function listReports(args) {
|
|
881
908
|
let query = supabase
|
|
882
909
|
.from('reports')
|
|
883
|
-
.select('id, report_type, severity, status, description, app_context, created_at, tester:testers(name, email)')
|
|
910
|
+
.select('id, report_type, severity, status, description, app_context, created_at, reporter_name, reporter_email, tester:testers(name, email)')
|
|
884
911
|
.eq('project_id', PROJECT_ID)
|
|
885
912
|
.order('created_at', { ascending: false })
|
|
886
913
|
.limit(Math.min(args.limit || 10, 50));
|
|
@@ -902,7 +929,7 @@ async function listReports(args) {
|
|
|
902
929
|
status: r.status,
|
|
903
930
|
description: r.description,
|
|
904
931
|
route: r.app_context?.currentRoute,
|
|
905
|
-
reporter: r.tester?.name || 'Anonymous',
|
|
932
|
+
reporter: r.tester?.name || r.reporter_name || 'Anonymous',
|
|
906
933
|
created_at: r.created_at,
|
|
907
934
|
})),
|
|
908
935
|
total: data?.length || 0,
|
|
@@ -937,7 +964,10 @@ async function getReport(args) {
|
|
|
937
964
|
reporter: data.tester ? {
|
|
938
965
|
name: data.tester.name,
|
|
939
966
|
email: data.tester.email,
|
|
940
|
-
} :
|
|
967
|
+
} : (data.reporter_name ? {
|
|
968
|
+
name: data.reporter_name,
|
|
969
|
+
email: data.reporter_email,
|
|
970
|
+
} : null),
|
|
941
971
|
track: data.track ? {
|
|
942
972
|
name: data.track.name,
|
|
943
973
|
icon: data.track.icon,
|
|
@@ -987,8 +1017,8 @@ async function updateReportStatus(args) {
|
|
|
987
1017
|
return { error: 'Invalid report_id format' };
|
|
988
1018
|
}
|
|
989
1019
|
const updates = { status: args.status };
|
|
990
|
-
if (args.
|
|
991
|
-
updates.
|
|
1020
|
+
if (args.resolution_notes) {
|
|
1021
|
+
updates.resolution_notes = args.resolution_notes;
|
|
992
1022
|
}
|
|
993
1023
|
const { error } = await supabase
|
|
994
1024
|
.from('reports')
|
|
@@ -1194,6 +1224,110 @@ async function updateTestCase(args) {
|
|
|
1194
1224
|
updatedFields: Object.keys(updates),
|
|
1195
1225
|
};
|
|
1196
1226
|
}
|
|
1227
|
+
async function deleteTestCases(args) {
|
|
1228
|
+
// Validate: exactly one input mode
|
|
1229
|
+
const modes = [
|
|
1230
|
+
args.test_case_id,
|
|
1231
|
+
args.test_key,
|
|
1232
|
+
args.test_case_ids,
|
|
1233
|
+
args.test_keys,
|
|
1234
|
+
].filter(v => v !== undefined && v !== null);
|
|
1235
|
+
if (modes.length === 0) {
|
|
1236
|
+
return { error: 'Must provide one of: test_case_id, test_key, test_case_ids, or test_keys' };
|
|
1237
|
+
}
|
|
1238
|
+
if (modes.length > 1) {
|
|
1239
|
+
return { error: 'Provide only one of: test_case_id, test_key, test_case_ids, or test_keys' };
|
|
1240
|
+
}
|
|
1241
|
+
let idsToDelete = [];
|
|
1242
|
+
// Single ID
|
|
1243
|
+
if (args.test_case_id) {
|
|
1244
|
+
if (!isValidUUID(args.test_case_id)) {
|
|
1245
|
+
return { error: 'Invalid test_case_id format (must be UUID)' };
|
|
1246
|
+
}
|
|
1247
|
+
idsToDelete = [args.test_case_id];
|
|
1248
|
+
}
|
|
1249
|
+
// Single key
|
|
1250
|
+
if (args.test_key) {
|
|
1251
|
+
const { data: existing } = await supabase
|
|
1252
|
+
.from('test_cases')
|
|
1253
|
+
.select('id')
|
|
1254
|
+
.eq('project_id', PROJECT_ID)
|
|
1255
|
+
.eq('test_key', args.test_key)
|
|
1256
|
+
.single();
|
|
1257
|
+
if (!existing) {
|
|
1258
|
+
return { error: `Test case with key "${args.test_key}" not found` };
|
|
1259
|
+
}
|
|
1260
|
+
idsToDelete = [existing.id];
|
|
1261
|
+
}
|
|
1262
|
+
// Bulk IDs
|
|
1263
|
+
if (args.test_case_ids) {
|
|
1264
|
+
if (!Array.isArray(args.test_case_ids) || args.test_case_ids.length === 0) {
|
|
1265
|
+
return { error: 'test_case_ids must be a non-empty array' };
|
|
1266
|
+
}
|
|
1267
|
+
if (args.test_case_ids.length > 50) {
|
|
1268
|
+
return { error: 'Cannot delete more than 50 test cases at once' };
|
|
1269
|
+
}
|
|
1270
|
+
const invalidIds = args.test_case_ids.filter(id => !isValidUUID(id));
|
|
1271
|
+
if (invalidIds.length > 0) {
|
|
1272
|
+
return { error: `Invalid UUID(s): ${invalidIds.join(', ')}` };
|
|
1273
|
+
}
|
|
1274
|
+
idsToDelete = args.test_case_ids;
|
|
1275
|
+
}
|
|
1276
|
+
// Bulk keys
|
|
1277
|
+
if (args.test_keys) {
|
|
1278
|
+
if (!Array.isArray(args.test_keys) || args.test_keys.length === 0) {
|
|
1279
|
+
return { error: 'test_keys must be a non-empty array' };
|
|
1280
|
+
}
|
|
1281
|
+
if (args.test_keys.length > 50) {
|
|
1282
|
+
return { error: 'Cannot delete more than 50 test cases at once' };
|
|
1283
|
+
}
|
|
1284
|
+
const { data: existing, error: lookupError } = await supabase
|
|
1285
|
+
.from('test_cases')
|
|
1286
|
+
.select('id, test_key')
|
|
1287
|
+
.eq('project_id', PROJECT_ID)
|
|
1288
|
+
.in('test_key', args.test_keys);
|
|
1289
|
+
if (lookupError) {
|
|
1290
|
+
return { error: lookupError.message };
|
|
1291
|
+
}
|
|
1292
|
+
const foundKeys = (existing || []).map((tc) => tc.test_key);
|
|
1293
|
+
const notFound = args.test_keys.filter(k => !foundKeys.includes(k));
|
|
1294
|
+
if (notFound.length > 0) {
|
|
1295
|
+
return { error: `Test case(s) not found: ${notFound.join(', ')}` };
|
|
1296
|
+
}
|
|
1297
|
+
idsToDelete = (existing || []).map((tc) => tc.id);
|
|
1298
|
+
}
|
|
1299
|
+
// Pre-fetch details for the response
|
|
1300
|
+
const { data: toDelete } = await supabase
|
|
1301
|
+
.from('test_cases')
|
|
1302
|
+
.select('id, test_key, title')
|
|
1303
|
+
.eq('project_id', PROJECT_ID)
|
|
1304
|
+
.in('id', idsToDelete);
|
|
1305
|
+
if (!toDelete || toDelete.length === 0) {
|
|
1306
|
+
return { error: 'No matching test cases found in this project' };
|
|
1307
|
+
}
|
|
1308
|
+
// Delete
|
|
1309
|
+
const { error: deleteError } = await supabase
|
|
1310
|
+
.from('test_cases')
|
|
1311
|
+
.delete()
|
|
1312
|
+
.eq('project_id', PROJECT_ID)
|
|
1313
|
+
.in('id', idsToDelete);
|
|
1314
|
+
if (deleteError) {
|
|
1315
|
+
return { error: deleteError.message };
|
|
1316
|
+
}
|
|
1317
|
+
return {
|
|
1318
|
+
success: true,
|
|
1319
|
+
deletedCount: toDelete.length,
|
|
1320
|
+
deleted: toDelete.map((tc) => ({
|
|
1321
|
+
id: tc.id,
|
|
1322
|
+
testKey: tc.test_key,
|
|
1323
|
+
title: tc.title,
|
|
1324
|
+
})),
|
|
1325
|
+
message: toDelete.length === 1
|
|
1326
|
+
? `Test case ${toDelete[0].test_key} deleted successfully`
|
|
1327
|
+
: `${toDelete.length} test cases deleted successfully`,
|
|
1328
|
+
warning: 'Associated test_assignments, test_feedback, and ai_test_runs have been cascade-deleted. Reports and qa_findings referencing these tests now have null test_case_id.',
|
|
1329
|
+
};
|
|
1330
|
+
}
|
|
1197
1331
|
async function listTestCases(args) {
|
|
1198
1332
|
let query = supabase
|
|
1199
1333
|
.from('test_cases')
|
|
@@ -3157,7 +3291,7 @@ async function markFixedWithCommit(args) {
|
|
|
3157
3291
|
const updates = {
|
|
3158
3292
|
status: 'resolved',
|
|
3159
3293
|
resolved_at: new Date().toISOString(),
|
|
3160
|
-
|
|
3294
|
+
resolution_notes: args.resolution_notes || `Fixed in commit ${args.commit_sha.slice(0, 7)}`,
|
|
3161
3295
|
code_context: {
|
|
3162
3296
|
...existingContext,
|
|
3163
3297
|
fix: {
|
|
@@ -3387,7 +3521,7 @@ async function createRegressionTest(args) {
|
|
|
3387
3521
|
{
|
|
3388
3522
|
stepNumber: 3,
|
|
3389
3523
|
action: 'Verify the fix is working',
|
|
3390
|
-
expectedResult: report.
|
|
3524
|
+
expectedResult: report.resolution_notes || 'Feature works as expected',
|
|
3391
3525
|
},
|
|
3392
3526
|
],
|
|
3393
3527
|
expected_result: `The bug "${report.title}" should not recur`,
|
|
@@ -3952,6 +4086,9 @@ async function main() {
|
|
|
3952
4086
|
case 'update_test_case':
|
|
3953
4087
|
result = await updateTestCase(args);
|
|
3954
4088
|
break;
|
|
4089
|
+
case 'delete_test_cases':
|
|
4090
|
+
result = await deleteTestCases(args);
|
|
4091
|
+
break;
|
|
3955
4092
|
case 'list_test_cases':
|
|
3956
4093
|
result = await listTestCases(args);
|
|
3957
4094
|
break;
|
package/package.json
CHANGED
package/src/index-api.ts
CHANGED
|
@@ -40,7 +40,7 @@ function validateConfig() {
|
|
|
40
40
|
// API helper
|
|
41
41
|
async function apiRequest(
|
|
42
42
|
endpoint: string,
|
|
43
|
-
method: 'GET' | 'POST' | 'PATCH' = 'GET',
|
|
43
|
+
method: 'GET' | 'POST' | 'PATCH' | 'DELETE' = 'GET',
|
|
44
44
|
body?: Record<string, unknown>
|
|
45
45
|
): Promise<{ data?: unknown; error?: string }> {
|
|
46
46
|
try {
|
|
@@ -129,7 +129,7 @@ const tools = [
|
|
|
129
129
|
enum: ['new', 'reviewed', 'in_progress', 'resolved', 'closed'],
|
|
130
130
|
description: 'New status',
|
|
131
131
|
},
|
|
132
|
-
|
|
132
|
+
resolution_notes: {
|
|
133
133
|
type: 'string',
|
|
134
134
|
description: 'Resolution notes (optional)',
|
|
135
135
|
},
|
|
@@ -241,6 +241,33 @@ const tools = [
|
|
|
241
241
|
required: ['test_key', 'title', 'steps', 'expected_result'],
|
|
242
242
|
},
|
|
243
243
|
},
|
|
244
|
+
{
|
|
245
|
+
name: 'delete_test_cases',
|
|
246
|
+
description: 'Delete one or more test cases. Supports single delete by ID or test_key, or bulk delete with arrays (max 50). WARNING: cascade-deletes associated test_assignments, test_feedback, and ai_test_runs.',
|
|
247
|
+
inputSchema: {
|
|
248
|
+
type: 'object' as const,
|
|
249
|
+
properties: {
|
|
250
|
+
test_case_id: {
|
|
251
|
+
type: 'string',
|
|
252
|
+
description: 'UUID of a single test case to delete',
|
|
253
|
+
},
|
|
254
|
+
test_key: {
|
|
255
|
+
type: 'string',
|
|
256
|
+
description: 'Delete a single test case by test_key (e.g., TC-001)',
|
|
257
|
+
},
|
|
258
|
+
test_case_ids: {
|
|
259
|
+
type: 'array',
|
|
260
|
+
items: { type: 'string' },
|
|
261
|
+
description: 'Array of test case UUIDs to bulk delete (max 50)',
|
|
262
|
+
},
|
|
263
|
+
test_keys: {
|
|
264
|
+
type: 'array',
|
|
265
|
+
items: { type: 'string' },
|
|
266
|
+
description: 'Array of test_keys to bulk delete (max 50)',
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
},
|
|
270
|
+
},
|
|
244
271
|
{
|
|
245
272
|
name: 'get_qa_tracks',
|
|
246
273
|
description: 'Get QA tracks for the project',
|
|
@@ -284,7 +311,7 @@ async function handleTool(
|
|
|
284
311
|
case 'update_report_status':
|
|
285
312
|
result = await apiRequest(`/reports/${args.report_id}`, 'PATCH', {
|
|
286
313
|
status: args.status,
|
|
287
|
-
|
|
314
|
+
resolution_notes: args.resolution_notes,
|
|
288
315
|
});
|
|
289
316
|
break;
|
|
290
317
|
|
|
@@ -340,6 +367,24 @@ async function handleTool(
|
|
|
340
367
|
});
|
|
341
368
|
break;
|
|
342
369
|
|
|
370
|
+
case 'delete_test_cases': {
|
|
371
|
+
const ids = args.test_case_ids as string[] || (args.test_case_id ? [args.test_case_id as string] : undefined);
|
|
372
|
+
const keys = args.test_keys as string[] || (args.test_key ? [args.test_key as string] : undefined);
|
|
373
|
+
|
|
374
|
+
if (ids) {
|
|
375
|
+
const params = new URLSearchParams();
|
|
376
|
+
ids.forEach((id: string) => params.append('ids', id));
|
|
377
|
+
result = await apiRequest(`/test-cases?${params.toString()}`, 'DELETE');
|
|
378
|
+
} else if (keys) {
|
|
379
|
+
const params = new URLSearchParams();
|
|
380
|
+
keys.forEach((k: string) => params.append('keys', k));
|
|
381
|
+
result = await apiRequest(`/test-cases?${params.toString()}`, 'DELETE');
|
|
382
|
+
} else {
|
|
383
|
+
result = { error: 'Must provide test_case_id, test_key, test_case_ids, or test_keys' };
|
|
384
|
+
}
|
|
385
|
+
break;
|
|
386
|
+
}
|
|
387
|
+
|
|
343
388
|
case 'get_qa_tracks':
|
|
344
389
|
result = await apiRequest('/qa-tracks');
|
|
345
390
|
break;
|
package/src/index.ts
CHANGED
|
@@ -144,7 +144,7 @@ const tools = [
|
|
|
144
144
|
enum: ['new', 'triaging', 'confirmed', 'in_progress', 'fixed', 'resolved', 'verified', 'wont_fix', 'duplicate'],
|
|
145
145
|
description: 'The new status for the report',
|
|
146
146
|
},
|
|
147
|
-
|
|
147
|
+
resolution_notes: {
|
|
148
148
|
type: 'string',
|
|
149
149
|
description: 'Optional resolution notes when marking as resolved',
|
|
150
150
|
},
|
|
@@ -292,6 +292,33 @@ const tools = [
|
|
|
292
292
|
},
|
|
293
293
|
},
|
|
294
294
|
},
|
|
295
|
+
{
|
|
296
|
+
name: 'delete_test_cases',
|
|
297
|
+
description: 'Delete one or more test cases. Supports single delete by ID or test_key, or bulk delete with arrays (max 50). WARNING: cascade-deletes associated test_assignments, test_feedback, and ai_test_runs. Reports/qa_findings keep their records but test_case_id becomes null.',
|
|
298
|
+
inputSchema: {
|
|
299
|
+
type: 'object' as const,
|
|
300
|
+
properties: {
|
|
301
|
+
test_case_id: {
|
|
302
|
+
type: 'string',
|
|
303
|
+
description: 'UUID of a single test case to delete',
|
|
304
|
+
},
|
|
305
|
+
test_key: {
|
|
306
|
+
type: 'string',
|
|
307
|
+
description: 'Delete a single test case by test_key (e.g., TC-001)',
|
|
308
|
+
},
|
|
309
|
+
test_case_ids: {
|
|
310
|
+
type: 'array',
|
|
311
|
+
items: { type: 'string' },
|
|
312
|
+
description: 'Array of test case UUIDs to bulk delete (max 50)',
|
|
313
|
+
},
|
|
314
|
+
test_keys: {
|
|
315
|
+
type: 'array',
|
|
316
|
+
items: { type: 'string' },
|
|
317
|
+
description: 'Array of test_keys to bulk delete (max 50)',
|
|
318
|
+
},
|
|
319
|
+
},
|
|
320
|
+
},
|
|
321
|
+
},
|
|
295
322
|
{
|
|
296
323
|
name: 'list_test_cases',
|
|
297
324
|
description: 'List all test cases in the project. Returns test_key, title, target_route, and other metadata. Use this to see existing tests before updating them.',
|
|
@@ -1024,15 +1051,15 @@ async function searchReports(args: { query?: string; route?: string }) {
|
|
|
1024
1051
|
async function updateReportStatus(args: {
|
|
1025
1052
|
report_id: string;
|
|
1026
1053
|
status: string;
|
|
1027
|
-
|
|
1054
|
+
resolution_notes?: string;
|
|
1028
1055
|
}) {
|
|
1029
1056
|
if (!isValidUUID(args.report_id)) {
|
|
1030
1057
|
return { error: 'Invalid report_id format' };
|
|
1031
1058
|
}
|
|
1032
1059
|
|
|
1033
1060
|
const updates: Record<string, unknown> = { status: args.status };
|
|
1034
|
-
if (args.
|
|
1035
|
-
updates.
|
|
1061
|
+
if (args.resolution_notes) {
|
|
1062
|
+
updates.resolution_notes = args.resolution_notes;
|
|
1036
1063
|
}
|
|
1037
1064
|
|
|
1038
1065
|
const { error } = await supabase
|
|
@@ -1281,6 +1308,130 @@ async function updateTestCase(args: {
|
|
|
1281
1308
|
};
|
|
1282
1309
|
}
|
|
1283
1310
|
|
|
1311
|
+
async function deleteTestCases(args: {
|
|
1312
|
+
test_case_id?: string;
|
|
1313
|
+
test_key?: string;
|
|
1314
|
+
test_case_ids?: string[];
|
|
1315
|
+
test_keys?: string[];
|
|
1316
|
+
}) {
|
|
1317
|
+
// Validate: exactly one input mode
|
|
1318
|
+
const modes = [
|
|
1319
|
+
args.test_case_id,
|
|
1320
|
+
args.test_key,
|
|
1321
|
+
args.test_case_ids,
|
|
1322
|
+
args.test_keys,
|
|
1323
|
+
].filter(v => v !== undefined && v !== null);
|
|
1324
|
+
|
|
1325
|
+
if (modes.length === 0) {
|
|
1326
|
+
return { error: 'Must provide one of: test_case_id, test_key, test_case_ids, or test_keys' };
|
|
1327
|
+
}
|
|
1328
|
+
if (modes.length > 1) {
|
|
1329
|
+
return { error: 'Provide only one of: test_case_id, test_key, test_case_ids, or test_keys' };
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
let idsToDelete: string[] = [];
|
|
1333
|
+
|
|
1334
|
+
// Single ID
|
|
1335
|
+
if (args.test_case_id) {
|
|
1336
|
+
if (!isValidUUID(args.test_case_id)) {
|
|
1337
|
+
return { error: 'Invalid test_case_id format (must be UUID)' };
|
|
1338
|
+
}
|
|
1339
|
+
idsToDelete = [args.test_case_id];
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
// Single key
|
|
1343
|
+
if (args.test_key) {
|
|
1344
|
+
const { data: existing } = await supabase
|
|
1345
|
+
.from('test_cases')
|
|
1346
|
+
.select('id')
|
|
1347
|
+
.eq('project_id', PROJECT_ID)
|
|
1348
|
+
.eq('test_key', args.test_key)
|
|
1349
|
+
.single();
|
|
1350
|
+
|
|
1351
|
+
if (!existing) {
|
|
1352
|
+
return { error: `Test case with key "${args.test_key}" not found` };
|
|
1353
|
+
}
|
|
1354
|
+
idsToDelete = [existing.id];
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
// Bulk IDs
|
|
1358
|
+
if (args.test_case_ids) {
|
|
1359
|
+
if (!Array.isArray(args.test_case_ids) || args.test_case_ids.length === 0) {
|
|
1360
|
+
return { error: 'test_case_ids must be a non-empty array' };
|
|
1361
|
+
}
|
|
1362
|
+
if (args.test_case_ids.length > 50) {
|
|
1363
|
+
return { error: 'Cannot delete more than 50 test cases at once' };
|
|
1364
|
+
}
|
|
1365
|
+
const invalidIds = args.test_case_ids.filter(id => !isValidUUID(id));
|
|
1366
|
+
if (invalidIds.length > 0) {
|
|
1367
|
+
return { error: `Invalid UUID(s): ${invalidIds.join(', ')}` };
|
|
1368
|
+
}
|
|
1369
|
+
idsToDelete = args.test_case_ids;
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
// Bulk keys
|
|
1373
|
+
if (args.test_keys) {
|
|
1374
|
+
if (!Array.isArray(args.test_keys) || args.test_keys.length === 0) {
|
|
1375
|
+
return { error: 'test_keys must be a non-empty array' };
|
|
1376
|
+
}
|
|
1377
|
+
if (args.test_keys.length > 50) {
|
|
1378
|
+
return { error: 'Cannot delete more than 50 test cases at once' };
|
|
1379
|
+
}
|
|
1380
|
+
const { data: existing, error: lookupError } = await supabase
|
|
1381
|
+
.from('test_cases')
|
|
1382
|
+
.select('id, test_key')
|
|
1383
|
+
.eq('project_id', PROJECT_ID)
|
|
1384
|
+
.in('test_key', args.test_keys);
|
|
1385
|
+
|
|
1386
|
+
if (lookupError) {
|
|
1387
|
+
return { error: lookupError.message };
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
const foundKeys = (existing || []).map((tc: any) => tc.test_key);
|
|
1391
|
+
const notFound = args.test_keys.filter(k => !foundKeys.includes(k));
|
|
1392
|
+
if (notFound.length > 0) {
|
|
1393
|
+
return { error: `Test case(s) not found: ${notFound.join(', ')}` };
|
|
1394
|
+
}
|
|
1395
|
+
idsToDelete = (existing || []).map((tc: any) => tc.id);
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
// Pre-fetch details for the response
|
|
1399
|
+
const { data: toDelete } = await supabase
|
|
1400
|
+
.from('test_cases')
|
|
1401
|
+
.select('id, test_key, title')
|
|
1402
|
+
.eq('project_id', PROJECT_ID)
|
|
1403
|
+
.in('id', idsToDelete);
|
|
1404
|
+
|
|
1405
|
+
if (!toDelete || toDelete.length === 0) {
|
|
1406
|
+
return { error: 'No matching test cases found in this project' };
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
// Delete
|
|
1410
|
+
const { error: deleteError } = await supabase
|
|
1411
|
+
.from('test_cases')
|
|
1412
|
+
.delete()
|
|
1413
|
+
.eq('project_id', PROJECT_ID)
|
|
1414
|
+
.in('id', idsToDelete);
|
|
1415
|
+
|
|
1416
|
+
if (deleteError) {
|
|
1417
|
+
return { error: deleteError.message };
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
return {
|
|
1421
|
+
success: true,
|
|
1422
|
+
deletedCount: toDelete.length,
|
|
1423
|
+
deleted: toDelete.map((tc: any) => ({
|
|
1424
|
+
id: tc.id,
|
|
1425
|
+
testKey: tc.test_key,
|
|
1426
|
+
title: tc.title,
|
|
1427
|
+
})),
|
|
1428
|
+
message: toDelete.length === 1
|
|
1429
|
+
? `Test case ${toDelete[0].test_key} deleted successfully`
|
|
1430
|
+
: `${toDelete.length} test cases deleted successfully`,
|
|
1431
|
+
warning: 'Associated test_assignments, test_feedback, and ai_test_runs have been cascade-deleted. Reports and qa_findings referencing these tests now have null test_case_id.',
|
|
1432
|
+
};
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1284
1435
|
async function listTestCases(args: {
|
|
1285
1436
|
track?: string;
|
|
1286
1437
|
priority?: string;
|
|
@@ -3607,7 +3758,7 @@ async function markFixedWithCommit(args: {
|
|
|
3607
3758
|
const updates = {
|
|
3608
3759
|
status: 'resolved',
|
|
3609
3760
|
resolved_at: new Date().toISOString(),
|
|
3610
|
-
|
|
3761
|
+
resolution_notes: args.resolution_notes || `Fixed in commit ${args.commit_sha.slice(0, 7)}`,
|
|
3611
3762
|
code_context: {
|
|
3612
3763
|
...existingContext,
|
|
3613
3764
|
fix: {
|
|
@@ -3889,7 +4040,7 @@ async function createRegressionTest(args: {
|
|
|
3889
4040
|
{
|
|
3890
4041
|
stepNumber: 3,
|
|
3891
4042
|
action: 'Verify the fix is working',
|
|
3892
|
-
expectedResult: report.
|
|
4043
|
+
expectedResult: report.resolution_notes || 'Feature works as expected',
|
|
3893
4044
|
},
|
|
3894
4045
|
],
|
|
3895
4046
|
expected_result: `The bug "${report.title}" should not recur`,
|
|
@@ -4527,6 +4678,9 @@ async function main() {
|
|
|
4527
4678
|
case 'update_test_case':
|
|
4528
4679
|
result = await updateTestCase(args as any);
|
|
4529
4680
|
break;
|
|
4681
|
+
case 'delete_test_cases':
|
|
4682
|
+
result = await deleteTestCases(args as any);
|
|
4683
|
+
break;
|
|
4530
4684
|
case 'list_test_cases':
|
|
4531
4685
|
result = await listTestCases(args as any);
|
|
4532
4686
|
break;
|