@adobe/ccweb-add-on-ssl 2.4.1 → 3.0.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 (57) hide show
  1. package/.mocharc.json +9 -2
  2. package/README.md +5 -4
  3. package/bin/run.js +3 -2
  4. package/dist/AnalyticsMarkers.d.ts +5 -2
  5. package/dist/AnalyticsMarkers.d.ts.map +1 -1
  6. package/dist/AnalyticsMarkers.js +3 -0
  7. package/dist/app/CommandExecutor.d.ts +1 -1
  8. package/dist/app/CommandExecutor.d.ts.map +1 -1
  9. package/dist/app/PurgeCommandExecutor.d.ts +53 -0
  10. package/dist/app/PurgeCommandExecutor.d.ts.map +1 -0
  11. package/dist/app/PurgeCommandExecutor.js +125 -0
  12. package/dist/app/SSLReader.d.ts +2 -1
  13. package/dist/app/SSLReader.d.ts.map +1 -1
  14. package/dist/app/SetupCommandExecutor.d.ts.map +1 -1
  15. package/dist/app/SetupCommandExecutor.js +10 -7
  16. package/dist/app/WxpSSLReader.d.ts +8 -1
  17. package/dist/app/WxpSSLReader.d.ts.map +1 -1
  18. package/dist/app/WxpSSLReader.js +59 -5
  19. package/dist/app/index.d.ts +2 -1
  20. package/dist/app/index.d.ts.map +1 -1
  21. package/dist/app/index.js +2 -1
  22. package/dist/commands/purge.d.ts +41 -0
  23. package/dist/commands/purge.d.ts.map +1 -0
  24. package/dist/commands/purge.js +56 -0
  25. package/dist/commands/setup.d.ts +5 -10
  26. package/dist/commands/setup.d.ts.map +1 -1
  27. package/dist/commands/setup.js +5 -32
  28. package/dist/config/inversify.config.d.ts +2 -1
  29. package/dist/config/inversify.config.d.ts.map +1 -1
  30. package/dist/config/inversify.config.js +6 -1
  31. package/dist/config/inversify.types.d.ts.map +1 -1
  32. package/dist/index.d.ts +1 -0
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +1 -0
  35. package/dist/models/Types.d.ts +0 -1
  36. package/dist/models/Types.d.ts.map +1 -1
  37. package/dist/tsconfig.tsbuildinfo +1 -1
  38. package/package.json +14 -12
  39. package/src/AnalyticsMarkers.ts +5 -2
  40. package/src/app/CommandExecutor.ts +1 -1
  41. package/src/app/PurgeCommandExecutor.ts +144 -0
  42. package/src/app/SSLReader.ts +2 -1
  43. package/src/app/SetupCommandExecutor.ts +10 -7
  44. package/src/app/WxpSSLReader.ts +71 -5
  45. package/src/app/index.ts +2 -1
  46. package/src/commands/purge.ts +72 -0
  47. package/src/commands/setup.ts +12 -40
  48. package/src/config/inversify.config.ts +9 -2
  49. package/src/config/inversify.types.ts +5 -1
  50. package/src/index.ts +2 -0
  51. package/src/test/app/PurgeCommandExecutor.spec.ts +195 -0
  52. package/src/test/app/SetupCommandExecutor.spec.ts +141 -34
  53. package/src/test/app/WxpSSLReader.spec.ts +107 -33
  54. package/src/test/commands/command.spec.ts +108 -0
  55. package/src/test/commands/purge.spec.ts +132 -0
  56. package/src/test/commands/setup.spec.ts +28 -29
  57. package/tsconfig.json +3 -1
@@ -22,10 +22,11 @@
22
22
  * SOFTWARE.
23
23
  ********************************************************************************/
24
24
 
25
- import type { Preferences, Logger } from "@adobe/ccweb-add-on-core";
25
+ import type { Logger, Preferences } from "@adobe/ccweb-add-on-core";
26
26
  import { ADD_ON_PREFERENCES_FILE, PreferenceJson } from "@adobe/ccweb-add-on-core";
