@vibescope/mcp-server 0.0.1 → 0.2.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.
Files changed (173) hide show
  1. package/README.md +113 -98
  2. package/dist/api-client.d.ts +1169 -0
  3. package/dist/api-client.js +713 -0
  4. package/dist/cli.d.ts +1 -6
  5. package/dist/cli.js +39 -240
  6. package/dist/config/tool-categories.d.ts +31 -0
  7. package/dist/config/tool-categories.js +253 -0
  8. package/dist/handlers/blockers.js +57 -58
  9. package/dist/handlers/bodies-of-work.d.ts +2 -0
  10. package/dist/handlers/bodies-of-work.js +108 -477
  11. package/dist/handlers/cost.d.ts +1 -0
  12. package/dist/handlers/cost.js +35 -113
  13. package/dist/handlers/decisions.d.ts +2 -0
  14. package/dist/handlers/decisions.js +28 -27
  15. package/dist/handlers/deployment.js +113 -828
  16. package/dist/handlers/discovery.d.ts +3 -0
  17. package/dist/handlers/discovery.js +26 -627
  18. package/dist/handlers/fallback.d.ts +2 -0
  19. package/dist/handlers/fallback.js +56 -142
  20. package/dist/handlers/findings.d.ts +8 -1
  21. package/dist/handlers/findings.js +65 -68
  22. package/dist/handlers/git-issues.d.ts +9 -13
  23. package/dist/handlers/git-issues.js +80 -225
  24. package/dist/handlers/ideas.d.ts +3 -0
  25. package/dist/handlers/ideas.js +53 -134
  26. package/dist/handlers/index.d.ts +2 -0
  27. package/dist/handlers/index.js +6 -0
  28. package/dist/handlers/milestones.d.ts +2 -0
  29. package/dist/handlers/milestones.js +51 -98
  30. package/dist/handlers/organizations.js +79 -275
  31. package/dist/handlers/progress.d.ts +2 -0
  32. package/dist/handlers/progress.js +25 -123
  33. package/dist/handlers/project.js +42 -221
  34. package/dist/handlers/requests.d.ts +2 -0
  35. package/dist/handlers/requests.js +23 -83
  36. package/dist/handlers/session.js +119 -590
  37. package/dist/handlers/sprints.d.ts +32 -0
  38. package/dist/handlers/sprints.js +275 -0
  39. package/dist/handlers/tasks.d.ts +7 -10
  40. package/dist/handlers/tasks.js +245 -894
  41. package/dist/handlers/tool-docs.d.ts +9 -0
  42. package/dist/handlers/tool-docs.js +904 -0
  43. package/dist/handlers/types.d.ts +11 -3
  44. package/dist/handlers/validation.d.ts +1 -1
  45. package/dist/handlers/validation.js +38 -153
  46. package/dist/index.js +493 -162
  47. package/dist/knowledge.js +106 -9
  48. package/dist/tools.js +34 -4
  49. package/dist/validators.d.ts +21 -0
  50. package/dist/validators.js +91 -0
  51. package/package.json +2 -3
  52. package/src/api-client.ts +1822 -0
  53. package/src/cli.test.ts +128 -302
  54. package/src/cli.ts +41 -285
  55. package/src/handlers/__test-setup__.ts +215 -0
  56. package/src/handlers/__test-utils__.ts +4 -134
  57. package/src/handlers/blockers.test.ts +114 -124
  58. package/src/handlers/blockers.ts +68 -70
  59. package/src/handlers/bodies-of-work.test.ts +236 -831
  60. package/src/handlers/bodies-of-work.ts +210 -525
  61. package/src/handlers/cost.test.ts +149 -113
  62. package/src/handlers/cost.ts +44 -132
  63. package/src/handlers/decisions.test.ts +111 -209
  64. package/src/handlers/decisions.ts +35 -27
  65. package/src/handlers/deployment.test.ts +193 -239
  66. package/src/handlers/deployment.ts +143 -896
  67. package/src/handlers/discovery.test.ts +20 -67
  68. package/src/handlers/discovery.ts +29 -714
  69. package/src/handlers/fallback.test.ts +206 -361
  70. package/src/handlers/fallback.ts +81 -156
  71. package/src/handlers/findings.test.ts +229 -320
  72. package/src/handlers/findings.ts +76 -64
  73. package/src/handlers/git-issues.test.ts +623 -0
  74. package/src/handlers/git-issues.ts +174 -0
  75. package/src/handlers/ideas.test.ts +229 -343
  76. package/src/handlers/ideas.ts +69 -143
  77. package/src/handlers/index.ts +6 -0
  78. package/src/handlers/milestones.test.ts +167 -281
  79. package/src/handlers/milestones.ts +54 -93
  80. package/src/handlers/organizations.test.ts +275 -467
  81. package/src/handlers/organizations.ts +84 -294
  82. package/src/handlers/progress.test.ts +112 -218
  83. package/src/handlers/progress.ts +29 -142
  84. package/src/handlers/project.test.ts +203 -226
  85. package/src/handlers/project.ts +48 -238
  86. package/src/handlers/requests.test.ts +74 -342
  87. package/src/handlers/requests.ts +25 -83
  88. package/src/handlers/session.test.ts +276 -206
  89. package/src/handlers/session.ts +136 -662
  90. package/src/handlers/sprints.test.ts +711 -0
  91. package/src/handlers/sprints.ts +510 -0
  92. package/src/handlers/tasks.test.ts +669 -353
  93. package/src/handlers/tasks.ts +263 -1015
  94. package/src/handlers/tool-docs.ts +1024 -0
  95. package/src/handlers/types.ts +12 -4
  96. package/src/handlers/validation.test.ts +237 -568
  97. package/src/handlers/validation.ts +43 -167
  98. package/src/index.ts +493 -186
  99. package/src/tools.ts +2532 -0
  100. package/src/validators.test.ts +223 -223
  101. package/src/validators.ts +127 -0
  102. package/tsconfig.json +1 -1
  103. package/vitest.config.ts +14 -13
  104. package/dist/cli.test.d.ts +0 -1
  105. package/dist/cli.test.js +0 -367
  106. package/dist/handlers/__test-utils__.d.ts +0 -72
  107. package/dist/handlers/__test-utils__.js +0 -176
  108. package/dist/handlers/checkouts.d.ts +0 -37
  109. package/dist/handlers/checkouts.js +0 -377
  110. package/dist/handlers/knowledge-query.d.ts +0 -22
  111. package/dist/handlers/knowledge-query.js +0 -253
  112. package/dist/handlers/knowledge.d.ts +0 -12
  113. package/dist/handlers/knowledge.js +0 -108
  114. package/dist/handlers/roles.d.ts +0 -30
  115. package/dist/handlers/roles.js +0 -281
  116. package/dist/handlers/tasks.test.d.ts +0 -1
  117. package/dist/handlers/tasks.test.js +0 -431
  118. package/dist/utils.test.d.ts +0 -1
  119. package/dist/utils.test.js +0 -532
  120. package/dist/validators.test.d.ts +0 -1
  121. package/dist/validators.test.js +0 -176
  122. package/src/knowledge.ts +0 -132
  123. package/src/tmpclaude-0078-cwd +0 -1
  124. package/src/tmpclaude-0ee1-cwd +0 -1
  125. package/src/tmpclaude-2dd5-cwd +0 -1
  126. package/src/tmpclaude-344c-cwd +0 -1
  127. package/src/tmpclaude-3860-cwd +0 -1
  128. package/src/tmpclaude-4b63-cwd +0 -1
  129. package/src/tmpclaude-5c73-cwd +0 -1
  130. package/src/tmpclaude-5ee3-cwd +0 -1
  131. package/src/tmpclaude-6795-cwd +0 -1
  132. package/src/tmpclaude-709e-cwd +0 -1
  133. package/src/tmpclaude-9839-cwd +0 -1
  134. package/src/tmpclaude-d829-cwd +0 -1
  135. package/src/tmpclaude-e072-cwd +0 -1
  136. package/src/tmpclaude-f6ee-cwd +0 -1
  137. package/tmpclaude-0439-cwd +0 -1
  138. package/tmpclaude-132f-cwd +0 -1
  139. package/tmpclaude-15bb-cwd +0 -1
  140. package/tmpclaude-165a-cwd +0 -1
  141. package/tmpclaude-1ba9-cwd +0 -1
  142. package/tmpclaude-21a3-cwd +0 -1
  143. package/tmpclaude-2a38-cwd +0 -1
  144. package/tmpclaude-2adf-cwd +0 -1
  145. package/tmpclaude-2f56-cwd +0 -1
  146. package/tmpclaude-3626-cwd +0 -1
  147. package/tmpclaude-3727-cwd +0 -1
  148. package/tmpclaude-40bc-cwd +0 -1
  149. package/tmpclaude-436f-cwd +0 -1
  150. package/tmpclaude-4783-cwd +0 -1
  151. package/tmpclaude-4b6d-cwd +0 -1
  152. package/tmpclaude-4ba4-cwd +0 -1
  153. package/tmpclaude-51e6-cwd +0 -1
  154. package/tmpclaude-5ecf-cwd +0 -1
  155. package/tmpclaude-6f97-cwd +0 -1
  156. package/tmpclaude-7fb2-cwd +0 -1
  157. package/tmpclaude-825c-cwd +0 -1
  158. package/tmpclaude-8baf-cwd +0 -1
  159. package/tmpclaude-8d9f-cwd +0 -1
  160. package/tmpclaude-975c-cwd +0 -1
  161. package/tmpclaude-9983-cwd +0 -1
  162. package/tmpclaude-a045-cwd +0 -1
  163. package/tmpclaude-ac4a-cwd +0 -1
  164. package/tmpclaude-b593-cwd +0 -1
  165. package/tmpclaude-b891-cwd +0 -1
  166. package/tmpclaude-c032-cwd +0 -1
  167. package/tmpclaude-cf43-cwd +0 -1
  168. package/tmpclaude-d040-cwd +0 -1
  169. package/tmpclaude-dcdd-cwd +0 -1
  170. package/tmpclaude-dcee-cwd +0 -1
  171. package/tmpclaude-e16b-cwd +0 -1
  172. package/tmpclaude-ecd2-cwd +0 -1
  173. package/tmpclaude-f48d-cwd +0 -1
