@positronic/cli 0.0.2 → 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.
Files changed (83) hide show
  1. package/dist/src/cli.js +16 -1
  2. package/dist/src/commands/helpers.js +11 -25
  3. package/dist/types/cli.d.ts.map +1 -1
  4. package/dist/types/commands/helpers.d.ts.map +1 -1
  5. package/package.json +11 -4
  6. package/dist/src/commands/brain.test.js +0 -2936
  7. package/dist/src/commands/helpers.test.js +0 -832
  8. package/dist/src/commands/project.test.js +0 -1201
  9. package/dist/src/commands/resources.test.js +0 -2511
  10. package/dist/src/commands/schedule.test.js +0 -1235
  11. package/dist/src/commands/secret.test.d.js +0 -1
  12. package/dist/src/commands/secret.test.js +0 -761
  13. package/dist/src/commands/server.test.js +0 -1237
  14. package/dist/src/commands/test-utils.js +0 -737
  15. package/dist/src/components/secret-sync.js +0 -303
  16. package/dist/src/test/mock-api-client.js +0 -371
  17. package/dist/src/test/test-dev-server.js +0 -1376
  18. package/dist/types/commands/test-utils.d.ts +0 -45
  19. package/dist/types/commands/test-utils.d.ts.map +0 -1
  20. package/dist/types/components/secret-sync.d.ts +0 -9
  21. package/dist/types/components/secret-sync.d.ts.map +0 -1
  22. package/dist/types/test/mock-api-client.d.ts +0 -25
  23. package/dist/types/test/mock-api-client.d.ts.map +0 -1
  24. package/dist/types/test/test-dev-server.d.ts +0 -129
  25. package/dist/types/test/test-dev-server.d.ts.map +0 -1
  26. package/src/cli.ts +0 -981
  27. package/src/commands/backend.ts +0 -63
  28. package/src/commands/brain.test.ts +0 -1004
  29. package/src/commands/brain.ts +0 -215
  30. package/src/commands/helpers.test.ts +0 -487
  31. package/src/commands/helpers.ts +0 -870
  32. package/src/commands/project-config-manager.ts +0 -152
  33. package/src/commands/project.test.ts +0 -502
  34. package/src/commands/project.ts +0 -109
  35. package/src/commands/resources.test.ts +0 -1052
  36. package/src/commands/resources.ts +0 -97
  37. package/src/commands/schedule.test.ts +0 -481
  38. package/src/commands/schedule.ts +0 -65
  39. package/src/commands/secret.test.ts +0 -210
  40. package/src/commands/secret.ts +0 -50
  41. package/src/commands/server.test.ts +0 -493
  42. package/src/commands/server.ts +0 -353
  43. package/src/commands/test-utils.ts +0 -324
  44. package/src/components/brain-history.tsx +0 -198
  45. package/src/components/brain-list.tsx +0 -105
  46. package/src/components/brain-rerun.tsx +0 -111
  47. package/src/components/brain-show.tsx +0 -92
  48. package/src/components/error.tsx +0 -24
  49. package/src/components/project-add.tsx +0 -59
  50. package/src/components/project-create.tsx +0 -83
  51. package/src/components/project-list.tsx +0 -83
  52. package/src/components/project-remove.tsx +0 -55
  53. package/src/components/project-select.tsx +0 -200
  54. package/src/components/project-show.tsx +0 -58
  55. package/src/components/resource-clear.tsx +0 -127
  56. package/src/components/resource-delete.tsx +0 -160
  57. package/src/components/resource-list.tsx +0 -177
  58. package/src/components/resource-sync.tsx +0 -170
  59. package/src/components/resource-types.tsx +0 -55
  60. package/src/components/resource-upload.tsx +0 -182
  61. package/src/components/schedule-create.tsx +0 -90
  62. package/src/components/schedule-delete.tsx +0 -116
  63. package/src/components/schedule-list.tsx +0 -186
  64. package/src/components/schedule-runs.tsx +0 -151
  65. package/src/components/secret-bulk.tsx +0 -79
  66. package/src/components/secret-create.tsx +0 -49
  67. package/src/components/secret-delete.tsx +0 -41
  68. package/src/components/secret-list.tsx +0 -41
  69. package/src/components/watch.tsx +0 -155
  70. package/src/hooks/useApi.ts +0 -183
  71. package/src/positronic.ts +0 -40
  72. package/src/test/data/resources/config.json +0 -1
  73. package/src/test/data/resources/data/config.json +0 -1
  74. package/src/test/data/resources/data/logo.png +0 -2
  75. package/src/test/data/resources/docs/api.md +0 -3
  76. package/src/test/data/resources/docs/readme.md +0 -3
  77. package/src/test/data/resources/example.md +0 -3
  78. package/src/test/data/resources/file with spaces.txt +0 -1
  79. package/src/test/data/resources/readme.md +0 -3
  80. package/src/test/data/resources/test.txt +0 -1
  81. package/src/test/mock-api-client.ts +0 -145
  82. package/src/test/test-dev-server.ts +0 -1003
  83. package/tsconfig.json +0 -11
