@positronic/cli 0.0.3 → 0.0.4
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/dist/src/commands/helpers.js +11 -25
- package/dist/types/commands/helpers.d.ts.map +1 -1
- package/package.json +5 -1
- package/dist/src/commands/brain.test.js +0 -2936
- package/dist/src/commands/helpers.test.js +0 -832
- package/dist/src/commands/project.test.js +0 -1201
- package/dist/src/commands/resources.test.js +0 -2511
- package/dist/src/commands/schedule.test.js +0 -1235
- package/dist/src/commands/secret.test.d.js +0 -1
- package/dist/src/commands/secret.test.js +0 -761
- package/dist/src/commands/server.test.js +0 -1237
- package/dist/src/commands/test-utils.js +0 -737
- package/dist/src/components/secret-sync.js +0 -303
- package/dist/src/test/mock-api-client.js +0 -371
- package/dist/src/test/test-dev-server.js +0 -1376
- package/dist/types/commands/test-utils.d.ts +0 -45
- package/dist/types/commands/test-utils.d.ts.map +0 -1
- package/dist/types/components/secret-sync.d.ts +0 -9
- package/dist/types/components/secret-sync.d.ts.map +0 -1
- package/dist/types/test/mock-api-client.d.ts +0 -25
- package/dist/types/test/mock-api-client.d.ts.map +0 -1
- package/dist/types/test/test-dev-server.d.ts +0 -129
- package/dist/types/test/test-dev-server.d.ts.map +0 -1
- package/src/cli.ts +0 -997
- package/src/commands/backend.ts +0 -63
- package/src/commands/brain.test.ts +0 -1004
- package/src/commands/brain.ts +0 -215
- package/src/commands/helpers.test.ts +0 -487
- package/src/commands/helpers.ts +0 -870
- package/src/commands/project-config-manager.ts +0 -152
- package/src/commands/project.test.ts +0 -502
- package/src/commands/project.ts +0 -109
- package/src/commands/resources.test.ts +0 -1052
- package/src/commands/resources.ts +0 -97
- package/src/commands/schedule.test.ts +0 -481
- package/src/commands/schedule.ts +0 -65
- package/src/commands/secret.test.ts +0 -210
- package/src/commands/secret.ts +0 -50
- package/src/commands/server.test.ts +0 -493
- package/src/commands/server.ts +0 -353
- package/src/commands/test-utils.ts +0 -324
- package/src/components/brain-history.tsx +0 -198
- package/src/components/brain-list.tsx +0 -105
- package/src/components/brain-rerun.tsx +0 -111
- package/src/components/brain-show.tsx +0 -92
- package/src/components/error.tsx +0 -24
- package/src/components/project-add.tsx +0 -59
- package/src/components/project-create.tsx +0 -83
- package/src/components/project-list.tsx +0 -83
- package/src/components/project-remove.tsx +0 -55
- package/src/components/project-select.tsx +0 -200
- package/src/components/project-show.tsx +0 -58
- package/src/components/resource-clear.tsx +0 -127
- package/src/components/resource-delete.tsx +0 -160
- package/src/components/resource-list.tsx +0 -177
- package/src/components/resource-sync.tsx +0 -170
- package/src/components/resource-types.tsx +0 -55
- package/src/components/resource-upload.tsx +0 -182
- package/src/components/schedule-create.tsx +0 -90
- package/src/components/schedule-delete.tsx +0 -116
- package/src/components/schedule-list.tsx +0 -186
- package/src/components/schedule-runs.tsx +0 -151
- package/src/components/secret-bulk.tsx +0 -79
- package/src/components/secret-create.tsx +0 -49
- package/src/components/secret-delete.tsx +0 -41
- package/src/components/secret-list.tsx +0 -41
- package/src/components/watch.tsx +0 -155
- package/src/hooks/useApi.ts +0 -183
- package/src/positronic.ts +0 -40
- package/src/test/data/resources/config.json +0 -1
- package/src/test/data/resources/data/config.json +0 -1
- package/src/test/data/resources/data/logo.png +0 -2
- package/src/test/data/resources/docs/api.md +0 -3
- package/src/test/data/resources/docs/readme.md +0 -3
- package/src/test/data/resources/example.md +0 -3
- package/src/test/data/resources/file with spaces.txt +0 -1
- package/src/test/data/resources/readme.md +0 -3
- package/src/test/data/resources/test.txt +0 -1
- package/src/test/mock-api-client.ts +0 -145
- package/src/test/test-dev-server.ts +0 -1003
- package/tsconfig.json +0 -11
|
@@ -1,1004 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from '@jest/globals';
|
|
2
|
-
import { createTestEnv, px } from './test-utils.js';
|
|
3
|
-
import nock from 'nock';
|
|
4
|
-
|
|
5
|
-
describe('CLI Integration: positronic brain commands', () => {
|
|
6
|
-
describe('brain run command', () => {
|
|
7
|
-
it('should successfully run a brain and return run ID', async () => {
|
|
8
|
-
const env = await createTestEnv();
|
|
9
|
-
const px = await env.start();
|
|
10
|
-
|
|
11
|
-
try {
|
|
12
|
-
const { waitForOutput } = await px(['run', 'test-brain']);
|
|
13
|
-
const isOutputRendered = await waitForOutput(/Run ID: run-\d+/);
|
|
14
|
-
expect(isOutputRendered).toBe(true);
|
|
15
|
-
} finally {
|
|
16
|
-
await env.stopAndCleanup();
|
|
17
|
-
}
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it('should run a brain with watch option', async () => {
|
|
21
|
-
const env = await createTestEnv();
|
|
22
|
-
const px = await env.start();
|
|
23
|
-
|
|
24
|
-
try {
|
|
25
|
-
const { waitForOutput, instance } = await px(['run', 'test-brain', '--watch']);
|
|
26
|
-
// The watch component should be rendered - first shows connecting message
|
|
27
|
-
const isOutputRendered = await waitForOutput(
|
|
28
|
-
/Connecting to watch service|Brain: test-brain/
|
|
29
|
-
);
|
|
30
|
-
expect(isOutputRendered).toBe(true);
|
|
31
|
-
|
|
32
|
-
// Unmount the component to trigger EventSource cleanup
|
|
33
|
-
instance.unmount();
|
|
34
|
-
} finally {
|
|
35
|
-
await env.stopAndCleanup();
|
|
36
|
-
}
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it('should run a brain with short watch flag', async () => {
|
|
40
|
-
const env = await createTestEnv();
|
|
41
|
-
const px = await env.start();
|
|
42
|
-
|
|
43
|
-
try {
|
|
44
|
-
const { waitForOutput, instance } = await px(['run', 'test-brain', '-w']);
|
|
45
|
-
// The watch component should be rendered - first shows connecting message
|
|
46
|
-
const isOutputRendered = await waitForOutput(
|
|
47
|
-
/Connecting to watch service|Brain: test-brain/
|
|
48
|
-
);
|
|
49
|
-
expect(isOutputRendered).toBe(true);
|
|
50
|
-
|
|
51
|
-
// Unmount the component to trigger EventSource cleanup
|
|
52
|
-
instance.unmount();
|
|
53
|
-
} finally {
|
|
54
|
-
await env.stopAndCleanup();
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
it('should handle brain not found (404) error with helpful message', async () => {
|
|
60
|
-
const env = await createTestEnv();
|
|
61
|
-
const px = await env.start();
|
|
62
|
-
|
|
63
|
-
try {
|
|
64
|
-
const { waitForOutput } = await px(['run', 'non-existent-brain']);
|
|
65
|
-
|
|
66
|
-
// Check for error component output
|
|
67
|
-
const foundErrorTitle = await waitForOutput(/Brain Not Found/i, 30);
|
|
68
|
-
expect(foundErrorTitle).toBe(true);
|
|
69
|
-
|
|
70
|
-
const foundErrorMessage = await waitForOutput(/Brain 'non-existent-brain' not found/i, 30);
|
|
71
|
-
expect(foundErrorMessage).toBe(true);
|
|
72
|
-
|
|
73
|
-
const foundHelpText = await waitForOutput(/brain name is spelled correctly/i, 30);
|
|
74
|
-
expect(foundHelpText).toBe(true);
|
|
75
|
-
|
|
76
|
-
// Verify the API was called
|
|
77
|
-
const calls = env.server.getLogs();
|
|
78
|
-
const runCall = calls.find(c => c.method === 'createBrainRun');
|
|
79
|
-
expect(runCall).toBeDefined();
|
|
80
|
-
expect(runCall?.args[0]).toBe('non-existent-brain');
|
|
81
|
-
} finally {
|
|
82
|
-
await env.stopAndCleanup();
|
|
83
|
-
}
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it('should handle API server error responses', async () => {
|
|
87
|
-
const env = await createTestEnv();
|
|
88
|
-
const px = await env.start();
|
|
89
|
-
|
|
90
|
-
try {
|
|
91
|
-
// Clear all existing nock interceptors to avoid conflicts
|
|
92
|
-
nock.cleanAll();
|
|
93
|
-
|
|
94
|
-
// Mock the server to return a 500 error
|
|
95
|
-
const port = env.server.port;
|
|
96
|
-
nock(`http://localhost:${port}`)
|
|
97
|
-
.post('/brains/runs')
|
|
98
|
-
.reply(500, 'Internal Server Error');
|
|
99
|
-
|
|
100
|
-
// Mock process.exit to prevent test from exiting
|
|
101
|
-
const originalExit = process.exit;
|
|
102
|
-
let exitCalled = false;
|
|
103
|
-
process.exit = ((code?: number) => {
|
|
104
|
-
exitCalled = true;
|
|
105
|
-
throw new Error(`process.exit called with code ${code}`);
|
|
106
|
-
}) as any;
|
|
107
|
-
|
|
108
|
-
try {
|
|
109
|
-
await expect(px(['run', 'test-brain'])).rejects.toThrow(
|
|
110
|
-
'process.exit called with code 1'
|
|
111
|
-
);
|
|
112
|
-
expect(exitCalled).toBe(true);
|
|
113
|
-
} finally {
|
|
114
|
-
process.exit = originalExit;
|
|
115
|
-
}
|
|
116
|
-
} finally {
|
|
117
|
-
await env.stopAndCleanup();
|
|
118
|
-
}
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it('should handle network connection errors (ECONNREFUSED)', async () => {
|
|
122
|
-
const env = await createTestEnv();
|
|
123
|
-
const px = await env.start();
|
|
124
|
-
|
|
125
|
-
try {
|
|
126
|
-
// Clear all existing nock interceptors to avoid conflicts
|
|
127
|
-
nock.cleanAll();
|
|
128
|
-
|
|
129
|
-
// Mock a connection refused error
|
|
130
|
-
const port = env.server.port;
|
|
131
|
-
nock(`http://localhost:${port}`).post('/brains/runs').replyWithError({
|
|
132
|
-
message: 'connect ECONNREFUSED',
|
|
133
|
-
code: 'ECONNREFUSED',
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
// Mock process.exit to prevent test from exiting
|
|
137
|
-
const originalExit = process.exit;
|
|
138
|
-
let exitCalled = false;
|
|
139
|
-
process.exit = ((code?: number) => {
|
|
140
|
-
exitCalled = true;
|
|
141
|
-
throw new Error(`process.exit called with code ${code}`);
|
|
142
|
-
}) as any;
|
|
143
|
-
|
|
144
|
-
try {
|
|
145
|
-
await expect(px(['run', 'test-brain'])).rejects.toThrow(
|
|
146
|
-
'process.exit called with code 1'
|
|
147
|
-
);
|
|
148
|
-
expect(exitCalled).toBe(true);
|
|
149
|
-
} finally {
|
|
150
|
-
process.exit = originalExit;
|
|
151
|
-
}
|
|
152
|
-
} finally {
|
|
153
|
-
await env.stopAndCleanup();
|
|
154
|
-
}
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
it('should handle other network errors', async () => {
|
|
158
|
-
const env = await createTestEnv();
|
|
159
|
-
const px = await env.start();
|
|
160
|
-
|
|
161
|
-
try {
|
|
162
|
-
// Clear all existing nock interceptors to avoid conflicts
|
|
163
|
-
nock.cleanAll();
|
|
164
|
-
|
|
165
|
-
// Mock a different network error (without ECONNREFUSED code)
|
|
166
|
-
const port = env.server.port;
|
|
167
|
-
nock(`http://localhost:${port}`)
|
|
168
|
-
.post('/brains/runs')
|
|
169
|
-
.replyWithError({
|
|
170
|
-
message: 'Network timeout error occurred',
|
|
171
|
-
code: 'TIMEOUT',
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
// Mock process.exit to prevent test from exiting
|
|
175
|
-
const originalExit = process.exit;
|
|
176
|
-
let exitCalled = false;
|
|
177
|
-
process.exit = ((code?: number) => {
|
|
178
|
-
exitCalled = true;
|
|
179
|
-
throw new Error(`process.exit called with code ${code}`);
|
|
180
|
-
}) as any;
|
|
181
|
-
|
|
182
|
-
try {
|
|
183
|
-
await expect(px(['run', 'test-brain'])).rejects.toThrow(
|
|
184
|
-
'process.exit called with code 1'
|
|
185
|
-
);
|
|
186
|
-
expect(exitCalled).toBe(true);
|
|
187
|
-
} finally {
|
|
188
|
-
process.exit = originalExit;
|
|
189
|
-
}
|
|
190
|
-
} finally {
|
|
191
|
-
await env.stopAndCleanup();
|
|
192
|
-
}
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
it('should handle network errors with specific error message', async () => {
|
|
196
|
-
const env = await createTestEnv();
|
|
197
|
-
const px = await env.start();
|
|
198
|
-
|
|
199
|
-
try {
|
|
200
|
-
// Clear all existing nock interceptors to avoid conflicts
|
|
201
|
-
nock.cleanAll();
|
|
202
|
-
|
|
203
|
-
// Mock a network error with a specific message (not ECONNREFUSED)
|
|
204
|
-
const port = env.server.port;
|
|
205
|
-
nock(`http://localhost:${port}`)
|
|
206
|
-
.post('/brains/runs')
|
|
207
|
-
.replyWithError(new Error('DNS resolution failed'));
|
|
208
|
-
|
|
209
|
-
// Mock process.exit to prevent test from exiting
|
|
210
|
-
const originalExit = process.exit;
|
|
211
|
-
let exitCalled = false;
|
|
212
|
-
process.exit = ((code?: number) => {
|
|
213
|
-
exitCalled = true;
|
|
214
|
-
throw new Error(`process.exit called with code ${code}`);
|
|
215
|
-
}) as any;
|
|
216
|
-
|
|
217
|
-
try {
|
|
218
|
-
await expect(px(['run', 'test-brain'])).rejects.toThrow(
|
|
219
|
-
'process.exit called with code 1'
|
|
220
|
-
);
|
|
221
|
-
expect(exitCalled).toBe(true);
|
|
222
|
-
} finally {
|
|
223
|
-
process.exit = originalExit;
|
|
224
|
-
}
|
|
225
|
-
} finally {
|
|
226
|
-
await env.stopAndCleanup();
|
|
227
|
-
}
|
|
228
|
-
});
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
describe('brain watch command', () => {
|
|
232
|
-
it('should watch a brain run by run ID', async () => {
|
|
233
|
-
const env = await createTestEnv();
|
|
234
|
-
const px = await env.start();
|
|
235
|
-
|
|
236
|
-
try {
|
|
237
|
-
const { waitForOutput, instance } = await px([
|
|
238
|
-
'watch',
|
|
239
|
-
'--run-id',
|
|
240
|
-
'test-run-123',
|
|
241
|
-
]);
|
|
242
|
-
const isOutputRendered = await waitForOutput(
|
|
243
|
-
/Connecting to watch service|Brain: test-brain/
|
|
244
|
-
);
|
|
245
|
-
expect(isOutputRendered).toBe(true);
|
|
246
|
-
|
|
247
|
-
// Unmount the component to trigger EventSource cleanup
|
|
248
|
-
instance.unmount();
|
|
249
|
-
} finally {
|
|
250
|
-
await env.stopAndCleanup();
|
|
251
|
-
}
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
it('should watch a brain run by run ID using short flag', async () => {
|
|
255
|
-
const env = await createTestEnv();
|
|
256
|
-
const px = await env.start();
|
|
257
|
-
|
|
258
|
-
try {
|
|
259
|
-
const { waitForOutput, instance } = await px(['watch', '--id', 'test-run-456']);
|
|
260
|
-
const isOutputRendered = await waitForOutput(
|
|
261
|
-
/Connecting to watch service|Brain: test-brain/
|
|
262
|
-
);
|
|
263
|
-
expect(isOutputRendered).toBe(true);
|
|
264
|
-
|
|
265
|
-
// Unmount the component to trigger EventSource cleanup
|
|
266
|
-
instance.unmount();
|
|
267
|
-
} finally {
|
|
268
|
-
await env.stopAndCleanup();
|
|
269
|
-
}
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
it('should watch the single active run when watching by brain name', async () => {
|
|
273
|
-
const env = await createTestEnv();
|
|
274
|
-
const { server } = env;
|
|
275
|
-
|
|
276
|
-
// Add a running brain run
|
|
277
|
-
server.addBrainRun({
|
|
278
|
-
brainRunId: 'run-active-123',
|
|
279
|
-
brainTitle: 'test brain',
|
|
280
|
-
type: 'START',
|
|
281
|
-
status: 'RUNNING',
|
|
282
|
-
createdAt: Date.now() - 60000, // 1 minute ago
|
|
283
|
-
startedAt: Date.now() - 60000,
|
|
284
|
-
});
|
|
285
|
-
|
|
286
|
-
const px = await env.start();
|
|
287
|
-
|
|
288
|
-
try {
|
|
289
|
-
const { waitForOutput, instance } = await px(['watch', 'test-brain']);
|
|
290
|
-
|
|
291
|
-
// Should connect to watch the active run
|
|
292
|
-
const isOutputRendered = await waitForOutput(
|
|
293
|
-
/Connecting to watch service|Brain: test-brain/
|
|
294
|
-
);
|
|
295
|
-
expect(isOutputRendered).toBe(true);
|
|
296
|
-
|
|
297
|
-
// Verify API was called to get active runs
|
|
298
|
-
const calls = server.getLogs();
|
|
299
|
-
const activeRunsCall = calls.find(c => c.method === 'getBrainActiveRuns');
|
|
300
|
-
expect(activeRunsCall).toBeDefined();
|
|
301
|
-
expect(activeRunsCall?.args[0]).toBe('test-brain');
|
|
302
|
-
|
|
303
|
-
// Unmount the component to trigger EventSource cleanup
|
|
304
|
-
instance.unmount();
|
|
305
|
-
} finally {
|
|
306
|
-
await env.stopAndCleanup();
|
|
307
|
-
}
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
it('should show error when no active runs found for brain name', async () => {
|
|
311
|
-
const env = await createTestEnv();
|
|
312
|
-
const px = await env.start();
|
|
313
|
-
|
|
314
|
-
try {
|
|
315
|
-
const { waitForOutput } = await px(['watch', 'test-brain']);
|
|
316
|
-
|
|
317
|
-
// Should show no active runs error
|
|
318
|
-
const foundTitle = await waitForOutput(/No Active Runs/i, 30);
|
|
319
|
-
expect(foundTitle).toBe(true);
|
|
320
|
-
|
|
321
|
-
const foundMessage = await waitForOutput(/No currently running brain runs found for brain "test-brain"/i, 30);
|
|
322
|
-
expect(foundMessage).toBe(true);
|
|
323
|
-
|
|
324
|
-
const foundDetails = await waitForOutput(/positronic run test-brain/i, 30);
|
|
325
|
-
expect(foundDetails).toBe(true);
|
|
326
|
-
|
|
327
|
-
// Verify API was called
|
|
328
|
-
const calls = env.server.getLogs();
|
|
329
|
-
const activeRunsCall = calls.find(c => c.method === 'getBrainActiveRuns');
|
|
330
|
-
expect(activeRunsCall).toBeDefined();
|
|
331
|
-
} finally {
|
|
332
|
-
await env.stopAndCleanup();
|
|
333
|
-
}
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
it('should show error when multiple active runs found for brain name', async () => {
|
|
337
|
-
const env = await createTestEnv();
|
|
338
|
-
const { server } = env;
|
|
339
|
-
|
|
340
|
-
// Add multiple running brain runs
|
|
341
|
-
server.addBrainRun({
|
|
342
|
-
brainRunId: 'run-active-1',
|
|
343
|
-
brainTitle: 'test brain',
|
|
344
|
-
type: 'START',
|
|
345
|
-
status: 'RUNNING',
|
|
346
|
-
createdAt: Date.now() - 120000, // 2 minutes ago
|
|
347
|
-
startedAt: Date.now() - 120000,
|
|
348
|
-
});
|
|
349
|
-
|
|
350
|
-
server.addBrainRun({
|
|
351
|
-
brainRunId: 'run-active-2',
|
|
352
|
-
brainTitle: 'test brain',
|
|
353
|
-
type: 'START',
|
|
354
|
-
status: 'RUNNING',
|
|
355
|
-
createdAt: Date.now() - 60000, // 1 minute ago
|
|
356
|
-
startedAt: Date.now() - 60000,
|
|
357
|
-
});
|
|
358
|
-
|
|
359
|
-
const px = await env.start();
|
|
360
|
-
|
|
361
|
-
try {
|
|
362
|
-
const { waitForOutput } = await px(['watch', 'test-brain']);
|
|
363
|
-
|
|
364
|
-
// Should show multiple active runs error
|
|
365
|
-
const foundTitle = await waitForOutput(/Multiple Active Runs/i, 30);
|
|
366
|
-
expect(foundTitle).toBe(true);
|
|
367
|
-
|
|
368
|
-
const foundMessage = await waitForOutput(/Found 2 active runs for brain "test-brain"/i, 30);
|
|
369
|
-
expect(foundMessage).toBe(true);
|
|
370
|
-
|
|
371
|
-
const foundDetails = await waitForOutput(/positronic watch --run-id run-active-/i, 30);
|
|
372
|
-
expect(foundDetails).toBe(true);
|
|
373
|
-
} finally {
|
|
374
|
-
await env.stopAndCleanup();
|
|
375
|
-
}
|
|
376
|
-
});
|
|
377
|
-
|
|
378
|
-
it('should handle API errors when looking up active runs', async () => {
|
|
379
|
-
const env = await createTestEnv();
|
|
380
|
-
const px = await env.start();
|
|
381
|
-
|
|
382
|
-
try {
|
|
383
|
-
// Clear all existing nock interceptors to avoid conflicts
|
|
384
|
-
nock.cleanAll();
|
|
385
|
-
|
|
386
|
-
// Mock the server to return a 500 error for active-runs endpoint
|
|
387
|
-
const port = env.server.port;
|
|
388
|
-
nock(`http://localhost:${port}`)
|
|
389
|
-
.get(/^\/brains\/(.+)\/active-runs$/)
|
|
390
|
-
.reply(500, 'Internal Server Error');
|
|
391
|
-
|
|
392
|
-
const { waitForOutput } = await px(['watch', 'test-brain']);
|
|
393
|
-
|
|
394
|
-
// Should show API error
|
|
395
|
-
const foundTitle = await waitForOutput(/API Error/i, 30);
|
|
396
|
-
expect(foundTitle).toBe(true);
|
|
397
|
-
|
|
398
|
-
const foundMessage = await waitForOutput(/Failed to get active runs for brain "test-brain"/i, 30);
|
|
399
|
-
expect(foundMessage).toBe(true);
|
|
400
|
-
|
|
401
|
-
const foundDetails = await waitForOutput(/Server returned 500/i, 30);
|
|
402
|
-
expect(foundDetails).toBe(true);
|
|
403
|
-
} finally {
|
|
404
|
-
await env.stopAndCleanup();
|
|
405
|
-
}
|
|
406
|
-
});
|
|
407
|
-
|
|
408
|
-
it('should handle connection errors when looking up active runs', async () => {
|
|
409
|
-
const env = await createTestEnv();
|
|
410
|
-
// Don't start the server to simulate connection error
|
|
411
|
-
|
|
412
|
-
try {
|
|
413
|
-
const { waitForOutput } = await px(['watch', 'test-brain'], { server: env.server });
|
|
414
|
-
|
|
415
|
-
// Should show connection error
|
|
416
|
-
const foundTitle = await waitForOutput(/Connection Error/i, 30);
|
|
417
|
-
expect(foundTitle).toBe(true);
|
|
418
|
-
|
|
419
|
-
const foundMessage = await waitForOutput(/Error connecting to the local development server/i, 30);
|
|
420
|
-
expect(foundMessage).toBe(true);
|
|
421
|
-
|
|
422
|
-
const foundDetails = await waitForOutput(/positronic server/i, 30);
|
|
423
|
-
expect(foundDetails).toBe(true);
|
|
424
|
-
} finally {
|
|
425
|
-
env.cleanup();
|
|
426
|
-
}
|
|
427
|
-
});
|
|
428
|
-
|
|
429
|
-
it('should show error when no run ID or brain name provided', async () => {
|
|
430
|
-
const env = await createTestEnv();
|
|
431
|
-
const px = await env.start();
|
|
432
|
-
|
|
433
|
-
try {
|
|
434
|
-
// This will throw an error during yargs validation
|
|
435
|
-
await expect(px(['watch'])).rejects.toThrow(
|
|
436
|
-
'You must provide either a brain name or a --run-id'
|
|
437
|
-
);
|
|
438
|
-
} finally {
|
|
439
|
-
await env.stopAndCleanup();
|
|
440
|
-
}
|
|
441
|
-
});
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
it('should display all step statuses correctly', async () => {
|
|
445
|
-
const env = await createTestEnv();
|
|
446
|
-
const px = await env.start();
|
|
447
|
-
|
|
448
|
-
try {
|
|
449
|
-
const { waitForOutput, instance } = await px([
|
|
450
|
-
'watch',
|
|
451
|
-
'--run-id',
|
|
452
|
-
'test-multi-status',
|
|
453
|
-
]);
|
|
454
|
-
|
|
455
|
-
// Check for all different step statuses
|
|
456
|
-
const foundComplete = await waitForOutput(/✔.*Complete Step/);
|
|
457
|
-
expect(foundComplete).toBe(true);
|
|
458
|
-
|
|
459
|
-
const foundError = await waitForOutput(/•.*Error Step/);
|
|
460
|
-
expect(foundError).toBe(true);
|
|
461
|
-
|
|
462
|
-
const foundRunning = await waitForOutput(/•.*Running Step/);
|
|
463
|
-
expect(foundRunning).toBe(true);
|
|
464
|
-
|
|
465
|
-
const foundPending = await waitForOutput(/•.*Pending Step/);
|
|
466
|
-
expect(foundPending).toBe(true);
|
|
467
|
-
|
|
468
|
-
// Unmount the component to trigger EventSource cleanup
|
|
469
|
-
instance.unmount();
|
|
470
|
-
} finally {
|
|
471
|
-
await env.stopAndCleanup();
|
|
472
|
-
}
|
|
473
|
-
});
|
|
474
|
-
|
|
475
|
-
});
|
|
476
|
-
|
|
477
|
-
describe('brain list command', () => {
|
|
478
|
-
it('should list brains when no brains exist', async () => {
|
|
479
|
-
const env = await createTestEnv();
|
|
480
|
-
const px = await env.start();
|
|
481
|
-
|
|
482
|
-
try {
|
|
483
|
-
const { waitForOutput } = await px(['list']);
|
|
484
|
-
|
|
485
|
-
// Wait for the empty state message
|
|
486
|
-
const foundEmpty = await waitForOutput(/No brains found/i, 30);
|
|
487
|
-
expect(foundEmpty).toBe(true);
|
|
488
|
-
|
|
489
|
-
// Verify API call was made
|
|
490
|
-
const calls = env.server.getLogs();
|
|
491
|
-
const listCall = calls.find(c => c.method === 'getBrains');
|
|
492
|
-
expect(listCall).toBeDefined();
|
|
493
|
-
} finally {
|
|
494
|
-
await env.stopAndCleanup();
|
|
495
|
-
}
|
|
496
|
-
});
|
|
497
|
-
|
|
498
|
-
it('should list brains when brains exist', async () => {
|
|
499
|
-
const env = await createTestEnv();
|
|
500
|
-
const { server } = env;
|
|
501
|
-
|
|
502
|
-
// Add test brains before starting
|
|
503
|
-
server.addBrain({
|
|
504
|
-
name: 'daily-report',
|
|
505
|
-
title: 'Daily Report Generator',
|
|
506
|
-
description: 'Generates daily reports from various data sources',
|
|
507
|
-
createdAt: Date.now() - 86400000,
|
|
508
|
-
lastModified: Date.now() - 3600000,
|
|
509
|
-
});
|
|
510
|
-
|
|
511
|
-
server.addBrain({
|
|
512
|
-
name: 'data-processor',
|
|
513
|
-
title: 'Data Processing Pipeline',
|
|
514
|
-
description: 'Processes incoming data and transforms it',
|
|
515
|
-
createdAt: Date.now() - 172800000,
|
|
516
|
-
lastModified: Date.now() - 7200000,
|
|
517
|
-
});
|
|
518
|
-
|
|
519
|
-
const px = await env.start();
|
|
520
|
-
|
|
521
|
-
try {
|
|
522
|
-
const { waitForOutput, instance } = await px(['list']);
|
|
523
|
-
|
|
524
|
-
// Wait for brains to appear
|
|
525
|
-
const foundBrains = await waitForOutput(/daily-report/i, 30);
|
|
526
|
-
expect(foundBrains).toBe(true);
|
|
527
|
-
|
|
528
|
-
// Check that all data is shown
|
|
529
|
-
const output = instance.lastFrame() || '';
|
|
530
|
-
expect(output).toContain('daily-report');
|
|
531
|
-
expect(output).toContain('Daily Report Generator');
|
|
532
|
-
expect(output).toContain('data-processor');
|
|
533
|
-
expect(output).toContain('Data Processing Pipeline');
|
|
534
|
-
|
|
535
|
-
// Verify API call
|
|
536
|
-
const calls = server.getLogs();
|
|
537
|
-
const listCall = calls.find(c => c.method === 'getBrains');
|
|
538
|
-
expect(listCall).toBeDefined();
|
|
539
|
-
} finally {
|
|
540
|
-
await env.stopAndCleanup();
|
|
541
|
-
}
|
|
542
|
-
});
|
|
543
|
-
|
|
544
|
-
it('should handle API errors gracefully', async () => {
|
|
545
|
-
const env = await createTestEnv();
|
|
546
|
-
// Don't start the server to simulate connection error
|
|
547
|
-
const { waitForOutput } = await px(['list'], { server: env.server });
|
|
548
|
-
|
|
549
|
-
try {
|
|
550
|
-
const foundError = await waitForOutput(/Error connecting to the local development server/i, 30);
|
|
551
|
-
expect(foundError).toBe(true);
|
|
552
|
-
} finally {
|
|
553
|
-
env.cleanup();
|
|
554
|
-
}
|
|
555
|
-
});
|
|
556
|
-
});
|
|
557
|
-
|
|
558
|
-
describe('brain history command', () => {
|
|
559
|
-
it('should show empty history when no runs exist', async () => {
|
|
560
|
-
const env = await createTestEnv();
|
|
561
|
-
const px = await env.start();
|
|
562
|
-
|
|
563
|
-
try {
|
|
564
|
-
const { waitForOutput } = await px(['history', 'test-brain']);
|
|
565
|
-
const foundMessage = await waitForOutput(/No run history found for brain: test-brain/i, 30);
|
|
566
|
-
expect(foundMessage).toBe(true);
|
|
567
|
-
|
|
568
|
-
// Verify API call was made
|
|
569
|
-
const calls = env.server.getLogs();
|
|
570
|
-
const historyCall = calls.find(c => c.method === 'getBrainHistory');
|
|
571
|
-
expect(historyCall).toBeDefined();
|
|
572
|
-
expect(historyCall?.args[0]).toBe('test-brain');
|
|
573
|
-
} finally {
|
|
574
|
-
await env.stopAndCleanup();
|
|
575
|
-
}
|
|
576
|
-
});
|
|
577
|
-
|
|
578
|
-
it('should show brain run history when runs exist', async () => {
|
|
579
|
-
const env = await createTestEnv();
|
|
580
|
-
const { server } = env;
|
|
581
|
-
|
|
582
|
-
// Add some brain runs to history
|
|
583
|
-
server.addBrainRun({
|
|
584
|
-
brainRunId: 'run-123',
|
|
585
|
-
brainTitle: 'Test Brain',
|
|
586
|
-
brainDescription: 'A test brain',
|
|
587
|
-
type: 'START',
|
|
588
|
-
status: 'COMPLETE',
|
|
589
|
-
createdAt: Date.now() - 3600000, // 1 hour ago
|
|
590
|
-
startedAt: Date.now() - 3600000,
|
|
591
|
-
completedAt: Date.now() - 3540000, // 1 minute duration
|
|
592
|
-
});
|
|
593
|
-
|
|
594
|
-
server.addBrainRun({
|
|
595
|
-
brainRunId: 'run-456',
|
|
596
|
-
brainTitle: 'Test Brain',
|
|
597
|
-
type: 'START',
|
|
598
|
-
status: 'ERROR',
|
|
599
|
-
error: { message: 'Connection failed' },
|
|
600
|
-
createdAt: Date.now() - 7200000, // 2 hours ago
|
|
601
|
-
startedAt: Date.now() - 7200000,
|
|
602
|
-
});
|
|
603
|
-
|
|
604
|
-
const px = await env.start();
|
|
605
|
-
|
|
606
|
-
try {
|
|
607
|
-
const { waitForOutput } = await px(['history', 'test-brain']);
|
|
608
|
-
|
|
609
|
-
// Check for header
|
|
610
|
-
const foundHeader = await waitForOutput(/Recent runs for brain "test-brain"/i, 30);
|
|
611
|
-
expect(foundHeader).toBe(true);
|
|
612
|
-
|
|
613
|
-
// Check for run IDs
|
|
614
|
-
const foundRun1 = await waitForOutput(/run-123/i, 30);
|
|
615
|
-
expect(foundRun1).toBe(true);
|
|
616
|
-
|
|
617
|
-
const foundRun2 = await waitForOutput(/run-456/i, 30);
|
|
618
|
-
expect(foundRun2).toBe(true);
|
|
619
|
-
|
|
620
|
-
// Check for statuses
|
|
621
|
-
const foundComplete = await waitForOutput(/COMPLETE/i, 30);
|
|
622
|
-
expect(foundComplete).toBe(true);
|
|
623
|
-
|
|
624
|
-
const foundError = await waitForOutput(/ERROR/i, 30);
|
|
625
|
-
expect(foundError).toBe(true);
|
|
626
|
-
} finally {
|
|
627
|
-
await env.stopAndCleanup();
|
|
628
|
-
}
|
|
629
|
-
});
|
|
630
|
-
|
|
631
|
-
it('should respect custom limit parameter', async () => {
|
|
632
|
-
const env = await createTestEnv();
|
|
633
|
-
const px = await env.start();
|
|
634
|
-
|
|
635
|
-
try {
|
|
636
|
-
const { waitForOutput } = await px([
|
|
637
|
-
'history',
|
|
638
|
-
'test-brain',
|
|
639
|
-
'--limit',
|
|
640
|
-
'20',
|
|
641
|
-
]);
|
|
642
|
-
|
|
643
|
-
// Wait for the component to render
|
|
644
|
-
await waitForOutput(/run history|Recent runs/i, 30);
|
|
645
|
-
|
|
646
|
-
// Verify API was called with correct limit
|
|
647
|
-
const calls = env.server.getLogs();
|
|
648
|
-
const historyCall = calls.find(c => c.method === 'getBrainHistory');
|
|
649
|
-
expect(historyCall).toBeDefined();
|
|
650
|
-
expect(historyCall?.args[1]).toBe(20);
|
|
651
|
-
} finally {
|
|
652
|
-
await env.stopAndCleanup();
|
|
653
|
-
}
|
|
654
|
-
});
|
|
655
|
-
|
|
656
|
-
it('should show error details for failed runs', async () => {
|
|
657
|
-
const env = await createTestEnv();
|
|
658
|
-
const { server } = env;
|
|
659
|
-
|
|
660
|
-
// Add a failed brain run
|
|
661
|
-
server.addBrainRun({
|
|
662
|
-
brainRunId: 'run-error',
|
|
663
|
-
brainTitle: 'Test Brain',
|
|
664
|
-
type: 'START',
|
|
665
|
-
status: 'ERROR',
|
|
666
|
-
error: 'Connection timeout',
|
|
667
|
-
createdAt: Date.now() - 1800000,
|
|
668
|
-
startedAt: Date.now() - 1800000,
|
|
669
|
-
});
|
|
670
|
-
|
|
671
|
-
const px = await env.start();
|
|
672
|
-
|
|
673
|
-
try {
|
|
674
|
-
const { waitForOutput } = await px(['history', 'test-brain']);
|
|
675
|
-
|
|
676
|
-
// Check for error section
|
|
677
|
-
const foundErrors = await waitForOutput(/Errors:/i, 30);
|
|
678
|
-
expect(foundErrors).toBe(true);
|
|
679
|
-
|
|
680
|
-
// Check for error message
|
|
681
|
-
const foundErrorMsg = await waitForOutput(/Connection timeout/i, 30);
|
|
682
|
-
expect(foundErrorMsg).toBe(true);
|
|
683
|
-
} finally {
|
|
684
|
-
await env.stopAndCleanup();
|
|
685
|
-
}
|
|
686
|
-
});
|
|
687
|
-
|
|
688
|
-
it('should handle server connection errors', async () => {
|
|
689
|
-
const env = await createTestEnv();
|
|
690
|
-
// Don't start the server to simulate connection error
|
|
691
|
-
|
|
692
|
-
try {
|
|
693
|
-
const { waitForOutput } = await px(['history', 'test-brain'], { server: env.server });
|
|
694
|
-
|
|
695
|
-
const foundError = await waitForOutput(/Error connecting to the local development server/i, 30);
|
|
696
|
-
expect(foundError).toBe(true);
|
|
697
|
-
} finally {
|
|
698
|
-
env.cleanup();
|
|
699
|
-
}
|
|
700
|
-
});
|
|
701
|
-
|
|
702
|
-
it('should handle API server errors', async () => {
|
|
703
|
-
const env = await createTestEnv();
|
|
704
|
-
const px = await env.start();
|
|
705
|
-
|
|
706
|
-
try {
|
|
707
|
-
// Clear all existing nock interceptors to avoid conflicts
|
|
708
|
-
nock.cleanAll();
|
|
709
|
-
|
|
710
|
-
// Mock the server to return a 500 error
|
|
711
|
-
const port = env.server.port;
|
|
712
|
-
nock(`http://localhost:${port}`)
|
|
713
|
-
.get(/^\/brains\/(.+)\/history/)
|
|
714
|
-
.reply(500, 'Internal Server Error');
|
|
715
|
-
|
|
716
|
-
const { waitForOutput } = await px(['history', 'test-brain']);
|
|
717
|
-
|
|
718
|
-
// The ErrorComponent will display the error
|
|
719
|
-
const foundError = await waitForOutput(/Error:|Failed|500/i, 30);
|
|
720
|
-
expect(foundError).toBe(true);
|
|
721
|
-
} finally {
|
|
722
|
-
await env.stopAndCleanup();
|
|
723
|
-
}
|
|
724
|
-
});
|
|
725
|
-
});
|
|
726
|
-
|
|
727
|
-
describe('brain show command', () => {
|
|
728
|
-
it('should show brain structure when brain exists', async () => {
|
|
729
|
-
const env = await createTestEnv();
|
|
730
|
-
const { server } = env;
|
|
731
|
-
|
|
732
|
-
// Add a brain to the test server with structure
|
|
733
|
-
server.addBrain({
|
|
734
|
-
name: 'test-brain',
|
|
735
|
-
title: 'Test Brain',
|
|
736
|
-
description: 'A test brain for unit testing',
|
|
737
|
-
steps: [
|
|
738
|
-
{
|
|
739
|
-
type: 'step',
|
|
740
|
-
title: 'Initialize',
|
|
741
|
-
},
|
|
742
|
-
{
|
|
743
|
-
type: 'step',
|
|
744
|
-
title: 'Process Data',
|
|
745
|
-
},
|
|
746
|
-
{
|
|
747
|
-
type: 'brain',
|
|
748
|
-
title: 'Nested Analysis',
|
|
749
|
-
innerBrain: {
|
|
750
|
-
title: 'Inner Brain',
|
|
751
|
-
description: 'Performs nested analysis',
|
|
752
|
-
steps: [
|
|
753
|
-
{
|
|
754
|
-
type: 'step',
|
|
755
|
-
title: 'Analyze Subset',
|
|
756
|
-
},
|
|
757
|
-
],
|
|
758
|
-
},
|
|
759
|
-
},
|
|
760
|
-
],
|
|
761
|
-
});
|
|
762
|
-
|
|
763
|
-
const px = await env.start();
|
|
764
|
-
|
|
765
|
-
try {
|
|
766
|
-
const { waitForOutput } = await px(['show', 'test-brain']);
|
|
767
|
-
|
|
768
|
-
// Check for brain title
|
|
769
|
-
const foundTitle = await waitForOutput(/Test Brain/, 30);
|
|
770
|
-
expect(foundTitle).toBe(true);
|
|
771
|
-
|
|
772
|
-
// Check for description
|
|
773
|
-
const foundDescription = await waitForOutput(/A test brain for unit testing/, 30);
|
|
774
|
-
expect(foundDescription).toBe(true);
|
|
775
|
-
|
|
776
|
-
// Check for steps
|
|
777
|
-
const foundSteps = await waitForOutput(/• Initialize/, 30);
|
|
778
|
-
expect(foundSteps).toBe(true);
|
|
779
|
-
} finally {
|
|
780
|
-
await env.stopAndCleanup();
|
|
781
|
-
}
|
|
782
|
-
});
|
|
783
|
-
|
|
784
|
-
it('should show error when brain does not exist', async () => {
|
|
785
|
-
const env = await createTestEnv();
|
|
786
|
-
const px = await env.start();
|
|
787
|
-
|
|
788
|
-
try {
|
|
789
|
-
const { waitForOutput } = await px(['show', 'non-existent-brain']);
|
|
790
|
-
const foundError = await waitForOutput(/Brain 'non-existent-brain' not found/, 30);
|
|
791
|
-
expect(foundError).toBe(true);
|
|
792
|
-
} finally {
|
|
793
|
-
await env.stopAndCleanup();
|
|
794
|
-
}
|
|
795
|
-
});
|
|
796
|
-
});
|
|
797
|
-
|
|
798
|
-
describe('brain rerun command', () => {
|
|
799
|
-
it('should successfully rerun a brain without specific run ID', async () => {
|
|
800
|
-
const env = await createTestEnv();
|
|
801
|
-
const px = await env.start();
|
|
802
|
-
|
|
803
|
-
try {
|
|
804
|
-
const { waitForOutput } = await px(['rerun', 'test-brain']);
|
|
805
|
-
|
|
806
|
-
// Check for success message
|
|
807
|
-
const foundSuccess = await waitForOutput(/Brain rerun started successfully/i, 30);
|
|
808
|
-
expect(foundSuccess).toBe(true);
|
|
809
|
-
|
|
810
|
-
// Check for new run ID
|
|
811
|
-
const foundRunId = await waitForOutput(/New run ID:.*rerun-/i, 30);
|
|
812
|
-
expect(foundRunId).toBe(true);
|
|
813
|
-
|
|
814
|
-
// Check for descriptive text
|
|
815
|
-
const foundDescription = await waitForOutput(/Rerunning brain "test-brain"/i, 30);
|
|
816
|
-
expect(foundDescription).toBe(true);
|
|
817
|
-
|
|
818
|
-
// Check for watch command suggestion
|
|
819
|
-
const foundWatchSuggestion = await waitForOutput(/Watch the run with: positronic watch --run-id/i, 30);
|
|
820
|
-
expect(foundWatchSuggestion).toBe(true);
|
|
821
|
-
|
|
822
|
-
// Verify API call
|
|
823
|
-
const calls = env.server.getLogs();
|
|
824
|
-
const rerunCall = calls.find(c => c.method === 'rerunBrain');
|
|
825
|
-
expect(rerunCall).toBeDefined();
|
|
826
|
-
expect(rerunCall?.args[0]).toBe('test-brain');
|
|
827
|
-
expect(rerunCall?.args[1]).toBeUndefined(); // no runId
|
|
828
|
-
} finally {
|
|
829
|
-
await env.stopAndCleanup();
|
|
830
|
-
}
|
|
831
|
-
});
|
|
832
|
-
|
|
833
|
-
it('should successfully rerun a brain with specific run ID', async () => {
|
|
834
|
-
const env = await createTestEnv();
|
|
835
|
-
const px = await env.start();
|
|
836
|
-
|
|
837
|
-
try {
|
|
838
|
-
const { waitForOutput } = await px(['rerun', 'test-brain', 'run-123']);
|
|
839
|
-
|
|
840
|
-
// Check for success message
|
|
841
|
-
const foundSuccess = await waitForOutput(/Brain rerun started successfully/i, 30);
|
|
842
|
-
expect(foundSuccess).toBe(true);
|
|
843
|
-
|
|
844
|
-
// Check for run details
|
|
845
|
-
const foundRunDetails = await waitForOutput(/from run run-123/i, 30);
|
|
846
|
-
expect(foundRunDetails).toBe(true);
|
|
847
|
-
|
|
848
|
-
// Verify API call
|
|
849
|
-
const calls = env.server.getLogs();
|
|
850
|
-
const rerunCall = calls.find(c => c.method === 'rerunBrain');
|
|
851
|
-
expect(rerunCall).toBeDefined();
|
|
852
|
-
expect(rerunCall?.args[0]).toBe('test-brain');
|
|
853
|
-
expect(rerunCall?.args[1]).toBe('run-123');
|
|
854
|
-
} finally {
|
|
855
|
-
await env.stopAndCleanup();
|
|
856
|
-
}
|
|
857
|
-
});
|
|
858
|
-
|
|
859
|
-
it('should successfully rerun a brain with step range options', async () => {
|
|
860
|
-
const env = await createTestEnv();
|
|
861
|
-
const px = await env.start();
|
|
862
|
-
|
|
863
|
-
try {
|
|
864
|
-
const { waitForOutput } = await px([
|
|
865
|
-
'rerun',
|
|
866
|
-
'test-brain',
|
|
867
|
-
'--starts-at',
|
|
868
|
-
'3',
|
|
869
|
-
'--stops-after',
|
|
870
|
-
'5',
|
|
871
|
-
]);
|
|
872
|
-
|
|
873
|
-
// Check for success message
|
|
874
|
-
const foundSuccess = await waitForOutput(/Brain rerun started successfully/i, 30);
|
|
875
|
-
expect(foundSuccess).toBe(true);
|
|
876
|
-
|
|
877
|
-
// Check for step range details
|
|
878
|
-
const foundStepRange = await waitForOutput(/starting at step 3, stopping after step 5/i, 30);
|
|
879
|
-
expect(foundStepRange).toBe(true);
|
|
880
|
-
|
|
881
|
-
// Verify API call
|
|
882
|
-
const calls = env.server.getLogs();
|
|
883
|
-
const rerunCall = calls.find(c => c.method === 'rerunBrain');
|
|
884
|
-
expect(rerunCall).toBeDefined();
|
|
885
|
-
expect(rerunCall?.args[0]).toBe('test-brain');
|
|
886
|
-
expect(rerunCall?.args[1]).toBeUndefined(); // no runId
|
|
887
|
-
expect(rerunCall?.args[2]).toBe(3); // startsAt
|
|
888
|
-
expect(rerunCall?.args[3]).toBe(5); // stopsAfter
|
|
889
|
-
} finally {
|
|
890
|
-
await env.stopAndCleanup();
|
|
891
|
-
}
|
|
892
|
-
});
|
|
893
|
-
|
|
894
|
-
it('should handle brain not found error', async () => {
|
|
895
|
-
const env = await createTestEnv();
|
|
896
|
-
const px = await env.start();
|
|
897
|
-
|
|
898
|
-
try {
|
|
899
|
-
const { waitForOutput } = await px(['rerun', 'non-existent-brain']);
|
|
900
|
-
|
|
901
|
-
// Check for error title
|
|
902
|
-
const foundErrorTitle = await waitForOutput(/Brain Rerun Failed/i, 30);
|
|
903
|
-
expect(foundErrorTitle).toBe(true);
|
|
904
|
-
|
|
905
|
-
// Check for error message
|
|
906
|
-
const foundErrorMessage = await waitForOutput(/Brain 'non-existent-brain' not found/i, 30);
|
|
907
|
-
expect(foundErrorMessage).toBe(true);
|
|
908
|
-
|
|
909
|
-
// Check for helpful details
|
|
910
|
-
const foundDetails = await waitForOutput(/positronic brain list/i, 30);
|
|
911
|
-
expect(foundDetails).toBe(true);
|
|
912
|
-
} finally {
|
|
913
|
-
await env.stopAndCleanup();
|
|
914
|
-
}
|
|
915
|
-
});
|
|
916
|
-
|
|
917
|
-
it('should handle run ID not found error', async () => {
|
|
918
|
-
const env = await createTestEnv();
|
|
919
|
-
const px = await env.start();
|
|
920
|
-
|
|
921
|
-
try {
|
|
922
|
-
const { waitForOutput } = await px(['rerun', 'test-brain', 'non-existent-run']);
|
|
923
|
-
|
|
924
|
-
// Check for error title
|
|
925
|
-
const foundErrorTitle = await waitForOutput(/Brain Rerun Failed/i, 30);
|
|
926
|
-
expect(foundErrorTitle).toBe(true);
|
|
927
|
-
|
|
928
|
-
// Check for error message
|
|
929
|
-
const foundErrorMessage = await waitForOutput(/Brain run 'non-existent-run' not found/i, 30);
|
|
930
|
-
expect(foundErrorMessage).toBe(true);
|
|
931
|
-
|
|
932
|
-
// Check for helpful details with runId
|
|
933
|
-
const foundDetails = await waitForOutput(/positronic brain history test-brain/i, 30);
|
|
934
|
-
expect(foundDetails).toBe(true);
|
|
935
|
-
} finally {
|
|
936
|
-
await env.stopAndCleanup();
|
|
937
|
-
}
|
|
938
|
-
});
|
|
939
|
-
|
|
940
|
-
it('should handle server connection errors', async () => {
|
|
941
|
-
const env = await createTestEnv();
|
|
942
|
-
// Don't start the server to simulate connection error
|
|
943
|
-
|
|
944
|
-
try {
|
|
945
|
-
const { waitForOutput } = await px(['rerun', 'test-brain'], { server: env.server });
|
|
946
|
-
|
|
947
|
-
// Check for error title
|
|
948
|
-
const foundErrorTitle = await waitForOutput(/Brain Rerun Failed/i, 30);
|
|
949
|
-
expect(foundErrorTitle).toBe(true);
|
|
950
|
-
|
|
951
|
-
// Check for connection error
|
|
952
|
-
const foundConnectionError = await waitForOutput(/Connection error/i, 30);
|
|
953
|
-
expect(foundConnectionError).toBe(true);
|
|
954
|
-
} finally {
|
|
955
|
-
env.cleanup();
|
|
956
|
-
}
|
|
957
|
-
});
|
|
958
|
-
|
|
959
|
-
it('should handle API server errors', async () => {
|
|
960
|
-
const env = await createTestEnv();
|
|
961
|
-
const px = await env.start();
|
|
962
|
-
|
|
963
|
-
try {
|
|
964
|
-
// Clear all existing nock interceptors to avoid conflicts
|
|
965
|
-
nock.cleanAll();
|
|
966
|
-
|
|
967
|
-
// Mock the server to return a 500 error
|
|
968
|
-
const port = env.server.port;
|
|
969
|
-
nock(`http://localhost:${port}`)
|
|
970
|
-
.post('/brains/runs/rerun')
|
|
971
|
-
.reply(500, 'Internal Server Error');
|
|
972
|
-
|
|
973
|
-
const { waitForOutput } = await px(['rerun', 'test-brain']);
|
|
974
|
-
|
|
975
|
-
// Check for error title
|
|
976
|
-
const foundErrorTitle = await waitForOutput(/Brain Rerun Failed/i, 30);
|
|
977
|
-
expect(foundErrorTitle).toBe(true);
|
|
978
|
-
|
|
979
|
-
// Check for server error
|
|
980
|
-
const foundServerError = await waitForOutput(/Server returned 500/i, 30);
|
|
981
|
-
expect(foundServerError).toBe(true);
|
|
982
|
-
} finally {
|
|
983
|
-
await env.stopAndCleanup();
|
|
984
|
-
}
|
|
985
|
-
});
|
|
986
|
-
});
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
describe('error handling', () => {
|
|
990
|
-
it('should handle missing brain name for run command', async () => {
|
|
991
|
-
const env = await createTestEnv();
|
|
992
|
-
const px = await env.start();
|
|
993
|
-
|
|
994
|
-
try {
|
|
995
|
-
// This will throw an error during yargs validation
|
|
996
|
-
await expect(px(['run'])).rejects.toThrow(
|
|
997
|
-
'Not enough non-option arguments: got 0, need at least 1'
|
|
998
|
-
);
|
|
999
|
-
} finally {
|
|
1000
|
-
await env.stopAndCleanup();
|
|
1001
|
-
}
|
|
1002
|
-
});
|
|
1003
|
-
});
|
|
1004
|
-
});
|