@onebun/core 0.2.9 → 0.2.11

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.
@@ -7,41 +7,34 @@ import {
7
7
  describe,
8
8
  test,
9
9
  expect,
10
- beforeEach,
11
10
  mock,
12
11
  } from 'bun:test';
13
12
  import { Context } from 'effect';
14
13
 
15
- import type { IConfig, OneBunAppConfig } from './config.interface';
16
-
17
14
  import type { SyncLogger } from '@onebun/logger';
18
15
 
16
+ import { createTestController } from '../testing/service-helpers';
19
17
  import { createMockConfig } from '../testing/test-utils';
20
18
 
21
19
  import { Controller } from './controller';
22
20
 
23
- describe('Controller', () => {
24
- let mockLogger: SyncLogger;
25
- let mockConfig: IConfig<OneBunAppConfig>;
26
-
27
- beforeEach(() => {
28
- mockLogger = {
29
- debug: mock(() => {}),
30
- info: mock(() => {}),
31
- warn: mock(() => {}),
32
- error: mock(() => {}),
33
- fatal: mock(() => {}),
34
- trace: mock(() => {}),
35
- child: mock(() => mockLogger),
36
- };
37
-
38
- mockConfig = createMockConfig({
39
- 'test': 'value',
40
- 'database.host': 'localhost',
41
- 'database.port': 5432,
42
- });
43
- });
21
+ function createMockLogger(): SyncLogger {
22
+
23
+ const noOp = () => {};
24
+ const logger: SyncLogger = {
25
+ trace: mock(noOp),
26
+ debug: mock(noOp),
27
+ info: mock(noOp),
28
+ warn: mock(noOp),
29
+ error: mock(noOp),
30
+ fatal: mock(noOp),
31
+ child: mock(() => logger),
32
+ };
33
+
34
+ return logger;
35
+ }
44
36
 
37
+ describe('Controller', () => {
45
38
  describe('Controller initialization', () => {
46
39
  test('should initialize controller with logger and config', () => {
47
40
  class TestController extends Controller {
@@ -50,14 +43,16 @@ describe('Controller', () => {
50
43
  }
51
44
  }
52
45
 
53
- const controller = new TestController();
54
-
55
- expect(() => {
56
- controller.initializeController(mockLogger, mockConfig);
57
- }).not.toThrow();
46
+ const { logger } = createTestController(TestController, {
47
+ config: {
48
+ 'test': 'value',
49
+ 'database.host': 'localhost',
50
+ 'database.port': 5432,
51
+ },
52
+ });
58
53
 
59
- expect(mockLogger.child).toHaveBeenCalledWith({ className: 'TestController' });
60
- expect(mockLogger.debug).toHaveBeenCalledWith('Controller TestController initialized');
54
+ expect(logger.child).toHaveBeenCalledWith({ className: 'TestController' });
55
+ expect(logger.debug).toHaveBeenCalledWith('Controller TestController initialized');
61
56
  });
62
57
 
63
58
  test('should throw error when logger is not provided', () => {
@@ -68,7 +63,8 @@ describe('Controller', () => {
68
63
  }
69
64
 
70
65
  const controller = new TestController();
71
-
66
+ const mockConfig = createMockConfig({ 'test': 'value' });
67
+
72
68
  expect(() => {
73
69
  controller.initializeController(undefined as any, mockConfig);
74
70
  }).toThrow('Logger is required for controller TestController');
@@ -82,7 +78,8 @@ describe('Controller', () => {
82
78
  }
83
79
 
84
80
  const controller = new TestController();
85
-
81
+ const mockConfig = createMockConfig({ 'test': 'value' });
82
+
86
83
  expect(() => {
87
84
  controller.initializeController(null as any, mockConfig);
88
85
  }).toThrow('Logger is required for controller TestController');
@@ -100,8 +97,7 @@ describe('Controller', () => {
100
97
  }
101
98
  }
102
99
 
103
- const controller = new TestController();
104
- controller.initializeController(mockLogger, mockConfig);
100
+ const { instance: controller } = createTestController(TestController);
105
101
 
106
102
  // The method should exist and be callable
107
103
  expect(typeof controller.testGetService).toBe('function');
@@ -117,8 +113,7 @@ describe('Controller', () => {
117
113
  }
118
114
  }
119
115
 
120
- const controller = new TestController();
121
- controller.initializeController(mockLogger, mockConfig);
116
+ const { instance: controller } = createTestController(TestController);
122
117
 
123
118
  const info = controller.getLoggerInfo();
124
119
  expect(info.hasLogger).toBe(true);
@@ -134,11 +129,10 @@ describe('Controller', () => {
134
129
  }
135
130
  }
136
131
 
137
- const controller = new CustomNamedController();
138
- controller.initializeController(mockLogger, mockConfig);
132
+ const { logger } = createTestController(CustomNamedController);
139
133
 
140
- expect(mockLogger.child).toHaveBeenCalledWith({ className: 'CustomNamedController' });
141
- expect(mockLogger.debug).toHaveBeenCalledWith('Controller CustomNamedController initialized');
134
+ expect(logger.child).toHaveBeenCalledWith({ className: 'CustomNamedController' });
135
+ expect(logger.debug).toHaveBeenCalledWith('Controller CustomNamedController initialized');
142
136
  });
143
137
 
144
138
  test('should provide typed config access', () => {
@@ -146,20 +140,19 @@ describe('Controller', () => {
146
140
  getConfigValue(path: string) {
147
141
  return this.config.get(path);
148
142
  }
149
-
143
+
150
144
  checkConfigInitialized() {
151
145
  return this.config.isInitialized;
152
146
  }
153
147
  }
154
148
 
155
- const controller = new TestController();
156
- const typedConfig = createMockConfig({
157
- 'server.port': 3000,
158
- 'server.host': '0.0.0.0',
149
+ const { instance: controller } = createTestController(TestController, {
150
+ config: {
151
+ 'server.port': 3000,
152
+ 'server.host': '0.0.0.0',
153
+ },
159
154
  });
160
-
161
- controller.initializeController(mockLogger, typedConfig);
162
-
155
+
163
156
  expect(controller.getConfigValue('server.port')).toBe(3000);
164
157
  expect(controller.getConfigValue('server.host')).toBe('0.0.0.0');
165
158
  expect(controller.checkConfigInitialized()).toBe(true);
@@ -180,11 +173,10 @@ describe('Controller', () => {
180
173
  }
181
174
  }
182
175
 
183
- const controller = new ExtendedController();
184
- controller.initializeController(mockLogger, mockConfig);
176
+ const { logger } = createTestController(ExtendedController);
185
177
 
186
- expect(mockLogger.child).toHaveBeenCalledWith({ className: 'ExtendedController' });
187
- expect(mockLogger.debug).toHaveBeenCalledWith('Controller ExtendedController initialized');
178
+ expect(logger.child).toHaveBeenCalledWith({ className: 'ExtendedController' });
179
+ expect(logger.debug).toHaveBeenCalledWith('Controller ExtendedController initialized');
188
180
  });
189
181
 
190
182
  test('should handle complex inheritance chains', () => {
@@ -206,11 +198,10 @@ describe('Controller', () => {
206
198
  }
207
199
  }
208
200
 
209
- const controller = new Level3Controller();
210
- controller.initializeController(mockLogger, mockConfig);
201
+ const { logger } = createTestController(Level3Controller);
211
202
 
212
- expect(mockLogger.child).toHaveBeenCalledWith({ className: 'Level3Controller' });
213
- expect(mockLogger.debug).toHaveBeenCalledWith('Controller Level3Controller initialized');
203
+ expect(logger.child).toHaveBeenCalledWith({ className: 'Level3Controller' });
204
+ expect(logger.debug).toHaveBeenCalledWith('Controller Level3Controller initialized');
214
205
  });
215
206
  });
216
207
 
@@ -220,13 +211,9 @@ describe('Controller', () => {
220
211
  // No additional methods
221
212
  }
222
213
 
223
- const controller = new EmptyController();
224
-
225
- expect(() => {
226
- controller.initializeController(mockLogger, mockConfig);
227
- }).not.toThrow();
214
+ const { logger } = createTestController(EmptyController);
228
215
 
229
- expect(mockLogger.child).toHaveBeenCalledWith({ className: 'EmptyController' });
216
+ expect(logger.child).toHaveBeenCalledWith({ className: 'EmptyController' });
230
217
  });
231
218
 
232
219
  test('should handle re-initialization attempts (no-op after first init)', () => {
@@ -236,26 +223,18 @@ describe('Controller', () => {
236
223
  }
237
224
  }
238
225
 
239
- const controller = new TestController();
240
-
241
- // First initialization
242
- controller.initializeController(mockLogger, mockConfig);
243
- expect(mockLogger.debug).toHaveBeenCalledWith('Controller TestController initialized');
226
+ const { instance: controller, logger, config } = createTestController(TestController);
227
+ expect(logger.debug).toHaveBeenCalledWith('Controller TestController initialized');
244
228
 
245
229
  // Second initialization — should be a no-op (already initialized)
246
- const newMockLogger = {
247
- ...mockLogger,
248
- child: mock(() => newMockLogger),
249
- debug: mock(() => {}),
250
- };
251
-
230
+ const newMockLogger = createMockLogger();
252
231
  const newConfig = createMockConfig({ 'newConfig': true });
253
232
  controller.initializeController(newMockLogger, newConfig);
254
233
 
255
234
  // Should NOT have been called — initializeController is a no-op after first init
256
235
  expect(newMockLogger.debug).not.toHaveBeenCalled();
257
236
  // Should still have original config
258
- expect((controller as any).config).toBe(mockConfig);
237
+ expect((controller as any).config).toBe(config);
259
238
  });
260
239
  });
261
240
 
@@ -272,9 +251,8 @@ describe('Controller', () => {
272
251
  }
273
252
  }
274
253
 
275
- const controller = new TestController();
276
- controller.initializeController(mockLogger, mockConfig);
277
-
254
+ const { instance: controller } = createTestController(TestController);
255
+
278
256
  const result = controller.testSetAndGetService();
279
257
  expect(result).toEqual({ value: 'test-value' });
280
258
  });
@@ -288,8 +266,7 @@ describe('Controller', () => {
288
266
  }
289
267
  }
290
268
 
291
- const controller = new TestController();
292
- controller.initializeController(mockLogger, mockConfig);
269
+ const { instance: controller } = createTestController(TestController);
293
270
 
294
271
  expect(() => controller.testGetMissingService()).toThrow('Service undefined not found');
295
272
  });
@@ -307,9 +284,8 @@ describe('Controller', () => {
307
284
  }
308
285
  }
309
286
 
310
- const controller = new TestController();
311
- controller.initializeController(mockLogger, mockConfig);
312
-
287
+ const { instance: controller } = createTestController(TestController);
288
+
313
289
  const result = controller.testServiceLookup();
314
290
  expect(result).toBe('test-service-value');
315
291
  });
@@ -323,8 +299,7 @@ describe('Controller', () => {
323
299
  }
324
300
  }
325
301
 
326
- const controller = new TestController();
327
- controller.initializeController(mockLogger, mockConfig);
302
+ const { instance: controller } = createTestController(TestController);
328
303
 
329
304
  // Test JSON content type
330
305
  const jsonRequest = new Request('http://test.com', {
@@ -356,9 +331,8 @@ describe('Controller', () => {
356
331
  }
357
332
  }
358
333
 
359
- const controller = new TestController();
360
- controller.initializeController(mockLogger, mockConfig);
361
-
334
+ const { instance: controller } = createTestController(TestController);
335
+
362
336
  const testData = { message: 'hello', number: 42 };
363
337
  const request = new Request('http://test.com', {
364
338
  method: 'POST',
@@ -377,8 +351,7 @@ describe('Controller', () => {
377
351
  }
378
352
  }
379
353
 
380
- const controller = new TestController();
381
- controller.initializeController(mockLogger, mockConfig);
354
+ const { instance: controller } = createTestController(TestController);
382
355
 
383
356
  const testData = { id: 1, name: 'Test' };
384
357
 
@@ -399,8 +372,7 @@ describe('Controller', () => {
399
372
  }
400
373
  }
401
374
 
402
- const controller = new TestController();
403
- controller.initializeController(mockLogger, mockConfig);
375
+ const { instance: controller } = createTestController(TestController);
404
376
 
405
377
  // Test with default values
406
378
  const defaultResponse = controller.testError('Something went wrong');
@@ -419,12 +391,11 @@ describe('Controller', () => {
419
391
  }
420
392
  }
421
393
 
422
- const controller = new TestController();
423
- controller.initializeController(mockLogger, mockConfig);
394
+ const { instance: controller } = createTestController(TestController);
424
395
 
425
396
  const testData = { legacy: true };
426
397
  const response = controller.testJson(testData, 201);
427
-
398
+
428
399
  expect(response.status).toBe(201);
429
400
  expect(response.headers.get('Content-Type')).toBe('application/json');
430
401
  });
@@ -438,9 +409,8 @@ describe('Controller', () => {
438
409
  }
439
410
  }
440
411
 
441
- const controller = new TestController();
442
- controller.initializeController(mockLogger, mockConfig);
443
-
412
+ const { instance: controller } = createTestController(TestController);
413
+
444
414
  const response = controller.testSuccessContent();
445
415
  const content = await response.json();
446
416
 
@@ -457,9 +427,8 @@ describe('Controller', () => {
457
427
  }
458
428
  }
459
429
 
460
- const controller = new TestController();
461
- controller.initializeController(mockLogger, mockConfig);
462
-
430
+ const { instance: controller } = createTestController(TestController);
431
+
463
432
  const response = controller.testErrorContent();
464
433
  const content = await response.json();
465
434
 
@@ -479,9 +448,8 @@ describe('Controller', () => {
479
448
  }
480
449
  }
481
450
 
482
- const controller = new TestController();
483
- controller.initializeController(mockLogger, mockConfig);
484
-
451
+ const { instance: controller } = createTestController(TestController);
452
+
485
453
  const response = controller.testEmptySuccess();
486
454
  expect(response.status).toBe(200);
487
455
  });
@@ -490,15 +458,14 @@ describe('Controller', () => {
490
458
  class TestController extends Controller {
491
459
  testGetServiceByClass() {
492
460
  class MockServiceClass {}
493
-
461
+
494
462
  // This will throw because the class is not registered
495
463
  expect(() => this.getService(MockServiceClass)).toThrow();
496
464
  }
497
465
  }
498
466
 
499
- const controller = new TestController();
500
- controller.initializeController(mockLogger, mockConfig);
501
-
467
+ const { instance: controller } = createTestController(TestController);
468
+
502
469
  controller.testGetServiceByClass();
503
470
  });
504
471
 
@@ -515,8 +482,7 @@ describe('Controller', () => {
515
482
  }
516
483
  }
517
484
 
518
- const controller = new TestController();
519
- controller.initializeController(mockLogger, mockConfig);
485
+ const { instance: controller } = createTestController(TestController);
520
486
 
521
487
  await expect(controller.testMalformedJson()).rejects.toThrow();
522
488
  });
