@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
|
-
import { describe, it, expect,
|
|
2
|
-
import type { SupabaseClient } from '@supabase/supabase-js';
|
|
3
|
-
import type { HandlerContext, TokenUsage } from './types.js';
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
4
2
|
import {
|
|
5
3
|
addMilestone,
|
|
6
4
|
updateMilestone,
|
|
@@ -9,129 +7,18 @@ import {
|
|
|
9
7
|
getMilestones,
|
|
10
8
|
} from './milestones.js';
|
|
11
9
|
import { ValidationError } from '../validators.js';
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
// Test Utilities
|
|
15
|
-
// ============================================================================
|
|
16
|
-
|
|
17
|
-
function createMockSupabase(overrides: {
|
|
18
|
-
selectResult?: { data: unknown; error: unknown };
|
|
19
|
-
insertResult?: { data: unknown; error: unknown };
|
|
20
|
-
updateResult?: { data: unknown; error: unknown };
|
|
21
|
-
deleteResult?: { data: unknown; error: unknown };
|
|
22
|
-
} = {}) {
|
|
23
|
-
const defaultResult = { data: null, error: null };
|
|
24
|
-
let currentOperation = 'select';
|
|
25
|
-
let insertThenSelect = false;
|
|
26
|
-
|
|
27
|
-
const mock = {
|
|
28
|
-
from: vi.fn().mockReturnThis(),
|
|
29
|
-
select: vi.fn(() => {
|
|
30
|
-
if (currentOperation === 'insert' || currentOperation === 'update') {
|
|
31
|
-
insertThenSelect = true;
|
|
32
|
-
} else {
|
|
33
|
-
currentOperation = 'select';
|
|
34
|
-
insertThenSelect = false;
|
|
35
|
-
}
|
|
36
|
-
return mock;
|
|
37
|
-
}),
|
|
38
|
-
insert: vi.fn(() => {
|
|
39
|
-
currentOperation = 'insert';
|
|
40
|
-
insertThenSelect = false;
|
|
41
|
-
return mock;
|
|
42
|
-
}),
|
|
43
|
-
update: vi.fn(() => {
|
|
44
|
-
currentOperation = 'update';
|
|
45
|
-
insertThenSelect = false;
|
|
46
|
-
return mock;
|
|
47
|
-
}),
|
|
48
|
-
delete: vi.fn(() => {
|
|
49
|
-
currentOperation = 'delete';
|
|
50
|
-
insertThenSelect = false;
|
|
51
|
-
return mock;
|
|
52
|
-
}),
|
|
53
|
-
eq: vi.fn().mockReturnThis(),
|
|
54
|
-
neq: vi.fn().mockReturnThis(),
|
|
55
|
-
in: vi.fn().mockReturnThis(),
|
|
56
|
-
is: vi.fn().mockReturnThis(),
|
|
57
|
-
not: vi.fn().mockReturnThis(),
|
|
58
|
-
or: vi.fn().mockReturnThis(),
|
|
59
|
-
gt: vi.fn().mockReturnThis(),
|
|
60
|
-
gte: vi.fn().mockReturnThis(),
|
|
61
|
-
lte: vi.fn().mockReturnThis(),
|
|
62
|
-
lt: vi.fn().mockReturnThis(),
|
|
63
|
-
order: vi.fn().mockReturnThis(),
|
|
64
|
-
limit: vi.fn().mockReturnThis(),
|
|
65
|
-
single: vi.fn(() => {
|
|
66
|
-
if (currentOperation === 'insert' || insertThenSelect) {
|
|
67
|
-
return Promise.resolve(overrides.insertResult ?? defaultResult);
|
|
68
|
-
}
|
|
69
|
-
if (currentOperation === 'select') {
|
|
70
|
-
return Promise.resolve(overrides.selectResult ?? defaultResult);
|
|
71
|
-
}
|
|
72
|
-
if (currentOperation === 'update') {
|
|
73
|
-
return Promise.resolve(overrides.updateResult ?? defaultResult);
|
|
74
|
-
}
|
|
75
|
-
return Promise.resolve(defaultResult);
|
|
76
|
-
}),
|
|
77
|
-
maybeSingle: vi.fn(() => {
|
|
78
|
-
return Promise.resolve(overrides.selectResult ?? defaultResult);
|
|
79
|
-
}),
|
|
80
|
-
then: vi.fn((resolve: (value: unknown) => void) => {
|
|
81
|
-
if (currentOperation === 'insert' || insertThenSelect) {
|
|
82
|
-
return Promise.resolve(overrides.insertResult ?? defaultResult).then(resolve);
|
|
83
|
-
}
|
|
84
|
-
if (currentOperation === 'select') {
|
|
85
|
-
return Promise.resolve(overrides.selectResult ?? defaultResult).then(resolve);
|
|
86
|
-
}
|
|
87
|
-
if (currentOperation === 'update') {
|
|
88
|
-
return Promise.resolve(overrides.updateResult ?? defaultResult).then(resolve);
|
|
89
|
-
}
|
|
90
|
-
if (currentOperation === 'delete') {
|
|
91
|
-
return Promise.resolve(overrides.deleteResult ?? defaultResult).then(resolve);
|
|
92
|
-
}
|
|
93
|
-
return Promise.resolve(defaultResult).then(resolve);
|
|
94
|
-
}),
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
return mock as unknown as SupabaseClient;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function createMockContext(
|
|
101
|
-
supabase: SupabaseClient,
|
|
102
|
-
options: { sessionId?: string | null } = {}
|
|
103
|
-
): HandlerContext {
|
|
104
|
-
const defaultTokenUsage: TokenUsage = {
|
|
105
|
-
callCount: 5,
|
|
106
|
-
totalTokens: 2500,
|
|
107
|
-
byTool: {},
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
const sessionId = 'sessionId' in options ? options.sessionId : 'session-123';
|
|
111
|
-
|
|
112
|
-
return {
|
|
113
|
-
supabase,
|
|
114
|
-
auth: { userId: 'user-123', apiKeyId: 'api-key-123' },
|
|
115
|
-
session: {
|
|
116
|
-
instanceId: 'instance-abc',
|
|
117
|
-
currentSessionId: sessionId,
|
|
118
|
-
currentPersona: 'Wave',
|
|
119
|
-
tokenUsage: defaultTokenUsage,
|
|
120
|
-
},
|
|
121
|
-
updateSession: vi.fn(),
|
|
122
|
-
};
|
|
123
|
-
}
|
|
10
|
+
import { createMockContext } from './__test-utils__.js';
|
|
11
|
+
import { mockApiClient } from './__test-setup__.js';
|
|
124
12
|
|
|
125
13
|
// ============================================================================
|
|
126
14
|
// addMilestone Tests
|
|
127
15
|
// ============================================================================
|
|
128
16
|
|
|
129
17
|
describe('addMilestone', () => {
|
|
130
|
-
beforeEach(() =>
|
|
18
|
+
beforeEach(() => mockApiClient.addMilestone.mockReset());
|
|
131
19
|
|
|
132
20
|
it('should throw error for missing task_id', async () => {
|
|
133
|
-
const
|
|
134
|
-
const ctx = createMockContext(supabase);
|
|
21
|
+
const ctx = createMockContext();
|
|
135
22
|
|
|
136
23
|
await expect(
|
|
137
24
|
addMilestone({ title: 'Milestone 1' }, ctx)
|
|
@@ -139,8 +26,7 @@ describe('addMilestone', () => {
|
|
|
139
26
|
});
|
|
140
27
|
|
|
141
28
|
it('should throw error for invalid task_id UUID', async () => {
|
|
142
|
-
const
|
|
143
|
-
const ctx = createMockContext(supabase);
|
|
29
|
+
const ctx = createMockContext();
|
|
144
30
|
|
|
145
31
|
await expect(
|
|
146
32
|
addMilestone({ task_id: 'invalid', title: 'Milestone 1' }, ctx)
|
|
@@ -148,42 +34,19 @@ describe('addMilestone', () => {
|
|
|
148
34
|
});
|
|
149
35
|
|
|
150
36
|
it('should throw error for missing title', async () => {
|
|
151
|
-
const
|
|
152
|
-
const ctx = createMockContext(supabase);
|
|
37
|
+
const ctx = createMockContext();
|
|
153
38
|
|
|
154
39
|
await expect(
|
|
155
40
|
addMilestone({ task_id: '123e4567-e89b-12d3-a456-426614174000' }, ctx)
|
|
156
41
|
).rejects.toThrow(ValidationError);
|
|
157
42
|
});
|
|
158
43
|
|
|
159
|
-
it('should throw error when task not found', async () => {
|
|
160
|
-
const supabase = createMockSupabase({
|
|
161
|
-
selectResult: { data: null, error: { message: 'Not found' } },
|
|
162
|
-
});
|
|
163
|
-
const ctx = createMockContext(supabase);
|
|
164
|
-
|
|
165
|
-
await expect(
|
|
166
|
-
addMilestone({
|
|
167
|
-
task_id: '123e4567-e89b-12d3-a456-426614174000',
|
|
168
|
-
title: 'Milestone 1',
|
|
169
|
-
}, ctx)
|
|
170
|
-
).rejects.toThrow('Task not found');
|
|
171
|
-
});
|
|
172
|
-
|
|
173
44
|
it('should add milestone successfully', async () => {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
title: 'First Milestone',
|
|
178
|
-
order_index: 0,
|
|
179
|
-
status: 'pending',
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
const supabase = createMockSupabase({
|
|
183
|
-
selectResult: { data: { id: 'task-1', project_id: 'proj-1' }, error: null },
|
|
184
|
-
insertResult: { data: mockMilestone, error: null },
|
|
45
|
+
mockApiClient.addMilestone.mockResolvedValue({
|
|
46
|
+
ok: true,
|
|
47
|
+
data: { milestone_id: 'milestone-1' },
|
|
185
48
|
});
|
|
186
|
-
const ctx = createMockContext(
|
|
49
|
+
const ctx = createMockContext();
|
|
187
50
|
|
|
188
51
|
const result = await addMilestone(
|
|
189
52
|
{
|
|
@@ -195,54 +58,51 @@ describe('addMilestone', () => {
|
|
|
195
58
|
|
|
196
59
|
expect(result.result).toMatchObject({
|
|
197
60
|
success: true,
|
|
198
|
-
|
|
61
|
+
milestone_id: 'milestone-1',
|
|
199
62
|
});
|
|
200
63
|
});
|
|
201
64
|
|
|
202
|
-
it('should
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
65
|
+
it('should call API client with correct parameters', async () => {
|
|
66
|
+
mockApiClient.addMilestone.mockResolvedValue({
|
|
67
|
+
ok: true,
|
|
68
|
+
data: { milestone_id: 'milestone-1' },
|
|
206
69
|
});
|
|
207
|
-
const ctx = createMockContext(
|
|
70
|
+
const ctx = createMockContext();
|
|
208
71
|
|
|
209
72
|
await addMilestone(
|
|
210
73
|
{
|
|
211
74
|
task_id: '123e4567-e89b-12d3-a456-426614174000',
|
|
212
75
|
title: 'Test Milestone',
|
|
76
|
+
description: 'A test milestone',
|
|
213
77
|
order_index: 5,
|
|
214
78
|
},
|
|
215
79
|
ctx
|
|
216
80
|
);
|
|
217
81
|
|
|
218
|
-
expect(
|
|
219
|
-
|
|
82
|
+
expect(mockApiClient.addMilestone).toHaveBeenCalledWith(
|
|
83
|
+
'123e4567-e89b-12d3-a456-426614174000',
|
|
84
|
+
{
|
|
85
|
+
title: 'Test Milestone',
|
|
86
|
+
description: 'A test milestone',
|
|
220
87
|
order_index: 5,
|
|
221
|
-
}
|
|
88
|
+
},
|
|
89
|
+
'session-123'
|
|
222
90
|
);
|
|
223
91
|
});
|
|
224
92
|
|
|
225
|
-
it('should
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
93
|
+
it('should throw error when API call fails', async () => {
|
|
94
|
+
mockApiClient.addMilestone.mockResolvedValue({
|
|
95
|
+
ok: false,
|
|
96
|
+
error: 'Task not found',
|
|
229
97
|
});
|
|
230
|
-
const ctx = createMockContext(
|
|
98
|
+
const ctx = createMockContext();
|
|
231
99
|
|
|
232
|
-
await
|
|
233
|
-
{
|
|
100
|
+
await expect(
|
|
101
|
+
addMilestone({
|
|
234
102
|
task_id: '123e4567-e89b-12d3-a456-426614174000',
|
|
235
|
-
title: '
|
|
236
|
-
},
|
|
237
|
-
|
|
238
|
-
);
|
|
239
|
-
|
|
240
|
-
expect(supabase.insert).toHaveBeenCalledWith(
|
|
241
|
-
expect.objectContaining({
|
|
242
|
-
created_by: 'agent',
|
|
243
|
-
created_by_session_id: 'my-session',
|
|
244
|
-
})
|
|
245
|
-
);
|
|
103
|
+
title: 'Milestone 1',
|
|
104
|
+
}, ctx)
|
|
105
|
+
).rejects.toThrow('Failed to add milestone: Task not found');
|
|
246
106
|
});
|
|
247
107
|
});
|
|
248
108
|
|
|
@@ -251,18 +111,16 @@ describe('addMilestone', () => {
|
|
|
251
111
|
// ============================================================================
|
|
252
112
|
|
|
253
113
|
describe('updateMilestone', () => {
|
|
254
|
-
beforeEach(() =>
|
|
114
|
+
beforeEach(() => mockApiClient.updateMilestone.mockReset());
|
|
255
115
|
|
|
256
116
|
it('should throw error for missing milestone_id', async () => {
|
|
257
|
-
const
|
|
258
|
-
const ctx = createMockContext(supabase);
|
|
117
|
+
const ctx = createMockContext();
|
|
259
118
|
|
|
260
119
|
await expect(updateMilestone({}, ctx)).rejects.toThrow(ValidationError);
|
|
261
120
|
});
|
|
262
121
|
|
|
263
122
|
it('should throw error for invalid milestone_id UUID', async () => {
|
|
264
|
-
const
|
|
265
|
-
const ctx = createMockContext(supabase);
|
|
123
|
+
const ctx = createMockContext();
|
|
266
124
|
|
|
267
125
|
await expect(
|
|
268
126
|
updateMilestone({ milestone_id: 'invalid', title: 'New Title' }, ctx)
|
|
@@ -270,8 +128,7 @@ describe('updateMilestone', () => {
|
|
|
270
128
|
});
|
|
271
129
|
|
|
272
130
|
it('should throw error when no fields to update', async () => {
|
|
273
|
-
const
|
|
274
|
-
const ctx = createMockContext(supabase);
|
|
131
|
+
const ctx = createMockContext();
|
|
275
132
|
|
|
276
133
|
await expect(
|
|
277
134
|
updateMilestone({ milestone_id: '123e4567-e89b-12d3-a456-426614174000' }, ctx)
|
|
@@ -279,8 +136,7 @@ describe('updateMilestone', () => {
|
|
|
279
136
|
});
|
|
280
137
|
|
|
281
138
|
it('should throw error for invalid status', async () => {
|
|
282
|
-
const
|
|
283
|
-
const ctx = createMockContext(supabase);
|
|
139
|
+
const ctx = createMockContext();
|
|
284
140
|
|
|
285
141
|
await expect(
|
|
286
142
|
updateMilestone({
|
|
@@ -291,10 +147,11 @@ describe('updateMilestone', () => {
|
|
|
291
147
|
});
|
|
292
148
|
|
|
293
149
|
it('should update title successfully', async () => {
|
|
294
|
-
|
|
295
|
-
|
|
150
|
+
mockApiClient.updateMilestone.mockResolvedValue({
|
|
151
|
+
ok: true,
|
|
152
|
+
data: { milestone: { id: 'milestone-1', title: 'Updated Title' } },
|
|
296
153
|
});
|
|
297
|
-
const ctx = createMockContext(
|
|
154
|
+
const ctx = createMockContext();
|
|
298
155
|
|
|
299
156
|
const result = await updateMilestone(
|
|
300
157
|
{
|
|
@@ -309,11 +166,12 @@ describe('updateMilestone', () => {
|
|
|
309
166
|
});
|
|
310
167
|
});
|
|
311
168
|
|
|
312
|
-
it('should
|
|
313
|
-
|
|
314
|
-
|
|
169
|
+
it('should call API client with correct parameters', async () => {
|
|
170
|
+
mockApiClient.updateMilestone.mockResolvedValue({
|
|
171
|
+
ok: true,
|
|
172
|
+
data: { milestone: { id: 'milestone-1', status: 'completed' } },
|
|
315
173
|
});
|
|
316
|
-
const ctx = createMockContext(
|
|
174
|
+
const ctx = createMockContext();
|
|
317
175
|
|
|
318
176
|
await updateMilestone(
|
|
319
177
|
{
|
|
@@ -323,34 +181,30 @@ describe('updateMilestone', () => {
|
|
|
323
181
|
ctx
|
|
324
182
|
);
|
|
325
183
|
|
|
326
|
-
expect(
|
|
327
|
-
|
|
184
|
+
expect(mockApiClient.updateMilestone).toHaveBeenCalledWith(
|
|
185
|
+
'123e4567-e89b-12d3-a456-426614174000',
|
|
186
|
+
{
|
|
187
|
+
title: undefined,
|
|
188
|
+
description: undefined,
|
|
328
189
|
status: 'completed',
|
|
329
|
-
|
|
330
|
-
}
|
|
190
|
+
order_index: undefined,
|
|
191
|
+
}
|
|
331
192
|
);
|
|
332
193
|
});
|
|
333
194
|
|
|
334
|
-
it('should
|
|
335
|
-
|
|
336
|
-
|
|
195
|
+
it('should throw error when API call fails', async () => {
|
|
196
|
+
mockApiClient.updateMilestone.mockResolvedValue({
|
|
197
|
+
ok: false,
|
|
198
|
+
error: 'Milestone not found',
|
|
337
199
|
});
|
|
338
|
-
const ctx = createMockContext(
|
|
200
|
+
const ctx = createMockContext();
|
|
339
201
|
|
|
340
|
-
await
|
|
341
|
-
{
|
|
202
|
+
await expect(
|
|
203
|
+
updateMilestone({
|
|
342
204
|
milestone_id: '123e4567-e89b-12d3-a456-426614174000',
|
|
343
|
-
|
|
344
|
-
},
|
|
345
|
-
|
|
346
|
-
);
|
|
347
|
-
|
|
348
|
-
expect(supabase.update).toHaveBeenCalledWith(
|
|
349
|
-
expect.objectContaining({
|
|
350
|
-
status: 'in_progress',
|
|
351
|
-
completed_at: null,
|
|
352
|
-
})
|
|
353
|
-
);
|
|
205
|
+
title: 'New Title',
|
|
206
|
+
}, ctx)
|
|
207
|
+
).rejects.toThrow('Failed to update milestone: Milestone not found');
|
|
354
208
|
});
|
|
355
209
|
});
|
|
356
210
|
|
|
@@ -359,18 +213,16 @@ describe('updateMilestone', () => {
|
|
|
359
213
|
// ============================================================================
|
|
360
214
|
|
|
361
215
|
describe('completeMilestone', () => {
|
|
362
|
-
beforeEach(() =>
|
|
216
|
+
beforeEach(() => mockApiClient.completeMilestone.mockReset());
|
|
363
217
|
|
|
364
218
|
it('should throw error for missing milestone_id', async () => {
|
|
365
|
-
const
|
|
366
|
-
const ctx = createMockContext(supabase);
|
|
219
|
+
const ctx = createMockContext();
|
|
367
220
|
|
|
368
221
|
await expect(completeMilestone({}, ctx)).rejects.toThrow(ValidationError);
|
|
369
222
|
});
|
|
370
223
|
|
|
371
224
|
it('should throw error for invalid milestone_id UUID', async () => {
|
|
372
|
-
const
|
|
373
|
-
const ctx = createMockContext(supabase);
|
|
225
|
+
const ctx = createMockContext();
|
|
374
226
|
|
|
375
227
|
await expect(
|
|
376
228
|
completeMilestone({ milestone_id: 'invalid' }, ctx)
|
|
@@ -378,13 +230,11 @@ describe('completeMilestone', () => {
|
|
|
378
230
|
});
|
|
379
231
|
|
|
380
232
|
it('should complete milestone successfully', async () => {
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
error: null,
|
|
385
|
-
},
|
|
233
|
+
mockApiClient.completeMilestone.mockResolvedValue({
|
|
234
|
+
ok: true,
|
|
235
|
+
data: { milestone: { id: 'milestone-1', status: 'completed' } },
|
|
386
236
|
});
|
|
387
|
-
const ctx = createMockContext(
|
|
237
|
+
const ctx = createMockContext();
|
|
388
238
|
|
|
389
239
|
const result = await completeMilestone(
|
|
390
240
|
{ milestone_id: '123e4567-e89b-12d3-a456-426614174000' },
|
|
@@ -396,24 +246,34 @@ describe('completeMilestone', () => {
|
|
|
396
246
|
});
|
|
397
247
|
});
|
|
398
248
|
|
|
399
|
-
it('should
|
|
400
|
-
|
|
401
|
-
|
|
249
|
+
it('should call API client completeMilestone', async () => {
|
|
250
|
+
mockApiClient.completeMilestone.mockResolvedValue({
|
|
251
|
+
ok: true,
|
|
252
|
+
data: { milestone: { id: 'milestone-1' } },
|
|
402
253
|
});
|
|
403
|
-
const ctx = createMockContext(
|
|
254
|
+
const ctx = createMockContext();
|
|
404
255
|
|
|
405
256
|
await completeMilestone(
|
|
406
257
|
{ milestone_id: '123e4567-e89b-12d3-a456-426614174000' },
|
|
407
258
|
ctx
|
|
408
259
|
);
|
|
409
260
|
|
|
410
|
-
expect(
|
|
411
|
-
|
|
412
|
-
status: 'completed',
|
|
413
|
-
completed_at: expect.any(String),
|
|
414
|
-
})
|
|
261
|
+
expect(mockApiClient.completeMilestone).toHaveBeenCalledWith(
|
|
262
|
+
'123e4567-e89b-12d3-a456-426614174000'
|
|
415
263
|
);
|
|
416
264
|
});
|
|
265
|
+
|
|
266
|
+
it('should throw error when API call fails', async () => {
|
|
267
|
+
mockApiClient.completeMilestone.mockResolvedValue({
|
|
268
|
+
ok: false,
|
|
269
|
+
error: 'Milestone not found',
|
|
270
|
+
});
|
|
271
|
+
const ctx = createMockContext();
|
|
272
|
+
|
|
273
|
+
await expect(
|
|
274
|
+
completeMilestone({ milestone_id: '123e4567-e89b-12d3-a456-426614174000' }, ctx)
|
|
275
|
+
).rejects.toThrow('Failed to complete milestone: Milestone not found');
|
|
276
|
+
});
|
|
417
277
|
});
|
|
418
278
|
|
|
419
279
|
// ============================================================================
|
|
@@ -421,18 +281,16 @@ describe('completeMilestone', () => {
|
|
|
421
281
|
// ============================================================================
|
|
422
282
|
|
|
423
283
|
describe('deleteMilestone', () => {
|
|
424
|
-
beforeEach(() =>
|
|
284
|
+
beforeEach(() => mockApiClient.deleteMilestone.mockReset());
|
|
425
285
|
|
|
426
286
|
it('should throw error for missing milestone_id', async () => {
|
|
427
|
-
const
|
|
428
|
-
const ctx = createMockContext(supabase);
|
|
287
|
+
const ctx = createMockContext();
|
|
429
288
|
|
|
430
289
|
await expect(deleteMilestone({}, ctx)).rejects.toThrow(ValidationError);
|
|
431
290
|
});
|
|
432
291
|
|
|
433
292
|
it('should throw error for invalid milestone_id UUID', async () => {
|
|
434
|
-
const
|
|
435
|
-
const ctx = createMockContext(supabase);
|
|
293
|
+
const ctx = createMockContext();
|
|
436
294
|
|
|
437
295
|
await expect(
|
|
438
296
|
deleteMilestone({ milestone_id: 'invalid' }, ctx)
|
|
@@ -440,10 +298,11 @@ describe('deleteMilestone', () => {
|
|
|
440
298
|
});
|
|
441
299
|
|
|
442
300
|
it('should delete milestone successfully', async () => {
|
|
443
|
-
|
|
444
|
-
|
|
301
|
+
mockApiClient.deleteMilestone.mockResolvedValue({
|
|
302
|
+
ok: true,
|
|
303
|
+
data: { success: true },
|
|
445
304
|
});
|
|
446
|
-
const ctx = createMockContext(
|
|
305
|
+
const ctx = createMockContext();
|
|
447
306
|
|
|
448
307
|
const result = await deleteMilestone(
|
|
449
308
|
{ milestone_id: '123e4567-e89b-12d3-a456-426614174000' },
|
|
@@ -456,19 +315,33 @@ describe('deleteMilestone', () => {
|
|
|
456
315
|
});
|
|
457
316
|
});
|
|
458
317
|
|
|
459
|
-
it('should call
|
|
460
|
-
|
|
461
|
-
|
|
318
|
+
it('should call API client deleteMilestone', async () => {
|
|
319
|
+
mockApiClient.deleteMilestone.mockResolvedValue({
|
|
320
|
+
ok: true,
|
|
321
|
+
data: { success: true },
|
|
462
322
|
});
|
|
463
|
-
const ctx = createMockContext(
|
|
323
|
+
const ctx = createMockContext();
|
|
464
324
|
|
|
465
325
|
await deleteMilestone(
|
|
466
326
|
{ milestone_id: '123e4567-e89b-12d3-a456-426614174000' },
|
|
467
327
|
ctx
|
|
468
328
|
);
|
|
469
329
|
|
|
470
|
-
expect(
|
|
471
|
-
|
|
330
|
+
expect(mockApiClient.deleteMilestone).toHaveBeenCalledWith(
|
|
331
|
+
'123e4567-e89b-12d3-a456-426614174000'
|
|
332
|
+
);
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
it('should throw error when API call fails', async () => {
|
|
336
|
+
mockApiClient.deleteMilestone.mockResolvedValue({
|
|
337
|
+
ok: false,
|
|
338
|
+
error: 'Milestone not found',
|
|
339
|
+
});
|
|
340
|
+
const ctx = createMockContext();
|
|
341
|
+
|
|
342
|
+
await expect(
|
|
343
|
+
deleteMilestone({ milestone_id: '123e4567-e89b-12d3-a456-426614174000' }, ctx)
|
|
344
|
+
).rejects.toThrow('Failed to delete milestone: Milestone not found');
|
|
472
345
|
});
|
|
473
346
|
});
|
|
474
347
|
|
|
@@ -477,18 +350,16 @@ describe('deleteMilestone', () => {
|
|
|
477
350
|
// ============================================================================
|
|
478
351
|
|
|
479
352
|
describe('getMilestones', () => {
|
|
480
|
-
beforeEach(() =>
|
|
353
|
+
beforeEach(() => mockApiClient.getMilestones.mockReset());
|
|
481
354
|
|
|
482
355
|
it('should throw error for missing task_id', async () => {
|
|
483
|
-
const
|
|
484
|
-
const ctx = createMockContext(supabase);
|
|
356
|
+
const ctx = createMockContext();
|
|
485
357
|
|
|
486
358
|
await expect(getMilestones({}, ctx)).rejects.toThrow(ValidationError);
|
|
487
359
|
});
|
|
488
360
|
|
|
489
361
|
it('should throw error for invalid task_id UUID', async () => {
|
|
490
|
-
const
|
|
491
|
-
const ctx = createMockContext(supabase);
|
|
362
|
+
const ctx = createMockContext();
|
|
492
363
|
|
|
493
364
|
await expect(
|
|
494
365
|
getMilestones({ task_id: 'invalid' }, ctx)
|
|
@@ -496,10 +367,20 @@ describe('getMilestones', () => {
|
|
|
496
367
|
});
|
|
497
368
|
|
|
498
369
|
it('should return empty list with zero stats', async () => {
|
|
499
|
-
|
|
500
|
-
|
|
370
|
+
mockApiClient.getMilestones.mockResolvedValue({
|
|
371
|
+
ok: true,
|
|
372
|
+
data: {
|
|
373
|
+
milestones: [],
|
|
374
|
+
stats: {
|
|
375
|
+
total: 0,
|
|
376
|
+
completed: 0,
|
|
377
|
+
in_progress: 0,
|
|
378
|
+
pending: 0,
|
|
379
|
+
progress_percentage: 0,
|
|
380
|
+
},
|
|
381
|
+
},
|
|
501
382
|
});
|
|
502
|
-
const ctx = createMockContext(
|
|
383
|
+
const ctx = createMockContext();
|
|
503
384
|
|
|
504
385
|
const result = await getMilestones(
|
|
505
386
|
{ task_id: '123e4567-e89b-12d3-a456-426614174000' },
|
|
@@ -518,7 +399,7 @@ describe('getMilestones', () => {
|
|
|
518
399
|
});
|
|
519
400
|
});
|
|
520
401
|
|
|
521
|
-
it('should return milestones with stats', async () => {
|
|
402
|
+
it('should return milestones with stats from API', async () => {
|
|
522
403
|
const mockMilestones = [
|
|
523
404
|
{ id: 'm1', title: 'Step 1', status: 'completed', order_index: 0 },
|
|
524
405
|
{ id: 'm2', title: 'Step 2', status: 'in_progress', order_index: 1 },
|
|
@@ -526,10 +407,20 @@ describe('getMilestones', () => {
|
|
|
526
407
|
{ id: 'm4', title: 'Step 4', status: 'pending', order_index: 3 },
|
|
527
408
|
];
|
|
528
409
|
|
|
529
|
-
|
|
530
|
-
|
|
410
|
+
mockApiClient.getMilestones.mockResolvedValue({
|
|
411
|
+
ok: true,
|
|
412
|
+
data: {
|
|
413
|
+
milestones: mockMilestones,
|
|
414
|
+
stats: {
|
|
415
|
+
total: 4,
|
|
416
|
+
completed: 1,
|
|
417
|
+
in_progress: 1,
|
|
418
|
+
pending: 2,
|
|
419
|
+
progress_percentage: 25,
|
|
420
|
+
},
|
|
421
|
+
},
|
|
531
422
|
});
|
|
532
|
-
const ctx = createMockContext(
|
|
423
|
+
const ctx = createMockContext();
|
|
533
424
|
|
|
534
425
|
const result = await getMilestones(
|
|
535
426
|
{ task_id: '123e4567-e89b-12d3-a456-426614174000' },
|
|
@@ -543,42 +434,37 @@ describe('getMilestones', () => {
|
|
|
543
434
|
completed: 1,
|
|
544
435
|
in_progress: 1,
|
|
545
436
|
pending: 2,
|
|
546
|
-
progress_percentage: 25,
|
|
437
|
+
progress_percentage: 25,
|
|
547
438
|
},
|
|
548
439
|
});
|
|
549
440
|
});
|
|
550
441
|
|
|
551
|
-
it('should
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
{
|
|
555
|
-
];
|
|
556
|
-
|
|
557
|
-
const supabase = createMockSupabase({
|
|
558
|
-
selectResult: { data: mockMilestones, error: null },
|
|
442
|
+
it('should call API client getMilestones', async () => {
|
|
443
|
+
mockApiClient.getMilestones.mockResolvedValue({
|
|
444
|
+
ok: true,
|
|
445
|
+
data: { milestones: [], stats: {} },
|
|
559
446
|
});
|
|
560
|
-
const ctx = createMockContext(
|
|
447
|
+
const ctx = createMockContext();
|
|
561
448
|
|
|
562
|
-
|
|
449
|
+
await getMilestones(
|
|
563
450
|
{ task_id: '123e4567-e89b-12d3-a456-426614174000' },
|
|
564
451
|
ctx
|
|
565
452
|
);
|
|
566
453
|
|
|
567
|
-
expect(
|
|
454
|
+
expect(mockApiClient.getMilestones).toHaveBeenCalledWith(
|
|
455
|
+
'123e4567-e89b-12d3-a456-426614174000'
|
|
456
|
+
);
|
|
568
457
|
});
|
|
569
458
|
|
|
570
|
-
it('should
|
|
571
|
-
|
|
572
|
-
|
|
459
|
+
it('should throw error when API call fails', async () => {
|
|
460
|
+
mockApiClient.getMilestones.mockResolvedValue({
|
|
461
|
+
ok: false,
|
|
462
|
+
error: 'Task not found',
|
|
573
463
|
});
|
|
574
|
-
const ctx = createMockContext(
|
|
464
|
+
const ctx = createMockContext();
|
|
575
465
|
|
|
576
|
-
await
|
|
577
|
-
{ task_id: '123e4567-e89b-12d3-a456-426614174000' },
|
|
578
|
-
|
|
579
|
-
);
|
|
580
|
-
|
|
581
|
-
expect(supabase.from).toHaveBeenCalledWith('task_milestones');
|
|
582
|
-
expect(supabase.order).toHaveBeenCalledWith('order_index', { ascending: true });
|
|
466
|
+
await expect(
|
|
467
|
+
getMilestones({ task_id: '123e4567-e89b-12d3-a456-426614174000' }, ctx)
|
|
468
|
+
).rejects.toThrow('Failed to get milestones: Task not found');
|
|
583
469
|
});
|
|
584
470
|
});
|