@hazeljs/core 0.2.0-beta.1
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/README.md +522 -0
- package/dist/__tests__/container.test.d.ts +2 -0
- package/dist/__tests__/container.test.d.ts.map +1 -0
- package/dist/__tests__/container.test.js +454 -0
- package/dist/__tests__/decorators.test.d.ts +2 -0
- package/dist/__tests__/decorators.test.d.ts.map +1 -0
- package/dist/__tests__/decorators.test.js +693 -0
- package/dist/__tests__/errors/http.error.test.d.ts +2 -0
- package/dist/__tests__/errors/http.error.test.d.ts.map +1 -0
- package/dist/__tests__/errors/http.error.test.js +117 -0
- package/dist/__tests__/filters/exception-filter.test.d.ts +2 -0
- package/dist/__tests__/filters/exception-filter.test.d.ts.map +1 -0
- package/dist/__tests__/filters/exception-filter.test.js +135 -0
- package/dist/__tests__/filters/http-exception.filter.test.d.ts +2 -0
- package/dist/__tests__/filters/http-exception.filter.test.d.ts.map +1 -0
- package/dist/__tests__/filters/http-exception.filter.test.js +119 -0
- package/dist/__tests__/hazel-app.test.d.ts +2 -0
- package/dist/__tests__/hazel-app.test.d.ts.map +1 -0
- package/dist/__tests__/hazel-app.test.js +682 -0
- package/dist/__tests__/hazel-module.test.d.ts +2 -0
- package/dist/__tests__/hazel-module.test.d.ts.map +1 -0
- package/dist/__tests__/hazel-module.test.js +408 -0
- package/dist/__tests__/hazel-response.test.d.ts +2 -0
- package/dist/__tests__/hazel-response.test.d.ts.map +1 -0
- package/dist/__tests__/hazel-response.test.js +138 -0
- package/dist/__tests__/health.test.d.ts +2 -0
- package/dist/__tests__/health.test.d.ts.map +1 -0
- package/dist/__tests__/health.test.js +147 -0
- package/dist/__tests__/index.test.d.ts +2 -0
- package/dist/__tests__/index.test.d.ts.map +1 -0
- package/dist/__tests__/index.test.js +239 -0
- package/dist/__tests__/interceptors/interceptor.test.d.ts +2 -0
- package/dist/__tests__/interceptors/interceptor.test.d.ts.map +1 -0
- package/dist/__tests__/interceptors/interceptor.test.js +166 -0
- package/dist/__tests__/logger.test.d.ts +2 -0
- package/dist/__tests__/logger.test.d.ts.map +1 -0
- package/dist/__tests__/logger.test.js +141 -0
- package/dist/__tests__/middleware/cors.test.d.ts +2 -0
- package/dist/__tests__/middleware/cors.test.d.ts.map +1 -0
- package/dist/__tests__/middleware/cors.test.js +129 -0
- package/dist/__tests__/middleware/csrf.test.d.ts +2 -0
- package/dist/__tests__/middleware/csrf.test.d.ts.map +1 -0
- package/dist/__tests__/middleware/csrf.test.js +247 -0
- package/dist/__tests__/middleware/global-middleware.test.d.ts +2 -0
- package/dist/__tests__/middleware/global-middleware.test.d.ts.map +1 -0
- package/dist/__tests__/middleware/global-middleware.test.js +259 -0
- package/dist/__tests__/middleware/rate-limit.test.d.ts +2 -0
- package/dist/__tests__/middleware/rate-limit.test.d.ts.map +1 -0
- package/dist/__tests__/middleware/rate-limit.test.js +264 -0
- package/dist/__tests__/middleware/security-headers.test.d.ts +2 -0
- package/dist/__tests__/middleware/security-headers.test.d.ts.map +1 -0
- package/dist/__tests__/middleware/security-headers.test.js +229 -0
- package/dist/__tests__/middleware/timeout.test.d.ts +2 -0
- package/dist/__tests__/middleware/timeout.test.d.ts.map +1 -0
- package/dist/__tests__/middleware/timeout.test.js +132 -0
- package/dist/__tests__/middleware.test.d.ts +2 -0
- package/dist/__tests__/middleware.test.d.ts.map +1 -0
- package/dist/__tests__/middleware.test.js +180 -0
- package/dist/__tests__/pipes/pipe.test.d.ts +2 -0
- package/dist/__tests__/pipes/pipe.test.d.ts.map +1 -0
- package/dist/__tests__/pipes/pipe.test.js +245 -0
- package/dist/__tests__/pipes/validation.pipe.test.d.ts +2 -0
- package/dist/__tests__/pipes/validation.pipe.test.d.ts.map +1 -0
- package/dist/__tests__/pipes/validation.pipe.test.js +297 -0
- package/dist/__tests__/request-parser.test.d.ts +2 -0
- package/dist/__tests__/request-parser.test.d.ts.map +1 -0
- package/dist/__tests__/request-parser.test.js +182 -0
- package/dist/__tests__/router.test.d.ts +2 -0
- package/dist/__tests__/router.test.d.ts.map +1 -0
- package/dist/__tests__/router.test.js +680 -0
- package/dist/__tests__/routing/route-matcher.test.d.ts +2 -0
- package/dist/__tests__/routing/route-matcher.test.d.ts.map +1 -0
- package/dist/__tests__/routing/route-matcher.test.js +219 -0
- package/dist/__tests__/routing/version.decorator.test.d.ts +2 -0
- package/dist/__tests__/routing/version.decorator.test.d.ts.map +1 -0
- package/dist/__tests__/routing/version.decorator.test.js +298 -0
- package/dist/__tests__/service.test.d.ts +2 -0
- package/dist/__tests__/service.test.d.ts.map +1 -0
- package/dist/__tests__/service.test.js +121 -0
- package/dist/__tests__/shutdown.test.d.ts +2 -0
- package/dist/__tests__/shutdown.test.d.ts.map +1 -0
- package/dist/__tests__/shutdown.test.js +250 -0
- package/dist/__tests__/testing/testing.module.test.d.ts +2 -0
- package/dist/__tests__/testing/testing.module.test.d.ts.map +1 -0
- package/dist/__tests__/testing/testing.module.test.js +370 -0
- package/dist/__tests__/upload/file-upload.test.d.ts +2 -0
- package/dist/__tests__/upload/file-upload.test.d.ts.map +1 -0
- package/dist/__tests__/upload/file-upload.test.js +498 -0
- package/dist/__tests__/utils/sanitize.test.d.ts +2 -0
- package/dist/__tests__/utils/sanitize.test.d.ts.map +1 -0
- package/dist/__tests__/utils/sanitize.test.js +291 -0
- package/dist/__tests__/validator.test.d.ts +2 -0
- package/dist/__tests__/validator.test.d.ts.map +1 -0
- package/dist/__tests__/validator.test.js +300 -0
- package/dist/container.d.ts +80 -0
- package/dist/container.d.ts.map +1 -0
- package/dist/container.js +271 -0
- package/dist/decorators.d.ts +92 -0
- package/dist/decorators.d.ts.map +1 -0
- package/dist/decorators.js +343 -0
- package/dist/errors/http.error.d.ts +31 -0
- package/dist/errors/http.error.d.ts.map +1 -0
- package/dist/errors/http.error.js +62 -0
- package/dist/filters/exception-filter.d.ts +39 -0
- package/dist/filters/exception-filter.d.ts.map +1 -0
- package/dist/filters/exception-filter.js +38 -0
- package/dist/filters/http-exception.filter.d.ts +9 -0
- package/dist/filters/http-exception.filter.d.ts.map +1 -0
- package/dist/filters/http-exception.filter.js +42 -0
- package/dist/hazel-app.d.ts +78 -0
- package/dist/hazel-app.d.ts.map +1 -0
- package/dist/hazel-app.js +453 -0
- package/dist/hazel-module.d.ts +20 -0
- package/dist/hazel-module.d.ts.map +1 -0
- package/dist/hazel-module.js +109 -0
- package/dist/hazel-response.d.ts +20 -0
- package/dist/hazel-response.d.ts.map +1 -0
- package/dist/hazel-response.js +68 -0
- package/dist/health.d.ts +73 -0
- package/dist/health.d.ts.map +1 -0
- package/dist/health.js +174 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +140 -0
- package/dist/interceptors/interceptor.d.ts +22 -0
- package/dist/interceptors/interceptor.d.ts.map +1 -0
- package/dist/interceptors/interceptor.js +46 -0
- package/dist/logger.d.ts +8 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +238 -0
- package/dist/middleware/cors.middleware.d.ts +44 -0
- package/dist/middleware/cors.middleware.d.ts.map +1 -0
- package/dist/middleware/cors.middleware.js +118 -0
- package/dist/middleware/csrf.middleware.d.ts +82 -0
- package/dist/middleware/csrf.middleware.d.ts.map +1 -0
- package/dist/middleware/csrf.middleware.js +183 -0
- package/dist/middleware/global-middleware.d.ts +111 -0
- package/dist/middleware/global-middleware.d.ts.map +1 -0
- package/dist/middleware/global-middleware.js +179 -0
- package/dist/middleware/rate-limit.middleware.d.ts +73 -0
- package/dist/middleware/rate-limit.middleware.d.ts.map +1 -0
- package/dist/middleware/rate-limit.middleware.js +124 -0
- package/dist/middleware/security-headers.middleware.d.ts +76 -0
- package/dist/middleware/security-headers.middleware.d.ts.map +1 -0
- package/dist/middleware/security-headers.middleware.js +123 -0
- package/dist/middleware/timeout.middleware.d.ts +25 -0
- package/dist/middleware/timeout.middleware.d.ts.map +1 -0
- package/dist/middleware/timeout.middleware.js +74 -0
- package/dist/middleware.d.ts +13 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +47 -0
- package/dist/pipes/pipe.d.ts +50 -0
- package/dist/pipes/pipe.d.ts.map +1 -0
- package/dist/pipes/pipe.js +96 -0
- package/dist/pipes/validation.pipe.d.ts +6 -0
- package/dist/pipes/validation.pipe.d.ts.map +1 -0
- package/dist/pipes/validation.pipe.js +61 -0
- package/dist/request-context.d.ts +17 -0
- package/dist/request-context.d.ts.map +1 -0
- package/dist/request-context.js +2 -0
- package/dist/request-parser.d.ts +7 -0
- package/dist/request-parser.d.ts.map +1 -0
- package/dist/request-parser.js +60 -0
- package/dist/router.d.ts +33 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +426 -0
- package/dist/routing/route-matcher.d.ts +39 -0
- package/dist/routing/route-matcher.d.ts.map +1 -0
- package/dist/routing/route-matcher.js +93 -0
- package/dist/routing/version.decorator.d.ts +36 -0
- package/dist/routing/version.decorator.d.ts.map +1 -0
- package/dist/routing/version.decorator.js +89 -0
- package/dist/service.d.ts +9 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.js +39 -0
- package/dist/shutdown.d.ts +32 -0
- package/dist/shutdown.d.ts.map +1 -0
- package/dist/shutdown.js +109 -0
- package/dist/testing/testing.module.d.ts +83 -0
- package/dist/testing/testing.module.d.ts.map +1 -0
- package/dist/testing/testing.module.js +164 -0
- package/dist/types.d.ts +76 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/upload/file-upload.d.ts +75 -0
- package/dist/upload/file-upload.d.ts.map +1 -0
- package/dist/upload/file-upload.js +261 -0
- package/dist/utils/sanitize.d.ts +45 -0
- package/dist/utils/sanitize.d.ts.map +1 -0
- package/dist/utils/sanitize.js +165 -0
- package/dist/validator.d.ts +7 -0
- package/dist/validator.d.ts.map +1 -0
- package/dist/validator.js +119 -0
- package/package.json +65 -0
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const container_1 = require("../container");
|
|
4
|
+
require("reflect-metadata");
|
|
5
|
+
// Mock logger
|
|
6
|
+
jest.mock('../logger', () => ({
|
|
7
|
+
info: jest.fn(),
|
|
8
|
+
debug: jest.fn(),
|
|
9
|
+
warn: jest.fn(),
|
|
10
|
+
error: jest.fn(),
|
|
11
|
+
isDebugEnabled: jest.fn().mockReturnValue(false),
|
|
12
|
+
}));
|
|
13
|
+
describe('Container', () => {
|
|
14
|
+
let container;
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
container = container_1.Container.createTestInstance();
|
|
17
|
+
});
|
|
18
|
+
describe('getInstance', () => {
|
|
19
|
+
it('should return singleton instance', () => {
|
|
20
|
+
const instance1 = container_1.Container.getInstance();
|
|
21
|
+
const instance2 = container_1.Container.getInstance();
|
|
22
|
+
expect(instance1).toBe(instance2);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
describe('createTestInstance', () => {
|
|
26
|
+
it('should create new container instance', () => {
|
|
27
|
+
const container1 = container_1.Container.createTestInstance();
|
|
28
|
+
const container2 = container_1.Container.createTestInstance();
|
|
29
|
+
expect(container1).not.toBe(container2);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
describe('register', () => {
|
|
33
|
+
it('should register value provider', () => {
|
|
34
|
+
const value = { data: 'test' };
|
|
35
|
+
container.register('TEST_TOKEN', value);
|
|
36
|
+
const resolved = container.resolve('TEST_TOKEN');
|
|
37
|
+
expect(resolved).toBe(value);
|
|
38
|
+
});
|
|
39
|
+
it('should register with default singleton scope', () => {
|
|
40
|
+
const value = { data: 'test' };
|
|
41
|
+
container.register('TEST_TOKEN', value);
|
|
42
|
+
const resolved1 = container.resolve('TEST_TOKEN');
|
|
43
|
+
const resolved2 = container.resolve('TEST_TOKEN');
|
|
44
|
+
expect(resolved1).toBe(resolved2);
|
|
45
|
+
});
|
|
46
|
+
it('should register with transient scope', () => {
|
|
47
|
+
// Note: Transient scope with direct values doesn't make much sense
|
|
48
|
+
// as values are not recreated. Use factory for true transient behavior.
|
|
49
|
+
container.registerProvider({
|
|
50
|
+
token: 'TEST_TOKEN',
|
|
51
|
+
useFactory: () => ({ data: 'test' }),
|
|
52
|
+
scope: container_1.Scope.TRANSIENT,
|
|
53
|
+
});
|
|
54
|
+
const resolved1 = container.resolve('TEST_TOKEN');
|
|
55
|
+
const resolved2 = container.resolve('TEST_TOKEN');
|
|
56
|
+
expect(resolved1).toEqual({ data: 'test' });
|
|
57
|
+
expect(resolved2).toEqual({ data: 'test' });
|
|
58
|
+
expect(resolved1).not.toBe(resolved2);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
describe('registerProvider', () => {
|
|
62
|
+
it('should register class provider', () => {
|
|
63
|
+
class TestService {
|
|
64
|
+
getValue() {
|
|
65
|
+
return 'test';
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
container.registerProvider({
|
|
69
|
+
token: TestService,
|
|
70
|
+
useClass: TestService,
|
|
71
|
+
});
|
|
72
|
+
const instance = container.resolve(TestService);
|
|
73
|
+
expect(instance).toBeInstanceOf(TestService);
|
|
74
|
+
expect(instance.getValue()).toBe('test');
|
|
75
|
+
});
|
|
76
|
+
it('should register value provider', () => {
|
|
77
|
+
const value = { data: 'test' };
|
|
78
|
+
container.registerProvider({
|
|
79
|
+
token: 'TEST_TOKEN',
|
|
80
|
+
useValue: value,
|
|
81
|
+
});
|
|
82
|
+
const resolved = container.resolve('TEST_TOKEN');
|
|
83
|
+
expect(resolved).toBe(value);
|
|
84
|
+
});
|
|
85
|
+
it('should register factory provider', () => {
|
|
86
|
+
const factory = jest.fn().mockReturnValue({ data: 'factory' });
|
|
87
|
+
container.registerProvider({
|
|
88
|
+
token: 'FACTORY_TOKEN',
|
|
89
|
+
useFactory: factory,
|
|
90
|
+
});
|
|
91
|
+
const resolved = container.resolve('FACTORY_TOKEN');
|
|
92
|
+
expect(factory).toHaveBeenCalled();
|
|
93
|
+
expect(resolved).toEqual({ data: 'factory' });
|
|
94
|
+
});
|
|
95
|
+
it('should inject dependencies into factory', () => {
|
|
96
|
+
class Dependency {
|
|
97
|
+
getValue() {
|
|
98
|
+
return 'dep';
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
container.registerProvider({
|
|
102
|
+
token: Dependency,
|
|
103
|
+
useClass: Dependency,
|
|
104
|
+
});
|
|
105
|
+
const factory = jest.fn((...args) => {
|
|
106
|
+
const dep = args[0];
|
|
107
|
+
return {
|
|
108
|
+
value: dep.getValue(),
|
|
109
|
+
};
|
|
110
|
+
});
|
|
111
|
+
container.registerProvider({
|
|
112
|
+
token: 'SERVICE',
|
|
113
|
+
useFactory: factory,
|
|
114
|
+
inject: [Dependency],
|
|
115
|
+
});
|
|
116
|
+
const resolved = container.resolve('SERVICE');
|
|
117
|
+
expect(resolved.value).toBe('dep');
|
|
118
|
+
});
|
|
119
|
+
it('should use specified scope', () => {
|
|
120
|
+
class TestService {
|
|
121
|
+
}
|
|
122
|
+
container.registerProvider({
|
|
123
|
+
token: TestService,
|
|
124
|
+
useClass: TestService,
|
|
125
|
+
scope: container_1.Scope.TRANSIENT,
|
|
126
|
+
});
|
|
127
|
+
const instance1 = container.resolve(TestService);
|
|
128
|
+
const instance2 = container.resolve(TestService);
|
|
129
|
+
expect(instance1).not.toBe(instance2);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
describe('resolve', () => {
|
|
133
|
+
it('should return undefined for unregistered token', () => {
|
|
134
|
+
const result = container.resolve('UNKNOWN');
|
|
135
|
+
expect(result).toBeUndefined();
|
|
136
|
+
});
|
|
137
|
+
it('should return undefined for null token', () => {
|
|
138
|
+
const result = container.resolve(null);
|
|
139
|
+
expect(result).toBeUndefined();
|
|
140
|
+
});
|
|
141
|
+
it('should auto-resolve classes', () => {
|
|
142
|
+
class AutoService {
|
|
143
|
+
getValue() {
|
|
144
|
+
return 'auto';
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const instance = container.resolve(AutoService);
|
|
148
|
+
expect(instance).toBeInstanceOf(AutoService);
|
|
149
|
+
expect(instance.getValue()).toBe('auto');
|
|
150
|
+
});
|
|
151
|
+
it('should resolve singleton only once', () => {
|
|
152
|
+
let callCount = 0;
|
|
153
|
+
const factory = jest.fn(() => {
|
|
154
|
+
callCount++;
|
|
155
|
+
return { count: callCount };
|
|
156
|
+
});
|
|
157
|
+
container.registerProvider({
|
|
158
|
+
token: 'SINGLETON',
|
|
159
|
+
useFactory: factory,
|
|
160
|
+
scope: container_1.Scope.SINGLETON,
|
|
161
|
+
});
|
|
162
|
+
const instance1 = container.resolve('SINGLETON');
|
|
163
|
+
const instance2 = container.resolve('SINGLETON');
|
|
164
|
+
expect(factory).toHaveBeenCalledTimes(1);
|
|
165
|
+
expect(instance1).toBe(instance2);
|
|
166
|
+
});
|
|
167
|
+
it('should resolve transient multiple times', () => {
|
|
168
|
+
let callCount = 0;
|
|
169
|
+
const factory = jest.fn(() => {
|
|
170
|
+
callCount++;
|
|
171
|
+
return { count: callCount };
|
|
172
|
+
});
|
|
173
|
+
container.registerProvider({
|
|
174
|
+
token: 'TRANSIENT',
|
|
175
|
+
useFactory: factory,
|
|
176
|
+
scope: container_1.Scope.TRANSIENT,
|
|
177
|
+
});
|
|
178
|
+
const instance1 = container.resolve('TRANSIENT');
|
|
179
|
+
const instance2 = container.resolve('TRANSIENT');
|
|
180
|
+
expect(factory).toHaveBeenCalledTimes(2);
|
|
181
|
+
expect(instance1).not.toBe(instance2);
|
|
182
|
+
});
|
|
183
|
+
it('should resolve request-scoped with requestId', () => {
|
|
184
|
+
const factory = jest.fn(() => ({ data: 'request' }));
|
|
185
|
+
container.registerProvider({
|
|
186
|
+
token: 'REQUEST_SERVICE',
|
|
187
|
+
useFactory: factory,
|
|
188
|
+
scope: container_1.Scope.REQUEST,
|
|
189
|
+
});
|
|
190
|
+
const instance1 = container.resolve('REQUEST_SERVICE', 'req-1');
|
|
191
|
+
const instance2 = container.resolve('REQUEST_SERVICE', 'req-1');
|
|
192
|
+
const instance3 = container.resolve('REQUEST_SERVICE', 'req-2');
|
|
193
|
+
expect(factory).toHaveBeenCalledTimes(2);
|
|
194
|
+
expect(instance1).toBe(instance2);
|
|
195
|
+
expect(instance1).not.toBe(instance3);
|
|
196
|
+
});
|
|
197
|
+
it('should throw error for request scope without requestId', () => {
|
|
198
|
+
container.registerProvider({
|
|
199
|
+
token: 'REQUEST_SERVICE',
|
|
200
|
+
useFactory: () => ({}),
|
|
201
|
+
scope: container_1.Scope.REQUEST,
|
|
202
|
+
});
|
|
203
|
+
expect(() => container.resolve('REQUEST_SERVICE')).toThrow(/Request scope requires requestId/);
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
describe('circular dependency detection', () => {
|
|
207
|
+
it('should detect circular dependencies', () => {
|
|
208
|
+
class ServiceA {
|
|
209
|
+
constructor(b) {
|
|
210
|
+
this.b = b;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
class ServiceB {
|
|
214
|
+
constructor(a) {
|
|
215
|
+
this.a = a;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
Reflect.defineMetadata('design:paramtypes', [ServiceB], ServiceA);
|
|
219
|
+
Reflect.defineMetadata('design:paramtypes', [ServiceA], ServiceB);
|
|
220
|
+
container.registerProvider({ token: ServiceA, useClass: ServiceA });
|
|
221
|
+
container.registerProvider({ token: ServiceB, useClass: ServiceB });
|
|
222
|
+
expect(() => container.resolve(ServiceA)).toThrow(/Circular dependency/);
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
describe('clearRequestScope', () => {
|
|
226
|
+
it('should clear request-scoped providers', () => {
|
|
227
|
+
const factory = jest.fn(() => ({ data: 'request' }));
|
|
228
|
+
container.registerProvider({
|
|
229
|
+
token: 'REQUEST_SERVICE',
|
|
230
|
+
useFactory: factory,
|
|
231
|
+
scope: container_1.Scope.REQUEST,
|
|
232
|
+
});
|
|
233
|
+
const instance1 = container.resolve('REQUEST_SERVICE', 'req-1');
|
|
234
|
+
container.clearRequestScope('req-1');
|
|
235
|
+
const instance2 = container.resolve('REQUEST_SERVICE', 'req-1');
|
|
236
|
+
expect(factory).toHaveBeenCalledTimes(2);
|
|
237
|
+
expect(instance1).not.toBe(instance2);
|
|
238
|
+
});
|
|
239
|
+
it('should handle clearing non-existent request scope', () => {
|
|
240
|
+
expect(() => container.clearRequestScope('non-existent')).not.toThrow();
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
describe('dependency injection', () => {
|
|
244
|
+
it('should inject constructor dependencies', () => {
|
|
245
|
+
class Dependency {
|
|
246
|
+
getValue() {
|
|
247
|
+
return 'dependency';
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
class Service {
|
|
251
|
+
constructor(dep) {
|
|
252
|
+
this.dep = dep;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
Reflect.defineMetadata('design:paramtypes', [Dependency], Service);
|
|
256
|
+
container.registerProvider({ token: Dependency, useClass: Dependency });
|
|
257
|
+
container.registerProvider({ token: Service, useClass: Service });
|
|
258
|
+
const instance = container.resolve(Service);
|
|
259
|
+
expect(instance.dep).toBeInstanceOf(Dependency);
|
|
260
|
+
expect(instance.dep.getValue()).toBe('dependency');
|
|
261
|
+
});
|
|
262
|
+
it('should inject multiple dependencies', () => {
|
|
263
|
+
class DepA {
|
|
264
|
+
getValue() {
|
|
265
|
+
return 'A';
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
class DepB {
|
|
269
|
+
getValue() {
|
|
270
|
+
return 'B';
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
class Service {
|
|
274
|
+
constructor(depA, depB) {
|
|
275
|
+
this.depA = depA;
|
|
276
|
+
this.depB = depB;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
Reflect.defineMetadata('design:paramtypes', [DepA, DepB], Service);
|
|
280
|
+
container.registerProvider({ token: DepA, useClass: DepA });
|
|
281
|
+
container.registerProvider({ token: DepB, useClass: DepB });
|
|
282
|
+
container.registerProvider({ token: Service, useClass: Service });
|
|
283
|
+
const instance = container.resolve(Service);
|
|
284
|
+
expect(instance.depA.getValue()).toBe('A');
|
|
285
|
+
expect(instance.depB.getValue()).toBe('B');
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
describe('complex scenarios', () => {
|
|
289
|
+
it('should handle nested dependencies', () => {
|
|
290
|
+
class Level3 {
|
|
291
|
+
getValue() {
|
|
292
|
+
return 'level3';
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
class Level2 {
|
|
296
|
+
constructor(level3) {
|
|
297
|
+
this.level3 = level3;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
class Level1 {
|
|
301
|
+
constructor(level2) {
|
|
302
|
+
this.level2 = level2;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
Reflect.defineMetadata('design:paramtypes', [Level3], Level2);
|
|
306
|
+
Reflect.defineMetadata('design:paramtypes', [Level2], Level1);
|
|
307
|
+
container.registerProvider({ token: Level3, useClass: Level3 });
|
|
308
|
+
container.registerProvider({ token: Level2, useClass: Level2 });
|
|
309
|
+
container.registerProvider({ token: Level1, useClass: Level1 });
|
|
310
|
+
const instance = container.resolve(Level1);
|
|
311
|
+
expect(instance.level2.level3.getValue()).toBe('level3');
|
|
312
|
+
});
|
|
313
|
+
it('should handle mixed scopes', () => {
|
|
314
|
+
class SingletonService {
|
|
315
|
+
getValue() {
|
|
316
|
+
return 'singleton';
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
class TransientService {
|
|
320
|
+
constructor(singleton) {
|
|
321
|
+
this.singleton = singleton;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
Reflect.defineMetadata('design:paramtypes', [SingletonService], TransientService);
|
|
325
|
+
container.registerProvider({
|
|
326
|
+
token: SingletonService,
|
|
327
|
+
useClass: SingletonService,
|
|
328
|
+
scope: container_1.Scope.SINGLETON,
|
|
329
|
+
});
|
|
330
|
+
container.registerProvider({
|
|
331
|
+
token: TransientService,
|
|
332
|
+
useClass: TransientService,
|
|
333
|
+
scope: container_1.Scope.TRANSIENT,
|
|
334
|
+
});
|
|
335
|
+
const instance1 = container.resolve(TransientService);
|
|
336
|
+
const instance2 = container.resolve(TransientService);
|
|
337
|
+
expect(instance1).not.toBe(instance2);
|
|
338
|
+
expect(instance1.singleton).toBe(instance2.singleton);
|
|
339
|
+
});
|
|
340
|
+
});
|
|
341
|
+
describe('edge cases and error handling', () => {
|
|
342
|
+
it('should handle circular dependencies gracefully', () => {
|
|
343
|
+
class ServiceA {
|
|
344
|
+
constructor(serviceB) {
|
|
345
|
+
this.serviceB = serviceB;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
class ServiceB {
|
|
349
|
+
constructor(serviceA) {
|
|
350
|
+
this.serviceA = serviceA;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
Reflect.defineMetadata('design:paramtypes', [ServiceB], ServiceA);
|
|
354
|
+
Reflect.defineMetadata('design:paramtypes', [ServiceA], ServiceB);
|
|
355
|
+
container.registerProvider({ token: ServiceA, useClass: ServiceA });
|
|
356
|
+
container.registerProvider({ token: ServiceB, useClass: ServiceB });
|
|
357
|
+
// This may throw or handle gracefully depending on implementation
|
|
358
|
+
expect(() => container.resolve(ServiceA)).toBeDefined();
|
|
359
|
+
});
|
|
360
|
+
it('should handle missing dependencies', () => {
|
|
361
|
+
class ServiceWithMissingDep {
|
|
362
|
+
constructor(missing) {
|
|
363
|
+
this.missing = missing;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
class MissingDep {
|
|
367
|
+
}
|
|
368
|
+
Reflect.defineMetadata('design:paramtypes', [MissingDep], ServiceWithMissingDep);
|
|
369
|
+
container.registerProvider({ token: ServiceWithMissingDep, useClass: ServiceWithMissingDep });
|
|
370
|
+
// Should auto-resolve or throw
|
|
371
|
+
expect(() => container.resolve(ServiceWithMissingDep)).toBeDefined();
|
|
372
|
+
});
|
|
373
|
+
it('should handle request scope with different request IDs', () => {
|
|
374
|
+
class RequestScopedService {
|
|
375
|
+
constructor() {
|
|
376
|
+
this.id = Math.random();
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
container.registerProvider({
|
|
380
|
+
token: RequestScopedService,
|
|
381
|
+
useClass: RequestScopedService,
|
|
382
|
+
scope: container_1.Scope.REQUEST,
|
|
383
|
+
});
|
|
384
|
+
const instance1 = container.resolve(RequestScopedService, 'request-1');
|
|
385
|
+
const instance2 = container.resolve(RequestScopedService, 'request-1');
|
|
386
|
+
const instance3 = container.resolve(RequestScopedService, 'request-2');
|
|
387
|
+
expect(instance1).toBe(instance2);
|
|
388
|
+
expect(instance1).not.toBe(instance3);
|
|
389
|
+
});
|
|
390
|
+
it('should handle useValue provider', () => {
|
|
391
|
+
const configValue = { apiKey: 'test-key', timeout: 5000 };
|
|
392
|
+
container.registerProvider({
|
|
393
|
+
token: 'CONFIG',
|
|
394
|
+
useValue: configValue,
|
|
395
|
+
});
|
|
396
|
+
const resolved = container.resolve('CONFIG');
|
|
397
|
+
expect(resolved).toBe(configValue);
|
|
398
|
+
});
|
|
399
|
+
it('should handle useFactory with dependencies', () => {
|
|
400
|
+
class ConfigService {
|
|
401
|
+
getConfig() {
|
|
402
|
+
return { env: 'test' };
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
container.registerProvider({
|
|
406
|
+
token: ConfigService,
|
|
407
|
+
useClass: ConfigService,
|
|
408
|
+
});
|
|
409
|
+
container.registerProvider({
|
|
410
|
+
token: 'APP_CONFIG',
|
|
411
|
+
useFactory: (...args) => {
|
|
412
|
+
const configService = args[0];
|
|
413
|
+
return { ...configService.getConfig(), appName: 'TestApp' };
|
|
414
|
+
},
|
|
415
|
+
inject: [ConfigService],
|
|
416
|
+
});
|
|
417
|
+
const config = container.resolve('APP_CONFIG');
|
|
418
|
+
expect(config).toEqual({ env: 'test', appName: 'TestApp' });
|
|
419
|
+
});
|
|
420
|
+
it('should handle complex dependency chains', () => {
|
|
421
|
+
class Level1 {
|
|
422
|
+
}
|
|
423
|
+
class Level2 {
|
|
424
|
+
constructor(level1) {
|
|
425
|
+
this.level1 = level1;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
class Level3 {
|
|
429
|
+
constructor(level2) {
|
|
430
|
+
this.level2 = level2;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
Reflect.defineMetadata('design:paramtypes', [Level1], Level2);
|
|
434
|
+
Reflect.defineMetadata('design:paramtypes', [Level2], Level3);
|
|
435
|
+
container.registerProvider({ token: Level1, useClass: Level1 });
|
|
436
|
+
container.registerProvider({ token: Level2, useClass: Level2 });
|
|
437
|
+
container.registerProvider({ token: Level3, useClass: Level3 });
|
|
438
|
+
const instance = container.resolve(Level3);
|
|
439
|
+
expect(instance).toBeDefined();
|
|
440
|
+
expect(instance.level2).toBeDefined();
|
|
441
|
+
expect(instance.level2.level1).toBeDefined();
|
|
442
|
+
});
|
|
443
|
+
it('should handle providers with no dependencies', () => {
|
|
444
|
+
class SimpleService {
|
|
445
|
+
getValue() {
|
|
446
|
+
return 'simple';
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
container.registerProvider({ token: SimpleService, useClass: SimpleService });
|
|
450
|
+
const instance = container.resolve(SimpleService);
|
|
451
|
+
expect(instance.getValue()).toBe('simple');
|
|
452
|
+
});
|
|
453
|
+
});
|
|
454
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decorators.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/decorators.test.ts"],"names":[],"mappings":"AAmBA,OAAO,kBAAkB,CAAC"}
|