@adaptivestone/framework 4.7.0 → 4.8.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.
Files changed (92) hide show
  1. package/CHANGELOG.md +9 -1
  2. package/Cli.js +1 -0
  3. package/commands/SyncIndexes.js +1 -1
  4. package/controllers/Auth.js +3 -5
  5. package/controllers/index.js +0 -2
  6. package/coverage/base.css +224 -0
  7. package/coverage/block-navigation.js +87 -0
  8. package/coverage/clover.xml +3542 -0
  9. package/coverage/coverage-final.json +44 -0
  10. package/coverage/favicon.png +0 -0
  11. package/coverage/framework/config/auth.js.html +100 -0
  12. package/coverage/framework/config/http.js.html +112 -0
  13. package/coverage/framework/config/i18n.js.html +121 -0
  14. package/coverage/framework/config/index.html +236 -0
  15. package/coverage/framework/config/log.js.html +151 -0
  16. package/coverage/framework/config/mail.js.html +172 -0
  17. package/coverage/framework/config/mongo.js.html +94 -0
  18. package/coverage/framework/config/rateLimiter.js.html +133 -0
  19. package/coverage/framework/config/redis.js.html +97 -0
  20. package/coverage/framework/config/validate.js.html +94 -0
  21. package/coverage/framework/controllers/Auth.js.html +715 -0
  22. package/coverage/framework/controllers/Home.js.html +169 -0
  23. package/coverage/framework/controllers/index.html +146 -0
  24. package/coverage/framework/controllers/index.js.html +259 -0
  25. package/coverage/framework/controllers/test/SomeController.js.html +571 -0
  26. package/coverage/framework/controllers/test/index.html +116 -0
  27. package/coverage/framework/helpers/files.js.html +310 -0
  28. package/coverage/framework/helpers/index.html +131 -0
  29. package/coverage/framework/helpers/logger.js.html +142 -0
  30. package/coverage/framework/helpers/redis/clearNamespace.js.html +127 -0
  31. package/coverage/framework/helpers/redis/index.html +116 -0
  32. package/coverage/framework/index.html +116 -0
  33. package/coverage/framework/models/Migration.js.html +130 -0
  34. package/coverage/framework/models/Sequence.js.html +151 -0
  35. package/coverage/framework/models/User.js.html +859 -0
  36. package/coverage/framework/models/index.html +146 -0
  37. package/coverage/framework/modules/AbstractController.js.html +1309 -0
  38. package/coverage/framework/modules/AbstractModel.js.html +268 -0
  39. package/coverage/framework/modules/Base.js.html +244 -0
  40. package/coverage/framework/modules/index.html +146 -0
  41. package/coverage/framework/server.js.html +1186 -0
  42. package/coverage/framework/services/cache/Cache.js.html +445 -0
  43. package/coverage/framework/services/cache/index.html +116 -0
  44. package/coverage/framework/services/documentation/DocumentationGenerator.js.html +592 -0
  45. package/coverage/framework/services/documentation/index.html +116 -0
  46. package/coverage/framework/services/http/HttpServer.js.html +373 -0
  47. package/coverage/framework/services/http/index.html +116 -0
  48. package/coverage/framework/services/http/middleware/AbstractMiddleware.js.html +238 -0
  49. package/coverage/framework/services/http/middleware/Auth.js.html +145 -0
  50. package/coverage/framework/services/http/middleware/GetUserByToken.js.html +223 -0
  51. package/coverage/framework/services/http/middleware/I18n.js.html +442 -0
  52. package/coverage/framework/services/http/middleware/Pagination.js.html +253 -0
  53. package/coverage/framework/services/http/middleware/PrepareAppInfo.js.html +139 -0
  54. package/coverage/framework/services/http/middleware/RateLimiter.js.html +472 -0
  55. package/coverage/framework/services/http/middleware/RequestLogger.js.html +151 -0
  56. package/coverage/framework/services/http/middleware/RequestParser.js.html +199 -0
  57. package/coverage/framework/services/http/middleware/Role.js.html +172 -0
  58. package/coverage/framework/services/http/middleware/index.html +251 -0
  59. package/coverage/framework/services/http/middleware/test/CheckFlag.js.html +139 -0
  60. package/coverage/framework/services/http/middleware/test/index.html +116 -0
  61. package/coverage/framework/services/messaging/email/index.html +116 -0
  62. package/coverage/framework/services/messaging/email/index.js.html +739 -0
  63. package/coverage/framework/services/messaging/index.html +116 -0
  64. package/coverage/framework/services/messaging/index.js.html +100 -0
  65. package/coverage/framework/services/validate/ValidateService.js.html +556 -0
  66. package/coverage/framework/services/validate/drivers/AbstractValidator.js.html +196 -0
  67. package/coverage/framework/services/validate/drivers/CustomValidator.js.html +241 -0
  68. package/coverage/framework/services/validate/drivers/YupValidator.js.html +394 -0
  69. package/coverage/framework/services/validate/drivers/index.html +146 -0
  70. package/coverage/framework/services/validate/index.html +116 -0
  71. package/coverage/index.html +356 -0
  72. package/coverage/prettify.css +1 -0
  73. package/coverage/prettify.js +2 -0
  74. package/coverage/sort-arrow-sprite.png +0 -0
  75. package/coverage/sorter.js +196 -0
  76. package/helpers/files.js +75 -0
  77. package/helpers/logger.js +19 -0
  78. package/models/Migration.test.js +19 -0
  79. package/modules/AbstractController.js +1 -3
  80. package/modules/Base.js +6 -56
  81. package/modules/BaseCli.js +0 -3
  82. package/package.json +2 -2
  83. package/server.d.ts +6 -0
  84. package/server.js +126 -78
  85. package/services/cache/Cache.js +10 -5
  86. package/services/cache/Cache.test.js +81 -0
  87. package/services/http/middleware/Auth.test.js +57 -0
  88. package/services/http/middleware/I18n.test.js +15 -3
  89. package/services/http/middleware/PrepareAppInfo.test.js +1 -1
  90. package/services/http/middleware/Role.test.js +93 -0
  91. package/tests/setup.js +1 -0
  92. package/tests/setupVitest.js +1 -0
