navy 6.0.0 → 7.0.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/__tests__/config-provider.js +75 -0
- package/lib/__tests__/config.js +130 -0
- package/lib/__tests__/driver-logging.js +148 -0
- package/lib/__tests__/driver.js +19 -0
- package/lib/__tests__/errors.js +49 -0
- package/lib/__tests__/http-proxy.js +214 -0
- package/lib/__tests__/index.js +25 -0
- package/lib/__tests__/service.js +16 -0
- package/lib/cli/__tests__/develop.js +239 -0
- package/lib/cli/__tests__/external-ip.js +68 -0
- package/lib/cli/__tests__/health.js +257 -0
- package/lib/cli/__tests__/https.js +210 -0
- package/lib/cli/__tests__/import.js +110 -0
- package/lib/cli/__tests__/index.js +118 -0
- package/lib/cli/__tests__/lan-ip.js +90 -0
- package/lib/cli/__tests__/launch.js +179 -0
- package/lib/cli/__tests__/live.js +155 -0
- package/lib/cli/__tests__/local-ip.js +72 -0
- package/lib/cli/__tests__/logs.js +52 -0
- package/lib/cli/__tests__/open.js +65 -0
- package/lib/cli/__tests__/program.js +472 -0
- package/lib/cli/__tests__/ps.js +345 -0
- package/lib/cli/__tests__/refresh-config.js +95 -0
- package/lib/cli/__tests__/run.js +54 -0
- package/lib/cli/__tests__/status.js +204 -0
- package/lib/cli/__tests__/updates.js +243 -0
- package/lib/cli/__tests__/wait-for-healthy.js +134 -0
- package/lib/cli/config/__tests__/index.js +275 -0
- package/lib/cli/config/__tests__/wrapper.js +53 -0
- package/lib/cli/config/index.js +19 -37
- package/lib/cli/config/wrapper.js +0 -6
- package/lib/cli/develop.js +7 -21
- package/lib/cli/doctor/__tests__/clean-compose-files.js +78 -0
- package/lib/cli/doctor/__tests__/index.js +67 -0
- package/lib/cli/doctor/__tests__/invalid-compose-config.js +103 -0
- package/lib/cli/doctor/__tests__/invalid-state.js +83 -0
- package/lib/cli/doctor/__tests__/util.js +91 -0
- package/lib/cli/doctor/clean-compose-files.js +5 -13
- package/lib/cli/doctor/index.js +0 -12
- package/lib/cli/doctor/invalid-compose-config.js +0 -4
- package/lib/cli/doctor/invalid-state.js +0 -6
- package/lib/cli/doctor/util.js +0 -10
- package/lib/cli/external-ip.js +0 -4
- package/lib/cli/health.js +0 -12
- package/lib/cli/https.js +9 -25
- package/lib/cli/import.js +0 -12
- package/lib/cli/index.js +0 -9
- package/lib/cli/lan-ip.js +2 -8
- package/lib/cli/launch.js +0 -9
- package/lib/cli/live.js +6 -16
- package/lib/cli/local-ip.js +2 -7
- package/lib/cli/logs.js +0 -3
- package/lib/cli/open.js +2 -7
- package/lib/cli/program.js +73 -101
- package/lib/cli/ps.js +1 -21
- package/lib/cli/refresh-config.js +0 -7
- package/lib/cli/run.js +0 -3
- package/lib/cli/status.js +0 -11
- package/lib/cli/updates.js +0 -22
- package/lib/cli/util/__tests__/get-or-initialise-navy.js +66 -0
- package/lib/cli/util/__tests__/import.js +123 -0
- package/lib/cli/util/__tests__/index.js +17 -0
- package/lib/cli/util/__tests__/merge-action-options.js +47 -0
- package/lib/cli/util/__tests__/reconfigure.js +78 -0
- package/lib/cli/util/get-or-initialise-navy.js +0 -7
- package/lib/cli/util/import.js +0 -9
- package/lib/cli/util/index.js +0 -2
- package/lib/cli/util/merge-action-options.js +20 -0
- package/lib/cli/util/reconfigure.js +0 -4
- package/lib/cli/wait-for-healthy.js +0 -21
- package/lib/client/registry/__tests__/get-credentials.js +62 -0
- package/lib/client/registry/__tests__/get-endpoint.js +124 -0
- package/lib/client/registry/__tests__/get-fat-manifest.js +67 -0
- package/lib/client/registry/__tests__/get-token.js +66 -0
- package/lib/client/registry/__tests__/helpers.js +26 -0
- package/lib/client/registry/get-credentials.js +3 -9
- package/lib/client/registry/get-endpoint.js +29 -63
- package/lib/client/registry/get-fat-manifest.js +2 -9
- package/lib/client/registry/get-token.js +2 -13
- package/lib/client/registry/helpers.js +0 -4
- package/lib/config-provider.js +0 -12
- package/lib/config-providers/filesystem/__tests__/index.js +176 -0
- package/lib/config-providers/filesystem/index.js +5 -23
- package/lib/config-providers/npm/__tests__/index.js +226 -0
- package/lib/config-providers/npm/__tests__/util.js +1 -2
- package/lib/config-providers/npm/index.js +12 -35
- package/lib/config-providers/npm/util.js +0 -3
- package/lib/config.js +4 -19
- package/lib/domain/__tests__/container-image.js +81 -0
- package/lib/domain/__tests__/oci-api-specification.js +23 -0
- package/lib/domain/container-image.js +8 -21
- package/lib/domain/oci-api-specification.js +3 -5
- package/lib/driver-logging.js +0 -19
- package/lib/driver.js +0 -4
- package/lib/drivers/docker-compose/__tests__/client.js +249 -0
- package/lib/drivers/docker-compose/__tests__/index.js +430 -0
- package/lib/drivers/docker-compose/client.js +0 -16
- package/lib/drivers/docker-compose/index.js +7 -49
- package/lib/errors.js +0 -10
- package/lib/http-proxy.js +28 -23
- package/lib/index.js +1 -9
- package/lib/middleware/__tests__/add-service-proxy-config.js +258 -0
- package/lib/middleware/__tests__/develop.js +120 -0
- package/lib/middleware/__tests__/helpers.js +154 -0
- package/lib/middleware/__tests__/port-override.js +125 -0
- package/lib/middleware/__tests__/set-env-vars.js +94 -0
- package/lib/middleware/__tests__/set-image.js +76 -0
- package/lib/middleware/__tests__/set-logging-driver.js +94 -0
- package/lib/middleware/__tests__/tag-override.js +92 -0
- package/lib/middleware/add-service-proxy-config.js +8 -16
- package/lib/middleware/develop.js +2 -5
- package/lib/middleware/helpers.js +6 -12
- package/lib/middleware/port-override.js +5 -8
- package/lib/middleware/set-env-vars.js +6 -6
- package/lib/middleware/set-image.js +4 -5
- package/lib/middleware/set-logging-driver.js +6 -6
- package/lib/middleware/tag-override.js +4 -6
- package/lib/navy/__tests__/default-middleware.js +40 -0
- package/lib/navy/__tests__/index.js +1612 -0
- package/lib/navy/__tests__/middleware.js +71 -0
- package/lib/navy/__tests__/plugin-interface.js +121 -0
- package/lib/navy/__tests__/state.js +103 -0
- package/lib/navy/__tests__/util.js +24 -0
- package/lib/navy/default-middleware.js +0 -10
- package/lib/navy/index.js +83 -138
- package/lib/navy/middleware.js +0 -6
- package/lib/navy/plugin-interface.js +2 -10
- package/lib/navy/state.js +12 -24
- package/lib/navy/util.js +0 -1
- package/lib/service.js +2 -3
- package/lib/util/__tests__/exec-async.js +83 -0
- package/lib/util/__tests__/external-ip.js +97 -2
- package/lib/util/__tests__/get-lan-ip.js +46 -0
- package/lib/util/__tests__/has-update.js +136 -0
- package/lib/util/__tests__/https.js +301 -0
- package/lib/util/__tests__/navyrc.js +45 -0
- package/lib/util/__tests__/service-host.js +63 -5
- package/lib/util/__tests__/table.js +44 -0
- package/lib/util/docker-client.js +2 -10
- package/lib/util/exec-async.js +0 -4
- package/lib/util/external-ip.js +8 -12
- package/lib/util/fs.js +1 -6
- package/lib/util/get-lan-ip.js +0 -5
- package/lib/util/has-update.js +2 -14
- package/lib/util/https.js +11 -55
- package/lib/util/navyrc.js +0 -5
- package/lib/util/service-host.js +0 -17
- package/lib/util/table.js +0 -6
- package/package.json +14 -13
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
var _chai = require("chai");
|
|
5
|
+
var _sinon = _interopRequireDefault(require("sinon"));
|
|
6
|
+
var _proxyquire = _interopRequireDefault(require("proxyquire"));
|
|
7
|
+
var _readline = _interopRequireDefault(require("readline"));
|
|
8
|
+
var _cliSpinners = require("cli-spinners");
|
|
9
|
+
var _stripAnsi = _interopRequireDefault(require("strip-ansi"));
|
|
10
|
+
/* eslint-env mocha */
|
|
11
|
+
|
|
12
|
+
// NOTE (potential source bug in updates.js, not locked in by these tests):
|
|
13
|
+
// The Promise.all body checks `if (!service || !service.raw || !service.raw.Image)`
|
|
14
|
+
// then immediately runs `updateStatus[service.id] = 'NO_IMAGE'`. If `service` were
|
|
15
|
+
// falsy the guard would let us through, but the very next assignment dereferences
|
|
16
|
+
// `service.id` and would throw. The `!service` part of the guard is therefore
|
|
17
|
+
// either dead defensive code or a latent bug.
|
|
18
|
+
|
|
19
|
+
describe('cli/updates', function () {
|
|
20
|
+
let sandbox;
|
|
21
|
+
let clock;
|
|
22
|
+
let getNavyStub;
|
|
23
|
+
let navyStub;
|
|
24
|
+
let hasUpdateStub;
|
|
25
|
+
let consoleLogStub;
|
|
26
|
+
let clearLineStub;
|
|
27
|
+
let cursorToStub;
|
|
28
|
+
let moveCursorStub;
|
|
29
|
+
let updatesCli;
|
|
30
|
+
function makeService(overrides = {}) {
|
|
31
|
+
return {
|
|
32
|
+
id: 'svc-1',
|
|
33
|
+
name: 'api',
|
|
34
|
+
image: 'navy/api:latest',
|
|
35
|
+
raw: {
|
|
36
|
+
Image: 'sha256:abcdef'
|
|
37
|
+
},
|
|
38
|
+
...overrides
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
beforeEach(function () {
|
|
42
|
+
sandbox = _sinon.default.createSandbox();
|
|
43
|
+
clock = sandbox.useFakeTimers();
|
|
44
|
+
navyStub = {
|
|
45
|
+
getNavyFile: sandbox.stub().resolves({}),
|
|
46
|
+
ps: sandbox.stub().resolves([])
|
|
47
|
+
};
|
|
48
|
+
getNavyStub = sandbox.stub().returns(navyStub);
|
|
49
|
+
hasUpdateStub = sandbox.stub().resolves(false);
|
|
50
|
+
consoleLogStub = sandbox.stub(console, 'log');
|
|
51
|
+
clearLineStub = sandbox.stub(_readline.default, 'clearLine');
|
|
52
|
+
cursorToStub = sandbox.stub(_readline.default, 'cursorTo');
|
|
53
|
+
moveCursorStub = sandbox.stub(_readline.default, 'moveCursor');
|
|
54
|
+
updatesCli = _proxyquire.default.noCallThru()('../updates', {
|
|
55
|
+
'../': {
|
|
56
|
+
getNavy: getNavyStub
|
|
57
|
+
},
|
|
58
|
+
'../util/has-update': hasUpdateStub
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
afterEach(function () {
|
|
62
|
+
sandbox.restore();
|
|
63
|
+
});
|
|
64
|
+
function combinedLog() {
|
|
65
|
+
return consoleLogStub.getCalls().map(c => (0, _stripAnsi.default)(String(c.args[0] || ''))).join('\n');
|
|
66
|
+
}
|
|
67
|
+
describe('default export', function () {
|
|
68
|
+
it('should resolve the navy instance using opts.navy', async function () {
|
|
69
|
+
await updatesCli({
|
|
70
|
+
navy: 'env-1'
|
|
71
|
+
});
|
|
72
|
+
(0, _chai.expect)(getNavyStub.calledOnce).to.equal(true);
|
|
73
|
+
(0, _chai.expect)(getNavyStub.firstCall.args[0]).to.equal('env-1');
|
|
74
|
+
});
|
|
75
|
+
it('should fetch the navy file and the running services list', async function () {
|
|
76
|
+
await updatesCli({
|
|
77
|
+
navy: 'env-1'
|
|
78
|
+
});
|
|
79
|
+
(0, _chai.expect)(navyStub.getNavyFile.calledOnce).to.equal(true);
|
|
80
|
+
(0, _chai.expect)(navyStub.ps.calledOnce).to.equal(true);
|
|
81
|
+
});
|
|
82
|
+
it('should print the table headers in the initial draw', async function () {
|
|
83
|
+
navyStub.ps.resolves([]);
|
|
84
|
+
await updatesCli({
|
|
85
|
+
navy: 'env-1'
|
|
86
|
+
});
|
|
87
|
+
const text = combinedLog();
|
|
88
|
+
(0, _chai.expect)(text).to.contain('NAME');
|
|
89
|
+
(0, _chai.expect)(text).to.contain('IMAGE');
|
|
90
|
+
(0, _chai.expect)(text).to.contain('UPDATES');
|
|
91
|
+
});
|
|
92
|
+
it('should clear the spinner interval before resolving', async function () {
|
|
93
|
+
navyStub.ps.resolves([makeService()]);
|
|
94
|
+
await updatesCli({
|
|
95
|
+
navy: 'env-1'
|
|
96
|
+
});
|
|
97
|
+
(0, _chai.expect)(clock.countTimers()).to.equal(0);
|
|
98
|
+
});
|
|
99
|
+
it('should call readline cursor/clear helpers when redrawing', async function () {
|
|
100
|
+
navyStub.ps.resolves([makeService()]);
|
|
101
|
+
await updatesCli({
|
|
102
|
+
navy: 'env-1'
|
|
103
|
+
});
|
|
104
|
+
(0, _chai.expect)(clearLineStub.called).to.equal(true);
|
|
105
|
+
(0, _chai.expect)(cursorToStub.called).to.equal(true);
|
|
106
|
+
(0, _chai.expect)(moveCursorStub.called).to.equal(true);
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
describe('renderStatus (via default export, final draw)', function () {
|
|
110
|
+
it('should mark a service as NO_IMAGE when raw is missing', async function () {
|
|
111
|
+
navyStub.ps.resolves([makeService({
|
|
112
|
+
raw: undefined
|
|
113
|
+
})]);
|
|
114
|
+
await updatesCli({
|
|
115
|
+
navy: 'env-1'
|
|
116
|
+
});
|
|
117
|
+
const text = combinedLog();
|
|
118
|
+
(0, _chai.expect)(text).to.contain('No image for service');
|
|
119
|
+
(0, _chai.expect)(hasUpdateStub.called).to.equal(false);
|
|
120
|
+
});
|
|
121
|
+
it('should mark a service as NO_IMAGE when raw.Image is missing', async function () {
|
|
122
|
+
navyStub.ps.resolves([makeService({
|
|
123
|
+
raw: {}
|
|
124
|
+
})]);
|
|
125
|
+
await updatesCli({
|
|
126
|
+
navy: 'env-1'
|
|
127
|
+
});
|
|
128
|
+
const text = combinedLog();
|
|
129
|
+
(0, _chai.expect)(text).to.contain('No image for service');
|
|
130
|
+
(0, _chai.expect)(hasUpdateStub.called).to.equal(false);
|
|
131
|
+
});
|
|
132
|
+
it('should mark a service as Update available when hasUpdate returns true', async function () {
|
|
133
|
+
navyStub.ps.resolves([makeService()]);
|
|
134
|
+
hasUpdateStub.resolves(true);
|
|
135
|
+
await updatesCli({
|
|
136
|
+
navy: 'env-1'
|
|
137
|
+
});
|
|
138
|
+
const text = combinedLog();
|
|
139
|
+
(0, _chai.expect)(text).to.contain('Update available');
|
|
140
|
+
});
|
|
141
|
+
it('should mark a service as Up to date when hasUpdate returns false', async function () {
|
|
142
|
+
navyStub.ps.resolves([makeService()]);
|
|
143
|
+
hasUpdateStub.resolves(false);
|
|
144
|
+
await updatesCli({
|
|
145
|
+
navy: 'env-1'
|
|
146
|
+
});
|
|
147
|
+
const text = combinedLog();
|
|
148
|
+
(0, _chai.expect)(text).to.contain('Up to date');
|
|
149
|
+
});
|
|
150
|
+
it('should mark a service as Not found when hasUpdate returns UNKNOWN_REMOTE', async function () {
|
|
151
|
+
navyStub.ps.resolves([makeService()]);
|
|
152
|
+
hasUpdateStub.resolves('UNKNOWN_REMOTE');
|
|
153
|
+
await updatesCli({
|
|
154
|
+
navy: 'env-1'
|
|
155
|
+
});
|
|
156
|
+
const text = combinedLog();
|
|
157
|
+
(0, _chai.expect)(text).to.contain('Not found');
|
|
158
|
+
});
|
|
159
|
+
it('should mark a service as Up to date when hasUpdate returns an unrecognised string', async function () {
|
|
160
|
+
navyStub.ps.resolves([makeService()]);
|
|
161
|
+
hasUpdateStub.resolves('SOME_OTHER_VALUE');
|
|
162
|
+
await updatesCli({
|
|
163
|
+
navy: 'env-1'
|
|
164
|
+
});
|
|
165
|
+
const text = combinedLog();
|
|
166
|
+
(0, _chai.expect)(text).to.contain('Up to date');
|
|
167
|
+
});
|
|
168
|
+
it('should mark a service as Internal error when hasUpdate throws', async function () {
|
|
169
|
+
navyStub.ps.resolves([makeService()]);
|
|
170
|
+
hasUpdateStub.rejects(new Error('boom'));
|
|
171
|
+
await updatesCli({
|
|
172
|
+
navy: 'env-1'
|
|
173
|
+
});
|
|
174
|
+
const text = combinedLog();
|
|
175
|
+
(0, _chai.expect)(text).to.contain('Internal error');
|
|
176
|
+
});
|
|
177
|
+
it('should mark a service as Internal error when hasUpdate throws an error with no stack', async function () {
|
|
178
|
+
navyStub.ps.resolves([makeService()]);
|
|
179
|
+
const errNoStack = {
|
|
180
|
+
message: 'no stack'
|
|
181
|
+
};
|
|
182
|
+
hasUpdateStub.callsFake(() => Promise.reject(errNoStack));
|
|
183
|
+
await updatesCli({
|
|
184
|
+
navy: 'env-1'
|
|
185
|
+
});
|
|
186
|
+
const text = combinedLog();
|
|
187
|
+
(0, _chai.expect)(text).to.contain('Internal error');
|
|
188
|
+
});
|
|
189
|
+
it('should pass image, raw image id, and navy file through to hasUpdate', async function () {
|
|
190
|
+
const navyFile = {
|
|
191
|
+
ignoreUnauthorizedRequestsForRegistries: ['r']
|
|
192
|
+
};
|
|
193
|
+
navyStub.getNavyFile.resolves(navyFile);
|
|
194
|
+
navyStub.ps.resolves([makeService({
|
|
195
|
+
image: 'navy/api:latest',
|
|
196
|
+
raw: {
|
|
197
|
+
Image: 'sha256:abc'
|
|
198
|
+
}
|
|
199
|
+
})]);
|
|
200
|
+
await updatesCli({
|
|
201
|
+
navy: 'env-1'
|
|
202
|
+
});
|
|
203
|
+
(0, _chai.expect)(hasUpdateStub.calledOnce).to.equal(true);
|
|
204
|
+
(0, _chai.expect)(hasUpdateStub.firstCall.args).to.eql(['navy/api:latest', 'sha256:abc', navyFile]);
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
describe('renderStatus null branch (initial Checking... state)', function () {
|
|
208
|
+
it('should print Checking... in the very first draw before any update results', async function () {
|
|
209
|
+
let resolveHasUpdate;
|
|
210
|
+
hasUpdateStub.callsFake(() => new Promise(resolve => {
|
|
211
|
+
resolveHasUpdate = resolve;
|
|
212
|
+
}));
|
|
213
|
+
navyStub.ps.resolves([makeService()]);
|
|
214
|
+
const cliPromise = updatesCli({
|
|
215
|
+
navy: 'env-1'
|
|
216
|
+
});
|
|
217
|
+
await clock.tickAsync(0);
|
|
218
|
+
const before = combinedLog();
|
|
219
|
+
(0, _chai.expect)(before).to.contain('Checking...');
|
|
220
|
+
resolveHasUpdate(false);
|
|
221
|
+
await cliPromise;
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
describe('spinner interval', function () {
|
|
225
|
+
it('should advance the spinner frame on each interval tick and wrap back to 0 after frames.length ticks', async function () {
|
|
226
|
+
let resolveHasUpdate;
|
|
227
|
+
hasUpdateStub.callsFake(() => new Promise(resolve => {
|
|
228
|
+
resolveHasUpdate = resolve;
|
|
229
|
+
}));
|
|
230
|
+
navyStub.ps.resolves([makeService()]);
|
|
231
|
+
const cliPromise = updatesCli({
|
|
232
|
+
navy: 'env-1'
|
|
233
|
+
});
|
|
234
|
+
await clock.tickAsync(0);
|
|
235
|
+
const drawsBefore = consoleLogStub.callCount;
|
|
236
|
+
await clock.tickAsync(_cliSpinners.dots.interval * (_cliSpinners.dots.frames.length + 1));
|
|
237
|
+
(0, _chai.expect)(consoleLogStub.callCount).to.be.greaterThan(drawsBefore);
|
|
238
|
+
resolveHasUpdate(false);
|
|
239
|
+
await cliPromise;
|
|
240
|
+
(0, _chai.expect)(clock.countTimers()).to.equal(0);
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
});
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
var _chai = require("chai");
|
|
5
|
+
var _sinon = _interopRequireDefault(require("sinon"));
|
|
6
|
+
var _proxyquire = _interopRequireDefault(require("proxyquire"));
|
|
7
|
+
/* eslint-env mocha */
|
|
8
|
+
|
|
9
|
+
describe('cli/wait-for-healthy', function () {
|
|
10
|
+
let sandbox;
|
|
11
|
+
let getNavyStub;
|
|
12
|
+
let navyStub;
|
|
13
|
+
let readlineStub;
|
|
14
|
+
let consoleLogStub;
|
|
15
|
+
let originalIsTTY;
|
|
16
|
+
let waitForHealthyCli;
|
|
17
|
+
let clock;
|
|
18
|
+
beforeEach(function () {
|
|
19
|
+
sandbox = _sinon.default.createSandbox();
|
|
20
|
+
navyStub = {
|
|
21
|
+
name: 'env-1',
|
|
22
|
+
waitForHealthy: sandbox.stub().resolves()
|
|
23
|
+
};
|
|
24
|
+
getNavyStub = sandbox.stub().returns(navyStub);
|
|
25
|
+
readlineStub = {
|
|
26
|
+
clearLine: sandbox.stub(),
|
|
27
|
+
cursorTo: sandbox.stub(),
|
|
28
|
+
moveCursor: sandbox.stub()
|
|
29
|
+
};
|
|
30
|
+
consoleLogStub = sandbox.stub(console, 'log');
|
|
31
|
+
originalIsTTY = process.stdout.isTTY;
|
|
32
|
+
waitForHealthyCli = _proxyquire.default.noCallThru()('../wait-for-healthy', {
|
|
33
|
+
'../': {
|
|
34
|
+
getNavy: getNavyStub
|
|
35
|
+
},
|
|
36
|
+
readline: readlineStub
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
afterEach(function () {
|
|
40
|
+
process.stdout.isTTY = originalIsTTY;
|
|
41
|
+
if (clock) {
|
|
42
|
+
clock.restore();
|
|
43
|
+
clock = null;
|
|
44
|
+
}
|
|
45
|
+
sandbox.restore();
|
|
46
|
+
});
|
|
47
|
+
describe('default export', function () {
|
|
48
|
+
describe('non-TTY mode', function () {
|
|
49
|
+
beforeEach(function () {
|
|
50
|
+
process.stdout.isTTY = false;
|
|
51
|
+
});
|
|
52
|
+
it('should call waitForHealthy with undefined when services are empty', async function () {
|
|
53
|
+
await waitForHealthyCli([], {
|
|
54
|
+
navy: 'env-1'
|
|
55
|
+
});
|
|
56
|
+
(0, _chai.expect)(navyStub.waitForHealthy.calledOnce).to.equal(true);
|
|
57
|
+
(0, _chai.expect)(navyStub.waitForHealthy.firstCall.args[0]).to.equal(undefined);
|
|
58
|
+
});
|
|
59
|
+
it('should call waitForHealthy with the supplied services', async function () {
|
|
60
|
+
await waitForHealthyCli(['web', 'api'], {
|
|
61
|
+
navy: 'env-1'
|
|
62
|
+
});
|
|
63
|
+
(0, _chai.expect)(navyStub.waitForHealthy.firstCall.args[0]).to.eql(['web', 'api']);
|
|
64
|
+
});
|
|
65
|
+
it('should log "Now available" after the wait completes', async function () {
|
|
66
|
+
await waitForHealthyCli([], {
|
|
67
|
+
navy: 'env-1'
|
|
68
|
+
});
|
|
69
|
+
(0, _chai.expect)(consoleLogStub.calledWith('Now available')).to.equal(true);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
describe('TTY mode', function () {
|
|
73
|
+
beforeEach(function () {
|
|
74
|
+
process.stdout.isTTY = true;
|
|
75
|
+
clock = _sinon.default.useFakeTimers();
|
|
76
|
+
});
|
|
77
|
+
it('should call waitForHealthy with a callback for service health updates', async function () {
|
|
78
|
+
const promise = waitForHealthyCli([], {
|
|
79
|
+
navy: 'env-1'
|
|
80
|
+
});
|
|
81
|
+
await promise;
|
|
82
|
+
(0, _chai.expect)(navyStub.waitForHealthy.calledOnce).to.equal(true);
|
|
83
|
+
(0, _chai.expect)(navyStub.waitForHealthy.firstCall.args[1]).to.be.a('function');
|
|
84
|
+
});
|
|
85
|
+
it('should print "Waiting" while no health status is reported', async function () {
|
|
86
|
+
await waitForHealthyCli([], {
|
|
87
|
+
navy: 'env-1'
|
|
88
|
+
});
|
|
89
|
+
const printed = consoleLogStub.getCalls().map(c => c.args[0] || '').join('\n');
|
|
90
|
+
(0, _chai.expect)(printed).to.contain('Waiting');
|
|
91
|
+
});
|
|
92
|
+
it('should render a checkmark for healthy services and spinner for unhealthy', async function () {
|
|
93
|
+
navyStub.waitForHealthy.callsFake(async (services, cb) => {
|
|
94
|
+
cb([{
|
|
95
|
+
service: 'web',
|
|
96
|
+
health: 'healthy'
|
|
97
|
+
}, {
|
|
98
|
+
service: 'api',
|
|
99
|
+
health: 'starting'
|
|
100
|
+
}]);
|
|
101
|
+
});
|
|
102
|
+
await waitForHealthyCli([], {
|
|
103
|
+
navy: 'env-1'
|
|
104
|
+
});
|
|
105
|
+
const printed = consoleLogStub.getCalls().map(c => c.args[0] || '').join('\n');
|
|
106
|
+
(0, _chai.expect)(printed).to.contain('web');
|
|
107
|
+
(0, _chai.expect)(printed).to.contain('api');
|
|
108
|
+
});
|
|
109
|
+
it('should advance the spinner frame and redraw on the timer interval', async function () {
|
|
110
|
+
navyStub.waitForHealthy.callsFake(() => new Promise(() => {}));
|
|
111
|
+
const promise = waitForHealthyCli([], {
|
|
112
|
+
navy: 'env-1'
|
|
113
|
+
});
|
|
114
|
+
clock.tick(1000);
|
|
115
|
+
(0, _chai.expect)(readlineStub.clearLine.called).to.equal(true);
|
|
116
|
+
promise.catch(() => {});
|
|
117
|
+
});
|
|
118
|
+
it('should wrap spinner index back to zero when reaching the end of frames', async function () {
|
|
119
|
+
navyStub.waitForHealthy.callsFake(() => new Promise(() => {}));
|
|
120
|
+
waitForHealthyCli([], {
|
|
121
|
+
navy: 'env-1'
|
|
122
|
+
}).catch(() => {});
|
|
123
|
+
clock.tick(10000);
|
|
124
|
+
(0, _chai.expect)(readlineStub.clearLine.called).to.equal(true);
|
|
125
|
+
});
|
|
126
|
+
it('should pass services through to waitForHealthy', async function () {
|
|
127
|
+
await waitForHealthyCli(['web'], {
|
|
128
|
+
navy: 'env-1'
|
|
129
|
+
});
|
|
130
|
+
(0, _chai.expect)(navyStub.waitForHealthy.firstCall.args[0]).to.eql(['web']);
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
});
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
var _chai = require("chai");
|
|
5
|
+
var _sinon = _interopRequireDefault(require("sinon"));
|
|
6
|
+
var _proxyquire = _interopRequireDefault(require("proxyquire"));
|
|
7
|
+
var _errors = require("../../../errors");
|
|
8
|
+
/* eslint-env mocha */
|
|
9
|
+
|
|
10
|
+
describe('cli/config/index', function () {
|
|
11
|
+
let sandbox;
|
|
12
|
+
let getConfigStub;
|
|
13
|
+
let setConfigStub;
|
|
14
|
+
let reconfigureAllNaviesStub;
|
|
15
|
+
let consoleLogStub;
|
|
16
|
+
let consoleErrorStub;
|
|
17
|
+
let processOnStub;
|
|
18
|
+
let actions;
|
|
19
|
+
let program;
|
|
20
|
+
let mod;
|
|
21
|
+
function loadModule() {
|
|
22
|
+
actions = {};
|
|
23
|
+
function commandFactory() {
|
|
24
|
+
const builder = {
|
|
25
|
+
description() {
|
|
26
|
+
return builder;
|
|
27
|
+
},
|
|
28
|
+
action(handler) {
|
|
29
|
+
builder._lastAction = handler;
|
|
30
|
+
return builder;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
return builder;
|
|
34
|
+
}
|
|
35
|
+
program = {
|
|
36
|
+
_commands: {},
|
|
37
|
+
command(spec) {
|
|
38
|
+
const name = spec.split(' ')[0];
|
|
39
|
+
const builder = commandFactory();
|
|
40
|
+
const origAction = builder.action.bind(builder);
|
|
41
|
+
builder.action = handler => {
|
|
42
|
+
actions[name] = handler;
|
|
43
|
+
return origAction(handler);
|
|
44
|
+
};
|
|
45
|
+
return builder;
|
|
46
|
+
},
|
|
47
|
+
parse: sandbox.stub(),
|
|
48
|
+
help: sandbox.stub(),
|
|
49
|
+
args: ['placeholder']
|
|
50
|
+
};
|
|
51
|
+
mod = _proxyquire.default.noCallThru()('../index', {
|
|
52
|
+
commander: {
|
|
53
|
+
program
|
|
54
|
+
},
|
|
55
|
+
'../../config': {
|
|
56
|
+
getConfig: getConfigStub,
|
|
57
|
+
setConfig: setConfigStub
|
|
58
|
+
},
|
|
59
|
+
'../../errors': {
|
|
60
|
+
NavyError: _errors.NavyError
|
|
61
|
+
},
|
|
62
|
+
'../util/reconfigure': {
|
|
63
|
+
reconfigureAllNavies: reconfigureAllNaviesStub
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
beforeEach(function () {
|
|
68
|
+
sandbox = _sinon.default.createSandbox();
|
|
69
|
+
getConfigStub = sandbox.stub().resolves({});
|
|
70
|
+
setConfigStub = sandbox.stub().resolves();
|
|
71
|
+
reconfigureAllNaviesStub = sandbox.stub().resolves();
|
|
72
|
+
consoleLogStub = sandbox.stub(console, 'log');
|
|
73
|
+
consoleErrorStub = sandbox.stub(console, 'error');
|
|
74
|
+
processOnStub = sandbox.stub(process, 'on');
|
|
75
|
+
loadModule();
|
|
76
|
+
});
|
|
77
|
+
afterEach(function () {
|
|
78
|
+
sandbox.restore();
|
|
79
|
+
});
|
|
80
|
+
describe('reconfigureIfNecessary', function () {
|
|
81
|
+
it('should call reconfigureAllNavies for externalIP', async function () {
|
|
82
|
+
await mod.reconfigureIfNecessary('externalIP');
|
|
83
|
+
(0, _chai.expect)(reconfigureAllNaviesStub.calledOnce).to.equal(true);
|
|
84
|
+
});
|
|
85
|
+
it('should not call reconfigureAllNavies for unrelated config props', async function () {
|
|
86
|
+
await mod.reconfigureIfNecessary('defaultNavy');
|
|
87
|
+
(0, _chai.expect)(reconfigureAllNaviesStub.called).to.equal(false);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
describe('module side effects', function () {
|
|
91
|
+
it('should register a process unhandledRejection handler', function () {
|
|
92
|
+
(0, _chai.expect)(processOnStub.calledWith('unhandledRejection')).to.equal(true);
|
|
93
|
+
});
|
|
94
|
+
it('should call program.parse with process.argv', function () {
|
|
95
|
+
(0, _chai.expect)(program.parse.calledOnce).to.equal(true);
|
|
96
|
+
(0, _chai.expect)(program.parse.firstCall.args[0]).to.equal(process.argv);
|
|
97
|
+
});
|
|
98
|
+
it('should not call help when args are present', function () {
|
|
99
|
+
(0, _chai.expect)(program.help.called).to.equal(false);
|
|
100
|
+
});
|
|
101
|
+
it('should call help when args are empty', function () {
|
|
102
|
+
sandbox.restore();
|
|
103
|
+
sandbox = _sinon.default.createSandbox();
|
|
104
|
+
getConfigStub = sandbox.stub().resolves({});
|
|
105
|
+
setConfigStub = sandbox.stub().resolves();
|
|
106
|
+
reconfigureAllNaviesStub = sandbox.stub().resolves();
|
|
107
|
+
sandbox.stub(console, 'log');
|
|
108
|
+
sandbox.stub(console, 'error');
|
|
109
|
+
sandbox.stub(process, 'on');
|
|
110
|
+
actions = {};
|
|
111
|
+
program = {
|
|
112
|
+
command(spec) {
|
|
113
|
+
const name = spec.split(' ')[0];
|
|
114
|
+
const builder = {
|
|
115
|
+
description() {
|
|
116
|
+
return builder;
|
|
117
|
+
},
|
|
118
|
+
action(handler) {
|
|
119
|
+
actions[name] = handler;
|
|
120
|
+
return builder;
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
return builder;
|
|
124
|
+
},
|
|
125
|
+
parse: sandbox.stub(),
|
|
126
|
+
help: sandbox.stub(),
|
|
127
|
+
args: []
|
|
128
|
+
};
|
|
129
|
+
_proxyquire.default.noCallThru()('../index', {
|
|
130
|
+
commander: {
|
|
131
|
+
program
|
|
132
|
+
},
|
|
133
|
+
'../../config': {
|
|
134
|
+
getConfig: getConfigStub,
|
|
135
|
+
setConfig: setConfigStub
|
|
136
|
+
},
|
|
137
|
+
'../../errors': {
|
|
138
|
+
NavyError: _errors.NavyError
|
|
139
|
+
},
|
|
140
|
+
'../util/reconfigure': {
|
|
141
|
+
reconfigureAllNavies: reconfigureAllNaviesStub
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
(0, _chai.expect)(program.help.calledOnce).to.equal(true);
|
|
145
|
+
});
|
|
146
|
+
describe('unhandledRejection handler', function () {
|
|
147
|
+
it('should call prettyPrint on a NavyError', function () {
|
|
148
|
+
const handler = processOnStub.firstCall.args[1];
|
|
149
|
+
const err = new _errors.NavyError('boom');
|
|
150
|
+
const prettyPrintStub = sandbox.stub(err, 'prettyPrint');
|
|
151
|
+
handler(err);
|
|
152
|
+
(0, _chai.expect)(prettyPrintStub.calledOnce).to.equal(true);
|
|
153
|
+
});
|
|
154
|
+
it('should log the stack of a generic error', function () {
|
|
155
|
+
const handler = processOnStub.firstCall.args[1];
|
|
156
|
+
const err = new Error('boom');
|
|
157
|
+
handler(err);
|
|
158
|
+
(0, _chai.expect)(consoleErrorStub.calledWith(err.stack)).to.equal(true);
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
describe('set command', function () {
|
|
163
|
+
it('should throw a NavyError for an unknown key', async function () {
|
|
164
|
+
let caught;
|
|
165
|
+
try {
|
|
166
|
+
await actions.set('not-a-key', 'value');
|
|
167
|
+
} catch (err) {
|
|
168
|
+
caught = err;
|
|
169
|
+
}
|
|
170
|
+
(0, _chai.expect)(caught).to.be.instanceof(_errors.NavyError);
|
|
171
|
+
(0, _chai.expect)(caught.message).to.contain('Invalid config key');
|
|
172
|
+
(0, _chai.expect)(setConfigStub.called).to.equal(false);
|
|
173
|
+
});
|
|
174
|
+
it('should map default-navy and call setConfig with merged config', async function () {
|
|
175
|
+
getConfigStub.resolves({
|
|
176
|
+
existing: 'value'
|
|
177
|
+
});
|
|
178
|
+
await actions.set('default-navy', 'dev');
|
|
179
|
+
(0, _chai.expect)(setConfigStub.calledOnce).to.equal(true);
|
|
180
|
+
(0, _chai.expect)(setConfigStub.firstCall.args[0]).to.eql({
|
|
181
|
+
existing: 'value',
|
|
182
|
+
defaultNavy: 'dev'
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
it('should reconfigure all navies when externalIP changes', async function () {
|
|
186
|
+
await actions.set('external-ip', '10.0.0.1');
|
|
187
|
+
(0, _chai.expect)(reconfigureAllNaviesStub.calledOnce).to.equal(true);
|
|
188
|
+
});
|
|
189
|
+
it('should not reconfigure when default-navy changes', async function () {
|
|
190
|
+
await actions.set('default-navy', 'dev');
|
|
191
|
+
(0, _chai.expect)(reconfigureAllNaviesStub.called).to.equal(false);
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
describe('get command', function () {
|
|
195
|
+
it('should throw a NavyError for an unknown key', async function () {
|
|
196
|
+
let caught;
|
|
197
|
+
try {
|
|
198
|
+
await actions.get('not-a-key');
|
|
199
|
+
} catch (err) {
|
|
200
|
+
caught = err;
|
|
201
|
+
}
|
|
202
|
+
(0, _chai.expect)(caught).to.be.instanceof(_errors.NavyError);
|
|
203
|
+
});
|
|
204
|
+
it('should print the mapped config value', async function () {
|
|
205
|
+
getConfigStub.resolves({
|
|
206
|
+
defaultNavy: 'dev'
|
|
207
|
+
});
|
|
208
|
+
await actions.get('default-navy');
|
|
209
|
+
(0, _chai.expect)(consoleLogStub.calledWith('dev')).to.equal(true);
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
describe('rm command', function () {
|
|
213
|
+
it('should throw a NavyError for an unknown key', async function () {
|
|
214
|
+
let caught;
|
|
215
|
+
try {
|
|
216
|
+
await actions.rm('not-a-key');
|
|
217
|
+
} catch (err) {
|
|
218
|
+
caught = err;
|
|
219
|
+
}
|
|
220
|
+
(0, _chai.expect)(caught).to.be.instanceof(_errors.NavyError);
|
|
221
|
+
(0, _chai.expect)(setConfigStub.called).to.equal(false);
|
|
222
|
+
});
|
|
223
|
+
it('should set the mapped property to null', async function () {
|
|
224
|
+
getConfigStub.resolves({
|
|
225
|
+
defaultNavy: 'dev'
|
|
226
|
+
});
|
|
227
|
+
await actions.rm('default-navy');
|
|
228
|
+
(0, _chai.expect)(setConfigStub.firstCall.args[0]).to.eql({
|
|
229
|
+
defaultNavy: null
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
it('should reconfigure all navies when externalIP is removed', async function () {
|
|
233
|
+
await actions.rm('external-ip');
|
|
234
|
+
(0, _chai.expect)(reconfigureAllNaviesStub.calledOnce).to.equal(true);
|
|
235
|
+
});
|
|
236
|
+
it('should not reconfigure when default-navy is removed', async function () {
|
|
237
|
+
await actions.rm('default-navy');
|
|
238
|
+
(0, _chai.expect)(reconfigureAllNaviesStub.called).to.equal(false);
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
describe('ls command', function () {
|
|
242
|
+
it('should print one line per known key with values', async function () {
|
|
243
|
+
getConfigStub.resolves({
|
|
244
|
+
defaultNavy: 'dev',
|
|
245
|
+
externalIP: '10.0.0.1',
|
|
246
|
+
tlsRootCaDir: null
|
|
247
|
+
});
|
|
248
|
+
await actions.ls();
|
|
249
|
+
const printed = consoleLogStub.firstCall.args[0];
|
|
250
|
+
(0, _chai.expect)(printed).to.contain('default-navy=dev');
|
|
251
|
+
(0, _chai.expect)(printed).to.contain('external-ip=10.0.0.1');
|
|
252
|
+
(0, _chai.expect)(printed).to.contain('tlsCa-dir=null');
|
|
253
|
+
});
|
|
254
|
+
it('should print null for missing keys', async function () {
|
|
255
|
+
getConfigStub.resolves({});
|
|
256
|
+
await actions.ls();
|
|
257
|
+
const printed = consoleLogStub.firstCall.args[0];
|
|
258
|
+
(0, _chai.expect)(printed).to.contain('default-navy=null');
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
describe('json command', function () {
|
|
262
|
+
it('should print the full config as pretty JSON', async function () {
|
|
263
|
+
getConfigStub.resolves({
|
|
264
|
+
a: 1,
|
|
265
|
+
b: 'two'
|
|
266
|
+
});
|
|
267
|
+
await actions.json();
|
|
268
|
+
(0, _chai.expect)(consoleLogStub.calledOnce).to.equal(true);
|
|
269
|
+
(0, _chai.expect)(consoleLogStub.firstCall.args[0]).to.equal(JSON.stringify({
|
|
270
|
+
a: 1,
|
|
271
|
+
b: 'two'
|
|
272
|
+
}, null, 2));
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
});
|