tlc-claude-code 1.4.4 → 1.4.5
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/dashboard/dist/App.js +28 -2
- package/dashboard/dist/api/health-diagnostics.d.ts +26 -0
- package/dashboard/dist/api/health-diagnostics.js +85 -0
- package/dashboard/dist/api/health-diagnostics.test.d.ts +1 -0
- package/dashboard/dist/api/health-diagnostics.test.js +126 -0
- package/dashboard/dist/api/index.d.ts +5 -0
- package/dashboard/dist/api/index.js +5 -0
- package/dashboard/dist/api/notes-api.d.ts +18 -0
- package/dashboard/dist/api/notes-api.js +68 -0
- package/dashboard/dist/api/notes-api.test.d.ts +1 -0
- package/dashboard/dist/api/notes-api.test.js +113 -0
- package/dashboard/dist/api/safeFetch.d.ts +50 -0
- package/dashboard/dist/api/safeFetch.js +135 -0
- package/dashboard/dist/api/safeFetch.test.d.ts +1 -0
- package/dashboard/dist/api/safeFetch.test.js +215 -0
- package/dashboard/dist/api/tasks-api.d.ts +32 -0
- package/dashboard/dist/api/tasks-api.js +98 -0
- package/dashboard/dist/api/tasks-api.test.d.ts +1 -0
- package/dashboard/dist/api/tasks-api.test.js +383 -0
- package/dashboard/dist/components/BugsPane.d.ts +20 -0
- package/dashboard/dist/components/BugsPane.js +210 -0
- package/dashboard/dist/components/BugsPane.test.d.ts +1 -0
- package/dashboard/dist/components/BugsPane.test.js +256 -0
- package/dashboard/dist/components/HealthPane.d.ts +3 -1
- package/dashboard/dist/components/HealthPane.js +44 -6
- package/dashboard/dist/components/HealthPane.test.js +105 -2
- package/dashboard/dist/components/RouterPane.d.ts +4 -3
- package/dashboard/dist/components/RouterPane.js +60 -57
- package/dashboard/dist/components/RouterPane.test.js +150 -96
- package/dashboard/dist/components/UpdateBanner.d.ts +26 -0
- package/dashboard/dist/components/UpdateBanner.js +30 -0
- package/dashboard/dist/components/UpdateBanner.test.d.ts +1 -0
- package/dashboard/dist/components/UpdateBanner.test.js +96 -0
- package/dashboard/dist/components/ui/EmptyState.d.ts +14 -0
- package/dashboard/dist/components/ui/EmptyState.js +58 -0
- package/dashboard/dist/components/ui/EmptyState.test.d.ts +1 -0
- package/dashboard/dist/components/ui/EmptyState.test.js +97 -0
- package/dashboard/dist/components/ui/ErrorState.d.ts +17 -0
- package/dashboard/dist/components/ui/ErrorState.js +80 -0
- package/dashboard/dist/components/ui/ErrorState.test.d.ts +1 -0
- package/dashboard/dist/components/ui/ErrorState.test.js +166 -0
- package/dashboard/package.json +3 -0
- package/package.json +1 -1
- package/server/dashboard/index.html +205 -8
- package/server/index.js +64 -0
- package/server/lib/api-provider.js +104 -186
- package/server/lib/api-provider.test.js +238 -336
- package/server/lib/cli-detector.js +90 -166
- package/server/lib/cli-detector.test.js +114 -269
- package/server/lib/cli-provider.js +142 -212
- package/server/lib/cli-provider.test.js +196 -349
- package/server/lib/debug.test.js +1 -1
- package/server/lib/devserver-router-api.js +54 -249
- package/server/lib/devserver-router-api.test.js +126 -426
- package/server/lib/introspect.js +309 -0
- package/server/lib/introspect.test.js +286 -0
- package/server/lib/model-router.js +107 -245
- package/server/lib/model-router.test.js +122 -313
- package/server/lib/output-schemas.js +146 -269
- package/server/lib/output-schemas.test.js +106 -307
- package/server/lib/provider-interface.js +99 -153
- package/server/lib/provider-interface.test.js +228 -394
- package/server/lib/provider-queue.js +164 -158
- package/server/lib/provider-queue.test.js +186 -315
- package/server/lib/router-config.js +99 -221
- package/server/lib/router-config.test.js +83 -237
- package/server/lib/router-setup-command.js +94 -419
- package/server/lib/router-setup-command.test.js +96 -375
- package/server/lib/router-status-api.js +93 -0
- package/server/lib/router-status-api.test.js +270 -0
|
@@ -1,426 +1,126 @@
|
|
|
1
|
-
import { describe, it, expect, vi
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
vi.
|
|
15
|
-
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
expect(
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
describe('
|
|
57
|
-
it('
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
};
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
};
|
|
93
|
-
const res = {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
);
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
status: 'completed',
|
|
128
|
-
result: { summary: 'Code looks good' },
|
|
129
|
-
}),
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
const handler = handleTaskStatus(mockQueue);
|
|
133
|
-
const req = { params: { taskId: 'task-123' } };
|
|
134
|
-
const res = {
|
|
135
|
-
json: vi.fn(),
|
|
136
|
-
status: vi.fn().mockReturnThis(),
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
await handler(req, res);
|
|
140
|
-
|
|
141
|
-
expect(res.json).toHaveBeenCalledWith(
|
|
142
|
-
expect.objectContaining({
|
|
143
|
-
status: 'completed',
|
|
144
|
-
result: { summary: 'Code looks good' },
|
|
145
|
-
})
|
|
146
|
-
);
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
it('returns 404 for unknown task', async () => {
|
|
150
|
-
const mockQueue = {
|
|
151
|
-
getTask: vi.fn().mockReturnValue(null),
|
|
152
|
-
};
|
|
153
|
-
|
|
154
|
-
const handler = handleTaskStatus(mockQueue);
|
|
155
|
-
const req = { params: { taskId: 'unknown' } };
|
|
156
|
-
const res = {
|
|
157
|
-
json: vi.fn(),
|
|
158
|
-
status: vi.fn().mockReturnThis(),
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
await handler(req, res);
|
|
162
|
-
|
|
163
|
-
expect(res.status).toHaveBeenCalledWith(404);
|
|
164
|
-
});
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
describe('handleReview', () => {
|
|
168
|
-
it('runs multiple providers for review', async () => {
|
|
169
|
-
const mockRouter = {
|
|
170
|
-
run: vi.fn().mockResolvedValue([
|
|
171
|
-
{ provider: 'claude', success: true, result: { score: 85 } },
|
|
172
|
-
{ provider: 'codex', success: true, result: { score: 80 } },
|
|
173
|
-
]),
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
const handler = handleReview(mockRouter);
|
|
177
|
-
const req = {
|
|
178
|
-
body: { code: 'function test() {}', prompt: 'Review this' },
|
|
179
|
-
};
|
|
180
|
-
const res = {
|
|
181
|
-
json: vi.fn(),
|
|
182
|
-
status: vi.fn().mockReturnThis(),
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
await handler(req, res);
|
|
186
|
-
|
|
187
|
-
expect(mockRouter.run).toHaveBeenCalledWith(
|
|
188
|
-
'review',
|
|
189
|
-
expect.any(String),
|
|
190
|
-
expect.any(Object)
|
|
191
|
-
);
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
it('returns consensus from multiple providers', async () => {
|
|
195
|
-
const mockRouter = {
|
|
196
|
-
run: vi.fn().mockResolvedValue([
|
|
197
|
-
{ provider: 'claude', success: true, result: { approved: true } },
|
|
198
|
-
{ provider: 'codex', success: true, result: { approved: true } },
|
|
199
|
-
{ provider: 'deepseek', success: true, result: { approved: false } },
|
|
200
|
-
]),
|
|
201
|
-
};
|
|
202
|
-
|
|
203
|
-
const handler = handleReview(mockRouter);
|
|
204
|
-
const req = {
|
|
205
|
-
body: { code: 'function test() {}' },
|
|
206
|
-
};
|
|
207
|
-
const res = {
|
|
208
|
-
json: vi.fn(),
|
|
209
|
-
status: vi.fn().mockReturnThis(),
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
await handler(req, res);
|
|
213
|
-
|
|
214
|
-
expect(res.json).toHaveBeenCalledWith(
|
|
215
|
-
expect.objectContaining({
|
|
216
|
-
consensus: expect.any(Object),
|
|
217
|
-
results: expect.any(Array),
|
|
218
|
-
})
|
|
219
|
-
);
|
|
220
|
-
});
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
describe('handleDesign', () => {
|
|
224
|
-
it('routes to gemini for design', async () => {
|
|
225
|
-
const mockRouter = {
|
|
226
|
-
run: vi.fn().mockResolvedValue([
|
|
227
|
-
{ provider: 'gemini', success: true, result: { mockups: [] } },
|
|
228
|
-
]),
|
|
229
|
-
};
|
|
230
|
-
|
|
231
|
-
const handler = handleDesign(mockRouter);
|
|
232
|
-
const req = {
|
|
233
|
-
body: { prompt: 'Design a login page' },
|
|
234
|
-
};
|
|
235
|
-
const res = {
|
|
236
|
-
json: vi.fn(),
|
|
237
|
-
status: vi.fn().mockReturnThis(),
|
|
238
|
-
};
|
|
239
|
-
|
|
240
|
-
await handler(req, res);
|
|
241
|
-
|
|
242
|
-
expect(mockRouter.run).toHaveBeenCalledWith(
|
|
243
|
-
'design',
|
|
244
|
-
expect.any(String),
|
|
245
|
-
expect.any(Object)
|
|
246
|
-
);
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
it('returns design result', async () => {
|
|
250
|
-
const mockRouter = {
|
|
251
|
-
run: vi.fn().mockResolvedValue([
|
|
252
|
-
{
|
|
253
|
-
provider: 'gemini',
|
|
254
|
-
success: true,
|
|
255
|
-
result: { mockups: ['mockup1.png'], rationale: 'Clean design' },
|
|
256
|
-
},
|
|
257
|
-
]),
|
|
258
|
-
};
|
|
259
|
-
|
|
260
|
-
const handler = handleDesign(mockRouter);
|
|
261
|
-
const req = {
|
|
262
|
-
body: { prompt: 'Design a dashboard' },
|
|
263
|
-
};
|
|
264
|
-
const res = {
|
|
265
|
-
json: vi.fn(),
|
|
266
|
-
status: vi.fn().mockReturnThis(),
|
|
267
|
-
};
|
|
268
|
-
|
|
269
|
-
await handler(req, res);
|
|
270
|
-
|
|
271
|
-
expect(res.json).toHaveBeenCalledWith(
|
|
272
|
-
expect.objectContaining({
|
|
273
|
-
result: expect.objectContaining({ mockups: expect.any(Array) }),
|
|
274
|
-
})
|
|
275
|
-
);
|
|
276
|
-
});
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
describe('handleHealth', () => {
|
|
280
|
-
it('shows provider availability', async () => {
|
|
281
|
-
const mockRouter = {
|
|
282
|
-
getStatus: vi.fn().mockReturnValue({
|
|
283
|
-
providers: {
|
|
284
|
-
claude: { detected: true, type: 'cli' },
|
|
285
|
-
codex: { detected: false, type: 'cli' },
|
|
286
|
-
deepseek: { detected: false, type: 'api' },
|
|
287
|
-
},
|
|
288
|
-
devserver: { configured: true },
|
|
289
|
-
}),
|
|
290
|
-
};
|
|
291
|
-
|
|
292
|
-
const handler = handleHealth(mockRouter);
|
|
293
|
-
const req = {};
|
|
294
|
-
const res = {
|
|
295
|
-
json: vi.fn(),
|
|
296
|
-
status: vi.fn().mockReturnThis(),
|
|
297
|
-
};
|
|
298
|
-
|
|
299
|
-
await handler(req, res);
|
|
300
|
-
|
|
301
|
-
expect(res.json).toHaveBeenCalledWith(
|
|
302
|
-
expect.objectContaining({
|
|
303
|
-
providers: expect.any(Object),
|
|
304
|
-
devserver: expect.any(Object),
|
|
305
|
-
})
|
|
306
|
-
);
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
it('returns healthy status', async () => {
|
|
310
|
-
const mockRouter = {
|
|
311
|
-
getStatus: vi.fn().mockReturnValue({
|
|
312
|
-
providers: { claude: { detected: true } },
|
|
313
|
-
devserver: { configured: true },
|
|
314
|
-
}),
|
|
315
|
-
};
|
|
316
|
-
|
|
317
|
-
const handler = handleHealth(mockRouter);
|
|
318
|
-
const req = {};
|
|
319
|
-
const res = {
|
|
320
|
-
json: vi.fn(),
|
|
321
|
-
status: vi.fn().mockReturnThis(),
|
|
322
|
-
};
|
|
323
|
-
|
|
324
|
-
await handler(req, res);
|
|
325
|
-
|
|
326
|
-
expect(res.json).toHaveBeenCalledWith(
|
|
327
|
-
expect.objectContaining({ healthy: true })
|
|
328
|
-
);
|
|
329
|
-
});
|
|
330
|
-
});
|
|
331
|
-
|
|
332
|
-
describe('validateAuth', () => {
|
|
333
|
-
it('rejects unauthenticated requests', () => {
|
|
334
|
-
const middleware = validateAuth('secret-key');
|
|
335
|
-
const req = { headers: {} };
|
|
336
|
-
const res = {
|
|
337
|
-
json: vi.fn(),
|
|
338
|
-
status: vi.fn().mockReturnThis(),
|
|
339
|
-
};
|
|
340
|
-
const next = vi.fn();
|
|
341
|
-
|
|
342
|
-
middleware(req, res, next);
|
|
343
|
-
|
|
344
|
-
expect(res.status).toHaveBeenCalledWith(401);
|
|
345
|
-
expect(next).not.toHaveBeenCalled();
|
|
346
|
-
});
|
|
347
|
-
|
|
348
|
-
it('accepts valid auth header', () => {
|
|
349
|
-
const middleware = validateAuth('secret-key');
|
|
350
|
-
const req = {
|
|
351
|
-
headers: { authorization: 'Bearer secret-key' },
|
|
352
|
-
};
|
|
353
|
-
const res = {
|
|
354
|
-
json: vi.fn(),
|
|
355
|
-
status: vi.fn().mockReturnThis(),
|
|
356
|
-
};
|
|
357
|
-
const next = vi.fn();
|
|
358
|
-
|
|
359
|
-
middleware(req, res, next);
|
|
360
|
-
|
|
361
|
-
expect(next).toHaveBeenCalled();
|
|
362
|
-
});
|
|
363
|
-
|
|
364
|
-
it('rejects invalid auth header', () => {
|
|
365
|
-
const middleware = validateAuth('secret-key');
|
|
366
|
-
const req = {
|
|
367
|
-
headers: { authorization: 'Bearer wrong-key' },
|
|
368
|
-
};
|
|
369
|
-
const res = {
|
|
370
|
-
json: vi.fn(),
|
|
371
|
-
status: vi.fn().mockReturnThis(),
|
|
372
|
-
};
|
|
373
|
-
const next = vi.fn();
|
|
374
|
-
|
|
375
|
-
middleware(req, res, next);
|
|
376
|
-
|
|
377
|
-
expect(res.status).toHaveBeenCalledWith(401);
|
|
378
|
-
expect(next).not.toHaveBeenCalled();
|
|
379
|
-
});
|
|
380
|
-
});
|
|
381
|
-
|
|
382
|
-
describe('validateRequestBody', () => {
|
|
383
|
-
it('validates required fields', () => {
|
|
384
|
-
const middleware = validateRequestBody(['prompt']);
|
|
385
|
-
const req = { body: {} };
|
|
386
|
-
const res = {
|
|
387
|
-
json: vi.fn(),
|
|
388
|
-
status: vi.fn().mockReturnThis(),
|
|
389
|
-
};
|
|
390
|
-
const next = vi.fn();
|
|
391
|
-
|
|
392
|
-
middleware(req, res, next);
|
|
393
|
-
|
|
394
|
-
expect(res.status).toHaveBeenCalledWith(400);
|
|
395
|
-
expect(next).not.toHaveBeenCalled();
|
|
396
|
-
});
|
|
397
|
-
|
|
398
|
-
it('passes valid body', () => {
|
|
399
|
-
const middleware = validateRequestBody(['prompt']);
|
|
400
|
-
const req = { body: { prompt: 'Test prompt' } };
|
|
401
|
-
const res = {
|
|
402
|
-
json: vi.fn(),
|
|
403
|
-
status: vi.fn().mockReturnThis(),
|
|
404
|
-
};
|
|
405
|
-
const next = vi.fn();
|
|
406
|
-
|
|
407
|
-
middleware(req, res, next);
|
|
408
|
-
|
|
409
|
-
expect(next).toHaveBeenCalled();
|
|
410
|
-
});
|
|
411
|
-
|
|
412
|
-
it('validates multiple required fields', () => {
|
|
413
|
-
const middleware = validateRequestBody(['prompt', 'capability']);
|
|
414
|
-
const req = { body: { prompt: 'Test' } };
|
|
415
|
-
const res = {
|
|
416
|
-
json: vi.fn(),
|
|
417
|
-
status: vi.fn().mockReturnThis(),
|
|
418
|
-
};
|
|
419
|
-
const next = vi.fn();
|
|
420
|
-
|
|
421
|
-
middleware(req, res, next);
|
|
422
|
-
|
|
423
|
-
expect(res.status).toHaveBeenCalledWith(400);
|
|
424
|
-
});
|
|
425
|
-
});
|
|
426
|
-
});
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
handleRun,
|
|
4
|
+
handleTask,
|
|
5
|
+
handleReview,
|
|
6
|
+
handleDesign,
|
|
7
|
+
handleHealth,
|
|
8
|
+
} from './devserver-router-api.js';
|
|
9
|
+
|
|
10
|
+
describe('Devserver Router API', () => {
|
|
11
|
+
describe('POST /api/run', () => {
|
|
12
|
+
it('queues task', async () => {
|
|
13
|
+
const req = { body: { provider: 'claude', prompt: 'test' } };
|
|
14
|
+
const res = { json: vi.fn(), status: vi.fn().mockReturnThis() };
|
|
15
|
+
|
|
16
|
+
await handleRun(req, res, { queue: { enqueue: vi.fn().mockReturnValue('task-1') } });
|
|
17
|
+
|
|
18
|
+
expect(res.json).toHaveBeenCalledWith(expect.objectContaining({ taskId: 'task-1' }));
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('returns taskId', async () => {
|
|
22
|
+
const req = { body: { provider: 'claude', prompt: 'test' } };
|
|
23
|
+
const res = { json: vi.fn(), status: vi.fn().mockReturnThis() };
|
|
24
|
+
|
|
25
|
+
await handleRun(req, res, { queue: { enqueue: vi.fn().mockReturnValue('abc123') } });
|
|
26
|
+
|
|
27
|
+
expect(res.json).toHaveBeenCalledWith({ taskId: 'abc123' });
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
describe('GET /api/task/:taskId', () => {
|
|
32
|
+
it('returns pending status', async () => {
|
|
33
|
+
const req = { params: { taskId: 'task-1' } };
|
|
34
|
+
const res = { json: vi.fn() };
|
|
35
|
+
|
|
36
|
+
await handleTask(req, res, { queue: { getTask: vi.fn().mockReturnValue({ status: 'pending' }) } });
|
|
37
|
+
|
|
38
|
+
expect(res.json).toHaveBeenCalledWith(expect.objectContaining({ status: 'pending' }));
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('returns completed result', async () => {
|
|
42
|
+
const req = { params: { taskId: 'task-1' } };
|
|
43
|
+
const res = { json: vi.fn() };
|
|
44
|
+
|
|
45
|
+
await handleTask(req, res, {
|
|
46
|
+
queue: { getTask: vi.fn().mockReturnValue({ status: 'completed', result: { data: 'test' } }) }
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
expect(res.json).toHaveBeenCalledWith(expect.objectContaining({
|
|
50
|
+
status: 'completed',
|
|
51
|
+
result: { data: 'test' }
|
|
52
|
+
}));
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe('POST /api/review', () => {
|
|
57
|
+
it('runs multiple providers', async () => {
|
|
58
|
+
const req = { body: { code: 'test code' } };
|
|
59
|
+
const res = { json: vi.fn() };
|
|
60
|
+
const runProvider = vi.fn().mockResolvedValue({ parsed: { score: 80 } });
|
|
61
|
+
|
|
62
|
+
await handleReview(req, res, { runProvider, providers: ['claude', 'codex'] });
|
|
63
|
+
|
|
64
|
+
expect(runProvider).toHaveBeenCalledTimes(2);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('returns consensus', async () => {
|
|
68
|
+
const req = { body: { code: 'test code' } };
|
|
69
|
+
const res = { json: vi.fn() };
|
|
70
|
+
const runProvider = vi.fn().mockResolvedValue({ parsed: { score: 80, approved: true } });
|
|
71
|
+
|
|
72
|
+
await handleReview(req, res, { runProvider, providers: ['claude'] });
|
|
73
|
+
|
|
74
|
+
expect(res.json).toHaveBeenCalledWith(expect.objectContaining({ consensus: expect.anything() }));
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
describe('POST /api/design', () => {
|
|
79
|
+
it('routes to gemini', async () => {
|
|
80
|
+
const req = { body: { description: 'design a button' } };
|
|
81
|
+
const res = { json: vi.fn() };
|
|
82
|
+
const runProvider = vi.fn().mockResolvedValue({ parsed: { mockups: [] } });
|
|
83
|
+
|
|
84
|
+
await handleDesign(req, res, { runProvider });
|
|
85
|
+
|
|
86
|
+
expect(runProvider).toHaveBeenCalledWith('gemini', expect.anything());
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe('GET /api/health', () => {
|
|
91
|
+
it('shows provider status', async () => {
|
|
92
|
+
const req = {};
|
|
93
|
+
const res = { json: vi.fn() };
|
|
94
|
+
|
|
95
|
+
await handleHealth(req, res, {
|
|
96
|
+
getProviderStatus: vi.fn().mockReturnValue({ claude: 'available', codex: 'unavailable' })
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
expect(res.json).toHaveBeenCalledWith(expect.objectContaining({
|
|
100
|
+
providers: expect.anything()
|
|
101
|
+
}));
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe('Authentication', () => {
|
|
106
|
+
it('rejects unauthenticated requests', async () => {
|
|
107
|
+
const req = { headers: {} };
|
|
108
|
+
const res = { json: vi.fn(), status: vi.fn().mockReturnThis() };
|
|
109
|
+
|
|
110
|
+
await handleRun(req, res, { requireAuth: true });
|
|
111
|
+
|
|
112
|
+
expect(res.status).toHaveBeenCalledWith(401);
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
describe('Validation', () => {
|
|
117
|
+
it('validates request body', async () => {
|
|
118
|
+
const req = { body: {} };
|
|
119
|
+
const res = { json: vi.fn(), status: vi.fn().mockReturnThis() };
|
|
120
|
+
|
|
121
|
+
await handleRun(req, res, { queue: { enqueue: vi.fn() } });
|
|
122
|
+
|
|
123
|
+
expect(res.status).toHaveBeenCalledWith(400);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
});
|