27
- import { assert } from "chai";
28
27
  import devcert from "@adobe/ccweb-add-on-devcert";
28
+ import chai, { assert, expect } from "chai";
29
+ import chaiAsPromised from "chai-as-promised";
29
30
  import type { Stats } from "fs-extra";
30
31
  import fs from "fs-extra";
31
32
  import "mocha";
@@ -35,6 +36,8 @@ import { stubInterface } from "ts-sinon";
35
36
  import type { SSLReader } from "../../app/index.js";
36
37
  import { WxpSSLReader } from "../../app/index.js";
37
38
 
39
+ chai.use(chaiAsPromised);
40
+
38
41
  describe("WxpSSLReader", () => {
39
42
  let sandbox: sinon.SinonSandbox;
40
43
 
@@ -42,11 +45,14 @@ describe("WxpSSLReader", () => {
42
45
  let logger: StubbedInstance<Logger>;
43
46
  let sslReader: SSLReader;
44
47
 
48
+ const hostname = "localhost.adobe.com";
49
+ const port = 5241;
50
+
45
51
  beforeEach(() => {
46
52
  sandbox = sinon.createSandbox();
47
53
 
48
- preferences = stubInterface<Preferences>();
49
- logger = stubInterface<Logger>();
54
+ preferences = stubInterface();
55
+ logger = stubInterface();
50
56
  sslReader = new WxpSSLReader(preferences, logger);
51
57
  });
52
58
 
@@ -56,7 +62,6 @@ describe("WxpSSLReader", () => {
56
62
 
57
63
  describe("isCustomSSL", () => {
58
64
  it(`should return false if SSL settings are not present in '${ADD_ON_PREFERENCES_FILE}' for the provided hostname.`, async () => {
59
- const hostname = "localhost";
60
65
  const userPreference = new PreferenceJson({});
61
66
 
62
67
  preferences.get.returns(userPreference);
@@ -67,7 +72,6 @@ describe("WxpSSLReader", () => {
67
72
  });
68
73
 
69
74
  it(`should return true if SSL settings are present in '${ADD_ON_PREFERENCES_FILE}' for the provided hostname.`, async () => {
70
- const hostname = "localhost";
71
75
  const userPreference = new PreferenceJson({
72
76
  ssl: {
73
77
  [hostname]: {
@@ -87,8 +91,6 @@ describe("WxpSSLReader", () => {
87
91
 
88
92
  describe("isWxpSSL", () => {
89
93
  it("should return false if devcert SSL certificate is not present for the provided hostname.", async () => {
90
- const hostname = "localhost";
91
-
92
94
  sandbox.stub(devcert, "hasCertificateFor").withArgs(hostname).returns(false);
93
95
 
94
96
  const isWxpSSL = sslReader.isWxpSSL(hostname);
@@ -97,8 +99,6 @@ describe("WxpSSLReader", () => {
97
99
  });
98
100
 
99
101
  it("should return false if devcert SSL certificate is not present for the provided hostname.", async () => {
100
- const hostname = "localhost";
101
-
102
102
  sandbox.stub(devcert, "hasCertificateFor").withArgs(hostname).returns(false);
103
103
 
104
104
  const isWxpSSL = sslReader.isWxpSSL(hostname);
@@ -109,8 +109,6 @@ describe("WxpSSLReader", () => {
109
109
 
110
110
  describe("read", async () => {
111
111
  it("should log error and exit when SSL certificate path is invalid for manually set up SSL.", async () => {
112
- const hostname = "localhost";
113
-
114
112
  const certificatePath = "/some-directory/localhost/ssl/";
115
113
  const keyPath = "/some-directory/localhost/ssl/private-key.key";
116
114
  const userPreference = new PreferenceJson({
@@ -128,18 +126,15 @@ describe("WxpSSLReader", () => {
128
126
  lstatStub.withArgs(certificatePath).returns(certificateStats);
129
127
  lstatStub.withArgs(keyPath).returns(keyStats);
130
128
 
131
- logger.error.returns();
132
129
  const exitStub = sandbox.stub(process, "exit");
133
130
 
134
- await sslReader.read(hostname);
131
+ await sslReader.read(hostname, port);
135
132
 
136
133
  assert.equal(logger.error.calledOnceWith("Invalid SSL certificate file path."), true);
137
134
  assert.equal(exitStub.calledWith(1), true);
138
135
  });
139
136
 
140
137
  it("should log error and exit when SSL key path is invalid for manually set up SSL.", async () => {
141
- const hostname = "localhost";
142
-
143
138
  const certificatePath = "/some-directory/localhost/ssl/certificate.cert";
144
139
  const keyPath = "/some-directory/localhost/ssl/";
145
140
  const userPreference = new PreferenceJson({
@@ -157,18 +152,15 @@ describe("WxpSSLReader", () => {
157
152
  lstatStub.withArgs(certificatePath).returns(certificateStats);
158
153
  lstatStub.withArgs(keyPath).returns(keyStats);
159
154
 
160
- logger.error.returns();
161
155
  const exitStub = sandbox.stub(process, "exit");
162
156
 
163
- await sslReader.read(hostname);
157
+ await sslReader.read(hostname, port);
164
158
 
165
159
  assert.equal(logger.error.calledOnceWith("Invalid SSL key file path."), true);
166
160
  assert.equal(exitStub.calledWith(1), true);
167
161
  });
168
162
 
169
- it("should read and return SSL certificate and key data for manually set up SSL.", async () => {
170
- const hostname = "localhost";
171
-
163
+ it("should read and return SSL data for manually set up SSL.", async () => {
172
164
  const certificatePath = "/some-directory/localhost/ssl/certificate.cert";
173
165
  const keyPath = "/some-directory/localhost/ssl/private-key.key";
174
166
  const userPreference = new PreferenceJson({
@@ -191,19 +183,97 @@ describe("WxpSSLReader", () => {
191
183
  readFileStub.withArgs(certificatePath).returns(certificateData);
192
184
  readFileStub.withArgs(keyPath).returns(keyData);
193
185
 
194
- const sslData = await sslReader.read(hostname);
186
+ const sslData = await sslReader.read(hostname, port);
187
+
188
+ assert.equal(sslData.cert, certificateData);
189
+ assert.equal(sslData.key, keyData);
190
+
191
+ assert.equal(
192
+ logger.warning.getCall(0).calledWith("Could not determine the expiry of your SSL certificate."),
193
+ true
194
+ );
195
+ assert.equal(
196
+ logger.warning
197
+ .getCall(1)
198
+ .calledWith("If you are unable to sideload your add-on, please check the validity of:"),
199
+ true
200
+ );
201
+ assert.equal(
202
+ logger.warning.getCall(2).calledWith(`https://${hostname}:${port} certificate in your browser.`),
203
+ true
204
+ );
205
+ });
206
+
207
+ it("should read and return SSL data for automatically set up, valid SSL.", async () => {
208
+ const userPreference = new PreferenceJson({});
209
+ preferences.get.returns(userPreference);
210
+
211
+ sandbox.stub(devcert, "hasCertificateFor").withArgs(hostname).returns(true);
212
+ sandbox.stub(devcert, "caExpiryInDays").returns(50);
213
+ sandbox.stub(devcert, "certificateExpiryInDays").returns(100);
214
+
215
+ const certificateData = <Buffer>{};
216
+ const keyData = <Buffer>{};
217
+
218
+ const certificateForStub = sandbox.stub(devcert, "certificateFor");
219
+ // @ts-ignore -- IReturnData mock response
220
+ certificateForStub.withArgs(hostname).returns({ cert: certificateData, key: keyData });
221
+
222
+ const sslData = await sslReader.read(hostname, port);
195
223
 
196
224
  assert.equal(sslData.cert, certificateData);
197
225
  assert.equal(sslData.key, keyData);
198
226
  });
199
227
 
200
- it("should read and return SSL certificate and key data for automatically set up SSL.", async () => {
201
- const hostname = "localhost";
228
+ it("should log error and exit when automatically set up SSL has expired.", async () => {
229
+ const userPreference = new PreferenceJson({});
230
+ preferences.get.returns(userPreference);
231
+
232
+ sandbox.stub(devcert, "hasCertificateFor").withArgs(hostname).returns(true);
233
+ sandbox.stub(devcert, "caExpiryInDays").returns(0);
234
+ sandbox.stub(devcert, "certificateExpiryInDays").returns(100);
235
+
236
+ const exitStub = sandbox.stub(process, "exit");
237
+ exitStub.throws();
238
+
239
+ await expect(sslReader.read(hostname, port)).to.be.rejectedWith();
202
240
 
241
+ assert.equal(
242
+ logger.error.getCall(0).calledWith("Could not locate a valid SSL certificate to host the add-on."),
243
+ true
244
+ );
245
+ assert.equal(logger.error.getCall(1).calledWith("The SSL certificate has expired."), true);
246
+ assert.equal(
247
+ logger.warning
248
+ .getCall(0)
249
+ .calledWith("To re-create the SSL certificate, you may run:", { prefix: "\n" }),
250
+ true
251
+ );
252
+ assert.equal(
253
+ logger.information
254
+ .getCall(0)
255
+ .calledWith("npx @adobe/ccweb-add-on-ssl setup --hostname [hostname]", { prefix: " " }),
256
+ true
257
+ );
258
+ assert.equal(logger.warning.getCall(1).calledWith("Example:", { prefix: "\n" }), true);
259
+ assert.equal(
260
+ logger.information.getCall(1).calledWith("npx @adobe/ccweb-add-on-ssl setup --hostname localhost", {
261
+ prefix: " ",
262
+ postfix: "\n"
263
+ }),
264
+ true
265
+ );
266
+
267
+ assert.equal(exitStub.calledWith(1), true);
268
+ });
269
+
270
+ it("should read and return SSL data for automatically set up, valid SSL nearing expiry.", async () => {
203
271
  const userPreference = new PreferenceJson({});
204
272
  preferences.get.returns(userPreference);
205
273
 
206
274
  sandbox.stub(devcert, "hasCertificateFor").withArgs(hostname).returns(true);
275
+ sandbox.stub(devcert, "caExpiryInDays").returns(5);
276
+ sandbox.stub(devcert, "certificateExpiryInDays").returns(100);
207
277
 
208
278
  const certificateData = <Buffer>{};
209
279
  const keyData = <Buffer>{};
@@ -212,30 +282,34 @@ describe("WxpSSLReader", () => {
212
282
  // @ts-ignore -- IReturnData mock response
213
283
  certificateForStub.withArgs(hostname).returns({ cert: certificateData, key: keyData });
214
284
 
215
- const sslData = await sslReader.read(hostname);
285
+ const sslData = await sslReader.read(hostname, port);
286
+
287
+ assert.equal(logger.warning.calledWith("Your SSL certificate will expire in 5 days."), true);
216
288
 
217
289
  assert.equal(sslData.cert, certificateData);
218
290
  assert.equal(sslData.key, keyData);
219
291
  });
220
292
 
221
293
  it("should log error and exit when SSL is not set up.", async () => {
222
- const hostname = "localhost";
223
-
224
294
  const userPreference = new PreferenceJson({});
225
295
  preferences.get.returns(userPreference);
226
296
 
227
297
  sandbox.stub(devcert, "hasCertificateFor").returns(false);
228
298
 
229
- logger.error.returns();
230
299
  const exitStub = sandbox.stub(process, "exit");
231
300
 
232
- await sslReader.read(hostname);
301
+ await sslReader.read(hostname, port);
233
302
 
234
303
  assert.equal(
235
- logger.error.calledOnceWith(
236
- "No SSL related certificate or key files were found. Please retry after setting them up.",
237
- { postfix: "\n" }
238
- ),
304
+ logger.error.getCall(0).calledWith("Could not locate a valid SSL certificate to host the add-on."),
305
+ true
306
+ );
307
+ assert.equal(
308
+ logger.error
309
+ .getCall(1)
310
+ .calledWith(
311
+ "If you had previously set it up, it may have been invalidated due to a version upgrade."
312
+ ),
239
313
  true
240
314
  );
241
315
  assert.equal(exitStub.calledWith(1), true);
@@ -0,0 +1,108 @@
1
+ /********************************************************************************
2
+ * MIT License
3
+
4
+ * © Copyright 2025 Adobe. All rights reserved.
5
+
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in all
14
+ * copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ * SOFTWARE.
23
+ ********************************************************************************/
24
+
25
+ import type { AnalyticsConsent, AnalyticsService } from "@adobe/ccweb-add-on-analytics";
26
+ import { ITypes as IAnalyticsTypes } from "@adobe/ccweb-add-on-analytics";
27
+ import { runCommand } from "@oclif/test";
28
+ import { assert } from "chai";
29
+ import "mocha";
30
+ import sinon from "sinon";
31
+ import type { StubbedInstance } from "ts-sinon";
32
+ import { stubInterface } from "ts-sinon";
33
+ import type { PurgeCommandExecutor, SetupCommandExecutor } from "../../app/index.js";
34
+ import { IContainer, ITypes } from "../../config/index.js";
35
+ import { SetupCommandOptions } from "../../models/SetupCommandOptions.js";
36
+ import type { SetupCommandValidator } from "../../validators/SetupCommandValidator.js";
37
+
38
+ describe("ccweb-add-on-ssl", () => {
39
+ let sandbox: sinon.SinonSandbox;
40
+
41
+ let container: sinon.SinonStub;
42
+ let namedContainer: sinon.SinonStub;
43
+
44
+ let analyticsConsent: StubbedInstance<AnalyticsConsent>;
45
+ let analyticsService: StubbedInstance<AnalyticsService>;
46
+
47
+ beforeEach(() => {
48
+ sandbox = sinon.createSandbox();
49
+
50
+ namedContainer = sandbox.stub(IContainer, "getNamed");
51
+
52
+ analyticsConsent = stubInterface();
53
+ analyticsService = stubInterface();
54
+
55
+ container = sandbox.stub(IContainer, "get");
56
+ container.withArgs(IAnalyticsTypes.AnalyticsConsent).returns(analyticsConsent);
57
+ container.withArgs(IAnalyticsTypes.AnalyticsService).returns(analyticsService);
58
+ });
59
+
60
+ afterEach(() => {
61
+ sandbox.restore();
62
+ });
63
+
64
+ describe("setup", () => {
65
+ let commandExecutor: StubbedInstance<SetupCommandExecutor>;
66
+ let commandValidator: StubbedInstance<SetupCommandValidator>;
67
+
68
+ beforeEach(() => {
69
+ commandExecutor = stubInterface();
70
+ commandExecutor.execute.resolves();
71
+
72
+ commandValidator = stubInterface();
73
+ commandValidator.validate.resolves();
74
+
75
+ namedContainer.withArgs(ITypes.CommandValidator, "setup").returns(commandValidator);
76
+ namedContainer.withArgs(ITypes.CommandExecutor, "setup").returns(commandExecutor);
77
+ });
78
+
79
+ it("should execute succesfully when correct parameters are passed.", async () => {
80
+ analyticsConsent.set.resolves();
81
+
82
+ await runCommand(["setup", "--hostname=localhost", "--analytics=on", "--verbose"], { root: "." });
83
+
84
+ const options = new SetupCommandOptions("localhost", false, true);
85
+ assert.equal(commandValidator.validate.calledOnceWith(options), true);
86
+ assert.equal(commandExecutor.execute.calledOnceWith(options), true);
87
+ });
88
+ });
89
+
90
+ describe("purge", () => {
91
+ let commandExecutor: StubbedInstance<PurgeCommandExecutor>;
92
+
93
+ beforeEach(() => {
94
+ commandExecutor = stubInterface();
95
+ commandExecutor.execute.resolves();
96
+
97
+ namedContainer.withArgs(ITypes.CommandExecutor, "purge").returns(commandExecutor);
98
+ });
99
+
100
+ it("should execute succesfully when correct parameters are passed.", async () => {
101
+ analyticsConsent.set.resolves();
102
+
103
+ await runCommand(["purge", "--analytics=on", "--verbose"], { root: "." });
104
+
105
+ assert.equal(commandExecutor.execute.calledOnce, true);
106
+ });
107
+ });
108
+ });
@@ -0,0 +1,132 @@
1
+ /********************************************************************************
2
+ * MIT License
3
+
4
+ * © Copyright 2023 Adobe. All rights reserved.
5
+
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in all
14
+ * copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ * SOFTWARE.
23
+ ********************************************************************************/
24
+
25
+ import type { AnalyticsConsent, AnalyticsService } from "@adobe/ccweb-add-on-analytics";
26
+ import { ITypes as IAnalyticsTypes } from "@adobe/ccweb-add-on-analytics";
27
+ import { Config } from "@oclif/core";
28
+ import chai, { assert, expect } from "chai";
29
+ import chaiAsPromised from "chai-as-promised";
30
+ import "mocha";
31
+ import sinon from "sinon";
32
+ import type { StubbedInstance } from "ts-sinon";
33
+ import { stubInterface } from "ts-sinon";
34
+ import { AnalyticsErrorMarkers } from "../../AnalyticsMarkers.js";
35
+ import { PurgeCommandExecutor } from "../../app/index.js";
36
+ import { Purge } from "../../commands/purge.js";
37
+ import { IContainer, ITypes } from "../../config/index.js";
38
+ import { PROGRAM_NAME } from "../../constants.js";
39
+
40
+ chai.use(chaiAsPromised);
41
+
42
+ describe("Purge", () => {
43
+ let sandbox: sinon.SinonSandbox;
44
+
45
+ let container: sinon.SinonStub;
46
+ let namedContainer: sinon.SinonStub;
47
+
48
+ let commandExecutor: StubbedInstance<PurgeCommandExecutor>;
49
+
50
+ let analyticsConsent: StubbedInstance<AnalyticsConsent>;
51
+ let analyticsService: StubbedInstance<AnalyticsService>;
52
+
53
+ beforeEach(() => {
54
+ sandbox = sinon.createSandbox();
55
+
56
+ commandExecutor = stubInterface();
57
+ commandExecutor.execute.resolves();
58
+
59
+ namedContainer = sandbox.stub(IContainer, "getNamed");
60
+
61
+ namedContainer.withArgs(ITypes.CommandExecutor, "purge").returns(commandExecutor);
62
+
63
+ analyticsConsent = stubInterface();
64
+ analyticsService = stubInterface();
65
+
66
+ container = sandbox.stub(IContainer, "get");
67
+ container.withArgs(IAnalyticsTypes.AnalyticsConsent).returns(analyticsConsent);
68
+ container.withArgs(IAnalyticsTypes.AnalyticsService).returns(analyticsService);
69
+ });
70
+
71
+ afterEach(() => {
72
+ sandbox.restore();
73
+ });
74
+
75
+ describe("run", () => {
76
+ it("should execute succesfully when correct parameters are passed without analytics.", async () => {
77
+ analyticsConsent.get.resolves(true);
78
+
79
+ const hostname = "localhost";
80
+ const purge = new Purge(["--verbose"], new Config({ name: PROGRAM_NAME, version: "1.0.0", root: "." }));
81
+ sandbox
82
+ // @ts-ignore - Sidestep `this.parse()` error when calling `run()` directly.
83
+ .stub(purge, "parse")
84
+ .resolves({
85
+ flags: { hostname, useExisting: false, analytics: undefined, verbose: false }
86
+ });
87
+
88
+ await purge.run();
89
+
90
+ assert.equal(analyticsConsent.get.callCount, 1);
91
+
92
+ assert.equal(commandExecutor.execute.calledOnce, true);
93
+ });
94
+
95
+ it("should execute succesfully when correct parameters are passed with analytics.", async () => {
96
+ analyticsConsent.get.resolves(true);
97
+ analyticsConsent.set.withArgs(false).resolves();
98
+
99
+ const hostname = "localhost";
100
+ const purge = new Purge(
101
+ ["--analytics", "off", "--verbose"],
102
+ new Config({ name: PROGRAM_NAME, version: "1.0.0", root: "." })
103
+ );
104
+ sandbox
105
+ // @ts-ignore - Sidestep `this.parse()` error when calling `run()` directly.
106
+ .stub(purge, "parse")
107
+ .resolves({
108
+ flags: { hostname, useExisting: false, analytics: "off", verbose: true }
109
+ });
110
+
111
+ await purge.run();
112
+
113
+ assert.equal(analyticsConsent.set.calledOnceWith(false), true);
114
+
115
+ assert.equal(commandExecutor.execute.calledOnce, true);
116
+ });
117
+ });
118
+
119
+ describe("catch", () => {
120
+ it("should fail for any errors in command execution.", async () => {
121
+ const purge = new Purge([], new Config({ name: PROGRAM_NAME, version: "1.0.0", root: "." }));
122
+
123
+ const error = new Error("Something went wrong.");
124
+ await expect(purge.catch(error)).to.be.rejectedWith(error);
125
+
126
+ assert.equal(
127
+ analyticsService.postEvent.calledOnceWith(AnalyticsErrorMarkers.ERROR_SSL_PURGE, error.message, false),
128
+ true
129
+ );
130
+ });
131
+ });
132
+ });
@@ -28,30 +28,27 @@ import { Config } from "@oclif/core";
28
28
  import chai, { assert, expect } from "chai";
29
29
  import chaiAsPromised from "chai-as-promised";
30
30
  import "mocha";
31
- import { createRequire } from "module";
32
31
  import sinon from "sinon";
33
32
  import type { StubbedInstance } from "ts-sinon";
34
33
  import { stubInterface } from "ts-sinon";
35
34
  import { AnalyticsErrorMarkers } from "../../AnalyticsMarkers.js";
36
- import type { CommandExecutor, SetupCommandExecutor } from "../../app/index.js";
35
+ import type { SetupCommandExecutor } from "../../app/index.js";
37
36
  import { Setup } from "../../commands/setup.js";
38
37
  import { IContainer, ITypes } from "../../config/index.js";
38
+ import { PROGRAM_NAME } from "../../constants.js";
39
39
  import { SetupCommandOptions } from "../../models/SetupCommandOptions.js";
40
- import type { CommandValidator } from "../../validators/CommandValidator.js";
41
40
  import type { SetupCommandValidator } from "../../validators/SetupCommandValidator.js";
42
41
 
43
42
  chai.use(chaiAsPromised);
44
43
 
45
- const { test } = createRequire(import.meta.url)("@oclif/test");
46
-
47
44
  describe("Setup", () => {
48
45
  let sandbox: sinon.SinonSandbox;
49
46
 
50
47
  let container: sinon.SinonStub;
51
48
  let namedContainer: sinon.SinonStub;
52
49
 
53
- let commandExecutor: StubbedInstance<CommandExecutor>;
54
- let commandValidator: StubbedInstance<CommandValidator>;
50
+ let commandExecutor: StubbedInstance<SetupCommandExecutor>;
51
+ let commandValidator: StubbedInstance<SetupCommandValidator>;
55
52
 
56
53
  let analyticsConsent: StubbedInstance<AnalyticsConsent>;
57
54
  let analyticsService: StubbedInstance<AnalyticsService>;
@@ -59,10 +56,10 @@ describe("Setup", () => {
59
56
  beforeEach(() => {
60
57
  sandbox = sinon.createSandbox();
61
58
 
62
- commandExecutor = stubInterface<SetupCommandExecutor>();
59
+ commandExecutor = stubInterface();
63
60
  commandExecutor.execute.resolves();
64
61
 
65
- commandValidator = stubInterface<SetupCommandValidator>();
62
+ commandValidator = stubInterface();
66
63
  commandValidator.validate.resolves();
67
64
 
68
65
  namedContainer = sandbox.stub(IContainer, "getNamed");
@@ -81,30 +78,27 @@ describe("Setup", () => {
81
78
  sandbox.restore();
82
79
  });
83
80
 
84
- describe("setup", () => {
85
- test.stdout()
86
- .command(["setup", "--hostname=localhost", "--analytics=on", "--verbose"])
87
- .it("should execute succesfully when correct parameters are passed.", async () => {
88
- analyticsConsent.set.resolves();
89
-
90
- const options = new SetupCommandOptions("localhost", false, true);
91
- assert.equal(commandValidator.validate.calledOnceWith(options), true);
92
- assert.equal(commandExecutor.execute.calledOnceWith(options), true);
93
- });
94
- });
95
-
96
81
  describe("run", () => {
97
82
  it("should execute succesfully when correct parameters are passed without analytics.", async () => {
98
83
  analyticsConsent.get.resolves(true);
99
84
 
100
85
  const hostname = "localhost";
101
- const setup = new Setup(["--hostname", hostname, "--verbose"], new Config({ root: "." }));
86
+ const setup = new Setup(
87
+ ["--hostname", hostname, "--verbose"],
88
+ new Config({ name: PROGRAM_NAME, version: "1.0.0", root: "." })
89
+ );
90
+ sandbox
91
+ // @ts-ignore - Sidestep `this.parse()` error when calling `run()` directly.
92
+ .stub(setup, "parse")
93
+ .resolves({
94
+ flags: { hostname, useExisting: false, analytics: undefined, verbose: false }
95
+ });
102
96
 
103
97
  await setup.run();
104
98
 
105
99
  assert.equal(analyticsConsent.get.callCount, 1);
106
100
 
107
- const options = new SetupCommandOptions(hostname, false, true);
101
+ const options = new SetupCommandOptions(hostname, false, false);
108
102
  assert.equal(commandValidator.validate.calledOnceWith(options), true);
109
103
  assert.equal(commandExecutor.execute.calledOnceWith(options), true);
110
104
  });
@@ -116,8 +110,14 @@ describe("Setup", () => {
116
110
  const hostname = "localhost";
117
111
  const setup = new Setup(
118
112
  ["--hostname", hostname, "--analytics", "off", "--verbose"],
119
- new Config({ root: "." })
113
+ new Config({ name: PROGRAM_NAME, version: "1.0.0", root: "." })
120
114
  );
115
+ sandbox
116
+ // @ts-ignore - Sidestep `this.parse()` error when calling `run()` directly.
117
+ .stub(setup, "parse")
118
+ .resolves({
119
+ flags: { hostname, useExisting: false, analytics: "off", verbose: true }
120
+ });
121
121
 
122
122
  await setup.run();
123
123
 
@@ -130,12 +130,11 @@ describe("Setup", () => {
130
130
  });
131
131
 
132
132
  describe("catch", () => {
133
- it("should fail when incorrect parameters are passed.", async () => {
134
- const setup = new Setup(["--incorrect-flag"], new Config({ root: "." }));
135
-
136
- const error = new Error("Nonexistent flag: --incorrect-flag\nSee more help with --help");
133
+ it("should fail for any errors in command execution.", async () => {
134
+ const setup = new Setup([], new Config({ name: PROGRAM_NAME, version: "1.0.0", root: "." }));
137
135
 
138
- await expect(setup.catch(error)).to.be.rejectedWith();
136
+ const error = new Error("Something went wrong.");
137
+ await expect(setup.catch(error)).to.be.rejectedWith(error);
139
138
 
140
139
  assert.equal(
141
140
  analyticsService.postEvent.calledOnceWith(AnalyticsErrorMarkers.ERROR_SSL_SETUP, error.message, false),
package/tsconfig.json CHANGED
@@ -17,11 +17,13 @@
17
17
  "outDir": "dist",
18
18
  "preserveConstEnums": true,
19
19
  "resolveJsonModule": true,
20
+ "skipLibCheck": true,
20
21
  "sourceMap": true,
21
22
  "strict": true,
22
23
  "target": "ESNext",
23
24
  "types": ["reflect-metadata"],
24
- "useUnknownInCatchVariables": false
25
+ "useUnknownInCatchVariables": false,
26
+ "isolatedDeclarations": true
25
27
  },
26
28
  "exclude": ["node_modules", "src/test/**/*"],
27
29
  "include": ["src/**/*"],