@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
@@ -1,6 +1,4 @@
1
1
  import { describe, it, expect, vi, beforeEach } from 'vitest';
2
- import type { SupabaseClient } from '@supabase/supabase-js';
3
- import type { HandlerContext, TokenUsage } from './types.js';
4
2
  import {
5
3
  claimDeploymentValidation,
6
4
  reportValidation,
@@ -12,113 +10,14 @@ import {
12
10
  getDeploymentRequirements,
13
11
  } from './deployment.js';
14
12
  import { ValidationError } from '../validators.js';
13
+ import { createMockContext } from './__test-utils__.js';
14
+ import { mockApiClient } from './__test-setup__.js';
15
15
 
16
16
  // ============================================================================
17
17
  // Test Utilities
18
18
  // ============================================================================
19
19
 
20
- function createMockSupabase(overrides: {
21
- selectResult?: { data: unknown; error: unknown };
22
- insertResult?: { data: unknown; error: unknown };
23
- updateResult?: { data: unknown; error: unknown };
24
- } = {}) {
25
- const defaultResult = { data: null, error: null };
26
- let currentOperation = 'select';
27
- let insertThenSelect = false;
28
-
29
- const mock = {
30
- from: vi.fn().mockReturnThis(),
31
- select: vi.fn(() => {
32
- if (currentOperation === 'insert') {
33
- insertThenSelect = true;
34
- } else {
35
- currentOperation = 'select';
36
- insertThenSelect = false;
37
- }
38
- return mock;
39
- }),
40
- insert: vi.fn(() => {
41
- currentOperation = 'insert';
42
- insertThenSelect = false;
43
- return mock;
44
- }),
45
- update: vi.fn(() => {
46
- currentOperation = 'update';
47
- insertThenSelect = false;
48
- return mock;
49
- }),
50
- delete: vi.fn(() => {
51
- currentOperation = 'delete';
52
- insertThenSelect = false;
53
- return mock;
54
- }),
55
- eq: vi.fn().mockReturnThis(),
56
- neq: vi.fn().mockReturnThis(),
57
- in: vi.fn().mockReturnThis(),
58
- is: vi.fn().mockReturnThis(),
59
- not: vi.fn().mockReturnThis(),
60
- or: vi.fn().mockReturnThis(),
61
- gte: vi.fn().mockReturnThis(),
62
- lte: vi.fn().mockReturnThis(),
63
- lt: vi.fn().mockReturnThis(),
64
- order: vi.fn().mockReturnThis(),
65
- limit: vi.fn().mockReturnThis(),
66
- single: vi.fn(() => {
67
- if (currentOperation === 'insert' || insertThenSelect) {
68
- return Promise.resolve(overrides.insertResult ?? defaultResult);
69
- }
70
- if (currentOperation === 'select') {
71
- return Promise.resolve(overrides.selectResult ?? defaultResult);
72
- }
73
- if (currentOperation === 'update') {
74
- return Promise.resolve(overrides.updateResult ?? defaultResult);
75
- }
76
- return Promise.resolve(defaultResult);
77
- }),
78
- maybeSingle: vi.fn(() => {
79
- return Promise.resolve(overrides.selectResult ?? defaultResult);
80
- }),
81
- then: vi.fn((resolve: (value: unknown) => void) => {
82
- if (currentOperation === 'insert' || insertThenSelect) {
83
- return Promise.resolve(overrides.insertResult ?? defaultResult).then(resolve);
84
- }
85
- if (currentOperation === 'select') {
86
- return Promise.resolve(overrides.selectResult ?? defaultResult).then(resolve);
87
- }
88
- if (currentOperation === 'update') {
89
- return Promise.resolve(overrides.updateResult ?? defaultResult).then(resolve);
90
- }
91
- return Promise.resolve(defaultResult).then(resolve);
92
- }),
93
- };
94
-
95
- return mock as unknown as SupabaseClient;
96
- }
97
-
98
- function createMockContext(
99
- supabase: SupabaseClient,
100
- options: { sessionId?: string | null } = {}
101
- ): HandlerContext {
102
- const defaultTokenUsage: TokenUsage = {
103
- callCount: 5,
104
- totalTokens: 2500,
105
- byTool: {},
106
- };
107
-
108
- const sessionId = 'sessionId' in options ? options.sessionId : 'session-123';
109
-
110
- return {
111
- supabase,
112
- auth: { userId: 'user-123', apiKeyId: 'api-key-123' },
113
- session: {
114
- instanceId: 'instance-abc',
115
- currentSessionId: sessionId,
116
- currentPersona: 'Wave',
117
- tokenUsage: defaultTokenUsage,
118
- },
119
- updateSession: vi.fn(),
120
- };
121
- }
20
+ const VALID_UUID = '123e4567-e89b-12d3-a456-426614174000';
122
21
 
123
22
  // ============================================================================
124
23
  // claimDeploymentValidation Tests
@@ -128,37 +27,58 @@ describe('claimDeploymentValidation', () => {
128
27
  beforeEach(() => vi.clearAllMocks());
129
28
 
130
29
  it('should throw error for missing project_id', async () => {
131
- const supabase = createMockSupabase();
132
- const ctx = createMockContext(supabase);
133
-
30
+ const ctx = createMockContext();
134
31
  await expect(claimDeploymentValidation({}, ctx)).rejects.toThrow(ValidationError);
135
32
  });
136
33
 
137
34
  it('should throw error for invalid project_id UUID', async () => {
138
- const supabase = createMockSupabase();
139
- const ctx = createMockContext(supabase);
140
-
35
+ const ctx = createMockContext();
141
36
  await expect(
142
37
  claimDeploymentValidation({ project_id: 'invalid' }, ctx)
143
38
  ).rejects.toThrow(ValidationError);
144
39
  });
145
40
 
146
41
  it('should return error when no pending deployment', async () => {
147
- const supabase = createMockSupabase({
148
- selectResult: { data: null, error: { message: 'Not found' } },
42
+ mockApiClient.claimDeploymentValidation.mockResolvedValue({
43
+ ok: true,
44
+ data: { success: false, error: 'No pending deployment found' },
149
45
  });
150
- const ctx = createMockContext(supabase);
46
+ const ctx = createMockContext();
151
47
 
152
- const result = await claimDeploymentValidation(
153
- { project_id: '123e4567-e89b-12d3-a456-426614174000' },
154
- ctx
155
- );
48
+ const result = await claimDeploymentValidation({ project_id: VALID_UUID }, ctx);
156
49
 
157
50
  expect(result.result).toMatchObject({
158
51
  success: false,
159
52
  error: 'No pending deployment found',
160
53
  });
161
54
  });
55
+
56
+ it('should claim deployment successfully', async () => {
57
+ mockApiClient.claimDeploymentValidation.mockResolvedValue({
58
+ ok: true,
59
+ data: { success: true, deployment_id: 'deploy-1' },
60
+ });
61
+ const ctx = createMockContext();
62
+
63
+ const result = await claimDeploymentValidation({ project_id: VALID_UUID }, ctx);
64
+
65
+ expect(result.result).toMatchObject({
66
+ success: true,
67
+ deployment_id: 'deploy-1',
68
+ });
69
+ });
70
+
71
+ it('should pass session_id to API client', async () => {
72
+ mockApiClient.claimDeploymentValidation.mockResolvedValue({
73
+ ok: true,
74
+ data: { success: true },
75
+ });
76
+ const ctx = createMockContext({ sessionId: 'my-session' });
77
+
78
+ await claimDeploymentValidation({ project_id: VALID_UUID }, ctx);
79
+
80
+ expect(mockApiClient.claimDeploymentValidation).toHaveBeenCalledWith(VALID_UUID, 'my-session');
81
+ });
162
82
  });
163
83
 
164
84
  // ============================================================================
@@ -169,31 +89,28 @@ describe('reportValidation', () => {
169
89
  beforeEach(() => vi.clearAllMocks());
170
90
 
171
91
  it('should throw error for missing project_id', async () => {
172
- const supabase = createMockSupabase();
173
- const ctx = createMockContext(supabase);
174
-
92
+ const ctx = createMockContext();
175
93
  await expect(
176
94
  reportValidation({ build_passed: true }, ctx)
177
95
  ).rejects.toThrow(ValidationError);
178
96
  });
179
97
 
180
98
  it('should throw error for missing build_passed', async () => {
181
- const supabase = createMockSupabase();
182
- const ctx = createMockContext(supabase);
183
-
99
+ const ctx = createMockContext();
184
100
  await expect(
185
- reportValidation({ project_id: '123e4567-e89b-12d3-a456-426614174000' }, ctx)
101
+ reportValidation({ project_id: VALID_UUID }, ctx)
186
102
  ).rejects.toThrow(ValidationError);
187
103
  });
188
104
 
189
105
  it('should return error when no deployment being validated', async () => {
190
- const supabase = createMockSupabase({
191
- selectResult: { data: null, error: { message: 'Not found' } },
106
+ mockApiClient.reportValidation.mockResolvedValue({
107
+ ok: true,
108
+ data: { success: false, error: 'No deployment being validated. Use claim_deployment_validation first.' },
192
109
  });
193
- const ctx = createMockContext(supabase);
110
+ const ctx = createMockContext();
194
111
 
195
112
  const result = await reportValidation(
196
- { project_id: '123e4567-e89b-12d3-a456-426614174000', build_passed: true },
113
+ { project_id: VALID_UUID, build_passed: true },
197
114
  ctx
198
115
  );
199
116
 
@@ -202,6 +119,24 @@ describe('reportValidation', () => {
202
119
  error: 'No deployment being validated. Use claim_deployment_validation first.',
203
120
  });
204
121
  });
122
+
123
+ it('should report validation successfully', async () => {
124
+ mockApiClient.reportValidation.mockResolvedValue({
125
+ ok: true,
126
+ data: { success: true, status: 'ready' },
127
+ });
128
+ const ctx = createMockContext();
129
+
130
+ const result = await reportValidation(
131
+ { project_id: VALID_UUID, build_passed: true, tests_passed: true },
132
+ ctx
133
+ );
134
+
135
+ expect(result.result).toMatchObject({
136
+ success: true,
137
+ status: 'ready',
138
+ });
139
+ });
205
140
  });
206
141
 
207
142
  // ============================================================================
@@ -212,22 +147,18 @@ describe('checkDeploymentStatus', () => {
212
147
  beforeEach(() => vi.clearAllMocks());
213
148
 
214
149
  it('should throw error for missing project_id', async () => {
215
- const supabase = createMockSupabase();
216
- const ctx = createMockContext(supabase);
217
-
150
+ const ctx = createMockContext();
218
151
  await expect(checkDeploymentStatus({}, ctx)).rejects.toThrow(ValidationError);
219
152
  });
220
153
 
221
154
  it('should return no deployment when none found', async () => {
222
- const supabase = createMockSupabase({
223
- selectResult: { data: null, error: { message: 'Not found' } },
155
+ mockApiClient.checkDeploymentStatus.mockResolvedValue({
156
+ ok: true,
157
+ data: { has_deployment: false, message: 'No deployments found for this project' },
224
158
  });
225
- const ctx = createMockContext(supabase);
159
+ const ctx = createMockContext();
226
160
 
227
- const result = await checkDeploymentStatus(
228
- { project_id: '123e4567-e89b-12d3-a456-426614174000' },
229
- ctx
230
- );
161
+ const result = await checkDeploymentStatus({ project_id: VALID_UUID }, ctx);
231
162
 
232
163
  expect(result.result).toMatchObject({
233
164
  has_deployment: false,
@@ -240,21 +171,14 @@ describe('checkDeploymentStatus', () => {
240
171
  id: 'deploy-1',
241
172
  status: 'deployed',
242
173
  environment: 'production',
243
- requested_by: 'agent',
244
- build_passed: true,
245
- tests_passed: true,
246
- created_at: '2025-01-14T10:00:00Z',
247
174
  };
248
-
249
- const supabase = createMockSupabase({
250
- selectResult: { data: mockDeployment, error: null },
175
+ mockApiClient.checkDeploymentStatus.mockResolvedValue({
176
+ ok: true,
177
+ data: { has_deployment: true, deployment: mockDeployment },
251
178
  });
252
- const ctx = createMockContext(supabase);
179
+ const ctx = createMockContext();
253
180
 
254
- const result = await checkDeploymentStatus(
255
- { project_id: '123e4567-e89b-12d3-a456-426614174000' },
256
- ctx
257
- );
181
+ const result = await checkDeploymentStatus({ project_id: VALID_UUID }, ctx);
258
182
 
259
183
  expect(result.result).toMatchObject({
260
184
  has_deployment: true,
@@ -271,28 +195,39 @@ describe('startDeployment', () => {
271
195
  beforeEach(() => vi.clearAllMocks());
272
196
 
273
197
  it('should throw error for missing project_id', async () => {
274
- const supabase = createMockSupabase();
275
- const ctx = createMockContext(supabase);
276
-
198
+ const ctx = createMockContext();
277
199
  await expect(startDeployment({}, ctx)).rejects.toThrow(ValidationError);
278
200
  });
279
201
 
280
202
  it('should return error when no deployment ready', async () => {
281
- const supabase = createMockSupabase({
282
- selectResult: { data: null, error: { message: 'Not found' } },
203
+ mockApiClient.startDeployment.mockResolvedValue({
204
+ ok: true,
205
+ data: { success: false, error: 'No deployment ready. Must pass validation first.' },
283
206
  });
284
- const ctx = createMockContext(supabase);
207
+ const ctx = createMockContext();
285
208
 
286
- const result = await startDeployment(
287
- { project_id: '123e4567-e89b-12d3-a456-426614174000' },
288
- ctx
289
- );
209
+ const result = await startDeployment({ project_id: VALID_UUID }, ctx);
290
210
 
291
211
  expect(result.result).toMatchObject({
292
212
  success: false,
293
213
  error: 'No deployment ready. Must pass validation first.',
294
214
  });
295
215
  });
216
+
217
+ it('should start deployment successfully', async () => {
218
+ mockApiClient.startDeployment.mockResolvedValue({
219
+ ok: true,
220
+ data: { success: true, status: 'deploying' },
221
+ });
222
+ const ctx = createMockContext();
223
+
224
+ const result = await startDeployment({ project_id: VALID_UUID }, ctx);
225
+
226
+ expect(result.result).toMatchObject({
227
+ success: true,
228
+ status: 'deploying',
229
+ });
230
+ });
296
231
  });
297
232
 
298
233
  // ============================================================================
@@ -303,31 +238,28 @@ describe('completeDeployment', () => {
303
238
  beforeEach(() => vi.clearAllMocks());
304
239
 
305
240
  it('should throw error for missing project_id', async () => {
306
- const supabase = createMockSupabase();
307
- const ctx = createMockContext(supabase);
308
-
241
+ const ctx = createMockContext();
309
242
  await expect(
310
243
  completeDeployment({ success: true }, ctx)
311
244
  ).rejects.toThrow(ValidationError);
312
245
  });
313
246
 
314
247
  it('should throw error for missing success', async () => {
315
- const supabase = createMockSupabase();
316
- const ctx = createMockContext(supabase);
317
-
248
+ const ctx = createMockContext();
318
249
  await expect(
319
- completeDeployment({ project_id: '123e4567-e89b-12d3-a456-426614174000' }, ctx)
250
+ completeDeployment({ project_id: VALID_UUID }, ctx)
320
251
  ).rejects.toThrow(ValidationError);
321
252
  });
322
253
 
323
254
  it('should return error when no deployment in progress', async () => {
324
- const supabase = createMockSupabase({
325
- selectResult: { data: null, error: { message: 'Not found' } },
255
+ mockApiClient.completeDeployment.mockResolvedValue({
256
+ ok: true,
257
+ data: { success: false, error: 'No deployment in progress. Use start_deployment first.' },
326
258
  });
327
- const ctx = createMockContext(supabase);
259
+ const ctx = createMockContext();
328
260
 
329
261
  const result = await completeDeployment(
330
- { project_id: '123e4567-e89b-12d3-a456-426614174000', success: true },
262
+ { project_id: VALID_UUID, success: true },
331
263
  ctx
332
264
  );
333
265
 
@@ -336,6 +268,24 @@ describe('completeDeployment', () => {
336
268
  error: 'No deployment in progress. Use start_deployment first.',
337
269
  });
338
270
  });
271
+
272
+ it('should complete deployment successfully', async () => {
273
+ mockApiClient.completeDeployment.mockResolvedValue({
274
+ ok: true,
275
+ data: { success: true, status: 'deployed' },
276
+ });
277
+ const ctx = createMockContext();
278
+
279
+ const result = await completeDeployment(
280
+ { project_id: VALID_UUID, success: true, summary: 'Deployed v1.2.0' },
281
+ ctx
282
+ );
283
+
284
+ expect(result.result).toMatchObject({
285
+ success: true,
286
+ status: 'deployed',
287
+ });
288
+ });
339
289
  });
340
290
 
341
291
  // ============================================================================
@@ -346,26 +296,40 @@ describe('cancelDeployment', () => {
346
296
  beforeEach(() => vi.clearAllMocks());
347
297
 
348
298
  it('should throw error for missing project_id', async () => {
349
- const supabase = createMockSupabase();
350
- const ctx = createMockContext(supabase);
351
-
299
+ const ctx = createMockContext();
352
300
  await expect(cancelDeployment({}, ctx)).rejects.toThrow(ValidationError);
353
301
  });
354
302
 
355
303
  it('should return error when no active deployment', async () => {
356
- const supabase = createMockSupabase({
357
- selectResult: { data: null, error: { message: 'Not found' } },
304
+ mockApiClient.cancelDeployment.mockResolvedValue({
305
+ ok: true,
306
+ data: { success: false, error: 'No active deployment' },
358
307
  });
359
- const ctx = createMockContext(supabase);
308
+ const ctx = createMockContext();
309
+
310
+ const result = await cancelDeployment({ project_id: VALID_UUID }, ctx);
311
+
312
+ expect(result.result).toMatchObject({
313
+ success: false,
314
+ error: 'No active deployment',
315
+ });
316
+ });
317
+
318
+ it('should cancel deployment successfully', async () => {
319
+ mockApiClient.cancelDeployment.mockResolvedValue({
320
+ ok: true,
321
+ data: { success: true, status: 'failed' },
322
+ });
323
+ const ctx = createMockContext();
360
324
 
361
325
  const result = await cancelDeployment(
362
- { project_id: '123e4567-e89b-12d3-a456-426614174000' },
326
+ { project_id: VALID_UUID, reason: 'Found critical bug' },
363
327
  ctx
364
328
  );
365
329
 
366
330
  expect(result.result).toMatchObject({
367
- success: false,
368
- error: 'No active deployment',
331
+ success: true,
332
+ status: 'failed',
369
333
  });
370
334
  });
371
335
  });
@@ -378,45 +342,31 @@ describe('addDeploymentRequirement', () => {
378
342
  beforeEach(() => vi.clearAllMocks());
379
343
 
380
344
  it('should throw error for missing project_id', async () => {
381
- const supabase = createMockSupabase();
382
- const ctx = createMockContext(supabase);
383
-
345
+ const ctx = createMockContext();
384
346
  await expect(
385
347
  addDeploymentRequirement({ type: 'migration', title: 'Test' }, ctx)
386
348
  ).rejects.toThrow(ValidationError);
387
349
  });
388
350
 
389
351
  it('should throw error for missing type', async () => {
390
- const supabase = createMockSupabase();
391
- const ctx = createMockContext(supabase);
392
-
352
+ const ctx = createMockContext();
393
353
  await expect(
394
- addDeploymentRequirement({
395
- project_id: '123e4567-e89b-12d3-a456-426614174000',
396
- title: 'Test',
397
- }, ctx)
354
+ addDeploymentRequirement({ project_id: VALID_UUID, title: 'Test' }, ctx)
398
355
  ).rejects.toThrow(ValidationError);
399
356
  });
400
357
 
401
358
  it('should throw error for missing title', async () => {
402
- const supabase = createMockSupabase();
403
- const ctx = createMockContext(supabase);
404
-
359
+ const ctx = createMockContext();
405
360
  await expect(
406
- addDeploymentRequirement({
407
- project_id: '123e4567-e89b-12d3-a456-426614174000',
408
- type: 'migration',
409
- }, ctx)
361
+ addDeploymentRequirement({ project_id: VALID_UUID, type: 'migration' }, ctx)
410
362
  ).rejects.toThrow(ValidationError);
411
363
  });
412
364
 
413
365
  it('should throw error for invalid type', async () => {
414
- const supabase = createMockSupabase();
415
- const ctx = createMockContext(supabase);
416
-
366
+ const ctx = createMockContext();
417
367
  await expect(
418
368
  addDeploymentRequirement({
419
- project_id: '123e4567-e89b-12d3-a456-426614174000',
369
+ project_id: VALID_UUID,
420
370
  type: 'invalid_type',
421
371
  title: 'Test',
422
372
  }, ctx)
@@ -424,12 +374,10 @@ describe('addDeploymentRequirement', () => {
424
374
  });
425
375
 
426
376
  it('should throw error for invalid stage', async () => {
427
- const supabase = createMockSupabase();
428
- const ctx = createMockContext(supabase);
429
-
377
+ const ctx = createMockContext();
430
378
  await expect(
431
379
  addDeploymentRequirement({
432
- project_id: '123e4567-e89b-12d3-a456-426614174000',
380
+ project_id: VALID_UUID,
433
381
  type: 'migration',
434
382
  title: 'Test',
435
383
  stage: 'invalid_stage',
@@ -438,17 +386,15 @@ describe('addDeploymentRequirement', () => {
438
386
  });
439
387
 
440
388
  it('should add requirement successfully', async () => {
441
- const supabase = createMockSupabase({
442
- insertResult: {
443
- data: { id: 'req-1', type: 'migration', title: 'Test Migration', stage: 'preparation', blocking: false },
444
- error: null,
445
- },
389
+ mockApiClient.addDeploymentRequirement.mockResolvedValue({
390
+ ok: true,
391
+ data: { success: true, requirement_id: 'req-1', stage: 'preparation' },
446
392
  });
447
- const ctx = createMockContext(supabase);
393
+ const ctx = createMockContext();
448
394
 
449
395
  const result = await addDeploymentRequirement(
450
396
  {
451
- project_id: '123e4567-e89b-12d3-a456-426614174000',
397
+ project_id: VALID_UUID,
452
398
  type: 'migration',
453
399
  title: 'Test Migration',
454
400
  },
@@ -471,46 +417,54 @@ describe('getDeploymentRequirements', () => {
471
417
  beforeEach(() => vi.clearAllMocks());
472
418
 
473
419
  it('should throw error for missing project_id', async () => {
474
- const supabase = createMockSupabase();
475
- const ctx = createMockContext(supabase);
476
-
420
+ const ctx = createMockContext();
477
421
  await expect(getDeploymentRequirements({}, ctx)).rejects.toThrow(ValidationError);
478
422
  });
479
423
 
480
424
  it('should return empty list when no requirements', async () => {
481
- const supabase = createMockSupabase({
482
- selectResult: { data: [], error: null },
425
+ mockApiClient.getDeploymentRequirements.mockResolvedValue({
426
+ ok: true,
427
+ data: { requirements: [], deployment_blocked: false },
483
428
  });
484
- const ctx = createMockContext(supabase);
485
-
486
- // Need to mock the .then() to return the array result
487
- vi.mocked(supabase.from('').select).mockReturnValue({
488
- ...supabase,
489
- then: (resolve: (val: unknown) => void) =>
490
- Promise.resolve({ data: [], error: null }).then(resolve),
491
- } as unknown as ReturnType<SupabaseClient['from']>);
429
+ const ctx = createMockContext();
492
430
 
493
- const result = await getDeploymentRequirements(
494
- { project_id: '123e4567-e89b-12d3-a456-426614174000' },
495
- ctx
496
- );
431
+ const result = await getDeploymentRequirements({ project_id: VALID_UUID }, ctx);
497
432
 
498
- expect(result.result).toHaveProperty('requirements');
499
- expect(result.result).toHaveProperty('deployment_blocked');
433
+ expect(result.result).toMatchObject({
434
+ requirements: [],
435
+ deployment_blocked: false,
436
+ });
500
437
  });
501
438
 
502
439
  it('should query with correct project_id', async () => {
503
- const supabase = createMockSupabase({
504
- selectResult: { data: [], error: null },
440
+ mockApiClient.getDeploymentRequirements.mockResolvedValue({
441
+ ok: true,
442
+ data: { requirements: [], deployment_blocked: false },
505
443
  });
506
- const ctx = createMockContext(supabase);
444
+ const ctx = createMockContext();
507
445
 
508
- await getDeploymentRequirements(
509
- { project_id: '123e4567-e89b-12d3-a456-426614174000' },
510
- ctx
446
+ await getDeploymentRequirements({ project_id: VALID_UUID }, ctx);
447
+
448
+ expect(mockApiClient.getDeploymentRequirements).toHaveBeenCalledWith(
449
+ VALID_UUID,
450
+ expect.any(Object)
511
451
  );
452
+ });
453
+
454
+ it('should return requirements list', async () => {
455
+ const mockReqs = [
456
+ { id: 'req-1', type: 'migration', title: 'Run migrations' },
457
+ { id: 'req-2', type: 'env_var', title: 'Set API_KEY' },
458
+ ];
459
+ mockApiClient.getDeploymentRequirements.mockResolvedValue({
460
+ ok: true,
461
+ data: { requirements: mockReqs, deployment_blocked: true },
462
+ });
463
+ const ctx = createMockContext();
464
+
465
+ const result = await getDeploymentRequirements({ project_id: VALID_UUID }, ctx);
512
466
 
513
- expect(supabase.from).toHaveBeenCalledWith('deployment_requirements');
514
- expect(supabase.eq).toHaveBeenCalled();
467
+ expect((result.result as { requirements: unknown[] }).requirements).toHaveLength(2);
468
+ expect(result.result).toHaveProperty('deployment_blocked', true);
515
469
  });
516
470
  });