@@ -1,1237 +0,0 @@
1
- function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
2
- try {
3
- var info = gen[key](arg);
4
- var value = info.value;
5
- } catch (error) {
6
- reject(error);
7
- return;
8
- }
9
- if (info.done) {
10
- resolve(value);
11
- } else {
12
- Promise.resolve(value).then(_next, _throw);
13
- }
14
- }
15
- function _async_to_generator(fn) {
16
- return function() {
17
- var self = this, args = arguments;
18
- return new Promise(function(resolve, reject) {
19
- var gen = fn.apply(self, args);
20
- function _next(value) {
21
- asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
22
- }
23
- function _throw(err) {
24
- asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
25
- }
26
- _next(undefined);
27
- });
28
- };
29
- }
30
- function _ts_generator(thisArg, body) {
31
- var f, y, t, _ = {
32
- label: 0,
33
- sent: function() {
34
- if (t[0] & 1) throw t[1];
35
- return t[1];
36
- },
37
- trys: [],
38
- ops: []
39
- }, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
40
- return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() {
41
- return this;
42
- }), g;
43
- function verb(n) {
44
- return function(v) {
45
- return step([
46
- n,
47
- v
48
- ]);
49
- };
50
- }
51
- function step(op) {
52
- if (f) throw new TypeError("Generator is already executing.");
53
- while(g && (g = 0, op[0] && (_ = 0)), _)try {
54
- if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
55
- if (y = 0, t) op = [
56
- op[0] & 2,
57
- t.value
58
- ];
59
- switch(op[0]){
60
- case 0:
61
- case 1:
62
- t = op;
63
- break;
64
- case 4:
65
- _.label++;
66
- return {
67
- value: op[1],
68
- done: false
69
- };
70
- case 5:
71
- _.label++;
72
- y = op[1];
73
- op = [
74
- 0
75
- ];
76
- continue;
77
- case 7:
78
- op = _.ops.pop();
79
- _.trys.pop();
80
- continue;
81
- default:
82
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
83
- _ = 0;
84
- continue;
85
- }
86
- if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) {
87
- _.label = op[1];
88
- break;
89
- }
90
- if (op[0] === 6 && _.label < t[1]) {
91
- _.label = t[1];
92
- t = op;
93
- break;
94
- }
95
- if (t && _.label < t[2]) {
96
- _.label = t[2];
97
- _.ops.push(op);
98
- break;
99
- }
100
- if (t[2]) _.ops.pop();
101
- _.trys.pop();
102
- continue;
103
- }
104
- op = body.call(thisArg, _);
105
- } catch (e) {
106
- op = [
107
- 6,
108
- e
109
- ];
110
- y = 0;
111
- } finally{
112
- f = t = 0;
113
- }
114
- if (op[0] & 5) throw op[1];
115
- return {
116
- value: op[0] ? op[1] : void 0,
117
- done: true
118
- };
119
- }
120
- }
121
- import * as fs from 'fs';
122
- import * as path from 'path';
123
- import { describe, it, expect, jest } from '@jest/globals';
124
- import { createTestEnv, waitForTypesFile, px } from './test-utils.js';
125
- describe('CLI Integration: positronic server', function() {
126
- var exitSpy;
127
- var env;
128
- beforeEach(function() {
129
- return _async_to_generator(function() {
130
- return _ts_generator(this, function(_state) {
131
- switch(_state.label){
132
- case 0:
133
- return [
134
- 4,
135
- createTestEnv()
136
- ];
137
- case 1:
138
- // Stub process.exit so cleanup doesn't terminate Jest
139
- env = _state.sent();
140
- exitSpy = jest.spyOn(process, 'exit').mockImplementation(function() {
141
- return undefined;
142
- });
143
- return [
144
- 2
145
- ];
146
- }
147
- });
148
- })();
149
- });
150
- afterEach(function() {
151
- return _async_to_generator(function() {
152
- return _ts_generator(this, function(_state) {
153
- env.cleanup();
154
- exitSpy.mockRestore();
155
- return [
156
- 2
157
- ];
158
- });
159
- })();
160
- });
161
- describe('Project validation', function() {
162
- it('should not have server command available outside a Positronic project', function() {
163
- return _async_to_generator(function() {
164
- var error;
165
- return _ts_generator(this, function(_state) {
166
- switch(_state.label){
167
- case 0:
168
- _state.trys.push([
169
- 0,
170
- 2,
171
- ,
172
- 3
173
- ]);
174
- return [
175
- 4,
176
- px([
177
- 'server'
178
- ])
179
- ];
180
- case 1:
181
- _state.sent();
182
- // If we get here, the command didn't fail as expected
183
- expect(false).toBe(true); // Force failure
184
- return [
185
- 3,
186
- 3
187
- ];
188
- case 2:
189
- error = _state.sent();
190
- // Check that the error message indicates unknown command
191
- expect(error.message).toContain('Unknown command: server');
192
- return [
193
- 3,
194
- 3
195
- ];
196
- case 3:
197
- return [
198
- 2
199
- ];
200
- }
201
- });
202
- })();
203
- });
204
- });
205
- describe('Server lifecycle', function() {
206
- it('should call setup() and start() methods on the dev server', function() {
207
- return _async_to_generator(function() {
208
- var server, methodCalls, setupCall, startCall;
209
- return _ts_generator(this, function(_state) {
210
- switch(_state.label){
211
- case 0:
212
- server = env.server;
213
- return [
214
- 4,
215
- px([
216
- 'server'
217
- ], {
218
- server: server
219
- })
220
- ];
221
- case 1:
222
- _state.sent();
223
- _state.label = 2;
224
- case 2:
225
- _state.trys.push([
226
- 2,
227
- ,
228
- 3,
229
- 5
230
- ]);
231
- methodCalls = server.getLogs();
232
- // Verify the method calls
233
- setupCall = methodCalls.find(function(call) {
234
- return call.method === 'setup';
235
- });
236
- startCall = methodCalls.find(function(call) {
237
- return call.method === 'start';
238
- });
239
- expect(setupCall).toBeDefined();
240
- expect(setupCall.args[0]).toBe(false); // force flag not set
241
- expect(startCall).toBeDefined();
242
- return [
243
- 3,
244
- 5
245
- ];
246
- case 3:
247
- process.emit('SIGINT');
248
- return [
249
- 4,
250
- new Promise(function(r) {
251
- return setImmediate(r);
252
- })
253
- ];
254
- case 4:
255
- _state.sent();
256
- return [
257
- 7
258
- ];
259
- case 5:
260
- return [
261
- 2
262
- ];
263
- }
264
- });
265
- })();
266
- });
267
- });
268
- describe('Initial sync tests', function() {
269
- it('should sync resources after server starts', function() {
270
- return _async_to_generator(function() {
271
- var server, uploads;
272
- return _ts_generator(this, function(_state) {
273
- switch(_state.label){
274
- case 0:
275
- server = env.server;
276
- // Start the CLI's server command which will sync resources
277
- return [
278
- 4,
279
- px([
280
- 'server'
281
- ], {
282
- server: server
283
- })
284
- ];
285
- case 1:
286
- _state.sent();
287
- _state.label = 2;
288
- case 2:
289
- _state.trys.push([
290
- 2,
291
- ,
292
- 3,
293
- 5
294
- ]);
295
- // Verify that the CLI attempted to upload both default resources
296
- uploads = server.getLogs().filter(function(c) {
297
- return c.method === 'upload';
298
- }).map(function(c) {
299
- return typeof c.args[0] === 'string' ? c.args[0] : '';
300
- });
301
- expect(uploads.length).toBe(9);
302
- // The multipart body should include the key/path for each resource
303
- expect(uploads.some(function(b) {
304
- return b.includes('config.json');
305
- })).toBe(true);
306
- expect(uploads.some(function(b) {
307
- return b.includes('data/config.json');
308
- })).toBe(true);
309
- expect(uploads.some(function(b) {
310
- return b.includes('data/logo.png');
311
- })).toBe(true);
312
- expect(uploads.some(function(b) {
313
- return b.includes('docs/api.md');
314
- })).toBe(true);
315
- expect(uploads.some(function(b) {
316
- return b.includes('docs/readme.md');
317
- })).toBe(true);
318
- expect(uploads.some(function(b) {
319
- return b.includes('example.md');
320
- })).toBe(true);
321
- expect(uploads.some(function(b) {
322
- return b.includes('file with spaces.txt');
323
- })).toBe(true);
324
- expect(uploads.some(function(b) {
325
- return b.includes('readme.md');
326
- })).toBe(true);
327
- expect(uploads.some(function(b) {
328
- return b.includes('test.txt');
329
- })).toBe(true);
330
- return [
331
- 3,
332
- 5
333
- ];
334
- case 3:
335
- process.emit('SIGINT');
336
- return [
337
- 4,
338
- new Promise(function(r) {
339
- return setImmediate(r);
340
- })
341
- ];
342
- case 4:
343
- _state.sent();
344
- return [
345
- 7
346
- ];
347
- case 5:
348
- return [
349
- 2
350
- ];
351
- }
352
- });
353
- })();
354
- });
355
- it('should generate types file after server starts', function() {
356
- return _async_to_generator(function() {
357
- var server, typesPath, typesContent;
358
- return _ts_generator(this, function(_state) {
359
- switch(_state.label){
360
- case 0:
361
- // Stub process.exit so cleanup doesn't terminate Jest
362
- server = env.server;
363
- return [
364
- 4,
365
- px([
366
- 'server'
367
- ], {
368
- server: server
369
- })
370
- ];
371
- case 1:
372
- _state.sent();
373
- _state.label = 2;
374
- case 2:
375
- _state.trys.push([
376
- 2,
377
- ,
378
- 4,
379
- 6
380
- ]);
381
- // Wait for types file to be generated with our resources
382
- typesPath = path.join(server.projectRootDir, 'resources.d.ts');
383
- return [
384
- 4,
385
- waitForTypesFile(typesPath, [
386
- 'readme: TextResource;',
387
- 'config: TextResource;',
388
- 'api: TextResource;'
389
- ])
390
- ];
391
- case 3:
392
- typesContent = _state.sent();
393
- // Check that the types file was generated with content
394
- expect(typesContent).not.toBe('');
395
- // Check for the module declaration
396
- expect(typesContent).toContain("declare module '@positronic/core'");
397
- // Check for resource type definitions
398
- expect(typesContent).toContain('interface TextResource');
399
- expect(typesContent).toContain('interface BinaryResource');
400
- expect(typesContent).toContain('interface Resources');
401
- // Check for the specific resources we created
402
- expect(typesContent).toContain('readme: TextResource;');
403
- expect(typesContent).toContain('config: TextResource;');
404
- expect(typesContent).toContain('docs: {');
405
- expect(typesContent).toContain('api: TextResource;');
406
- return [
407
- 3,
408
- 6
409
- ];
410
- case 4:
411
- // Trigger the cleanup path in ServerCommand to close file watchers
412
- process.emit('SIGINT');
413
- return [
414
- 4,
415
- new Promise(function(r) {
416
- return setImmediate(r);
417
- })
418
- ];
419
- case 5:
420
- _state.sent();
421
- return [
422
- 7
423
- ];
424
- case 6:
425
- return [
426
- 2
427
- ];
428
- }
429
- });
430
- })();
431
- });
432
- });
433
- describe('Error handling', function() {
434
- it('should handle server startup errors gracefully', function() {
435
- return _async_to_generator(function() {
436
- var server, originalStart, errorCallback, consoleErrorSpy;
437
- return _ts_generator(this, function(_state) {
438
- switch(_state.label){
439
- case 0:
440
- server = env.server;
441
- // Mock the server to emit an error after start
442
- originalStart = server.start.bind(server);
443
- server.start = jest.fn(function(port) {
444
- return _async_to_generator(function() {
445
- var handle, originalOnError;
446
- return _ts_generator(this, function(_state) {
447
- switch(_state.label){
448
- case 0:
449
- return [
450
- 4,
451
- originalStart(port)
452
- ];
453
- case 1:
454
- handle = _state.sent();
455
- // Intercept the onError callback
456
- originalOnError = handle.onError.bind(handle);
457
- handle.onError = function(callback) {
458
- errorCallback = callback;
459
- originalOnError(callback);
460
- };
461
- // Emit error after a short delay
462
- setTimeout(function() {
463
- if (errorCallback) {
464
- errorCallback(new Error('Mock server error'));
465
- }
466
- }, 10);
467
- return [
468
- 2,
469
- handle
470
- ];
471
- }
472
- });
473
- })();
474
- });
475
- // Use a console spy to capture error output
476
- consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(function() {});
477
- _state.label = 1;
478
- case 1:
479
- _state.trys.push([
480
- 1,
481
- ,
482
- 4,
483
- 5
484
- ]);
485
- return [
486
- 4,
487
- px([
488
- 'server'
489
- ], {
490
- server: server
491
- })
492
- ];
493
- case 2:
494
- _state.sent();
495
- // Wait for error to be logged
496
- return [
497
- 4,
498
- new Promise(function(resolve) {
499
- return setTimeout(resolve, 50);
500
- })
501
- ];
502
- case 3:
503
- _state.sent();
504
- // Verify error was logged
505
- expect(consoleErrorSpy).toHaveBeenCalledWith('Failed to start dev server:', expect.any(Error));
506
- // Verify process.exit was called
507
- expect(exitSpy).toHaveBeenCalledWith(1);
508
- return [
509
- 3,
510
- 5
511
- ];
512
- case 4:
513
- consoleErrorSpy.mockRestore();
514
- return [
515
- 7
516
- ];
517
- case 5:
518
- return [
519
- 2
520
- ];
521
- }
522
- });
523
- })();
524
- });
525
- it('should handle server timeout and exit appropriately', function() {
526
- return _async_to_generator(function() {
527
- var server, originalStart, consoleErrorSpy, methodCalls, startCall;
528
- return _ts_generator(this, function(_state) {
529
- switch(_state.label){
530
- case 0:
531
- server = env.server;
532
- // Mock the server handle to simulate timeout
533
- originalStart = server.start.bind(server);
534
- server.start = jest.fn(function(port) {
535
- return _async_to_generator(function() {
536
- var handle;
537
- return _ts_generator(this, function(_state) {
538
- switch(_state.label){
539
- case 0:
540
- return [
541
- 4,
542
- originalStart(port)
543
- ];
544
- case 1:
545
- handle = _state.sent();
546
- // Override waitUntilReady to always return false (timeout)
547
- handle.waitUntilReady = jest.fn().mockResolvedValue(false);
548
- return [
549
- 2,
550
- handle
551
- ];
552
- }
553
- });
554
- })();
555
- });
556
- consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(function() {});
557
- _state.label = 1;
558
- case 1:
559
- _state.trys.push([
560
- 1,
561
- ,
562
- 3,
563
- 4
564
- ]);
565
- return [
566
- 4,
567
- px([
568
- 'server'
569
- ], {
570
- server: server
571
- })
572
- ];
573
- case 2:
574
- _state.sent();
575
- // Verify timeout message was logged
576
- expect(consoleErrorSpy).toHaveBeenCalledWith('⚠️ Server startup timeout: The server is taking longer than expected to initialize.');
577
- // Verify process.exit was called
578
- expect(exitSpy).toHaveBeenCalledWith(1);
579
- // Verify server was killed
580
- methodCalls = server.getLogs();
581
- startCall = methodCalls.find(function(call) {
582
- return call.method === 'start';
583
- });
584
- expect(startCall).toBeDefined();
585
- return [
586
- 3,
587
- 4
588
- ];
589
- case 3:
590
- consoleErrorSpy.mockRestore();
591
- return [
592
- 7
593
- ];
594
- case 4:
595
- return [
596
- 2
597
- ];
598
- }
599
- });
600
- })();
601
- });
602
- it('should handle resource sync with successful upload count', function() {
603
- return _async_to_generator(function() {
604
- var server, consoleLogSpy, successLogCall;
605
- return _ts_generator(this, function(_state) {
606
- switch(_state.label){
607
- case 0:
608
- server = env.server;
609
- consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(function() {});
610
- _state.label = 1;
611
- case 1:
612
- _state.trys.push([
613
- 1,
614
- ,
615
- 4,
616
- 6
617
- ]);
618
- return [
619
- 4,
620
- px([
621
- 'server'
622
- ], {
623
- server: server
624
- })
625
- ];
626
- case 2:
627
- _state.sent();
628
- // Wait for sync to complete
629
- return [
630
- 4,
631
- new Promise(function(resolve) {
632
- return setTimeout(resolve, 100);
633
- })
634
- ];
635
- case 3:
636
- _state.sent();
637
- // Verify successful sync was logged
638
- successLogCall = consoleLogSpy.mock.calls.find(function(call) {
639
- var _call_, _call_1;
640
- return ((_call_ = call[0]) === null || _call_ === void 0 ? void 0 : _call_.includes('✅ Synced')) && ((_call_1 = call[0]) === null || _call_1 === void 0 ? void 0 : _call_1.includes('resources'));
641
- });
642
- expect(successLogCall).toBeDefined();
643
- // The log should show number of uploads
644
- expect(successLogCall[0]).toMatch(/✅ Synced \d+ resources/);
645
- return [
646
- 3,
647
- 6
648
- ];
649
- case 4:
650
- process.emit('SIGINT');
651
- return [
652
- 4,
653
- new Promise(function(r) {
654
- return setImmediate(r);
655
- })
656
- ];
657
- case 5:
658
- _state.sent();
659
- consoleLogSpy.mockRestore();
660
- return [
661
- 7
662
- ];
663
- case 6:
664
- return [
665
- 2
666
- ];
667
- }
668
- });
669
- })();
670
- });
671
- });
672
- describe('File watching', function() {
673
- it('should set up file watching for resources and brains', function() {
674
- return _async_to_generator(function() {
675
- var server, methodCalls, startCall;
676
- return _ts_generator(this, function(_state) {
677
- switch(_state.label){
678
- case 0:
679
- server = env.server;
680
- _state.label = 1;
681
- case 1:
682
- _state.trys.push([
683
- 1,
684
- ,
685
- 3,
686
- 5
687
- ]);
688
- return [
689
- 4,
690
- px([
691
- 'server'
692
- ], {
693
- server: server
694
- })
695
- ];
696
- case 2:
697
- _state.sent();
698
- // The fact that the server starts successfully means file watching was set up
699
- // We can verify this indirectly by checking that the server is running
700
- methodCalls = server.getLogs();
701
- startCall = methodCalls.find(function(call) {
702
- return call.method === 'start';
703
- });
704
- expect(startCall).toBeDefined();
705
- // The server should continue running (no exit called)
706
- expect(exitSpy).not.toHaveBeenCalled();
707
- return [
708
- 3,
709
- 5
710
- ];
711
- case 3:
712
- process.emit('SIGINT');
713
- return [
714
- 4,
715
- new Promise(function(r) {
716
- return setImmediate(r);
717
- })
718
- ];
719
- case 4:
720
- _state.sent();
721
- return [
722
- 7
723
- ];
724
- case 5:
725
- return [
726
- 2
727
- ];
728
- }
729
- });
730
- })();
731
- });
732
- // Skip the watcher error test as it requires complex mocking that doesn't align
733
- // with our integration testing philosophy. The error handling is already covered
734
- // by the fact that the server continues running even with watcher issues.
735
- });
736
- describe('Signal handling and cleanup', function() {
737
- it('should exit cleanly on SIGTERM signal', function() {
738
- return _async_to_generator(function() {
739
- var server;
740
- return _ts_generator(this, function(_state) {
741
- switch(_state.label){
742
- case 0:
743
- server = env.server;
744
- _state.label = 1;
745
- case 1:
746
- _state.trys.push([
747
- 1,
748
- ,
749
- 5,
750
- 6
751
- ]);
752
- return [
753
- 4,
754
- px([
755
- 'server'
756
- ], {
757
- server: server
758
- })
759
- ];
760
- case 2:
761
- _state.sent();
762
- // Wait for server to be fully started
763
- return [
764
- 4,
765
- new Promise(function(resolve) {
766
- return setTimeout(resolve, 50);
767
- })
768
- ];
769
- case 3:
770
- _state.sent();
771
- // Emit SIGTERM
772
- process.emit('SIGTERM');
773
- // Wait for cleanup
774
- return [
775
- 4,
776
- new Promise(function(resolve) {
777
- return setTimeout(resolve, 50);
778
- })
779
- ];
780
- case 4:
781
- _state.sent();
782
- // Verify process.exit was called with success code
783
- expect(exitSpy).toHaveBeenCalledWith(0);
784
- return [
785
- 3,
786
- 6
787
- ];
788
- case 5:
789
- return [
790
- 7
791
- ];
792
- case 6:
793
- return [
794
- 2
795
- ];
796
- }
797
- });
798
- })();
799
- });
800
- it('should handle server close event', function() {
801
- return _async_to_generator(function() {
802
- var server, originalStart, closeCallback;
803
- return _ts_generator(this, function(_state) {
804
- switch(_state.label){
805
- case 0:
806
- server = env.server;
807
- // Mock the server to emit close event
808
- originalStart = server.start.bind(server);
809
- server.start = jest.fn(function(port) {
810
- return _async_to_generator(function() {
811
- var handle, originalOnClose;
812
- return _ts_generator(this, function(_state) {
813
- switch(_state.label){
814
- case 0:
815
- return [
816
- 4,
817
- originalStart(port)
818
- ];
819
- case 1:
820
- handle = _state.sent();
821
- // Intercept the onClose callback
822
- originalOnClose = handle.onClose.bind(handle);
823
- handle.onClose = function(callback) {
824
- closeCallback = callback;
825
- originalOnClose(callback);
826
- };
827
- // Emit close event after a delay
828
- setTimeout(function() {
829
- if (closeCallback) {
830
- closeCallback(42); // Custom exit code
831
- }
832
- }, 100);
833
- return [
834
- 2,
835
- handle
836
- ];
837
- }
838
- });
839
- })();
840
- });
841
- _state.label = 1;
842
- case 1:
843
- _state.trys.push([
844
- 1,
845
- ,
846
- 4,
847
- 5
848
- ]);
849
- return [
850
- 4,
851
- px([
852
- 'server'
853
- ], {
854
- server: server
855
- })
856
- ];
857
- case 2:
858
- _state.sent();
859
- // Wait for close event
860
- return [
861
- 4,
862
- new Promise(function(resolve) {
863
- return setTimeout(resolve, 150);
864
- })
865
- ];
866
- case 3:
867
- _state.sent();
868
- // Verify process.exit was called with server's exit code
869
- expect(exitSpy).toHaveBeenCalledWith(42);
870
- return [
871
- 3,
872
- 5
873
- ];
874
- case 4:
875
- return [
876
- 7
877
- ];
878
- case 5:
879
- return [
880
- 2
881
- ];
882
- }
883
- });
884
- })();
885
- });
886
- });
887
- describe('Command line arguments', function() {
888
- it('should pass --force flag to server setup', function() {
889
- return _async_to_generator(function() {
890
- var server, methodCalls, setupCall;
891
- return _ts_generator(this, function(_state) {
892
- switch(_state.label){
893
- case 0:
894
- server = env.server;
895
- _state.label = 1;
896
- case 1:
897
- _state.trys.push([
898
- 1,
899
- ,
900
- 3,
901
- 5
902
- ]);
903
- return [
904
- 4,
905
- px([
906
- 'server',
907
- '--force'
908
- ], {
909
- server: server
910
- })
911
- ];
912
- case 2:
913
- _state.sent();
914
- // Verify setup was called with force=true
915
- methodCalls = server.getLogs();
916
- setupCall = methodCalls.find(function(call) {
917
- return call.method === 'setup';
918
- });
919
- expect(setupCall).toBeDefined();
920
- expect(setupCall.args[0]).toBe(true); // force flag is true
921
- return [
922
- 3,
923
- 5
924
- ];
925
- case 3:
926
- process.emit('SIGINT');
927
- return [
928
- 4,
929
- new Promise(function(r) {
930
- return setImmediate(r);
931
- })
932
- ];
933
- case 4:
934
- _state.sent();
935
- return [
936
- 7
937
- ];
938
- case 5:
939
- return [
940
- 2
941
- ];
942
- }
943
- });
944
- })();
945
- });
946
- it('should pass custom port to server start', function() {
947
- return _async_to_generator(function() {
948
- var server, customPort, methodCalls, startCall;
949
- return _ts_generator(this, function(_state) {
950
- switch(_state.label){
951
- case 0:
952
- server = env.server;
953
- customPort = 8765;
954
- _state.label = 1;
955
- case 1:
956
- _state.trys.push([
957
- 1,
958
- ,
959
- 3,
960
- 5
961
- ]);
962
- return [
963
- 4,
964
- px([
965
- 'server',
966
- '--port',
967
- String(customPort)
968
- ], {
969
- server: server
970
- })
971
- ];
972
- case 2:
973
- _state.sent();
974
- // Verify start was called with custom port
975
- methodCalls = server.getLogs();
976
- startCall = methodCalls.find(function(call) {
977
- return call.method === 'start';
978
- });
979
- expect(startCall).toBeDefined();
980
- expect(startCall.args[0]).toBe(customPort);
981
- return [
982
- 3,
983
- 5
984
- ];
985
- case 3:
986
- process.emit('SIGINT');
987
- return [
988
- 4,
989
- new Promise(function(r) {
990
- return setImmediate(r);
991
- })
992
- ];
993
- case 4:
994
- _state.sent();
995
- return [
996
- 7
997
- ];
998
- case 5:
999
- return [
1000
- 2
1001
- ];
1002
- }
1003
- });
1004
- })();
1005
- });
1006
- it('should always register log callbacks for server logs', function() {
1007
- return _async_to_generator(function() {
1008
- var server, methodCalls, startCall, onLogCall, onErrorCall, onWarningCall;
1009
- return _ts_generator(this, function(_state) {
1010
- switch(_state.label){
1011
- case 0:
1012
- server = env.server;
1013
- _state.label = 1;
1014
- case 1:
1015
- _state.trys.push([
1016
- 1,
1017
- ,
1018
- 3,
1019
- 5
1020
- ]);
1021
- return [
1022
- 4,
1023
- px([
1024
- 'server'
1025
- ], {
1026
- server: server
1027
- })
1028
- ];
1029
- case 2:
1030
- _state.sent();
1031
- // Verify server started
1032
- methodCalls = server.getLogs();
1033
- startCall = methodCalls.find(function(call) {
1034
- return call.method === 'start';
1035
- });
1036
- expect(startCall).toBeDefined();
1037
- // Verify callbacks were registered (server always manages its own logs)
1038
- onLogCall = methodCalls.find(function(call) {
1039
- return call.method === 'onLog';
1040
- });
1041
- onErrorCall = methodCalls.find(function(call) {
1042
- return call.method === 'onError';
1043
- });
1044
- onWarningCall = methodCalls.find(function(call) {
1045
- return call.method === 'onWarning';
1046
- });
1047
- expect(onLogCall).toBeDefined();
1048
- expect(onErrorCall).toBeDefined();
1049
- expect(onWarningCall).toBeDefined();
1050
- return [
1051
- 3,
1052
- 5
1053
- ];
1054
- case 3:
1055
- process.emit('SIGINT');
1056
- return [
1057
- 4,
1058
- new Promise(function(r) {
1059
- return setImmediate(r);
1060
- })
1061
- ];
1062
- case 4:
1063
- _state.sent();
1064
- return [
1065
- 7
1066
- ];
1067
- case 5:
1068
- return [
1069
- 2
1070
- ];
1071
- }
1072
- });
1073
- })();
1074
- });
1075
- it('should create default log file when no --log-file is specified', function() {
1076
- return _async_to_generator(function() {
1077
- var server, defaultLogFile;
1078
- return _ts_generator(this, function(_state) {
1079
- switch(_state.label){
1080
- case 0:
1081
- server = env.server;
1082
- defaultLogFile = path.join(server.projectRootDir, '.positronic-server.log');
1083
- _state.label = 1;
1084
- case 1:
1085
- _state.trys.push([
1086
- 1,
1087
- ,
1088
- 4,
1089
- 6
1090
- ]);
1091
- // Ensure no existing log file
1092
- if (fs.existsSync(defaultLogFile)) {
1093
- fs.unlinkSync(defaultLogFile);
1094
- }
1095
- return [
1096
- 4,
1097
- px([
1098
- 'server'
1099
- ], {
1100
- server: server
1101
- })
1102
- ];
1103
- case 2:
1104
- _state.sent();
1105
- // Give time for log file creation
1106
- return [
1107
- 4,
1108
- new Promise(function(resolve) {
1109
- return setTimeout(resolve, 100);
1110
- })
1111
- ];
1112
- case 3:
1113
- _state.sent();
1114
- // Verify default log file was created
1115
- expect(fs.existsSync(defaultLogFile)).toBe(true);
1116
- return [
1117
- 3,
1118
- 6
1119
- ];
1120
- case 4:
1121
- process.emit('SIGINT');
1122
- return [
1123
- 4,
1124
- new Promise(function(r) {
1125
- return setImmediate(r);
1126
- })
1127
- ];
1128
- case 5:
1129
- _state.sent();
1130
- // Clean up
1131
- if (fs.existsSync(defaultLogFile)) {
1132
- fs.unlinkSync(defaultLogFile);
1133
- }
1134
- return [
1135
- 7
1136
- ];
1137
- case 6:
1138
- return [
1139
- 2
1140
- ];
1141
- }
1142
- });
1143
- })();
1144
- });
1145
- it('should redirect output to log file when --log-file is specified', function() {
1146
- return _async_to_generator(function() {
1147
- var server, logFile, methodCalls, startCall, onLogCall, onErrorCall, onWarningCall, logContent;
1148
- return _ts_generator(this, function(_state) {
1149
- switch(_state.label){
1150
- case 0:
1151
- server = env.server;
1152
- logFile = path.join(server.projectRootDir, 'test-server.log');
1153
- _state.label = 1;
1154
- case 1:
1155
- _state.trys.push([
1156
- 1,
1157
- ,
1158
- 4,
1159
- 6
1160
- ]);
1161
- return [
1162
- 4,
1163
- px([
1164
- 'server',
1165
- '--log-file',
1166
- logFile
1167
- ], {
1168
- server: server
1169
- })
1170
- ];
1171
- case 2:
1172
- _state.sent();
1173
- // Give time for server startup and initial logging
1174
- return [
1175
- 4,
1176
- new Promise(function(resolve) {
1177
- return setTimeout(resolve, 500);
1178
- })
1179
- ];
1180
- case 3:
1181
- _state.sent();
1182
- // Verify log file was created
1183
- expect(fs.existsSync(logFile)).toBe(true);
1184
- // Verify server started successfully and callbacks were registered
1185
- methodCalls = server.getLogs();
1186
- startCall = methodCalls.find(function(call) {
1187
- return call.method === 'start';
1188
- });
1189
- expect(startCall).toBeDefined();
1190
- // Verify callbacks were registered
1191
- onLogCall = methodCalls.find(function(call) {
1192
- return call.method === 'onLog';
1193
- });
1194
- onErrorCall = methodCalls.find(function(call) {
1195
- return call.method === 'onError';
1196
- });
1197
- onWarningCall = methodCalls.find(function(call) {
1198
- return call.method === 'onWarning';
1199
- });
1200
- expect(onLogCall).toBeDefined();
1201
- expect(onErrorCall).toBeDefined();
1202
- expect(onWarningCall).toBeDefined();
1203
- // Check log file contains expected output
1204
- logContent = fs.readFileSync(logFile, 'utf-8');
1205
- expect(logContent).toContain('[INFO]');
1206
- expect(logContent).toContain('Synced');
1207
- return [
1208
- 3,
1209
- 6
1210
- ];
1211
- case 4:
1212
- process.emit('SIGINT');
1213
- return [
1214
- 4,
1215
- new Promise(function(r) {
1216
- return setImmediate(r);
1217
- })
1218
- ];
1219
- case 5:
1220
- _state.sent();
1221
- // Clean up log file
1222
- if (fs.existsSync(logFile)) {
1223
- fs.unlinkSync(logFile);
1224
- }
1225
- return [
1226
- 7
1227
- ];
1228
- case 6:
1229
- return [
1230
- 2
1231
- ];
1232
- }
1233
- });
1234
- })();
1235
- });
1236
- });
1237
- });