@cinnabun/core 0.0.1 → 0.0.2
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/__tests__/autowired.test.d.ts +1 -0
- package/dist/__tests__/autowired.test.js +109 -0
- package/dist/__tests__/autowired.test.js.map +1 -0
- package/dist/__tests__/cinnabun-application.test.d.ts +1 -0
- package/dist/__tests__/cinnabun-application.test.js +96 -0
- package/dist/__tests__/cinnabun-application.test.js.map +1 -0
- package/dist/__tests__/cinnabun-factory.test.d.ts +1 -0
- package/dist/__tests__/cinnabun-factory.test.js +269 -0
- package/dist/__tests__/cinnabun-factory.test.js.map +1 -0
- package/dist/__tests__/circular-dependency.test.d.ts +1 -0
- package/dist/__tests__/circular-dependency.test.js +318 -0
- package/dist/__tests__/circular-dependency.test.js.map +1 -0
- package/dist/__tests__/compression.test.d.ts +1 -0
- package/dist/__tests__/compression.test.js +459 -0
- package/dist/__tests__/compression.test.js.map +1 -0
- package/dist/__tests__/config.test.d.ts +1 -0
- package/dist/__tests__/config.test.js +86 -0
- package/dist/__tests__/config.test.js.map +1 -0
- package/dist/__tests__/cors.test.d.ts +1 -0
- package/dist/__tests__/cors.test.js +575 -0
- package/dist/__tests__/cors.test.js.map +1 -0
- package/dist/__tests__/env-config.test.d.ts +1 -0
- package/dist/__tests__/env-config.test.js +367 -0
- package/dist/__tests__/env-config.test.js.map +1 -0
- package/dist/__tests__/exception.test.d.ts +1 -0
- package/dist/__tests__/exception.test.js +207 -0
- package/dist/__tests__/exception.test.js.map +1 -0
- package/dist/__tests__/guards-interceptors.test.d.ts +1 -0
- package/dist/__tests__/guards-interceptors.test.js +660 -0
- package/dist/__tests__/guards-interceptors.test.js.map +1 -0
- package/dist/__tests__/health-check.test.d.ts +1 -0
- package/dist/__tests__/health-check.test.js +240 -0
- package/dist/__tests__/health-check.test.js.map +1 -0
- package/dist/__tests__/http.test.d.ts +1 -0
- package/dist/__tests__/http.test.js +629 -0
- package/dist/__tests__/http.test.js.map +1 -0
- package/dist/__tests__/integration/e2e.test.d.ts +1 -0
- package/dist/__tests__/integration/e2e.test.js +192 -0
- package/dist/__tests__/integration/e2e.test.js.map +1 -0
- package/dist/__tests__/integration/performance.bench.d.ts +1 -0
- package/dist/__tests__/integration/performance.bench.js +129 -0
- package/dist/__tests__/integration/performance.bench.js.map +1 -0
- package/dist/__tests__/integration/validation.test.d.ts +1 -0
- package/dist/__tests__/integration/validation.test.js +133 -0
- package/dist/__tests__/integration/validation.test.js.map +1 -0
- package/dist/__tests__/lifecycle-management.test.d.ts +1 -0
- package/dist/__tests__/lifecycle-management.test.js +688 -0
- package/dist/__tests__/lifecycle-management.test.js.map +1 -0
- package/dist/__tests__/lifecycle.test.d.ts +1 -0
- package/dist/__tests__/lifecycle.test.js +196 -0
- package/dist/__tests__/lifecycle.test.js.map +1 -0
- package/dist/__tests__/logger.test.d.ts +1 -0
- package/dist/__tests__/logger.test.js +109 -0
- package/dist/__tests__/logger.test.js.map +1 -0
- package/dist/__tests__/middleware.test.d.ts +1 -0
- package/dist/__tests__/middleware.test.js +329 -0
- package/dist/__tests__/middleware.test.js.map +1 -0
- package/dist/__tests__/module.test.d.ts +1 -0
- package/dist/__tests__/module.test.js +280 -0
- package/dist/__tests__/module.test.js.map +1 -0
- package/dist/__tests__/plugin.test.d.ts +1 -0
- package/dist/__tests__/plugin.test.js +283 -0
- package/dist/__tests__/plugin.test.js.map +1 -0
- package/dist/__tests__/request-logger.test.d.ts +1 -0
- package/dist/__tests__/request-logger.test.js +342 -0
- package/dist/__tests__/request-logger.test.js.map +1 -0
- package/dist/__tests__/request-mapping.test.d.ts +1 -0
- package/dist/__tests__/request-mapping.test.js +201 -0
- package/dist/__tests__/request-mapping.test.js.map +1 -0
- package/dist/__tests__/routes.test.d.ts +1 -0
- package/dist/__tests__/routes.test.js +119 -0
- package/dist/__tests__/routes.test.js.map +1 -0
- package/dist/__tests__/scan-fixtures/controllers/hello.controller.d.ts +4 -0
- package/dist/__tests__/scan-fixtures/controllers/hello.controller.js +28 -0
- package/dist/__tests__/scan-fixtures/controllers/hello.controller.js.map +1 -0
- package/dist/__tests__/scan-fixtures/modules/feature.module.d.ts +6 -0
- package/dist/__tests__/scan-fixtures/modules/feature.module.js +28 -0
- package/dist/__tests__/scan-fixtures/modules/feature.module.js.map +1 -0
- package/dist/__tests__/scan-fixtures/services/greeting.service.d.ts +4 -0
- package/dist/__tests__/scan-fixtures/services/greeting.service.js +18 -0
- package/dist/__tests__/scan-fixtures/services/greeting.service.js.map +1 -0
- package/dist/__tests__/scanner.test.d.ts +1 -0
- package/dist/__tests__/scanner.test.js +49 -0
- package/dist/__tests__/scanner.test.js.map +1 -0
- package/dist/__tests__/validation.test.d.ts +1 -0
- package/dist/__tests__/validation.test.js +561 -0
- package/dist/__tests__/validation.test.js.map +1 -0
- package/dist/__tests__/websocket-auth.test.d.ts +1 -0
- package/dist/__tests__/websocket-auth.test.js +431 -0
- package/dist/__tests__/websocket-auth.test.js.map +1 -0
- package/dist/__tests__/websocket-decorators.test.d.ts +1 -0
- package/dist/__tests__/websocket-decorators.test.js +173 -0
- package/dist/__tests__/websocket-decorators.test.js.map +1 -0
- package/dist/__tests__/websocket-validation.test.d.ts +1 -0
- package/dist/__tests__/websocket-validation.test.js +827 -0
- package/dist/__tests__/websocket-validation.test.js.map +1 -0
- package/dist/__tests__/websocket.test.d.ts +1 -0
- package/dist/__tests__/websocket.test.js +415 -0
- package/dist/__tests__/websocket.test.js.map +1 -0
- package/dist/config/config.module.d.ts +2 -0
- package/dist/config/config.module.js +18 -0
- package/dist/config/config.module.js.map +1 -0
- package/dist/config/config.service.d.ts +15 -0
- package/dist/config/config.service.js +58 -0
- package/dist/config/config.service.js.map +1 -0
- package/dist/config/schemas.d.ts +107 -0
- package/dist/config/schemas.js +87 -0
- package/dist/config/schemas.js.map +1 -0
- package/dist/core/app.d.ts +44 -0
- package/dist/core/app.js +178 -0
- package/dist/core/app.js.map +1 -0
- package/dist/core/cinnabun-factory.d.ts +5 -0
- package/dist/core/cinnabun-factory.js +130 -0
- package/dist/core/cinnabun-factory.js.map +1 -0
- package/dist/core/config-loader.d.ts +2 -0
- package/dist/core/config-loader.js +76 -0
- package/dist/core/config-loader.js.map +1 -0
- package/dist/core/config.d.ts +12 -0
- package/dist/core/config.js +27 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/container.d.ts +10 -0
- package/dist/core/container.js +82 -0
- package/dist/core/container.js.map +1 -0
- package/dist/core/dependency-validator.d.ts +12 -0
- package/dist/core/dependency-validator.js +76 -0
- package/dist/core/dependency-validator.js.map +1 -0
- package/dist/core/guard.d.ts +3 -0
- package/dist/core/guard.js +2 -0
- package/dist/core/guard.js.map +1 -0
- package/dist/core/interceptor.d.ts +4 -0
- package/dist/core/interceptor.js +2 -0
- package/dist/core/interceptor.js.map +1 -0
- package/dist/core/logger.d.ts +15 -0
- package/dist/core/logger.js +71 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/module-resolver.d.ts +6 -0
- package/dist/core/module-resolver.js +67 -0
- package/dist/core/module-resolver.js.map +1 -0
- package/dist/core/plugin.d.ts +12 -0
- package/dist/core/plugin.js +2 -0
- package/dist/core/plugin.js.map +1 -0
- package/dist/core/router.d.ts +38 -0
- package/dist/core/router.js +406 -0
- package/dist/core/router.js.map +1 -0
- package/dist/core/scanner.d.ts +7 -0
- package/dist/core/scanner.js +83 -0
- package/dist/core/scanner.js.map +1 -0
- package/dist/core/shutdown-manager.d.ts +15 -0
- package/dist/core/shutdown-manager.js +68 -0
- package/dist/core/shutdown-manager.js.map +1 -0
- package/dist/core/websocket-handler.d.ts +41 -0
- package/dist/core/websocket-handler.js +242 -0
- package/dist/core/websocket-handler.js.map +1 -0
- package/dist/decorators/autowired.d.ts +3 -0
- package/dist/decorators/autowired.js +11 -0
- package/dist/decorators/autowired.js.map +1 -0
- package/dist/decorators/cinnabun-application.d.ts +14 -0
- package/dist/decorators/cinnabun-application.js +17 -0
- package/dist/decorators/cinnabun-application.js.map +1 -0
- package/dist/decorators/lifecycle.d.ts +2 -0
- package/dist/decorators/lifecycle.js +12 -0
- package/dist/decorators/lifecycle.js.map +1 -0
- package/dist/decorators/middleware.d.ts +2 -0
- package/dist/decorators/middleware.js +12 -0
- package/dist/decorators/middleware.js.map +1 -0
- package/dist/decorators/module.d.ts +10 -0
- package/dist/decorators/module.js +13 -0
- package/dist/decorators/module.js.map +1 -0
- package/dist/decorators/on-shutdown.d.ts +1 -0
- package/dist/decorators/on-shutdown.js +10 -0
- package/dist/decorators/on-shutdown.js.map +1 -0
- package/dist/decorators/params.d.ts +6 -0
- package/dist/decorators/params.js +31 -0
- package/dist/decorators/params.js.map +1 -0
- package/dist/decorators/request-mapping.d.ts +7 -0
- package/dist/decorators/request-mapping.js +34 -0
- package/dist/decorators/request-mapping.js.map +1 -0
- package/dist/decorators/response.d.ts +2 -0
- package/dist/decorators/response.js +17 -0
- package/dist/decorators/response.js.map +1 -0
- package/dist/decorators/rest-controller.d.ts +1 -0
- package/dist/decorators/rest-controller.js +19 -0
- package/dist/decorators/rest-controller.js.map +1 -0
- package/dist/decorators/routes.d.ts +5 -0
- package/dist/decorators/routes.js +19 -0
- package/dist/decorators/routes.js.map +1 -0
- package/dist/decorators/service.d.ts +1 -0
- package/dist/decorators/service.js +7 -0
- package/dist/decorators/service.js.map +1 -0
- package/dist/decorators/use-guard.d.ts +2 -0
- package/dist/decorators/use-guard.js +12 -0
- package/dist/decorators/use-guard.js.map +1 -0
- package/dist/decorators/use-interceptor.d.ts +2 -0
- package/dist/decorators/use-interceptor.js +12 -0
- package/dist/decorators/use-interceptor.js.map +1 -0
- package/dist/decorators/validate.d.ts +12 -0
- package/dist/decorators/validate.js +7 -0
- package/dist/decorators/validate.js.map +1 -0
- package/dist/decorators/websocket.d.ts +9 -0
- package/dist/decorators/websocket.js +38 -0
- package/dist/decorators/websocket.js.map +1 -0
- package/dist/decorators/ws-event.d.ts +28 -0
- package/dist/decorators/ws-event.js +37 -0
- package/dist/decorators/ws-event.js.map +1 -0
- package/dist/decorators/ws-gateway.d.ts +18 -0
- package/dist/decorators/ws-gateway.js +24 -0
- package/dist/decorators/ws-gateway.js.map +1 -0
- package/dist/dev/index.d.ts +6 -0
- package/dist/dev/index.js +28 -0
- package/dist/dev/index.js.map +1 -0
- package/dist/exceptions/circular-dependency-error.d.ts +5 -0
- package/dist/exceptions/circular-dependency-error.js +16 -0
- package/dist/exceptions/circular-dependency-error.js.map +1 -0
- package/dist/exceptions/http-exception.d.ts +41 -0
- package/dist/exceptions/http-exception.js +96 -0
- package/dist/exceptions/http-exception.js.map +1 -0
- package/dist/guards/jwt-websocket.guard.d.ts +11 -0
- package/dist/guards/jwt-websocket.guard.js +37 -0
- package/dist/guards/jwt-websocket.guard.js.map +1 -0
- package/dist/guards/websocket-auth.guard.d.ts +16 -0
- package/dist/guards/websocket-auth.guard.js +43 -0
- package/dist/guards/websocket-auth.guard.js.map +1 -0
- package/dist/health/health-check.service.d.ts +45 -0
- package/dist/health/health-check.service.js +95 -0
- package/dist/health/health-check.service.js.map +1 -0
- package/dist/health/health.controller.d.ts +15 -0
- package/dist/health/health.controller.js +63 -0
- package/dist/health/health.controller.js.map +1 -0
- package/dist/health/health.module.d.ts +2 -0
- package/dist/health/health.module.js +20 -0
- package/dist/health/health.module.js.map +1 -0
- package/dist/index.d.ts +74 -11
- package/dist/index.js +54 -0
- package/dist/index.js.map +1 -0
- package/dist/metadata/storage.d.ts +171 -0
- package/dist/metadata/storage.js +257 -0
- package/dist/metadata/storage.js.map +1 -0
- package/dist/middleware/compression.middleware.d.ts +32 -0
- package/dist/middleware/compression.middleware.js +113 -0
- package/dist/middleware/compression.middleware.js.map +1 -0
- package/dist/middleware/cors.middleware.d.ts +18 -0
- package/dist/middleware/cors.middleware.js +79 -0
- package/dist/middleware/cors.middleware.js.map +1 -0
- package/dist/middleware/performance-tracker.middleware.d.ts +35 -0
- package/dist/middleware/performance-tracker.middleware.js +79 -0
- package/dist/middleware/performance-tracker.middleware.js.map +1 -0
- package/dist/middleware/request-logger.middleware.d.ts +32 -0
- package/dist/middleware/request-logger.middleware.js +125 -0
- package/dist/middleware/request-logger.middleware.js.map +1 -0
- package/dist/types/index.d.ts +14 -0
- package/dist/types/index.js +5 -0
- package/dist/types/index.js.map +1 -0
- package/dist/validation/helpers.d.ts +36 -0
- package/dist/validation/helpers.js +27 -0
- package/dist/validation/helpers.js.map +1 -0
- package/dist/websocket/error.d.ts +27 -0
- package/dist/websocket/error.js +38 -0
- package/dist/websocket/error.js.map +1 -0
- package/package.json +38 -5
- package/LICENSE +0 -9
|
@@ -0,0 +1,688 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
import "reflect-metadata";
|
|
11
|
+
import { describe, it, expect, afterEach } from "bun:test";
|
|
12
|
+
import { CinnabunFactory } from "../core/cinnabun-factory.js";
|
|
13
|
+
import { Service } from "../decorators/service.js";
|
|
14
|
+
import { RestController } from "../decorators/rest-controller.js";
|
|
15
|
+
import { GetMapping } from "../decorators/routes.js";
|
|
16
|
+
import { PreDestroy } from "../decorators/lifecycle.js";
|
|
17
|
+
import { OnShutdown } from "../decorators/on-shutdown.js";
|
|
18
|
+
import { CinnabunApplication as CinnabunAppDecorator } from "../decorators/cinnabun-application.js";
|
|
19
|
+
let app = null;
|
|
20
|
+
afterEach(async () => {
|
|
21
|
+
if (app) {
|
|
22
|
+
await app.close();
|
|
23
|
+
app = null;
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
describe("Lifecycle Management", () => {
|
|
27
|
+
describe("Graceful Shutdown", () => {
|
|
28
|
+
it("calls PreDestroy hooks on shutdown", async () => {
|
|
29
|
+
let cleaned = false;
|
|
30
|
+
let TestService = class TestService {
|
|
31
|
+
async cleanup() {
|
|
32
|
+
cleaned = true;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
__decorate([
|
|
36
|
+
PreDestroy(),
|
|
37
|
+
__metadata("design:type", Function),
|
|
38
|
+
__metadata("design:paramtypes", []),
|
|
39
|
+
__metadata("design:returntype", Promise)
|
|
40
|
+
], TestService.prototype, "cleanup", null);
|
|
41
|
+
TestService = __decorate([
|
|
42
|
+
Service()
|
|
43
|
+
], TestService);
|
|
44
|
+
let App = class App {
|
|
45
|
+
service;
|
|
46
|
+
constructor(service) {
|
|
47
|
+
this.service = service;
|
|
48
|
+
}
|
|
49
|
+
index() {
|
|
50
|
+
return { status: "ok" };
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
__decorate([
|
|
54
|
+
GetMapping("/"),
|
|
55
|
+
__metadata("design:type", Function),
|
|
56
|
+
__metadata("design:paramtypes", []),
|
|
57
|
+
__metadata("design:returntype", void 0)
|
|
58
|
+
], App.prototype, "index", null);
|
|
59
|
+
App = __decorate([
|
|
60
|
+
CinnabunAppDecorator({ port: 0, scanPaths: [] }),
|
|
61
|
+
RestController(),
|
|
62
|
+
__metadata("design:paramtypes", [TestService])
|
|
63
|
+
], App);
|
|
64
|
+
app = await CinnabunFactory.run(App);
|
|
65
|
+
await app.close();
|
|
66
|
+
expect(cleaned).toBe(true);
|
|
67
|
+
});
|
|
68
|
+
it("calls multiple PreDestroy hooks in parallel", async () => {
|
|
69
|
+
const cleanupOrder = [];
|
|
70
|
+
let Service1 = class Service1 {
|
|
71
|
+
async cleanup() {
|
|
72
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
73
|
+
cleanupOrder.push(1);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
__decorate([
|
|
77
|
+
PreDestroy(),
|
|
78
|
+
__metadata("design:type", Function),
|
|
79
|
+
__metadata("design:paramtypes", []),
|
|
80
|
+
__metadata("design:returntype", Promise)
|
|
81
|
+
], Service1.prototype, "cleanup", null);
|
|
82
|
+
Service1 = __decorate([
|
|
83
|
+
Service()
|
|
84
|
+
], Service1);
|
|
85
|
+
let Service2 = class Service2 {
|
|
86
|
+
async cleanup() {
|
|
87
|
+
await new Promise((r) => setTimeout(r, 10));
|
|
88
|
+
cleanupOrder.push(2);
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
__decorate([
|
|
92
|
+
PreDestroy(),
|
|
93
|
+
__metadata("design:type", Function),
|
|
94
|
+
__metadata("design:paramtypes", []),
|
|
95
|
+
__metadata("design:returntype", Promise)
|
|
96
|
+
], Service2.prototype, "cleanup", null);
|
|
97
|
+
Service2 = __decorate([
|
|
98
|
+
Service()
|
|
99
|
+
], Service2);
|
|
100
|
+
let App = class App {
|
|
101
|
+
service1;
|
|
102
|
+
service2;
|
|
103
|
+
constructor(service1, service2) {
|
|
104
|
+
this.service1 = service1;
|
|
105
|
+
this.service2 = service2;
|
|
106
|
+
}
|
|
107
|
+
index() {
|
|
108
|
+
return { status: "ok" };
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
__decorate([
|
|
112
|
+
GetMapping("/"),
|
|
113
|
+
__metadata("design:type", Function),
|
|
114
|
+
__metadata("design:paramtypes", []),
|
|
115
|
+
__metadata("design:returntype", void 0)
|
|
116
|
+
], App.prototype, "index", null);
|
|
117
|
+
App = __decorate([
|
|
118
|
+
CinnabunAppDecorator({ port: 0, scanPaths: [] }),
|
|
119
|
+
RestController(),
|
|
120
|
+
__metadata("design:paramtypes", [Service1,
|
|
121
|
+
Service2])
|
|
122
|
+
], App);
|
|
123
|
+
app = await CinnabunFactory.run(App);
|
|
124
|
+
const start = Date.now();
|
|
125
|
+
await app.close();
|
|
126
|
+
const duration = Date.now() - start;
|
|
127
|
+
// Both services cleaned up
|
|
128
|
+
expect(cleanupOrder).toContain(1);
|
|
129
|
+
expect(cleanupOrder).toContain(2);
|
|
130
|
+
// Service2 finished first (shorter delay)
|
|
131
|
+
expect(cleanupOrder[0]).toBe(2);
|
|
132
|
+
expect(cleanupOrder[1]).toBe(1);
|
|
133
|
+
// Parallel execution: should take ~50ms, not 60ms (sequential)
|
|
134
|
+
expect(duration).toBeLessThan(100);
|
|
135
|
+
});
|
|
136
|
+
it("continues shutdown even if PreDestroy fails", async () => {
|
|
137
|
+
let service2Cleaned = false;
|
|
138
|
+
let FailingService = class FailingService {
|
|
139
|
+
async cleanup() {
|
|
140
|
+
throw new Error("Cleanup failed");
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
__decorate([
|
|
144
|
+
PreDestroy(),
|
|
145
|
+
__metadata("design:type", Function),
|
|
146
|
+
__metadata("design:paramtypes", []),
|
|
147
|
+
__metadata("design:returntype", Promise)
|
|
148
|
+
], FailingService.prototype, "cleanup", null);
|
|
149
|
+
FailingService = __decorate([
|
|
150
|
+
Service()
|
|
151
|
+
], FailingService);
|
|
152
|
+
let WorkingService = class WorkingService {
|
|
153
|
+
async cleanup() {
|
|
154
|
+
service2Cleaned = true;
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
__decorate([
|
|
158
|
+
PreDestroy(),
|
|
159
|
+
__metadata("design:type", Function),
|
|
160
|
+
__metadata("design:paramtypes", []),
|
|
161
|
+
__metadata("design:returntype", Promise)
|
|
162
|
+
], WorkingService.prototype, "cleanup", null);
|
|
163
|
+
WorkingService = __decorate([
|
|
164
|
+
Service()
|
|
165
|
+
], WorkingService);
|
|
166
|
+
let App = class App {
|
|
167
|
+
failing;
|
|
168
|
+
working;
|
|
169
|
+
constructor(failing, working) {
|
|
170
|
+
this.failing = failing;
|
|
171
|
+
this.working = working;
|
|
172
|
+
}
|
|
173
|
+
index() {
|
|
174
|
+
return { status: "ok" };
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
__decorate([
|
|
178
|
+
GetMapping("/"),
|
|
179
|
+
__metadata("design:type", Function),
|
|
180
|
+
__metadata("design:paramtypes", []),
|
|
181
|
+
__metadata("design:returntype", void 0)
|
|
182
|
+
], App.prototype, "index", null);
|
|
183
|
+
App = __decorate([
|
|
184
|
+
CinnabunAppDecorator({ port: 0, scanPaths: [] }),
|
|
185
|
+
RestController(),
|
|
186
|
+
__metadata("design:paramtypes", [FailingService,
|
|
187
|
+
WorkingService])
|
|
188
|
+
], App);
|
|
189
|
+
app = await CinnabunFactory.run(App);
|
|
190
|
+
await app.close();
|
|
191
|
+
// Second service should still clean up despite first one failing
|
|
192
|
+
expect(service2Cleaned).toBe(true);
|
|
193
|
+
});
|
|
194
|
+
it("is idempotent - calling close() multiple times is safe", async () => {
|
|
195
|
+
let cleanupCount = 0;
|
|
196
|
+
let TestService = class TestService {
|
|
197
|
+
async cleanup() {
|
|
198
|
+
cleanupCount++;
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
__decorate([
|
|
202
|
+
PreDestroy(),
|
|
203
|
+
__metadata("design:type", Function),
|
|
204
|
+
__metadata("design:paramtypes", []),
|
|
205
|
+
__metadata("design:returntype", Promise)
|
|
206
|
+
], TestService.prototype, "cleanup", null);
|
|
207
|
+
TestService = __decorate([
|
|
208
|
+
Service()
|
|
209
|
+
], TestService);
|
|
210
|
+
let App = class App {
|
|
211
|
+
service;
|
|
212
|
+
constructor(service) {
|
|
213
|
+
this.service = service;
|
|
214
|
+
}
|
|
215
|
+
index() {
|
|
216
|
+
return { status: "ok" };
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
__decorate([
|
|
220
|
+
GetMapping("/"),
|
|
221
|
+
__metadata("design:type", Function),
|
|
222
|
+
__metadata("design:paramtypes", []),
|
|
223
|
+
__metadata("design:returntype", void 0)
|
|
224
|
+
], App.prototype, "index", null);
|
|
225
|
+
App = __decorate([
|
|
226
|
+
CinnabunAppDecorator({ port: 0, scanPaths: [] }),
|
|
227
|
+
RestController(),
|
|
228
|
+
__metadata("design:paramtypes", [TestService])
|
|
229
|
+
], App);
|
|
230
|
+
app = await CinnabunFactory.run(App);
|
|
231
|
+
// Call close multiple times
|
|
232
|
+
await app.close();
|
|
233
|
+
await app.close();
|
|
234
|
+
await app.close();
|
|
235
|
+
// Cleanup should only happen once
|
|
236
|
+
expect(cleanupCount).toBe(1);
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
describe("Shutdown Timeout", () => {
|
|
240
|
+
it("enforces cleanup timeout", async () => {
|
|
241
|
+
let SlowService = class SlowService {
|
|
242
|
+
async cleanup() {
|
|
243
|
+
// Takes 10 seconds
|
|
244
|
+
await new Promise((r) => setTimeout(r, 10000));
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
__decorate([
|
|
248
|
+
PreDestroy(),
|
|
249
|
+
__metadata("design:type", Function),
|
|
250
|
+
__metadata("design:paramtypes", []),
|
|
251
|
+
__metadata("design:returntype", Promise)
|
|
252
|
+
], SlowService.prototype, "cleanup", null);
|
|
253
|
+
SlowService = __decorate([
|
|
254
|
+
Service()
|
|
255
|
+
], SlowService);
|
|
256
|
+
let App = class App {
|
|
257
|
+
service;
|
|
258
|
+
constructor(service) {
|
|
259
|
+
this.service = service;
|
|
260
|
+
}
|
|
261
|
+
index() {
|
|
262
|
+
return { status: "ok" };
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
__decorate([
|
|
266
|
+
GetMapping("/"),
|
|
267
|
+
__metadata("design:type", Function),
|
|
268
|
+
__metadata("design:paramtypes", []),
|
|
269
|
+
__metadata("design:returntype", void 0)
|
|
270
|
+
], App.prototype, "index", null);
|
|
271
|
+
App = __decorate([
|
|
272
|
+
CinnabunAppDecorator({
|
|
273
|
+
port: 0,
|
|
274
|
+
scanPaths: [],
|
|
275
|
+
shutdownTimeout: 100, // 100ms timeout
|
|
276
|
+
}),
|
|
277
|
+
RestController(),
|
|
278
|
+
__metadata("design:paramtypes", [SlowService])
|
|
279
|
+
], App);
|
|
280
|
+
app = await CinnabunFactory.run(App);
|
|
281
|
+
const start = Date.now();
|
|
282
|
+
await app.close();
|
|
283
|
+
const duration = Date.now() - start;
|
|
284
|
+
// Should timeout after ~100ms, not wait 10 seconds
|
|
285
|
+
expect(duration).toBeLessThan(500);
|
|
286
|
+
});
|
|
287
|
+
it("uses default timeout of 5 seconds", async () => {
|
|
288
|
+
let cleaned = false;
|
|
289
|
+
let TestService = class TestService {
|
|
290
|
+
async cleanup() {
|
|
291
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
292
|
+
cleaned = true;
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
__decorate([
|
|
296
|
+
PreDestroy(),
|
|
297
|
+
__metadata("design:type", Function),
|
|
298
|
+
__metadata("design:paramtypes", []),
|
|
299
|
+
__metadata("design:returntype", Promise)
|
|
300
|
+
], TestService.prototype, "cleanup", null);
|
|
301
|
+
TestService = __decorate([
|
|
302
|
+
Service()
|
|
303
|
+
], TestService);
|
|
304
|
+
let App = class App {
|
|
305
|
+
service;
|
|
306
|
+
constructor(service) {
|
|
307
|
+
this.service = service;
|
|
308
|
+
}
|
|
309
|
+
index() {
|
|
310
|
+
return { status: "ok" };
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
__decorate([
|
|
314
|
+
GetMapping("/"),
|
|
315
|
+
__metadata("design:type", Function),
|
|
316
|
+
__metadata("design:paramtypes", []),
|
|
317
|
+
__metadata("design:returntype", void 0)
|
|
318
|
+
], App.prototype, "index", null);
|
|
319
|
+
App = __decorate([
|
|
320
|
+
CinnabunAppDecorator({
|
|
321
|
+
port: 0,
|
|
322
|
+
scanPaths: [],
|
|
323
|
+
// No shutdownTimeout specified - should use default 5000ms
|
|
324
|
+
}),
|
|
325
|
+
RestController(),
|
|
326
|
+
__metadata("design:paramtypes", [TestService])
|
|
327
|
+
], App);
|
|
328
|
+
app = await CinnabunFactory.run(App);
|
|
329
|
+
await app.close();
|
|
330
|
+
// Cleanup should succeed within default timeout
|
|
331
|
+
expect(cleaned).toBe(true);
|
|
332
|
+
});
|
|
333
|
+
});
|
|
334
|
+
describe("Custom Shutdown Hooks", () => {
|
|
335
|
+
it("registers and executes custom shutdown hooks", async () => {
|
|
336
|
+
let hookExecuted = false;
|
|
337
|
+
let App = class App {
|
|
338
|
+
index() {
|
|
339
|
+
return { status: "ok" };
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
__decorate([
|
|
343
|
+
GetMapping("/"),
|
|
344
|
+
__metadata("design:type", Function),
|
|
345
|
+
__metadata("design:paramtypes", []),
|
|
346
|
+
__metadata("design:returntype", void 0)
|
|
347
|
+
], App.prototype, "index", null);
|
|
348
|
+
App = __decorate([
|
|
349
|
+
CinnabunAppDecorator({ port: 0, scanPaths: [] }),
|
|
350
|
+
RestController()
|
|
351
|
+
], App);
|
|
352
|
+
app = await CinnabunFactory.run(App);
|
|
353
|
+
// Register custom shutdown hook
|
|
354
|
+
app.onShutdown("CustomCleanup", async () => {
|
|
355
|
+
hookExecuted = true;
|
|
356
|
+
});
|
|
357
|
+
await app.close();
|
|
358
|
+
expect(hookExecuted).toBe(true);
|
|
359
|
+
});
|
|
360
|
+
it("executes custom hooks with timeout", async () => {
|
|
361
|
+
let App = class App {
|
|
362
|
+
index() {
|
|
363
|
+
return { status: "ok" };
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
__decorate([
|
|
367
|
+
GetMapping("/"),
|
|
368
|
+
__metadata("design:type", Function),
|
|
369
|
+
__metadata("design:paramtypes", []),
|
|
370
|
+
__metadata("design:returntype", void 0)
|
|
371
|
+
], App.prototype, "index", null);
|
|
372
|
+
App = __decorate([
|
|
373
|
+
CinnabunAppDecorator({ port: 0, scanPaths: [] }),
|
|
374
|
+
RestController()
|
|
375
|
+
], App);
|
|
376
|
+
app = await CinnabunFactory.run(App);
|
|
377
|
+
// Register hook that takes too long
|
|
378
|
+
app.onShutdown("SlowHook", async () => {
|
|
379
|
+
await new Promise((r) => setTimeout(r, 5000));
|
|
380
|
+
}, 100);
|
|
381
|
+
const start = Date.now();
|
|
382
|
+
await app.close();
|
|
383
|
+
const duration = Date.now() - start;
|
|
384
|
+
// Should timeout after ~100ms
|
|
385
|
+
expect(duration).toBeLessThan(500);
|
|
386
|
+
});
|
|
387
|
+
it("continues if custom hook fails", async () => {
|
|
388
|
+
let hook2Executed = false;
|
|
389
|
+
let App = class App {
|
|
390
|
+
index() {
|
|
391
|
+
return { status: "ok" };
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
__decorate([
|
|
395
|
+
GetMapping("/"),
|
|
396
|
+
__metadata("design:type", Function),
|
|
397
|
+
__metadata("design:paramtypes", []),
|
|
398
|
+
__metadata("design:returntype", void 0)
|
|
399
|
+
], App.prototype, "index", null);
|
|
400
|
+
App = __decorate([
|
|
401
|
+
CinnabunAppDecorator({ port: 0, scanPaths: [] }),
|
|
402
|
+
RestController()
|
|
403
|
+
], App);
|
|
404
|
+
app = await CinnabunFactory.run(App);
|
|
405
|
+
app.onShutdown("FailingHook", async () => {
|
|
406
|
+
throw new Error("Hook failed");
|
|
407
|
+
});
|
|
408
|
+
app.onShutdown("WorkingHook", async () => {
|
|
409
|
+
hook2Executed = true;
|
|
410
|
+
});
|
|
411
|
+
await app.close();
|
|
412
|
+
// Second hook should execute despite first one failing
|
|
413
|
+
expect(hook2Executed).toBe(true);
|
|
414
|
+
});
|
|
415
|
+
it("executes multiple hooks in parallel", async () => {
|
|
416
|
+
const hookOrder = [];
|
|
417
|
+
let App = class App {
|
|
418
|
+
index() {
|
|
419
|
+
return { status: "ok" };
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
__decorate([
|
|
423
|
+
GetMapping("/"),
|
|
424
|
+
__metadata("design:type", Function),
|
|
425
|
+
__metadata("design:paramtypes", []),
|
|
426
|
+
__metadata("design:returntype", void 0)
|
|
427
|
+
], App.prototype, "index", null);
|
|
428
|
+
App = __decorate([
|
|
429
|
+
CinnabunAppDecorator({ port: 0, scanPaths: [] }),
|
|
430
|
+
RestController()
|
|
431
|
+
], App);
|
|
432
|
+
app = await CinnabunFactory.run(App);
|
|
433
|
+
app.onShutdown("Hook1", async () => {
|
|
434
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
435
|
+
hookOrder.push(1);
|
|
436
|
+
});
|
|
437
|
+
app.onShutdown("Hook2", async () => {
|
|
438
|
+
await new Promise((r) => setTimeout(r, 10));
|
|
439
|
+
hookOrder.push(2);
|
|
440
|
+
});
|
|
441
|
+
const start = Date.now();
|
|
442
|
+
await app.close();
|
|
443
|
+
const duration = Date.now() - start;
|
|
444
|
+
// Both hooks executed
|
|
445
|
+
expect(hookOrder).toContain(1);
|
|
446
|
+
expect(hookOrder).toContain(2);
|
|
447
|
+
// Hook2 finished first (shorter delay)
|
|
448
|
+
expect(hookOrder[0]).toBe(2);
|
|
449
|
+
// Parallel execution: should take ~50ms, not 60ms
|
|
450
|
+
expect(duration).toBeLessThan(100);
|
|
451
|
+
});
|
|
452
|
+
});
|
|
453
|
+
describe("@OnShutdown Decorator", () => {
|
|
454
|
+
it("executes methods decorated with @OnShutdown", async () => {
|
|
455
|
+
let shutdownCalled = false;
|
|
456
|
+
let TestService = class TestService {
|
|
457
|
+
async onShutdown() {
|
|
458
|
+
shutdownCalled = true;
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
__decorate([
|
|
462
|
+
OnShutdown(),
|
|
463
|
+
__metadata("design:type", Function),
|
|
464
|
+
__metadata("design:paramtypes", []),
|
|
465
|
+
__metadata("design:returntype", Promise)
|
|
466
|
+
], TestService.prototype, "onShutdown", null);
|
|
467
|
+
TestService = __decorate([
|
|
468
|
+
Service()
|
|
469
|
+
], TestService);
|
|
470
|
+
let App = class App {
|
|
471
|
+
service;
|
|
472
|
+
constructor(service) {
|
|
473
|
+
this.service = service;
|
|
474
|
+
}
|
|
475
|
+
index() {
|
|
476
|
+
return { status: "ok" };
|
|
477
|
+
}
|
|
478
|
+
};
|
|
479
|
+
__decorate([
|
|
480
|
+
GetMapping("/"),
|
|
481
|
+
__metadata("design:type", Function),
|
|
482
|
+
__metadata("design:paramtypes", []),
|
|
483
|
+
__metadata("design:returntype", void 0)
|
|
484
|
+
], App.prototype, "index", null);
|
|
485
|
+
App = __decorate([
|
|
486
|
+
CinnabunAppDecorator({ port: 0, scanPaths: [] }),
|
|
487
|
+
RestController(),
|
|
488
|
+
__metadata("design:paramtypes", [TestService])
|
|
489
|
+
], App);
|
|
490
|
+
app = await CinnabunFactory.run(App);
|
|
491
|
+
await app.close();
|
|
492
|
+
// Note: @OnShutdown is defined but not yet wired up in the container
|
|
493
|
+
// This test documents the expected behavior when implemented
|
|
494
|
+
// For now, it verifies the decorator doesn't break anything
|
|
495
|
+
expect(shutdownCalled).toBe(false); // Will be true when fully implemented
|
|
496
|
+
});
|
|
497
|
+
});
|
|
498
|
+
describe("Error Handling", () => {
|
|
499
|
+
it("logs errors but completes shutdown", async () => {
|
|
500
|
+
let Service1 = class Service1 {
|
|
501
|
+
async cleanup() {
|
|
502
|
+
throw new Error("Service1 failed");
|
|
503
|
+
}
|
|
504
|
+
};
|
|
505
|
+
__decorate([
|
|
506
|
+
PreDestroy(),
|
|
507
|
+
__metadata("design:type", Function),
|
|
508
|
+
__metadata("design:paramtypes", []),
|
|
509
|
+
__metadata("design:returntype", Promise)
|
|
510
|
+
], Service1.prototype, "cleanup", null);
|
|
511
|
+
Service1 = __decorate([
|
|
512
|
+
Service()
|
|
513
|
+
], Service1);
|
|
514
|
+
let Service2 = class Service2 {
|
|
515
|
+
async cleanup() {
|
|
516
|
+
throw new Error("Service2 failed");
|
|
517
|
+
}
|
|
518
|
+
};
|
|
519
|
+
__decorate([
|
|
520
|
+
PreDestroy(),
|
|
521
|
+
__metadata("design:type", Function),
|
|
522
|
+
__metadata("design:paramtypes", []),
|
|
523
|
+
__metadata("design:returntype", Promise)
|
|
524
|
+
], Service2.prototype, "cleanup", null);
|
|
525
|
+
Service2 = __decorate([
|
|
526
|
+
Service()
|
|
527
|
+
], Service2);
|
|
528
|
+
let App = class App {
|
|
529
|
+
s1;
|
|
530
|
+
s2;
|
|
531
|
+
constructor(s1, s2) {
|
|
532
|
+
this.s1 = s1;
|
|
533
|
+
this.s2 = s2;
|
|
534
|
+
}
|
|
535
|
+
index() {
|
|
536
|
+
return { status: "ok" };
|
|
537
|
+
}
|
|
538
|
+
};
|
|
539
|
+
__decorate([
|
|
540
|
+
GetMapping("/"),
|
|
541
|
+
__metadata("design:type", Function),
|
|
542
|
+
__metadata("design:paramtypes", []),
|
|
543
|
+
__metadata("design:returntype", void 0)
|
|
544
|
+
], App.prototype, "index", null);
|
|
545
|
+
App = __decorate([
|
|
546
|
+
CinnabunAppDecorator({ port: 0, scanPaths: [] }),
|
|
547
|
+
RestController(),
|
|
548
|
+
__metadata("design:paramtypes", [Service1,
|
|
549
|
+
Service2])
|
|
550
|
+
], App);
|
|
551
|
+
app = await CinnabunFactory.run(App);
|
|
552
|
+
// Should not throw despite errors
|
|
553
|
+
await expect(app.close()).resolves.toBeUndefined();
|
|
554
|
+
});
|
|
555
|
+
it("provides helpful error messages", async () => {
|
|
556
|
+
let DatabaseService = class DatabaseService {
|
|
557
|
+
async cleanup() {
|
|
558
|
+
throw new Error("Failed to close database connections");
|
|
559
|
+
}
|
|
560
|
+
};
|
|
561
|
+
__decorate([
|
|
562
|
+
PreDestroy(),
|
|
563
|
+
__metadata("design:type", Function),
|
|
564
|
+
__metadata("design:paramtypes", []),
|
|
565
|
+
__metadata("design:returntype", Promise)
|
|
566
|
+
], DatabaseService.prototype, "cleanup", null);
|
|
567
|
+
DatabaseService = __decorate([
|
|
568
|
+
Service()
|
|
569
|
+
], DatabaseService);
|
|
570
|
+
let App = class App {
|
|
571
|
+
db;
|
|
572
|
+
constructor(db) {
|
|
573
|
+
this.db = db;
|
|
574
|
+
}
|
|
575
|
+
index() {
|
|
576
|
+
return { status: "ok" };
|
|
577
|
+
}
|
|
578
|
+
};
|
|
579
|
+
__decorate([
|
|
580
|
+
GetMapping("/"),
|
|
581
|
+
__metadata("design:type", Function),
|
|
582
|
+
__metadata("design:paramtypes", []),
|
|
583
|
+
__metadata("design:returntype", void 0)
|
|
584
|
+
], App.prototype, "index", null);
|
|
585
|
+
App = __decorate([
|
|
586
|
+
CinnabunAppDecorator({ port: 0, scanPaths: [] }),
|
|
587
|
+
RestController(),
|
|
588
|
+
__metadata("design:paramtypes", [DatabaseService])
|
|
589
|
+
], App);
|
|
590
|
+
app = await CinnabunFactory.run(App);
|
|
591
|
+
// Errors are logged but shutdown completes
|
|
592
|
+
await expect(app.close()).resolves.toBeUndefined();
|
|
593
|
+
});
|
|
594
|
+
});
|
|
595
|
+
describe("Integration", () => {
|
|
596
|
+
it("shuts down complex application gracefully", async () => {
|
|
597
|
+
const events = [];
|
|
598
|
+
let DatabaseService = class DatabaseService {
|
|
599
|
+
async disconnect() {
|
|
600
|
+
await new Promise((r) => setTimeout(r, 20));
|
|
601
|
+
events.push("database");
|
|
602
|
+
}
|
|
603
|
+
};
|
|
604
|
+
__decorate([
|
|
605
|
+
PreDestroy(),
|
|
606
|
+
__metadata("design:type", Function),
|
|
607
|
+
__metadata("design:paramtypes", []),
|
|
608
|
+
__metadata("design:returntype", Promise)
|
|
609
|
+
], DatabaseService.prototype, "disconnect", null);
|
|
610
|
+
DatabaseService = __decorate([
|
|
611
|
+
Service()
|
|
612
|
+
], DatabaseService);
|
|
613
|
+
let CacheService = class CacheService {
|
|
614
|
+
async flush() {
|
|
615
|
+
await new Promise((r) => setTimeout(r, 10));
|
|
616
|
+
events.push("cache");
|
|
617
|
+
}
|
|
618
|
+
};
|
|
619
|
+
__decorate([
|
|
620
|
+
PreDestroy(),
|
|
621
|
+
__metadata("design:type", Function),
|
|
622
|
+
__metadata("design:paramtypes", []),
|
|
623
|
+
__metadata("design:returntype", Promise)
|
|
624
|
+
], CacheService.prototype, "flush", null);
|
|
625
|
+
CacheService = __decorate([
|
|
626
|
+
Service()
|
|
627
|
+
], CacheService);
|
|
628
|
+
let QueueService = class QueueService {
|
|
629
|
+
async drain() {
|
|
630
|
+
await new Promise((r) => setTimeout(r, 30));
|
|
631
|
+
events.push("queue");
|
|
632
|
+
}
|
|
633
|
+
};
|
|
634
|
+
__decorate([
|
|
635
|
+
PreDestroy(),
|
|
636
|
+
__metadata("design:type", Function),
|
|
637
|
+
__metadata("design:paramtypes", []),
|
|
638
|
+
__metadata("design:returntype", Promise)
|
|
639
|
+
], QueueService.prototype, "drain", null);
|
|
640
|
+
QueueService = __decorate([
|
|
641
|
+
Service()
|
|
642
|
+
], QueueService);
|
|
643
|
+
let App = class App {
|
|
644
|
+
db;
|
|
645
|
+
cache;
|
|
646
|
+
queue;
|
|
647
|
+
constructor(db, cache, queue) {
|
|
648
|
+
this.db = db;
|
|
649
|
+
this.cache = cache;
|
|
650
|
+
this.queue = queue;
|
|
651
|
+
}
|
|
652
|
+
index() {
|
|
653
|
+
return { status: "ok" };
|
|
654
|
+
}
|
|
655
|
+
};
|
|
656
|
+
__decorate([
|
|
657
|
+
GetMapping("/"),
|
|
658
|
+
__metadata("design:type", Function),
|
|
659
|
+
__metadata("design:paramtypes", []),
|
|
660
|
+
__metadata("design:returntype", void 0)
|
|
661
|
+
], App.prototype, "index", null);
|
|
662
|
+
App = __decorate([
|
|
663
|
+
CinnabunAppDecorator({ port: 0, scanPaths: [] }),
|
|
664
|
+
RestController(),
|
|
665
|
+
__metadata("design:paramtypes", [DatabaseService,
|
|
666
|
+
CacheService,
|
|
667
|
+
QueueService])
|
|
668
|
+
], App);
|
|
669
|
+
app = await CinnabunFactory.run(App);
|
|
670
|
+
// Register external resource cleanup
|
|
671
|
+
app.onShutdown("Redis", async () => {
|
|
672
|
+
await new Promise((r) => setTimeout(r, 5));
|
|
673
|
+
events.push("redis");
|
|
674
|
+
});
|
|
675
|
+
const start = Date.now();
|
|
676
|
+
await app.close();
|
|
677
|
+
const duration = Date.now() - start;
|
|
678
|
+
// All resources cleaned up
|
|
679
|
+
expect(events).toContain("database");
|
|
680
|
+
expect(events).toContain("cache");
|
|
681
|
+
expect(events).toContain("queue");
|
|
682
|
+
expect(events).toContain("redis");
|
|
683
|
+
// Parallel execution: should take ~30ms (longest), not 65ms (sum)
|
|
684
|
+
expect(duration).toBeLessThan(100);
|
|
685
|
+
});
|
|
686
|
+
});
|
|
687
|
+
});
|
|
688
|
+
//# sourceMappingURL=lifecycle-management.test.js.map
|