@vibescope/mcp-server 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +113 -98
- package/dist/api-client.d.ts +1114 -0
- package/dist/api-client.js +698 -0
- package/dist/cli.d.ts +1 -6
- package/dist/cli.js +39 -240
- package/dist/config/tool-categories.d.ts +31 -0
- package/dist/config/tool-categories.js +253 -0
- package/dist/handlers/blockers.js +57 -58
- package/dist/handlers/bodies-of-work.d.ts +2 -0
- package/dist/handlers/bodies-of-work.js +106 -476
- package/dist/handlers/cost.d.ts +1 -0
- package/dist/handlers/cost.js +35 -113
- package/dist/handlers/decisions.d.ts +2 -0
- package/dist/handlers/decisions.js +28 -27
- package/dist/handlers/deployment.js +112 -828
- package/dist/handlers/discovery.js +31 -0
- package/dist/handlers/fallback.d.ts +2 -0
- package/dist/handlers/fallback.js +39 -134
- package/dist/handlers/findings.js +43 -67
- package/dist/handlers/git-issues.d.ts +9 -13
- package/dist/handlers/git-issues.js +80 -225
- package/dist/handlers/ideas.d.ts +3 -0
- package/dist/handlers/ideas.js +53 -134
- package/dist/handlers/index.d.ts +2 -0
- package/dist/handlers/index.js +6 -0
- package/dist/handlers/milestones.d.ts +2 -0
- package/dist/handlers/milestones.js +51 -98
- package/dist/handlers/organizations.js +79 -275
- package/dist/handlers/progress.d.ts +2 -0
- package/dist/handlers/progress.js +25 -123
- package/dist/handlers/project.js +42 -221
- package/dist/handlers/requests.d.ts +2 -0
- package/dist/handlers/requests.js +23 -83
- package/dist/handlers/session.js +99 -585
- package/dist/handlers/sprints.d.ts +32 -0
- package/dist/handlers/sprints.js +274 -0
- package/dist/handlers/tasks.d.ts +7 -10
- package/dist/handlers/tasks.js +230 -900
- package/dist/handlers/tool-docs.d.ts +8 -0
- package/dist/handlers/tool-docs.js +657 -0
- package/dist/handlers/types.d.ts +11 -3
- package/dist/handlers/validation.d.ts +1 -1
- package/dist/handlers/validation.js +26 -153
- package/dist/index.js +473 -160
- package/dist/knowledge.js +106 -9
- package/dist/tools.js +4 -0
- package/dist/validators.d.ts +21 -0
- package/dist/validators.js +91 -0
- package/package.json +2 -3
- package/src/api-client.ts +1752 -0
- package/src/cli.test.ts +128 -302
- package/src/cli.ts +41 -285
- package/src/handlers/__test-setup__.ts +210 -0
- package/src/handlers/__test-utils__.ts +4 -134
- package/src/handlers/blockers.test.ts +114 -124
- package/src/handlers/blockers.ts +68 -70
- package/src/handlers/bodies-of-work.test.ts +236 -831
- package/src/handlers/bodies-of-work.ts +194 -525
- package/src/handlers/cost.test.ts +149 -113
- package/src/handlers/cost.ts +44 -132
- package/src/handlers/decisions.test.ts +111 -209
- package/src/handlers/decisions.ts +35 -27
- package/src/handlers/deployment.test.ts +193 -239
- package/src/handlers/deployment.ts +140 -895
- package/src/handlers/discovery.test.ts +20 -67
- package/src/handlers/discovery.ts +32 -0
- package/src/handlers/fallback.test.ts +128 -361
- package/src/handlers/fallback.ts +62 -148
- package/src/handlers/findings.test.ts +127 -345
- package/src/handlers/findings.ts +49 -66
- package/src/handlers/git-issues.test.ts +623 -0
- package/src/handlers/git-issues.ts +174 -0
- package/src/handlers/ideas.test.ts +229 -343
- package/src/handlers/ideas.ts +69 -143
- package/src/handlers/index.ts +6 -0
- package/src/handlers/milestones.test.ts +167 -281
- package/src/handlers/milestones.ts +54 -93
- package/src/handlers/organizations.test.ts +275 -467
- package/src/handlers/organizations.ts +84 -294
- package/src/handlers/progress.test.ts +112 -218
- package/src/handlers/progress.ts +29 -142
- package/src/handlers/project.test.ts +203 -226
- package/src/handlers/project.ts +48 -238
- package/src/handlers/requests.test.ts +74 -342
- package/src/handlers/requests.ts +25 -83
- package/src/handlers/session.test.ts +241 -206
- package/src/handlers/session.ts +110 -657
- package/src/handlers/sprints.test.ts +711 -0
- package/src/handlers/sprints.ts +497 -0
- package/src/handlers/tasks.test.ts +608 -353
- package/src/handlers/tasks.ts +248 -1025
- package/src/handlers/types.ts +12 -4
- package/src/handlers/validation.test.ts +189 -572
- package/src/handlers/validation.ts +29 -166
- package/src/index.ts +473 -184
- package/src/knowledge.ts +107 -9
- package/src/tools.ts +2506 -0
- package/src/validators.test.ts +223 -223
- package/src/validators.ts +127 -0
- package/tsconfig.json +1 -1
- package/vitest.config.ts +14 -13
- package/dist/cli.test.d.ts +0 -1
- package/dist/cli.test.js +0 -367
- package/dist/handlers/__test-utils__.d.ts +0 -72
- package/dist/handlers/__test-utils__.js +0 -176
- package/dist/handlers/checkouts.d.ts +0 -37
- package/dist/handlers/checkouts.js +0 -377
- package/dist/handlers/knowledge-query.d.ts +0 -22
- package/dist/handlers/knowledge-query.js +0 -253
- package/dist/handlers/knowledge.d.ts +0 -12
- package/dist/handlers/knowledge.js +0 -108
- package/dist/handlers/roles.d.ts +0 -30
- package/dist/handlers/roles.js +0 -281
- package/dist/handlers/tasks.test.d.ts +0 -1
- package/dist/handlers/tasks.test.js +0 -431
- package/dist/utils.test.d.ts +0 -1
- package/dist/utils.test.js +0 -532
- package/dist/validators.test.d.ts +0 -1
- package/dist/validators.test.js +0 -176
- package/src/tmpclaude-0078-cwd +0 -1
- package/src/tmpclaude-0ee1-cwd +0 -1
- package/src/tmpclaude-2dd5-cwd +0 -1
- package/src/tmpclaude-344c-cwd +0 -1
- package/src/tmpclaude-3860-cwd +0 -1
- package/src/tmpclaude-4b63-cwd +0 -1
- package/src/tmpclaude-5c73-cwd +0 -1
- package/src/tmpclaude-5ee3-cwd +0 -1
- package/src/tmpclaude-6795-cwd +0 -1
- package/src/tmpclaude-709e-cwd +0 -1
- package/src/tmpclaude-9839-cwd +0 -1
- package/src/tmpclaude-d829-cwd +0 -1
- package/src/tmpclaude-e072-cwd +0 -1
- package/src/tmpclaude-f6ee-cwd +0 -1
- package/tmpclaude-0439-cwd +0 -1
- package/tmpclaude-132f-cwd +0 -1
- package/tmpclaude-15bb-cwd +0 -1
- package/tmpclaude-165a-cwd +0 -1
- package/tmpclaude-1ba9-cwd +0 -1
- package/tmpclaude-21a3-cwd +0 -1
- package/tmpclaude-2a38-cwd +0 -1
- package/tmpclaude-2adf-cwd +0 -1
- package/tmpclaude-2f56-cwd +0 -1
- package/tmpclaude-3626-cwd +0 -1
- package/tmpclaude-3727-cwd +0 -1
- package/tmpclaude-40bc-cwd +0 -1
- package/tmpclaude-436f-cwd +0 -1
- package/tmpclaude-4783-cwd +0 -1
- package/tmpclaude-4b6d-cwd +0 -1
- package/tmpclaude-4ba4-cwd +0 -1
- package/tmpclaude-51e6-cwd +0 -1
- package/tmpclaude-5ecf-cwd +0 -1
- package/tmpclaude-6f97-cwd +0 -1
- package/tmpclaude-7fb2-cwd +0 -1
- package/tmpclaude-825c-cwd +0 -1
- package/tmpclaude-8baf-cwd +0 -1
- package/tmpclaude-8d9f-cwd +0 -1
- package/tmpclaude-975c-cwd +0 -1
- package/tmpclaude-9983-cwd +0 -1
- package/tmpclaude-a045-cwd +0 -1
- package/tmpclaude-ac4a-cwd +0 -1
- package/tmpclaude-b593-cwd +0 -1
- package/tmpclaude-b891-cwd +0 -1
- package/tmpclaude-c032-cwd +0 -1
- package/tmpclaude-cf43-cwd +0 -1
- package/tmpclaude-d040-cwd +0 -1
- package/tmpclaude-dcdd-cwd +0 -1
- package/tmpclaude-dcee-cwd +0 -1
- package/tmpclaude-e16b-cwd +0 -1
- package/tmpclaude-ecd2-cwd +0 -1
- 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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
148
|
-
|
|
42
|
+
mockApiClient.claimDeploymentValidation.mockResolvedValue({
|
|
43
|
+
ok: true,
|
|
44
|
+
data: { success: false, error: 'No pending deployment found' },
|
|
149
45
|
});
|
|
150
|
-
const ctx = createMockContext(
|
|
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
|
|
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
|
|
182
|
-
const ctx = createMockContext(supabase);
|
|
183
|
-
|
|
99
|
+
const ctx = createMockContext();
|
|
184
100
|
await expect(
|
|
185
|
-
reportValidation({ project_id:
|
|
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
|
-
|
|
191
|
-
|
|
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(
|
|
110
|
+
const ctx = createMockContext();
|
|
194
111
|
|
|
195
112
|
const result = await reportValidation(
|
|
196
|
-
{ project_id:
|
|
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
|
|
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
|
-
|
|
223
|
-
|
|
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(
|
|
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
|
-
|
|
250
|
-
|
|
175
|
+
mockApiClient.checkDeploymentStatus.mockResolvedValue({
|
|
176
|
+
ok: true,
|
|
177
|
+
data: { has_deployment: true, deployment: mockDeployment },
|
|
251
178
|
});
|
|
252
|
-
const ctx = createMockContext(
|
|
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
|
|
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
|
-
|
|
282
|
-
|
|
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(
|
|
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
|
|
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
|
|
316
|
-
const ctx = createMockContext(supabase);
|
|
317
|
-
|
|
248
|
+
const ctx = createMockContext();
|
|
318
249
|
await expect(
|
|
319
|
-
completeDeployment({ project_id:
|
|
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
|
-
|
|
325
|
-
|
|
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(
|
|
259
|
+
const ctx = createMockContext();
|
|
328
260
|
|
|
329
261
|
const result = await completeDeployment(
|
|
330
|
-
{ project_id:
|
|
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
|
|
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
|
-
|
|
357
|
-
|
|
304
|
+
mockApiClient.cancelDeployment.mockResolvedValue({
|
|
305
|
+
ok: true,
|
|
306
|
+
data: { success: false, error: 'No active deployment' },
|
|
358
307
|
});
|
|
359
|
-
const ctx = createMockContext(
|
|
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: '
|
|
326
|
+
{ project_id: VALID_UUID, reason: 'Found critical bug' },
|
|
363
327
|
ctx
|
|
364
328
|
);
|
|
365
329
|
|
|
366
330
|
expect(result.result).toMatchObject({
|
|
367
|
-
success:
|
|
368
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
415
|
-
const ctx = createMockContext(supabase);
|
|
416
|
-
|
|
366
|
+
const ctx = createMockContext();
|
|
417
367
|
await expect(
|
|
418
368
|
addDeploymentRequirement({
|
|
419
|
-
project_id:
|
|
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
|
|
428
|
-
const ctx = createMockContext(supabase);
|
|
429
|
-
|
|
377
|
+
const ctx = createMockContext();
|
|
430
378
|
await expect(
|
|
431
379
|
addDeploymentRequirement({
|
|
432
|
-
project_id:
|
|
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
|
-
|
|
442
|
-
|
|
443
|
-
|
|
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(
|
|
393
|
+
const ctx = createMockContext();
|
|
448
394
|
|
|
449
395
|
const result = await addDeploymentRequirement(
|
|
450
396
|
{
|
|
451
|
-
project_id:
|
|
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
|
|
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
|
-
|
|
482
|
-
|
|
425
|
+
mockApiClient.getDeploymentRequirements.mockResolvedValue({
|
|
426
|
+
ok: true,
|
|
427
|
+
data: { requirements: [], deployment_blocked: false },
|
|
483
428
|
});
|
|
484
|
-
const ctx = createMockContext(
|
|
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).
|
|
499
|
-
|
|
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
|
-
|
|
504
|
-
|
|
440
|
+
mockApiClient.getDeploymentRequirements.mockResolvedValue({
|
|
441
|
+
ok: true,
|
|
442
|
+
data: { requirements: [], deployment_blocked: false },
|
|
505
443
|
});
|
|
506
|
-
const ctx = createMockContext(
|
|
444
|
+
const ctx = createMockContext();
|
|
507
445
|
|
|
508
|
-
await getDeploymentRequirements(
|
|
509
|
-
|
|
510
|
-
|
|
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(
|
|
514
|
-
expect(
|
|
467
|
+
expect((result.result as { requirements: unknown[] }).requirements).toHaveLength(2);
|
|
468
|
+
expect(result.result).toHaveProperty('deployment_blocked', true);
|
|
515
469
|
});
|
|
516
470
|
});
|