@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
|
@@ -68,14 +68,17 @@ describe('listOrganizations', () => {
|
|
|
68
68
|
expect(result.result).toMatchObject({ count: 1 });
|
|
69
69
|
});
|
|
70
70
|
|
|
71
|
-
it('should
|
|
71
|
+
it('should return error when query fails', async () => {
|
|
72
72
|
mockApiClient.listOrganizations.mockResolvedValue({
|
|
73
73
|
ok: false,
|
|
74
74
|
error: 'Failed to list organizations',
|
|
75
75
|
});
|
|
76
76
|
const ctx = createMockContext();
|
|
77
77
|
|
|
78
|
-
await
|
|
78
|
+
const result = await listOrganizations({}, ctx);
|
|
79
|
+
|
|
80
|
+
expect(result.isError).toBe(true);
|
|
81
|
+
expect(result.result).toMatchObject({ error: 'Failed to list organizations' });
|
|
79
82
|
});
|
|
80
83
|
});
|
|
81
84
|
|
|
@@ -91,16 +94,17 @@ describe('createOrganization', () => {
|
|
|
91
94
|
await expect(createOrganization({}, ctx)).rejects.toThrow(ValidationError);
|
|
92
95
|
});
|
|
93
96
|
|
|
94
|
-
it('should
|
|
97
|
+
it('should return error when slug is taken', async () => {
|
|
95
98
|
mockApiClient.createOrganization.mockResolvedValue({
|
|
96
99
|
ok: false,
|
|
97
100
|
error: 'Organization slug "test-org" is already taken',
|
|
98
101
|
});
|
|
99
102
|
const ctx = createMockContext();
|
|
100
103
|
|
|
101
|
-
await
|
|
102
|
-
|
|
103
|
-
).
|
|
104
|
+
const result = await createOrganization({ name: 'Test Org' }, ctx);
|
|
105
|
+
|
|
106
|
+
expect(result.isError).toBe(true);
|
|
107
|
+
expect(result.result).toMatchObject({ error: 'Organization slug "test-org" is already taken' });
|
|
104
108
|
});
|
|
105
109
|
|
|
106
110
|
it('should create organization with auto-generated slug', async () => {
|
|
@@ -138,16 +142,17 @@ describe('createOrganization', () => {
|
|
|
138
142
|
expect(result.result).toMatchObject({ success: true });
|
|
139
143
|
});
|
|
140
144
|
|
|
141
|
-
it('should
|
|
145
|
+
it('should return error when insert fails', async () => {
|
|
142
146
|
mockApiClient.createOrganization.mockResolvedValue({
|
|
143
147
|
ok: false,
|
|
144
148
|
error: 'Failed to create organization',
|
|
145
149
|
});
|
|
146
150
|
const ctx = createMockContext();
|
|
147
151
|
|
|
148
|
-
await
|
|
149
|
-
|
|
150
|
-
).
|
|
152
|
+
const result = await createOrganization({ name: 'Test Org' }, ctx);
|
|
153
|
+
|
|
154
|
+
expect(result.isError).toBe(true);
|
|
155
|
+
expect(result.result).toMatchObject({ error: 'Failed to create organization' });
|
|
151
156
|
});
|
|
152
157
|
});
|
|
153
158
|
|
|
@@ -174,9 +179,10 @@ describe('updateOrganization', () => {
|
|
|
174
179
|
|
|
175
180
|
it('should throw error when no updates provided', async () => {
|
|
176
181
|
const ctx = createMockContext();
|
|
182
|
+
|
|
177
183
|
await expect(
|
|
178
184
|
updateOrganization({ organization_id: VALID_UUID }, ctx)
|
|
179
|
-
).rejects.toThrow(
|
|
185
|
+
).rejects.toThrow(ValidationError);
|
|
180
186
|
});
|
|
181
187
|
|
|
182
188
|
it('should update organization successfully', async () => {
|
|
@@ -194,16 +200,17 @@ describe('updateOrganization', () => {
|
|
|
194
200
|
expect(result.result).toMatchObject({ success: true });
|
|
195
201
|
});
|
|
196
202
|
|
|
197
|
-
it('should
|
|
203
|
+
it('should return error when update fails', async () => {
|
|
198
204
|
mockApiClient.updateOrganization.mockResolvedValue({
|
|
199
205
|
ok: false,
|
|
200
206
|
error: 'Failed to update organization',
|
|
201
207
|
});
|
|
202
208
|
const ctx = createMockContext();
|
|
203
209
|
|
|
204
|
-
await
|
|
205
|
-
|
|
206
|
-
).
|
|
210
|
+
const result = await updateOrganization({ organization_id: VALID_UUID, name: 'New Name' }, ctx);
|
|
211
|
+
|
|
212
|
+
expect(result.isError).toBe(true);
|
|
213
|
+
expect(result.result).toMatchObject({ error: 'Failed to update organization' });
|
|
207
214
|
});
|
|
208
215
|
});
|
|
209
216
|
|
|
@@ -241,16 +248,17 @@ describe('deleteOrganization', () => {
|
|
|
241
248
|
});
|
|
242
249
|
});
|
|
243
250
|
|
|
244
|
-
it('should
|
|
251
|
+
it('should return error when delete fails', async () => {
|
|
245
252
|
mockApiClient.deleteOrganization.mockResolvedValue({
|
|
246
253
|
ok: false,
|
|
247
254
|
error: 'Failed to delete organization',
|
|
248
255
|
});
|
|
249
256
|
const ctx = createMockContext();
|
|
250
257
|
|
|
251
|
-
await
|
|
252
|
-
|
|
253
|
-
).
|
|
258
|
+
const result = await deleteOrganization({ organization_id: VALID_UUID }, ctx);
|
|
259
|
+
|
|
260
|
+
expect(result.isError).toBe(true);
|
|
261
|
+
expect(result.result).toMatchObject({ error: 'Failed to delete organization' });
|
|
254
262
|
});
|
|
255
263
|
});
|
|
256
264
|
|
|
@@ -303,16 +311,17 @@ describe('listOrgMembers', () => {
|
|
|
303
311
|
expect(result.result).toMatchObject({ count: 2 });
|
|
304
312
|
});
|
|
305
313
|
|
|
306
|
-
it('should
|
|
314
|
+
it('should return error when query fails', async () => {
|
|
307
315
|
mockApiClient.listOrgMembers.mockResolvedValue({
|
|
308
316
|
ok: false,
|
|
309
317
|
error: 'Failed to list members',
|
|
310
318
|
});
|
|
311
319
|
const ctx = createMockContext();
|
|
312
320
|
|
|
313
|
-
await
|
|
314
|
-
|
|
315
|
-
).
|
|
321
|
+
const result = await listOrgMembers({ organization_id: VALID_UUID }, ctx);
|
|
322
|
+
|
|
323
|
+
expect(result.isError).toBe(true);
|
|
324
|
+
expect(result.result).toMatchObject({ error: 'Failed to list members' });
|
|
316
325
|
});
|
|
317
326
|
});
|
|
318
327
|
|
|
@@ -337,16 +346,17 @@ describe('inviteMember', () => {
|
|
|
337
346
|
).rejects.toThrow(ValidationError);
|
|
338
347
|
});
|
|
339
348
|
|
|
340
|
-
it('should
|
|
349
|
+
it('should return error when invite already exists', async () => {
|
|
341
350
|
mockApiClient.inviteMember.mockResolvedValue({
|
|
342
351
|
ok: false,
|
|
343
352
|
error: 'A pending invite already exists for test@example.com',
|
|
344
353
|
});
|
|
345
354
|
const ctx = createMockContext();
|
|
346
355
|
|
|
347
|
-
await
|
|
348
|
-
|
|
349
|
-
).
|
|
356
|
+
const result = await inviteMember({ organization_id: VALID_UUID, email: 'test@example.com' }, ctx);
|
|
357
|
+
|
|
358
|
+
expect(result.isError).toBe(true);
|
|
359
|
+
expect(result.result).toMatchObject({ error: 'A pending invite already exists for test@example.com' });
|
|
350
360
|
});
|
|
351
361
|
|
|
352
362
|
it('should create invite successfully with default role', async () => {
|
|
@@ -371,16 +381,17 @@ describe('inviteMember', () => {
|
|
|
371
381
|
});
|
|
372
382
|
});
|
|
373
383
|
|
|
374
|
-
it('should
|
|
384
|
+
it('should return error when insert fails', async () => {
|
|
375
385
|
mockApiClient.inviteMember.mockResolvedValue({
|
|
376
386
|
ok: false,
|
|
377
387
|
error: 'Failed to create invite',
|
|
378
388
|
});
|
|
379
389
|
const ctx = createMockContext();
|
|
380
390
|
|
|
381
|
-
await
|
|
382
|
-
|
|
383
|
-
).
|
|
391
|
+
const result = await inviteMember({ organization_id: VALID_UUID, email: 'test@example.com' }, ctx);
|
|
392
|
+
|
|
393
|
+
expect(result.isError).toBe(true);
|
|
394
|
+
expect(result.result).toMatchObject({ error: 'Failed to create invite' });
|
|
384
395
|
});
|
|
385
396
|
});
|
|
386
397
|
|
|
@@ -412,20 +423,21 @@ describe('updateMemberRole', () => {
|
|
|
412
423
|
).rejects.toThrow(ValidationError);
|
|
413
424
|
});
|
|
414
425
|
|
|
415
|
-
it('should
|
|
426
|
+
it('should return error when changing own role', async () => {
|
|
416
427
|
mockApiClient.updateMemberRole.mockResolvedValue({
|
|
417
428
|
ok: false,
|
|
418
429
|
error: 'Cannot change your own role',
|
|
419
430
|
});
|
|
420
431
|
const ctx = createMockContext({ userId: VALID_UUID });
|
|
421
432
|
|
|
422
|
-
await
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
).
|
|
433
|
+
const result = await updateMemberRole({
|
|
434
|
+
organization_id: OTHER_UUID,
|
|
435
|
+
user_id: VALID_UUID,
|
|
436
|
+
role: 'admin',
|
|
437
|
+
}, ctx);
|
|
438
|
+
|
|
439
|
+
expect(result.isError).toBe(true);
|
|
440
|
+
expect(result.result).toMatchObject({ error: 'Cannot change your own role' });
|
|
429
441
|
});
|
|
430
442
|
|
|
431
443
|
it('should update member role successfully', async () => {
|
|
@@ -444,20 +456,21 @@ describe('updateMemberRole', () => {
|
|
|
444
456
|
expect(result.result).toMatchObject({ success: true });
|
|
445
457
|
});
|
|
446
458
|
|
|
447
|
-
it('should
|
|
459
|
+
it('should return error when update fails', async () => {
|
|
448
460
|
mockApiClient.updateMemberRole.mockResolvedValue({
|
|
449
461
|
ok: false,
|
|
450
462
|
error: 'Failed to update member role',
|
|
451
463
|
});
|
|
452
464
|
const ctx = createMockContext();
|
|
453
465
|
|
|
454
|
-
await
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
).
|
|
466
|
+
const result = await updateMemberRole({
|
|
467
|
+
organization_id: VALID_UUID,
|
|
468
|
+
user_id: OTHER_UUID,
|
|
469
|
+
role: 'admin',
|
|
470
|
+
}, ctx);
|
|
471
|
+
|
|
472
|
+
expect(result.isError).toBe(true);
|
|
473
|
+
expect(result.result).toMatchObject({ error: 'Failed to update member role' });
|
|
461
474
|
});
|
|
462
475
|
});
|
|
463
476
|
|
|
@@ -503,16 +516,17 @@ describe('removeMember', () => {
|
|
|
503
516
|
});
|
|
504
517
|
});
|
|
505
518
|
|
|
506
|
-
it('should
|
|
519
|
+
it('should return error when delete fails', async () => {
|
|
507
520
|
mockApiClient.removeMember.mockResolvedValue({
|
|
508
521
|
ok: false,
|
|
509
522
|
error: 'Failed to remove member',
|
|
510
523
|
});
|
|
511
524
|
const ctx = createMockContext();
|
|
512
525
|
|
|
513
|
-
await
|
|
514
|
-
|
|
515
|
-
).
|
|
526
|
+
const result = await removeMember({ organization_id: VALID_UUID, user_id: OTHER_UUID }, ctx);
|
|
527
|
+
|
|
528
|
+
expect(result.isError).toBe(true);
|
|
529
|
+
expect(result.result).toMatchObject({ error: 'Failed to remove member' });
|
|
516
530
|
});
|
|
517
531
|
});
|
|
518
532
|
|
|
@@ -528,16 +542,17 @@ describe('leaveOrganization', () => {
|
|
|
528
542
|
await expect(leaveOrganization({}, ctx)).rejects.toThrow(ValidationError);
|
|
529
543
|
});
|
|
530
544
|
|
|
531
|
-
it('should
|
|
545
|
+
it('should return error when user is owner', async () => {
|
|
532
546
|
mockApiClient.leaveOrganization.mockResolvedValue({
|
|
533
547
|
ok: false,
|
|
534
548
|
error: 'Owner cannot leave. Transfer ownership first or delete the organization.',
|
|
535
549
|
});
|
|
536
550
|
const ctx = createMockContext();
|
|
537
551
|
|
|
538
|
-
await
|
|
539
|
-
|
|
540
|
-
).
|
|
552
|
+
const result = await leaveOrganization({ organization_id: VALID_UUID }, ctx);
|
|
553
|
+
|
|
554
|
+
expect(result.isError).toBe(true);
|
|
555
|
+
expect(result.result).toMatchObject({ error: 'Owner cannot leave. Transfer ownership first or delete the organization.' });
|
|
541
556
|
});
|
|
542
557
|
|
|
543
558
|
it('should leave organization successfully', async () => {
|
|
@@ -555,16 +570,17 @@ describe('leaveOrganization', () => {
|
|
|
555
570
|
});
|
|
556
571
|
});
|
|
557
572
|
|
|
558
|
-
it('should
|
|
573
|
+
it('should return error when delete fails', async () => {
|
|
559
574
|
mockApiClient.leaveOrganization.mockResolvedValue({
|
|
560
575
|
ok: false,
|
|
561
576
|
error: 'Failed to leave organization',
|
|
562
577
|
});
|
|
563
578
|
const ctx = createMockContext();
|
|
564
579
|
|
|
565
|
-
await
|
|
566
|
-
|
|
567
|
-
).
|
|
580
|
+
const result = await leaveOrganization({ organization_id: VALID_UUID }, ctx);
|
|
581
|
+
|
|
582
|
+
expect(result.isError).toBe(true);
|
|
583
|
+
expect(result.result).toMatchObject({ error: 'Failed to leave organization' });
|
|
568
584
|
});
|
|
569
585
|
});
|
|
570
586
|
|
|
@@ -589,16 +605,17 @@ describe('shareProjectWithOrg', () => {
|
|
|
589
605
|
).rejects.toThrow(ValidationError);
|
|
590
606
|
});
|
|
591
607
|
|
|
592
|
-
it('should
|
|
608
|
+
it('should return error when project not found or not owned', async () => {
|
|
593
609
|
mockApiClient.shareProjectWithOrg.mockResolvedValue({
|
|
594
610
|
ok: false,
|
|
595
611
|
error: 'Project not found or you are not the owner',
|
|
596
612
|
});
|
|
597
613
|
const ctx = createMockContext();
|
|
598
614
|
|
|
599
|
-
await
|
|
600
|
-
|
|
601
|
-
).
|
|
615
|
+
const result = await shareProjectWithOrg({ project_id: VALID_UUID, organization_id: OTHER_UUID }, ctx);
|
|
616
|
+
|
|
617
|
+
expect(result.isError).toBe(true);
|
|
618
|
+
expect(result.result).toMatchObject({ error: 'Project not found or you are not the owner' });
|
|
602
619
|
});
|
|
603
620
|
|
|
604
621
|
it('should share project successfully with default permission', async () => {
|
|
@@ -616,16 +633,17 @@ describe('shareProjectWithOrg', () => {
|
|
|
616
633
|
expect(result.result).toMatchObject({ success: true });
|
|
617
634
|
});
|
|
618
635
|
|
|
619
|
-
it('should
|
|
636
|
+
it('should return error when share already exists', async () => {
|
|
620
637
|
mockApiClient.shareProjectWithOrg.mockResolvedValue({
|
|
621
638
|
ok: false,
|
|
622
639
|
error: 'Project is already shared with this organization',
|
|
623
640
|
});
|
|
624
641
|
const ctx = createMockContext();
|
|
625
642
|
|
|
626
|
-
await
|
|
627
|
-
|
|
628
|
-
).
|
|
643
|
+
const result = await shareProjectWithOrg({ project_id: VALID_UUID, organization_id: OTHER_UUID }, ctx);
|
|
644
|
+
|
|
645
|
+
expect(result.isError).toBe(true);
|
|
646
|
+
expect(result.result).toMatchObject({ error: 'Project is already shared with this organization' });
|
|
629
647
|
});
|
|
630
648
|
});
|
|
631
649
|
|
|
@@ -673,20 +691,21 @@ describe('updateProjectShare', () => {
|
|
|
673
691
|
expect(result.result).toMatchObject({ success: true });
|
|
674
692
|
});
|
|
675
693
|
|
|
676
|
-
it('should
|
|
694
|
+
it('should return error when update fails', async () => {
|
|
677
695
|
mockApiClient.updateProjectShare.mockResolvedValue({
|
|
678
696
|
ok: false,
|
|
679
697
|
error: 'Failed to update share',
|
|
680
698
|
});
|
|
681
699
|
const ctx = createMockContext();
|
|
682
700
|
|
|
683
|
-
await
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
).
|
|
701
|
+
const result = await updateProjectShare({
|
|
702
|
+
project_id: VALID_UUID,
|
|
703
|
+
organization_id: OTHER_UUID,
|
|
704
|
+
permission: 'write',
|
|
705
|
+
}, ctx);
|
|
706
|
+
|
|
707
|
+
expect(result.isError).toBe(true);
|
|
708
|
+
expect(result.result).toMatchObject({ error: 'Failed to update share' });
|
|
690
709
|
});
|
|
691
710
|
});
|
|
692
711
|
|
|
@@ -732,16 +751,17 @@ describe('unshareProject', () => {
|
|
|
732
751
|
});
|
|
733
752
|
});
|
|
734
753
|
|
|
735
|
-
it('should
|
|
754
|
+
it('should return error when delete fails', async () => {
|
|
736
755
|
mockApiClient.unshareProject.mockResolvedValue({
|
|
737
756
|
ok: false,
|
|
738
757
|
error: 'Failed to unshare project',
|
|
739
758
|
});
|
|
740
759
|
const ctx = createMockContext();
|
|
741
760
|
|
|
742
|
-
await
|
|
743
|
-
|
|
744
|
-
).
|
|
761
|
+
const result = await unshareProject({ project_id: VALID_UUID, organization_id: OTHER_UUID }, ctx);
|
|
762
|
+
|
|
763
|
+
expect(result.isError).toBe(true);
|
|
764
|
+
expect(result.result).toMatchObject({ error: 'Failed to unshare project' });
|
|
745
765
|
});
|
|
746
766
|
});
|
|
747
767
|
|
|
@@ -791,15 +811,16 @@ describe('listProjectShares', () => {
|
|
|
791
811
|
expect(result.result).toMatchObject({ count: 1 });
|
|
792
812
|
});
|
|
793
813
|
|
|
794
|
-
it('should
|
|
814
|
+
it('should return error when query fails', async () => {
|
|
795
815
|
mockApiClient.listProjectShares.mockResolvedValue({
|
|
796
816
|
ok: false,
|
|
797
817
|
error: 'Failed to list shares',
|
|
798
818
|
});
|
|
799
819
|
const ctx = createMockContext();
|
|
800
820
|
|
|
801
|
-
await
|
|
802
|
-
|
|
803
|
-
).
|
|
821
|
+
const result = await listProjectShares({ project_id: VALID_UUID }, ctx);
|
|
822
|
+
|
|
823
|
+
expect(result.isError).toBe(true);
|
|
824
|
+
expect(result.result).toMatchObject({ error: 'Failed to list shares' });
|
|
804
825
|
});
|
|
805
826
|
});
|
|
@@ -107,7 +107,7 @@ export const listOrganizations: Handler = async (_args, _ctx) => {
|
|
|
107
107
|
const response = await apiClient.listOrganizations();
|
|
108
108
|
|
|
109
109
|
if (!response.ok) {
|
|
110
|
-
|
|
110
|
+
return { result: { error: response.error || 'Failed to list organizations' }, isError: true };
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
return { result: response.data };
|
|
@@ -124,7 +124,7 @@ export const createOrganization: Handler = async (args, _ctx) => {
|
|
|
124
124
|
});
|
|
125
125
|
|
|
126
126
|
if (!response.ok) {
|
|
127
|
-
|
|
127
|
+
return { result: { error: response.error || 'Failed to create organization' }, isError: true };
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
return { result: response.data };
|
|
@@ -147,7 +147,7 @@ export const updateOrganization: Handler = async (args, _ctx) => {
|
|
|
147
147
|
const response = await apiClient.updateOrganization(organization_id, updates);
|
|
148
148
|
|
|
149
149
|
if (!response.ok) {
|
|
150
|
-
|
|
150
|
+
return { result: { error: response.error || 'Failed to update organization' }, isError: true };
|
|
151
151
|
}
|
|
152
152
|
|
|
153
153
|
return { result: response.data };
|
|
@@ -160,7 +160,7 @@ export const deleteOrganization: Handler = async (args, _ctx) => {
|
|
|
160
160
|
const response = await apiClient.deleteOrganization(organization_id);
|
|
161
161
|
|
|
162
162
|
if (!response.ok) {
|
|
163
|
-
|
|
163
|
+
return { result: { error: response.error || 'Failed to delete organization' }, isError: true };
|
|
164
164
|
}
|
|
165
165
|
|
|
166
166
|
return { result: response.data };
|
|
@@ -177,7 +177,7 @@ export const listOrgMembers: Handler = async (args, _ctx) => {
|
|
|
177
177
|
const response = await apiClient.listOrgMembers(organization_id);
|
|
178
178
|
|
|
179
179
|
if (!response.ok) {
|
|
180
|
-
|
|
180
|
+
return { result: { error: response.error || 'Failed to list members' }, isError: true };
|
|
181
181
|
}
|
|
182
182
|
|
|
183
183
|
return { result: response.data };
|
|
@@ -190,7 +190,7 @@ export const inviteMember: Handler = async (args, _ctx) => {
|
|
|
190
190
|
const response = await apiClient.inviteMember(organization_id, email, role as AssignableRole);
|
|
191
191
|
|
|
192
192
|
if (!response.ok) {
|
|
193
|
-
|
|
193
|
+
return { result: { error: response.error || 'Failed to create invite' }, isError: true };
|
|
194
194
|
}
|
|
195
195
|
|
|
196
196
|
return { result: response.data };
|
|
@@ -207,7 +207,7 @@ export const updateMemberRole: Handler = async (args, _ctx) => {
|
|
|
207
207
|
const response = await apiClient.updateMemberRole(organization_id, user_id, role as Role);
|
|
208
208
|
|
|
209
209
|
if (!response.ok) {
|
|
210
|
-
|
|
210
|
+
return { result: { error: response.error || 'Failed to update member role' }, isError: true };
|
|
211
211
|
}
|
|
212
212
|
|
|
213
213
|
return { result: response.data };
|
|
@@ -220,7 +220,7 @@ export const removeMember: Handler = async (args, _ctx) => {
|
|
|
220
220
|
const response = await apiClient.removeMember(organization_id, user_id);
|
|
221
221
|
|
|
222
222
|
if (!response.ok) {
|
|
223
|
-
|
|
223
|
+
return { result: { error: response.error || 'Failed to remove member' }, isError: true };
|
|
224
224
|
}
|
|
225
225
|
|
|
226
226
|
return { result: response.data };
|
|
@@ -233,7 +233,7 @@ export const leaveOrganization: Handler = async (args, _ctx) => {
|
|
|
233
233
|
const response = await apiClient.leaveOrganization(organization_id);
|
|
234
234
|
|
|
235
235
|
if (!response.ok) {
|
|
236
|
-
|
|
236
|
+
return { result: { error: response.error || 'Failed to leave organization' }, isError: true };
|
|
237
237
|
}
|
|
238
238
|
|
|
239
239
|
return { result: response.data };
|
|
@@ -250,7 +250,7 @@ export const shareProjectWithOrg: Handler = async (args, _ctx) => {
|
|
|
250
250
|
const response = await apiClient.shareProjectWithOrg(project_id, organization_id, permission as Permission);
|
|
251
251
|
|
|
252
252
|
if (!response.ok) {
|
|
253
|
-
|
|
253
|
+
return { result: { error: response.error || 'Failed to share project' }, isError: true };
|
|
254
254
|
}
|
|
255
255
|
|
|
256
256
|
return { result: response.data };
|
|
@@ -263,7 +263,7 @@ export const updateProjectShare: Handler = async (args, _ctx) => {
|
|
|
263
263
|
const response = await apiClient.updateProjectShare(project_id, organization_id, permission as Permission);
|
|
264
264
|
|
|
265
265
|
if (!response.ok) {
|
|
266
|
-
|
|
266
|
+
return { result: { error: response.error || 'Failed to update share' }, isError: true };
|
|
267
267
|
}
|
|
268
268
|
|
|
269
269
|
return { result: response.data };
|
|
@@ -276,7 +276,7 @@ export const unshareProject: Handler = async (args, _ctx) => {
|
|
|
276
276
|
const response = await apiClient.unshareProject(project_id, organization_id);
|
|
277
277
|
|
|
278
278
|
if (!response.ok) {
|
|
279
|
-
|
|
279
|
+
return { result: { error: response.error || 'Failed to unshare project' }, isError: true };
|
|
280
280
|
}
|
|
281
281
|
|
|
282
282
|
return { result: response.data };
|
|
@@ -289,7 +289,7 @@ export const listProjectShares: Handler = async (args, _ctx) => {
|
|
|
289
289
|
const response = await apiClient.listProjectShares(project_id);
|
|
290
290
|
|
|
291
291
|
if (!response.ok) {
|
|
292
|
-
|
|
292
|
+
return { result: { error: response.error || 'Failed to list shares' }, isError: true };
|
|
293
293
|
}
|
|
294
294
|
|
|
295
295
|
return { result: response.data };
|
|
@@ -103,19 +103,22 @@ describe('logProgress', () => {
|
|
|
103
103
|
expect(mockApiClient.logProgress).toHaveBeenCalled();
|
|
104
104
|
});
|
|
105
105
|
|
|
106
|
-
it('should
|
|
106
|
+
it('should return error when API call fails', async () => {
|
|
107
107
|
mockApiClient.logProgress.mockResolvedValue({
|
|
108
108
|
ok: false,
|
|
109
109
|
error: 'Insert failed',
|
|
110
110
|
});
|
|
111
111
|
const ctx = createMockContext();
|
|
112
112
|
|
|
113
|
-
await
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
).
|
|
113
|
+
const result = await logProgress({
|
|
114
|
+
project_id: '123e4567-e89b-12d3-a456-426614174000',
|
|
115
|
+
summary: 'Progress',
|
|
116
|
+
}, ctx);
|
|
117
|
+
|
|
118
|
+
expect(result.isError).toBe(true);
|
|
119
|
+
expect(result.result).toMatchObject({
|
|
120
|
+
error: 'Insert failed',
|
|
121
|
+
});
|
|
119
122
|
});
|
|
120
123
|
|
|
121
124
|
it('should throw error for missing project_id', async () => {
|
|
@@ -246,18 +249,21 @@ describe('getActivityFeed', () => {
|
|
|
246
249
|
);
|
|
247
250
|
});
|
|
248
251
|
|
|
249
|
-
it('should
|
|
252
|
+
it('should return error when API call fails', async () => {
|
|
250
253
|
mockApiClient.getActivityFeed.mockResolvedValue({
|
|
251
254
|
ok: false,
|
|
252
255
|
error: 'Query failed',
|
|
253
256
|
});
|
|
254
257
|
const ctx = createMockContext();
|
|
255
258
|
|
|
256
|
-
await
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
).
|
|
259
|
+
const result = await getActivityFeed(
|
|
260
|
+
{ project_id: '123e4567-e89b-12d3-a456-426614174000' },
|
|
261
|
+
ctx
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
expect(result.isError).toBe(true);
|
|
265
|
+
expect(result.result).toMatchObject({
|
|
266
|
+
error: 'Query failed',
|
|
267
|
+
});
|
|
262
268
|
});
|
|
263
269
|
});
|
package/src/handlers/progress.ts
CHANGED
|
@@ -42,7 +42,7 @@ export const logProgress: Handler = async (args, ctx) => {
|
|
|
42
42
|
});
|
|
43
43
|
|
|
44
44
|
if (!response.ok) {
|
|
45
|
-
|
|
45
|
+
return { result: { error: response.error || 'Failed to log progress' }, isError: true };
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
return { result: { success: true, progress_id: response.data?.progress_id } };
|
|
@@ -62,7 +62,7 @@ export const getActivityFeed: Handler = async (args, _ctx) => {
|
|
|
62
62
|
});
|
|
63
63
|
|
|
64
64
|
if (!response.ok) {
|
|
65
|
-
|
|
65
|
+
return { result: { error: response.error || 'Failed to fetch activity feed' }, isError: true };
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
return { result: { activities: response.data?.activities || [] } };
|