@oalacea/daemon 0.7.2 → 0.7.3
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/dist/prompts/DEPS_EFFICIENCY.md +558 -0
- package/dist/prompts/E2E.md +491 -0
- package/dist/prompts/EXECUTE.md +1060 -0
- package/dist/prompts/INTEGRATION_API.md +484 -0
- package/dist/prompts/INTEGRATION_DB.md +425 -0
- package/dist/prompts/PERF_API.md +433 -0
- package/dist/prompts/PERF_DB.md +430 -0
- package/dist/prompts/PERF_FRONT.md +357 -0
- package/dist/prompts/REMEDIATION.md +482 -0
- package/dist/prompts/UNIT.md +260 -0
- package/dist/templates/README.md +221 -0
- package/dist/templates/k6/load-test.js +54 -0
- package/dist/templates/nestjs/controller.spec.ts +203 -0
- package/dist/templates/nestjs/e2e/api.e2e-spec.ts +451 -0
- package/dist/templates/nestjs/e2e/auth.e2e-spec.ts +533 -0
- package/dist/templates/nestjs/fixtures/test-module.ts +311 -0
- package/dist/templates/nestjs/guard.spec.ts +314 -0
- package/dist/templates/nestjs/interceptor.spec.ts +458 -0
- package/dist/templates/nestjs/module.spec.ts +173 -0
- package/dist/templates/nestjs/pipe.spec.ts +474 -0
- package/dist/templates/nestjs/service.spec.ts +296 -0
- package/dist/templates/playwright/e2e.spec.ts +61 -0
- package/dist/templates/rust/Cargo.toml +72 -0
- package/dist/templates/rust/actix-controller.test.rs +114 -0
- package/dist/templates/rust/axum-handler.test.rs +117 -0
- package/dist/templates/rust/integration.test.rs +63 -0
- package/dist/templates/rust/rocket-route.test.rs +106 -0
- package/dist/templates/rust/unit.test.rs +38 -0
- package/dist/templates/vitest/angular-component.test.ts +38 -0
- package/dist/templates/vitest/api.test.ts +51 -0
- package/dist/templates/vitest/component.test.ts +27 -0
- package/dist/templates/vitest/hook.test.ts +36 -0
- package/dist/templates/vitest/solid-component.test.ts +34 -0
- package/dist/templates/vitest/svelte-component.test.ts +33 -0
- package/dist/templates/vitest/vue-component.test.ts +39 -0
- package/package.json +2 -2
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NestJS Interceptor Test Template
|
|
3
|
+
*
|
|
4
|
+
* Tests for NestJS interceptors following best practices:
|
|
5
|
+
* - Request/response transformation
|
|
6
|
+
* - Logging and monitoring
|
|
7
|
+
* - Caching behavior
|
|
8
|
+
* - Timeout handling
|
|
9
|
+
*
|
|
10
|
+
* @package test
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { ExecutionContext, CallHandler } from '@nestjs/common';
|
|
14
|
+
import { of, Observable, throwError } from 'rxjs';
|
|
15
|
+
import { Test, TestingModule } from '@nestjs/testing';
|
|
16
|
+
import { LoggingInterceptor } from './logging.interceptor';
|
|
17
|
+
import { TransformInterceptor } from './transform.interceptor';
|
|
18
|
+
import { TimeoutInterceptor } from './timeout.interceptor';
|
|
19
|
+
import { CacheInterceptor } from './cache.interceptor';
|
|
20
|
+
|
|
21
|
+
describe('LoggingInterceptor', () => {
|
|
22
|
+
let interceptor: LoggingInterceptor;
|
|
23
|
+
let consoleSpy: jest.SpyInstance;
|
|
24
|
+
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
interceptor = new LoggingInterceptor();
|
|
27
|
+
consoleSpy = jest.spyOn(console, 'log').mockImplementation();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
afterEach(() => {
|
|
31
|
+
consoleSpy.mockRestore();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
describe('intercept', () => {
|
|
35
|
+
it('should log request start and end', (done) => {
|
|
36
|
+
const context = createMockExecutionContext();
|
|
37
|
+
const next: CallHandler = {
|
|
38
|
+
handle: () => of({ data: 'test' }),
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const result$ = interceptor.intercept(context, next) as Observable<any>;
|
|
42
|
+
|
|
43
|
+
result$.subscribe({
|
|
44
|
+
complete: () => {
|
|
45
|
+
expect(consoleSpy).toHaveBeenCalled();
|
|
46
|
+
done();
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should log request method and url', (done) => {
|
|
52
|
+
const request = {
|
|
53
|
+
method: 'GET',
|
|
54
|
+
url: '/test',
|
|
55
|
+
};
|
|
56
|
+
const context = createMockExecutionContext(request);
|
|
57
|
+
const next: CallHandler = {
|
|
58
|
+
handle: () => of({ data: 'test' }),
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const result$ = interceptor.intercept(context, next) as Observable<any>;
|
|
62
|
+
|
|
63
|
+
result$.subscribe({
|
|
64
|
+
complete: () => {
|
|
65
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
66
|
+
expect.stringContaining('GET')
|
|
67
|
+
);
|
|
68
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
69
|
+
expect.stringContaining('/test')
|
|
70
|
+
);
|
|
71
|
+
done();
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('should log response time', (done) => {
|
|
77
|
+
const context = createMockExecutionContext();
|
|
78
|
+
const next: CallHandler = {
|
|
79
|
+
handle: () => of({ data: 'test' }),
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const startTime = Date.now();
|
|
83
|
+
|
|
84
|
+
const result$ = interceptor.intercept(context, next) as Observable<any>;
|
|
85
|
+
|
|
86
|
+
result$.subscribe({
|
|
87
|
+
complete: () => {
|
|
88
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
89
|
+
expect.stringContaining('ms')
|
|
90
|
+
);
|
|
91
|
+
done();
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('should log errors', (done) => {
|
|
97
|
+
const context = createMockExecutionContext();
|
|
98
|
+
const error = new Error('Test error');
|
|
99
|
+
const next: CallHandler = {
|
|
100
|
+
handle: () => throwError(() => error),
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const result$ = interceptor.intercept(context, next) as Observable<any>;
|
|
104
|
+
|
|
105
|
+
result$.subscribe({
|
|
106
|
+
error: () => {
|
|
107
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
108
|
+
expect.stringContaining('Error')
|
|
109
|
+
);
|
|
110
|
+
done();
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
describe('log formatting', () => {
|
|
117
|
+
it('should format logs with timestamp', (done) => {
|
|
118
|
+
const context = createMockExecutionContext();
|
|
119
|
+
const next: CallHandler = {
|
|
120
|
+
handle: () => of({ data: 'test' }),
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const result$ = interceptor.intercept(context, next) as Observable<any>;
|
|
124
|
+
|
|
125
|
+
result$.subscribe({
|
|
126
|
+
complete: () => {
|
|
127
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
128
|
+
expect.stringMatching(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/)
|
|
129
|
+
);
|
|
130
|
+
done();
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('should include correlation ID if present', (done) => {
|
|
136
|
+
const request = {
|
|
137
|
+
headers: {
|
|
138
|
+
'x-correlation-id': 'test-123',
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
const context = createMockExecutionContext(request);
|
|
142
|
+
const next: CallHandler = {
|
|
143
|
+
handle: () => of({ data: 'test' }),
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
const result$ = interceptor.intercept(context, next) as Observable<any>;
|
|
147
|
+
|
|
148
|
+
result$.subscribe({
|
|
149
|
+
complete: () => {
|
|
150
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
151
|
+
expect.stringContaining('test-123')
|
|
152
|
+
);
|
|
153
|
+
done();
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
describe('TransformInterceptor', () => {
|
|
161
|
+
let interceptor: TransformInterceptor;
|
|
162
|
+
|
|
163
|
+
beforeEach(() => {
|
|
164
|
+
interceptor = new TransformInterceptor();
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
describe('intercept', () => {
|
|
168
|
+
it('should wrap response data', (done) => {
|
|
169
|
+
const context = createMockExecutionContext();
|
|
170
|
+
const next: CallHandler = {
|
|
171
|
+
handle: () => of({ data: 'test' }),
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const result$ = interceptor.intercept(context, next) as Observable<any>;
|
|
175
|
+
|
|
176
|
+
result$.subscribe({
|
|
177
|
+
next: (data) => {
|
|
178
|
+
expect(data).toHaveProperty('data');
|
|
179
|
+
expect(data.data).toEqual({ data: 'test' });
|
|
180
|
+
done();
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('should include timestamp in response', (done) => {
|
|
186
|
+
const context = createMockExecutionContext();
|
|
187
|
+
const next: CallHandler = {
|
|
188
|
+
handle: () => of({ data: 'test' }),
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
const result$ = interceptor.intercept(context, next) as Observable<any>;
|
|
192
|
+
|
|
193
|
+
result$.subscribe({
|
|
194
|
+
next: (data) => {
|
|
195
|
+
expect(data).toHaveProperty('timestamp');
|
|
196
|
+
expect(typeof data.timestamp).toBe('number');
|
|
197
|
+
done();
|
|
198
|
+
},
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it('should include status code', (done) => {
|
|
203
|
+
const context = createMockExecutionContext();
|
|
204
|
+
const next: CallHandler = {
|
|
205
|
+
handle: () => of({ data: 'test' }),
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
const result$ = interceptor.intercept(context, next) as Observable<any>;
|
|
209
|
+
|
|
210
|
+
result$.subscribe({
|
|
211
|
+
next: (data) => {
|
|
212
|
+
expect(data).toHaveProperty('statusCode');
|
|
213
|
+
done();
|
|
214
|
+
},
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it('should handle null responses', (done) => {
|
|
219
|
+
const context = createMockExecutionContext();
|
|
220
|
+
const next: CallHandler = {
|
|
221
|
+
handle: () => of(null),
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
const result$ = interceptor.intercept(context, next) as Observable<any>;
|
|
225
|
+
|
|
226
|
+
result$.subscribe({
|
|
227
|
+
next: (data) => {
|
|
228
|
+
expect(data).toHaveProperty('data');
|
|
229
|
+
expect(data.data).toBeNull();
|
|
230
|
+
done();
|
|
231
|
+
},
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it('should handle array responses', (done) => {
|
|
236
|
+
const context = createMockExecutionContext();
|
|
237
|
+
const next: CallHandler = {
|
|
238
|
+
handle: () => of([{ id: 1 }, { id: 2 }]),
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
const result$ = interceptor.intercept(context, next) as Observable<any>;
|
|
242
|
+
|
|
243
|
+
result$.subscribe({
|
|
244
|
+
next: (data) => {
|
|
245
|
+
expect(data).toHaveProperty('data');
|
|
246
|
+
expect(Array.isArray(data.data)).toBe(true);
|
|
247
|
+
done();
|
|
248
|
+
},
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
describe('TimeoutInterceptor', () => {
|
|
255
|
+
let interceptor: TimeoutInterceptor;
|
|
256
|
+
|
|
257
|
+
beforeEach(() => {
|
|
258
|
+
interceptor = new TimeoutInterceptor(5000); // 5 second timeout
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
describe('intercept', () => {
|
|
262
|
+
it('should complete requests within timeout', (done) => {
|
|
263
|
+
const context = createMockExecutionContext();
|
|
264
|
+
const next: CallHandler = {
|
|
265
|
+
handle: () => of({ data: 'test' }),
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
const result$ = interceptor.intercept(context, next) as Observable<any>;
|
|
269
|
+
|
|
270
|
+
result$.subscribe({
|
|
271
|
+
next: (data) => {
|
|
272
|
+
expect(data).toEqual({ data: 'test' });
|
|
273
|
+
done();
|
|
274
|
+
},
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it('should timeout slow requests', (done) => {
|
|
279
|
+
const context = createMockExecutionContext();
|
|
280
|
+
const next: CallHandler = {
|
|
281
|
+
handle: () =>
|
|
282
|
+
new Observable((subscriber) => {
|
|
283
|
+
setTimeout(() => subscriber.next({ data: 'test' }), 10000);
|
|
284
|
+
}),
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
const result$ = interceptor.intercept(context, next) as Observable<any>;
|
|
288
|
+
|
|
289
|
+
result$.subscribe({
|
|
290
|
+
error: (error) => {
|
|
291
|
+
expect(error).toBeDefined();
|
|
292
|
+
done();
|
|
293
|
+
},
|
|
294
|
+
});
|
|
295
|
+
}, 6000);
|
|
296
|
+
|
|
297
|
+
it('should handle timeout from constructor', () => {
|
|
298
|
+
const customInterceptor = new TimeoutInterceptor(1000);
|
|
299
|
+
expect(customInterceptor).toBeDefined();
|
|
300
|
+
});
|
|
301
|
+
});
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
describe('CacheInterceptor', () => {
|
|
305
|
+
let interceptor: CacheInterceptor;
|
|
306
|
+
let cacheManager: any;
|
|
307
|
+
|
|
308
|
+
beforeEach(() => {
|
|
309
|
+
cacheManager = {
|
|
310
|
+
get: jest.fn(),
|
|
311
|
+
set: jest.fn(),
|
|
312
|
+
};
|
|
313
|
+
interceptor = new CacheInterceptor(cacheManager);
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
describe('intercept', () => {
|
|
317
|
+
it('should return cached value if available', (done) => {
|
|
318
|
+
const context = createMockExecutionContext();
|
|
319
|
+
const cachedValue = { data: 'cached' };
|
|
320
|
+
|
|
321
|
+
jest.spyOn(interceptor, 'getCacheKey').mockReturnValue('cache-key');
|
|
322
|
+
cacheManager.get.mockResolvedValue(cachedValue);
|
|
323
|
+
|
|
324
|
+
const next: CallHandler = {
|
|
325
|
+
handle: () => of({ data: 'fresh' }),
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
const result$ = interceptor.intercept(context, next) as Observable<any>;
|
|
329
|
+
|
|
330
|
+
result$.subscribe({
|
|
331
|
+
next: (data) => {
|
|
332
|
+
expect(data).toEqual(cachedValue);
|
|
333
|
+
expect(cacheManager.get).toHaveBeenCalledWith('cache-key');
|
|
334
|
+
expect(cacheManager.set).not.toHaveBeenCalled();
|
|
335
|
+
done();
|
|
336
|
+
},
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
it('should cache fresh values', (done) => {
|
|
341
|
+
const context = createMockExecutionContext();
|
|
342
|
+
const freshValue = { data: 'fresh' };
|
|
343
|
+
|
|
344
|
+
jest.spyOn(interceptor, 'getCacheKey').mockReturnValue('cache-key');
|
|
345
|
+
cacheManager.get.mockResolvedValue(null);
|
|
346
|
+
|
|
347
|
+
const next: CallHandler = {
|
|
348
|
+
handle: () => of(freshValue),
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
const result$ = interceptor.intercept(context, next) as Observable<any>;
|
|
352
|
+
|
|
353
|
+
result$.subscribe({
|
|
354
|
+
next: (data) => {
|
|
355
|
+
expect(data).toEqual(freshValue);
|
|
356
|
+
expect(cacheManager.set).toHaveBeenCalled();
|
|
357
|
+
done();
|
|
358
|
+
},
|
|
359
|
+
});
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
it('should respect cache TTL', (done) => {
|
|
363
|
+
const context = createMockExecutionContext();
|
|
364
|
+
const freshValue = { data: 'fresh' };
|
|
365
|
+
|
|
366
|
+
jest.spyOn(interceptor, 'getCacheKey').mockReturnValue('cache-key');
|
|
367
|
+
cacheManager.get.mockResolvedValue(null);
|
|
368
|
+
|
|
369
|
+
const next: CallHandler = {
|
|
370
|
+
handle: () => of(freshValue),
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
const result$ = interceptor.intercept(context, next) as Observable<any>;
|
|
374
|
+
|
|
375
|
+
result$.subscribe({
|
|
376
|
+
next: () => {
|
|
377
|
+
expect(cacheManager.set).toHaveBeenCalledWith(
|
|
378
|
+
'cache-key',
|
|
379
|
+
freshValue,
|
|
380
|
+
expect.any(Number)
|
|
381
|
+
);
|
|
382
|
+
done();
|
|
383
|
+
},
|
|
384
|
+
});
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
it('should skip cache for POST requests', (done) => {
|
|
388
|
+
const request = { method: 'POST' };
|
|
389
|
+
const context = createMockExecutionContext(request);
|
|
390
|
+
const next: CallHandler = {
|
|
391
|
+
handle: () => of({ data: 'test' }),
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
const result$ = interceptor.intercept(context, next) as Observable<any>;
|
|
395
|
+
|
|
396
|
+
result$.subscribe({
|
|
397
|
+
next: (data) => {
|
|
398
|
+
expect(cacheManager.get).not.toHaveBeenCalled();
|
|
399
|
+
expect(cacheManager.set).not.toHaveBeenCalled();
|
|
400
|
+
done();
|
|
401
|
+
},
|
|
402
|
+
});
|
|
403
|
+
});
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
describe('cache key generation', () => {
|
|
407
|
+
it('should generate cache key from request', () => {
|
|
408
|
+
const request = {
|
|
409
|
+
method: 'GET',
|
|
410
|
+
url: '/test',
|
|
411
|
+
query: { page: '1' },
|
|
412
|
+
};
|
|
413
|
+
const context = createMockExecutionContext(request);
|
|
414
|
+
|
|
415
|
+
const cacheKey = interceptor.getCacheKey(context);
|
|
416
|
+
|
|
417
|
+
expect(cacheKey).toContain('GET');
|
|
418
|
+
expect(cacheKey).toContain('/test');
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
it('should handle cache key with user-specific data', () => {
|
|
422
|
+
const request = {
|
|
423
|
+
method: 'GET',
|
|
424
|
+
url: '/test',
|
|
425
|
+
user: { id: '123' },
|
|
426
|
+
};
|
|
427
|
+
const context = createMockExecutionContext(request);
|
|
428
|
+
|
|
429
|
+
const cacheKey = interceptor.getCacheKey(context);
|
|
430
|
+
|
|
431
|
+
expect(cacheKey).toContain('123');
|
|
432
|
+
});
|
|
433
|
+
});
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
// Helper function to create mock ExecutionContext
|
|
437
|
+
function createMockExecutionContext(request: any = {}): ExecutionContext {
|
|
438
|
+
return {
|
|
439
|
+
switchToHttp: () => ({
|
|
440
|
+
getRequest: () => ({
|
|
441
|
+
method: request.method || 'GET',
|
|
442
|
+
url: request.url || '/',
|
|
443
|
+
headers: request.headers || {},
|
|
444
|
+
query: request.query || {},
|
|
445
|
+
user: request.user,
|
|
446
|
+
...request,
|
|
447
|
+
}),
|
|
448
|
+
getResponse: () => ({
|
|
449
|
+
statusCode: 200,
|
|
450
|
+
}),
|
|
451
|
+
}),
|
|
452
|
+
getHandler: () => ({}),
|
|
453
|
+
getClass: () => ({}),
|
|
454
|
+
getArgByIndex: () => ({}),
|
|
455
|
+
getArgs: () => [],
|
|
456
|
+
getType: () => 'http',
|
|
457
|
+
} as unknown as ExecutionContext;
|
|
458
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NestJS Module Test Template
|
|
3
|
+
*
|
|
4
|
+
* Tests for NestJS modules following best practices:
|
|
5
|
+
* - Provider availability
|
|
6
|
+
* - Dependency injection
|
|
7
|
+
* - Module configuration
|
|
8
|
+
*
|
|
9
|
+
* @package test
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { Test, TestingModule } from '@nestjs/testing';
|
|
13
|
+
import { GameModule } from './game.module';
|
|
14
|
+
import { GameController } from './game.controller';
|
|
15
|
+
import { GameService } from './game.service';
|
|
16
|
+
import { GameRepository } from './game.repository';
|
|
17
|
+
import { ConfigModule } from '@nestjs/config';
|
|
18
|
+
|
|
19
|
+
describe('GameModule', () => {
|
|
20
|
+
let module: TestingModule;
|
|
21
|
+
|
|
22
|
+
beforeEach(async () => {
|
|
23
|
+
module = await Test.createTestingModule({
|
|
24
|
+
imports: [GameModule],
|
|
25
|
+
}).compile();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
afterEach(async () => {
|
|
29
|
+
if (module) {
|
|
30
|
+
await module.close();
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should be defined', () => {
|
|
35
|
+
expect(module.get(GameModule)).toBeDefined();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should instantiate the controller', () => {
|
|
39
|
+
const controller = module.get<GameController>(GameController);
|
|
40
|
+
expect(controller).toBeDefined();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should instantiate the service', () => {
|
|
44
|
+
const service = module.get<GameService>(GameService);
|
|
45
|
+
expect(service).toBeDefined();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('should instantiate the repository', () => {
|
|
49
|
+
const repository = module.get<GameRepository>(GameRepository);
|
|
50
|
+
expect(repository).toBeDefined();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should load configuration', () => {
|
|
54
|
+
const configModule = module.get(ConfigModule);
|
|
55
|
+
expect(configModule).toBeDefined();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe('dependency injection', () => {
|
|
59
|
+
it('should inject service into controller', () => {
|
|
60
|
+
const controller = module.get<GameController>(GameController);
|
|
61
|
+
const service = module.get<GameService>(GameService);
|
|
62
|
+
|
|
63
|
+
expect(controller).toHaveProperty('gameService');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should inject repository into service', () => {
|
|
67
|
+
const service = module.get<GameService>(GameService);
|
|
68
|
+
const repository = module.get<GameRepository>(GameRepository);
|
|
69
|
+
|
|
70
|
+
expect(service).toBeDefined();
|
|
71
|
+
expect(repository).toBeDefined();
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe('module configuration', () => {
|
|
76
|
+
it('should be able to create module with custom imports', async () => {
|
|
77
|
+
const customModule: TestingModule = await Test.createTestingModule({
|
|
78
|
+
imports: [GameModule],
|
|
79
|
+
})
|
|
80
|
+
.overrideProvider(GameRepository)
|
|
81
|
+
.useValue({})
|
|
82
|
+
.compile();
|
|
83
|
+
|
|
84
|
+
expect(customModule).toBeDefined();
|
|
85
|
+
await customModule.close();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('should handle module providers correctly', async () => {
|
|
89
|
+
const testModule = await Test.createTestingModule({
|
|
90
|
+
imports: [GameModule],
|
|
91
|
+
}).compile();
|
|
92
|
+
|
|
93
|
+
const providers = testModule.get<GameModule>(GameModule)['providers'];
|
|
94
|
+
|
|
95
|
+
expect(providers).toBeDefined();
|
|
96
|
+
expect(Array.isArray(providers)).toBe(true);
|
|
97
|
+
|
|
98
|
+
await testModule.close();
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
describe('global modules', () => {
|
|
103
|
+
it('should handle global module providers if applicable', async () => {
|
|
104
|
+
const globalModule = await Test.createTestingModule({
|
|
105
|
+
imports: [GameModule],
|
|
106
|
+
})
|
|
107
|
+
.overrideProvider(GameRepository)
|
|
108
|
+
.useClass(GameRepository) // Use actual implementation
|
|
109
|
+
.compile();
|
|
110
|
+
|
|
111
|
+
expect(globalModule).toBeDefined();
|
|
112
|
+
await globalModule.close();
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
describe('circular dependencies', () => {
|
|
117
|
+
it('should resolve forwardRef if present', async () => {
|
|
118
|
+
// For modules with circular dependencies using forwardRef
|
|
119
|
+
try {
|
|
120
|
+
const circularModule = await Test.createTestingModule({
|
|
121
|
+
imports: [GameModule],
|
|
122
|
+
}).compile();
|
|
123
|
+
|
|
124
|
+
expect(circularModule).toBeDefined();
|
|
125
|
+
await circularModule.close();
|
|
126
|
+
} catch (error) {
|
|
127
|
+
// If forwardRef is not properly handled, this will fail
|
|
128
|
+
fail('Module should resolve circular dependencies');
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
describe('dynamic modules', () => {
|
|
134
|
+
it('should register dynamic providers if applicable', async () => {
|
|
135
|
+
// For modules that use .register() or .forRoot()
|
|
136
|
+
const dynamicModule = await Test.createTestingModule({
|
|
137
|
+
imports: [
|
|
138
|
+
GameModule.register({
|
|
139
|
+
apiKey: 'test-key',
|
|
140
|
+
apiUrl: 'https://api.test.com',
|
|
141
|
+
}),
|
|
142
|
+
],
|
|
143
|
+
}).compile();
|
|
144
|
+
|
|
145
|
+
expect(dynamicModule).toBeDefined();
|
|
146
|
+
await dynamicModule.close();
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('should handle .forRoot() pattern if applicable', async () => {
|
|
150
|
+
const forRootModule = await Test.createTestingModule({
|
|
151
|
+
imports: [GameModule.forRoot()],
|
|
152
|
+
}).compile();
|
|
153
|
+
|
|
154
|
+
expect(forRootModule).toBeDefined();
|
|
155
|
+
await forRootModule.close();
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('should handle .forRootAsync() pattern if applicable', async () => {
|
|
159
|
+
const forRootAsyncModule = await Test.createTestingModule({
|
|
160
|
+
imports: [
|
|
161
|
+
GameModule.forRootAsync({
|
|
162
|
+
useFactory: () => ({
|
|
163
|
+
apiKey: 'test-key',
|
|
164
|
+
}),
|
|
165
|
+
}),
|
|
166
|
+
],
|
|
167
|
+
}).compile();
|
|
168
|
+
|
|
169
|
+
expect(forRootAsyncModule).toBeDefined();
|
|
170
|
+
await forRootAsyncModule.close();
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
});
|