@navios/core 0.8.0 → 0.9.0

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 (56) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/lib/{index-BDNl7j1G.d.cts → index-D9MNh6Tx.d.mts} +36 -13
  3. package/lib/index-D9MNh6Tx.d.mts.map +1 -0
  4. package/lib/{index-BoP0cWT6.d.mts → index-Db1d3cwD.d.cts} +36 -13
  5. package/lib/index-Db1d3cwD.d.cts.map +1 -0
  6. package/lib/index.cjs +2 -2
  7. package/lib/index.d.cts +2 -2
  8. package/lib/index.d.mts +2 -2
  9. package/lib/index.mjs +2 -2
  10. package/lib/legacy-compat/index.cjs +1 -1
  11. package/lib/legacy-compat/index.cjs.map +1 -1
  12. package/lib/legacy-compat/index.d.cts +3 -3
  13. package/lib/legacy-compat/index.d.cts.map +1 -1
  14. package/lib/legacy-compat/index.d.mts +3 -3
  15. package/lib/legacy-compat/index.d.mts.map +1 -1
  16. package/lib/legacy-compat/index.mjs +1 -1
  17. package/lib/legacy-compat/index.mjs.map +1 -1
  18. package/lib/{src-B6eISODM.cjs → src-BRPtJ9fG.cjs} +12 -9
  19. package/lib/src-BRPtJ9fG.cjs.map +1 -0
  20. package/lib/{src-gBAChVRL.mjs → src-Bo23RIo-.mjs} +13 -10
  21. package/lib/src-Bo23RIo-.mjs.map +1 -0
  22. package/lib/testing/index.cjs +346 -29
  23. package/lib/testing/index.cjs.map +1 -1
  24. package/lib/testing/index.d.cts +299 -63
  25. package/lib/testing/index.d.cts.map +1 -1
  26. package/lib/testing/index.d.mts +299 -63
  27. package/lib/testing/index.d.mts.map +1 -1
  28. package/lib/testing/index.mjs +347 -31
  29. package/lib/testing/index.mjs.map +1 -1
  30. package/lib/{use-guards.decorator-COR-9mZY.cjs → use-guards.decorator-Bs8oDHOi.cjs} +13 -9
  31. package/lib/use-guards.decorator-Bs8oDHOi.cjs.map +1 -0
  32. package/lib/{use-guards.decorator-CUww54Nt.mjs → use-guards.decorator-CzVXuLkz.mjs} +12 -8
  33. package/lib/use-guards.decorator-CzVXuLkz.mjs.map +1 -0
  34. package/package.json +2 -2
  35. package/src/__tests__/controller-resolver.spec.mts +19 -13
  36. package/src/__tests__/testing-module.spec.mts +459 -0
  37. package/src/__tests__/unit-testing-module.spec.mts +424 -0
  38. package/src/decorators/controller.decorator.mts +19 -2
  39. package/src/decorators/module.decorator.mts +23 -5
  40. package/src/legacy-compat/__type-tests__/legacy-decorators.spec-d.mts +2 -6
  41. package/src/legacy-compat/decorators/multipart.decorator.mts +4 -4
  42. package/src/legacy-compat/decorators/stream.decorator.mts +4 -4
  43. package/src/navios.application.mts +9 -9
  44. package/src/navios.environment.mts +3 -1
  45. package/src/navios.factory.mts +9 -27
  46. package/src/services/instance-resolver.service.mts +8 -7
  47. package/src/services/module-loader.service.mts +3 -2
  48. package/src/testing/index.mts +1 -0
  49. package/src/testing/testing-module.mts +255 -93
  50. package/src/testing/unit-testing-module.mts +298 -0
  51. package/lib/index-BDNl7j1G.d.cts.map +0 -1
  52. package/lib/index-BoP0cWT6.d.mts.map +0 -1
  53. package/lib/src-B6eISODM.cjs.map +0 -1
  54. package/lib/src-gBAChVRL.mjs.map +0 -1
  55. package/lib/use-guards.decorator-COR-9mZY.cjs.map +0 -1
  56. package/lib/use-guards.decorator-CUww54Nt.mjs.map +0 -1
