@vida-global/core 1.1.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 (52) hide show
  1. package/README.md +9 -0
  2. package/index.js +17 -0
  3. package/lib/active_record/README.md +205 -0
  4. package/lib/active_record/baseRecord.js +112 -0
  5. package/lib/active_record/db/connection.js +128 -0
  6. package/lib/active_record/db/connectionConfiguration.js +114 -0
  7. package/lib/active_record/db/importSchema.js +4 -0
  8. package/lib/active_record/db/migration.js +132 -0
  9. package/lib/active_record/db/migrationTemplate.js +8 -0
  10. package/lib/active_record/db/migrationVersion.js +68 -0
  11. package/lib/active_record/db/migrator.js +169 -0
  12. package/lib/active_record/db/queryInterface.js +47 -0
  13. package/lib/active_record/db/schema.js +113 -0
  14. package/lib/active_record/index.js +6 -0
  15. package/lib/active_record/utils.js +43 -0
  16. package/lib/http/README.md +32 -0
  17. package/lib/http/client.js +129 -0
  18. package/lib/http/error.js +34 -0
  19. package/lib/logger/README.md +2 -0
  20. package/lib/logger/index.js +16 -0
  21. package/lib/release/develop.js +27 -0
  22. package/lib/release/git.js +86 -0
  23. package/lib/release/increment.js +56 -0
  24. package/lib/release/index.js +10 -0
  25. package/lib/release/release.js +30 -0
  26. package/lib/release/utils.js +44 -0
  27. package/lib/server/README.md +37 -0
  28. package/lib/server/index.js +9 -0
  29. package/lib/server/server.js +359 -0
  30. package/lib/server/serverController.js +344 -0
  31. package/lib/server/systemController.js +23 -0
  32. package/package.json +37 -0
  33. package/scripts/active_record/migrate.js +30 -0
  34. package/scripts/release.js +62 -0
  35. package/test/active_record/baseRecord.test.js +179 -0
  36. package/test/active_record/db/connection.test.js +221 -0
  37. package/test/active_record/db/connectionConfiguration.test.js +184 -0
  38. package/test/active_record/db/migrator.test.js +266 -0
  39. package/test/active_record/db/queryInterface.test.js +66 -0
  40. package/test/http/client.test.js +271 -0
  41. package/test/http/error.test.js +71 -0
  42. package/test/release/develop.test.js +57 -0
  43. package/test/release/git.test.js +189 -0
  44. package/test/release/increment.test.js +145 -0
  45. package/test/release/release.test.js +72 -0
  46. package/test/release/utils.test.js +148 -0
  47. package/test/server/helpers/controllers/barController.js +9 -0
  48. package/test/server/helpers/controllers/fooController.js +48 -0
  49. package/test/server/helpers/controllers/sub/bazController.js +10 -0
  50. package/test/server/helpers/server.js +14 -0
  51. package/test/server/server.test.js +188 -0
  52. package/test/server/serverController.test.js +251 -0