@@ -531,15 +497,14 @@ describe('Controller', () => {
531
497
  }
532
498
  }
533
499
 
534
- const controller = new TestController();
535
- controller.initializeController(mockLogger, mockConfig);
536
-
500
+ const { instance: controller } = createTestController(TestController);
501
+
537
502
  const result = controller.testTextResponse();
538
-
503
+
539
504
  expect(result).toBeInstanceOf(Response);
540
505
  expect(result.status).toBe(200);
541
506
  expect(result.headers.get('Content-Type')).toBe('text/plain');
542
-
507
+
543
508
  const text = await result.text();
544
509
  expect(text).toBe('Hello, World!');
545
510
  });
@@ -551,15 +516,14 @@ describe('Controller', () => {
551
516
  }
552
517
  }
553
518
 
554
- const controller = new TestController();
555
- controller.initializeController(mockLogger, mockConfig);
556
-
519
+ const { instance: controller } = createTestController(TestController);
520
+
557
521
  const result = controller.testTextResponseWithStatus();
558
-
522
+
559
523
  expect(result).toBeInstanceOf(Response);
560
524
  expect(result.status).toBe(404);
561
525
  expect(result.headers.get('Content-Type')).toBe('text/plain');
562
-
526
+
563
527
  const text = await result.text();
