@vibescope/mcp-server 0.2.1 → 0.2.3
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 +63 -38
- package/dist/api-client.d.ts +187 -0
- package/dist/api-client.js +53 -1
- package/dist/handlers/blockers.js +9 -8
- package/dist/handlers/bodies-of-work.js +14 -14
- package/dist/handlers/connectors.d.ts +45 -0
- package/dist/handlers/connectors.js +183 -0
- package/dist/handlers/cost.d.ts +10 -0
- package/dist/handlers/cost.js +54 -0
- package/dist/handlers/decisions.js +3 -3
- package/dist/handlers/deployment.js +35 -19
- package/dist/handlers/discovery.d.ts +7 -0
- package/dist/handlers/discovery.js +61 -2
- package/dist/handlers/fallback.js +5 -4
- package/dist/handlers/file-checkouts.d.ts +2 -0
- package/dist/handlers/file-checkouts.js +38 -6
- package/dist/handlers/findings.js +13 -12
- package/dist/handlers/git-issues.js +4 -4
- package/dist/handlers/ideas.js +5 -5
- package/dist/handlers/index.d.ts +1 -0
- package/dist/handlers/index.js +3 -0
- package/dist/handlers/milestones.js +5 -5
- package/dist/handlers/organizations.js +13 -13
- package/dist/handlers/progress.js +2 -2
- package/dist/handlers/project.js +6 -6
- package/dist/handlers/requests.js +3 -3
- package/dist/handlers/session.js +28 -9
- package/dist/handlers/sprints.js +17 -17
- package/dist/handlers/tasks.d.ts +2 -0
- package/dist/handlers/tasks.js +78 -20
- package/dist/handlers/types.d.ts +64 -2
- package/dist/handlers/types.js +48 -1
- package/dist/handlers/validation.js +3 -3
- package/dist/index.js +7 -2716
- package/dist/token-tracking.d.ts +74 -0
- package/dist/token-tracking.js +122 -0
- package/dist/tools.js +298 -9
- package/dist/utils.d.ts +5 -0
- package/dist/utils.js +17 -0
- package/docs/TOOLS.md +2053 -0
- package/package.json +4 -1
- package/scripts/generate-docs.ts +212 -0
- package/src/api-client.test.ts +723 -0
- package/src/api-client.ts +236 -1
- package/src/handlers/__test-setup__.ts +9 -0
- package/src/handlers/blockers.test.ts +31 -19
- package/src/handlers/blockers.ts +9 -8
- package/src/handlers/bodies-of-work.test.ts +55 -32
- package/src/handlers/bodies-of-work.ts +14 -14
- package/src/handlers/connectors.test.ts +834 -0
- package/src/handlers/connectors.ts +229 -0
- package/src/handlers/cost.ts +66 -0
- package/src/handlers/decisions.test.ts +34 -25
- package/src/handlers/decisions.ts +3 -3
- package/src/handlers/deployment.ts +39 -19
- package/src/handlers/discovery.ts +61 -2
- package/src/handlers/fallback.test.ts +26 -22
- package/src/handlers/fallback.ts +5 -4
- package/src/handlers/file-checkouts.test.ts +242 -49
- package/src/handlers/file-checkouts.ts +44 -6
- package/src/handlers/findings.test.ts +38 -24
- package/src/handlers/findings.ts +13 -12
- package/src/handlers/git-issues.test.ts +51 -43
- package/src/handlers/git-issues.ts +4 -4
- package/src/handlers/ideas.test.ts +28 -23
- package/src/handlers/ideas.ts +5 -5
- package/src/handlers/index.ts +3 -0
- package/src/handlers/milestones.test.ts +33 -28
- package/src/handlers/milestones.ts +5 -5
- package/src/handlers/organizations.test.ts +104 -83
- package/src/handlers/organizations.ts +13 -13
- package/src/handlers/progress.test.ts +20 -14
- package/src/handlers/progress.ts +2 -2
- package/src/handlers/project.test.ts +34 -27
- package/src/handlers/project.ts +6 -6
- package/src/handlers/requests.test.ts +27 -18
- package/src/handlers/requests.ts +3 -3
- package/src/handlers/session.test.ts +47 -0
- package/src/handlers/session.ts +26 -9
- package/src/handlers/sprints.test.ts +71 -50
- package/src/handlers/sprints.ts +17 -17
- package/src/handlers/tasks.test.ts +77 -15
- package/src/handlers/tasks.ts +90 -21
- package/src/handlers/tool-categories.test.ts +66 -0
- package/src/handlers/types.ts +81 -2
- package/src/handlers/validation.test.ts +78 -45
- package/src/handlers/validation.ts +3 -3
- package/src/index.ts +12 -2732
- package/src/token-tracking.test.ts +453 -0
- package/src/token-tracking.ts +164 -0
- package/src/tools.ts +298 -9
- package/src/utils.test.ts +2 -2
- package/src/utils.ts +17 -0
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* - checkin_file: Check in a file after editing
|
|
7
7
|
* - get_file_checkouts: Get active checkouts for a project
|
|
8
8
|
* - abandon_checkout: Force release a checkout
|
|
9
|
+
* - is_file_available: Check if a file is available for checkout
|
|
9
10
|
*/
|
|
10
11
|
|
|
11
12
|
import type { Handler, HandlerRegistry } from './types.js';
|
|
@@ -41,6 +42,11 @@ const abandonCheckoutSchema = {
|
|
|
41
42
|
file_path: { type: 'string' as const },
|
|
42
43
|
};
|
|
43
44
|
|
|
45
|
+
const isFileAvailableSchema = {
|
|
46
|
+
project_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
|
|
47
|
+
file_path: { type: 'string' as const, required: true as const },
|
|
48
|
+
};
|
|
49
|
+
|
|
44
50
|
export const checkoutFile: Handler = async (args, ctx) => {
|
|
45
51
|
const { project_id, file_path, reason } = parseArgs(args, checkoutFileSchema);
|
|
46
52
|
|
|
@@ -48,7 +54,7 @@ export const checkoutFile: Handler = async (args, ctx) => {
|
|
|
48
54
|
const response = await apiClient.checkoutFile(project_id, file_path, reason, ctx.session.currentSessionId || undefined);
|
|
49
55
|
|
|
50
56
|
if (!response.ok) {
|
|
51
|
-
|
|
57
|
+
return { result: { error: response.error || 'Failed to checkout file' }, isError: true };
|
|
52
58
|
}
|
|
53
59
|
|
|
54
60
|
return { result: response.data };
|
|
@@ -59,7 +65,7 @@ export const checkinFile: Handler = async (args, ctx) => {
|
|
|
59
65
|
|
|
60
66
|
// Validate that either checkout_id or both project_id and file_path are provided
|
|
61
67
|
if (!checkout_id && (!project_id || !file_path)) {
|
|
62
|
-
|
|
68
|
+
return { result: { error: 'Either checkout_id or both project_id and file_path are required' }, isError: true };
|
|
63
69
|
}
|
|
64
70
|
|
|
65
71
|
const apiClient = getApiClient();
|
|
@@ -71,7 +77,7 @@ export const checkinFile: Handler = async (args, ctx) => {
|
|
|
71
77
|
}, ctx.session.currentSessionId || undefined);
|
|
72
78
|
|
|
73
79
|
if (!response.ok) {
|
|
74
|
-
|
|
80
|
+
return { result: { error: response.error || 'Failed to checkin file' }, isError: true };
|
|
75
81
|
}
|
|
76
82
|
|
|
77
83
|
return { result: response.data };
|
|
@@ -88,7 +94,7 @@ export const getFileCheckouts: Handler = async (args, _ctx) => {
|
|
|
88
94
|
});
|
|
89
95
|
|
|
90
96
|
if (!response.ok) {
|
|
91
|
-
|
|
97
|
+
return { result: { error: response.error || 'Failed to get file checkouts' }, isError: true };
|
|
92
98
|
}
|
|
93
99
|
|
|
94
100
|
return { result: response.data };
|
|
@@ -99,7 +105,7 @@ export const abandonCheckout: Handler = async (args, _ctx) => {
|
|
|
99
105
|
|
|
100
106
|
// Validate that either checkout_id or both project_id and file_path are provided
|
|
101
107
|
if (!checkout_id && (!project_id || !file_path)) {
|
|
102
|
-
|
|
108
|
+
return { result: { error: 'Either checkout_id or both project_id and file_path are required' }, isError: true };
|
|
103
109
|
}
|
|
104
110
|
|
|
105
111
|
const apiClient = getApiClient();
|
|
@@ -110,12 +116,43 @@ export const abandonCheckout: Handler = async (args, _ctx) => {
|
|
|
110
116
|
});
|
|
111
117
|
|
|
112
118
|
if (!response.ok) {
|
|
113
|
-
|
|
119
|
+
return { result: { error: response.error || 'Failed to abandon checkout' }, isError: true };
|
|
114
120
|
}
|
|
115
121
|
|
|
116
122
|
return { result: response.data };
|
|
117
123
|
};
|
|
118
124
|
|
|
125
|
+
export const isFileAvailable: Handler = async (args, _ctx) => {
|
|
126
|
+
const { project_id, file_path } = parseArgs(args, isFileAvailableSchema);
|
|
127
|
+
|
|
128
|
+
const apiClient = getApiClient();
|
|
129
|
+
const response = await apiClient.getFileCheckouts(project_id, {
|
|
130
|
+
status: 'checked_out',
|
|
131
|
+
file_path,
|
|
132
|
+
limit: 1
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
if (!response.ok) {
|
|
136
|
+
return { result: { error: response.error || 'Failed to check file availability' }, isError: true };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const checkouts = response.data?.checkouts || [];
|
|
140
|
+
const activeCheckout = checkouts.length > 0 ? checkouts[0] : null;
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
result: {
|
|
144
|
+
available: !activeCheckout,
|
|
145
|
+
file_path,
|
|
146
|
+
checked_out_by: activeCheckout ? {
|
|
147
|
+
checkout_id: activeCheckout.id,
|
|
148
|
+
checked_out_by: activeCheckout.checked_out_by,
|
|
149
|
+
checked_out_at: activeCheckout.checked_out_at,
|
|
150
|
+
reason: activeCheckout.checkout_reason
|
|
151
|
+
} : null
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
};
|
|
155
|
+
|
|
119
156
|
/**
|
|
120
157
|
* File Checkouts handlers registry
|
|
121
158
|
*/
|
|
@@ -124,4 +161,5 @@ export const fileCheckoutHandlers: HandlerRegistry = {
|
|
|
124
161
|
checkin_file: checkinFile,
|
|
125
162
|
get_file_checkouts: getFileCheckouts,
|
|
126
163
|
abandon_checkout: abandonCheckout,
|
|
164
|
+
is_file_available: isFileAvailable,
|
|
127
165
|
};
|
|
@@ -106,16 +106,17 @@ describe('addFinding', () => {
|
|
|
106
106
|
);
|
|
107
107
|
});
|
|
108
108
|
|
|
109
|
-
it('should
|
|
109
|
+
it('should return error when API call fails', async () => {
|
|
110
110
|
mockApiClient.addFinding.mockResolvedValue({
|
|
111
111
|
ok: false,
|
|
112
112
|
error: 'Insert failed',
|
|
113
113
|
});
|
|
114
114
|
const ctx = createMockContext();
|
|
115
115
|
|
|
116
|
-
await
|
|
117
|
-
|
|
118
|
-
).
|
|
116
|
+
const result = await addFinding({ project_id: VALID_UUID, title: 'Test' }, ctx);
|
|
117
|
+
|
|
118
|
+
expect(result.isError).toBe(true);
|
|
119
|
+
expect(result.result).toMatchObject({ error: 'Insert failed' });
|
|
119
120
|
});
|
|
120
121
|
});
|
|
121
122
|
|
|
@@ -259,16 +260,17 @@ describe('getFindings', () => {
|
|
|
259
260
|
);
|
|
260
261
|
});
|
|
261
262
|
|
|
262
|
-
it('should
|
|
263
|
+
it('should return error when API call fails', async () => {
|
|
263
264
|
mockApiClient.getFindings.mockResolvedValue({
|
|
264
265
|
ok: false,
|
|
265
266
|
error: 'Query failed',
|
|
266
267
|
});
|
|
267
268
|
const ctx = createMockContext();
|
|
268
269
|
|
|
269
|
-
await
|
|
270
|
-
|
|
271
|
-
).
|
|
270
|
+
const result = await getFindings({ project_id: VALID_UUID }, ctx);
|
|
271
|
+
|
|
272
|
+
expect(result.isError).toBe(true);
|
|
273
|
+
expect(result.result).toMatchObject({ error: 'Query failed' });
|
|
272
274
|
});
|
|
273
275
|
});
|
|
274
276
|
|
|
@@ -339,16 +341,17 @@ describe('updateFinding', () => {
|
|
|
339
341
|
);
|
|
340
342
|
});
|
|
341
343
|
|
|
342
|
-
it('should
|
|
344
|
+
it('should return error when API call fails', async () => {
|
|
343
345
|
mockApiClient.updateFinding.mockResolvedValue({
|
|
344
346
|
ok: false,
|
|
345
347
|
error: 'Update failed',
|
|
346
348
|
});
|
|
347
349
|
const ctx = createMockContext();
|
|
348
350
|
|
|
349
|
-
await
|
|
350
|
-
|
|
351
|
-
).
|
|
351
|
+
const result = await updateFinding({ finding_id: VALID_UUID, title: 'Test' }, ctx);
|
|
352
|
+
|
|
353
|
+
expect(result.isError).toBe(true);
|
|
354
|
+
expect(result.result).toMatchObject({ error: 'Update failed' });
|
|
352
355
|
});
|
|
353
356
|
});
|
|
354
357
|
|
|
@@ -397,16 +400,17 @@ describe('deleteFinding', () => {
|
|
|
397
400
|
expect(mockApiClient.deleteFinding).toHaveBeenCalledWith(VALID_UUID);
|
|
398
401
|
});
|
|
399
402
|
|
|
400
|
-
it('should
|
|
403
|
+
it('should return error when API call fails', async () => {
|
|
401
404
|
mockApiClient.deleteFinding.mockResolvedValue({
|
|
402
405
|
ok: false,
|
|
403
406
|
error: 'Delete failed',
|
|
404
407
|
});
|
|
405
408
|
const ctx = createMockContext();
|
|
406
409
|
|
|
407
|
-
await
|
|
408
|
-
|
|
409
|
-
).
|
|
410
|
+
const result = await deleteFinding({ finding_id: VALID_UUID }, ctx);
|
|
411
|
+
|
|
412
|
+
expect(result.isError).toBe(true);
|
|
413
|
+
expect(result.result).toMatchObject({ error: 'Delete failed' });
|
|
410
414
|
});
|
|
411
415
|
});
|
|
412
416
|
|
|
@@ -461,16 +465,17 @@ describe('getFindingsStats', () => {
|
|
|
461
465
|
expect(mockApiClient.getFindingsStats).toHaveBeenCalledWith(VALID_UUID);
|
|
462
466
|
});
|
|
463
467
|
|
|
464
|
-
it('should
|
|
468
|
+
it('should return error when API call fails', async () => {
|
|
465
469
|
mockApiClient.getFindingsStats.mockResolvedValue({
|
|
466
470
|
ok: false,
|
|
467
471
|
error: 'Query failed',
|
|
468
472
|
});
|
|
469
473
|
const ctx = createMockContext();
|
|
470
474
|
|
|
471
|
-
await
|
|
472
|
-
|
|
473
|
-
).
|
|
475
|
+
const result = await getFindingsStats({ project_id: VALID_UUID }, ctx);
|
|
476
|
+
|
|
477
|
+
expect(result.isError).toBe(true);
|
|
478
|
+
expect(result.result).toMatchObject({ error: 'Query failed' });
|
|
474
479
|
});
|
|
475
480
|
});
|
|
476
481
|
|
|
@@ -495,6 +500,14 @@ describe('queryKnowledgeBase', () => {
|
|
|
495
500
|
).rejects.toThrow(ValidationError);
|
|
496
501
|
});
|
|
497
502
|
|
|
503
|
+
it('should throw error for invalid scope value', async () => {
|
|
504
|
+
const ctx = createMockContext();
|
|
505
|
+
|
|
506
|
+
await expect(
|
|
507
|
+
queryKnowledgeBase({ project_id: VALID_UUID, scope: 'invalid_scope' }, ctx)
|
|
508
|
+
).rejects.toThrow(ValidationError);
|
|
509
|
+
});
|
|
510
|
+
|
|
498
511
|
it('should query with default parameters', async () => {
|
|
499
512
|
mockApiClient.queryKnowledgeBase.mockResolvedValue({
|
|
500
513
|
ok: true,
|
|
@@ -605,15 +618,16 @@ describe('queryKnowledgeBase', () => {
|
|
|
605
618
|
);
|
|
606
619
|
});
|
|
607
620
|
|
|
608
|
-
it('should
|
|
621
|
+
it('should return error when API call fails', async () => {
|
|
609
622
|
mockApiClient.queryKnowledgeBase.mockResolvedValue({
|
|
610
623
|
ok: false,
|
|
611
624
|
error: 'Query failed',
|
|
612
625
|
});
|
|
613
626
|
const ctx = createMockContext();
|
|
614
627
|
|
|
615
|
-
await
|
|
616
|
-
|
|
617
|
-
).
|
|
628
|
+
const result = await queryKnowledgeBase({ project_id: VALID_UUID }, ctx);
|
|
629
|
+
|
|
630
|
+
expect(result.isError).toBe(true);
|
|
631
|
+
expect(result.result).toMatchObject({ error: 'Query failed' });
|
|
618
632
|
});
|
|
619
633
|
});
|
package/src/handlers/findings.ts
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import type { Handler, HandlerRegistry } from './types.js';
|
|
13
|
+
import { success, error } from './types.js';
|
|
13
14
|
import { parseArgs, uuidValidator, createEnumValidator } from '../validators.js';
|
|
14
15
|
import { getApiClient } from '../api-client.js';
|
|
15
16
|
|
|
@@ -86,10 +87,10 @@ export const addFinding: Handler = async (args, ctx) => {
|
|
|
86
87
|
}, ctx.session.currentSessionId || undefined);
|
|
87
88
|
|
|
88
89
|
if (!response.ok) {
|
|
89
|
-
|
|
90
|
+
return error(response.error || 'Failed to add finding');
|
|
90
91
|
}
|
|
91
92
|
|
|
92
|
-
return
|
|
93
|
+
return success(response.data);
|
|
93
94
|
};
|
|
94
95
|
|
|
95
96
|
export const getFindings: Handler = async (args, _ctx) => {
|
|
@@ -107,10 +108,10 @@ export const getFindings: Handler = async (args, _ctx) => {
|
|
|
107
108
|
});
|
|
108
109
|
|
|
109
110
|
if (!response.ok) {
|
|
110
|
-
|
|
111
|
+
return error(response.error || 'Failed to get findings');
|
|
111
112
|
}
|
|
112
113
|
|
|
113
|
-
return
|
|
114
|
+
return success(response.data);
|
|
114
115
|
};
|
|
115
116
|
|
|
116
117
|
/**
|
|
@@ -125,10 +126,10 @@ export const getFindingsStats: Handler = async (args, _ctx) => {
|
|
|
125
126
|
const response = await apiClient.getFindingsStats(project_id);
|
|
126
127
|
|
|
127
128
|
if (!response.ok) {
|
|
128
|
-
|
|
129
|
+
return error(response.error || 'Failed to get findings stats');
|
|
129
130
|
}
|
|
130
131
|
|
|
131
|
-
return
|
|
132
|
+
return success(response.data);
|
|
132
133
|
};
|
|
133
134
|
|
|
134
135
|
export const updateFinding: Handler = async (args, _ctx) => {
|
|
@@ -144,10 +145,10 @@ export const updateFinding: Handler = async (args, _ctx) => {
|
|
|
144
145
|
});
|
|
145
146
|
|
|
146
147
|
if (!response.ok) {
|
|
147
|
-
|
|
148
|
+
return error(response.error || 'Failed to update finding');
|
|
148
149
|
}
|
|
149
150
|
|
|
150
|
-
return
|
|
151
|
+
return success(response.data);
|
|
151
152
|
};
|
|
152
153
|
|
|
153
154
|
export const deleteFinding: Handler = async (args, _ctx) => {
|
|
@@ -157,10 +158,10 @@ export const deleteFinding: Handler = async (args, _ctx) => {
|
|
|
157
158
|
const response = await apiClient.deleteFinding(finding_id);
|
|
158
159
|
|
|
159
160
|
if (!response.ok) {
|
|
160
|
-
|
|
161
|
+
return error(response.error || 'Failed to delete finding');
|
|
161
162
|
}
|
|
162
163
|
|
|
163
|
-
return
|
|
164
|
+
return success(response.data);
|
|
164
165
|
};
|
|
165
166
|
|
|
166
167
|
/**
|
|
@@ -183,10 +184,10 @@ export const queryKnowledgeBase: Handler = async (args, _ctx) => {
|
|
|
183
184
|
});
|
|
184
185
|
|
|
185
186
|
if (!response.ok) {
|
|
186
|
-
|
|
187
|
+
return error(response.error || 'Failed to query knowledge base');
|
|
187
188
|
}
|
|
188
189
|
|
|
189
|
-
return
|
|
190
|
+
return success(response.data);
|
|
190
191
|
};
|
|
191
192
|
|
|
192
193
|
/**
|
|
@@ -60,7 +60,7 @@ describe('addGitIssue', () => {
|
|
|
60
60
|
issue_type: 'invalid_type',
|
|
61
61
|
branch: 'feature/test',
|
|
62
62
|
}, ctx)
|
|
63
|
-
).rejects.toThrow(
|
|
63
|
+
).rejects.toThrow(ValidationError);
|
|
64
64
|
});
|
|
65
65
|
|
|
66
66
|
it('should throw error for invalid task_id UUID', async () => {
|
|
@@ -180,35 +180,37 @@ describe('addGitIssue', () => {
|
|
|
180
180
|
}
|
|
181
181
|
});
|
|
182
182
|
|
|
183
|
-
it('should
|
|
183
|
+
it('should return error when API call fails', async () => {
|
|
184
184
|
mockApiClient.addGitIssue.mockResolvedValue({
|
|
185
185
|
ok: false,
|
|
186
186
|
error: 'Insert failed',
|
|
187
187
|
});
|
|
188
188
|
const ctx = createMockContext();
|
|
189
189
|
|
|
190
|
-
await
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
).
|
|
190
|
+
const result = await addGitIssue({
|
|
191
|
+
project_id: VALID_PROJECT_ID,
|
|
192
|
+
issue_type: 'merge_conflict',
|
|
193
|
+
branch: 'feature/test',
|
|
194
|
+
}, ctx);
|
|
195
|
+
|
|
196
|
+
expect(result.isError).toBe(true);
|
|
197
|
+
expect(result.result).toMatchObject({ error: 'Insert failed' });
|
|
197
198
|
});
|
|
198
199
|
|
|
199
|
-
it('should
|
|
200
|
+
it('should return default error message when API fails without error', async () => {
|
|
200
201
|
mockApiClient.addGitIssue.mockResolvedValue({
|
|
201
202
|
ok: false,
|
|
202
203
|
});
|
|
203
204
|
const ctx = createMockContext();
|
|
204
205
|
|
|
205
|
-
await
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
).
|
|
206
|
+
const result = await addGitIssue({
|
|
207
|
+
project_id: VALID_PROJECT_ID,
|
|
208
|
+
issue_type: 'merge_conflict',
|
|
209
|
+
branch: 'feature/test',
|
|
210
|
+
}, ctx);
|
|
211
|
+
|
|
212
|
+
expect(result.isError).toBe(true);
|
|
213
|
+
expect(result.result).toMatchObject({ error: 'Failed to add git issue' });
|
|
212
214
|
});
|
|
213
215
|
});
|
|
214
216
|
|
|
@@ -314,27 +316,29 @@ describe('resolveGitIssue', () => {
|
|
|
314
316
|
);
|
|
315
317
|
});
|
|
316
318
|
|
|
317
|
-
it('should
|
|
319
|
+
it('should return error when API call fails', async () => {
|
|
318
320
|
mockApiClient.resolveGitIssue.mockResolvedValue({
|
|
319
321
|
ok: false,
|
|
320
322
|
error: 'Update failed',
|
|
321
323
|
});
|
|
322
324
|
const ctx = createMockContext();
|
|
323
325
|
|
|
324
|
-
await
|
|
325
|
-
|
|
326
|
-
).
|
|
326
|
+
const result = await resolveGitIssue({ git_issue_id: VALID_GIT_ISSUE_ID }, ctx);
|
|
327
|
+
|
|
328
|
+
expect(result.isError).toBe(true);
|
|
329
|
+
expect(result.result).toMatchObject({ error: 'Update failed' });
|
|
327
330
|
});
|
|
328
331
|
|
|
329
|
-
it('should
|
|
332
|
+
it('should return default error message when API fails without error', async () => {
|
|
330
333
|
mockApiClient.resolveGitIssue.mockResolvedValue({
|
|
331
334
|
ok: false,
|
|
332
335
|
});
|
|
333
336
|
const ctx = createMockContext();
|
|
334
337
|
|
|
335
|
-
await
|
|
336
|
-
|
|
337
|
-
).
|
|
338
|
+
const result = await resolveGitIssue({ git_issue_id: VALID_GIT_ISSUE_ID }, ctx);
|
|
339
|
+
|
|
340
|
+
expect(result.isError).toBe(true);
|
|
341
|
+
expect(result.result).toMatchObject({ error: 'Failed to resolve git issue' });
|
|
338
342
|
});
|
|
339
343
|
});
|
|
340
344
|
|
|
@@ -364,7 +368,7 @@ describe('getGitIssues', () => {
|
|
|
364
368
|
|
|
365
369
|
await expect(
|
|
366
370
|
getGitIssues({ project_id: VALID_PROJECT_ID, status: 'invalid_status' }, ctx)
|
|
367
|
-
).rejects.toThrow(
|
|
371
|
+
).rejects.toThrow(ValidationError);
|
|
368
372
|
});
|
|
369
373
|
|
|
370
374
|
it('should throw error for invalid issue_type filter', async () => {
|
|
@@ -372,7 +376,7 @@ describe('getGitIssues', () => {
|
|
|
372
376
|
|
|
373
377
|
await expect(
|
|
374
378
|
getGitIssues({ project_id: VALID_PROJECT_ID, issue_type: 'invalid_type' }, ctx)
|
|
375
|
-
).rejects.toThrow(
|
|
379
|
+
).rejects.toThrow(ValidationError);
|
|
376
380
|
});
|
|
377
381
|
|
|
378
382
|
it('should return empty list when no git issues', async () => {
|
|
@@ -519,27 +523,29 @@ describe('getGitIssues', () => {
|
|
|
519
523
|
);
|
|
520
524
|
});
|
|
521
525
|
|
|
522
|
-
it('should
|
|
526
|
+
it('should return error when API call fails', async () => {
|
|
523
527
|
mockApiClient.getGitIssues.mockResolvedValue({
|
|
524
528
|
ok: false,
|
|
525
529
|
error: 'Query failed',
|
|
526
530
|
});
|
|
527
531
|
const ctx = createMockContext();
|
|
528
532
|
|
|
529
|
-
await
|
|
530
|
-
|
|
531
|
-
).
|
|
533
|
+
const result = await getGitIssues({ project_id: VALID_PROJECT_ID }, ctx);
|
|
534
|
+
|
|
535
|
+
expect(result.isError).toBe(true);
|
|
536
|
+
expect(result.result).toMatchObject({ error: 'Query failed' });
|
|
532
537
|
});
|
|
533
538
|
|
|
534
|
-
it('should
|
|
539
|
+
it('should return default error message when API fails without error', async () => {
|
|
535
540
|
mockApiClient.getGitIssues.mockResolvedValue({
|
|
536
541
|
ok: false,
|
|
537
542
|
});
|
|
538
543
|
const ctx = createMockContext();
|
|
539
544
|
|
|
540
|
-
await
|
|
541
|
-
|
|
542
|
-
).
|
|
545
|
+
const result = await getGitIssues({ project_id: VALID_PROJECT_ID }, ctx);
|
|
546
|
+
|
|
547
|
+
expect(result.isError).toBe(true);
|
|
548
|
+
expect(result.result).toMatchObject({ error: 'Failed to fetch git issues' });
|
|
543
549
|
});
|
|
544
550
|
});
|
|
545
551
|
|
|
@@ -598,26 +604,28 @@ describe('deleteGitIssue', () => {
|
|
|
598
604
|
);
|
|
599
605
|
});
|
|
600
606
|
|
|
601
|
-
it('should
|
|
607
|
+
it('should return error when API call fails', async () => {
|
|
602
608
|
mockApiClient.deleteGitIssue.mockResolvedValue({
|
|
603
609
|
ok: false,
|
|
604
610
|
error: 'Delete failed',
|
|
605
611
|
});
|
|
606
612
|
const ctx = createMockContext();
|
|
607
613
|
|
|
608
|
-
await
|
|
609
|
-
|
|
610
|
-
).
|
|
614
|
+
const result = await deleteGitIssue({ git_issue_id: VALID_GIT_ISSUE_ID }, ctx);
|
|
615
|
+
|
|
616
|
+
expect(result.isError).toBe(true);
|
|
617
|
+
expect(result.result).toMatchObject({ error: 'Delete failed' });
|
|
611
618
|
});
|
|
612
619
|
|
|
613
|
-
it('should
|
|
620
|
+
it('should return default error message when API fails without error', async () => {
|
|
614
621
|
mockApiClient.deleteGitIssue.mockResolvedValue({
|
|
615
622
|
ok: false,
|
|
616
623
|
});
|
|
617
624
|
const ctx = createMockContext();
|
|
618
625
|
|
|
619
|
-
await
|
|
620
|
-
|
|
621
|
-
).
|
|
626
|
+
const result = await deleteGitIssue({ git_issue_id: VALID_GIT_ISSUE_ID }, ctx);
|
|
627
|
+
|
|
628
|
+
expect(result.isError).toBe(true);
|
|
629
|
+
expect(result.result).toMatchObject({ error: 'Failed to delete git issue' });
|
|
622
630
|
});
|
|
623
631
|
});
|
|
@@ -70,7 +70,7 @@ export const addGitIssue: Handler = async (args, ctx) => {
|
|
|
70
70
|
}, ctx.session.currentSessionId || undefined);
|
|
71
71
|
|
|
72
72
|
if (!response.ok) {
|
|
73
|
-
|
|
73
|
+
return { result: { error: response.error || 'Failed to add git issue' }, isError: true };
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
return { result: response.data };
|
|
@@ -86,7 +86,7 @@ export const resolveGitIssue: Handler = async (args, ctx) => {
|
|
|
86
86
|
}, ctx.session.currentSessionId || undefined);
|
|
87
87
|
|
|
88
88
|
if (!response.ok) {
|
|
89
|
-
|
|
89
|
+
return { result: { error: response.error || 'Failed to resolve git issue' }, isError: true };
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
return { result: response.data };
|
|
@@ -104,7 +104,7 @@ export const getGitIssues: Handler = async (args, _ctx) => {
|
|
|
104
104
|
});
|
|
105
105
|
|
|
106
106
|
if (!response.ok) {
|
|
107
|
-
|
|
107
|
+
return { result: { error: response.error || 'Failed to fetch git issues' }, isError: true };
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
return { result: response.data };
|
|
@@ -117,7 +117,7 @@ export const deleteGitIssue: Handler = async (args, _ctx) => {
|
|
|
117
117
|
const response = await apiClient.deleteGitIssue(git_issue_id);
|
|
118
118
|
|
|
119
119
|
if (!response.ok) {
|
|
120
|
-
|
|
120
|
+
return { result: { error: response.error || 'Failed to delete git issue' }, isError: true };
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
return { result: response.data };
|