/[sudobot]/trunk/tests/framework/container/Container.test.ts
ViewVC logotype

Contents of /trunk/tests/framework/container/Container.test.ts

Parent Directory Parent Directory | Revision Log Revision Log


Revision 575 - (show annotations)
Mon Jul 29 17:59:26 2024 UTC (8 months ago) by rakinar2
File MIME type: application/typescript
File size: 11590 byte(s)
chore: add trunk
1 import "reflect-metadata";
2
3 import Container from "@framework/container/Container";
4 import crypto from "crypto";
5 import { afterEach, beforeEach, describe, expect, it } from "vitest";
6
7 describe("container bindings", () => {
8 let container: Container;
9
10 beforeEach(() => {
11 container = Container.getInstance();
12 });
13
14 afterEach(() => {
15 Container.destroyGlobalContainer();
16 });
17
18 it("should bind and resolve a class instance", () => {
19 class MyClass {}
20
21 container.bind(MyClass);
22 const instance = container.resolveByClass(MyClass);
23
24 expect(instance).toBeInstanceOf(MyClass);
25 });
26
27 it("should bind and resolve a singleton class instance", () => {
28 class MyClass {}
29
30 container.bind(MyClass, { singleton: true });
31 const instance1 = container.resolveByClass(MyClass);
32 const instance2 = container.resolveByClass(MyClass);
33
34 expect(instance1).toBeInstanceOf(MyClass);
35 expect(instance2).toBe(instance1);
36 });
37
38 it("should bind and resolve a class instance with a custom key", () => {
39 class MyClass {}
40
41 container.bind(MyClass, { key: "myClass", singleton: true });
42
43 const instance = container.resolve("myClass");
44 expect(instance).toBeInstanceOf(MyClass);
45
46 const instance2 = container.resolveByClass(MyClass);
47 expect(instance2).toBeInstanceOf(MyClass);
48
49 expect(instance2).toBe(instance);
50 });
51
52 it("should implicitly resolve a class instance if not bound", () => {
53 class MyClass {}
54
55 const instance = container.resolveByClass(MyClass);
56 expect(instance).toBeInstanceOf(MyClass);
57 });
58 });
59
60 describe("container bindings with dependencies", () => {
61 let container: Container;
62
63 beforeEach(() => {
64 container = Container.getInstance();
65 });
66
67 afterEach(() => {
68 Container.destroyGlobalContainer();
69 });
70
71 it("should bind and resolve a class instance with dependencies", () => {
72 class MyClass {}
73
74 class MyDependentClass {
75 public constructor(public myClass: MyClass) {}
76 }
77
78 Reflect.defineMetadata("design:paramtypes", [MyClass], MyDependentClass);
79
80 container.bind(MyClass);
81 container.bind(MyDependentClass);
82
83 const instance = container.resolveByClass(MyDependentClass);
84
85 expect(instance).toBeInstanceOf(MyDependentClass);
86 expect(instance.myClass).toBeInstanceOf(MyClass);
87 });
88
89 it("should bind and resolve a class instance with singleton dependencies", () => {
90 class MyClass {}
91 class MyClass2 {}
92
93 class MyDependentClass {
94 public constructor(
95 public myClass: MyClass,
96 public myClass2: MyClass2
97 ) {}
98 }
99
100 Reflect.defineMetadata("design:paramtypes", [MyClass, MyClass2], MyDependentClass);
101
102 container.bind(MyClass, { singleton: true });
103 container.bind(MyClass2, { singleton: true });
104 container.bind(MyDependentClass, { singleton: true });
105
106 const instance1 = container.resolveByClass(MyDependentClass);
107 const instance2 = container.resolveByClass(MyDependentClass);
108
109 expect(instance1).toBeInstanceOf(MyDependentClass);
110 expect(instance2).toBe(instance1);
111 expect(instance1.myClass).toBeInstanceOf(MyClass);
112 expect(instance2.myClass).toBe(instance1.myClass);
113 expect(instance1.myClass2).toBeInstanceOf(MyClass2);
114 expect(instance2.myClass2).toBe(instance1.myClass2);
115 });
116
117 it("should bind and resolve a class instance with deep and circular dependencies", () => {
118 class Level0Dep0 {}
119 class Level0Dep1 {}
120
121 class Level1Dep0 {
122 public constructor(public level0Dep0: Level0Dep0) {}
123 }
124
125 Reflect.defineMetadata("design:paramtypes", [Level0Dep0], Level1Dep0);
126
127 class Level1Dep1 {
128 public constructor(
129 public level0Dep0: Level0Dep0,
130 public level0Dep1: Level0Dep1
131 ) {}
132 }
133
134 Reflect.defineMetadata("design:paramtypes", [Level0Dep0, Level0Dep1], Level1Dep1);
135
136 class Level2Dep0 {
137 public constructor(
138 public level1Dep0: Level1Dep0,
139 public level0Dep1: Level0Dep1
140 ) {}
141 }
142
143 Reflect.defineMetadata("design:paramtypes", [Level1Dep0, Level0Dep1], Level2Dep0);
144
145 class Level2Dep1 {
146 public constructor(
147 public level1Dep0: Level1Dep0,
148 public level0Dep0: Level0Dep0,
149 public level1Dep1: Level1Dep1
150 ) {}
151 }
152
153 Reflect.defineMetadata(
154 "design:paramtypes",
155 [Level1Dep0, Level0Dep0, Level1Dep1],
156 Level2Dep1
157 );
158
159 class Application {
160 public constructor(
161 public level2Dep1: Level2Dep1,
162 public level2Dep0: Level2Dep0
163 ) {}
164 }
165
166 Reflect.defineMetadata("design:paramtypes", [Level2Dep1, Level2Dep0], Application);
167
168 container.bind(Level0Dep0, { singleton: true });
169 container.bind(Level0Dep1, { singleton: true });
170 container.bind(Level1Dep0, { singleton: true });
171 container.bind(Level1Dep1, { singleton: true });
172 container.bind(Level2Dep0, { singleton: true });
173 container.bind(Level2Dep1, { singleton: true });
174 container.bind(Application);
175
176 const instance = container.resolveByClass(Application);
177
178 expect(instance).toBeInstanceOf(Application);
179
180 expect(instance.level2Dep0).toBeInstanceOf(Level2Dep0);
181 expect(instance.level2Dep1).toBeInstanceOf(Level2Dep1);
182
183 expect(instance.level2Dep0.level1Dep0).toBeInstanceOf(Level1Dep0);
184 expect(instance.level2Dep0.level0Dep1).toBeInstanceOf(Level0Dep1);
185
186 expect(instance.level2Dep1.level1Dep0).toBeInstanceOf(Level1Dep0);
187 expect(instance.level2Dep1.level0Dep0).toBeInstanceOf(Level0Dep0);
188 expect(instance.level2Dep1.level1Dep1).toBeInstanceOf(Level1Dep1);
189
190 expect(instance.level2Dep1.level1Dep1.level0Dep0).toBeInstanceOf(Level0Dep0);
191 expect(instance.level2Dep1.level1Dep1.level0Dep1).toBeInstanceOf(Level0Dep1);
192
193 expect(instance.level2Dep1.level1Dep1.level0Dep0).toBe(
194 instance.level2Dep0.level1Dep0.level0Dep0
195 );
196 expect(instance.level2Dep1.level1Dep1.level0Dep1).toBe(instance.level2Dep0.level0Dep1);
197
198 expect(instance.level2Dep0.level1Dep0.level0Dep0).toBe(
199 instance.level2Dep1.level1Dep0.level0Dep0
200 );
201 expect(instance.level2Dep0.level0Dep1).toBe(instance.level2Dep1.level1Dep1.level0Dep1);
202
203 expect(instance.level2Dep1.level1Dep0.level0Dep0).toBe(
204 instance.level2Dep0.level1Dep0.level0Dep0
205 );
206 expect(instance.level2Dep1.level1Dep1.level0Dep1).toBe(instance.level2Dep0.level0Dep1);
207
208 expect(instance.level2Dep1.level1Dep0.level0Dep0).toBe(
209 instance.level2Dep0.level1Dep0.level0Dep0
210 );
211 expect(instance.level2Dep1.level1Dep1.level0Dep1).toBe(instance.level2Dep0.level0Dep1);
212 });
213 });
214
215 describe("container bindings with factories", () => {
216 let container: Container;
217
218 beforeEach(() => {
219 container = Container.getInstance();
220 });
221
222 afterEach(() => {
223 Container.destroyGlobalContainer();
224 });
225
226 it("should bind and resolve a class instance with a factory", async () => {
227 class MyClass {
228 public constructor(public value: number) {}
229 }
230
231 await container.bind(MyClass, {
232 factory: () => new MyClass(crypto.getRandomValues(new Uint32Array(1))[0])
233 });
234
235 const instance = container.resolveByClass(MyClass);
236 const instance2 = container.resolveByClass(MyClass);
237
238 expect(instance).toBeInstanceOf(MyClass);
239 expect(instance2).toBeInstanceOf(MyClass);
240 expect(instance2).not.toBe(instance);
241 expect(instance.value).not.toBe(instance2.value);
242 });
243
244 it("should bind and resolve a singleton class instance with a factory", () => {
245 class MyClass {
246 public constructor(public value: number) {}
247 }
248
249 container.bind(MyClass, {
250 factory: () => new MyClass(Math.ceil(Math.random() * 100)),
251 singleton: true
252 });
253
254 const instance = container.resolveByClass(MyClass);
255 const instance2 = container.resolveByClass(MyClass);
256
257 expect(instance).toBeInstanceOf(MyClass);
258 expect(instance2).toBeInstanceOf(MyClass);
259 expect(instance2).toBe(instance);
260 expect(instance.value).toBe(instance2.value);
261 });
262 });
263
264 describe("global containers", () => {
265 it("should return the same container instance", () => {
266 const container1 = Container.getInstance();
267 const container2 = Container.getInstance();
268
269 expect(container2).toBe(container1);
270 });
271
272 it("should destroy the global container instance", () => {
273 const container1 = Container.getInstance();
274 Container.destroyGlobalContainer();
275 const container2 = Container.getInstance();
276
277 expect(container2).not.toBe(container1);
278 });
279 });
280
281 describe("container property injection", () => {
282 let container: Container;
283
284 beforeEach(() => {
285 container = Container.getInstance();
286 });
287
288 afterEach(() => {
289 Container.destroyGlobalContainer();
290 });
291
292 it("should inject a class instance with properties", () => {
293 class MyClass {
294 public value = 42;
295 }
296
297 class MyDependentClass {
298 @Container.Inject()
299 public myClass!: MyClass;
300 }
301
302 Reflect.defineMetadata("design:type", MyClass, MyDependentClass.prototype, "myClass");
303
304 container.bind(MyClass, { singleton: true });
305 container.bind(MyDependentClass, { singleton: true });
306
307 const instance = container.resolveByClass(MyDependentClass);
308
309 expect(instance).toBeInstanceOf(MyDependentClass);
310 expect(instance.myClass).toBeInstanceOf(MyClass);
311 expect(instance.myClass.value).toBe(42);
312
313 const instance2 = container.resolveByClass(MyDependentClass);
314
315 expect(instance2).toBe(instance);
316 expect(instance2.myClass).toBe(instance.myClass);
317 expect(instance2.myClass.value).toBe(instance.myClass.value);
318
319 instance.myClass.value = 100;
320
321 expect(instance2.myClass.value).toBe(100);
322 });
323
324 it("should inject a class instance with properties and singleton dependencies", () => {
325 class MyClass {
326 public value = 42;
327 }
328
329 class MyDependentClass {
330 @Container.Inject()
331 public myClass!: MyClass;
332 }
333
334 Reflect.defineMetadata("design:type", MyClass, MyDependentClass.prototype, "myClass");
335
336 container.bind(MyClass, { singleton: true });
337 container.bind(MyDependentClass, { singleton: true });
338
339 const instance = container.resolveByClass(MyDependentClass);
340
341 expect(instance).toBeInstanceOf(MyDependentClass);
342 expect(instance.myClass).toBeInstanceOf(MyClass);
343 expect(instance.myClass.value).toBe(42);
344
345 const instance2 = container.resolveByClass(MyDependentClass);
346
347 expect(instance2).toBe(instance);
348 expect(instance2.myClass).toBe(instance.myClass);
349 expect(instance2.myClass.value).toBe(instance.myClass.value);
350
351 instance.myClass.value = 100;
352
353 expect(instance2.myClass.value).toBe(100);
354 });
355 });

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26