@vibescope/mcp-server 0.0.1
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 +98 -0
- package/dist/cli.d.ts +34 -0
- package/dist/cli.js +356 -0
- package/dist/cli.test.d.ts +1 -0
- package/dist/cli.test.js +367 -0
- package/dist/handlers/__test-utils__.d.ts +72 -0
- package/dist/handlers/__test-utils__.js +176 -0
- package/dist/handlers/blockers.d.ts +18 -0
- package/dist/handlers/blockers.js +81 -0
- package/dist/handlers/bodies-of-work.d.ts +34 -0
- package/dist/handlers/bodies-of-work.js +614 -0
- package/dist/handlers/checkouts.d.ts +37 -0
- package/dist/handlers/checkouts.js +377 -0
- package/dist/handlers/cost.d.ts +39 -0
- package/dist/handlers/cost.js +247 -0
- package/dist/handlers/decisions.d.ts +16 -0
- package/dist/handlers/decisions.js +64 -0
- package/dist/handlers/deployment.d.ts +36 -0
- package/dist/handlers/deployment.js +1062 -0
- package/dist/handlers/discovery.d.ts +14 -0
- package/dist/handlers/discovery.js +870 -0
- package/dist/handlers/fallback.d.ts +18 -0
- package/dist/handlers/fallback.js +216 -0
- package/dist/handlers/findings.d.ts +18 -0
- package/dist/handlers/findings.js +110 -0
- package/dist/handlers/git-issues.d.ts +22 -0
- package/dist/handlers/git-issues.js +247 -0
- package/dist/handlers/ideas.d.ts +19 -0
- package/dist/handlers/ideas.js +188 -0
- package/dist/handlers/index.d.ts +29 -0
- package/dist/handlers/index.js +65 -0
- package/dist/handlers/knowledge-query.d.ts +22 -0
- package/dist/handlers/knowledge-query.js +253 -0
- package/dist/handlers/knowledge.d.ts +12 -0
- package/dist/handlers/knowledge.js +108 -0
- package/dist/handlers/milestones.d.ts +20 -0
- package/dist/handlers/milestones.js +179 -0
- package/dist/handlers/organizations.d.ts +36 -0
- package/dist/handlers/organizations.js +428 -0
- package/dist/handlers/progress.d.ts +14 -0
- package/dist/handlers/progress.js +149 -0
- package/dist/handlers/project.d.ts +20 -0
- package/dist/handlers/project.js +278 -0
- package/dist/handlers/requests.d.ts +16 -0
- package/dist/handlers/requests.js +131 -0
- package/dist/handlers/roles.d.ts +30 -0
- package/dist/handlers/roles.js +281 -0
- package/dist/handlers/session.d.ts +20 -0
- package/dist/handlers/session.js +791 -0
- package/dist/handlers/tasks.d.ts +52 -0
- package/dist/handlers/tasks.js +1111 -0
- package/dist/handlers/tasks.test.d.ts +1 -0
- package/dist/handlers/tasks.test.js +431 -0
- package/dist/handlers/types.d.ts +94 -0
- package/dist/handlers/types.js +1 -0
- package/dist/handlers/validation.d.ts +16 -0
- package/dist/handlers/validation.js +188 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2707 -0
- package/dist/knowledge.d.ts +6 -0
- package/dist/knowledge.js +121 -0
- package/dist/tools.d.ts +2 -0
- package/dist/tools.js +2498 -0
- package/dist/utils.d.ts +149 -0
- package/dist/utils.js +317 -0
- package/dist/utils.test.d.ts +1 -0
- package/dist/utils.test.js +532 -0
- package/dist/validators.d.ts +35 -0
- package/dist/validators.js +111 -0
- package/dist/validators.test.d.ts +1 -0
- package/dist/validators.test.js +176 -0
- package/package.json +44 -0
- package/src/cli.test.ts +442 -0
- package/src/cli.ts +439 -0
- package/src/handlers/__test-utils__.ts +217 -0
- package/src/handlers/blockers.test.ts +390 -0
- package/src/handlers/blockers.ts +110 -0
- package/src/handlers/bodies-of-work.test.ts +1276 -0
- package/src/handlers/bodies-of-work.ts +783 -0
- package/src/handlers/cost.test.ts +436 -0
- package/src/handlers/cost.ts +322 -0
- package/src/handlers/decisions.test.ts +401 -0
- package/src/handlers/decisions.ts +86 -0
- package/src/handlers/deployment.test.ts +516 -0
- package/src/handlers/deployment.ts +1289 -0
- package/src/handlers/discovery.test.ts +254 -0
- package/src/handlers/discovery.ts +969 -0
- package/src/handlers/fallback.test.ts +687 -0
- package/src/handlers/fallback.ts +260 -0
- package/src/handlers/findings.test.ts +565 -0
- package/src/handlers/findings.ts +153 -0
- package/src/handlers/ideas.test.ts +753 -0
- package/src/handlers/ideas.ts +247 -0
- package/src/handlers/index.ts +69 -0
- package/src/handlers/milestones.test.ts +584 -0
- package/src/handlers/milestones.ts +217 -0
- package/src/handlers/organizations.test.ts +997 -0
- package/src/handlers/organizations.ts +550 -0
- package/src/handlers/progress.test.ts +369 -0
- package/src/handlers/progress.ts +188 -0
- package/src/handlers/project.test.ts +562 -0
- package/src/handlers/project.ts +352 -0
- package/src/handlers/requests.test.ts +531 -0
- package/src/handlers/requests.ts +150 -0
- package/src/handlers/session.test.ts +459 -0
- package/src/handlers/session.ts +912 -0
- package/src/handlers/tasks.test.ts +602 -0
- package/src/handlers/tasks.ts +1393 -0
- package/src/handlers/types.ts +88 -0
- package/src/handlers/validation.test.ts +880 -0
- package/src/handlers/validation.ts +223 -0
- package/src/index.ts +3205 -0
- package/src/knowledge.ts +132 -0
- package/src/tmpclaude-0078-cwd +1 -0
- package/src/tmpclaude-0ee1-cwd +1 -0
- package/src/tmpclaude-2dd5-cwd +1 -0
- package/src/tmpclaude-344c-cwd +1 -0
- package/src/tmpclaude-3860-cwd +1 -0
- package/src/tmpclaude-4b63-cwd +1 -0
- package/src/tmpclaude-5c73-cwd +1 -0
- package/src/tmpclaude-5ee3-cwd +1 -0
- package/src/tmpclaude-6795-cwd +1 -0
- package/src/tmpclaude-709e-cwd +1 -0
- package/src/tmpclaude-9839-cwd +1 -0
- package/src/tmpclaude-d829-cwd +1 -0
- package/src/tmpclaude-e072-cwd +1 -0
- package/src/tmpclaude-f6ee-cwd +1 -0
- package/src/utils.test.ts +681 -0
- package/src/utils.ts +375 -0
- package/src/validators.test.ts +223 -0
- package/src/validators.ts +122 -0
- package/tmpclaude-0439-cwd +1 -0
- package/tmpclaude-132f-cwd +1 -0
- package/tmpclaude-15bb-cwd +1 -0
- package/tmpclaude-165a-cwd +1 -0
- package/tmpclaude-1ba9-cwd +1 -0
- package/tmpclaude-21a3-cwd +1 -0
- package/tmpclaude-2a38-cwd +1 -0
- package/tmpclaude-2adf-cwd +1 -0
- package/tmpclaude-2f56-cwd +1 -0
- package/tmpclaude-3626-cwd +1 -0
- package/tmpclaude-3727-cwd +1 -0
- package/tmpclaude-40bc-cwd +1 -0
- package/tmpclaude-436f-cwd +1 -0
- package/tmpclaude-4783-cwd +1 -0
- package/tmpclaude-4b6d-cwd +1 -0
- package/tmpclaude-4ba4-cwd +1 -0
- package/tmpclaude-51e6-cwd +1 -0
- package/tmpclaude-5ecf-cwd +1 -0
- package/tmpclaude-6f97-cwd +1 -0
- package/tmpclaude-7fb2-cwd +1 -0
- package/tmpclaude-825c-cwd +1 -0
- package/tmpclaude-8baf-cwd +1 -0
- package/tmpclaude-8d9f-cwd +1 -0
- package/tmpclaude-975c-cwd +1 -0
- package/tmpclaude-9983-cwd +1 -0
- package/tmpclaude-a045-cwd +1 -0
- package/tmpclaude-ac4a-cwd +1 -0
- package/tmpclaude-b593-cwd +1 -0
- package/tmpclaude-b891-cwd +1 -0
- package/tmpclaude-c032-cwd +1 -0
- package/tmpclaude-cf43-cwd +1 -0
- package/tmpclaude-d040-cwd +1 -0
- package/tmpclaude-dcdd-cwd +1 -0
- package/tmpclaude-dcee-cwd +1 -0
- package/tmpclaude-e16b-cwd +1 -0
- package/tmpclaude-ecd2-cwd +1 -0
- package/tmpclaude-f48d-cwd +1 -0
- package/tsconfig.json +16 -0
- package/vitest.config.ts +13 -0
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cost Handlers Unit Tests
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
6
|
+
import {
|
|
7
|
+
getCostSummary,
|
|
8
|
+
getCostAlerts,
|
|
9
|
+
addCostAlert,
|
|
10
|
+
updateCostAlert,
|
|
11
|
+
deleteCostAlert,
|
|
12
|
+
getTaskCosts,
|
|
13
|
+
} from './cost.js';
|
|
14
|
+
import { createMockSupabase, createMockContext, testUUID } from './__test-utils__.js';
|
|
15
|
+
|
|
16
|
+
describe('Cost Handlers', () => {
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
vi.clearAllMocks();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// ============================================================================
|
|
22
|
+
// getCostSummary
|
|
23
|
+
// ============================================================================
|
|
24
|
+
|
|
25
|
+
describe('getCostSummary', () => {
|
|
26
|
+
it('should return error when project_id is missing', async () => {
|
|
27
|
+
const supabase = createMockSupabase();
|
|
28
|
+
const ctx = createMockContext(supabase);
|
|
29
|
+
|
|
30
|
+
const result = await getCostSummary({}, ctx);
|
|
31
|
+
|
|
32
|
+
expect(result.isError).toBe(true);
|
|
33
|
+
expect(result.result).toEqual({ error: 'project_id is required' });
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should return daily cost summary with totals', async () => {
|
|
37
|
+
const mockData = [
|
|
38
|
+
{
|
|
39
|
+
date: '2025-01-14',
|
|
40
|
+
session_count: 5,
|
|
41
|
+
total_tokens: 10000,
|
|
42
|
+
total_calls: 50,
|
|
43
|
+
estimated_cost_usd: '0.50',
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
date: '2025-01-13',
|
|
47
|
+
session_count: 3,
|
|
48
|
+
total_tokens: 6000,
|
|
49
|
+
total_calls: 30,
|
|
50
|
+
estimated_cost_usd: '0.30',
|
|
51
|
+
},
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
const supabase = createMockSupabase({
|
|
55
|
+
selectResult: { data: mockData, error: null },
|
|
56
|
+
});
|
|
57
|
+
const ctx = createMockContext(supabase);
|
|
58
|
+
|
|
59
|
+
const result = await getCostSummary({ project_id: testUUID() }, ctx);
|
|
60
|
+
|
|
61
|
+
expect(result.isError).toBeUndefined();
|
|
62
|
+
expect(result.result.period).toBe('daily');
|
|
63
|
+
expect(result.result.summary).toEqual(mockData);
|
|
64
|
+
expect(result.result.totals).toEqual({
|
|
65
|
+
sessions: 8,
|
|
66
|
+
tokens: 16000,
|
|
67
|
+
calls: 80,
|
|
68
|
+
cost: 0.8,
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should handle weekly period', async () => {
|
|
73
|
+
const supabase = createMockSupabase({
|
|
74
|
+
selectResult: { data: [], error: null },
|
|
75
|
+
});
|
|
76
|
+
const ctx = createMockContext(supabase);
|
|
77
|
+
|
|
78
|
+
const result = await getCostSummary(
|
|
79
|
+
{ project_id: testUUID(), period: 'weekly' },
|
|
80
|
+
ctx
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
expect(result.result.period).toBe('weekly');
|
|
84
|
+
expect(supabase.from).toHaveBeenCalledWith('weekly_cost_summary');
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('should handle monthly period', async () => {
|
|
88
|
+
const supabase = createMockSupabase({
|
|
89
|
+
selectResult: { data: [], error: null },
|
|
90
|
+
});
|
|
91
|
+
const ctx = createMockContext(supabase);
|
|
92
|
+
|
|
93
|
+
const result = await getCostSummary(
|
|
94
|
+
{ project_id: testUUID(), period: 'monthly' },
|
|
95
|
+
ctx
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
expect(result.result.period).toBe('monthly');
|
|
99
|
+
expect(supabase.from).toHaveBeenCalledWith('monthly_cost_summary');
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('should return error on database failure', async () => {
|
|
103
|
+
const supabase = createMockSupabase({
|
|
104
|
+
selectResult: { data: null, error: { message: 'Database error' } },
|
|
105
|
+
});
|
|
106
|
+
const ctx = createMockContext(supabase);
|
|
107
|
+
|
|
108
|
+
const result = await getCostSummary({ project_id: testUUID() }, ctx);
|
|
109
|
+
|
|
110
|
+
expect(result.isError).toBe(true);
|
|
111
|
+
expect(result.result.error).toContain('Failed to get cost summary');
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// ============================================================================
|
|
116
|
+
// getCostAlerts
|
|
117
|
+
// ============================================================================
|
|
118
|
+
|
|
119
|
+
describe('getCostAlerts', () => {
|
|
120
|
+
it('should return all alerts for user', async () => {
|
|
121
|
+
const mockAlerts = [
|
|
122
|
+
{ id: 'alert-1', threshold_amount: 10, threshold_period: 'daily' },
|
|
123
|
+
{ id: 'alert-2', threshold_amount: 50, threshold_period: 'weekly' },
|
|
124
|
+
];
|
|
125
|
+
|
|
126
|
+
const supabase = createMockSupabase({
|
|
127
|
+
selectResult: { data: mockAlerts, error: null },
|
|
128
|
+
});
|
|
129
|
+
const ctx = createMockContext(supabase);
|
|
130
|
+
|
|
131
|
+
const result = await getCostAlerts({}, ctx);
|
|
132
|
+
|
|
133
|
+
expect(result.result.alerts).toEqual(mockAlerts);
|
|
134
|
+
expect(result.result.count).toBe(2);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('should filter by project_id when provided', async () => {
|
|
138
|
+
const supabase = createMockSupabase({
|
|
139
|
+
selectResult: { data: [], error: null },
|
|
140
|
+
});
|
|
141
|
+
const ctx = createMockContext(supabase);
|
|
142
|
+
|
|
143
|
+
await getCostAlerts({ project_id: testUUID() }, ctx);
|
|
144
|
+
|
|
145
|
+
expect(supabase.eq).toHaveBeenCalledWith('project_id', testUUID());
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('should return error on database failure', async () => {
|
|
149
|
+
const supabase = createMockSupabase({
|
|
150
|
+
selectResult: { data: null, error: { message: 'Query failed' } },
|
|
151
|
+
});
|
|
152
|
+
const ctx = createMockContext(supabase);
|
|
153
|
+
|
|
154
|
+
const result = await getCostAlerts({}, ctx);
|
|
155
|
+
|
|
156
|
+
expect(result.isError).toBe(true);
|
|
157
|
+
expect(result.result.error).toContain('Failed to get cost alerts');
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// ============================================================================
|
|
162
|
+
// addCostAlert
|
|
163
|
+
// ============================================================================
|
|
164
|
+
|
|
165
|
+
describe('addCostAlert', () => {
|
|
166
|
+
it('should return error when threshold_amount is missing', async () => {
|
|
167
|
+
const supabase = createMockSupabase();
|
|
168
|
+
const ctx = createMockContext(supabase);
|
|
169
|
+
|
|
170
|
+
const result = await addCostAlert(
|
|
171
|
+
{ threshold_period: 'daily' },
|
|
172
|
+
ctx
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
expect(result.isError).toBe(true);
|
|
176
|
+
expect(result.result.error).toContain('threshold_amount must be a positive number');
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('should return error when threshold_amount is not positive', async () => {
|
|
180
|
+
const supabase = createMockSupabase();
|
|
181
|
+
const ctx = createMockContext(supabase);
|
|
182
|
+
|
|
183
|
+
const result = await addCostAlert(
|
|
184
|
+
{ threshold_amount: -5, threshold_period: 'daily' },
|
|
185
|
+
ctx
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
expect(result.isError).toBe(true);
|
|
189
|
+
expect(result.result.error).toContain('threshold_amount must be a positive number');
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('should return error when threshold_period is invalid', async () => {
|
|
193
|
+
const supabase = createMockSupabase();
|
|
194
|
+
const ctx = createMockContext(supabase);
|
|
195
|
+
|
|
196
|
+
const result = await addCostAlert(
|
|
197
|
+
{ threshold_amount: 10, threshold_period: 'yearly' },
|
|
198
|
+
ctx
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
expect(result.isError).toBe(true);
|
|
202
|
+
expect(result.result.error).toContain('threshold_period must be');
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it('should create alert successfully', async () => {
|
|
206
|
+
const mockAlert = {
|
|
207
|
+
id: 'new-alert',
|
|
208
|
+
threshold_amount: 10,
|
|
209
|
+
threshold_period: 'daily',
|
|
210
|
+
alert_type: 'warning',
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
const supabase = createMockSupabase({
|
|
214
|
+
insertResult: { data: mockAlert, error: null },
|
|
215
|
+
});
|
|
216
|
+
const ctx = createMockContext(supabase);
|
|
217
|
+
|
|
218
|
+
const result = await addCostAlert(
|
|
219
|
+
{ threshold_amount: 10, threshold_period: 'daily' },
|
|
220
|
+
ctx
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
expect(result.result.success).toBe(true);
|
|
224
|
+
expect(result.result.alert).toEqual(mockAlert);
|
|
225
|
+
expect(result.result.message).toContain('Alert created');
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
it('should create alert with project_id', async () => {
|
|
229
|
+
const supabase = createMockSupabase({
|
|
230
|
+
insertResult: { data: { id: 'new-alert' }, error: null },
|
|
231
|
+
});
|
|
232
|
+
const ctx = createMockContext(supabase);
|
|
233
|
+
|
|
234
|
+
await addCostAlert(
|
|
235
|
+
{
|
|
236
|
+
project_id: testUUID(),
|
|
237
|
+
threshold_amount: 10,
|
|
238
|
+
threshold_period: 'daily',
|
|
239
|
+
},
|
|
240
|
+
ctx
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
expect(supabase.insert).toHaveBeenCalledWith(
|
|
244
|
+
expect.objectContaining({
|
|
245
|
+
project_id: testUUID(),
|
|
246
|
+
})
|
|
247
|
+
);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it('should return error on database failure', async () => {
|
|
251
|
+
const supabase = createMockSupabase({
|
|
252
|
+
insertResult: { data: null, error: { message: 'Insert failed' } },
|
|
253
|
+
});
|
|
254
|
+
const ctx = createMockContext(supabase);
|
|
255
|
+
|
|
256
|
+
const result = await addCostAlert(
|
|
257
|
+
{ threshold_amount: 10, threshold_period: 'daily' },
|
|
258
|
+
ctx
|
|
259
|
+
);
|
|
260
|
+
|
|
261
|
+
expect(result.isError).toBe(true);
|
|
262
|
+
expect(result.result.error).toContain('Failed to create cost alert');
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
// ============================================================================
|
|
267
|
+
// updateCostAlert
|
|
268
|
+
// ============================================================================
|
|
269
|
+
|
|
270
|
+
describe('updateCostAlert', () => {
|
|
271
|
+
it('should return error when alert_id is missing', async () => {
|
|
272
|
+
const supabase = createMockSupabase();
|
|
273
|
+
const ctx = createMockContext(supabase);
|
|
274
|
+
|
|
275
|
+
const result = await updateCostAlert({}, ctx);
|
|
276
|
+
|
|
277
|
+
expect(result.isError).toBe(true);
|
|
278
|
+
expect(result.result.error).toBe('alert_id is required');
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it('should return error when no updates provided', async () => {
|
|
282
|
+
const supabase = createMockSupabase();
|
|
283
|
+
const ctx = createMockContext(supabase);
|
|
284
|
+
|
|
285
|
+
const result = await updateCostAlert({ alert_id: testUUID() }, ctx);
|
|
286
|
+
|
|
287
|
+
expect(result.isError).toBe(true);
|
|
288
|
+
expect(result.result.error).toBe('No updates provided');
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it('should update alert successfully', async () => {
|
|
292
|
+
const mockUpdatedAlert = {
|
|
293
|
+
id: testUUID(),
|
|
294
|
+
threshold_amount: 20,
|
|
295
|
+
threshold_period: 'weekly',
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
const supabase = createMockSupabase({
|
|
299
|
+
updateResult: { data: mockUpdatedAlert, error: null },
|
|
300
|
+
});
|
|
301
|
+
const ctx = createMockContext(supabase);
|
|
302
|
+
|
|
303
|
+
const result = await updateCostAlert(
|
|
304
|
+
{ alert_id: testUUID(), threshold_amount: 20 },
|
|
305
|
+
ctx
|
|
306
|
+
);
|
|
307
|
+
|
|
308
|
+
expect(result.result.success).toBe(true);
|
|
309
|
+
expect(result.result.alert).toEqual(mockUpdatedAlert);
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
it('should update enabled status', async () => {
|
|
313
|
+
const supabase = createMockSupabase({
|
|
314
|
+
updateResult: { data: { id: testUUID(), enabled: false }, error: null },
|
|
315
|
+
});
|
|
316
|
+
const ctx = createMockContext(supabase);
|
|
317
|
+
|
|
318
|
+
await updateCostAlert({ alert_id: testUUID(), enabled: false }, ctx);
|
|
319
|
+
|
|
320
|
+
expect(supabase.update).toHaveBeenCalledWith({ enabled: false });
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
it('should return error on database failure', async () => {
|
|
324
|
+
const supabase = createMockSupabase({
|
|
325
|
+
updateResult: { data: null, error: { message: 'Update failed' } },
|
|
326
|
+
});
|
|
327
|
+
const ctx = createMockContext(supabase);
|
|
328
|
+
|
|
329
|
+
const result = await updateCostAlert(
|
|
330
|
+
{ alert_id: testUUID(), threshold_amount: 20 },
|
|
331
|
+
ctx
|
|
332
|
+
);
|
|
333
|
+
|
|
334
|
+
expect(result.isError).toBe(true);
|
|
335
|
+
expect(result.result.error).toContain('Failed to update cost alert');
|
|
336
|
+
});
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
// ============================================================================
|
|
340
|
+
// deleteCostAlert
|
|
341
|
+
// ============================================================================
|
|
342
|
+
|
|
343
|
+
describe('deleteCostAlert', () => {
|
|
344
|
+
it('should return error when alert_id is missing', async () => {
|
|
345
|
+
const supabase = createMockSupabase();
|
|
346
|
+
const ctx = createMockContext(supabase);
|
|
347
|
+
|
|
348
|
+
const result = await deleteCostAlert({}, ctx);
|
|
349
|
+
|
|
350
|
+
expect(result.isError).toBe(true);
|
|
351
|
+
expect(result.result.error).toBe('alert_id is required');
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
it('should delete alert successfully', async () => {
|
|
355
|
+
const supabase = createMockSupabase({
|
|
356
|
+
deleteResult: { data: null, error: null },
|
|
357
|
+
});
|
|
358
|
+
const ctx = createMockContext(supabase);
|
|
359
|
+
|
|
360
|
+
const result = await deleteCostAlert({ alert_id: testUUID() }, ctx);
|
|
361
|
+
|
|
362
|
+
expect(result.result.success).toBe(true);
|
|
363
|
+
expect(result.result.deleted_alert_id).toBe(testUUID());
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
it('should return error on database failure', async () => {
|
|
367
|
+
const supabase = createMockSupabase({
|
|
368
|
+
deleteResult: { data: null, error: { message: 'Delete failed' } },
|
|
369
|
+
});
|
|
370
|
+
const ctx = createMockContext(supabase);
|
|
371
|
+
|
|
372
|
+
const result = await deleteCostAlert({ alert_id: testUUID() }, ctx);
|
|
373
|
+
|
|
374
|
+
expect(result.isError).toBe(true);
|
|
375
|
+
expect(result.result.error).toContain('Failed to delete cost alert');
|
|
376
|
+
});
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
// ============================================================================
|
|
380
|
+
// getTaskCosts
|
|
381
|
+
// ============================================================================
|
|
382
|
+
|
|
383
|
+
describe('getTaskCosts', () => {
|
|
384
|
+
it('should return error when project_id is missing', async () => {
|
|
385
|
+
const supabase = createMockSupabase();
|
|
386
|
+
const ctx = createMockContext(supabase);
|
|
387
|
+
|
|
388
|
+
const result = await getTaskCosts({}, ctx);
|
|
389
|
+
|
|
390
|
+
expect(result.isError).toBe(true);
|
|
391
|
+
expect(result.result.error).toBe('project_id is required');
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
it('should return task costs with total', async () => {
|
|
395
|
+
const mockTasks = [
|
|
396
|
+
{ id: 'task-1', title: 'Task 1', estimated_cost_usd: '0.50' },
|
|
397
|
+
{ id: 'task-2', title: 'Task 2', estimated_cost_usd: '0.30' },
|
|
398
|
+
];
|
|
399
|
+
|
|
400
|
+
const supabase = createMockSupabase({
|
|
401
|
+
selectResult: { data: mockTasks, error: null },
|
|
402
|
+
});
|
|
403
|
+
const ctx = createMockContext(supabase);
|
|
404
|
+
|
|
405
|
+
const result = await getTaskCosts({ project_id: testUUID() }, ctx);
|
|
406
|
+
|
|
407
|
+
expect(result.result.project_id).toBe(testUUID());
|
|
408
|
+
expect(result.result.tasks).toEqual(mockTasks);
|
|
409
|
+
expect(result.result.total_cost_usd).toBe(0.8);
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
it('should handle empty task list', async () => {
|
|
413
|
+
const supabase = createMockSupabase({
|
|
414
|
+
selectResult: { data: [], error: null },
|
|
415
|
+
});
|
|
416
|
+
const ctx = createMockContext(supabase);
|
|
417
|
+
|
|
418
|
+
const result = await getTaskCosts({ project_id: testUUID() }, ctx);
|
|
419
|
+
|
|
420
|
+
expect(result.result.tasks).toEqual([]);
|
|
421
|
+
expect(result.result.total_cost_usd).toBe(0);
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
it('should return error on database failure', async () => {
|
|
425
|
+
const supabase = createMockSupabase({
|
|
426
|
+
selectResult: { data: null, error: { message: 'Query failed' } },
|
|
427
|
+
});
|
|
428
|
+
const ctx = createMockContext(supabase);
|
|
429
|
+
|
|
430
|
+
const result = await getTaskCosts({ project_id: testUUID() }, ctx);
|
|
431
|
+
|
|
432
|
+
expect(result.isError).toBe(true);
|
|
433
|
+
expect(result.result.error).toContain('Failed to get task costs');
|
|
434
|
+
});
|
|
435
|
+
});
|
|
436
|
+
});
|