@@ -0,0 +1,145 @@
1
+ const fs = require('fs');
2
+ const { increment } = require('../../lib/release/increment');
3
+ const Git = require('../../lib/release/git');
4
+ const TestHelpers = require('@vida-global/test-helpers');
5
+ const utils = require('../../lib/release/utils');
6
+
7
+
8
+ jest.mock('../../lib/release/git', () => {
9
+ return { add: jest.fn(), createBranch: jest.fn(), commit: jest.fn(), pull: jest.fn(), push: jest.fn() };
10
+ });
11
+ jest.mock('../../lib/release/utils', () => {
12
+ return { getCurrentVersion: jest.fn(), confirmContinue: jest.fn() };
13
+ });
14
+ jest.mock('fs', () => {
15
+ return { readFileSync: jest.fn(), writeFileSync: jest.fn() };
16
+ });
17
+
18
+
19
+ let config;
20
+ let version;
21
+ const packageJsonPath = `${process.cwd()}/package.json`;
22
+ beforeEach(() => {
23
+ jest.resetAllMocks();
24
+ version = [TestHelpers.Faker.Math.randomNumber(),
25
+ TestHelpers.Faker.Math.randomNumber(),
26
+ TestHelpers.Faker.Math.randomNumber()];
27
+ utils.getCurrentVersion.mockImplementation(() => version);
28
+ utils.confirmContinue.mockImplementation(() => true);
29
+
30
+ config = {
31
+ [TestHelpers.Faker.Text.randomString()]: TestHelpers.Faker.Text.randomString(),
32
+ [TestHelpers.Faker.Text.randomString()]: TestHelpers.Faker.Text.randomString(),
33
+ version: version.join('.')
34
+ }
35
+ fs.readFileSync.mockImplementation(() => JSON.stringify(config, null, 4));
36
+ });
37
+
38
+
39
+ describe('increment', () => {
40
+ it ('runs steps in the proper order', async () => {
41
+ await increment('point');
42
+
43
+ expect(Git.pull).toHaveBeenCalledTimes(1);
44
+ expect(utils.getCurrentVersion).toHaveBeenCalledTimes(1);
45
+ expect(Git.pull).toHaveBeenCalledBefore(utils.getCurrentVersion);
46
+
47
+ expect(utils.confirmContinue).toHaveBeenCalledTimes(1);
48
+ expect(utils.getCurrentVersion).toHaveBeenCalledBefore(utils.confirmContinue);
49
+
50
+ expect(Git.createBranch).toHaveBeenCalledTimes(1);
51
+ expect(utils.confirmContinue).toHaveBeenCalledBefore(Git.createBranch);
52
+
53
+ expect(fs.readFileSync).toHaveBeenCalledTimes(1);
54
+ expect(Git.createBranch).toHaveBeenCalledBefore(fs.readFileSync);
55
+
56
+ expect(fs.writeFileSync).toHaveBeenCalledTimes(1);
57
+ expect(fs.readFileSync).toHaveBeenCalledBefore(fs.writeFileSync);
58
+
59
+ expect(Git.push).toHaveBeenCalledTimes(1);
60
+ expect(fs.writeFileSync).toHaveBeenCalledBefore(Git.push);
61
+ });
62
+
63
+
64
+ describe('getNewVersion', () => {
65
+ it ('increments the major version', async () => {
66
+ await increment('major');
67
+ const newVersion = [...version];
68
+ newVersion[0] = newVersion[0] + 1;
69
+ newVersion[1] = 0;
70
+ newVersion[2] = 0;
71
+ expect(Git.createBranch).toHaveBeenCalledWith(`release/${newVersion.join('.')}`);
72
+ });
73
+
74
+ it ('increments the minor version', async () => {
75
+ await increment('minor');
76
+ const newVersion = [...version];
77
+ newVersion[1] = newVersion[1] + 1;
78
+ newVersion[2] = 0;
79
+ expect(Git.createBranch).toHaveBeenCalledWith(`release/${newVersion.join('.')}`);
80
+ });
81
+
82
+ it ('increments the point version', async () => {
83
+ await increment('point');
84
+ const newVersion = [...version];
85
+ newVersion[2] = newVersion[2] + 1;
86
+ expect(Git.createBranch).toHaveBeenCalledWith(`release/${newVersion.join('.')}`);
87
+ });
88
+
89
+ it ('throws an error for an invalid type', async () => {
90
+ expect(async () => {await increment('foo')}).rejects.toThrow();;
91
+ });
92
+ });
93
+
94
+
95
+ describe('confirmation', () => {
96
+ it ('asks the user if they want to upgrade to the new version', async () => {
97
+ await increment('point');
98
+ const newVersion = [version[0], version[1], version[2]+1].join('.');
99
+ expect(utils.confirmContinue).toHaveBeenCalledWith(`Do you want to create version ${newVersion}?`);
100
+ });
101
+
102
+ it ('does not continue if the user responds "n"', async () => {
103
+ utils.confirmContinue.mockImplementation(() => false);
104
+ await increment('point');
105
+
106
+ expect(Git.createBranch).not.toHaveBeenCalled();
107
+ expect(fs.readFileSync).not.toHaveBeenCalled();
108
+ expect(fs.writeFileSync).not.toHaveBeenCalled();
109
+ expect(Git.push).not.toHaveBeenCalled();
110
+ });
111
+ });
112
+
113
+
114
+ describe('createBranch', () => {
115
+ it ('creates a release branch for the new version', async () => {
116
+ await increment('point');
117
+ const newVersion = [version[0], version[1], version[2]+1].join('.');
118
+ expect(Git.createBranch).toHaveBeenCalledWith(`release/${newVersion}`);
119
+ });
120
+ });
121
+
122
+
123
+ describe('updatePackageVersion', () => {
124
+ it ('writes the new package version to package.json', async () => {
125
+ const newVersion = [version[0], version[1], version[2]+1].join('.');
126
+ const newConfig = structuredClone(config);
127
+ newConfig.version = newVersion;
128
+
129
+ await increment('point');
130
+
131
+ expect(fs.writeFileSync).toHaveBeenCalledWith(packageJsonPath, JSON.stringify(newConfig, null, 4));
132
+ });
133
+
134
+ it ('adds package.json to be committed', async () => {
135
+ await increment('point');
136
+ expect(Git.add).toHaveBeenCalledWith(packageJsonPath);
137
+ });
138
+
139
+ it ('adds a descriptive commit message', async () => {
140
+ const newVersion = [version[0], version[1], version[2]+1].join('.');
141
+ await increment('point');
142
+ expect(Git.commit).toHaveBeenCalledWith(`build: incrementing build to ${newVersion}`);
143
+ });
144
+ });
145
+ });
@@ -0,0 +1,72 @@
1
+ const { release } = require('../../lib/release/release');
2
+ const Git = require('../../lib/release/git');
3
+ const TestHelpers = require('@vida-global/test-helpers');
4
+ const utils = require('../../lib/release/utils');
5
+
6
+
7
+ jest.mock('../../lib/release/git', () => {
8
+ return { pull: jest.fn(), forceRemotePush: jest.fn(), branches: jest.fn() };
9
+ });
10
+ jest.mock('../../lib/release/utils', () => {
11
+ return { getCurrentVersion: jest.fn(), confirmContinue: jest.fn() };
12
+ });
13
+
14
+
15
+ let env;
16
+ let version;
17
+ beforeEach(() => {
18
+ jest.resetAllMocks();
19
+ env = TestHelpers.Faker.Text.randomString();
20
+ version = [TestHelpers.Faker.Math.randomNumber(),
21
+ TestHelpers.Faker.Math.randomNumber(),
22
+ TestHelpers.Faker.Math.randomNumber()];
23
+ utils.getCurrentVersion.mockImplementation(() => version);
24
+ utils.confirmContinue.mockImplementation(() => true);
25
+ });
26
+
27
+
28
+ describe('release', () => {
29
+ it ('runs steps in the proper order', async () => {
30
+ await release(env);
31
+
32
+ expect(Git.pull).toHaveBeenCalledTimes(1)
33
+ expect(utils.confirmContinue).toHaveBeenCalledTimes(1)
34
+ expect(Git.pull).toHaveBeenCalledBefore(utils.confirmContinue)
35
+
36
+ expect(Git.forceRemotePush).toHaveBeenCalledTimes(1)
37
+ expect(utils.confirmContinue).toHaveBeenCalledBefore(Git.forceRemotePush)
38
+ });
39
+
40
+ it ('defaults to the current version', async () => {
41
+ await release(env);
42
+ const versionStr = version.join('.');
43
+ expect(utils.confirmContinue).toHaveBeenCalledWith(`Do you want to release version ${versionStr} to ${env}?`);
44
+ expect(Git.forceRemotePush).toHaveBeenCalledWith(`release/${versionStr}`, `release/${env}`);
45
+ });
46
+
47
+ it ('accepts an alternate version', async () => {
48
+ const altVersion = [TestHelpers.Faker.Math.randomNumber(),
49
+ TestHelpers.Faker.Math.randomNumber(),
50
+ TestHelpers.Faker.Math.randomNumber()].join('.');
51
+ Git.branches.mockImplementation(() => [`origin/release/${version.join('.')}`, `origin/release/${altVersion}`, TestHelpers.Faker.Text.randomString()]);
52
+ await release(env, altVersion);
53
+ expect(utils.confirmContinue).toHaveBeenCalledWith(`Do you want to release version ${altVersion} to ${env}?`);
54
+ expect(Git.forceRemotePush).toHaveBeenCalledWith(`release/${altVersion}`, `release/${env}`);
55
+ });
56
+
57
+ it ('does not continue if the user responds "n"', async () => {
58
+ utils.confirmContinue.mockImplementation(() => false);
59
+ await release(env);
60
+
61
+ expect(Git.forceRemotePush).not.toHaveBeenCalled();
62
+ });
63
+
64
+ it ('throws an error if the version does not exist', async () => {
65
+ const altVersion = [TestHelpers.Faker.Math.randomNumber(),
66
+ TestHelpers.Faker.Math.randomNumber(),
67
+ TestHelpers.Faker.Math.randomNumber()].join('.');
68
+ Git.branches.mockImplementation(() => [`origin/release/${version.join('.')}`, TestHelpers.Faker.Text.randomString()]);
69
+ expect(async () => { await release(env, altVersion)}).rejects.toThrow();
70
+ });
71
+ });
72
+
@@ -0,0 +1,148 @@
1
+ const Git = require('../../lib/release/git');
2
+ const TestHelpers = require('@vida-global/test-helpers');
3
+ const utils = require('../../lib/release/utils');
4
+ const readline = require('node:readline/promises');
5
+
6
+
7
+ jest.mock('node:readline/promises', () => {
8
+ return { createInterface: jest.fn(), question: jest.fn(), close: jest.fn() };
9
+ });
10
+
11
+ jest.mock('../../lib/release/git', () => {
12
+ return {branches: jest.fn()};
13
+ });
14
+
15
+
16
+ beforeEach(() => {
17
+ jest.resetAllMocks();
18
+ readline.createInterface.mockImplementation(() => {
19
+ return {question: readline.question, close: readline.close};
20
+ });
21
+ });
22
+
23
+
24
+ describe('getCurrentVersion', () => {
25
+ const major1 = TestHelpers.Faker.Math.randomNumber();
26
+ const minor1 = TestHelpers.Faker.Math.randomNumber();
27
+ const point1 = TestHelpers.Faker.Math.randomNumber();
28
+ const diff1 = TestHelpers.Faker.Math.randomNumber();
29
+ const diff2 = TestHelpers.Faker.Math.randomNumber();
30
+
31
+ const b1 = `origin/release/${major1}/${minor1}/${point1}`;
32
+
33
+ it ('sorts branches by primary version first', () => {
34
+ const b2 = `origin/release/${major1 + diff1 }.${minor1 + diff2}.${point1 + diff1}`;
35
+ const b3 = `origin/release/${major1 + diff1 + diff2}.${minor1 }.${point1}`;
36
+ Git.branches.mockImplementation(() => [b1, b3, b2]);
37
+ expect(utils.getCurrentVersion()).toEqual([major1+diff1+diff2, minor1, point1]);
38
+ });
39
+
40
+ it ('sorts branches by minor version second', () => {
41
+ const b2 = `origin/release/${major1 + diff1}.${minor1 + diff1 + diff2}.${point1}`;
42
+ const b3 = `origin/release/${major1 + diff1}.${minor1 + diff1 }.${point1 + diff1}`;
43
+ Git.branches.mockImplementation(() => [b1, b3, b2]);
44
+ expect(utils.getCurrentVersion()).toEqual([major1+diff1, minor1+diff1+diff2, point1]);
45
+ });
46
+
47
+ it ('sorts branches by point version last', () => {
48
+ const b2 = `origin/release/${major1 + diff1}.${minor1 + diff1}.${point1 + diff1}`;
49
+ const b3 = `origin/release/${major1 + diff1}.${minor1 + diff1}.${point1 + diff1 + diff2}`;
50
+ Git.branches.mockImplementation(() => [b1, b3, b2]);
51
+ expect(utils.getCurrentVersion()).toEqual([major1+diff1, minor1+diff1, point1+diff1+diff2]);
52
+ });
53
+
54
+ it ('ignores branches that don not fit the pattern', () => {
55
+ const b2 = `origin/release/${major1 + diff1}.${minor1}.${point1}`;
56
+ const b3 = `origin/release/${TestHelpers.Faker.Text.randomString()}`;
57
+ const b4 = TestHelpers.Faker.Text.randomString();
58
+ Git.branches.mockImplementation(() => [b1, b3, b2, b4]);
59
+ expect(utils.getCurrentVersion()).toEqual([major1+diff1, minor1, point1]);
60
+ });
61
+
62
+ it ('defaults to 1.0.0', () => {
63
+ const b2 = `origin/release/${TestHelpers.Faker.Text.randomString()}`;
64
+ const b3 = `origin/release/${TestHelpers.Faker.Text.randomString()}`;
65
+ const b4 = TestHelpers.Faker.Text.randomString();
66
+ Git.branches.mockImplementation(() => [b3, b2, b4]);
67
+ expect(utils.getCurrentVersion()).toEqual([1, 0, 0]);
68
+ });
69
+ });
70
+
71
+
72
+ describe('getInput', () => {
73
+ let question;
74
+ let numOptions;
75
+ let options;
76
+ let answer;
77
+ beforeEach(() => {
78
+ question = TestHelpers.Faker.Text.randomSentence();
79
+ numOptions = TestHelpers.Faker.Math.randomNumber(10);
80
+ options = [...Array(numOptions)].map(() => TestHelpers.Faker.Text.randomString());
81
+ answer = options[Math.floor(Math.random() * numOptions)];
82
+ readline.question.mockImplementation(() => answer);
83
+ });
84
+
85
+ it ('asks the question and appends the options', async () => {
86
+ await utils.getInput(question, options)
87
+ expect(readline.question).toHaveBeenCalledWith(`${question} [${options.join(',')}] `);
88
+ });
89
+
90
+ it ('asks the question once if it gets a valid answer', async () => {
91
+ const theAnswer = await utils.getInput(question, options)
92
+ expect(readline.question).toHaveBeenCalledTimes(1);
93
+ expect(theAnswer).toEqual(answer);
94
+ });
95
+
96
+ it ('closes the input after getting an answer', async () => {
97
+ await utils.getInput(question, options)
98
+ expect(readline.question).toHaveBeenCalledTimes(1);
99
+ expect(readline.close).toHaveBeenCalledTimes(1);
100
+ expect(readline.question).toHaveBeenCalledBefore(readline.close);
101
+ });
102
+
103
+ it ('repeats the question until it gets a valid answer', async () => {
104
+ const numAttempts = TestHelpers.Faker.Math.randomNumber(10);
105
+ let attemptNumber = 1;
106
+
107
+ readline.question.mockImplementation(() => {
108
+ if (attemptNumber == numAttempts) return answer;
109
+ attemptNumber = attemptNumber + 1;
110
+ return TestHelpers.Faker.Text.randomString();
111
+ });
112
+
113
+ const theAnswer = await utils.getInput(question, options)
114
+ expect(readline.question).toHaveBeenCalledTimes(numAttempts);
115
+ expect(readline.close).toHaveBeenCalledTimes(numAttempts);
116
+ expect(theAnswer).toEqual(answer);
117
+
118
+ for(let i=1; i <= numAttempts; i++){
119
+ expect(readline.question).toHaveBeenNthCalledWith(i, `${question} [${options.join(',')}] `);
120
+ }
121
+ });
122
+ });
123
+
124
+
125
+ describe('confirmContinue', () => {
126
+ let question;
127
+ beforeEach(() => {
128
+ question = TestHelpers.Faker.Text.randomSentence();
129
+ });
130
+
131
+ it ('asks the question with "y" or "n" as options', async () => {
132
+ readline.question.mockImplementation(() => 'y');
133
+ await utils.confirmContinue(question);
134
+ expect(readline.question).toHaveBeenCalledWith(`${question} [y,n] `);
135
+ });
136
+
137
+ it ('returns true if the user answers "y"', async () => {
138
+ readline.question.mockImplementation(() => 'y');
139
+ const confirmed = await utils.confirmContinue(question);
140
+ expect(confirmed).toBeTruthy();
141
+ });
142
+
143
+ it ('returns false if the user answers "n"', async () => {
144
+ readline.question.mockImplementation(() => 'n');
145
+ const confirmed = await utils.confirmContinue(question);
146
+ expect(confirmed).toBeFalsy();
147
+ });
148
+ });
@@ -0,0 +1,9 @@
1
+ const { VidaServerController } = require('../../../../lib/server');
2
+
3
+
4
+ class BarController extends VidaServerController {
5
+
6
+ }
7
+
8
+
9
+ module.exports = { BarController };
@@ -0,0 +1,48 @@
1
+ const { VidaServerController } = require('../../../../lib/server');
2
+
3
+
4
+ class FooController extends VidaServerController {
5
+ postIndex() {}
6
+ getFooBar() {}
7
+ getBazBan() {}
8
+ postFooBar() {}
9
+ putFooBar() {}
10
+ deleteFooBar() {}
11
+ patchFooBar() {}
12
+ notAnAction() {}
13
+
14
+ static get routes() {
15
+ return {getBazBan: '/baz/:id/ban'};
16
+ }
17
+
18
+
19
+ setupCallbacks() {
20
+ this.beforeCallback('beforeCallback1', {only: 'postIndex'});
21
+ this.beforeCallback('beforeCallback2');
22
+ this.beforeCallback('beforeCallback3', {except: ['getFooBar', 'getBazBan']});
23
+ this.beforeCallback('beforeCallback4', {only: 'putFooBar'});
24
+ this.beforeCallback('beforeCallback5');
25
+
26
+ this.afterCallback('afterCallback1');
27
+ this.afterCallback('afterCallback2', {except: ['getFooBar', 'getBazBan']});
28
+ this.afterCallback('afterCallback3', {only: 'postIndex'});
29
+ this.afterCallback('afterCallback4', {only: 'deleteFooBar'});
30
+ this.afterCallback('afterCallback5');
31
+ }
32
+
33
+
34
+ beforeCallback1() {}
35
+ beforeCallback2() {}
36
+ beforeCallback3() {}
37
+ beforeCallback4() { return false; }
38
+ beforeCallback5() {}
39
+
40
+ afterCallback1() {}
41
+ afterCallback2() {}
42
+ afterCallback3() {}
43
+ afterCallback4() { return false; }
44
+ afterCallback5() {}
45
+ }
46
+
47
+
48
+ module.exports = { FooController };
@@ -0,0 +1,10 @@
1
+ const { VidaServerController } = require('../../../../../lib/server');
2
+
3
+
4
+ class BazController extends VidaServerController {
5
+
6
+ }
7
+
8
+
9
+ module.exports = { BazController };
10
+
@@ -0,0 +1,14 @@
1
+ const { VidaServer } = require('../../../lib/server');
2
+
3
+
4
+ class TestServer extends VidaServer {
5
+ get controllerDirectories() {
6
+ return [`${__dirname}/controllers`];
7
+ }
8
+ }
9
+
10
+
11
+ module.exports = {
12
+ TestServer
13
+ }
14
+
@@ -0,0 +1,188 @@
1
+ const { BarController } = require('./helpers/controllers/barController');
2
+ const { BazController } = require('./helpers/controllers/sub/bazController');
3
+ const { FooController } = require('./helpers/controllers/fooController');
4
+ const { Request } = require('jest-express/lib/request');
5
+ const { Response } = require('jest-express/lib/response');
6
+ const { TestServer } = require('./helpers/server');
7
+ const { VidaServer } = require('../../lib/server');
8
+
9
+
10
+ jest.mock('express', () => {
11
+ const { Response } = require('jest-express/lib/response');
12
+
13
+ // missing functions in mock Response
14
+ Response.prototype.on = jest.fn();
15
+
16
+ return require('jest-express');
17
+ });
18
+
19
+
20
+ afterEach(() => {
21
+ jest.clearAllMocks();
22
+ });
23
+
24
+
25
+ describe('VidaServer', () => {
26
+ describe('VidaServer#controllers', () => {
27
+ it ('searches directories and subdirectories', () => {
28
+ const server = new TestServer();
29
+ const controllers = server.controllerClasses;
30
+ expect(controllers.length).toEqual(3);
31
+ expect(controllers[0]).toBe(BarController);
32
+ expect(controllers[1]).toBe(FooController);
33
+ expect(controllers[2]).toBe(BazController);
34
+ });
35
+
36
+ it ('initializes controllers with the correct path', () => {
37
+ const server = new TestServer();
38
+ const controllers = server.controllerClasses;
39
+ expect(controllers[0].routePrefix).toEqual('/bar');
40
+ expect(controllers[1].routePrefix).toEqual('/foo');
41
+ expect(controllers[2].routePrefix).toEqual('/sub/baz');
42
+ });
43
+ });
44
+
45
+
46
+ describe('VidaServer#registerControllers', () => {
47
+ it ('it configures routes on underlying express server', () => {
48
+ const server = new TestServer();
49
+ const getSpy = jest.spyOn(server, '_get').mockImplementation(() => null);
50
+ const postSpy = jest.spyOn(server, '_post').mockImplementation(() => null);
51
+ const deleteSpy = jest.spyOn(server, '_delete').mockImplementation(() => null);
52
+ const putSpy = jest.spyOn(server, '_put').mockImplementation(() => null);
53
+ const patchSpy = jest.spyOn(server, '_patch').mockImplementation(() => null);
54
+ server.registerControllers();
55
+
56
+ expect(getSpy).toHaveBeenCalledTimes(4);
57
+ expect(getSpy).toHaveBeenCalledWith('/foo/fooBar', expect.any(Function));
58
+ expect(getSpy).toHaveBeenCalledWith('/baz/:id/ban', expect.any(Function));
59
+ // Built in system routes
60
+ expect(getSpy).toHaveBeenCalledWith('/', expect.any(Function));
61
+ expect(getSpy).toHaveBeenCalledWith('/system/healthCheck', expect.any(Function));
62
+
63
+ expect(postSpy).toHaveBeenCalledTimes(2);
64
+ expect(postSpy).toHaveBeenCalledWith('/foo', expect.any(Function));
65
+ expect(postSpy).toHaveBeenCalledWith('/foo/fooBar', expect.any(Function));
66
+
67
+ expect(putSpy).toHaveBeenCalledTimes(1);
68
+ expect(putSpy).toHaveBeenCalledWith('/foo/fooBar', expect.any(Function));
69
+
70
+ expect(deleteSpy).toHaveBeenCalledTimes(1);
71
+ expect(deleteSpy).toHaveBeenCalledWith('/foo/fooBar', expect.any(Function));
72
+
73
+ expect(patchSpy).toHaveBeenCalledTimes(1);
74
+ expect(patchSpy).toHaveBeenCalledWith('/foo/fooBar', expect.any(Function));
75
+ });
76
+ });
77
+
78
+
79
+ describe('VidaServer#requestHandler', () => {
80
+ const server = new TestServer();
81
+ const request = new Request();
82
+ const response = new Response();
83
+
84
+ const beforeSpy1 = jest.spyOn(FooController.prototype, 'beforeCallback1');
85
+ const beforeSpy2 = jest.spyOn(FooController.prototype, 'beforeCallback2');
86
+ const beforeSpy3 = jest.spyOn(FooController.prototype, 'beforeCallback3');
87
+ const beforeSpy4 = jest.spyOn(FooController.prototype, 'beforeCallback4');
88
+ const beforeSpy5 = jest.spyOn(FooController.prototype, 'beforeCallback5');
89
+
90
+ const afterSpy1 = jest.spyOn(FooController.prototype, 'afterCallback1');
91
+ const afterSpy2 = jest.spyOn(FooController.prototype, 'afterCallback2');
92
+ const afterSpy3 = jest.spyOn(FooController.prototype, 'afterCallback3');
93
+ const afterSpy4 = jest.spyOn(FooController.prototype, 'afterCallback4');
94
+ const afterSpy5 = jest.spyOn(FooController.prototype, 'afterCallback5');
95
+
96
+ it ('calls callbacks in the right order', async () => {
97
+ const postIndexSpy = jest.spyOn(FooController.prototype, 'postIndex');
98
+ const requestHandler = server.requestHandler('postIndex', FooController);
99
+ await requestHandler(request, response);
100
+
101
+ expect(beforeSpy1).toHaveBeenCalledTimes(1);
102
+ expect(beforeSpy1).toHaveBeenCalledBefore(beforeSpy2);
103
+ expect(beforeSpy2).toHaveBeenCalledTimes(1);
104
+ expect(beforeSpy2).toHaveBeenCalledBefore(beforeSpy3);
105
+ expect(beforeSpy3).toHaveBeenCalledTimes(1);
106
+ expect(beforeSpy3).toHaveBeenCalledBefore(beforeSpy5);
107
+ expect(beforeSpy5).toHaveBeenCalledTimes(1);
108
+ expect(beforeSpy5).toHaveBeenCalledBefore(postIndexSpy);
109
+
110
+ expect(postIndexSpy).toHaveBeenCalledTimes(1);
111
+ expect(postIndexSpy).toHaveBeenCalledBefore(afterSpy1);
112
+
113
+ expect(afterSpy1).toHaveBeenCalledTimes(1);
114
+ expect(afterSpy1).toHaveBeenCalledBefore(afterSpy2);
115
+ expect(afterSpy2).toHaveBeenCalledTimes(1);
116
+ expect(afterSpy2).toHaveBeenCalledBefore(afterSpy3);
117
+ expect(afterSpy3).toHaveBeenCalledTimes(1);
118
+ expect(afterSpy3).toHaveBeenCalledBefore(afterSpy5);
119
+ expect(afterSpy5).toHaveBeenCalledTimes(1);
120
+ });
121
+
122
+ it ('calls does not call callbacks that should only be called for some actions', async () => {
123
+ const postFooBarSpy = jest.spyOn(FooController.prototype, 'postFooBar');
124
+ const requestHandler = server.requestHandler('postFooBar', FooController);
125
+ await requestHandler(request, response);
126
+
127
+ expect(beforeSpy1).toHaveBeenCalledTimes(0);
128
+ expect(beforeSpy2).toHaveBeenCalledTimes(1);
129
+ expect(beforeSpy3).toHaveBeenCalledTimes(1);
130
+ expect(beforeSpy5).toHaveBeenCalledTimes(1);
131
+
132
+ expect(postFooBarSpy).toHaveBeenCalledTimes(1);
133
+
134
+ expect(afterSpy1).toHaveBeenCalledTimes(1);
135
+ expect(afterSpy2).toHaveBeenCalledTimes(1);
136
+ expect(afterSpy3).toHaveBeenCalledTimes(0);
137
+ expect(afterSpy5).toHaveBeenCalledTimes(1);
138
+ });
139
+
140
+ it ('calls does not call callbacks that should not be called for some actions', async () => {
141
+ const getFooBarSpy = jest.spyOn(FooController.prototype, 'getFooBar');
142
+ const requestHandler = server.requestHandler('getFooBar', FooController);
143
+ await requestHandler(request, response);
144
+
145
+ expect(beforeSpy1).toHaveBeenCalledTimes(0);
146
+ expect(beforeSpy2).toHaveBeenCalledTimes(1);
147
+ expect(beforeSpy3).toHaveBeenCalledTimes(0);
148
+ expect(beforeSpy5).toHaveBeenCalledTimes(1);
149
+
150
+ expect(getFooBarSpy).toHaveBeenCalledTimes(1);
151
+
152
+ expect(afterSpy1).toHaveBeenCalledTimes(1);
153
+ expect(afterSpy2).toHaveBeenCalledTimes(0);
154
+ expect(afterSpy3).toHaveBeenCalledTimes(0);
155
+ expect(afterSpy5).toHaveBeenCalledTimes(1);
156
+ });
157
+
158
+ it ('does not call the action when a callback return false', async () => {
159
+ const putFooBarSpy = jest.spyOn(FooController.prototype, 'putFooBar');
160
+ const requestHandler = server.requestHandler('putFooBar', FooController);
161
+ await requestHandler(request, response);
162
+
163
+ expect(beforeSpy4).toHaveBeenCalledTimes(1);
164
+ expect(beforeSpy5).toHaveBeenCalledTimes(0);
165
+
166
+ expect(putFooBarSpy).toHaveBeenCalledTimes(0);
167
+
168
+ expect(afterSpy1).toHaveBeenCalledTimes(0);
169
+ expect(afterSpy2).toHaveBeenCalledTimes(0);
170
+ expect(afterSpy3).toHaveBeenCalledTimes(0);
171
+ expect(afterSpy5).toHaveBeenCalledTimes(0);
172
+ });
173
+
174
+ it ('does not call the subsequent callbacks when a callback return false', async () => {
175
+ const deleteFooBarSpy = jest.spyOn(FooController.prototype, 'deleteFooBar');
176
+ const requestHandler = server.requestHandler('deleteFooBar', FooController);
177
+ await requestHandler(request, response);
178
+
179
+ expect(deleteFooBarSpy).toHaveBeenCalledTimes(1);
180
+
181
+ expect(afterSpy1).toHaveBeenCalledTimes(1);
182
+ expect(afterSpy2).toHaveBeenCalledTimes(1);
183
+ expect(afterSpy3).toHaveBeenCalledTimes(0);
184
+ expect(afterSpy4).toHaveBeenCalledTimes(1);
185
+ expect(afterSpy5).toHaveBeenCalledTimes(0);
186
+ });
187
+ });
188
+ });