Skip to content
This repository was archived by the owner on Mar 28, 2022. It is now read-only.

Commit b252557

Browse files
committed
feat: Add/remove routers on start/stop plugin methods, not in init method
1 parent f6f9251 commit b252557

9 files changed

Lines changed: 199 additions & 20 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1414
### Changed
1515
- chore(deps): Update dependencies
1616
- test(e2e): Move utils to a support folder
17+
- feat: Add/remove routers on start/stop plugin methods, not in init method.
1718
### Fixed
1819
### Removed
1920

jest.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ module.exports = {
2626

2727
// The glob patterns Jest uses to detect test files
2828
testMatch: ["<rootDir>/test/unit/**/*.spec.js"],
29-
// testMatch: ["<rootDir>/test/unit/**/Alerts.spec.js"],
29+
// testMatch: ["<rootDir>/test/unit/**/Plugin.spec.js"],
3030

3131
// The test environment that will be used for testing
3232
testEnvironment: "node",

jest.e2e.config.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ module.exports = {
55
// Automatically clear mock calls and instances between every test
66
clearMocks: true,
77

8-
testMatch: ["**/test/e2e/**/?(*.)+(spec|test).js?(x)"],
8+
testMatch: ["<rootDir>/test/e2e/**/*.spec.js"],
9+
// testMatch: ["<rootDir>/test/e2e/**/alerts-api.spec.js"],
910

1011
// Indicates whether the coverage information should be collected while executing the test
1112
collectCoverage: false,

src/Alerts.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ class AlertsApi {
2020
constructor(core) {
2121
this._core = core;
2222
this._tracer = core.tracer;
23-
this._alerts = this._core.alerts;
2423
this._router = express.Router();
2524
this._router.get("/", this.getCollection.bind(this));
2625
this._router.get("/:id", this.getModel.bind(this));
@@ -42,7 +41,7 @@ class AlertsApi {
4241
}
4342

4443
_parseCollection() {
45-
return this._alerts.map(this._parseModel);
44+
return this._core.alerts.map(this._parseModel);
4645
}
4746

4847
getCollection(req, res) {
@@ -54,7 +53,7 @@ class AlertsApi {
5453
getModel(req, res, next) {
5554
const id = req.params.id;
5655
this._tracer.verbose(`${PLUGIN_NAME}: Sending alert ${id} | ${req.id}`);
57-
const foundAlert = this._alerts.find((alert) => alert.context === id);
56+
const foundAlert = this._core.alerts.find((alert) => alert.context === id);
5857
if (foundAlert) {
5958
res.status(200);
6059
res.send(this._parseModel(foundAlert));

src/Plugin.js

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -69,12 +69,23 @@ class Plugin {
6969

7070
async init() {
7171
await this._deprecatedApi.init();
72-
this._core.onChangeSettings(this._onChangeSettings);
7372
this._initRouter();
73+
}
74+
75+
start() {
76+
this._stopListeningOnChangeSettings = this._core.onChangeSettings(this._onChangeSettings);
7477
this._addDeprecatedRouter();
7578
this._addRouter();
7679
}
7780

81+
stop() {
82+
if (this._stopListeningOnChangeSettings) {
83+
this._stopListeningOnChangeSettings();
84+
}
85+
this._removeDeprecatedRouter();
86+
this._removeRouter();
87+
}
88+
7889
_initRouter() {
7990
this._router = express.Router();
8091
this._router.use(SETTINGS, this._settingsApi.router);
@@ -85,13 +96,7 @@ class Plugin {
8596
}
8697

8798
_addDeprecatedRouter() {
88-
if (
89-
this._settings.get(ADMIN_API_DEPRECATED_PATHS_OPTION) === false &&
90-
this._addedDeprecatedRouter
91-
) {
92-
this._core.removeRouter(DEPRECATED_API_PATH, this._deprecatedApi.router);
93-
this._addedDeprecatedRouter = false;
94-
}
99+
this._removeDeprecatedRouter();
95100
if (
96101
this._settings.get(ADMIN_API_DEPRECATED_PATHS_OPTION) === true &&
97102
!this._addedDeprecatedRouter
@@ -101,12 +106,24 @@ class Plugin {
101106
}
102107
}
103108

109+
_removeDeprecatedRouter() {
110+
if (this._addedDeprecatedRouter) {
111+
this._core.removeRouter(DEPRECATED_API_PATH, this._deprecatedApi.router);
112+
this._addedDeprecatedRouter = false;
113+
}
114+
}
115+
104116
_addRouter() {
105-
if (this._previousRoutersPath) {
106-
this._core.removeRouter(this._previousRoutersPath, this._router);
117+
this._removeRouter();
118+
this._routersPath = this._settings.get(ADMIN_API_PATH_OPTION);
119+
this._core.addRouter(this._routersPath, this._router);
120+
}
121+
122+
_removeRouter() {
123+
if (this._routersPath) {
124+
this._core.removeRouter(this._routersPath, this._router);
125+
this._routersPath = null;
107126
}
108-
this._previousRoutersPath = this._settings.get(ADMIN_API_PATH_OPTION);
109-
this._core.addRouter(this._previousRoutersPath, this._router);
110127
}
111128

112129
_onChangeSettings(newSettings) {

test/e2e/alerts-api.spec.js

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*
2+
Copyright 2020 Javier Brea
3+
4+
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
5+
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
8+
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
9+
*/
10+
const path = require("path");
11+
const fsExtra = require("fs-extra");
12+
13+
const { CliRunner, request, fixturesFolder, wait } = require("./support/utils");
14+
15+
describe("alerts api", () => {
16+
let cli;
17+
beforeAll(async () => {
18+
fsExtra.removeSync(fixturesFolder("files-watch"));
19+
fsExtra.copySync(fixturesFolder("web-tutorial"), fixturesFolder("files-watch"));
20+
cli = new CliRunner(["node", "start.js", "--path=files-watch", "--behavior=foo"], {
21+
cwd: path.resolve(__dirname, "fixtures"),
22+
});
23+
await wait(1000);
24+
});
25+
26+
afterAll(async () => {
27+
await cli.kill();
28+
});
29+
30+
describe("when started", () => {
31+
it("should return behavior not found alert", async () => {
32+
const response = await request("/admin/alerts");
33+
expect(response.length).toEqual(1);
34+
});
35+
36+
it("should return specific alert when requested by id", async () => {
37+
const response = await request("/admin/alerts/mocks%3Abehaviors%3Acurrent");
38+
expect(response).toEqual({
39+
id: "mocks:behaviors:current",
40+
context: "mocks:behaviors:current",
41+
message: 'Defined behavior "foo" was not found. The first one found was used instead',
42+
error: null,
43+
});
44+
});
45+
46+
it("should serve users collection mock under the /api/users path", async () => {
47+
const users = await request("/api/users");
48+
expect(users).toEqual([
49+
{ id: 1, name: "John Doe" },
50+
{ id: 2, name: "Jane Doe" },
51+
]);
52+
});
53+
});
54+
55+
describe("when behavior is modified", () => {
56+
beforeAll(async () => {
57+
await request("/admin/settings", {
58+
method: "PATCH",
59+
body: {
60+
behavior: "dynamic",
61+
},
62+
});
63+
await wait();
64+
}, 10000);
65+
66+
it("should return no alerts", async () => {
67+
const response = await request("/admin/alerts");
68+
expect(response.length).toEqual(0);
69+
});
70+
});
71+
72+
describe("when files contain an error", () => {
73+
beforeAll(async () => {
74+
fsExtra.copySync(fixturesFolder("files-error"), fixturesFolder("files-watch"));
75+
await wait(6000);
76+
}, 10000);
77+
78+
it("should return one alert", async () => {
79+
const response = await request("/admin/alerts");
80+
expect(response.length).toEqual(1);
81+
});
82+
83+
it("should return specific alert when requested by id", async () => {
84+
const response = await request(
85+
"/admin/alerts/plugins%3A%40mocks-server%2Fcore%2Fplugin-files-loader%3Aload"
86+
);
87+
expect(response.id).toEqual("plugins:@mocks-server/core/plugin-files-loader:load");
88+
expect(response.message).toEqual(expect.stringContaining("test/e2e/fixtures/files-watch"));
89+
expect(response.error.name).toEqual("ReferenceError");
90+
expect(response.error.message).toEqual("FOO is not defined");
91+
expect(response.error.stack).toEqual(
92+
expect.stringContaining("test/e2e/fixtures/files-watch/fixtures/users.js:1:18")
93+
);
94+
});
95+
});
96+
97+
describe("when files error is fixed", () => {
98+
beforeAll(async () => {
99+
fsExtra.copySync(fixturesFolder("web-tutorial"), fixturesFolder("files-watch"));
100+
await wait(6000);
101+
}, 10000);
102+
103+
it("should return no alerts", async () => {
104+
const response = await request("/admin/alerts");
105+
expect(response.length).toEqual(0);
106+
});
107+
108+
it("should serve users collection mock under the /api/users path", async () => {
109+
const users = await request("/api/users");
110+
expect(users).toEqual([
111+
{ id: 1, name: "John Doe" },
112+
{ id: 2, name: "Jane Doe" },
113+
]);
114+
});
115+
});
116+
});

test/e2e/fixtures-api.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Unless required by applicable law or agreed to in writing, software distributed
1010

1111
const { startServer, stopServer, request } = require("./support/utils");
1212

13-
describe("fixtures api", () => {
13+
describe("alerts api", () => {
1414
let server;
1515
beforeAll(async () => {
1616
server = await startServer("web-tutorial");
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
//eslint-disable-next-line
2+
module.exports = FOO;

test/unit/src/Plugin.spec.js

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,39 +91,80 @@ describe("Plugin", () => {
9191
await plugin.init();
9292
expect(deprecatedApiInstance.init.callCount).toEqual(1);
9393
});
94+
});
9495

96+
describe("when started", () => {
9597
it("should add deprecated router if adminApiDeprecatedPaths setting returns true", async () => {
9698
coreInstance.settings.get.withArgs("adminApiDeprecatedPaths").returns(true);
9799
await plugin.init();
100+
plugin.start();
98101
expect(coreInstance.addRouter.callCount).toEqual(2);
99102
});
100103

101104
it("should not add deprecated router if adminApiDeprecatedPaths setting returns false", async () => {
102105
coreInstance.settings.get.withArgs("adminApiDeprecatedPaths").returns(false);
103106
await plugin.init();
107+
plugin.start();
104108
expect(coreInstance.addRouter.callCount).toEqual(1);
105109
});
106110

107111
it("should register Express router into the core under the path returned by settings", async () => {
108112
coreInstance.settings.get.withArgs("adminApiPath").returns("/foo");
109113
await plugin.init();
114+
plugin.start();
110115
expect(coreInstance.addRouter.getCall(0).args).toEqual(["/foo", plugin._router]);
111116
});
112117
});
113118

119+
describe("when stopped", () => {
120+
it("should remove deprecated router if it was started", async () => {
121+
expect.assertions(2);
122+
coreInstance.settings.get.withArgs("adminApiDeprecatedPaths").returns(true);
123+
await plugin.init();
124+
plugin.start();
125+
plugin.stop();
126+
expect(coreInstance.removeRouter.callCount).toEqual(1);
127+
expect(coreInstance.removeRouter.getCall(0).args[0]).toEqual("/mocks");
128+
});
129+
130+
it("should remove router if it was started", async () => {
131+
expect.assertions(2);
132+
coreInstance.settings.get.withArgs("adminApiPath").returns("/admin");
133+
await plugin.init();
134+
plugin.start();
135+
plugin.stop();
136+
expect(coreInstance.removeRouter.callCount).toEqual(1);
137+
expect(coreInstance.removeRouter.getCall(0).args[0]).toEqual("/admin");
138+
});
139+
140+
it("should stop listening to onChangeSettings events", async () => {
141+
const removeListenerSpy = sandbox.spy();
142+
coreInstance.onChangeSettings.returns(removeListenerSpy);
143+
coreInstance.settings.get.withArgs("adminApiPath").returns("/admin");
144+
await plugin.init();
145+
plugin.start();
146+
plugin.stop();
147+
expect(removeListenerSpy.callCount).toEqual(1);
148+
});
149+
});
150+
114151
describe("when settings change", () => {
115-
it("should not register deprecated router again if it is enabled and was already registered", async () => {
152+
it("should remove deprecated router and add it again if it is enabled and was already registered", async () => {
153+
expect.assertions(2);
116154
coreInstance.settings.get.withArgs("adminApiDeprecatedPaths").returns(true);
117155
await plugin.init();
156+
plugin.start();
118157
coreInstance.onChangeSettings.getCall(0).args[0]({
119158
adminApiDeprecatedPaths: true,
120159
});
121-
expect(coreInstance.addRouter.callCount).toEqual(2);
160+
expect(coreInstance.removeRouter.callCount).toEqual(1);
161+
expect(coreInstance.addRouter.callCount).toEqual(3);
122162
});
123163

124164
it("should remove deprecated router if it is disabled and was already enabled", async () => {
125165
coreInstance.settings.get.withArgs("adminApiDeprecatedPaths").returns(true);
126166
await plugin.init();
167+
plugin.start();
127168
coreInstance.settings.get.withArgs("adminApiDeprecatedPaths").returns(false);
128169
coreInstance.onChangeSettings.getCall(0).args[0]({
129170
adminApiDeprecatedPaths: false,
@@ -134,6 +175,7 @@ describe("Plugin", () => {
134175
it("should not remove deprecated router if it was not added", async () => {
135176
coreInstance.settings.get.withArgs("adminApiDeprecatedPaths").returns(false);
136177
await plugin.init();
178+
plugin.start();
137179
coreInstance.settings.get.withArgs("adminApiDeprecatedPaths").returns(false);
138180
coreInstance.onChangeSettings.getCall(0).args[0]({
139181
adminApiDeprecatedPaths: false,
@@ -145,6 +187,7 @@ describe("Plugin", () => {
145187
expect.assertions(3);
146188
coreInstance.settings.get.withArgs("adminApiPath").returns("/foo");
147189
await plugin.init();
190+
plugin.start();
148191
coreInstance.settings.get.withArgs("adminApiPath").returns("/foo2");
149192
coreInstance.onChangeSettings.getCall(0).args[0]({
150193
adminApiPath: "/foo2",

0 commit comments

Comments
 (0)