Skip to content

Commit 52a94c9

Browse files
authored
Added Client.snapshotVersion(), replaced snapshot() for security matters (#204)
1 parent 13c9812 commit 52a94c9

File tree

4 files changed

+53
-46
lines changed

4 files changed

+53
-46
lines changed

src/client.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,11 @@ export class Client {
117117
*/
118118
static testMode(testEnabled?: boolean): void;
119119

120+
/**
121+
* Returns the current snapshot version
122+
*/
123+
static get snapshotVersion(): number
124+
120125
}
121126

122127
/**

src/client.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,10 @@ export class Client {
254254
static testMode(testEnabled = true) {
255255
Client.testEnabled = testEnabled;
256256
}
257+
258+
static get snapshotVersion() {
259+
return GlobalSnapshot.snapshot?.data.domain.version || 0;
260+
}
257261
}
258262

259263
// Type export placeholders

tests/switcher-functional.test.js

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -320,10 +320,10 @@ describe('Integrated test - Switcher:', function () {
320320
});
321321

322322
it('should use silent mode when fail to check switchers', async function() {
323-
//given
323+
// given
324324
given(fetchStub, 0, { status: 429 });
325325

326-
//test
326+
// test
327327
Client.buildContext(contextSettings, { silentMode: '5m', regexSafe: false, snapshotLocation: './tests/snapshot/' });
328328
await Client.checkSwitchers(['FEATURE01', 'FEATURE02']).catch(e => {
329329
assert.equal(e.message, 'Something went wrong: [FEATURE01,FEATURE02] not found');
@@ -400,45 +400,45 @@ describe('Integrated test - Switcher:', function () {
400400
});
401401

402402
it('should NOT throw when switcher keys provided were configured properly', async function() {
403-
//given
403+
// given
404404
given(fetchStub, 0, { json: () => generateAuth('[auth_token]', 5), status: 200 });
405405
const response = { not_found: [] };
406406
given(fetchStub, 1, { json: () => response, status: 200 });
407407

408-
//test
408+
// test
409409
Client.buildContext(contextSettings);
410410
await assertResolve(assert, Client.checkSwitchers(['FEATURE01', 'FEATURE02']));
411411
});
412412

413413
it('should throw when switcher keys provided were not configured properly', async function() {
414-
//given
414+
// given
415415
given(fetchStub, 0, { json: () => generateAuth('[auth_token]', 5), status: 200 });
416416
const response = { not_found: ['FEATURE02'] };
417417
given(fetchStub, 1, { json: () => response, status: 200 });
418418

419-
//test
419+
// test
420420
Client.buildContext(contextSettings);
421421
await assertReject(assert, Client.checkSwitchers(['FEATURE01', 'FEATURE02']),
422422
'Something went wrong: [FEATURE02] not found');
423423
});
424424

425425
it('should throw when no switcher keys were provided', async function() {
426-
//given
426+
// given
427427
given(fetchStub, 0, { json: () => generateAuth('[auth_token]', 5), status: 200 });
428428
given(fetchStub, 1, { status: 422 });
429429

430-
//test
430+
// test
431431
Client.buildContext(contextSettings);
432432
await assertReject(assert, Client.checkSwitchers([]),
433433
'Something went wrong: [checkSwitchers] failed with status 422');
434434
});
435435

436436
it('should throw when switcher keys provided were invalid', async function() {
437-
//given
437+
// given
438438
given(fetchStub, 0, { json: () => generateAuth('[auth_token]', 5), status: 200 });
439439
given(fetchStub, 1, { errno: 'ERROR' });
440440

441-
//test
441+
// test
442442
Client.buildContext(contextSettings);
443443
await assertReject(assert, Client.checkSwitchers('FEATURE02'),
444444
'Something went wrong: [checkSwitchers] failed with status undefined');

tests/switcher-snapshot.test.js

Lines changed: 34 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -43,37 +43,35 @@ describe('E2E test - Switcher local - Snapshot:', function () {
4343
});
4444

4545
it('should update snapshot', async function () {
46-
//given
46+
// given
4747
fetchStub = stub(FetchFacade, 'fetch');
4848

4949
given(fetchStub, 0, { json: () => generateAuth('[auth_token]', 5), status: 200 });
5050
given(fetchStub, 1, { json: () => generateStatus(false), status: 200 }); // Snapshot outdated
5151
given(fetchStub, 2, { json: () => JSON.parse(dataJSON), status: 200 });
5252

53-
//test
53+
// test
5454
Client.buildContext({ url, apiKey, domain, component, environment }, {
55-
snapshotLocation: 'generated-snapshots/',
5655
local: true,
5756
regexSafe: false
5857
});
5958

6059
await Client.loadSnapshot({ watchSnapshot: true });
61-
assert.isTrue(await Client.checkSnapshot());
6260

63-
//restore state to avoid process leakage
64-
Client.unloadSnapshot();
65-
unlinkSync(`generated-snapshots/${environment}.json`);
61+
assert.equal(Client.snapshotVersion, 0);
62+
assert.isTrue(await Client.checkSnapshot());
63+
assert.isAbove(Client.snapshotVersion, 0);
6664
});
6765

6866
it('should update snapshot - store file', async function () {
69-
//given
67+
// given
7068
fetchStub = stub(FetchFacade, 'fetch');
7169

7270
given(fetchStub, 0, { json: () => generateAuth('[auth_token]', 5), status: 200 });
7371
given(fetchStub, 1, { json: () => generateStatus(false), status: 200 }); // Snapshot outdated
7472
given(fetchStub, 2, { json: () => JSON.parse(dataJSON), status: 200 });
7573

76-
//test
74+
// test
7775
Client.buildContext({ url, apiKey, domain, component, environment }, {
7876
snapshotLocation: 'generated-snapshots/',
7977
local: true,
@@ -84,19 +82,19 @@ describe('E2E test - Switcher local - Snapshot:', function () {
8482
assert.isTrue(await Client.checkSnapshot());
8583
assert.isTrue(existsSync(`generated-snapshots/${environment}.json`));
8684

87-
//restore state to avoid process leakage
85+
// restore state to avoid process leakage
8886
Client.unloadSnapshot();
8987
});
9088

9189
it('should update snapshot during load - store file', async function () {
92-
//given
90+
// given
9391
fetchStub = stub(FetchFacade, 'fetch');
9492

9593
given(fetchStub, 0, { json: () => generateAuth('[auth_token]', 5), status: 200 });
9694
given(fetchStub, 1, { json: () => generateStatus(false), status: 200 }); // Snapshot outdated
9795
given(fetchStub, 2, { json: () => JSON.parse(dataJSON), status: 200 });
9896

99-
//test
97+
// test
10098
Client.buildContext({ url, apiKey, domain, component, environment }, {
10199
snapshotLocation: 'generated-snapshots/',
102100
local: true,
@@ -106,92 +104,92 @@ describe('E2E test - Switcher local - Snapshot:', function () {
106104
await Client.loadSnapshot({ watchSnapshot: true, fetchRemote: true });
107105
assert.isTrue(existsSync(`generated-snapshots/${environment}.json`));
108106

109-
//restore state to avoid process leakage
107+
// restore state to avoid process leakage
110108
Client.unloadSnapshot();
111109
});
112110

113111
it('should NOT update snapshot', async function () {
114-
//given
112+
// given
115113
fetchStub = stub(FetchFacade, 'fetch');
116114

117115
given(fetchStub, 0, { json: () => generateAuth('[auth_token]', 5), status: 200 });
118116
given(fetchStub, 1, { json: () => generateStatus(true), status: 200 }); // No available update
119117

120-
//test
118+
// test
121119
await Client.loadSnapshot();
122120
assert.isFalse(await Client.checkSnapshot());
123121
});
124122

125123
it('should NOT update snapshot - check Snapshot Error', async function () {
126124
this.timeout(3000);
127125

128-
//given
126+
// given
129127
fetchStub = stub(FetchFacade, 'fetch');
130128

131129
given(fetchStub, 0, { json: () => generateAuth('[auth_token]', 5), status: 200 });
132130
givenError(fetchStub, 1, { errno: 'ECONNREFUSED' });
133131

134-
//test
132+
// test
135133
Client.testMode();
136134
await Client.loadSnapshot();
137135
await assertReject(assert, Client.checkSnapshot(),
138136
'Something went wrong: Connection has been refused - ECONNREFUSED');
139137
});
140138

141139
it('should NOT update snapshot - resolve Snapshot Error', async function () {
142-
//given
140+
// given
143141
fetchStub = stub(FetchFacade, 'fetch');
144142

145143
given(fetchStub, 0, { json: () => generateAuth('[auth_token]', 5), status: 200 });
146144
given(fetchStub, 1, { json: () => generateStatus(false), status: 200 }); // Snapshot outdated
147145
givenError(fetchStub, 2, { errno: 'ECONNREFUSED' });
148146

149-
//test
147+
// test
150148
Client.testMode();
151149
await Client.loadSnapshot();
152150
await assertReject(assert, Client.checkSnapshot(),
153151
'Something went wrong: Connection has been refused - ECONNREFUSED');
154152
});
155153

156154
it('should NOT check snapshot with success - Snapshot not loaded', async function () {
157-
//given
155+
// given
158156
fetchStub = stub(FetchFacade, 'fetch');
159157

160158
given(fetchStub, 0, { json: () => generateAuth('[auth_token]', 5), status: 200 });
161159
given(fetchStub, 1, { json: () => generateStatus(true), status: 200 });
162160

163-
//pre-load snapshot
161+
// pre-load snapshot
164162
Client.testMode(false);
165163
await Client.loadSnapshot();
166164
assert.equal(await Client.checkSnapshot(), false);
167165

168-
//unload snapshot
166+
// unload snapshot
169167
Client.unloadSnapshot();
170168

171-
//test
169+
// test
172170
let error = null;
173171
await Client.checkSnapshot().catch((err) => error = err);
174172
assert.exists(error);
175173
assert.equal(error.message, 'Something went wrong: Snapshot is not loaded. Use Client.loadSnapshot()');
176174
});
177175

178176
it('should update snapshot', async function () {
179-
//given
177+
// given
180178
fetchStub = stub(FetchFacade, 'fetch');
181179

182180
given(fetchStub, 0, { json: () => generateAuth('[auth_token]', 5), status: 200 });
183181
given(fetchStub, 1, { json: () => generateStatus(false), status: 200 }); // Snapshot outdated
184182
given(fetchStub, 2, { json: () => JSON.parse(dataJSON), status: 200 });
185183

186-
//test
184+
// test
187185
Client.buildContext({ url, apiKey, domain, component, environment }, {
188186
snapshotLocation: 'generated-snapshots/'
189187
});
190188

191189
await Client.loadSnapshot();
192190
assert.isNotNull(Client.snapshot);
193191

194-
//restore state to avoid process leakage
192+
// restore state to avoid process leakage
195193
Client.unloadSnapshot();
196194
unlinkSync(`generated-snapshots/${environment}.json`);
197195
});
@@ -234,28 +232,28 @@ describe('E2E test - Fail response - Snapshot:', function () {
234232
});
235233

236234
it('should NOT update snapshot - Too many requests at checkSnapshotVersion', async function () {
237-
//given
235+
// given
238236
fetchStub = stub(FetchFacade, 'fetch');
239237

240238
given(fetchStub, 0, { json: () => generateAuth('[auth_token]', 5), status: 200 });
241239
given(fetchStub, 1, { status: 429 });
242240

243-
//test
241+
// test
244242
Client.testMode();
245243
await Client.loadSnapshot();
246244
await assertReject(assert, Client.checkSnapshot(),
247245
'Something went wrong: [checkSnapshotVersion] failed with status 429');
248246
});
249247

250248
it('should NOT update snapshot - Too many requests at resolveSnapshot', async function () {
251-
//given
249+
// given
252250
fetchStub = stub(FetchFacade, 'fetch');
253251

254252
given(fetchStub, 0, { json: () => generateAuth('[auth_token]', 5), status: 200 });
255253
given(fetchStub, 1, { json: () => generateStatus(false), status: 200 }); // Snapshot outdated
256254
given(fetchStub, 2, { status: 429 });
257255

258-
//test
256+
// test
259257
Client.buildContext({ url, apiKey, domain, component, environment }, {
260258
snapshotLocation: 'generated-snapshots/',
261259
regexSafe: false
@@ -299,7 +297,7 @@ describe('E2E test - Snapshot AutoUpdater:', function () {
299297
it('should auto update snapshot every second', async function () {
300298
this.timeout(3000);
301299

302-
//given
300+
// given
303301
fetchStub = stub(FetchFacade, 'fetch');
304302

305303
given(fetchStub, 0, { json: () => generateAuth('[auth_token]', 5), status: 200 });
@@ -308,7 +306,7 @@ describe('E2E test - Snapshot AutoUpdater:', function () {
308306
given(fetchStub, 3, { json: () => generateStatus(false), status: 200 }); // Loading updated version
309307
given(fetchStub, 4, { json: () => JSON.parse(dataJSONV2), status: 200 });
310308

311-
//test
309+
// test
312310
Client.buildContext({ url, apiKey, domain, component, environment }, {
313311
snapshotLocation: 'generated-snapshots/',
314312
local: true,
@@ -336,12 +334,12 @@ describe('E2E test - Snapshot AutoUpdater:', function () {
336334
this.timeout(3000);
337335
fetchStub = stub(FetchFacade, 'fetch');
338336

339-
//given
337+
// given
340338
given(fetchStub, 0, { json: () => generateAuth('[auth_token]', 5), status: 200 });
341339
given(fetchStub, 1, { json: () => generateStatus(false), status: 200 });
342340
given(fetchStub, 2, { json: () => JSON.parse(dataJSON), status: 200 });
343341

344-
//test
342+
// test
345343
Client.buildContext({ url, apiKey, domain, component, environment }, {
346344
local: true,
347345
regexSafe: false
@@ -354,15 +352,15 @@ describe('E2E test - Snapshot AutoUpdater:', function () {
354352

355353
await Client.loadSnapshot({ watchSnapshot: false, fetchRemote: true });
356354

357-
//next call will fail
355+
// next call will fail
358356
givenError(fetchStub, 3, { errno: 'ECONNREFUSED' });
359357

360358
await sleep(1000);
361359

362360
assert.exists(error);
363361
assert.equal(error.message, 'Something went wrong: Connection has been refused - ECONNREFUSED');
364362

365-
//tearDown
363+
// tearDown
366364
Client.terminateSnapshotAutoUpdate();
367365
});
368366

0 commit comments

Comments
 (0)