@onebun/core 0.2.8 → 0.2.10
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/package.json +9 -1
- package/src/application/application.test.ts +46 -0
- package/src/application/application.ts +12 -12
- package/src/module/controller.test.ts +110 -126
- package/src/module/module.ts +10 -0
- package/src/module/service.test.ts +78 -51
- package/src/queue/adapters/redis.adapter.test.ts +7 -25
- package/src/queue/docs-examples.test.ts +2 -2
- package/src/redis/shared-redis.test.ts +21 -39
- package/src/testing/containers.test.ts +35 -0
- package/src/testing/containers.ts +89 -0
- package/src/testing/docs-examples.test.ts +214 -0
- package/src/testing/index.ts +2 -0
- package/src/testing/service-helpers.test.ts +166 -0
- package/src/testing/service-helpers.ts +69 -0
- package/src/testing/testing-module.test.ts +80 -0
- package/src/testing/testing-module.ts +47 -2
- package/src/websocket/ws-storage-redis.test.ts +6 -24
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onebun/core",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.10",
|
|
4
4
|
"description": "Core package for OneBun framework - decorators, DI, modules, controllers",
|
|
5
5
|
"license": "LGPL-3.0",
|
|
6
6
|
"author": "RemRyahirev",
|
|
@@ -51,6 +51,14 @@
|
|
|
51
51
|
"bun-types": "^1.3.8",
|
|
52
52
|
"testcontainers": "^11.7.1"
|
|
53
53
|
},
|
|
54
|
+
"peerDependencies": {
|
|
55
|
+
"testcontainers": ">=10.0.0"
|
|
56
|
+
},
|
|
57
|
+
"peerDependenciesMeta": {
|
|
58
|
+
"testcontainers": {
|
|
59
|
+
"optional": true
|
|
60
|
+
}
|
|
61
|
+
},
|
|
54
62
|
"engines": {
|
|
55
63
|
"bun": ">=1.2.12"
|
|
56
64
|
}
|
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
} from '../decorators/decorators';
|
|
39
39
|
import { Controller as BaseController } from '../module/controller';
|
|
40
40
|
import { BaseMiddleware } from '../module/middleware';
|
|
41
|
+
import { clearGlobalServicesRegistry } from '../module/module';
|
|
41
42
|
import { Service } from '../module/service';
|
|
42
43
|
import { QueueService, QUEUE_NOT_ENABLED_ERROR_MESSAGE } from '../queue';
|
|
43
44
|
import { Subscribe } from '../queue/decorators';
|
|
@@ -157,6 +158,7 @@ describe('OneBunApplication', () => {
|
|
|
157
158
|
|
|
158
159
|
afterEach(() => {
|
|
159
160
|
register.clear();
|
|
161
|
+
clearGlobalServicesRegistry();
|
|
160
162
|
});
|
|
161
163
|
|
|
162
164
|
describe('Constructor and basic initialization', () => {
|
|
@@ -4036,5 +4038,49 @@ describe('OneBunApplication', () => {
|
|
|
4036
4038
|
|
|
4037
4039
|
await app.stop();
|
|
4038
4040
|
});
|
|
4041
|
+
|
|
4042
|
+
test('QueueService is injectable in controllers of child modules', async () => {
|
|
4043
|
+
@Controller('/child-queue')
|
|
4044
|
+
class ChildQueueController extends BaseController {
|
|
4045
|
+
constructor(private queueService: QueueService) {
|
|
4046
|
+
super();
|
|
4047
|
+
}
|
|
4048
|
+
|
|
4049
|
+
publishTest(pattern: string, data: unknown) {
|
|
4050
|
+
return this.queueService.publish(pattern, data);
|
|
4051
|
+
}
|
|
4052
|
+
}
|
|
4053
|
+
|
|
4054
|
+
@Controller('/child-sub')
|
|
4055
|
+
class ChildSubscribeController extends BaseController {
|
|
4056
|
+
@Subscribe('child.test')
|
|
4057
|
+
async handle(): Promise<void> {}
|
|
4058
|
+
}
|
|
4059
|
+
|
|
4060
|
+
@Module({
|
|
4061
|
+
controllers: [ChildQueueController, ChildSubscribeController],
|
|
4062
|
+
})
|
|
4063
|
+
class ChildQueueModule {}
|
|
4064
|
+
|
|
4065
|
+
@Module({
|
|
4066
|
+
imports: [ChildQueueModule],
|
|
4067
|
+
})
|
|
4068
|
+
class ParentQueueModule {}
|
|
4069
|
+
|
|
4070
|
+
const app = createTestApp(ParentQueueModule, {
|
|
4071
|
+
port: 0,
|
|
4072
|
+
queue: { enabled: true, adapter: 'memory' },
|
|
4073
|
+
});
|
|
4074
|
+
await app.start();
|
|
4075
|
+
|
|
4076
|
+
const rootModule = (app as unknown as { rootModule: ModuleInstance }).rootModule;
|
|
4077
|
+
const controller = rootModule.getControllerInstance?.(ChildQueueController) as
|
|
4078
|
+
| ChildQueueController
|
|
4079
|
+
| undefined;
|
|
4080
|
+
expect(controller).toBeDefined();
|
|
4081
|
+
await expect(controller!.publishTest('child.test', { ok: true })).resolves.toBeDefined();
|
|
4082
|
+
|
|
4083
|
+
await app.stop();
|
|
4084
|
+
});
|
|
4039
4085
|
});
|
|
4040
4086
|
});
|
|
@@ -53,7 +53,7 @@ import {
|
|
|
53
53
|
DEFAULT_SSE_HEARTBEAT_MS,
|
|
54
54
|
DEFAULT_SSE_TIMEOUT,
|
|
55
55
|
} from '../module/controller';
|
|
56
|
-
import { OneBunModule } from '../module/module';
|
|
56
|
+
import { OneBunModule, registerGlobalService } from '../module/module';
|
|
57
57
|
import {
|
|
58
58
|
QueueService,
|
|
59
59
|
QueueServiceProxy,
|
|
@@ -490,19 +490,19 @@ export class OneBunApplication {
|
|
|
490
490
|
this.logger.info('Application configuration initialized');
|
|
491
491
|
}
|
|
492
492
|
|
|
493
|
-
//
|
|
494
|
-
// so
|
|
495
|
-
this.rootModule = OneBunModule.create(this.moduleClass, this.loggerLayer, this.config);
|
|
496
|
-
|
|
497
|
-
// Register QueueService proxy in DI so controllers/providers/middleware can inject QueueService.
|
|
493
|
+
// Register QueueService proxy in global registry BEFORE creating the root module,
|
|
494
|
+
// so all modules (including child modules) pick it up via PHASE 0 of initModule().
|
|
498
495
|
// After initializeQueue(), setDelegate(real) is called when queue is enabled.
|
|
499
496
|
this.queueServiceProxy = new QueueServiceProxy();
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
497
|
+
registerGlobalService(
|
|
498
|
+
QueueServiceTag as Context.Tag<unknown, QueueService>,
|
|
499
|
+
this.queueServiceProxy as unknown as QueueService,
|
|
500
|
+
);
|
|
501
|
+
|
|
502
|
+
// Create the root module AFTER config is initialized and QueueService proxy is registered,
|
|
503
|
+
// so services can safely use this.config.get() in their constructors
|
|
504
|
+
// and inject QueueService in any module depth.
|
|
505
|
+
this.rootModule = OneBunModule.create(this.moduleClass, this.loggerLayer, this.config);
|
|
506
506
|
|
|
507
507
|
// Register test provider overrides (must happen before setup() so controllers receive mocks)
|
|
508
508
|
if (this.options._testProviders) {
|
|
@@ -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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
46
|
+
const { logger } = createTestController(TestController, {
|
|
47
|
+
config: {
|
|
48
|
+
'test': 'value',
|
|
49
|
+
'database.host': 'localhost',
|
|
50
|
+
'database.port': 5432,
|
|
51
|
+
},
|
|
52
|
+
});
|
|
58
53
|
|
|
59
|
-
expect(
|
|
60
|
-
expect(
|
|
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 =
|
|
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 =
|
|
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
|
|
138
|
-
controller.initializeController(mockLogger, mockConfig);
|
|
132
|
+
const { logger } = createTestController(CustomNamedController);
|
|
139
133
|
|
|
140
|
-
expect(
|
|
141
|
-
expect(
|
|
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 =
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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
|
|
184
|
-
controller.initializeController(mockLogger, mockConfig);
|
|
176
|
+
const { logger } = createTestController(ExtendedController);
|
|
185
177
|
|
|
186
|
-
expect(
|
|
187
|
-
expect(
|
|
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
|
|
210
|
-
controller.initializeController(mockLogger, mockConfig);
|
|
201
|
+
const { logger } = createTestController(Level3Controller);
|
|
211
202
|
|
|
212
|
-
expect(
|
|
213
|
-
expect(
|
|
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
|
|
224
|
-
|
|
225
|
-
expect(() => {
|
|
226
|
-
controller.initializeController(mockLogger, mockConfig);
|
|
227
|
-
}).not.toThrow();
|
|
214
|
+
const { logger } = createTestController(EmptyController);
|
|
228
215
|
|
|
229
|
-
expect(
|
|
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 =
|
|
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(
|
|
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 =
|
|
276
|
-
|
|
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 =
|
|
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 =
|
|
311
|
-
|
|
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 =
|
|
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 =
|
|
360
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
442
|
-
|
|
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 =
|
|
461
|
-
|
|
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 =
|
|
483
|
-
|
|
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 =
|
|
500
|
-
|
|
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 =
|
|
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 =
|
|
535
|
-
|
|
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 =
|
|
555
|
-
|
|
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 =
|
|
575
|
-
|
|
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 =
|
|
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
|
|
package/src/module/module.ts
CHANGED
|
@@ -72,6 +72,16 @@ export function clearGlobalServicesRegistry(): void {
|
|
|
72
72
|
processedGlobalModules.clear();
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Register a service instance in the global services registry so it is available
|
|
77
|
+
* in all modules (including child modules) via PHASE 0 of module initialization.
|
|
78
|
+
* Must be called BEFORE creating the root module.
|
|
79
|
+
* @internal
|
|
80
|
+
*/
|
|
81
|
+
export function registerGlobalService<T>(tag: Context.Tag<unknown, T>, instance: T): void {
|
|
82
|
+
globalServicesRegistry.set(tag as Context.Tag<unknown, unknown>, instance);
|
|
83
|
+
}
|
|
84
|
+
|
|
75
85
|
/**
|
|
76
86
|
* Get all global services (useful for debugging)
|
|
77
87
|
* @internal
|