564
528
  expect(text).toBe('Not Found');
565
529
  });
@@ -571,14 +535,13 @@ describe('Controller', () => {
571
535
  }
572
536
  }
573
537
 
574
- const controller = new TestController();
575
- controller.initializeController(mockLogger, mockConfig);
576
-
538
+ const { instance: controller } = createTestController(TestController);
539
+
577
540
  const result = controller.testEmptyTextResponse();
578
-
541
+
579
542
  expect(result).toBeInstanceOf(Response);
580
543
  expect(result.status).toBe(200);
581
-
544
+
582
545
  const text = await result.text();
583
546
  expect(text).toBe('');
584
547
  });
@@ -597,6 +560,13 @@ describe('Controller', () => {
597
560
  }
598
561
  }
599
562
 
563
+ const mockLogger = createMockLogger();
564
+ const mockConfig = createMockConfig({
565
+ 'test': 'value',
566
+ 'database.host': 'localhost',
567
+ 'database.port': 5432,
568
+ });
569
+
600
570
  // Set init context before construction (as the framework does)
601
571
  Controller.setInitContext(mockLogger, mockConfig);
602
572
  let controller: TestController;
@@ -621,6 +591,11 @@ describe('Controller', () => {
621
591
  }
622
592
  }
623
593
 
594
+ const mockLogger = createMockLogger();
595
+ const mockConfig = createMockConfig({
596
+ 'database.host': 'localhost',
597
+ });
598
+
624
599
  Controller.setInitContext(mockLogger, mockConfig);
