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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 575 - (hide 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 rakinar2 575 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