@nest-yalc-2/app 1.3.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/LICENSE +661 -0
- package/README.md +49 -0
- package/package.json +33 -0
- package/src/__tests__/app-bootstrap-base.helper.spec.ts +38 -0
- package/src/__tests__/app-bootstrap-standalone.helper.spec.ts +35 -0
- package/src/__tests__/app-bootstrap.helper.spec.ts +98 -0
- package/src/__tests__/app-config.service.spec.ts +55 -0
- package/src/__tests__/app-context.service.spec.ts +28 -0
- package/src/__tests__/app.events.spec.ts +12 -0
- package/src/__tests__/app.helper.spec.ts +204 -0
- package/src/__tests__/base-app-module.helper.spec.ts +231 -0
- package/src/__tests__/base-app.controller.spec.ts +72 -0
- package/src/__tests__/base-app.service.spec.ts +39 -0
- package/src/__tests__/cls.module.spec.ts +33 -0
- package/src/__tests__/life-cycle-handler.service.spec.ts +48 -0
- package/src/__tests__/tsconfig.json +3 -0
- package/src/__tests__/unwrap-result.interceptor.spec.ts +52 -0
- package/src/app-bootstrap-base.helper.d.ts +41 -0
- package/src/app-bootstrap-base.helper.js +108 -0
- package/src/app-bootstrap-base.helper.js.map +1 -0
- package/src/app-bootstrap-base.helper.ts +199 -0
- package/src/app-bootstrap-standalone.helper.d.ts +15 -0
- package/src/app-bootstrap-standalone.helper.js +45 -0
- package/src/app-bootstrap-standalone.helper.js.map +1 -0
- package/src/app-bootstrap-standalone.helper.ts +60 -0
- package/src/app-bootstrap.helper.d.ts +43 -0
- package/src/app-bootstrap.helper.js +164 -0
- package/src/app-bootstrap.helper.js.map +1 -0
- package/src/app-bootstrap.helper.ts +263 -0
- package/src/app-config.service.d.ts +14 -0
- package/src/app-config.service.js +45 -0
- package/src/app-config.service.js.map +1 -0
- package/src/app-config.service.ts +61 -0
- package/src/app-context.module.d.ts +2 -0
- package/src/app-context.module.js +19 -0
- package/src/app-context.module.js.map +1 -0
- package/src/app-context.module.ts +6 -0
- package/src/app-context.service.d.ts +7 -0
- package/src/app-context.service.js +26 -0
- package/src/app-context.service.js.map +1 -0
- package/src/app-context.service.ts +16 -0
- package/src/app-imports.factory.d.ts +19 -0
- package/src/app-imports.factory.js +223 -0
- package/src/app-imports.factory.js.map +1 -0
- package/src/app-imports.factory.ts +320 -0
- package/src/app.events.d.ts +5 -0
- package/src/app.events.js +10 -0
- package/src/app.events.js.map +1 -0
- package/src/app.events.ts +8 -0
- package/src/app.helper.d.ts +17 -0
- package/src/app.helper.js +26 -0
- package/src/app.helper.js.map +1 -0
- package/src/app.helper.ts +73 -0
- package/src/base-app-module.helper.d.ts +24 -0
- package/src/base-app-module.helper.js +326 -0
- package/src/base-app-module.helper.js.map +1 -0
- package/src/base-app-module.helper.ts +447 -0
- package/src/base-app.controller.d.ts +9 -0
- package/src/base-app.controller.js +56 -0
- package/src/base-app.controller.js.map +1 -0
- package/src/base-app.controller.ts +43 -0
- package/src/base-app.interface.d.ts +33 -0
- package/src/base-app.interface.js +3 -0
- package/src/base-app.interface.js.map +1 -0
- package/src/base-app.interface.ts +55 -0
- package/src/base-app.service.d.ts +7 -0
- package/src/base-app.service.js +78 -0
- package/src/base-app.service.js.map +1 -0
- package/src/base-app.service.ts +29 -0
- package/src/cls.module.d.ts +15 -0
- package/src/cls.module.js +51 -0
- package/src/cls.module.js.map +1 -0
- package/src/cls.module.ts +68 -0
- package/src/conf.type.d.ts +21 -0
- package/src/conf.type.js +3 -0
- package/src/conf.type.js.map +1 -0
- package/src/conf.type.ts +22 -0
- package/src/db-conf.helper.ts-off +93 -0
- package/src/def.const.d.ts +10 -0
- package/src/def.const.js +14 -0
- package/src/def.const.js.map +1 -0
- package/src/def.const.ts +33 -0
- package/src/global.enum.d.ts +6 -0
- package/src/global.enum.js +11 -0
- package/src/global.enum.js.map +1 -0
- package/src/global.enum.ts +6 -0
- package/src/index.d.ts +0 -0
- package/src/index.js +2 -0
- package/src/index.js.map +1 -0
- package/src/index.ts +0 -0
- package/src/life-cycle-handler.service.d.ts +13 -0
- package/src/life-cycle-handler.service.js +47 -0
- package/src/life-cycle-handler.service.js.map +1 -0
- package/src/life-cycle-handler.service.ts +58 -0
- package/src/unwrap-result.interceptor.d.ts +5 -0
- package/src/unwrap-result.interceptor.js +36 -0
- package/src/unwrap-result.interceptor.js.map +1 -0
- package/src/unwrap-result.interceptor.ts +32 -0
- package/src/yalc-controller.interface.d.ts +3 -0
- package/src/yalc-controller.interface.js +3 -0
- package/src/yalc-controller.interface.js.map +1 -0
- package/src/yalc-controller.interface.ts +6 -0
package/README.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# @nest-yalc-2/app
|
|
2
|
+
|
|
3
|
+
Application bootstrap helpers and base NestJS module utilities for YALC apps.
|
|
4
|
+
|
|
5
|
+
Use this package when an application wants shared configuration loading,
|
|
6
|
+
application-scoped logger and event providers, CLS wiring, lifecycle handling,
|
|
7
|
+
Swagger setup, and consistent bootstrap behavior.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install @nest-yalc-2/app
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Install the NestJS peer packages used by your application, such as
|
|
16
|
+
`@nestjs/common`, `@nestjs/core`, `@nestjs/config`, and the selected HTTP
|
|
17
|
+
platform package.
|
|
18
|
+
|
|
19
|
+
## Import Surface
|
|
20
|
+
|
|
21
|
+
The package ships helpers through subpath exports. Import the specific helper
|
|
22
|
+
module you need, for example `@nest-yalc-2/app/app-bootstrap.helper.js`.
|
|
23
|
+
|
|
24
|
+
- `AppBootstrap` and `StandaloneAppBootstrap` for HTTP and standalone Nest app
|
|
25
|
+
startup.
|
|
26
|
+
- `YalcBaseAppModule` and `YalcDefaultAppModule` for reusable base module
|
|
27
|
+
composition.
|
|
28
|
+
- `AppDependencyFactory` for assembling common app imports and providers.
|
|
29
|
+
- `AppConfigService`, `createAppConfigProvider`, and token helpers for
|
|
30
|
+
application-scoped configuration.
|
|
31
|
+
- `YalcClsModule`, `YalcAlsService`, and `YalcGlobalClsService` for context
|
|
32
|
+
storage.
|
|
33
|
+
- `UnwrapResultInterceptor` for unwrapping `neverthrow` Result values.
|
|
34
|
+
|
|
35
|
+
## Example
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
import { AppBootstrap } from '@nest-yalc-2/app/app-bootstrap.helper.js';
|
|
39
|
+
import { AppModule } from './app.module.js';
|
|
40
|
+
|
|
41
|
+
await new AppBootstrap(AppModule).bootstrap();
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Documentation
|
|
45
|
+
|
|
46
|
+
- Integration guide:
|
|
47
|
+
https://github.com/NestDevLab/nestjs-yalc/blob/dev/docs/how-to-integrate-nestjs-yalc.md
|
|
48
|
+
- Backend blueprint:
|
|
49
|
+
https://github.com/NestDevLab/nestjs-yalc/blob/dev/docs/backend-blueprint.md
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nest-yalc-2/app",
|
|
3
|
+
"version": "1.3.3",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "",
|
|
6
|
+
"main": "./src/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/index.js",
|
|
9
|
+
"./*.js": "./src/*.js",
|
|
10
|
+
"./*": "./src/*"
|
|
11
|
+
},
|
|
12
|
+
"author": "",
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"types": "./src/index.d.ts",
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"src"
|
|
18
|
+
],
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"tslib": "^2.6.3"
|
|
21
|
+
},
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "git+https://github.com/Nek97/nestjs-yalc.git"
|
|
25
|
+
},
|
|
26
|
+
"bugs": {
|
|
27
|
+
"url": "https://github.com/Nek97/nestjs-yalc/issues"
|
|
28
|
+
},
|
|
29
|
+
"homepage": "https://github.com/Nek97/nestjs-yalc#readme",
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { describe, expect, it, jest } from '@jest/globals';
|
|
2
|
+
import { BaseAppBootstrap } from '../app-bootstrap-base.helper.js';
|
|
3
|
+
|
|
4
|
+
class DummyModule {}
|
|
5
|
+
|
|
6
|
+
class TestBootstrap extends BaseAppBootstrap<any> {
|
|
7
|
+
constructor() {
|
|
8
|
+
super('test-app', DummyModule as any, {
|
|
9
|
+
globalsOptions: { skipMultiServerCheck: true },
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
describe('BaseAppBootstrap', () => {
|
|
15
|
+
it('should monkey patch app init/close and update flags', async () => {
|
|
16
|
+
const bootstrap = new TestBootstrap();
|
|
17
|
+
|
|
18
|
+
const closeFn = jest.fn().mockResolvedValue(undefined);
|
|
19
|
+
const initFn = jest.fn().mockResolvedValue(undefined);
|
|
20
|
+
|
|
21
|
+
const fakeApp: any = {
|
|
22
|
+
close: closeFn,
|
|
23
|
+
init: initFn,
|
|
24
|
+
get: jest.fn(),
|
|
25
|
+
useLogger: jest.fn(),
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
bootstrap.setApp(fakeApp);
|
|
29
|
+
|
|
30
|
+
expect(bootstrap.isAppClosed()).toBe(false);
|
|
31
|
+
await fakeApp.init();
|
|
32
|
+
expect(bootstrap.isAppClosed()).toBe(false);
|
|
33
|
+
|
|
34
|
+
await bootstrap.closeApp();
|
|
35
|
+
expect(closeFn).toHaveBeenCalled();
|
|
36
|
+
expect(bootstrap.isAppClosed()).toBe(true);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { describe, expect, it, jest, beforeEach } from '@jest/globals';
|
|
2
|
+
import { StandaloneAppBootstrap } from '../app-bootstrap-standalone.helper.js';
|
|
3
|
+
import { NestFactory } from '@nestjs/core';
|
|
4
|
+
|
|
5
|
+
describe('StandaloneAppBootstrap', () => {
|
|
6
|
+
const logger = { log: jest.fn(), debug: jest.fn() };
|
|
7
|
+
const initMock = jest.fn().mockResolvedValue(undefined);
|
|
8
|
+
const fakeApp = {
|
|
9
|
+
init: initMock,
|
|
10
|
+
close: jest.fn().mockResolvedValue(undefined),
|
|
11
|
+
get: jest.fn().mockReturnValue(logger),
|
|
12
|
+
useLogger: jest.fn(),
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
jest.clearAllMocks();
|
|
17
|
+
jest
|
|
18
|
+
.spyOn(NestFactory, 'createApplicationContext')
|
|
19
|
+
.mockResolvedValue(fakeApp as any);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should create application context and initialize', async () => {
|
|
23
|
+
const bootstrap = new StandaloneAppBootstrap(
|
|
24
|
+
'standalone',
|
|
25
|
+
class Dummy {},
|
|
26
|
+
{ skipMultiServerCheck: true } as any,
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
await bootstrap.initApp();
|
|
30
|
+
|
|
31
|
+
expect(NestFactory.createApplicationContext).toHaveBeenCalled();
|
|
32
|
+
expect(initMock).toHaveBeenCalled();
|
|
33
|
+
expect(bootstrap.getApp()).toBe(fakeApp);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { describe, expect, it, jest, beforeEach } from '@jest/globals';
|
|
2
|
+
import '@nest-yalc-2/jest/common-mocks.helper.js';
|
|
3
|
+
|
|
4
|
+
import { AppBootstrap } from '../app-bootstrap.helper.js';
|
|
5
|
+
import { SYSTEM_LOGGER_SERVICE } from '../def.const.js';
|
|
6
|
+
import { UnwrapResultInterceptor } from '../unwrap-result.interceptor.js';
|
|
7
|
+
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
|
|
8
|
+
|
|
9
|
+
jest.mock('class-validator', () => ({
|
|
10
|
+
useContainer: jest.fn(),
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
jest.mock('@fastify/cookie', () => jest.fn());
|
|
14
|
+
|
|
15
|
+
describe('AppBootstrap.applyBootstrapGlobals', () => {
|
|
16
|
+
const logger = { log: jest.fn(), debug: jest.fn() };
|
|
17
|
+
const customFilter = { handle: jest.fn() };
|
|
18
|
+
const createDocumentMock = jest.fn().mockReturnValue({ doc: true });
|
|
19
|
+
const setupSwaggerMock = jest.fn();
|
|
20
|
+
const setTitleMock = jest.fn().mockReturnThis();
|
|
21
|
+
const setDescriptionMock = jest.fn().mockReturnThis();
|
|
22
|
+
const buildMock = jest.fn().mockReturnValue({ swagger: true });
|
|
23
|
+
|
|
24
|
+
const fakeApp = {
|
|
25
|
+
useGlobalPipes: jest.fn(),
|
|
26
|
+
setGlobalPrefix: jest.fn(),
|
|
27
|
+
register: jest.fn(),
|
|
28
|
+
useGlobalInterceptors: jest.fn(),
|
|
29
|
+
useGlobalFilters: jest.fn(),
|
|
30
|
+
useLogger: jest.fn(),
|
|
31
|
+
get: jest.fn().mockImplementation((token) =>
|
|
32
|
+
token === SYSTEM_LOGGER_SERVICE ? logger : undefined,
|
|
33
|
+
),
|
|
34
|
+
select: jest.fn().mockReturnValue({}),
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
beforeEach(() => {
|
|
38
|
+
jest.restoreAllMocks();
|
|
39
|
+
jest.clearAllMocks();
|
|
40
|
+
jest.spyOn(SwaggerModule, 'createDocument').mockImplementation((...args: any[]) =>
|
|
41
|
+
createDocumentMock(...args),
|
|
42
|
+
);
|
|
43
|
+
jest.spyOn(SwaggerModule, 'setup').mockImplementation((...args: any[]) =>
|
|
44
|
+
setupSwaggerMock(...args),
|
|
45
|
+
);
|
|
46
|
+
jest
|
|
47
|
+
.spyOn(DocumentBuilder.prototype, 'setTitle')
|
|
48
|
+
.mockImplementation(function (...args: any[]) {
|
|
49
|
+
setTitleMock(...args);
|
|
50
|
+
return this;
|
|
51
|
+
});
|
|
52
|
+
jest
|
|
53
|
+
.spyOn(DocumentBuilder.prototype, 'setDescription')
|
|
54
|
+
.mockImplementation(function (...args: any[]) {
|
|
55
|
+
setDescriptionMock(...args);
|
|
56
|
+
return this;
|
|
57
|
+
});
|
|
58
|
+
jest.spyOn(DocumentBuilder.prototype, 'build').mockImplementation((...args: any[]) =>
|
|
59
|
+
buildMock(...args),
|
|
60
|
+
);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('should set up pipes, filters, interceptors and swagger', async () => {
|
|
64
|
+
const bootstrap = new AppBootstrap(
|
|
65
|
+
'app',
|
|
66
|
+
class Dummy {},
|
|
67
|
+
{ skipMultiServerCheck: true } as any,
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
bootstrap['app'] = fakeApp as any;
|
|
71
|
+
bootstrap['loggerService'] = logger as any;
|
|
72
|
+
bootstrap.getConf = jest.fn().mockReturnValue({
|
|
73
|
+
apiPrefix: 'api',
|
|
74
|
+
host: 'localhost',
|
|
75
|
+
port: 3000,
|
|
76
|
+
}) as any;
|
|
77
|
+
bootstrap.getModule = jest.fn().mockReturnValue({}) as any;
|
|
78
|
+
|
|
79
|
+
await bootstrap.applyBootstrapGlobals({
|
|
80
|
+
enableSwagger: true,
|
|
81
|
+
apiPrefix: 'api',
|
|
82
|
+
filters: [customFilter as any],
|
|
83
|
+
validationPipeOptions: { transformOptions: { enableImplicitConversion: true } },
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
expect(fakeApp.useGlobalPipes).toHaveBeenCalled();
|
|
87
|
+
expect(fakeApp.setGlobalPrefix).toHaveBeenCalledWith('api');
|
|
88
|
+
expect(fakeApp.register).toHaveBeenCalled();
|
|
89
|
+
expect(fakeApp.useGlobalInterceptors).toHaveBeenCalledWith(
|
|
90
|
+
expect.any(UnwrapResultInterceptor),
|
|
91
|
+
);
|
|
92
|
+
expect(fakeApp.useGlobalFilters).toHaveBeenCalled();
|
|
93
|
+
expect(createDocumentMock).toHaveBeenCalled();
|
|
94
|
+
expect(setTitleMock).toHaveBeenCalled();
|
|
95
|
+
expect(setDescriptionMock).toHaveBeenCalled();
|
|
96
|
+
expect(setupSwaggerMock).toHaveBeenCalled();
|
|
97
|
+
});
|
|
98
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { describe, expect, it, jest } from '@jest/globals';
|
|
2
|
+
import { ConfigService } from '@nestjs/config';
|
|
3
|
+
import {
|
|
4
|
+
AppConfigService,
|
|
5
|
+
createAppConfigProvider,
|
|
6
|
+
getAppConfigToken,
|
|
7
|
+
getAppEventToken,
|
|
8
|
+
getAppLoggerToken,
|
|
9
|
+
} from '../app-config.service.js';
|
|
10
|
+
|
|
11
|
+
describe('AppConfigService', () => {
|
|
12
|
+
it('should expose config values', () => {
|
|
13
|
+
const config = { appName: 'demo' };
|
|
14
|
+
const configService = {
|
|
15
|
+
get: jest.fn().mockReturnValue(config),
|
|
16
|
+
} as unknown as ConfigService;
|
|
17
|
+
|
|
18
|
+
const service = new AppConfigService(configService, 'demoAlias');
|
|
19
|
+
|
|
20
|
+
expect(service.values).toBe(config);
|
|
21
|
+
expect(service.get()).toBe(config);
|
|
22
|
+
expect(configService.get).toHaveBeenCalledWith('demoAlias');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should throw when config is missing', () => {
|
|
26
|
+
const configService = {
|
|
27
|
+
get: jest.fn().mockReturnValue(undefined),
|
|
28
|
+
} as unknown as ConfigService;
|
|
29
|
+
|
|
30
|
+
expect(() => new AppConfigService(configService, 'missing')).toThrow(
|
|
31
|
+
"AppConfigService: No config found for app alias 'missing'",
|
|
32
|
+
);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe('AppConfigService tokens', () => {
|
|
37
|
+
it('should generate token names', () => {
|
|
38
|
+
expect(getAppConfigToken('alias')).toBe('aliasConfig');
|
|
39
|
+
expect(getAppEventToken('alias')).toBe('aliasEvent');
|
|
40
|
+
expect(getAppLoggerToken('alias')).toBe('aliasLogger');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should build a provider that resolves AppConfigService', () => {
|
|
44
|
+
const provider = createAppConfigProvider('alias');
|
|
45
|
+
const config = { foo: 'bar' };
|
|
46
|
+
const configService = { get: jest.fn().mockReturnValue(config) };
|
|
47
|
+
|
|
48
|
+
const instance = (provider as any).useFactory(
|
|
49
|
+
configService as ConfigService,
|
|
50
|
+
) as AppConfigService;
|
|
51
|
+
|
|
52
|
+
expect(provider.inject).toEqual([ConfigService]);
|
|
53
|
+
expect(instance.values).toBe(config);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { describe, expect, it } from '@jest/globals';
|
|
2
|
+
import {
|
|
3
|
+
GraphQLObjectType,
|
|
4
|
+
GraphQLSchema,
|
|
5
|
+
GraphQLString,
|
|
6
|
+
} from 'graphql';
|
|
7
|
+
import { AppContextService } from '../app-context.service.js';
|
|
8
|
+
|
|
9
|
+
describe('AppContextService', () => {
|
|
10
|
+
it('should store initialized apps and schema', () => {
|
|
11
|
+
const service = new AppContextService();
|
|
12
|
+
service.initializedApps.add('app1');
|
|
13
|
+
|
|
14
|
+
const schema = new GraphQLSchema({
|
|
15
|
+
query: new GraphQLObjectType({
|
|
16
|
+
name: 'Query',
|
|
17
|
+
fields: {
|
|
18
|
+
hello: { type: GraphQLString },
|
|
19
|
+
},
|
|
20
|
+
}),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
service.setSchema(schema);
|
|
24
|
+
|
|
25
|
+
expect(service.initializedApps.has('app1')).toBe(true);
|
|
26
|
+
expect(service.schema).toBe(schema);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { describe, expect, it } from '@jest/globals';
|
|
2
|
+
import { AppEvents } from '../app.events.js';
|
|
3
|
+
|
|
4
|
+
describe('AppEvents enum', () => {
|
|
5
|
+
it('should expose the expected event names', () => {
|
|
6
|
+
expect(AppEvents.BEFORE_ALL_ROUTES).toBe('app.before_all_routes');
|
|
7
|
+
expect(AppEvents.AFTER_ALL_ROUTES).toBe('app.after_all_routes');
|
|
8
|
+
expect(AppEvents.BEFORE_GQL_CONTEXT_MIDDLEWARE).toBe(
|
|
9
|
+
'app.before_gql_context_middleware',
|
|
10
|
+
);
|
|
11
|
+
});
|
|
12
|
+
});
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { jest } from '@jest/globals';
|
|
2
|
+
import '@nest-yalc-2/jest/common-mocks.helper.js';
|
|
3
|
+
import { expect, describe, it, beforeEach, afterEach } from '@jest/globals';
|
|
4
|
+
import { createMock, DeepMocked } from '@golevelup/ts-jest';
|
|
5
|
+
import { DynamicModule, INestApplicationContext, Module } from '@nestjs/common';
|
|
6
|
+
import { NestFactory } from '@nestjs/core';
|
|
7
|
+
import { executeStandaloneFunction } from '../app.helper.js';
|
|
8
|
+
import {
|
|
9
|
+
getBootstrappedApps,
|
|
10
|
+
getMainBootstrappedApp,
|
|
11
|
+
} from '../app-bootstrap-base.helper.js';
|
|
12
|
+
import { AppBootstrap } from '../app-bootstrap.helper.js';
|
|
13
|
+
import { yalcBaseAppModuleMetadataFactory } from '../base-app-module.helper.js';
|
|
14
|
+
|
|
15
|
+
@Module(
|
|
16
|
+
yalcBaseAppModuleMetadataFactory(TestModule1, 'test1', {
|
|
17
|
+
configFactory: () => ({}),
|
|
18
|
+
logger: true,
|
|
19
|
+
}),
|
|
20
|
+
)
|
|
21
|
+
class TestModule1 {}
|
|
22
|
+
|
|
23
|
+
@Module(
|
|
24
|
+
yalcBaseAppModuleMetadataFactory(TestModule2, 'test2', {
|
|
25
|
+
configFactory: () => ({}),
|
|
26
|
+
logger: true,
|
|
27
|
+
}),
|
|
28
|
+
)
|
|
29
|
+
class TestModule2 {}
|
|
30
|
+
|
|
31
|
+
@Module(
|
|
32
|
+
yalcBaseAppModuleMetadataFactory(BrokenModule, 'brokenModule', {
|
|
33
|
+
configFactory: () => ({}),
|
|
34
|
+
logger: true,
|
|
35
|
+
providers: [
|
|
36
|
+
{
|
|
37
|
+
provide: 'brokenService',
|
|
38
|
+
useFactory: () => {
|
|
39
|
+
throw new Error('Test');
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
}),
|
|
44
|
+
)
|
|
45
|
+
class BrokenModule {}
|
|
46
|
+
|
|
47
|
+
describe('test standalone app functions', () => {
|
|
48
|
+
let mockedModule: DeepMocked<DynamicModule>;
|
|
49
|
+
let mockedServiceFunction: jest.Mock<() => string>;
|
|
50
|
+
|
|
51
|
+
beforeEach(async () => {
|
|
52
|
+
jest.resetAllMocks();
|
|
53
|
+
mockedServiceFunction = jest.fn(() => 'Test');
|
|
54
|
+
|
|
55
|
+
mockedModule = createMock<DynamicModule>({
|
|
56
|
+
imports: [],
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const mockedCreateApplicationContext = jest.spyOn(
|
|
60
|
+
NestFactory,
|
|
61
|
+
'createApplicationContext',
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
mockedCreateApplicationContext.mockImplementation(
|
|
65
|
+
() =>
|
|
66
|
+
createMock<INestApplicationContext>({
|
|
67
|
+
get: mockedServiceFunction,
|
|
68
|
+
}) as any,
|
|
69
|
+
);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
afterEach(() => {
|
|
73
|
+
getBootstrappedApps().forEach((app) => app.closeApp());
|
|
74
|
+
getBootstrappedApps().clear();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should run executeStandaloneFunction', async () => {
|
|
78
|
+
const mockedFunction = jest.fn(async (service: any) => {
|
|
79
|
+
return service;
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
await executeStandaloneFunction(
|
|
83
|
+
mockedModule,
|
|
84
|
+
mockedServiceFunction,
|
|
85
|
+
mockedFunction,
|
|
86
|
+
{},
|
|
87
|
+
{ closeApp: true },
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
expect(mockedFunction).toHaveBeenCalledTimes(1);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('should run executeStandaloneFunction without close app', async () => {
|
|
94
|
+
const mockedFunction = jest.fn(async (service: any) => {
|
|
95
|
+
return service;
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
await executeStandaloneFunction(
|
|
99
|
+
mockedModule,
|
|
100
|
+
mockedServiceFunction,
|
|
101
|
+
mockedFunction,
|
|
102
|
+
{},
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
expect(mockedFunction).toHaveBeenCalledTimes(1);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('should run executeStandaloneFunction with class module', async () => {
|
|
109
|
+
const mockedFunction = jest.fn(async (service: any) => {
|
|
110
|
+
return service;
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
class TestModule {
|
|
114
|
+
constructor() {}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
await executeStandaloneFunction(
|
|
118
|
+
TestModule,
|
|
119
|
+
mockedServiceFunction,
|
|
120
|
+
mockedFunction,
|
|
121
|
+
{},
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
expect(mockedFunction).toHaveBeenCalledTimes(1);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('should trigger an error if we bootstrap multiple servers', async () => {
|
|
128
|
+
await new AppBootstrap('test1', TestModule1).initApp();
|
|
129
|
+
|
|
130
|
+
let error: any = null;
|
|
131
|
+
try {
|
|
132
|
+
await new AppBootstrap('test2', TestModule2, {}).initApp();
|
|
133
|
+
} catch (e: any) {
|
|
134
|
+
// eslint-disable-next-line no-console
|
|
135
|
+
console.log(e);
|
|
136
|
+
error = e;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
await expect(error).not.toBe(null);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('should not trigger an error when the first app cannot be initialized because of an error', async () => {
|
|
143
|
+
try {
|
|
144
|
+
await new AppBootstrap('brokenModule', BrokenModule).initApp();
|
|
145
|
+
} catch (e: any) {
|
|
146
|
+
// nothing
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
let error: any = null;
|
|
150
|
+
try {
|
|
151
|
+
await new AppBootstrap('test2', TestModule2, {}).initApp();
|
|
152
|
+
} catch (e: any) {
|
|
153
|
+
// eslint-disable-next-line no-console
|
|
154
|
+
console.log(e);
|
|
155
|
+
error = e;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
await expect(error).toBe(null);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('should not trigger an error if we bootstrap multiple servers with the skip option', async () => {
|
|
162
|
+
await new AppBootstrap('test1', TestModule1, {
|
|
163
|
+
skipMultiServerCheck: true,
|
|
164
|
+
}).initApp();
|
|
165
|
+
|
|
166
|
+
let error: any = null;
|
|
167
|
+
try {
|
|
168
|
+
await new AppBootstrap('test2', TestModule2, {
|
|
169
|
+
skipMultiServerCheck: true,
|
|
170
|
+
}).initApp();
|
|
171
|
+
} catch (e: any) {
|
|
172
|
+
// eslint-disable-next-line no-console
|
|
173
|
+
console.log(e);
|
|
174
|
+
error = e;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
await expect(error).toBe(null);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('should not trigger an error if we bootstrap multiple servers with the env variable', async () => {
|
|
181
|
+
process.env.APP_SKIP_MULTISERVER_CHECK = 'true';
|
|
182
|
+
|
|
183
|
+
await new AppBootstrap('test1', TestModule1).initApp();
|
|
184
|
+
|
|
185
|
+
let error: any = null;
|
|
186
|
+
try {
|
|
187
|
+
await new AppBootstrap('test2', TestModule2).initApp();
|
|
188
|
+
} catch (e: any) {
|
|
189
|
+
// eslint-disable-next-line no-console
|
|
190
|
+
console.log(e);
|
|
191
|
+
error = e;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
await expect(error).toBe(null);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('should get the main bootstrapped app', async () => {
|
|
198
|
+
const app = await new AppBootstrap('test1', TestModule1).initApp();
|
|
199
|
+
|
|
200
|
+
const mainApp = getMainBootstrappedApp();
|
|
201
|
+
|
|
202
|
+
expect(mainApp === app).toBe(true);
|
|
203
|
+
});
|
|
204
|
+
});
|