625
600
  let controller: TestController;
626
601
  try {
@@ -635,6 +610,9 @@ describe('Controller', () => {
635
610
  test('should create child logger with correct className in constructor', () => {
636
611
  class MyCustomController extends Controller {}
637
612
 
613
+ const mockLogger = createMockLogger();
614
+ const mockConfig = createMockConfig({});
615
+
638
616
  Controller.setInitContext(mockLogger, mockConfig);
639
617
  try {
640
618
  new MyCustomController();
@@ -660,6 +638,9 @@ describe('Controller', () => {
660
638
  test('initializeController should be a no-op if already initialized via init context', () => {
661
639
  class TestController extends Controller {}
662
640
 
641
+ const mockLogger = createMockLogger();
642
+ const mockConfig = createMockConfig({});
643
+
663
644
  Controller.setInitContext(mockLogger, mockConfig);
664
645
  let controller: TestController;
665
646
  try {
@@ -669,7 +650,7 @@ describe('Controller', () => {
669
650
  }
670
651
 
671
652
  // Call initializeController again — should be a no-op
672
- const otherLogger = { ...mockLogger, child: mock(() => ({ ...mockLogger })) };
653
+ const otherLogger = createMockLogger();
673
654
  const otherConfig = createMockConfig({ other: 'config' });
674
655
  controller.initializeController(otherLogger, otherConfig);
675
656
 
@@ -678,6 +659,9 @@ describe('Controller', () => {
678
659
  });
679
660
 
680
661
  test('clearInitContext should prevent subsequent constructors from picking up context', () => {
662
+ const mockLogger = createMockLogger();
663
+ const mockConfig = createMockConfig({});
664
+
681
665
  Controller.setInitContext(mockLogger, mockConfig);
682
666
  Controller.clearInitContext();
683
667