@@ -15,7 +15,15 @@ import {
15
15
  listProjectShares,
16
16
  } from './organizations.js';
17
17
  import { ValidationError } from '../validators.js';
18
- import { createMockSupabase, createMockContext, testUUID } from './__test-utils__.js';
18
+ import { createMockContext } from './__test-utils__.js';
19
+ import { mockApiClient } from './__test-setup__.js';
20
+
21
+ // ============================================================================
22
+ // Test Utilities
23
+ // ============================================================================
24
+
25
+ const VALID_UUID = '123e4567-e89b-12d3-a456-426614174000';
26
+ const OTHER_UUID = '223e4567-e89b-12d3-a456-426614174001';
19
27
 
20
28
  // ============================================================================
21
29
  // listOrganizations Tests
@@ -25,10 +33,11 @@ describe('listOrganizations', () => {
25
33
  beforeEach(() => vi.clearAllMocks());
26
34
 
27
35
  it('should return empty array when no organizations', async () => {
28
- const supabase = createMockSupabase({
29
- selectResult: { data: [], error: null },
36
+ mockApiClient.listOrganizations.mockResolvedValue({
37
+ ok: true,
38
+ data: { organizations: [], count: 0 },
30
39
  });
31
- const ctx = createMockContext(supabase);
40
+ const ctx = createMockContext();
32
41
 
33
42
  const result = await listOrganizations({}, ctx);
34
43
 
@@ -39,44 +48,34 @@ describe('listOrganizations', () => {
39
48
  });
40
49
 
41
50
  it('should return organizations with role and joined_at', async () => {
42
- const supabase = createMockSupabase({
43
- selectResult: {
44
- data: [
45
- {
46
- role: 'admin',
47
- joined_at: '2025-01-14T12:00:00Z',
48
- organizations: {
49
- id: 'org-1',
50
- name: 'Test Org',
51
- slug: 'test-org',
52
- description: 'A test organization',
53
- logo_url: null,
54
- owner_id: 'user-123',
55
- created_at: '2025-01-01T00:00:00Z',
56
- },
57
- },
58
- ],
59
- error: null,
51
+ mockApiClient.listOrganizations.mockResolvedValue({
52
+ ok: true,
53
+ data: {
54
+ organizations: [{
55
+ id: 'org-1',
56
+ name: 'Test Org',
57
+ slug: 'test-org',
58
+ role: 'admin',
59
+ joined_at: '2025-01-14T12:00:00Z',
60
+ }],
61
+ count: 1,
60
62
  },
61
63
  });
62
- const ctx = createMockContext(supabase);
64
+ const ctx = createMockContext();
63
65
 
64
66
  const result = await listOrganizations({}, ctx);
65
67
 
66
- expect(result.result).toMatchObject({
67
- count: 1,
68
- });
68
+ expect(result.result).toMatchObject({ count: 1 });
69
69
  });
70
70
 
71
71
  it('should throw error when query fails', async () => {
72
- const supabase = createMockSupabase({
73
- selectResult: { data: null, error: { message: 'Query failed' } },
72
+ mockApiClient.listOrganizations.mockResolvedValue({
73
+ ok: false,
74
+ error: 'Failed to list organizations',
74
75
  });
75
- const ctx = createMockContext(supabase);
76
+ const ctx = createMockContext();
76
77
 
77
- await expect(listOrganizations({}, ctx)).rejects.toThrow(
78
- 'Failed to list organizations: Query failed'
79
- );
78
+ await expect(listOrganizations({}, ctx)).rejects.toThrow('Failed to list organizations');
80
79
  });
81
80
  });
82
81
 
@@ -88,17 +87,16 @@ describe('createOrganization', () => {
88
87
  beforeEach(() => vi.clearAllMocks());
89
88
 
90
89
  it('should throw error for missing name', async () => {
91
- const supabase = createMockSupabase();
92
- const ctx = createMockContext(supabase);
93
-
90
+ const ctx = createMockContext();
94
91
  await expect(createOrganization({}, ctx)).rejects.toThrow(ValidationError);
95
92
  });
96
93
 
97
94
  it('should throw error when slug is taken', async () => {
98
- const supabase = createMockSupabase({
99
- selectResult: { data: { id: 'existing-org' }, error: null },
95
+ mockApiClient.createOrganization.mockResolvedValue({
96
+ ok: false,
97
+ error: 'Organization slug "test-org" is already taken',
100
98
  });
101
- const ctx = createMockContext(supabase);
99
+ const ctx = createMockContext();
102
100
 
103
101
  await expect(
104
102
  createOrganization({ name: 'Test Org' }, ctx)
@@ -106,19 +104,16 @@ describe('createOrganization', () => {
106
104
  });
107
105
 
108
106
  it('should create organization with auto-generated slug', async () => {
109
- const supabase = createMockSupabase({
110
- selectResult: { data: null, error: null }, // No existing slug
111
- insertResult: {
112
- data: {
113
- id: 'org-1',
114
- name: 'Test Org',
115
- slug: 'test-org',
116
- owner_id: 'user-123',
117
- },
118
- error: null,
107
+ mockApiClient.createOrganization.mockResolvedValue({
108
+ ok: true,
109
+ data: {
110
+ success: true,
111
+ organization_id: 'org-1',
112
+ slug: 'test-org',
113
+ message: 'Organization "Test Org" created. You are the owner.',
119
114
  },
120
115
  });
121
- const ctx = createMockContext(supabase);
116
+ const ctx = createMockContext();
122
117
 
123
118
  const result = await createOrganization({ name: 'Test Org' }, ctx);
124
119
 
@@ -129,40 +124,30 @@ describe('createOrganization', () => {
129
124
  });
130
125
 
131
126
  it('should create organization with custom slug', async () => {
132
- const supabase = createMockSupabase({
133
- selectResult: { data: null, error: null },
134
- insertResult: {
135
- data: {
136
- id: 'org-1',
137
- name: 'Test Org',
138
- slug: 'my-custom-slug',
139
- owner_id: 'user-123',
140
- },
141
- error: null,
142
- },
127
+ mockApiClient.createOrganization.mockResolvedValue({
128
+ ok: true,
129
+ data: { success: true, organization_id: 'org-1', slug: 'my-custom-slug' },
143
130
  });
144
- const ctx = createMockContext(supabase);
131
+ const ctx = createMockContext();
145
132
 
146
133
  const result = await createOrganization(
147
134
  { name: 'Test Org', slug: 'my-custom-slug' },
148
135
  ctx
149
136
  );
150
137
 
151
- expect(result.result).toMatchObject({
152
- success: true,
153
- });
138
+ expect(result.result).toMatchObject({ success: true });
154
139
  });
155
140
 
156
141
  it('should throw error when insert fails', async () => {
157
- const supabase = createMockSupabase({
158
- selectResult: { data: null, error: null },
159
- insertResult: { data: null, error: { message: 'Insert failed' } },
142
+ mockApiClient.createOrganization.mockResolvedValue({
143
+ ok: false,
144
+ error: 'Failed to create organization',
160
145
  });
161
- const ctx = createMockContext(supabase);
146
+ const ctx = createMockContext();
162
147
 
163
148
  await expect(
164
149
  createOrganization({ name: 'Test Org' }, ctx)
165
- ).rejects.toThrow('Failed to create organization: Insert failed');
150
+ ).rejects.toThrow('Failed to create organization');
166
151
  });
167
152
  });
168
153
 
@@ -174,64 +159,51 @@ describe('updateOrganization', () => {
174
159
  beforeEach(() => vi.clearAllMocks());
175
160
 
176
161
  it('should throw error for missing organization_id', async () => {
177
- const supabase = createMockSupabase();
178
- const ctx = createMockContext(supabase);
179
-
162
+ const ctx = createMockContext();
180
163
  await expect(
181
164
  updateOrganization({ name: 'New Name' }, ctx)
182
165
  ).rejects.toThrow(ValidationError);
183
166
  });
184
167
 
185
168
  it('should throw error for invalid organization_id UUID', async () => {
186
- const supabase = createMockSupabase();
187
- const ctx = createMockContext(supabase);
188
-
169
+ const ctx = createMockContext();
189
170
  await expect(
190
171
  updateOrganization({ organization_id: 'invalid', name: 'New Name' }, ctx)
191
172
  ).rejects.toThrow(ValidationError);
192
173
  });
193
174
 
194
175
  it('should throw error when no updates provided', async () => {
195
- const supabase = createMockSupabase();
196
- const ctx = createMockContext(supabase);
197
-
176
+ const ctx = createMockContext();
198
177
  await expect(
199
- updateOrganization({ organization_id: testUUID() }, ctx)
178
+ updateOrganization({ organization_id: VALID_UUID }, ctx)
200
179
  ).rejects.toThrow('No updates provided');
201
180
  });
202
181
 
203
182
  it('should update organization successfully', async () => {
204
- const supabase = createMockSupabase({
205
- updateResult: {
206
- data: { id: testUUID(), name: 'Updated Org', description: 'New desc' },
207
- error: null,
208
- },
183
+ mockApiClient.updateOrganization.mockResolvedValue({
184
+ ok: true,
185
+ data: { success: true, organization_id: VALID_UUID },
209
186
  });
210
- const ctx = createMockContext(supabase);
187
+ const ctx = createMockContext();
211
188
 
212
189
  const result = await updateOrganization(
213
- {
214
- organization_id: testUUID(),
215
- name: 'Updated Org',
216
- description: 'New desc',
217
- },
190
+ { organization_id: VALID_UUID, name: 'Updated Org', description: 'New desc' },
218
191
  ctx
219
192
  );
220
193
 
221
- expect(result.result).toMatchObject({
222
- success: true,
223
- });
194
+ expect(result.result).toMatchObject({ success: true });
224
195
  });
225
196
 
226
197
  it('should throw error when update fails', async () => {
227
- const supabase = createMockSupabase({
228
- updateResult: { data: null, error: { message: 'Update failed' } },
198
+ mockApiClient.updateOrganization.mockResolvedValue({
199
+ ok: false,
200
+ error: 'Failed to update organization',
229
201
  });
230
- const ctx = createMockContext(supabase);
202
+ const ctx = createMockContext();
231
203
 
232
204
  await expect(
233
- updateOrganization({ organization_id: testUUID(), name: 'New Name' }, ctx)
234
- ).rejects.toThrow('Failed to update organization: Update failed');
205
+ updateOrganization({ organization_id: VALID_UUID, name: 'New Name' }, ctx)
206
+ ).rejects.toThrow('Failed to update organization');
235
207
  });
236
208
  });
237
209
 
@@ -243,28 +215,25 @@ describe('deleteOrganization', () => {
243
215
  beforeEach(() => vi.clearAllMocks());
244
216
 
245
217
  it('should throw error for missing organization_id', async () => {
246
- const supabase = createMockSupabase();
247
- const ctx = createMockContext(supabase);
248
-
218
+ const ctx = createMockContext();
249
219
  await expect(deleteOrganization({}, ctx)).rejects.toThrow(ValidationError);
250
220
  });
251
221
 
252
222
  it('should throw error for invalid organization_id UUID', async () => {
253
- const supabase = createMockSupabase();
254
- const ctx = createMockContext(supabase);
255
-
223
+ const ctx = createMockContext();
256
224
  await expect(
257
225
  deleteOrganization({ organization_id: 'invalid' }, ctx)
258
226
  ).rejects.toThrow(ValidationError);
259
227
  });
260
228
 
261
229
  it('should delete organization successfully', async () => {
262
- const supabase = createMockSupabase({
263
- deleteResult: { data: null, error: null },
230
+ mockApiClient.deleteOrganization.mockResolvedValue({
231
+ ok: true,
232
+ data: { success: true, message: 'Organization deleted. All shares have been removed.' },
264
233
  });
265
- const ctx = createMockContext(supabase);
234
+ const ctx = createMockContext();
266
235
 
267
- const result = await deleteOrganization({ organization_id: testUUID() }, ctx);
236
+ const result = await deleteOrganization({ organization_id: VALID_UUID }, ctx);
268
237
 
269
238
  expect(result.result).toMatchObject({
270
239
  success: true,
@@ -273,14 +242,15 @@ describe('deleteOrganization', () => {
273
242
  });
274
243
 
275
244
  it('should throw error when delete fails', async () => {
276
- const supabase = createMockSupabase({
277
- deleteResult: { data: null, error: { message: 'Delete failed' } },
245
+ mockApiClient.deleteOrganization.mockResolvedValue({
246
+ ok: false,
247
+ error: 'Failed to delete organization',
278
248
  });
279
- const ctx = createMockContext(supabase);
249
+ const ctx = createMockContext();
280
250
 
281
251
  await expect(
282
- deleteOrganization({ organization_id: testUUID() }, ctx)
283
- ).rejects.toThrow('Failed to delete organization: Delete failed');
252
+ deleteOrganization({ organization_id: VALID_UUID }, ctx)
253
+ ).rejects.toThrow('Failed to delete organization');
284
254
  });
285
255
  });
286
256
 
@@ -292,63 +262,57 @@ describe('listOrgMembers', () => {
292
262
  beforeEach(() => vi.clearAllMocks());
293
263
 
294
264
  it('should throw error for missing organization_id', async () => {
295
- const supabase = createMockSupabase();
296
- const ctx = createMockContext(supabase);
297
-
265
+ const ctx = createMockContext();
298
266
  await expect(listOrgMembers({}, ctx)).rejects.toThrow(ValidationError);
299
267
  });
300
268
 
301
269
  it('should throw error for invalid organization_id UUID', async () => {
302
- const supabase = createMockSupabase();
303
- const ctx = createMockContext(supabase);
304
-
270
+ const ctx = createMockContext();
305
271
  await expect(
306
272
  listOrgMembers({ organization_id: 'invalid' }, ctx)
307
273
  ).rejects.toThrow(ValidationError);
308
274
  });
309
275
 
310
276
  it('should return empty array when no members', async () => {
311
- const supabase = createMockSupabase({
312
- selectResult: { data: [], error: null },
277
+ mockApiClient.listOrgMembers.mockResolvedValue({
278
+ ok: true,
279
+ data: { members: [], count: 0 },
313
280
  });
314
- const ctx = createMockContext(supabase);
281
+ const ctx = createMockContext();
315
282
 
316
- const result = await listOrgMembers({ organization_id: testUUID() }, ctx);
283
+ const result = await listOrgMembers({ organization_id: VALID_UUID }, ctx);
317
284
 
318
- expect(result.result).toMatchObject({
319
- members: [],
320
- count: 0,
321
- });
285
+ expect(result.result).toMatchObject({ members: [], count: 0 });
322
286
  });
323
287
 
324
288
  it('should return members list', async () => {
325
- const supabase = createMockSupabase({
326
- selectResult: {
327
- data: [
328
- { id: 'm-1', user_id: 'u-1', role: 'owner', joined_at: '2025-01-01T00:00:00Z' },
329
- { id: 'm-2', user_id: 'u-2', role: 'member', joined_at: '2025-01-02T00:00:00Z' },
289
+ mockApiClient.listOrgMembers.mockResolvedValue({
290
+ ok: true,
291
+ data: {
292
+ members: [
293
+ { id: 'm-1', user_id: 'u-1', role: 'owner' },
294
+ { id: 'm-2', user_id: 'u-2', role: 'member' },
330
295
  ],
331
- error: null,
296
+ count: 2,
332
297
  },
333
298
  });
334
- const ctx = createMockContext(supabase);
299
+ const ctx = createMockContext();
335
300
 
336
- const result = await listOrgMembers({ organization_id: testUUID() }, ctx);
301
+ const result = await listOrgMembers({ organization_id: VALID_UUID }, ctx);
337
302
 
338
- expect(result.result).toMatchObject({
339
- count: 2,
340
- });
303
+ expect(result.result).toMatchObject({ count: 2 });
341
304
  });
342
305
 
343
306
  it('should throw error when query fails', async () => {
344
- const supabase = createMockSupabase({
345
- selectResult: { data: null, error: { message: 'Query failed' } },
307
+ mockApiClient.listOrgMembers.mockResolvedValue({
308
+ ok: false,
309
+ error: 'Failed to list members',
346
310
  });
347
- const ctx = createMockContext(supabase);
311
+ const ctx = createMockContext();
348
312
 
349
313
  await expect(
350
- listOrgMembers({ organization_id: testUUID() }, ctx)
351
- ).rejects.toThrow('Failed to list members: Query failed');
314
+ listOrgMembers({ organization_id: VALID_UUID }, ctx)
315
+ ).rejects.toThrow('Failed to list members');
352
316
  });
353
317
  });
354
318
 
@@ -360,69 +324,44 @@ describe('inviteMember', () => {
360
324
  beforeEach(() => vi.clearAllMocks());
361
325
 
362
326
  it('should throw error for missing organization_id', async () => {
363
- const supabase = createMockSupabase();
364
- const ctx = createMockContext(supabase);
365
-
327
+ const ctx = createMockContext();
366
328
  await expect(
367
329
  inviteMember({ email: 'test@example.com' }, ctx)
368
330
  ).rejects.toThrow(ValidationError);
369
331
  });
370
332
 
371
333
  it('should throw error for missing email', async () => {
372
- const supabase = createMockSupabase();
373
- const ctx = createMockContext(supabase);
374
-
334
+ const ctx = createMockContext();
375
335
  await expect(
376
- inviteMember({ organization_id: testUUID() }, ctx)
336
+ inviteMember({ organization_id: VALID_UUID }, ctx)
377
337
  ).rejects.toThrow(ValidationError);
378
338
  });
379
339
 
380
- it('should throw error for invalid role', async () => {
381
- const supabase = createMockSupabase({
382
- selectResult: { data: null, error: null }, // No existing invite
383
- });
384
- const ctx = createMockContext(supabase);
385
-
386
- await expect(
387
- inviteMember({
388
- organization_id: testUUID(),
389
- email: 'test@example.com',
390
- role: 'owner', // Invalid - can't invite as owner
391
- }, ctx)
392
- ).rejects.toThrow('Invalid role. Must be admin, member, or viewer.');
393
- });
394
-
395
340
  it('should throw error when invite already exists', async () => {
396
- const supabase = createMockSupabase({
397
- selectResult: { data: { id: 'existing-invite' }, error: null },
341
+ mockApiClient.inviteMember.mockResolvedValue({
342
+ ok: false,
343
+ error: 'A pending invite already exists for test@example.com',
398
344
  });
399
- const ctx = createMockContext(supabase);
345
+ const ctx = createMockContext();
400
346
 
401
347
  await expect(
402
- inviteMember({
403
- organization_id: testUUID(),
404
- email: 'test@example.com',
405
- }, ctx)
348
+ inviteMember({ organization_id: VALID_UUID, email: 'test@example.com' }, ctx)
406
349
  ).rejects.toThrow('A pending invite already exists for test@example.com');
407
350
  });
408
351
 
409
352
  it('should create invite successfully with default role', async () => {
410
- const supabase = createMockSupabase({
411
- selectResult: { data: null, error: null }, // No existing invite
412
- insertResult: {
413
- data: {
414
- id: 'invite-1',
415
- email: 'test@example.com',
416
- role: 'member',
417
- token: 'abc123',
418
- },
419
- error: null,
353
+ mockApiClient.inviteMember.mockResolvedValue({
354
+ ok: true,
355
+ data: {
356
+ success: true,
357
+ invite_id: 'invite-1',
358
+ message: 'Invite sent to test@example.com with role "member"',
420
359
  },
421
360
  });
422
- const ctx = createMockContext(supabase);
361
+ const ctx = createMockContext();
423
362
 
424
363
  const result = await inviteMember({
425
- organization_id: testUUID(),
364
+ organization_id: VALID_UUID,
426
365
  email: 'test@example.com',
427
366
  }, ctx);
428
367
 
@@ -433,18 +372,15 @@ describe('inviteMember', () => {
433
372
  });
434
373
 
435
374
  it('should throw error when insert fails', async () => {
436
- const supabase = createMockSupabase({
437
- selectResult: { data: null, error: null },
438
- insertResult: { data: null, error: { message: 'Insert failed' } },
375
+ mockApiClient.inviteMember.mockResolvedValue({
376
+ ok: false,
377
+ error: 'Failed to create invite',
439
378
  });
440
- const ctx = createMockContext(supabase);
379
+ const ctx = createMockContext();
441
380
 
442
381
  await expect(
443
- inviteMember({
444
- organization_id: testUUID(),
445
- email: 'test@example.com',
446
- }, ctx)
447
- ).rejects.toThrow('Failed to create invite: Insert failed');
382
+ inviteMember({ organization_id: VALID_UUID, email: 'test@example.com' }, ctx)
383
+ ).rejects.toThrow('Failed to create invite');
448
384
  });
449
385
  });
450
386
 
@@ -456,105 +392,72 @@ describe('updateMemberRole', () => {
456
392
  beforeEach(() => vi.clearAllMocks());
457
393
 
458
394
  it('should throw error for missing organization_id', async () => {
459
- const supabase = createMockSupabase();
460
- const ctx = createMockContext(supabase);
461
-
395
+ const ctx = createMockContext();
462
396
  await expect(
463
- updateMemberRole({ user_id: testUUID(), role: 'admin' }, ctx)
397
+ updateMemberRole({ user_id: VALID_UUID, role: 'admin' }, ctx)
464
398
  ).rejects.toThrow(ValidationError);
465
399
  });
466
400
 
467
401
  it('should throw error for missing user_id', async () => {
468
- const supabase = createMockSupabase();
469
- const ctx = createMockContext(supabase);
470
-
402
+ const ctx = createMockContext();
471
403
  await expect(
472
- updateMemberRole({ organization_id: testUUID(), role: 'admin' }, ctx)
404
+ updateMemberRole({ organization_id: VALID_UUID, role: 'admin' }, ctx)
473
405
  ).rejects.toThrow(ValidationError);
474
406
  });
475
407
 
476
408
  it('should throw error for missing role', async () => {
477
- const supabase = createMockSupabase();
478
- const ctx = createMockContext(supabase);
479
-
409
+ const ctx = createMockContext();
480
410
  await expect(
481
- updateMemberRole({ organization_id: testUUID(), user_id: testUUID() }, ctx)
411
+ updateMemberRole({ organization_id: VALID_UUID, user_id: OTHER_UUID }, ctx)
482
412
  ).rejects.toThrow(ValidationError);
483
413
  });
484
414
 
485
- it('should throw error for invalid role', async () => {
486
- const supabase = createMockSupabase();
487
- const ctx = createMockContext(supabase);
488
-
489
- await expect(
490
- updateMemberRole({
491
- organization_id: testUUID(),
492
- user_id: testUUID(),
493
- role: 'superadmin',
494
- }, ctx)
495
- ).rejects.toThrow('Invalid role. Must be one of: viewer, member, admin, owner');
496
- });
497
-
498
- it('should throw error when trying to assign owner role', async () => {
499
- const supabase = createMockSupabase();
500
- const ctx = createMockContext(supabase);
501
-
502
- await expect(
503
- updateMemberRole({
504
- organization_id: testUUID(),
505
- user_id: testUUID(),
506
- role: 'owner',
507
- }, ctx)
508
- ).rejects.toThrow('Cannot assign owner role. Use transfer ownership instead.');
509
- });
510
-
511
415
  it('should throw error when changing own role', async () => {
512
- const supabase = createMockSupabase();
513
- const ownUserId = testUUID(); // Use valid UUID
514
- const ctx = createMockContext(supabase, { userId: ownUserId });
416
+ mockApiClient.updateMemberRole.mockResolvedValue({
417
+ ok: false,
418
+ error: 'Cannot change your own role',
419
+ });
420
+ const ctx = createMockContext({ userId: VALID_UUID });
515
421
 
516
422
  await expect(
517
423
  updateMemberRole({
518
- organization_id: testUUID(),
519
- user_id: ownUserId,
424
+ organization_id: OTHER_UUID,
425
+ user_id: VALID_UUID,
520
426
  role: 'admin',
521
427
  }, ctx)
522
428
  ).rejects.toThrow('Cannot change your own role');
523
429
  });
524
430
 
525
431
  it('should update member role successfully', async () => {
526
- const supabase = createMockSupabase({
527
- updateResult: {
528
- data: { user_id: testUUID(), role: 'admin' },
529
- error: null,
530
- },
432
+ mockApiClient.updateMemberRole.mockResolvedValue({
433
+ ok: true,
434
+ data: { success: true, user_id: OTHER_UUID, role: 'admin' },
531
435
  });
532
- const ctx = createMockContext(supabase);
436
+ const ctx = createMockContext();
533
437
 
534
438
  const result = await updateMemberRole({
535
- organization_id: testUUID(),
536
- user_id: testUUID(),
439
+ organization_id: VALID_UUID,
440
+ user_id: OTHER_UUID,
537
441
  role: 'admin',
538
442
  }, ctx);
539
443
 
540
- expect(result.result).toMatchObject({
541
- success: true,
542
- });
444
+ expect(result.result).toMatchObject({ success: true });
543
445
  });
544
446
 
545
447
  it('should throw error when update fails', async () => {
546
- const supabase = createMockSupabase({
547
- updateResult: { data: null, error: { message: 'Update failed' } },
448
+ mockApiClient.updateMemberRole.mockResolvedValue({
449
+ ok: false,
450
+ error: 'Failed to update member role',
548
451
  });
549
- const ctx = createMockContext(supabase);
452
+ const ctx = createMockContext();
550
453
 
551
454
  await expect(
552
455
  updateMemberRole({
553
- organization_id: testUUID(),
554
- user_id: testUUID(),
456
+ organization_id: VALID_UUID,
457
+ user_id: OTHER_UUID,
555
458
  role: 'admin',
556
459
  }, ctx)
557
- ).rejects.toThrow('Failed to update member role: Update failed');
460
+ ).rejects.toThrow('Failed to update member role');
558
461
  });
559
462
  });
560
463
 
@@ -566,32 +469,32 @@ describe('removeMember', () => {
566
469
  beforeEach(() => vi.clearAllMocks());
567
470
 
568
471
  it('should throw error for missing organization_id', async () => {
569
- const supabase = createMockSupabase();
570
- const ctx = createMockContext(supabase);
571
-
472
+ const ctx = createMockContext();
572
473
  await expect(
573
- removeMember({ user_id: testUUID() }, ctx)
474
+ removeMember({ user_id: VALID_UUID }, ctx)
574
475
  ).rejects.toThrow(ValidationError);
575
476
  });
576
477
 
577
478
  it('should throw error for missing user_id', async () => {
578
- const supabase = createMockSupabase();
579
- const ctx = createMockContext(supabase);
580
-
479
+ const ctx = createMockContext();
581
480
  await expect(
582
- removeMember({ organization_id: testUUID() }, ctx)
481
+ removeMember({ organization_id: VALID_UUID }, ctx)
583
482
  ).rejects.toThrow(ValidationError);
584
483
  });
585
484
 
586
485
  it('should remove member successfully', async () => {
587
- const supabase = createMockSupabase({
588
- deleteResult: { data: null, error: null },
486
+ mockApiClient.removeMember.mockResolvedValue({
487
+ ok: true,
488
+ data: {
489
+ success: true,
490
+ message: 'Member removed. Their org-scoped API keys have been invalidated.',
491
+ },
589
492
  });
590
- const ctx = createMockContext(supabase);
493
+ const ctx = createMockContext();
591
494
 
592
495
  const result = await removeMember({
593
- organization_id: testUUID(),
594
- user_id: testUUID(),
496
+ organization_id: VALID_UUID,
497
+ user_id: OTHER_UUID,
595
498
  }, ctx);
596
499
 
597
500
  expect(result.result).toMatchObject({
@@ -601,17 +504,15 @@ describe('removeMember', () => {
601
504
  });
602
505
 
603
506
  it('should throw error when delete fails', async () => {
604
- const supabase = createMockSupabase({
605
- deleteResult: { data: null, error: { message: 'Delete failed' } },
507
+ mockApiClient.removeMember.mockResolvedValue({
508
+ ok: false,
509
+ error: 'Failed to remove member',
606
510
  });
607
- const ctx = createMockContext(supabase);
511
+ const ctx = createMockContext();
608
512
 
609
513
  await expect(
610
- removeMember({
611
- organization_id: testUUID(),
612
- user_id: testUUID(),
613
- }, ctx)
614
- ).rejects.toThrow('Failed to remove member: Delete failed');
514
+ removeMember({ organization_id: VALID_UUID, user_id: OTHER_UUID }, ctx)
515
+ ).rejects.toThrow('Failed to remove member');
615
516
  });
616
517
  });
617
518
 
@@ -623,31 +524,30 @@ describe('leaveOrganization', () => {
623
524
  beforeEach(() => vi.clearAllMocks());
624
525
 
625
526
  it('should throw error for missing organization_id', async () => {
626
- const supabase = createMockSupabase();
627
- const ctx = createMockContext(supabase);
628
-
527
+ const ctx = createMockContext();
629
528
  await expect(leaveOrganization({}, ctx)).rejects.toThrow(ValidationError);
630
529
  });
631
530
 
632
531
  it('should throw error when user is owner', async () => {
633
- const supabase = createMockSupabase({
634
- selectResult: { data: { role: 'owner' }, error: null },
532
+ mockApiClient.leaveOrganization.mockResolvedValue({
533
+ ok: false,
534
+ error: 'Owner cannot leave. Transfer ownership first or delete the organization.',
635
535
  });
636
- const ctx = createMockContext(supabase);
536
+ const ctx = createMockContext();
637
537
 
638
538
  await expect(
639
- leaveOrganization({ organization_id: testUUID() }, ctx)
539
+ leaveOrganization({ organization_id: VALID_UUID }, ctx)
640
540
  ).rejects.toThrow('Owner cannot leave. Transfer ownership first or delete the organization.');
641
541
  });
642
542
 
643
543
  it('should leave organization successfully', async () => {
644
- const supabase = createMockSupabase({
645
- selectResult: { data: { role: 'member' }, error: null },
646
- deleteResult: { data: null, error: null },
544
+ mockApiClient.leaveOrganization.mockResolvedValue({
545
+ ok: true,
546
+ data: { success: true, message: 'You have left the organization.' },
647
547
  });
648
- const ctx = createMockContext(supabase);
548
+ const ctx = createMockContext();
649
549
 
650
- const result = await leaveOrganization({ organization_id: testUUID() }, ctx);
550
+ const result = await leaveOrganization({ organization_id: VALID_UUID }, ctx);
651
551
 
652
552
  expect(result.result).toMatchObject({
653
553
  success: true,
@@ -656,15 +556,15 @@ describe('leaveOrganization', () => {
656
556
  });
657
557
 
658
558
  it('should throw error when delete fails', async () => {
659
- const supabase = createMockSupabase({
660
- selectResult: { data: { role: 'member' }, error: null },
661
- deleteResult: { data: null, error: { message: 'Delete failed' } },
559
+ mockApiClient.leaveOrganization.mockResolvedValue({
560
+ ok: false,
561
+ error: 'Failed to leave organization',
662
562
  });
663
- const ctx = createMockContext(supabase);
563
+ const ctx = createMockContext();
664
564
 
665
565
  await expect(
666
- leaveOrganization({ organization_id: testUUID() }, ctx)
667
- ).rejects.toThrow('Failed to leave organization: Delete failed');
566
+ leaveOrganization({ organization_id: VALID_UUID }, ctx)
567
+ ).rejects.toThrow('Failed to leave organization');
668
568
  });
669
569
  });
670
570
 
@@ -676,109 +576,55 @@ describe('shareProjectWithOrg', () => {
676
576
  beforeEach(() => vi.clearAllMocks());
677
577
 
678
578
  it('should throw error for missing project_id', async () => {
679
- const supabase = createMockSupabase();
680
- const ctx = createMockContext(supabase);
681
-
579
+ const ctx = createMockContext();
682
580
  await expect(
683
- shareProjectWithOrg({ organization_id: testUUID() }, ctx)
581
+ shareProjectWithOrg({ organization_id: VALID_UUID }, ctx)
684
582
  ).rejects.toThrow(ValidationError);
685
583
  });
686
584
 
687
585
  it('should throw error for missing organization_id', async () => {
688
- const supabase = createMockSupabase();
689
- const ctx = createMockContext(supabase);
690
-
586
+ const ctx = createMockContext();
691
587
  await expect(
692
- shareProjectWithOrg({ project_id: testUUID() }, ctx)
588
+ shareProjectWithOrg({ project_id: VALID_UUID }, ctx)
693
589
  ).rejects.toThrow(ValidationError);
694
590
  });
695
591
 
696
- it('should throw error for invalid permission', async () => {
697
- const supabase = createMockSupabase();
698
- const ctx = createMockContext(supabase);
699
-
700
- await expect(
701
- shareProjectWithOrg({
702
- project_id: testUUID(),
703
- organization_id: testUUID(),
704
- permission: 'invalid',
705
- }, ctx)
706
- ).rejects.toThrow('Invalid permission. Must be one of: read, write, admin');
707
- });
708
-
709
592
  it('should throw error when project not found or not owned', async () => {
710
- const supabase = createMockSupabase({
711
- selectResult: { data: null, error: null },
593
+ mockApiClient.shareProjectWithOrg.mockResolvedValue({
594
+ ok: false,
595
+ error: 'Project not found or you are not the owner',
712
596
  });
713
- const ctx = createMockContext(supabase);
597
+ const ctx = createMockContext();
714
598
 
715
599
  await expect(
716
- shareProjectWithOrg({
717
- project_id: testUUID(),
718
- organization_id: testUUID(),
719
- }, ctx)
600
+ shareProjectWithOrg({ project_id: VALID_UUID, organization_id: OTHER_UUID }, ctx)
720
601
  ).rejects.toThrow('Project not found or you are not the owner');
721
602
  });
722
603
 
723
604
  it('should share project successfully with default permission', async () => {
724
- // Need to simulate multiple select calls
725
- const supabase = createMockSupabase();
726
- let selectCount = 0;
727
- supabase.select = vi.fn(() => {
728
- selectCount++;
729
- return supabase;
730
- });
731
- supabase.single = vi.fn(() => {
732
- if (selectCount === 1) {
733
- // First call: get project
734
- return Promise.resolve({ data: { id: testUUID(), name: 'Test Project' }, error: null });
735
- }
736
- // Insert result
737
- return Promise.resolve({
738
- data: { id: 'share-1', permission: 'read' },
739
- error: null,
740
- });
741
- });
742
- supabase.maybeSingle = vi.fn(() => {
743
- // Check for existing share
744
- return Promise.resolve({ data: null, error: null });
745
- });
746
-
747
- const ctx = createMockContext(supabase);
605
+ mockApiClient.shareProjectWithOrg.mockResolvedValue({
606
+ ok: true,
607
+ data: { success: true, share_id: 'share-1', permission: 'read' },
608
+ });
609
+ const ctx = createMockContext();
748
610
 
749
611
  const result = await shareProjectWithOrg({
750
- project_id: testUUID(),
751
- organization_id: testUUID(),
612
+ project_id: VALID_UUID,
613
+ organization_id: OTHER_UUID,
752
614
  }, ctx);
753
615
 
754
- expect(result.result).toMatchObject({
755
- success: true,
756
- });
616
+ expect(result.result).toMatchObject({ success: true });
757
617
  });
758
618
 
759
619
  it('should throw error when share already exists', async () => {
760
- const supabase = createMockSupabase();
761
- let selectCount = 0;
762
- supabase.select = vi.fn(() => {
763
- selectCount++;
764
- return supabase;
765
- });
766
- supabase.single = vi.fn(() => {
767
- // First call: get project
768
- return Promise.resolve({ data: { id: testUUID(), name: 'Test Project' }, error: null });
620
+ mockApiClient.shareProjectWithOrg.mockResolvedValue({
621
+ ok: false,
622
+ error: 'Project is already shared with this organization',
769
623
  });
770
- supabase.maybeSingle = vi.fn(() => {
771
- // Check for existing share - exists
772
- return Promise.resolve({ data: { id: 'existing-share' }, error: null });
773
- });
774
-
775
- const ctx = createMockContext(supabase);
624
+ const ctx = createMockContext();
776
625
 
777
626
  await expect(
778
- shareProjectWithOrg({
779
- project_id: testUUID(),
780
- organization_id: testUUID(),
781
- }, ctx)
627
+ shareProjectWithOrg({ project_id: VALID_UUID, organization_id: OTHER_UUID }, ctx)
782
628
  ).rejects.toThrow('Project is already shared with this organization');
783
629
  });
784
630
  });
@@ -791,78 +637,56 @@ describe('updateProjectShare', () => {
791
637
  beforeEach(() => vi.clearAllMocks());
792
638
 
793
639
  it('should throw error for missing project_id', async () => {
794
- const supabase = createMockSupabase();
795
- const ctx = createMockContext(supabase);
796
-
640
+ const ctx = createMockContext();
797
641
  await expect(
798
- updateProjectShare({ organization_id: testUUID(), permission: 'write' }, ctx)
642
+ updateProjectShare({ organization_id: VALID_UUID, permission: 'write' }, ctx)
799
643
  ).rejects.toThrow(ValidationError);
800
644
  });
801
645
 
802
646
  it('should throw error for missing organization_id', async () => {
803
- const supabase = createMockSupabase();
804
- const ctx = createMockContext(supabase);
805
-
647
+ const ctx = createMockContext();
806
648
  await expect(
807
- updateProjectShare({ project_id: testUUID(), permission: 'write' }, ctx)
649
+ updateProjectShare({ project_id: VALID_UUID, permission: 'write' }, ctx)
808
650
  ).rejects.toThrow(ValidationError);
809
651
  });
810
652
 
811
653
  it('should throw error for missing permission', async () => {
812
- const supabase = createMockSupabase();
813
- const ctx = createMockContext(supabase);
814
-
654
+ const ctx = createMockContext();
815
655
  await expect(
816
- updateProjectShare({ project_id: testUUID(), organization_id: testUUID() }, ctx)
656
+ updateProjectShare({ project_id: VALID_UUID, organization_id: OTHER_UUID }, ctx)
817
657
  ).rejects.toThrow(ValidationError);
818
658
  });
819
659
 
820
- it('should throw error for invalid permission', async () => {
821
- const supabase = createMockSupabase();
822
- const ctx = createMockContext(supabase);
823
-
824
- await expect(
825
- updateProjectShare({
826
- project_id: testUUID(),
827
- organization_id: testUUID(),
828
- permission: 'invalid',
829
- }, ctx)
830
- ).rejects.toThrow('Invalid permission. Must be one of: read, write, admin');
831
- });
832
-
833
660
  it('should update share permission successfully', async () => {
834
- const supabase = createMockSupabase({
835
- updateResult: {
836
- data: { id: 'share-1', permission: 'write' },
837
- error: null,
838
- },
661
+ mockApiClient.updateProjectShare.mockResolvedValue({
662
+ ok: true,
663
+ data: { success: true, share_id: 'share-1', permission: 'write' },
839
664
  });
840
- const ctx = createMockContext(supabase);
665
+ const ctx = createMockContext();
841
666
 
842
667
  const result = await updateProjectShare({
843
- project_id: testUUID(),
844
- organization_id: testUUID(),
668
+ project_id: VALID_UUID,
669
+ organization_id: OTHER_UUID,
845
670
  permission: 'write',
846
671
  }, ctx);
847
672
 
848
- expect(result.result).toMatchObject({
849
- success: true,
850
- });
673
+ expect(result.result).toMatchObject({ success: true });
851
674
  });
852
675
 
853
676
  it('should throw error when update fails', async () => {
854
- const supabase = createMockSupabase({
855
- updateResult: { data: null, error: { message: 'Update failed' } },
677
+ mockApiClient.updateProjectShare.mockResolvedValue({
678
+ ok: false,
679
+ error: 'Failed to update share',
856
680
  });
857
- const ctx = createMockContext(supabase);
681
+ const ctx = createMockContext();
858
682
 
859
683
  await expect(
860
684
  updateProjectShare({
861
- project_id: testUUID(),
862
- organization_id: testUUID(),
685
+ project_id: VALID_UUID,
686
+ organization_id: OTHER_UUID,
863
687
  permission: 'write',
864
688
  }, ctx)
865
- ).rejects.toThrow('Failed to update share: Update failed');
689
+ ).rejects.toThrow('Failed to update share');
866
690
  });
867
691
  });
868
692
 
@@ -874,32 +698,32 @@ describe('unshareProject', () => {
874
698
  beforeEach(() => vi.clearAllMocks());
875
699
 
876
700
  it('should throw error for missing project_id', async () => {
877
- const supabase = createMockSupabase();
878
- const ctx = createMockContext(supabase);
879
-
701
+ const ctx = createMockContext();
880
702
  await expect(
881
- unshareProject({ organization_id: testUUID() }, ctx)
703
+ unshareProject({ organization_id: VALID_UUID }, ctx)
882
704
  ).rejects.toThrow(ValidationError);
883
705
  });
884
706
 
885
707
  it('should throw error for missing organization_id', async () => {
886
- const supabase = createMockSupabase();
887
- const ctx = createMockContext(supabase);
888
-
708
+ const ctx = createMockContext();
889
709
  await expect(
890
- unshareProject({ project_id: testUUID() }, ctx)
710
+ unshareProject({ project_id: VALID_UUID }, ctx)
891
711
  ).rejects.toThrow(ValidationError);
892
712
  });
893
713
 
894
714
  it('should unshare project successfully', async () => {
895
- const supabase = createMockSupabase({
896
- deleteResult: { data: null, error: null },
715
+ mockApiClient.unshareProject.mockResolvedValue({
716
+ ok: true,
717
+ data: {
718
+ success: true,
719
+ message: 'Project share removed. Org members can no longer access this project.',
720
+ },
897
721
  });
898
- const ctx = createMockContext(supabase);
722
+ const ctx = createMockContext();
899
723
 
900
724
  const result = await unshareProject({
901
- project_id: testUUID(),
902
- organization_id: testUUID(),
725
+ project_id: VALID_UUID,
726
+ organization_id: OTHER_UUID,
903
727
  }, ctx);
904
728
 
905
729
  expect(result.result).toMatchObject({
@@ -909,17 +733,15 @@ describe('unshareProject', () => {
909
733
  });
910
734
 
911
735
  it('should throw error when delete fails', async () => {
912
- const supabase = createMockSupabase({
913
- deleteResult: { data: null, error: { message: 'Delete failed' } },
736
+ mockApiClient.unshareProject.mockResolvedValue({
737
+ ok: false,
738
+ error: 'Failed to unshare project',
914
739
  });
915
- const ctx = createMockContext(supabase);
740
+ const ctx = createMockContext();
916
741
 
917
742
  await expect(
918
- unshareProject({
919
- project_id: testUUID(),
920
- organization_id: testUUID(),
921
- }, ctx)
922
- ).rejects.toThrow('Failed to unshare project: Delete failed');
743
+ unshareProject({ project_id: VALID_UUID, organization_id: OTHER_UUID }, ctx)
744
+ ).rejects.toThrow('Failed to unshare project');
923
745
  });
924
746
  });
925
747
 
@@ -931,67 +753,53 @@ describe('listProjectShares', () => {
931
753
  beforeEach(() => vi.clearAllMocks());
932
754
 
933
755
  it('should throw error for missing project_id', async () => {
934
- const supabase = createMockSupabase();
935
- const ctx = createMockContext(supabase);
936
-
756
+ const ctx = createMockContext();
937
757
  await expect(listProjectShares({}, ctx)).rejects.toThrow(ValidationError);
938
758
  });
939
759
 
940
760
  it('should throw error for invalid project_id UUID', async () => {
941
- const supabase = createMockSupabase();
942
- const ctx = createMockContext(supabase);
943
-
761
+ const ctx = createMockContext();
944
762
  await expect(
945
763
  listProjectShares({ project_id: 'invalid' }, ctx)
946
764
  ).rejects.toThrow(ValidationError);
947
765
  });
948
766
 
949
767
  it('should return empty array when no shares', async () => {
950
- const supabase = createMockSupabase({
951
- selectResult: { data: [], error: null },
768
+ mockApiClient.listProjectShares.mockResolvedValue({
769
+ ok: true,
770
+ data: { shares: [], count: 0 },
952
771
  });
953
- const ctx = createMockContext(supabase);
772
+ const ctx = createMockContext();
954
773
 
955
- const result = await listProjectShares({ project_id: testUUID() }, ctx);
774
+ const result = await listProjectShares({ project_id: VALID_UUID }, ctx);
956
775
 
957
- expect(result.result).toMatchObject({
958
- shares: [],
959
- count: 0,
960
- });
776
+ expect(result.result).toMatchObject({ shares: [], count: 0 });
961
777
  });
962
778
 
963
779
  it('should return shares list', async () => {
964
- const supabase = createMockSupabase({
965
- selectResult: {
966
- data: [
967
- {
968
- id: 'share-1',
969
- permission: 'read',
970
- shared_at: '2025-01-14T12:00:00Z',
971
- shared_by: 'user-123',
972
- organizations: { id: 'org-1', name: 'Test Org', slug: 'test-org' },
973
- },
974
- ],
975
- error: null,
780
+ mockApiClient.listProjectShares.mockResolvedValue({
781
+ ok: true,
782
+ data: {
783
+ shares: [{ id: 'share-1', permission: 'read', organization: { name: 'Test Org' } }],
784
+ count: 1,
976
785
  },
977
786
  });
978
- const ctx = createMockContext(supabase);
787
+ const ctx = createMockContext();
979
788
 
980
- const result = await listProjectShares({ project_id: testUUID() }, ctx);
789
+ const result = await listProjectShares({ project_id: VALID_UUID }, ctx);
981
790
 
982
- expect(result.result).toMatchObject({
983
- count: 1,
984
- });
791
+ expect(result.result).toMatchObject({ count: 1 });
985
792
  });
986
793
 
987
794
  it('should throw error when query fails', async () => {
988
- const supabase = createMockSupabase({
989
- selectResult: { data: null, error: { message: 'Query failed' } },
795
+ mockApiClient.listProjectShares.mockResolvedValue({
796
+ ok: false,
797
+ error: 'Failed to list shares',
990
798
  });
991
- const ctx = createMockContext(supabase);
799
+ const ctx = createMockContext();
992
800
 
993
801
  await expect(
994
- listProjectShares({ project_id: testUUID() }, ctx)
995
- ).rejects.toThrow('Failed to list shares: Query failed');
802
+ listProjectShares({ project_id: VALID_UUID }, ctx)
803
+ ).rejects.toThrow('Failed to list shares');
996
804
  });
997
805
  });