package/server.js CHANGED
@@ -1,9 +1,14 @@
1
1
  /* eslint-disable no-console */
2
2
  const EventEmitter = require('node:events');
3
+ const { hrtime } = require('node:process');
3
4
 
4
5
  require('dotenv').config();
5
6
  const merge = require('deepmerge');
6
7
  const winston = require('winston');
8
+ const { getFilesPathWithInheritance } = require('./helpers/files');
9
+ const { consoleLogger } = require('./helpers/logger');
10
+
11
+ const Cache = require('./services/cache/Cache');
7
12
 
8
13
  /**
9
14
  * Main framework class.
@@ -11,6 +16,8 @@ const winston = require('winston');
11
16
  class Server {
12
17
  #realLogger = null;
13
18
 
19
+ #isInited = false;
20
+
14
21
  /**
15
22
  * Construct new server
16
23
  * @param {Object} config main config object
@@ -46,6 +53,7 @@ class Server {
46
53
  this.cache = {
47
54
  configs: new Map(),
48
55
  models: new Map(),
56
+ modelConstructors: new Map(),
49
57
  };
50
58
 
51
59
  this.cli = false;
@@ -57,13 +65,11 @@ class Server {
57
65
  * @returns {Promise}
58
66
  */
59
67
  async startServer(callbackBefore404 = async () => Promise.resolve()) {
60
- // const HttpServer = require('./services/http/HttpServer');
61
- // const ControllerManager = require('./controllers/index');
62
- // TODO wait until https://github.com/nodejs/node/issues/35889
63
68
  const [{ default: HttpServer }, { default: ControllerManager }] =
64
69
  await Promise.all([
65
70
  import('./services/http/HttpServer.js'), // Speed optimisation
66
71
  import('./controllers/index.js'), // Speed optimisation
72
+ this.init(),
67
73
  ]);
68
74
 
69
75
  this.addErrorHandling();
@@ -78,20 +84,115 @@ class Server {
78
84
  this.app.httpServer.add404Page();
79
85
  }
80
86
 
87
+ /**
88
+ * Do an initialization (config reading, etc)
89
+ * @returns {Promise}
90
+ */
91
+ async init() {
92
+ if (this.#isInited) {
93
+ return true;
94
+ }
95
+
96
+ console.time('Server init. Done');
97
+ await Promise.all([this.#initConfigFiles(), this.#loadModelFiles()]);
98
+
99
+ this.#isInited = true;
100
+
101
+ console.timeEnd('Server init. Done');
102
+
103
+ return true;
104
+ }
105
+
106
+ async #initConfigFiles() {
107
+ const files = await getFilesPathWithInheritance({
108
+ internalFolder: `${__dirname}/config`,
109
+ externalFolder: this.app.foldersConfig.config,
110
+ loggerFileType: 'CONFIG',
111
+ logger: (m) => consoleLogger('info', m),
112
+ filter: {
113
+ startWithCapital: false,
114
+ },
115
+ });
116
+
117
+ const configFiles = {};
118
+
119
+ for (const file of files) {
120
+ const config = file.file.split('.');
121
+ if (!configFiles[config[0]]) {
122
+ configFiles[config[0]] = {};
123
+ }
124
+ if (config.length === 2) {
125
+ configFiles[config[0]].default = file.path;
126
+ } else {
127
+ configFiles[config[0]][config[1]] = file.path;
128
+ }
129
+ }
130
+
131
+ const loadConfig = async (configName, values) => {
132
+ const promises = [import(values.default)];
133
+ if (process.env.NODE_ENV && values[process.env.NODE_ENV]) {
134
+ promises.push(import(values[process.env.NODE_ENV]));
135
+ }
136
+ const result = await Promise.all(promises);
137
+ return {
138
+ name: configName,
139
+ finalValue: merge(result[0].default, result[1]?.default || {}, {
140
+ arrayMerge: (destinationArray, sourceArray) => sourceArray,
141
+ }),
142
+ };
143
+ };
144
+
145
+ const loadingPromises = [];
146
+
147
+ for (const [configFile, value] of Object.entries(configFiles)) {
148
+ loadingPromises.push(loadConfig(configFile, value));
149
+ }
150
+
151
+ const configs = await Promise.all(loadingPromises);
152
+
153
+ for (const config of configs) {
154
+ this.cache.configs.set(config.name, config.finalValue);
155
+ }
156
+ return true;
157
+ }
158
+
159
+ async #loadModelFiles() {
160
+ const files = await getFilesPathWithInheritance({
161
+ internalFolder: `${__dirname}/models`,
162
+ externalFolder: this.app.foldersConfig.models,
163
+ loggerFileType: 'MODEL',
164
+ logger: (m) => consoleLogger('info', m),
165
+ });
166
+
167
+ const promises = [];
168
+ for (const file of files) {
169
+ const t = hrtime.bigint();
170
+ promises.push(
171
+ import(file.path).then((f) => ({
172
+ name: file.file.split('.')[0],
173
+ file: f,
174
+ took: hrtime.bigint() - t,
175
+ })),
176
+ );
177
+ }
178
+
179
+ const loadedModels = await Promise.all(promises);
180
+
181
+ for (const model of loadedModels) {
182
+ this.cache.modelConstructors.set(model.name, model.file.default);
183
+ }
184
+ return true;
185
+ }
186
+
81
187
  /**
82
188
  * Add error logging on promise reject
83
189
  */
84
- // eslint-disable-next-line class-methods-use-this
85
190
  addErrorHandling() {
86
- process.on('uncaughtException', console.error);
87
- process.on('unhandledRejection', (reason, p) => {
88
- console.log(
89
- 'Possibly Unhandled Rejection at: Promise ',
90
- p,
91
- ' reason: ',
92
- reason,
93
- );
94
- console.trace('unhandledRejection');
191
+ process.on('uncaughtException', (e) =>
192
+ this.app.logger.error('uncaughtException', e),
193
+ );
194
+ process.on('unhandledRejection', (e) => {
195
+ this.app.logger.error('unhandledRejection', e);
95
196
  });
96
197
  }
97
198
 
@@ -106,24 +207,15 @@ class Server {
106
207
  * @returns {Object} config object. Structure depends of config file
107
208
  */
108
209
  getConfig(configName) {
109
- // const configName = name.charAt(0).toUpperCase() + name.slice(1);
210
+ if (!this.#isInited) {
211
+ throw new Error('You should call Server.init() before using it');
212
+ }
213
+
110
214
  if (!this.cache.configs.has(configName)) {
111
- let envConfig = {};
112
- if (process.env.NODE_ENV) {
113
- envConfig =
114
- this.getFileWithExtendingInhirence(
115
- 'config',
116
- `${configName}.${process.env.NODE_ENV}.js`,
117
- ) || envConfig;
118
- }
119
- this.cache.configs.set(
120
- configName,
121
- merge(
122
- this.getFileWithExtendingInhirence('config', configName),
123
- envConfig,
124
- { arrayMerge: (destinationArray, sourceArray) => sourceArray },
125
- ),
215
+ this.logger.warn(
216
+ `You asked for config ${configName} that not exists. Please check you codebase `,
126
217
  );
218
+ return {};
127
219
  }
128
220
  return this.cache.configs.get(configName);
129
221
  }
@@ -225,14 +317,14 @@ class Server {
225
317
  */
226
318
  getModel(modelName) {
227
319
  if (modelName.endsWith('s')) {
228
- console.warn(
320
+ this.app.logger.warn(
229
321
  `Probably your model name '${modelName}' in plural from. Try to avoid plural form`,
230
322
  );
231
323
  }
232
324
  if (!this.cache.models.has(modelName)) {
233
- const Model = this.getFileWithExtendingInhirence('models', modelName);
325
+ const Model = this.cache.modelConstructors.get(modelName);
234
326
  if (!Model) {
235
- console.error(`Model not found: ${modelName}`);
327
+ this.app.logger.error(`Model not found: ${modelName}`);
236
328
  return false;
237
329
  }
238
330
  try {
@@ -240,8 +332,8 @@ class Server {
240
332
 
241
333
  this.cache.models.set(modelName, model.mongooseModel);
242
334
  } catch (e) {
243
- console.error(`Problem with model ${modelName}, ${e.message}`);
244
- console.error(e);
335
+ this.app.logger.error(`Problem with model ${modelName}, ${e.message}`);
336
+ this.app.logger.error(e);
245
337
  }
246
338
  }
247
339
  return this.cache.models.get(modelName);
@@ -254,9 +346,7 @@ class Server {
254
346
  */
255
347
  async runCliCommand(commandName, args) {
256
348
  if (!this.cli) {
257
- // TODO wait until https://github.com/nodejs/node/issues/35889
258
349
  const { default: BaseCli } = await import('./modules/BaseCli.js'); // Speed optimisation
259
- // const BaseCli = require('./modules/BaseCli');
260
350
  this.cli = new BaseCli(this);
261
351
  }
262
352
  return this.cli.run(commandName, args);
@@ -268,52 +358,10 @@ class Server {
268
358
  */
269
359
  getCache() {
270
360
  if (!this.cacheService) {
271
- // eslint-disable-next-line global-require
272
- const Cache = require('./services/cache/Cache'); // Speed optimisation
273
361
  this.cacheService = new Cache(this.app);
274
362
  }
275
363
  return this.cacheService;
276
364
  }
277
-
278
- /**
279
- * Get file using Inhirence (ability to overrite models, configs, etc)
280
- * @param {('models'|'config')} fileType type of file to load
281
- * @param {string} fileName name of file to load
282
- */
283
- getFileWithExtendingInhirence(fileType, fileName) {
284
- let file;
285
- try {
286
- // eslint-disable-next-line global-require, import/no-dynamic-require
287
- file = require(`${this.config.folders[fileType]}/${fileName}`);
288
- } catch (e) {
289
- try {
290
- // eslint-disable-next-line global-require, import/no-dynamic-require
291
- file = require(`./${fileType}/${fileName}`);
292
- } catch (e2) {
293
- const levels = [
294
- 'error',
295
- 'warn',
296
- 'info',
297
- 'http',
298
- 'verbose',
299
- 'debug',
300
- 'silly',
301
- ];
302
-
303
- if (
304
- !process.env.LOGGER_CONSOLE_LEVEL ||
305
- levels.indexOf(process.env.LOGGER_CONSOLE_LEVEL) > 0 // as a warn level
306
- ) {
307
- console.warn(
308
- `Config not found '${fileName}'. This can be a normal (in case this an environment config)`,
309
- );
310
- }
311
-
312
- file = false;
313
- }
314
- }
315
- return file;
316
- }
317
365
  }
318
366
 
319
367
  module.exports = Server;
@@ -1,13 +1,17 @@
1
- const redis = require('redis');
2
1
  const Base = require('../../modules/Base');
3
2
 
4
3
  class Cache extends Base {
5
4
  constructor(app) {
5
+ super(app);
6
+ this.whenReady = this.#init();
7
+ }
8
+
9
+ async #init() {
6
10
  // todo for now only redis. refactor for drives support in future
7
11
  // at least memory and redis drivers should be presented
8
12
  // memory drives should works on master process level
9
13
  // we should support multiple cashe same time
10
- super(app);
14
+ const redis = await import('redis');
11
15
  const conf = this.app.getConfig('redis');
12
16
  this.redisClient = redis.createClient({
13
17
  url: conf.url,
@@ -33,6 +37,7 @@ class Cache extends Base {
33
37
  }
34
38
 
35
39
  async getSetValue(keyValue, onNotFound, storeTime = 60 * 5) {
40
+ await this.whenReady;
36
41
  if (!this.redisClient.isOpen) {
37
42
  await this.redisClient.connect();
38
43
  }
@@ -58,9 +63,7 @@ class Cache extends Base {
58
63
  try {
59
64
  result = await onNotFound();
60
65
  } catch (e) {
61
- this.logger.error(
62
- `Cache onNotFound for key '${key}' error: ${e.message}`,
63
- );
66
+ this.logger.error(`Cache onNotFound for key '${key}' error: ${e}`);
64
67
  this.promiseMapping.delete(key);
65
68
  reject(e);
66
69
  return Promise.reject(e);
@@ -100,6 +103,8 @@ class Cache extends Base {
100
103
  }
101
104
 
102
105
  async removeKey(keyValue) {
106
+ await this.whenReady;
107
+
103
108
  if (!this.redisClient.isOpen) {
104
109
  await this.redisClient.connect();
105
110
  }
@@ -0,0 +1,81 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { setTimeout } from 'node:timers/promises';
3
+
4
+ describe('cache', () => {
5
+ const time = Date.now();
6
+
7
+ it('can get set values', async () => {
8
+ expect.assertions(2);
9
+
10
+ const { cache } = global.server.app;
11
+
12
+ const res = await cache.getSetValue('TEST_TIME', () => time);
13
+ expect(res).toStrictEqual(time);
14
+
15
+ const res2 = await cache.getSetValue('TEST_TIME', () => '123');
16
+ expect(res2).toStrictEqual(time);
17
+ });
18
+
19
+ it('can delete values', async () => {
20
+ expect.assertions(1);
21
+ const { cache } = global.server.app;
22
+
23
+ await cache.removeKey('TEST_TIME');
24
+
25
+ const res2 = await cache.getSetValue('TEST_TIME', () => '123');
26
+ expect(res2).toBe('123');
27
+ });
28
+
29
+ it('can works with big int', async () => {
30
+ expect.assertions(2);
31
+ const { cache } = global.server.app;
32
+
33
+ const res = await cache.getSetValue('BIN_INT', () => 1n);
34
+ expect(res).toBe(1n);
35
+
36
+ const res2 = await cache.getSetValue('BIN_INT', () => '1111');
37
+ expect(res2).toBe(1n);
38
+ });
39
+
40
+ it('can execute only one request per time', async () => {
41
+ expect.assertions(3);
42
+ const { cache } = global.server.app;
43
+ let counter = 0;
44
+
45
+ const f = async () => {
46
+ await setTimeout(10);
47
+ counter += 1;
48
+ return 1;
49
+ };
50
+
51
+ const [res, res1] = await Promise.all([
52
+ cache.getSetValue('T', f),
53
+ cache.getSetValue('T', f),
54
+ ]);
55
+
56
+ expect(counter).toBe(1);
57
+
58
+ expect(res).toBe(1);
59
+ expect(res1).toBe(1);
60
+ });
61
+
62
+ it('can handle problems on onNotFound', async () => {
63
+ expect.assertions(1);
64
+ const getAsyncThrow = async () => {
65
+ throw new Error('err');
66
+ };
67
+ let err;
68
+
69
+ const { cache } = global.server.app;
70
+
71
+ try {
72
+ await Promise.all([
73
+ cache.getSetValue('THROW', getAsyncThrow),
74
+ cache.getSetValue('THROW', getAsyncThrow),
75
+ ]);
76
+ } catch (e) {
77
+ err = e;
78
+ }
79
+ expect(err.message).toBe('err');
80
+ });
81
+ });
@@ -0,0 +1,57 @@
1
+ import { beforeAll, describe, it, expect } from 'vitest';
2
+ import Auth from './Auth';
3
+
4
+ describe('atuh middleware methods', () => {
5
+ let middleware;
6
+ beforeAll(() => {
7
+ middleware = new Auth(global.server.app);
8
+ });
9
+ it('have description fields', async () => {
10
+ expect.assertions(1);
11
+ expect(middleware.constructor.description).toBeDefined();
12
+ });
13
+
14
+ it('middleware pass when user presented', async () => {
15
+ expect.assertions(1);
16
+ let isCalled = false;
17
+ const nextFunction = () => {
18
+ isCalled = true;
19
+ };
20
+ const req = {
21
+ appInfo: {
22
+ user: true,
23
+ },
24
+ };
25
+ await middleware.middleware(req, {}, nextFunction);
26
+ expect(isCalled).toBeTruthy();
27
+ });
28
+
29
+ it('middleware NOT pass when user NOT presented', async () => {
30
+ expect.assertions(3);
31
+ let isCalled = false;
32
+ let status;
33
+ let isSend;
34
+ const nextFunction = () => {
35
+ isCalled = true;
36
+ };
37
+ const req = {
38
+ appInfo: {}, // no user
39
+ };
40
+ await middleware.middleware(
41
+ req,
42
+ {
43
+ status(statusCode) {
44
+ status = statusCode;
45
+ return this;
46
+ },
47
+ json() {
48
+ isSend = true;
49
+ },
50
+ },
51
+ nextFunction,
52
+ );
53
+ expect(isCalled).toBeFalsy();
54
+ expect(status).toBe(401);
55
+ expect(isSend).toBeTruthy();
56
+ });
57
+ });
@@ -12,7 +12,7 @@ describe('i18n middleware methods', () => {
12
12
  });
13
13
 
14
14
  it('detectors should works correctly', async () => {
15
- expect.assertions(5);
15
+ expect.assertions(6);
16
16
  const request = {
17
17
  get: () => 'en',
18
18
  query: {
@@ -42,10 +42,13 @@ describe('i18n middleware methods', () => {
42
42
  };
43
43
  lang = await middleware.detectLang(request);
44
44
  expect(lang).toBe('en');
45
+
46
+ lang = await middleware.detectLang(request, false);
47
+ expect(lang).toBe('en-GB');
45
48
  });
46
49
 
47
50
  it('middleware that works', async () => {
48
- expect.assertions(4);
51
+ expect.assertions(6);
49
52
  let isCalled = false;
50
53
  const nextFunction = () => {
51
54
  isCalled = true;
@@ -55,10 +58,19 @@ describe('i18n middleware methods', () => {
55
58
  appInfo: {},
56
59
  };
57
60
  await middleware.middleware(req, {}, nextFunction);
58
- expect(isCalled).toBe(true);
61
+ expect(isCalled).toBeTruthy();
59
62
  expect(req.appInfo.i18n).toBeDefined();
63
+ expect(req.appInfo.i18n.language).toBe('en');
60
64
  expect(req.appInfo.i18n.t('aaaaa')).toBe('aaaaa');
61
65
  expect(req.i18n.t('aaaaa')).toBe('aaaaa'); // proxy test
66
+
67
+ const req2 = {
68
+ get: () => 'fakeLang',
69
+ appInfo: {},
70
+ };
71
+
72
+ await middleware.middleware(req2, {}, nextFunction);
73
+ expect(req2.appInfo.i18n.language).toBe('en');
62
74
  });
63
75
 
64
76
  it('middleware disabled', async () => {
@@ -17,7 +17,7 @@ describe('prepareAppInfo methods', () => {
17
17
  };
18
18
  const req = {};
19
19
  await middleware.middleware(req, {}, nextFunction);
20
- expect(isCalled).toBe(true);
20
+ expect(isCalled).toBeTruthy();
21
21
  expect(req.appInfo).toBeDefined();
22
22
  req.appInfo.test = 5;
23
23
  await middleware.middleware(req, {}, nextFunction);
@@ -0,0 +1,93 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import Role from './Role';
3
+
4
+ describe('role middleware methods', () => {
5
+ it('have description fields', async () => {
6
+ expect.assertions(1);
7
+ const middleware = new Role(global.server.app);
8
+ expect(middleware.constructor.description).toBeDefined();
9
+ });
10
+
11
+ it('middleware pass when user presented with a right role', async () => {
12
+ expect.assertions(1);
13
+ let isCalled = false;
14
+ const nextFunction = () => {
15
+ isCalled = true;
16
+ };
17
+ const req = {
18
+ appInfo: {
19
+ user: {
20
+ roles: ['role1', 'role2'],
21
+ },
22
+ },
23
+ };
24
+ const middleware = new Role(global.server.app, {
25
+ roles: ['admin', 'role1'],
26
+ });
27
+
28
+ await middleware.middleware(req, {}, nextFunction);
29
+ expect(isCalled).toBeTruthy();
30
+ });
31
+
32
+ it('middleware NOT pass when user NOT presented', async () => {
33
+ expect.assertions(3);
34
+ let isCalled = false;
35
+ let status;
36
+ let isSend;
37
+ const nextFunction = () => {
38
+ isCalled = true;
39
+ };
40
+ const req = {
41
+ appInfo: {}, // no user
42
+ };
43
+ const middleware = new Role(global.server.app);
44
+ await middleware.middleware(
45
+ req,
46
+ {
47
+ status(statusCode) {
48
+ status = statusCode;
49
+ return this;
50
+ },
51
+ send() {
52
+ isSend = true;
53
+ },
54
+ },
55
+ nextFunction,
56
+ );
57
+ expect(isCalled).toBeFalsy();
58
+ expect(status).toBe(401);
59
+ expect(isSend).toBeTruthy();
60
+ });
61
+
62
+ it('middleware NOT pass when user have a wrong role', async () => {
63
+ expect.assertions(3);
64
+ let isCalled = false;
65
+ let status;
66
+ let isSend;
67
+ const nextFunction = () => {
68
+ isCalled = true;
69
+ };
70
+ const req = {
71
+ appInfo: {
72
+ user: { roles: ['role1', 'role2'] },
73
+ },
74
+ };
75
+ const middleware = new Role(global.server.app, { roles: ['admin'] });
76
+ await middleware.middleware(
77
+ req,
78
+ {
79
+ status(statusCode) {
80
+ status = statusCode;
81
+ return this;
82
+ },
83
+ json() {
84
+ isSend = true;
85
+ },
86
+ },
87
+ nextFunction,
88
+ );
89
+ expect(isCalled).toBeFalsy();
90
+ expect(status).toBe(403);
91
+ expect(isSend).toBeTruthy();
92
+ });
93
+ });
package/tests/setup.js CHANGED
@@ -38,6 +38,7 @@ beforeAll(async () => {
38
38
  process.env.TEST_FOLDER_MIGRATIONS || path.resolve('./migrations'),
39
39
  },
40
40
  });
41
+ await global.server.init();
41
42
  global.server.updateConfig('mongo', {
42
43
  connectionString: connectionStringMongo,
43
44
  });
@@ -40,6 +40,7 @@ beforeAll(async () => {
40
40
  process.env.TEST_FOLDER_MIGRATIONS || path.resolve('./migrations'),
41
41
  },
42
42
  });
43
+ await global.server.init();
43
44
  global.server.updateConfig('mongo', {
44
45
  connectionString: connectionStringMongo,
45
46
  });