@adaptivestone/framework 4.5.0 → 4.7.0
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/CHANGELOG.md +9 -0
- package/cluster.js +1 -2
- package/commands/GetOpenApiJson.js +1 -2
- package/commands/migration/Migrate.js +2 -2
- package/controllers/Auth.test.js +2 -0
- package/controllers/Home.test.js +2 -0
- package/controllers/index.js +0 -1
- package/models/Sequence.test.js +2 -0
- package/models/User.test.js +2 -0
- package/modules/AbstractController.js +1 -1
- package/modules/Base.js +2 -63
- package/modules/BaseCli.js +0 -1
- package/modules/Modules.test.js +3 -2
- package/package.json +6 -18
- package/server.d.ts +8 -1
- package/server.js +83 -9
- package/services/documentation/DocumentationGenerator.js +18 -20
- package/services/http/middleware/I18n.test.js +12 -5
- package/services/http/middleware/PrepareAppInfo.test.js +7 -3
- package/services/http/middleware/RateLimiter.test.js +10 -6
- package/services/http/middleware/RequestParser.test.js +7 -4
- package/services/validate/ValidateService.js +3 -7
- package/services/validate/ValidateService.test.js +2 -0
- package/tests/setup.js +1 -1
- package/tests/setupVitest.js +119 -0
- package/vitest.config.js +9 -0
- package/babel.config.js +0 -3
package/CHANGELOG.md
CHANGED
package/cluster.js
CHANGED
|
@@ -13,8 +13,7 @@ class GetOpenApiJson extends AbstractCommand {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
try {
|
|
16
|
-
|
|
17
|
-
jsonFile = require(jsonFile);
|
|
16
|
+
jsonFile = JSON.parse(await fs.readFile(jsonFile, 'utf8'));
|
|
18
17
|
} catch (e) {
|
|
19
18
|
this.logger.error(
|
|
20
19
|
'No npm package detected. Please start this command via NPM as it depends on package.json',
|
|
@@ -31,8 +31,8 @@ class Migrate extends AbstractCommand {
|
|
|
31
31
|
|
|
32
32
|
for (const migration of migrations) {
|
|
33
33
|
this.logger.info(`=== Start migration ${migration.file} ===`);
|
|
34
|
-
// eslint-disable-next-line
|
|
35
|
-
const MigrationCommand =
|
|
34
|
+
// eslint-disable-next-line no-await-in-loop
|
|
35
|
+
const MigrationCommand = await import(migration.path);
|
|
36
36
|
const migrationCommand = new MigrationCommand(this.app);
|
|
37
37
|
// eslint-disable-next-line no-await-in-loop
|
|
38
38
|
await migrationCommand.up();
|
package/controllers/Auth.test.js
CHANGED
package/controllers/Home.test.js
CHANGED
package/controllers/index.js
CHANGED
|
@@ -33,7 +33,6 @@ class ControllerManager extends Base {
|
|
|
33
33
|
// TODO wait until https://github.com/nodejs/node/issues/35889
|
|
34
34
|
controllers.push(
|
|
35
35
|
import(controller.path).then(({ default: ControllerModule }) => {
|
|
36
|
-
// eslint-disable-next-line import/no-dynamic-require, global-require
|
|
37
36
|
// const ControllerModule = require(controller.path);
|
|
38
37
|
const contollerName = ControllerModule.name.toLowerCase();
|
|
39
38
|
let prefix = path.dirname(controller.file);
|
package/models/Sequence.test.js
CHANGED
package/models/User.test.js
CHANGED
|
@@ -271,7 +271,7 @@ class AbstractController extends Base {
|
|
|
271
271
|
*/
|
|
272
272
|
if (!this.app.httpServer) {
|
|
273
273
|
this.app.documentation.push(
|
|
274
|
-
DocumentationGenerator.convertDataToDocumentationElement(
|
|
274
|
+
new DocumentationGenerator(this.app).convertDataToDocumentationElement(
|
|
275
275
|
this.getConstructorName(),
|
|
276
276
|
routesInfo,
|
|
277
277
|
middlewaresInfo,
|
package/modules/Base.js
CHANGED
|
@@ -29,71 +29,10 @@ class Base {
|
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
31
|
* Get winston loger for given label
|
|
32
|
-
* @param {
|
|
32
|
+
* @param {string} label name of logger
|
|
33
33
|
*/
|
|
34
34
|
getLogger(label) {
|
|
35
|
-
|
|
36
|
-
const winston = require('winston'); // speed up optimisation
|
|
37
|
-
const alignColorsAndTime = winston.format.combine(
|
|
38
|
-
winston.format.colorize({
|
|
39
|
-
all: true,
|
|
40
|
-
}),
|
|
41
|
-
winston.format.label({
|
|
42
|
-
label: ` \x1B[32m[${label}]\x1B[39m`,
|
|
43
|
-
}),
|
|
44
|
-
winston.format.timestamp(),
|
|
45
|
-
winston.format.printf(
|
|
46
|
-
(info) =>
|
|
47
|
-
`(${process.pid}) ${info.label} ${info.timestamp} ${info.level} : ${info.message}`,
|
|
48
|
-
),
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
const logConfig = this.app.getConfig('log').transports;
|
|
52
|
-
|
|
53
|
-
function IsConstructor(f) {
|
|
54
|
-
try {
|
|
55
|
-
Reflect.construct(String, [], f);
|
|
56
|
-
} catch (e) {
|
|
57
|
-
return false;
|
|
58
|
-
}
|
|
59
|
-
return true;
|
|
60
|
-
}
|
|
61
|
-
const transports = [];
|
|
62
|
-
for (const log of logConfig) {
|
|
63
|
-
if (log.enable) {
|
|
64
|
-
if (log.transport === 'console') {
|
|
65
|
-
transports.push(
|
|
66
|
-
new winston.transports.Console({
|
|
67
|
-
level: log.transportOptions.level,
|
|
68
|
-
format: winston.format.combine(
|
|
69
|
-
winston.format.colorize(),
|
|
70
|
-
alignColorsAndTime,
|
|
71
|
-
),
|
|
72
|
-
}),
|
|
73
|
-
);
|
|
74
|
-
} else {
|
|
75
|
-
// eslint-disable-next-line global-require, import/no-dynamic-require
|
|
76
|
-
let Tr = require(log.transport);
|
|
77
|
-
if (!IsConstructor(Tr) && Tr.default) {
|
|
78
|
-
Tr = Tr.default;
|
|
79
|
-
} else {
|
|
80
|
-
// eslint-disable-next-line no-console
|
|
81
|
-
console.error(
|
|
82
|
-
`${log.transport} not a constructor. Please check it`,
|
|
83
|
-
);
|
|
84
|
-
// eslint-disable-next-line no-continue
|
|
85
|
-
continue;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
transports.push(new Tr(log.transportOptions));
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
return winston.createLogger({
|
|
94
|
-
level: 'silly',
|
|
95
|
-
transports,
|
|
96
|
-
});
|
|
35
|
+
return this.app.logger.child({ label });
|
|
97
36
|
}
|
|
98
37
|
|
|
99
38
|
async getFilesPathWithInheritance(internalFolder, externalFolder) {
|
package/modules/BaseCli.js
CHANGED
|
@@ -50,7 +50,6 @@ class Cli extends Base {
|
|
|
50
50
|
// TODO wait until https://github.com/nodejs/node/issues/35889
|
|
51
51
|
const { default: Command } = await import(this.commands[command]);
|
|
52
52
|
|
|
53
|
-
// eslint-disable-next-line import/no-dynamic-require, global-require
|
|
54
53
|
// const Command = require(this.commands[command]);
|
|
55
54
|
|
|
56
55
|
const c = new Command(this.app, this.commands, args);
|
package/modules/Modules.test.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import SomeController from '../controllers/test/SomeController';
|
|
3
|
+
import AbstractController from './AbstractController';
|
|
3
4
|
|
|
4
5
|
describe('abstract controller methods', () => {
|
|
5
6
|
it('can get routes', async () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adaptivestone/framework",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.7.0",
|
|
4
4
|
"description": "Adaptive stone node js framework",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"engines": {
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"scripts": {
|
|
15
15
|
"dev": "nodemon ./index.js",
|
|
16
16
|
"prod": "nodemon ./cluster.js",
|
|
17
|
-
"test": "
|
|
17
|
+
"test": "vitest run --coverage",
|
|
18
18
|
"prettier": "prettier --check '**/*.(js|jsx|ts|tsx|json|css|scss|md)'",
|
|
19
19
|
"lint": "eslint '**/*.js'",
|
|
20
20
|
"lint:fix": "eslint '**/*.js' --fix",
|
|
@@ -25,18 +25,6 @@
|
|
|
25
25
|
"benchmark2": "h2load -n 10000 -c 50 https://localhost:3300/",
|
|
26
26
|
"redis:docker": "docker run --rm -p 6379:6379 redis"
|
|
27
27
|
},
|
|
28
|
-
"jest": {
|
|
29
|
-
"setupFilesAfterEnv": [
|
|
30
|
-
"./tests/setup.js"
|
|
31
|
-
],
|
|
32
|
-
"testEnvironment": "node",
|
|
33
|
-
"verbose": true,
|
|
34
|
-
"collectCoverage": true,
|
|
35
|
-
"coverageReporters": [
|
|
36
|
-
"text",
|
|
37
|
-
"text-summary"
|
|
38
|
-
]
|
|
39
|
-
},
|
|
40
28
|
"author": "Andrey Logunov",
|
|
41
29
|
"license": "MIT",
|
|
42
30
|
"dependencies": {
|
|
@@ -63,17 +51,17 @@
|
|
|
63
51
|
"yup": "^1.0.0"
|
|
64
52
|
},
|
|
65
53
|
"devDependencies": {
|
|
66
|
-
"@
|
|
54
|
+
"@vitest/coverage-v8": "^0.34.3",
|
|
67
55
|
"eslint": "^8.0.0",
|
|
68
56
|
"eslint-config-airbnb-base": "^15.0.0",
|
|
69
57
|
"eslint-config-prettier": "^9.0.0",
|
|
70
|
-
"eslint-plugin-
|
|
58
|
+
"eslint-plugin-vitest": "^0.3.1",
|
|
71
59
|
"husky": "^8.0.0",
|
|
72
|
-
"jest": "^29.0.0",
|
|
73
60
|
"lint-staged": "^14.0.0",
|
|
74
61
|
"mongodb-memory-server": "^8.0.2",
|
|
75
62
|
"nodemon": "^3.0.1",
|
|
76
|
-
"prettier": "^3.0.0"
|
|
63
|
+
"prettier": "^3.0.0",
|
|
64
|
+
"vitest": "^0.34.3"
|
|
77
65
|
},
|
|
78
66
|
"lint-staged": {
|
|
79
67
|
"**/*.{js,jsx,ts,tsx,json,css,scss,md}": [
|
package/server.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { Model as MongooseModel, Schema } from 'mongoose';
|
|
|
7
7
|
|
|
8
8
|
import BaseCli from './modules/BaseCli';
|
|
9
9
|
import Cache from './services/cache/Cache';
|
|
10
|
+
import winston from 'winston';
|
|
10
11
|
|
|
11
12
|
type ServerConfig = {
|
|
12
13
|
folders: ExpandDeep<TFolderConfig>;
|
|
@@ -22,6 +23,7 @@ declare class Server {
|
|
|
22
23
|
foldersConfig: Server['config']['folders'];
|
|
23
24
|
events: EventEmitter;
|
|
24
25
|
get cache(): Server['cacheService'];
|
|
26
|
+
get logger(): winston.Logger;
|
|
25
27
|
httpServer: null;
|
|
26
28
|
controllerManager: null;
|
|
27
29
|
};
|
|
@@ -39,7 +41,7 @@ declare class Server {
|
|
|
39
41
|
constructor(config: ExpandDeep<ServerConfig>);
|
|
40
42
|
|
|
41
43
|
/**
|
|
42
|
-
* Start server (http
|
|
44
|
+
* Start server (http + init all http ralated functions)
|
|
43
45
|
*/
|
|
44
46
|
startServer(callbackBefore404?: Promise<null>): Promise<null>;
|
|
45
47
|
|
|
@@ -54,6 +56,11 @@ declare class Server {
|
|
|
54
56
|
*/
|
|
55
57
|
getConfig(configName: string): {};
|
|
56
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Return or create new logger instance. This is a main logger instance
|
|
61
|
+
*/
|
|
62
|
+
getLogger(): winston.Logger;
|
|
63
|
+
|
|
57
64
|
/**
|
|
58
65
|
* Primary designed for tests when we need to update some configs before start testing
|
|
59
66
|
* Should be called before any initialization was done
|
package/server.js
CHANGED
|
@@ -3,11 +3,14 @@ const EventEmitter = require('node:events');
|
|
|
3
3
|
|
|
4
4
|
require('dotenv').config();
|
|
5
5
|
const merge = require('deepmerge');
|
|
6
|
+
const winston = require('winston');
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Main framework class.
|
|
9
10
|
*/
|
|
10
11
|
class Server {
|
|
12
|
+
#realLogger = null;
|
|
13
|
+
|
|
11
14
|
/**
|
|
12
15
|
* Construct new server
|
|
13
16
|
* @param {Object} config main config object
|
|
@@ -33,6 +36,9 @@ class Server {
|
|
|
33
36
|
get cache() {
|
|
34
37
|
return that.getCache();
|
|
35
38
|
},
|
|
39
|
+
get logger() {
|
|
40
|
+
return that.getLogger();
|
|
41
|
+
},
|
|
36
42
|
httpServer: null,
|
|
37
43
|
controllerManager: null,
|
|
38
44
|
};
|
|
@@ -46,21 +52,17 @@ class Server {
|
|
|
46
52
|
}
|
|
47
53
|
|
|
48
54
|
/**
|
|
49
|
-
* Start server (http
|
|
55
|
+
* Start server (http + init all http ralated functions)
|
|
50
56
|
* @param <Promise>callbackBefore404 code that should be executed before adding page 404
|
|
51
57
|
* @returns {Promise}
|
|
52
58
|
*/
|
|
53
59
|
async startServer(callbackBefore404 = async () => Promise.resolve()) {
|
|
54
|
-
// eslint-disable-next-line global-require
|
|
55
60
|
// const HttpServer = require('./services/http/HttpServer');
|
|
56
|
-
// eslint-disable-next-line global-require
|
|
57
61
|
// const ControllerManager = require('./controllers/index');
|
|
58
62
|
// TODO wait until https://github.com/nodejs/node/issues/35889
|
|
59
63
|
const [{ default: HttpServer }, { default: ControllerManager }] =
|
|
60
64
|
await Promise.all([
|
|
61
|
-
// eslint-disable-next-line import/extensions
|
|
62
65
|
import('./services/http/HttpServer.js'), // Speed optimisation
|
|
63
|
-
// eslint-disable-next-line import/extensions
|
|
64
66
|
import('./controllers/index.js'), // Speed optimisation
|
|
65
67
|
]);
|
|
66
68
|
|
|
@@ -126,6 +128,80 @@ class Server {
|
|
|
126
128
|
return this.cache.configs.get(configName);
|
|
127
129
|
}
|
|
128
130
|
|
|
131
|
+
/**
|
|
132
|
+
* Return or create new logger instance. This is a main logger instance
|
|
133
|
+
*/
|
|
134
|
+
getLogger() {
|
|
135
|
+
if (!this.#realLogger) {
|
|
136
|
+
this.#realLogger = this.#createLogger();
|
|
137
|
+
}
|
|
138
|
+
return this.#realLogger;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
#createLogger() {
|
|
142
|
+
const alignColorsAndTime = winston.format.combine(
|
|
143
|
+
winston.format.colorize({
|
|
144
|
+
all: true,
|
|
145
|
+
}),
|
|
146
|
+
winston.format.timestamp(),
|
|
147
|
+
winston.format.printf(
|
|
148
|
+
(info) =>
|
|
149
|
+
`(${process.pid}) \x1B[32m[${info.label ?? 'SERVER'}]\x1B[39m ${
|
|
150
|
+
info.timestamp
|
|
151
|
+
} ${info.level} : ${info.message} ${info?.stack ?? ''} ${
|
|
152
|
+
info.durationMs ? `Duration: ${info.durationMs}ms` : ''
|
|
153
|
+
}`,
|
|
154
|
+
),
|
|
155
|
+
);
|
|
156
|
+
const logConfig = this.app.getConfig('log').transports;
|
|
157
|
+
function IsConstructor(f) {
|
|
158
|
+
try {
|
|
159
|
+
Reflect.construct(String, [], f);
|
|
160
|
+
} catch (e) {
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const logger = winston.createLogger({
|
|
167
|
+
format: winston.format.errors({ stack: true }),
|
|
168
|
+
level: 'silly',
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
for (const log of logConfig) {
|
|
172
|
+
if (log.enable) {
|
|
173
|
+
if (log.transport === 'console') {
|
|
174
|
+
logger.add(
|
|
175
|
+
new winston.transports.Console({
|
|
176
|
+
level: log.transportOptions.level,
|
|
177
|
+
format: winston.format.combine(
|
|
178
|
+
winston.format.colorize(),
|
|
179
|
+
alignColorsAndTime,
|
|
180
|
+
),
|
|
181
|
+
}),
|
|
182
|
+
);
|
|
183
|
+
} else {
|
|
184
|
+
import(log.transport).then((Tr) => {
|
|
185
|
+
let Transport = Tr.default;
|
|
186
|
+
if (!IsConstructor(Transport) && Transport.default) {
|
|
187
|
+
Transport = Transport.default;
|
|
188
|
+
} else {
|
|
189
|
+
console.error(
|
|
190
|
+
`${log.transport} not a constructor. Please check it`,
|
|
191
|
+
);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
logger.profile(`Adding new logger ${log.transport}`);
|
|
195
|
+
logger.add(new Transport(log.transportOptions));
|
|
196
|
+
logger.profile(`Adding new logger ${log.transport}`);
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return logger;
|
|
203
|
+
}
|
|
204
|
+
|
|
129
205
|
/**
|
|
130
206
|
* Primary designed for tests when we need to update some configs before start testing
|
|
131
207
|
* Should be called before any initialization was done
|
|
@@ -178,11 +254,9 @@ class Server {
|
|
|
178
254
|
*/
|
|
179
255
|
async runCliCommand(commandName, args) {
|
|
180
256
|
if (!this.cli) {
|
|
181
|
-
// eslint-disable-next-line import/extensions
|
|
182
257
|
// TODO wait until https://github.com/nodejs/node/issues/35889
|
|
183
|
-
|
|
184
|
-
//
|
|
185
|
-
const BaseCli = require('./modules/BaseCli');
|
|
258
|
+
const { default: BaseCli } = await import('./modules/BaseCli.js'); // Speed optimisation
|
|
259
|
+
// const BaseCli = require('./modules/BaseCli');
|
|
186
260
|
this.cli = new BaseCli(this);
|
|
187
261
|
}
|
|
188
262
|
return this.cli.run(commandName, args);
|
|
@@ -2,7 +2,8 @@ const Base = require('../../modules/Base');
|
|
|
2
2
|
const ValidateService = require('../validate/ValidateService');
|
|
3
3
|
|
|
4
4
|
class DocumentationGenerator extends Base {
|
|
5
|
-
|
|
5
|
+
// eslint-disable-next-line class-methods-use-this
|
|
6
|
+
processingFields(fieldsByRoute) {
|
|
6
7
|
const fields = [];
|
|
7
8
|
if (!fieldsByRoute) {
|
|
8
9
|
return fields;
|
|
@@ -36,7 +37,8 @@ class DocumentationGenerator extends Base {
|
|
|
36
37
|
return fields;
|
|
37
38
|
}
|
|
38
39
|
|
|
39
|
-
|
|
40
|
+
// eslint-disable-next-line class-methods-use-this
|
|
41
|
+
selectUniqueFields(fields) {
|
|
40
42
|
return Array.from(
|
|
41
43
|
new Map(fields.map((item) => [item.name, item])).values(),
|
|
42
44
|
).reduce((uniqueArray, item) => {
|
|
@@ -52,7 +54,7 @@ class DocumentationGenerator extends Base {
|
|
|
52
54
|
}, []);
|
|
53
55
|
}
|
|
54
56
|
|
|
55
|
-
|
|
57
|
+
groupFieldsFromSchemas(schemas) {
|
|
56
58
|
const result = [];
|
|
57
59
|
schemas.forEach((schema) => {
|
|
58
60
|
const convertedSchema = new ValidateService(this.app, schema).validator;
|
|
@@ -71,7 +73,7 @@ class DocumentationGenerator extends Base {
|
|
|
71
73
|
return result;
|
|
72
74
|
}
|
|
73
75
|
|
|
74
|
-
|
|
76
|
+
convertDataToDocumentationElement(
|
|
75
77
|
controllerName,
|
|
76
78
|
routesInfo,
|
|
77
79
|
middlewaresInfo,
|
|
@@ -80,23 +82,19 @@ class DocumentationGenerator extends Base {
|
|
|
80
82
|
return {
|
|
81
83
|
contollerName: controllerName,
|
|
82
84
|
routesInfo: routesInfo.map((route) => {
|
|
83
|
-
const middlewareQueryParams = ValidateService
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
},
|
|
90
|
-
).query;
|
|
85
|
+
const middlewareQueryParams = new ValidateService(
|
|
86
|
+
this.app,
|
|
87
|
+
).getMiddlewareParams(middlewaresInfo, routeMiddlewaresReg, {
|
|
88
|
+
method: route.method.toLowerCase(),
|
|
89
|
+
path: route.fullPath,
|
|
90
|
+
}).query;
|
|
91
91
|
|
|
92
|
-
const middlewareRequestParams = ValidateService
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
},
|
|
99
|
-
).request;
|
|
92
|
+
const middlewareRequestParams = new ValidateService(
|
|
93
|
+
this.app,
|
|
94
|
+
).getMiddlewareParams(middlewaresInfo, routeMiddlewaresReg, {
|
|
95
|
+
method: route.method.toLowerCase(),
|
|
96
|
+
path: route.fullPath,
|
|
97
|
+
}).request;
|
|
100
98
|
|
|
101
99
|
const queryParams = this.groupFieldsFromSchemas(middlewareQueryParams);
|
|
102
100
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
import { beforeAll, describe, it, expect } from 'vitest';
|
|
2
|
+
import I18n from './I18n';
|
|
2
3
|
|
|
3
4
|
describe('i18n middleware methods', () => {
|
|
4
5
|
let middleware;
|
|
@@ -45,13 +46,16 @@ describe('i18n middleware methods', () => {
|
|
|
45
46
|
|
|
46
47
|
it('middleware that works', async () => {
|
|
47
48
|
expect.assertions(4);
|
|
48
|
-
|
|
49
|
+
let isCalled = false;
|
|
50
|
+
const nextFunction = () => {
|
|
51
|
+
isCalled = true;
|
|
52
|
+
};
|
|
49
53
|
const req = {
|
|
50
54
|
get: () => 'en',
|
|
51
55
|
appInfo: {},
|
|
52
56
|
};
|
|
53
57
|
await middleware.middleware(req, {}, nextFunction);
|
|
54
|
-
expect(
|
|
58
|
+
expect(isCalled).toBe(true);
|
|
55
59
|
expect(req.appInfo.i18n).toBeDefined();
|
|
56
60
|
expect(req.appInfo.i18n.t('aaaaa')).toBe('aaaaa');
|
|
57
61
|
expect(req.i18n.t('aaaaa')).toBe('aaaaa'); // proxy test
|
|
@@ -62,13 +66,16 @@ describe('i18n middleware methods', () => {
|
|
|
62
66
|
global.server.app.updateConfig('i18n', { enabled: false });
|
|
63
67
|
middleware = new I18n(global.server.app);
|
|
64
68
|
|
|
65
|
-
|
|
69
|
+
let isCalled = false;
|
|
70
|
+
const nextFunction = () => {
|
|
71
|
+
isCalled = true;
|
|
72
|
+
};
|
|
66
73
|
const req = {
|
|
67
74
|
get: () => 'en',
|
|
68
75
|
appInfo: {},
|
|
69
76
|
};
|
|
70
77
|
await middleware.middleware(req, {}, nextFunction);
|
|
71
|
-
expect(
|
|
78
|
+
expect(isCalled).toBe(true);
|
|
72
79
|
expect(req.appInfo.i18n).toBeDefined();
|
|
73
80
|
expect(req.appInfo.i18n.t('aaaaa')).toBe('aaaaa');
|
|
74
81
|
expect(req.i18n.t('aaaaa')).toBe('aaaaa'); // proxy test
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import PrepareAppInfo from './PrepareAppInfo';
|
|
2
3
|
|
|
3
4
|
describe('prepareAppInfo methods', () => {
|
|
4
5
|
it('have description fields', async () => {
|
|
@@ -10,10 +11,13 @@ describe('prepareAppInfo methods', () => {
|
|
|
10
11
|
it('middleware that works', async () => {
|
|
11
12
|
expect.assertions(3);
|
|
12
13
|
const middleware = new PrepareAppInfo(global.server.app);
|
|
13
|
-
|
|
14
|
+
let isCalled = false;
|
|
15
|
+
const nextFunction = () => {
|
|
16
|
+
isCalled = true;
|
|
17
|
+
};
|
|
14
18
|
const req = {};
|
|
15
19
|
await middleware.middleware(req, {}, nextFunction);
|
|
16
|
-
expect(
|
|
20
|
+
expect(isCalled).toBe(true);
|
|
17
21
|
expect(req.appInfo).toBeDefined();
|
|
18
22
|
req.appInfo.test = 5;
|
|
19
23
|
await middleware.middleware(req, {}, nextFunction);
|
|
@@ -1,14 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { setTimeout } from 'node:timers/promises';
|
|
2
|
+
import crypto from 'node:crypto';
|
|
3
|
+
import { beforeAll, afterAll, describe, it, expect } from 'vitest';
|
|
4
|
+
|
|
5
|
+
import RateLimiter from './RateLimiter';
|
|
3
6
|
|
|
4
7
|
let mongoRateLimiter;
|
|
5
8
|
|
|
6
9
|
describe('rate limiter methods', () => {
|
|
7
|
-
beforeAll(() => {
|
|
10
|
+
beforeAll(async () => {
|
|
11
|
+
await setTimeout(20);
|
|
12
|
+
|
|
8
13
|
mongoRateLimiter = new RateLimiter(global.server.app, {
|
|
9
14
|
driver: 'mongo',
|
|
10
15
|
limiterOptions: {
|
|
11
|
-
keyPrefix: `mongo_${Date.now()}`,
|
|
16
|
+
keyPrefix: `mongo_${Date.now()}_${crypto.randomUUID()}}`,
|
|
12
17
|
},
|
|
13
18
|
});
|
|
14
19
|
});
|
|
@@ -89,7 +94,6 @@ describe('rate limiter methods', () => {
|
|
|
89
94
|
const rateLimiter = new RateLimiter(global.server.app, {
|
|
90
95
|
driver: 'unknown',
|
|
91
96
|
});
|
|
92
|
-
const nextFunction = jest.fn(() => {});
|
|
93
97
|
const req = {
|
|
94
98
|
appInfo: {},
|
|
95
99
|
};
|
|
@@ -106,7 +110,7 @@ describe('rate limiter methods', () => {
|
|
|
106
110
|
isSend = true;
|
|
107
111
|
},
|
|
108
112
|
},
|
|
109
|
-
|
|
113
|
+
() => {},
|
|
110
114
|
);
|
|
111
115
|
expect(status).toBe(500);
|
|
112
116
|
expect(isSend).toBe(true);
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { createServer } from 'node:http';
|
|
2
|
+
import { describe, it, expect } from 'vitest';
|
|
3
|
+
|
|
4
|
+
import RequestParser from './RequestParser';
|
|
3
5
|
|
|
4
|
-
|
|
6
|
+
// TODO change on ESM
|
|
7
|
+
const formidable = require('formidable');
|
|
5
8
|
|
|
6
9
|
describe('reqest parser limiter methods', () => {
|
|
7
10
|
it('have description fields', async () => {
|
|
@@ -24,7 +27,7 @@ describe('reqest parser limiter methods', () => {
|
|
|
24
27
|
expect(req.body.multipleFiles).toBeDefined();
|
|
25
28
|
expect(
|
|
26
29
|
req.body.multipleFiles[0] instanceof formidable.PersistentFile,
|
|
27
|
-
).
|
|
30
|
+
).toBeTruthy();
|
|
28
31
|
|
|
29
32
|
res.writeHead(200);
|
|
30
33
|
res.end('ok');
|
|
@@ -41,7 +41,7 @@ class ValidateService extends Base {
|
|
|
41
41
|
/**
|
|
42
42
|
* Filter middlewares by route path and select all parameters
|
|
43
43
|
*/
|
|
44
|
-
|
|
44
|
+
filterRelatedParametersByRoute(middlewares, method, path) {
|
|
45
45
|
const middlewaresParams = middlewares
|
|
46
46
|
.filter(
|
|
47
47
|
(middleware) =>
|
|
@@ -63,11 +63,7 @@ class ValidateService extends Base {
|
|
|
63
63
|
/**
|
|
64
64
|
* Group all middleware(routes + controller) parameters
|
|
65
65
|
*/
|
|
66
|
-
|
|
67
|
-
controllerMiddlewares,
|
|
68
|
-
AllrouteMiddlewares,
|
|
69
|
-
options,
|
|
70
|
-
) {
|
|
66
|
+
getMiddlewareParams(controllerMiddlewares, AllrouteMiddlewares, options) {
|
|
71
67
|
const { method, path } = options;
|
|
72
68
|
const routeMiddlewaresParams = this.filterRelatedParametersByRoute(
|
|
73
69
|
AllrouteMiddlewares,
|
|
@@ -134,7 +130,7 @@ class ValidateService extends Base {
|
|
|
134
130
|
this.validator,
|
|
135
131
|
selectedReqData,
|
|
136
132
|
);
|
|
137
|
-
const additionalMiddlewareSchemas = this.
|
|
133
|
+
const additionalMiddlewareSchemas = this.getMiddlewareParams(
|
|
138
134
|
middlewaresInfo,
|
|
139
135
|
routeMiddlewaresReg,
|
|
140
136
|
routeOptions,
|
package/tests/setup.js
CHANGED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { beforeAll, beforeEach, afterEach, afterAll } from 'vitest';
|
|
3
|
+
|
|
4
|
+
const { MongoMemoryReplSet } = require('mongodb-memory-server');
|
|
5
|
+
|
|
6
|
+
const mongoose = require('mongoose');
|
|
7
|
+
|
|
8
|
+
let mongoMemoryServerInstance;
|
|
9
|
+
|
|
10
|
+
const redis = require('redis');
|
|
11
|
+
const Server = require('../server');
|
|
12
|
+
|
|
13
|
+
const clearRedisNamespace = require('../helpers/redis/clearNamespace');
|
|
14
|
+
|
|
15
|
+
// eslint-disable-next-line vitest/no-hooks, vitest/require-top-level-describe
|
|
16
|
+
beforeAll(async () => {
|
|
17
|
+
mongoMemoryServerInstance = await MongoMemoryReplSet.create({
|
|
18
|
+
// binary: { version: '4.4.6' },
|
|
19
|
+
replSet: { count: 1, storageEngine: 'wiredTiger' },
|
|
20
|
+
});
|
|
21
|
+
await mongoMemoryServerInstance.waitUntilRunning();
|
|
22
|
+
process.env.LOGGER_CONSOLE_LEVEL = 'error';
|
|
23
|
+
const connectionStringMongo = await mongoMemoryServerInstance.getUri();
|
|
24
|
+
// console.info('MONGO_URI: ', connectionStringMongo);
|
|
25
|
+
global.server = new Server({
|
|
26
|
+
folders: {
|
|
27
|
+
config: process.env.TEST_FOLDER_CONFIG || path.resolve('./config'),
|
|
28
|
+
controllers:
|
|
29
|
+
process.env.TEST_FOLDER_CONTROLLERS || path.resolve('./controllers'),
|
|
30
|
+
views: process.env.TEST_FOLDER_VIEWS || path.resolve('./views'),
|
|
31
|
+
public: process.env.TEST_FOLDER_PUBLIC || path.resolve('./public'),
|
|
32
|
+
models: process.env.TEST_FOLDER_MODELS || path.resolve('./models'),
|
|
33
|
+
emails:
|
|
34
|
+
process.env.TEST_FOLDER_EMAIL ||
|
|
35
|
+
process.env.TEST_FOLDER_EMAILS ||
|
|
36
|
+
path.resolve('./services/messaging/email/templates'),
|
|
37
|
+
locales: process.env.TEST_FOLDER_LOCALES || path.resolve('./locales'),
|
|
38
|
+
commands: process.env.TEST_FOLDER_COMMANDS || path.resolve('./commands'),
|
|
39
|
+
migrations:
|
|
40
|
+
process.env.TEST_FOLDER_MIGRATIONS || path.resolve('./migrations'),
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
global.server.updateConfig('mongo', {
|
|
44
|
+
connectionString: connectionStringMongo,
|
|
45
|
+
});
|
|
46
|
+
global.server.updateConfig('http', { port: 0 }); // allow to use random
|
|
47
|
+
global.server.updateConfig('mail', { transport: 'stub' });
|
|
48
|
+
if (!global.testSetup) {
|
|
49
|
+
global.testSetup = {};
|
|
50
|
+
}
|
|
51
|
+
global.server.testingGetUrl = (urlPart) =>
|
|
52
|
+
`http://127.0.0.1:${global.server.getConfig('http').port}${urlPart}`;
|
|
53
|
+
if (!global.testSetup.disableUserCreate) {
|
|
54
|
+
const User = global.server.app.getModel('User');
|
|
55
|
+
global.user = await User.create({
|
|
56
|
+
email: 'test@test.com',
|
|
57
|
+
password: 'testPassword',
|
|
58
|
+
isVerified: true,
|
|
59
|
+
name: {
|
|
60
|
+
nick: 'testUserNickName',
|
|
61
|
+
},
|
|
62
|
+
}).catch((e) => {
|
|
63
|
+
// eslint-disable-next-line no-console
|
|
64
|
+
console.error(e);
|
|
65
|
+
// eslint-disable-next-line no-console
|
|
66
|
+
console.info(
|
|
67
|
+
'That error can happens in case you have custom user model. Please use global.testSetup.disableUserCreate flag to skip user creating',
|
|
68
|
+
);
|
|
69
|
+
});
|
|
70
|
+
global.authToken = await global.user.generateToken();
|
|
71
|
+
}
|
|
72
|
+
if (typeof global.testSetup.beforeAll === 'function') {
|
|
73
|
+
await global.testSetup.beforeAll();
|
|
74
|
+
}
|
|
75
|
+
await global.server.startServer();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// eslint-disable-next-line vitest/no-hooks, vitest/require-top-level-describe
|
|
79
|
+
beforeEach(() => {
|
|
80
|
+
if (global.server) {
|
|
81
|
+
const key = `test-${Math.random().toString(36).substring(7)}`;
|
|
82
|
+
global.server.app.updateConfig('redis', {
|
|
83
|
+
namespace: key,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// eslint-disable-next-line vitest/no-hooks, vitest/require-top-level-describe
|
|
89
|
+
afterEach(async () => {
|
|
90
|
+
if (global.server) {
|
|
91
|
+
const { url, namespace } = global.server.getConfig('redis');
|
|
92
|
+
const redisClient = redis.createClient({ url });
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
await redisClient.connect();
|
|
96
|
+
await clearRedisNamespace(redisClient, namespace);
|
|
97
|
+
await redisClient.disconnect();
|
|
98
|
+
} catch (err) {
|
|
99
|
+
// that ok. No redis connection
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// eslint-disable-next-line vitest/no-hooks, vitest/require-top-level-describe
|
|
105
|
+
afterAll(async () => {
|
|
106
|
+
if (global.server) {
|
|
107
|
+
global.server.app.httpServer.shutdown();
|
|
108
|
+
global.server.app.events.emit('shutdown');
|
|
109
|
+
}
|
|
110
|
+
// setTimeout(async () => {
|
|
111
|
+
if (typeof global.testSetup.afterAll === 'function') {
|
|
112
|
+
await global.testSetup.afterAll();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
await mongoose.disconnect();
|
|
116
|
+
await mongoMemoryServerInstance.stop();
|
|
117
|
+
|
|
118
|
+
// }, 2000);
|
|
119
|
+
});
|
package/vitest.config.js
ADDED
package/babel.config.js
DELETED