@@ -0,0 +1,459 @@
1
+ import { Injectable, InjectableScope, InjectionToken } from '@navios/di'
2
+ import { afterEach, describe, expect, it } from 'vitest'
3
+
4
+ import { Module } from '../decorators/module.decorator.mjs'
5
+ import { createTestingModule, TestingModule } from '../testing/testing-module.mjs'
6
+
7
+ describe('TestingModule', () => {
8
+ let testingModule: TestingModule | null = null
9
+
10
+ afterEach(async () => {
11
+ if (testingModule) {
12
+ await testingModule.close()
13
+ testingModule = null
14
+ }
15
+ })
16
+
17
+ describe('create', () => {
18
+ it('should create a testing module with static create method', () => {
19
+ @Module()
20
+ class TestAppModule {}
21
+
22
+ testingModule = TestingModule.create(TestAppModule, { adapter: [] })
23
+
24
+ expect(testingModule).toBeInstanceOf(TestingModule)
25
+ })
26
+
27
+ it('should apply initial overrides from options', async () => {
28
+ const TOKEN = InjectionToken.create<string>('test-token')
29
+
30
+ @Module()
31
+ class TestAppModule {}
32
+
33
+ testingModule = TestingModule.create(TestAppModule, {
34
+ adapter: [],
35
+ overrides: [{ token: TOKEN, useValue: 'overridden-value' }],
36
+ })
37
+
38
+ await testingModule.compile()
39
+ const value = await testingModule.get(TOKEN)
40
+
41
+ expect(value).toBe('overridden-value')
42
+ })
43
+ })
44
+
45
+ describe('createTestingModule (deprecated)', () => {
46
+ it('should still work for backwards compatibility', () => {
47
+ @Module()
48
+ class TestAppModule {}
49
+
50
+ testingModule = createTestingModule(TestAppModule, { adapter: [] })
51
+
52
+ expect(testingModule).toBeInstanceOf(TestingModule)
53
+ })
54
+ })
55
+
56
+ describe('compile', () => {
57
+ it('should return this for chaining', async () => {
58
+ @Module()
59
+ class TestAppModule {}
60
+
61
+ testingModule = TestingModule.create(TestAppModule, { adapter: [] })
62
+ const result = await testingModule.compile()
63
+
64
+ expect(result).toBe(testingModule)
65
+ })
66
+
67
+ it('should only compile once even when called multiple times', async () => {
68
+ @Module()
69
+ class TestAppModule {}
70
+
71
+ testingModule = TestingModule.create(TestAppModule, { adapter: [] })
72
+
73
+ const result1 = await testingModule.compile()
74
+ const app1 = result1.getApp()
75
+
76
+ const result2 = await testingModule.compile()
77
+ const app2 = result2.getApp()
78
+
79
+ expect(app1).toBe(app2)
80
+ })
81
+ })
82
+
83
+ describe('init', () => {
84
+ it('should return this for chaining', async () => {
85
+ @Module()
86
+ class TestAppModule {}
87
+
88
+ testingModule = TestingModule.create(TestAppModule, { adapter: [] })
89
+ const result = await testingModule.init()
90
+
91
+ expect(result).toBe(testingModule)
92
+ })
93
+
94
+ it('should compile automatically if not already compiled', async () => {
95
+ @Module()
96
+ class TestAppModule {}
97
+
98
+ testingModule = TestingModule.create(TestAppModule, { adapter: [] })
99
+ await testingModule.init()
100
+
101
+ expect(() => testingModule!.getApp()).not.toThrow()
102
+ })
103
+
104
+ it('should start a request scope after init', async () => {
105
+ @Module()
106
+ class TestAppModule {}
107
+
108
+ testingModule = TestingModule.create(TestAppModule, { adapter: [] })
109
+ await testingModule.init()
110
+
111
+ expect(() => testingModule!.getScopedContainer()).not.toThrow()
112
+ })
113
+ })
114
+
115
+ describe('getApp', () => {
116
+ it('should throw if not compiled', () => {
117
+ @Module()
118
+ class TestAppModule {}
119
+
120
+ testingModule = TestingModule.create(TestAppModule, { adapter: [] })
121
+
122
+ expect(() => testingModule!.getApp()).toThrow(
123
+ 'TestingModule not compiled. Call compile() or init() first.',
124
+ )
125
+ })
126
+
127
+ it('should return the app after compile', async () => {
128
+ @Module()
129
+ class TestAppModule {}
130
+
131
+ testingModule = TestingModule.create(TestAppModule, { adapter: [] })
132
+ await testingModule.compile()
133
+
134
+ const app = testingModule.getApp()
135
+ expect(app).toBeDefined()
136
+ })
137
+ })
138
+
139
+ describe('getScopedContainer', () => {
140
+ it('should throw if init was not called', async () => {
141
+ @Module()
142
+ class TestAppModule {}
143
+
144
+ testingModule = TestingModule.create(TestAppModule, { adapter: [] })
145
+ await testingModule.compile()
146
+
147
+ expect(() => testingModule!.getScopedContainer()).toThrow(
148
+ 'No scoped container available. Call init() first.',
149
+ )
150
+ })
151
+
152
+ it('should return scoped container after init', async () => {
153
+ @Module()
154
+ class TestAppModule {}
155
+
156
+ testingModule = TestingModule.create(TestAppModule, { adapter: [] })
157
+ await testingModule.init()
158
+
159
+ const scopedContainer = testingModule.getScopedContainer()
160
+ expect(scopedContainer).toBeDefined()
161
+ })
162
+ })
163
+
164
+ describe('overrideProvider', () => {
165
+ it('should override with useValue', async () => {
166
+ @Injectable()
167
+ class OriginalService {
168
+ getValue() {
169
+ return 'original'
170
+ }
171
+ }
172
+
173
+ @Module()
174
+ class TestAppModule {}
175
+
176
+ const mockService = { getValue: () => 'mocked' }
177
+
178
+ testingModule = TestingModule.create(TestAppModule, { adapter: [] })
179
+ .overrideProvider(OriginalService)
180
+ .useValue(mockService)
181
+
182
+ await testingModule.compile()
183
+ const service = await testingModule.get(OriginalService)
184
+
185
+ expect(service.getValue()).toBe('mocked')
186
+ })
187
+
188
+ it('should override with useClass', async () => {
189
+ @Injectable()
190
+ class OriginalService {
191
+ getValue() {
192
+ return 'original'
193
+ }
194
+ }
195
+
196
+ @Injectable()
197
+ class MockService {
198
+ getValue() {
199
+ return 'mocked-class'
200
+ }
201
+ }
202
+
203
+ @Module()
204
+ class TestAppModule {}
205
+
206
+ testingModule = TestingModule.create(TestAppModule, { adapter: [] })
207
+ .overrideProvider(OriginalService)
208
+ .useClass(MockService)
209
+
210
+ await testingModule.compile()
211
+ const service = await testingModule.get(OriginalService)
212
+
213
+ expect(service.getValue()).toBe('mocked-class')
214
+ })
215
+
216
+ it('should support chaining multiple overrides', async () => {
217
+ @Injectable()
218
+ class ServiceA {
219
+ getValue() {
220
+ return 'a'
221
+ }
222
+ }
223
+
224
+ @Injectable()
225
+ class ServiceB {
226
+ getValue() {
227
+ return 'b'
228
+ }
229
+ }
230
+
231
+ @Module()
232
+ class TestAppModule {}
233
+
234
+ testingModule = TestingModule.create(TestAppModule, { adapter: [] })
235
+ .overrideProvider(ServiceA)
236
+ .useValue({ getValue: () => 'mocked-a' })
237
+ .overrideProvider(ServiceB)
238
+ .useValue({ getValue: () => 'mocked-b' })
239
+
240
+ await testingModule.compile()
241
+
242
+ const serviceA = await testingModule.get(ServiceA)
243
+ const serviceB = await testingModule.get(ServiceB)
244
+
245
+ expect(serviceA.getValue()).toBe('mocked-a')
246
+ expect(serviceB.getValue()).toBe('mocked-b')
247
+ })
248
+ })
249
+
250
+ describe('get', () => {
251
+ it('should resolve services after compile', async () => {
252
+ @Injectable()
253
+ class TestService {
254
+ getValue() {
255
+ return 'test'
256
+ }
257
+ }
258
+
259
+ @Module()
260
+ class TestAppModule {}
261
+
262
+ testingModule = TestingModule.create(TestAppModule, { adapter: [] })
263
+ await testingModule.compile()
264
+
265
+ const service = await testingModule.get(TestService)
266
+ expect(service.getValue()).toBe('test')
267
+ })
268
+
269
+ it('should resolve request-scoped services after init', async () => {
270
+ @Injectable({ scope: InjectableScope.Request })
271
+ class RequestScopedService {
272
+ getValue() {
273
+ return 'request-scoped'
274
+ }
275
+ }
276
+
277
+ @Module()
278
+ class TestAppModule {}
279
+
280
+ testingModule = TestingModule.create(TestAppModule, { adapter: [] })
281
+ await testingModule.init()
282
+
283
+ const service = await testingModule.get(RequestScopedService)
284
+ expect(service.getValue()).toBe('request-scoped')
285
+ })
286
+ })
287
+
288
+ describe('close', () => {
289
+ it('should clean up all resources', async () => {
290
+ @Module()
291
+ class TestAppModule {}
292
+
293
+ testingModule = TestingModule.create(TestAppModule, { adapter: [] })
294
+ await testingModule.init()
295
+
296
+ await testingModule.close()
297
+
298
+ // After close, getApp and getScopedContainer should throw
299
+ expect(() => testingModule!.getApp()).toThrow()
300
+ expect(() => testingModule!.getScopedContainer()).toThrow()
301
+
302
+ testingModule = null // Prevent afterEach from calling close again
303
+ })
304
+ })
305
+
306
+ describe('assertion helpers', () => {
307
+ it('should expose expectResolved', async () => {
308
+ @Injectable()
309
+ class TestService {}
310
+
311
+ @Module()
312
+ class TestAppModule {}
313
+
314
+ testingModule = TestingModule.create(TestAppModule, { adapter: [] })
315
+ await testingModule.compile()
316
+
317
+ await testingModule.get(TestService)
318
+
319
+ expect(() => testingModule!.expectResolved(TestService)).not.toThrow()
320
+ })
321
+
322
+ it('should expose expectNotResolved', async () => {
323
+ @Injectable()
324
+ class TestService {}
325
+
326
+ @Module()
327
+ class TestAppModule {}
328
+
329
+ testingModule = TestingModule.create(TestAppModule, { adapter: [] })
330
+ await testingModule.compile()
331
+
332
+ expect(() => testingModule!.expectNotResolved(TestService)).not.toThrow()
333
+ })
334
+
335
+ it('should expose expectSingleton', async () => {
336
+ @Injectable({ scope: InjectableScope.Singleton })
337
+ class SingletonService {}
338
+
339
+ @Module()
340
+ class TestAppModule {}
341
+
342
+ testingModule = TestingModule.create(TestAppModule, { adapter: [] })
343
+ await testingModule.compile()
344
+
345
+ expect(() => testingModule!.expectSingleton(SingletonService)).not.toThrow()
346
+ })
347
+
348
+ it('should expose expectTransient', async () => {
349
+ @Injectable({ scope: InjectableScope.Transient })
350
+ class TransientService {}
351
+
352
+ @Module()
353
+ class TestAppModule {}
354
+
355
+ testingModule = TestingModule.create(TestAppModule, { adapter: [] })
356
+ await testingModule.compile()
357
+
358
+ expect(() => testingModule!.expectTransient(TransientService)).not.toThrow()
359
+ })
360
+
361
+ it('should expose expectRequestScoped', async () => {
362
+ @Injectable({ scope: InjectableScope.Request })
363
+ class RequestService {}
364
+
365
+ @Module()
366
+ class TestAppModule {}
367
+
368
+ testingModule = TestingModule.create(TestAppModule, { adapter: [] })
369
+ await testingModule.compile()
370
+
371
+ expect(() => testingModule!.expectRequestScoped(RequestService)).not.toThrow()
372
+ })
373
+ })
374
+
375
+ describe('method call tracking', () => {
376
+ it('should record and assert method calls', async () => {
377
+ @Injectable()
378
+ class MockService {
379
+ doSomething(arg: string) {
380
+ return arg.toUpperCase()
381
+ }
382
+ }
383
+
384
+ @Module()
385
+ class TestAppModule {}
386
+
387
+ testingModule = TestingModule.create(TestAppModule, { adapter: [] })
388
+ await testingModule.compile()
389
+
390
+ // Manually record a method call (typically done in mock implementations)
391
+ testingModule.recordMethodCall(MockService, 'doSomething', ['test'], 'TEST')
392
+
393
+ expect(() => testingModule!.expectCalled(MockService, 'doSomething')).not.toThrow()
394
+ expect(() =>
395
+ testingModule!.expectCalledWith(MockService, 'doSomething', ['test']),
396
+ ).not.toThrow()
397
+ expect(() =>
398
+ testingModule!.expectCallCount(MockService, 'doSomething', 1),
399
+ ).not.toThrow()
400
+ })
401
+
402
+ it('should get method calls for custom assertions', async () => {
403
+ @Injectable()
404
+ class MockService {}
405
+
406
+ @Module()
407
+ class TestAppModule {}
408
+
409
+ testingModule = TestingModule.create(TestAppModule, { adapter: [] })
410
+ await testingModule.compile()
411
+
412
+ testingModule.recordMethodCall(MockService, 'method1', ['a'])
413
+ testingModule.recordMethodCall(MockService, 'method2', ['b', 'c'])
414
+
415
+ const calls = testingModule.getMethodCalls(MockService)
416
+
417
+ expect(calls).toHaveLength(2)
418
+ expect(calls[0].method).toBe('method1')
419
+ expect(calls[1].method).toBe('method2')
420
+ })
421
+ })
422
+
423
+ describe('dependency graph', () => {
424
+ it('should expose getDependencyGraph', async () => {
425
+ @Injectable()
426
+ class TestService {}
427
+
428
+ @Module()
429
+ class TestAppModule {}
430
+
431
+ testingModule = TestingModule.create(TestAppModule, { adapter: [] })
432
+ await testingModule.compile()
433
+
434
+ await testingModule.get(TestService)
435
+
436
+ const graph = testingModule.getDependencyGraph()
437
+
438
+ expect(graph).toHaveProperty('nodes')
439
+ expect(graph).toHaveProperty('rootTokens')
440
+ })
441
+
442
+ it('should expose getSimplifiedDependencyGraph', async () => {
443
+ @Injectable()
444
+ class TestService {}
445
+
446
+ @Module()
447
+ class TestAppModule {}
448
+
449
+ testingModule = TestingModule.create(TestAppModule, { adapter: [] })
450
+ await testingModule.compile()
451
+
452
+ await testingModule.get(TestService)
453
+
454
+ const graph = testingModule.getSimplifiedDependencyGraph()
455
+
456
+ expect(typeof graph).toBe('object')
457
+ })
458
+ })
459
+ })