From 33cdcab1f66f1426cdd848f19e401ae582da4a72 Mon Sep 17 00:00:00 2001 From: Emmanuel Zamora Date: Thu, 28 Sep 2023 11:59:08 -0300 Subject: [PATCH 01/38] [SDKS-7563] stabilize integration tests for flag sets --- package-lock.json | 4 +-- package.json | 2 +- src/__tests__/browserSuites/manager.spec.js | 3 ++- .../browserSuites/ready-from-cache.spec.js | 20 +++----------- src/__tests__/browserSuites/telemetry.spec.js | 5 ++-- src/__tests__/nodeSuites/manager.spec.js | 3 ++- src/__tests__/nodeSuites/telemetry.spec.js | 3 ++- src/__tests__/offline/browser.spec.js | 9 ++++--- src/__tests__/offline/node.spec.js | 27 ++++++++++++------- src/settings/defaults/version.js | 2 +- 10 files changed, 39 insertions(+), 39 deletions(-) diff --git a/package-lock.json b/package-lock.json index 976d0a61a..84247d463 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@splitsoftware/splitio", - "version": "10.23.1", + "version": "10.23.2-rc.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@splitsoftware/splitio", - "version": "10.23.1", + "version": "10.23.2-rc.0", "license": "Apache-2.0", "dependencies": { "@splitsoftware/splitio-commons": "1.9.1", diff --git a/package.json b/package.json index 10c413c7f..07f7461dd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio", - "version": "10.23.1", + "version": "10.23.2-rc.0", "description": "Split SDK", "files": [ "README.md", diff --git a/src/__tests__/browserSuites/manager.spec.js b/src/__tests__/browserSuites/manager.spec.js index 856242a5d..a8f1ecd20 100644 --- a/src/__tests__/browserSuites/manager.spec.js +++ b/src/__tests__/browserSuites/manager.spec.js @@ -39,7 +39,8 @@ export default async function (settings, fetchMock, assert) { 'killed': mockSplits.splits[index].killed, 'changeNumber': mockSplits.splits[index].changeNumber, 'treatments': map(mockSplits.splits[index].conditions[0].partitions, partition => partition.treatment), - 'configs': mockSplits.splits[index].configurations || {} + 'configs': mockSplits.splits[index].configurations || {}, + 'sets': mockSplits.splits[index].sets || [] }); assert.equal(manager.split('non_existent'), null, 'Trying to get a manager.split() of a Split that does not exist returns null.'); diff --git a/src/__tests__/browserSuites/ready-from-cache.spec.js b/src/__tests__/browserSuites/ready-from-cache.spec.js index c274c8e04..ebf1e50d7 100644 --- a/src/__tests__/browserSuites/ready-from-cache.spec.js +++ b/src/__tests__/browserSuites/ready-from-cache.spec.js @@ -477,7 +477,7 @@ export default function (fetchMock, assert) { events: 'https://events.baseurl/readyFromCache_5' }; localStorage.clear(); - t.plan(7); + t.plan(6); fetchMock.getOnce(testUrls.sdk + '/splitChanges?since=-1&names=p1__split,p2__split', { status: 200, body: { splits: [splitDeclarations.p1__split, splitDeclarations.p2__split], since: -1, till: 1457552620999 } }, { delay: 10 }); // short delay to let emit SDK_READY_FROM_CACHE // fetchMock.getOnce(testUrls.sdk + '/splitChanges?since=1457552620999&names=p1__split', { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); @@ -503,10 +503,6 @@ export default function (fetchMock, assert) { const client = splitio.client(); const manager = splitio.manager(); - client.once(client.Event.SDK_READY_FROM_CACHE, () => { - t.deepEqual(manager.names(), ['p2__split'], 'stored p3__split must be removed because doesn\'t match the filter'); - }); - client.once(client.Event.SDK_READY, () => { t.deepEqual(manager.names(), ['p1__split', 'p2__split'], 'p1__split should be added for evaluation'); @@ -571,7 +567,7 @@ export default function (fetchMock, assert) { events: 'https://events.baseurl/readyFromCache_6' }; localStorage.clear(); - t.plan(7); + t.plan(6); fetchMock.getOnce(testUrls.sdk + '/splitChanges?since=25&names=p2__split&prefixes=p1', { status: 200, body: { splits: [splitDeclarations.p1__split, splitDeclarations.p2__split], since: 25, till: 1457552620999 } }, { delay: 10 }); // short delay to let emit SDK_READY_FROM_CACHE fetchMock.getOnce(testUrls.sdk + '/mySegments/nicolas%40split.io', { status: 200, body: { mySegments: [] } }); @@ -597,15 +593,11 @@ export default function (fetchMock, assert) { const client = splitio.client(); const manager = splitio.manager(); - client.once(client.Event.SDK_READY_FROM_CACHE, () => { - t.deepEqual(manager.names(), ['p2__split', 'p1__split'], 'splits shouldn\'t be removed for evaluation'); - }); - client.once(client.Event.SDK_READY, () => { t.deepEqual(manager.names(), ['p2__split', 'p1__split'], 'active splits should be present for evaluation'); client.destroy().then(() => { - t.equal(localStorage.getItem('some_user_item'), 'user_item', 'user items at localStorage must not be changed'); + t.equal(localStorage.getItem('some_user_item'), 'user_item', ' user items at localStorage must not be changed'); t.equal(localStorage.getItem('readyFromCache_6.SPLITIO.splits.till'), '1457552620999', 'splits.till must correspond to the till of the last successfully fetched Splits'); t.equal(localStorage.getItem('readyFromCache_6.SPLITIO.split.p1__split'), JSON.stringify(splitDeclarations.p1__split), 'split declarations must be cached'); t.equal(localStorage.getItem('readyFromCache_6.SPLITIO.split.p2__split'), JSON.stringify(splitDeclarations.p2__split), 'split declarations must be cached'); @@ -740,7 +732,7 @@ export default function (fetchMock, assert) { events: 'https://events.baseurl/readyFromCache_9' }; localStorage.clear(); - t.plan(7); + t.plan(6); fetchMock.getOnce(testUrls.sdk + '/splitChanges?since=-1&names=no%20exist%20trim,no_exist,p3__split&prefixes=no%20exist%20trim,p2', { status: 200, body: { splits: [splitDeclarations.p2__split, splitDeclarations.p3__split], since: -1, till: 1457552620999 } }, { delay: 10 }); // short delay to let emit SDK_READY_FROM_CACHE fetchMock.getOnce(testUrls.sdk + '/mySegments/nicolas%40split.io', { status: 200, body: { mySegments: [] } }); @@ -766,10 +758,6 @@ export default function (fetchMock, assert) { const client = splitio.client(); const manager = splitio.manager(); - client.once(client.Event.SDK_READY_FROM_CACHE, () => { - t.deepEqual(manager.names(), ['p2__split'], 'stored p1__split must be removed because doesn\'t match the filter'); - }); - client.once(client.Event.SDK_READY, () => { t.deepEqual(manager.names(), ['p3__split', 'p2__split'], 'active splits should be present for evaluation'); diff --git a/src/__tests__/browserSuites/telemetry.spec.js b/src/__tests__/browserSuites/telemetry.spec.js index 03eeb21e9..d58e56923 100644 --- a/src/__tests__/browserSuites/telemetry.spec.js +++ b/src/__tests__/browserSuites/telemetry.spec.js @@ -54,7 +54,7 @@ export default async function telemetryBrowserSuite(fetchMock, assert) { const data = JSON.parse(opts.body); // Validate last successful sync - assert.deepEqual(Object.keys(data.lS), ['sp', 'ms', 'te'], 'Successful splitChanges, mySegments and metrics/config requests'); + assert.deepEqual(Object.keys(data.lS), ['ms', 'sp', 'te'], 'Successful splitChanges, mySegments and metrics/config requests'); lastSync = data.lS; delete data.lS; // Validate http and method latencies @@ -106,7 +106,8 @@ export default async function telemetryBrowserSuite(fetchMock, assert) { oM: 0, st: 'memory', aF: 1, rF: 0, sE: false, rR: { sp: 99999, ms: 60, im: 300, ev: 60, te: 1 } /* override featuresRefreshRate */, uO: { s: true, e: true, a: false, st: false, t: true } /* override sdk, events and telemetry URLs */, - iQ: 30000, eQ: 500, iM: 0, iL: false, hP: false, nR: 1 /* 1 non ready usage */, t: [], i: [], uC: 2 /* Default GRANTED */ + iQ: 30000, eQ: 500, iM: 0, iL: false, hP: false, nR: 1 /* 1 non ready usage */, t: [], i: [], uC: 2 /* Default GRANTED */, + fsT: 0, fsI: 0 }, 'metrics/config JSON payload should be the expected'); finish.next(); diff --git a/src/__tests__/nodeSuites/manager.spec.js b/src/__tests__/nodeSuites/manager.spec.js index 2658d2ebc..4d29bcbb6 100644 --- a/src/__tests__/nodeSuites/manager.spec.js +++ b/src/__tests__/nodeSuites/manager.spec.js @@ -38,7 +38,8 @@ export default async function (settings, fetchMock, assert) { 'killed': mockSplits.splits[index].killed, 'changeNumber': mockSplits.splits[index].changeNumber, 'treatments': map(mockSplits.splits[index].conditions[0].partitions, partition => partition.treatment), - 'configs': mockSplits.splits[index].configurations || {} + 'configs': mockSplits.splits[index].configurations || {}, + 'sets': mockSplits.splits[index].sets || [] }); assert.equal(manager.split('non_existent'), null, 'Trying to get a manager.split() of a Split that does not exist returns null.'); diff --git a/src/__tests__/nodeSuites/telemetry.spec.js b/src/__tests__/nodeSuites/telemetry.spec.js index da39b1134..25e096c94 100644 --- a/src/__tests__/nodeSuites/telemetry.spec.js +++ b/src/__tests__/nodeSuites/telemetry.spec.js @@ -100,7 +100,8 @@ export default async function telemetryNodejsSuite(key, fetchMock, assert) { oM: 0, st: 'memory', aF: 1, rF: 0, sE: false, rR: { sp: 99999, se: 60, im: 300, ev: 60, te: 1 } /* override featuresRefreshRate */, uO: { s: true, e: true, a: false, st: false, t: true } /* override sdk, events and telemetry URLs */, - iQ: 30000, eQ: 500, iM: 0, iL: false, hP: false, nR: 1 /* 1 non ready usage */, t: [], uC: 0 /* NA */ + iQ: 30000, eQ: 500, iM: 0, iL: false, hP: false, nR: 1 /* 1 non ready usage */, t: [], uC: 0 /* NA */, + fsT: 0, fsI: 0 }, 'metrics/config JSON payload should be the expected'); finish.next(); diff --git a/src/__tests__/offline/browser.spec.js b/src/__tests__/offline/browser.spec.js index ee3baf2fc..181cf6176 100644 --- a/src/__tests__/offline/browser.spec.js +++ b/src/__tests__/offline/browser.spec.js @@ -40,7 +40,8 @@ tape('Browser offline mode', function (assert) { testing_split: 'on', testing_split_with_config: { treatment: 'off', - config: '{ "color": "blue" }' + config: '{ "color": "blue" }', + sets: [] } }; @@ -166,10 +167,10 @@ tape('Browser offline mode', function (assert) { // Manager tests const expectedSplitView1 = { - name: 'testing_split', trafficType: 'localhost', killed: false, changeNumber: 0, treatments: ['on'], configs: {} + name: 'testing_split', trafficType: 'localhost', killed: false, changeNumber: 0, treatments: ['on'], configs: {}, sets: [] }; const expectedSplitView2 = { - name: 'testing_split_with_config', trafficType: 'localhost', killed: false, changeNumber: 0, treatments: ['off'], configs: { off: '{ "color": "blue" }' } + name: 'testing_split_with_config', trafficType: 'localhost', killed: false, changeNumber: 0, treatments: ['off'], configs: { off: '{ "color": "blue" }' }, sets: [] }; assert.deepEqual(manager.names(), ['testing_split', 'testing_split_with_config']); assert.deepEqual(manager.split('testing_split'), expectedSplitView1); @@ -267,7 +268,7 @@ tape('Browser offline mode', function (assert) { // Manager tests const expectedSplitView3 = { - name: 'testing_split_with_config', trafficType: 'localhost', killed: false, changeNumber: 0, treatments: ['nope'], configs: {} + name: 'testing_split_with_config', trafficType: 'localhost', killed: false, changeNumber: 0, treatments: ['nope'], configs: {}, sets: [] }; assert.deepEqual(manager.names(), ['testing_split', 'testing_split_2', 'testing_split_3', 'testing_split_with_config']); assert.deepEqual(manager.split('testing_split'), expectedSplitView1); diff --git a/src/__tests__/offline/node.spec.js b/src/__tests__/offline/node.spec.js index 9fb3cb511..dfa74a98e 100644 --- a/src/__tests__/offline/node.spec.js +++ b/src/__tests__/offline/node.spec.js @@ -249,15 +249,18 @@ function ManagerDotSplitTests(assert) { const expectedView1 = { name: 'testing_split', changeNumber: 0, killed: false, trafficType: 'localhost', - treatments: ['on'], configs: {} + treatments: ['on'], configs: {}, + sets: [] }; const expectedView2 = { name: 'testing_split2', changeNumber: 0, killed: false, trafficType: 'localhost', - treatments: ['off'], configs: {} + treatments: ['off'], configs: {}, + sets: [] }; const expectedView3 = { name: 'testing_split3', changeNumber: 0, killed: false, trafficType: 'localhost', - treatments: ['custom_treatment'], configs: {} + treatments: ['custom_treatment'], configs: {}, + sets: [] }; assert.deepEqual(manager.split('testing_split'), expectedView1); @@ -289,7 +292,8 @@ function ManagerDotYamlTests(mockFileName, assert) { killed: false, trafficType: 'localhost', treatments: ['on'], - configs: {} + configs: {}, + sets: [] }; const expectedView2 = { name: 'testing_split_only_wl', @@ -297,7 +301,8 @@ function ManagerDotYamlTests(mockFileName, assert) { killed: false, trafficType: 'localhost', treatments: ['whitelisted'], - configs: {} + configs: {}, + sets: [] }; const expectedView3 = { name: 'testing_split_with_wl', @@ -308,13 +313,15 @@ function ManagerDotYamlTests(mockFileName, assert) { configs: { not_in_whitelist: '{"color": "green"}', multi_key_wl: '{"color": "brown"}' - } + }, + sets: [] }; const expectedView4 = { name: 'testing_split_off_with_config', changeNumber: 0, killed: false, trafficType: 'localhost', treatments: ['off'], configs: { off: '{"color": "green"}' - } + }, + sets: [] }; assert.deepEqual(manager.split('testing_split_on'), expectedView1); @@ -395,15 +402,15 @@ function MultipleInstancesTests(assert) { const expectedView1 = { name: 'testing_split', changeNumber: 0, killed: false, trafficType: 'localhost', - treatments: ['on'], configs: {} + treatments: ['on'], configs: {}, sets: [] }; const expectedView2 = { name: 'testing_split2', changeNumber: 0, killed: false, trafficType: 'localhost', - treatments: ['off'], configs: {} + treatments: ['off'], configs: {}, sets: [] }; const expectedView3 = { name: 'testing_split3', changeNumber: 0, killed: false, trafficType: 'localhost', - treatments: ['custom_treatment'], configs: {} + treatments: ['custom_treatment'], configs: {}, sets: [] }; assert.deepEqual(manager.split('testing_split'), expectedView1); diff --git a/src/settings/defaults/version.js b/src/settings/defaults/version.js index fc16e17c3..99f181e5a 100644 --- a/src/settings/defaults/version.js +++ b/src/settings/defaults/version.js @@ -1 +1 @@ -export const packageVersion = '10.23.1'; +export const packageVersion = '10.23.2-rc.0'; From a2fa07a2d9a61f5a84b33b27658d19c74883be9a Mon Sep 17 00:00:00 2001 From: Emmanuel Zamora Date: Thu, 28 Sep 2023 16:58:19 -0300 Subject: [PATCH 02/38] Update commons rc version --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 84247d463..bf1a63fb9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "10.23.2-rc.0", "license": "Apache-2.0", "dependencies": { - "@splitsoftware/splitio-commons": "1.9.1", + "@splitsoftware/splitio-commons": "1.9.2-rc.0", "@types/google.analytics": "0.0.40", "@types/ioredis": "^4.28.0", "bloom-filters": "^3.0.0", @@ -802,9 +802,9 @@ "dev": true }, "node_modules/@splitsoftware/splitio-commons": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.9.1.tgz", - "integrity": "sha512-+1lFUBj+Jgfd8l7nT49+rZodwgZXJ6c20+4PPb0O+iTmUZXHtYvfS2AK8A9j6QREubeccc6UMlfjH3SQVgwEbg==", + "version": "1.9.2-rc.0", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.9.2-rc.0.tgz", + "integrity": "sha512-sKsaIAELQDwA/LUpZflVkkrtBzaghrz1tqnpLV3iVeiXBWK3Im3pEHoMm4bwa3wtoawbInrFbIdN+oIXrCHkRw==", "dependencies": { "tslib": "^2.3.1" }, @@ -8312,9 +8312,9 @@ "dev": true }, "@splitsoftware/splitio-commons": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.9.1.tgz", - "integrity": "sha512-+1lFUBj+Jgfd8l7nT49+rZodwgZXJ6c20+4PPb0O+iTmUZXHtYvfS2AK8A9j6QREubeccc6UMlfjH3SQVgwEbg==", + "version": "1.9.2-rc.0", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.9.2-rc.0.tgz", + "integrity": "sha512-sKsaIAELQDwA/LUpZflVkkrtBzaghrz1tqnpLV3iVeiXBWK3Im3pEHoMm4bwa3wtoawbInrFbIdN+oIXrCHkRw==", "requires": { "tslib": "^2.3.1" } diff --git a/package.json b/package.json index 07f7461dd..f8780ffa3 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "node": ">=6" }, "dependencies": { - "@splitsoftware/splitio-commons": "1.9.1", + "@splitsoftware/splitio-commons": "1.9.2-rc.0", "@types/google.analytics": "0.0.40", "@types/ioredis": "^4.28.0", "bloom-filters": "^3.0.0", From b7b8434cfa541cf820a2dc583ac9e7c325d80ad8 Mon Sep 17 00:00:00 2001 From: Emmanuel Zamora Date: Mon, 2 Oct 2023 16:46:06 -0300 Subject: [PATCH 03/38] Improve ready from cache tests --- .../browserSuites/ready-from-cache.spec.js | 53 ++++++++++++++++++- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/src/__tests__/browserSuites/ready-from-cache.spec.js b/src/__tests__/browserSuites/ready-from-cache.spec.js index ebf1e50d7..0ab98eb87 100644 --- a/src/__tests__/browserSuites/ready-from-cache.spec.js +++ b/src/__tests__/browserSuites/ready-from-cache.spec.js @@ -503,6 +503,11 @@ export default function (fetchMock, assert) { const client = splitio.client(); const manager = splitio.manager(); + client.once(client.Event.SDK_READY_FROM_CACHE, () => { + t.fail('It should not emit SDK_READY_FROM_CACHE because localstorage is cleaned and there isn\'t cached data'); + t.end(); + }); + client.once(client.Event.SDK_READY, () => { t.deepEqual(manager.names(), ['p1__split', 'p2__split'], 'p1__split should be added for evaluation'); @@ -517,6 +522,46 @@ export default function (fetchMock, assert) { }); }); + assert.test(t => { // Testing when we start with cached data with split filter, and the same split filter is provided in the config + const testUrls = { + sdk: 'https://sdk.baseurl/readyFromCache_5', + events: 'https://events.baseurl/readyFromCache_5' + }; + + localStorage.clear(); + t.plan(1); + + fetchMock.getOnce(testUrls.sdk + '/splitChanges?since=25&names=p2__split,p3__split', { status: 200, body: { splits: [splitDeclarations.p2__split, splitDeclarations.p3__split], since: -1, till: 1457552620999 } }, { delay: 10 }); // short delay to let emit SDK_READY_FROM_CACHE + fetchMock.getOnce(testUrls.sdk + '/mySegments/nicolas%40split.io', { status: 200, body: { mySegments: [] } }); + + localStorage.setItem('some_user_item', 'user_item'); + localStorage.setItem('readyFromCache_5.SPLITIO.splits.till', 25); + localStorage.setItem('readyFromCache_5.SPLITIO.splits.filterQuery', '&names=p2__split,p3__split'); + localStorage.setItem('readyFromCache_5.SPLITIO.split.p2__split', JSON.stringify(splitDeclarations.p2__split)); + localStorage.setItem('readyFromCache_5.SPLITIO.split.p3__split', JSON.stringify(splitDeclarations.p3__split)); + + const splitio = SplitFactory({ + ...baseConfig, + storage: { + type: 'LOCALSTORAGE', + prefix: 'readyFromCache_5' + }, + urls: testUrls, + sync: { + splitFilters: [{ type: 'byName', values: ['p2__split', 'p3__split'] }] + }, + debug: true + }); + const client = splitio.client(); + + client.once(client.Event.SDK_READY_FROM_CACHE, () => { + t.assert('It should emit SDK_READY_FROM_CACHE because there is cached data for the same queryFilter'); + client.destroy().then(() => { + t.end(); + }); + }); + }); + assert.test(t => { // Testing when we start from scratch, and a valid split filter config const testUrls = { sdk: 'https://sdk.baseurl/readyFromCache_5B', @@ -567,7 +612,7 @@ export default function (fetchMock, assert) { events: 'https://events.baseurl/readyFromCache_6' }; localStorage.clear(); - t.plan(6); + t.plan(7); fetchMock.getOnce(testUrls.sdk + '/splitChanges?since=25&names=p2__split&prefixes=p1', { status: 200, body: { splits: [splitDeclarations.p1__split, splitDeclarations.p2__split], since: 25, till: 1457552620999 } }, { delay: 10 }); // short delay to let emit SDK_READY_FROM_CACHE fetchMock.getOnce(testUrls.sdk + '/mySegments/nicolas%40split.io', { status: 200, body: { mySegments: [] } }); @@ -593,11 +638,15 @@ export default function (fetchMock, assert) { const client = splitio.client(); const manager = splitio.manager(); + client.once(client.Event.SDK_READY_FROM_CACHE, () => { + t.deepEqual(manager.names(), ['p2__split', 'p1__split'], 'splits shouldn\'t be removed for evaluation'); + }); + client.once(client.Event.SDK_READY, () => { t.deepEqual(manager.names(), ['p2__split', 'p1__split'], 'active splits should be present for evaluation'); client.destroy().then(() => { - t.equal(localStorage.getItem('some_user_item'), 'user_item', ' user items at localStorage must not be changed'); + t.equal(localStorage.getItem('some_user_item'), 'user_item', 'user items at localStorage must not be changed'); t.equal(localStorage.getItem('readyFromCache_6.SPLITIO.splits.till'), '1457552620999', 'splits.till must correspond to the till of the last successfully fetched Splits'); t.equal(localStorage.getItem('readyFromCache_6.SPLITIO.split.p1__split'), JSON.stringify(splitDeclarations.p1__split), 'split declarations must be cached'); t.equal(localStorage.getItem('readyFromCache_6.SPLITIO.split.p2__split'), JSON.stringify(splitDeclarations.p2__split), 'split declarations must be cached'); From c510e3631979880b33d3b8eb9c3edf394ca50c0f Mon Sep 17 00:00:00 2001 From: Emmanuel Zamora Date: Mon, 2 Oct 2023 11:28:26 -0300 Subject: [PATCH 04/38] [SDKS-7564] Flag sets integration tests --- package-lock.json | 26 +- package.json | 2 +- .../fetch-specific-splits.spec.js | 40 ++- src/__tests__/browserSuites/flag-sets.spec.js | 204 +++++++++++ .../browserSuites/push-flag-sets.spec.js | 262 ++++++++++++++ src/__tests__/browserSuites/telemetry.spec.js | 337 +++++++++++------- .../mocks/message.SPLIT_UPDATE.FS.1.json | 4 + .../mocks/message.SPLIT_UPDATE.FS.2.json | 4 + .../mocks/message.SPLIT_UPDATE.FS.3.json | 4 + .../mocks/message.SPLIT_UPDATE.FS.4.json | 4 + .../mocks/message.SPLIT_UPDATE.FS.4None.json | 4 + .../mocks/message.SPLIT_UPDATE.FS.5.json | 4 + .../mocks/message.SPLIT_UPDATE.FS.kill.json | 4 + ...itchanges.since.-1.till.1602796638344.json | 150 ++++++++ ...ince.1602796638344.till.1602797638344.json | 78 ++++ ...ince.1602797638344.till.1602798638344.json | 78 ++++ .../nodeSuites/fetch-specific-splits.spec.js | 30 +- src/__tests__/nodeSuites/flag-sets.spec.js | 215 +++++++++++ .../nodeSuites/push-flag-sets.spec.js | 267 ++++++++++++++ src/__tests__/online/browser.spec.js | 3 + src/__tests__/online/node.spec.js | 4 + src/__tests__/push/browser.spec.js | 4 + src/__tests__/push/node.spec.js | 4 + 23 files changed, 1585 insertions(+), 147 deletions(-) create mode 100644 src/__tests__/browserSuites/flag-sets.spec.js create mode 100644 src/__tests__/browserSuites/push-flag-sets.spec.js create mode 100644 src/__tests__/mocks/message.SPLIT_UPDATE.FS.1.json create mode 100644 src/__tests__/mocks/message.SPLIT_UPDATE.FS.2.json create mode 100644 src/__tests__/mocks/message.SPLIT_UPDATE.FS.3.json create mode 100644 src/__tests__/mocks/message.SPLIT_UPDATE.FS.4.json create mode 100644 src/__tests__/mocks/message.SPLIT_UPDATE.FS.4None.json create mode 100644 src/__tests__/mocks/message.SPLIT_UPDATE.FS.5.json create mode 100644 src/__tests__/mocks/message.SPLIT_UPDATE.FS.kill.json create mode 100644 src/__tests__/mocks/splitchanges.since.-1.till.1602796638344.json create mode 100644 src/__tests__/mocks/splitchanges.since.1602796638344.till.1602797638344.json create mode 100644 src/__tests__/mocks/splitchanges.since.1602797638344.till.1602798638344.json create mode 100644 src/__tests__/nodeSuites/flag-sets.spec.js create mode 100644 src/__tests__/nodeSuites/push-flag-sets.spec.js diff --git a/package-lock.json b/package-lock.json index bf1a63fb9..fd1396791 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "10.23.2-rc.0", "license": "Apache-2.0", "dependencies": { - "@splitsoftware/splitio-commons": "1.9.2-rc.0", + "@splitsoftware/splitio-commons": "1.9.2-rc.1", "@types/google.analytics": "0.0.40", "@types/ioredis": "^4.28.0", "bloom-filters": "^3.0.0", @@ -802,9 +802,9 @@ "dev": true }, "node_modules/@splitsoftware/splitio-commons": { - "version": "1.9.2-rc.0", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.9.2-rc.0.tgz", - "integrity": "sha512-sKsaIAELQDwA/LUpZflVkkrtBzaghrz1tqnpLV3iVeiXBWK3Im3pEHoMm4bwa3wtoawbInrFbIdN+oIXrCHkRw==", + "version": "1.9.2-rc.1", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.9.2-rc.1.tgz", + "integrity": "sha512-a9NYF8gh8QRstWuVwLZ55YxeOwbAWL/dFralfxvRcBX1zVwUfKMvyW0bPKzXUzqTbvxsfWI4PV95xFgbQk/m/Q==", "dependencies": { "tslib": "^2.3.1" }, @@ -7036,9 +7036,9 @@ } }, "node_modules/tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" }, "node_modules/tty-browserify": { "version": "0.0.1", @@ -8312,9 +8312,9 @@ "dev": true }, "@splitsoftware/splitio-commons": { - "version": "1.9.2-rc.0", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.9.2-rc.0.tgz", - "integrity": "sha512-sKsaIAELQDwA/LUpZflVkkrtBzaghrz1tqnpLV3iVeiXBWK3Im3pEHoMm4bwa3wtoawbInrFbIdN+oIXrCHkRw==", + "version": "1.9.2-rc.1", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.9.2-rc.1.tgz", + "integrity": "sha512-a9NYF8gh8QRstWuVwLZ55YxeOwbAWL/dFralfxvRcBX1zVwUfKMvyW0bPKzXUzqTbvxsfWI4PV95xFgbQk/m/Q==", "requires": { "tslib": "^2.3.1" } @@ -13162,9 +13162,9 @@ } }, "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" }, "tty-browserify": { "version": "0.0.1", diff --git a/package.json b/package.json index f8780ffa3..ff68ea6e5 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "node": ">=6" }, "dependencies": { - "@splitsoftware/splitio-commons": "1.9.2-rc.0", + "@splitsoftware/splitio-commons": "1.9.2-rc.1", "@types/google.analytics": "0.0.40", "@types/ioredis": "^4.28.0", "bloom-filters": "^3.0.0", diff --git a/src/__tests__/browserSuites/fetch-specific-splits.spec.js b/src/__tests__/browserSuites/fetch-specific-splits.spec.js index 14dc031ea..a15f68b42 100644 --- a/src/__tests__/browserSuites/fetch-specific-splits.spec.js +++ b/src/__tests__/browserSuites/fetch-specific-splits.spec.js @@ -1,3 +1,4 @@ +import sinon from 'sinon'; import { SplitFactory } from '../../'; import { splitFilters, queryStrings, groupedFilters } from '../mocks/fetchSpecificSplits'; @@ -14,7 +15,7 @@ const baseConfig = { export default function fetchSpecificSplits(fetchMock, assert) { - assert.plan(splitFilters.length); + assert.plan(splitFilters.length+1); for (let i = 0; i < splitFilters.length; i++) { const urls = { sdk: 'https://sdkurl' + i }; @@ -45,4 +46,41 @@ export default function fetchSpecificSplits(fetchMock, assert) { } } + + // Flag sets + assert.test(async (t) => { + + const splitFilters = [{ type: 'bySet', values: ['set_x ', 'set_x', 'set_3', 'set_2', 'set_3', 'set_ww', 'invalid+', '_invalid', '4_valid'] }]; + const baseUrls = { sdk: 'https://sdk.baseurl' }; + + const config = { + ...baseConfig, + urls: baseUrls, + debug: 'WARN', + sync: { + splitFilters + } + }; + + const logSpy = sinon.spy(console, 'log'); + + fetchMock.get(baseUrls.sdk + '/mySegments/nicolas%40split.io', { status: 200, body: { 'mySegments': [] } }); + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=-1&sets=4_valid,set_2,set_3,set_ww,set_x', function () { + t.pass('flag set query correctly formed'); + return { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }; + }); + + const factory = SplitFactory(config); + + const client = factory.client(); + client.ready().then(() => { + t.true(logSpy.calledWithExactly('[WARN] splitio => settings: bySet filter value "set_x " has extra whitespace, trimming.')); + t.true(logSpy.calledWithExactly('[WARN] splitio => settings: you passed invalid+, flag set must adhere to the regular expressions /^[a-z0-9][_a-z0-9]{0,49}$/. This means a flag set must start with a letter or number, be in lowercase, alphanumeric and have a max length of 50 characters. invalid+ was discarded.')); + t.true(logSpy.calledWithExactly('[WARN] splitio => settings: you passed _invalid, flag set must adhere to the regular expressions /^[a-z0-9][_a-z0-9]{0,49}$/. This means a flag set must start with a letter or number, be in lowercase, alphanumeric and have a max length of 50 characters. _invalid was discarded.')); + logSpy.restore(); + factory.client().destroy().then(() => { + t.end(); + }); + }); + }, 'FlagSets config'); } diff --git a/src/__tests__/browserSuites/flag-sets.spec.js b/src/__tests__/browserSuites/flag-sets.spec.js new file mode 100644 index 000000000..cae4fddc1 --- /dev/null +++ b/src/__tests__/browserSuites/flag-sets.spec.js @@ -0,0 +1,204 @@ +import { SplitFactory } from '../..'; + +import splitChange2 from '../mocks/splitChanges.since.-1.till.1602796638344.json'; +import splitChange1 from '../mocks/splitchanges.since.1602796638344.till.1602797638344.json'; +import splitChange0 from '../mocks/splitchanges.since.1602797638344.till.1602798638344.json'; + +const baseUrls = { sdk: 'https://sdk.baseurl' }; + +const baseConfig = { + core: { + authorizationKey: '', + key: 'nicolas@split.io' + }, + urls: baseUrls, + scheduler: { featuresRefreshRate: 0.01 }, + streamingEnabled: false +}; + +export default function flagSets(fetchMock, t) { + fetchMock.get(baseUrls.sdk + '/mySegments/nicolas%40split.io', { status: 200, body: { 'mySegments': [] } }); + + t.test(async (assert) => { + let factory; + let manager; + + // Receive split change with 1 split belonging to set_1 & set_2 and one belonging to set_3 + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=-1&sets=set_1,set_2', function () { + return { status: 200, body: splitChange2}; + }); + + // Receive split change with 1 split belonging to set_1 only + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1602796638344&sets=set_1,set_2', function () { + // stored feature flags before update + const storedFlags = manager.splits(); + assert.true(storedFlags.length === 1, 'only one feature flag should be added'); + assert.true(storedFlags[0].name === 'workm'); + assert.deepEqual(storedFlags[0].sets, ['set_1','set_2']); + + // send split change + return { status: 200, body: splitChange1}; + }); + + // Receive split change with 1 split belonging to set_3 only + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1602797638344&sets=set_1,set_2', function () { + // stored feature flags before update + const storedFlags = manager.splits(); + assert.true(storedFlags.length === 1); + assert.true(storedFlags[0].name === 'workm'); + assert.deepEqual(storedFlags[0].sets, ['set_1'], 'the feature flag should be updated'); + + // send split change + return { status: 200, body: splitChange0}; + }); + + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1602798638344&sets=set_1,set_2', async function () { + // stored feature flags before update + const storedFlags = manager.splits(); + assert.true(storedFlags.length === 0, 'the feature flag should be removed'); + await factory.client().destroy(); + assert.end(); + + return { status: 200, body: {} }; + }); + + // Initialize a factory with polling and sets set_1 & set_2 configured. + const splitFilters = [{ type: 'bySet', values: ['set_1','set_2'] }]; + factory = SplitFactory({ ...baseConfig, sync: { splitFilters }}); + await factory.client().ready(); + manager = factory.manager(); + + }, 'Polling - SDK with sets configured updates flags according to sets'); + + t.test(async (assert) => { + let factory; + let manager; + + // Receive split change with 1 split belonging to set_1 & set_2 and one belonging to set_3 + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=-1', function () { + return { status: 200, body: splitChange2}; + }); + + // Receive split change with 1 split belonging to set_1 only + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1602796638344', function () { + // stored feature flags before update + const storedFlags = manager.splits(); + assert.true(storedFlags.length === 2, 'every feature flag should be added'); + assert.true(storedFlags[0].name === 'workm'); + assert.true(storedFlags[1].name === 'workm_set_3'); + assert.deepEqual(storedFlags[0].sets, ['set_1','set_2']); + assert.deepEqual(storedFlags[1].sets, ['set_3']); + + // send split change + return { status: 200, body: splitChange1}; + }); + + // Receive split change with 1 split belonging to set_3 only + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1602797638344', function () { + // stored feature flags before update + const storedFlags = manager.splits(); + assert.true(storedFlags.length === 2); + assert.true(storedFlags[0].name === 'workm'); + assert.true(storedFlags[1].name === 'workm_set_3'); + assert.deepEqual(storedFlags[0].sets, ['set_1'], 'the feature flag should be updated'); + assert.deepEqual(storedFlags[1].sets, ['set_3'], 'the feature flag should remain as it was'); + + // send split change + return { status: 200, body: splitChange0}; + }); + + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1602798638344', async function () { + // stored feature flags before update + const storedFlags = manager.splits(); + assert.true(storedFlags.length === 2); + assert.true(storedFlags[0].name === 'workm'); + assert.true(storedFlags[1].name === 'workm_set_3'); + assert.deepEqual(storedFlags[0].sets, ['set_3'], 'the feature flag should be updated'); + assert.deepEqual(storedFlags[1].sets, ['set_3'], 'the feature flag should remain as it was'); + await factory.client().destroy(); + assert.end(); + return { status: 200, body: {} }; + }); + + // Initialize a factory with polling and no sets configured. + factory = SplitFactory(baseConfig); + await factory.client().ready(); + manager = factory.manager(); + + }, 'Poling - SDK with no sets configured does not take sets into account when updating flags'); + + // EVALUATION + + t.test(async (assert) => { + fetchMock.reset(); + assert.plan(8); + + let factory, client = []; + + fetchMock.get(baseUrls.sdk + '/mySegments/nicolas%40split.io', { status: 200, body: { 'mySegments': [] } }); + // Receive split change with 1 split belonging to set_1 & set_2 and one belonging to set_3 + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=-1&sets=set_1', function () { + return { status: 200, body: splitChange2}; + }); + + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1602796638344&sets=set_1', async function () { + // stored feature flags before update + assert.deepEqual(client.getTreatmentsByFlagSet('set_1'), {workm: 'on'}, 'only the flag in set_1 can be evaluated'); + assert.deepEqual(client.getTreatmentsByFlagSet('set_2'), {}, 'only the flag in set_1 can be evaluated'); + assert.deepEqual(client.getTreatmentsByFlagSet('set_3'), {}, 'only the flag in set_1 can be evaluated'); + assert.deepEqual(client.getTreatmentsWithConfigByFlagSet('set_1'), { workm: { treatment: 'on', config: null } }, 'only the flag in set_1 can be evaluated'); + assert.deepEqual(client.getTreatmentsWithConfigByFlagSet('set_2'), {}, 'only the flag in set_1 can be evaluated'); + assert.deepEqual(client.getTreatmentsWithConfigByFlagSet('set_3'), {}, 'only the flag in set_1 can be evaluated'); + assert.deepEqual(client.getTreatmentsByFlagSets(['set_1','set_2','set_3']), {workm: 'on'}, 'only the flag in set_1 can be evaluated'); + assert.deepEqual(client.getTreatmentsWithConfigByFlagSets(['set_1','set_2','set_3']), { workm: { treatment: 'on', config: null } }, 'only the flag in set_1 can be evaluated'); + await client.destroy(); + assert.end(); + + // send split change + return { status: 200, body: splitChange1}; + }); + + // Initialize a factory with set_1 configured. + const splitFilters = [{ type: 'bySet', values: ['set_1'] }]; + factory = SplitFactory({ ...baseConfig, sync: { splitFilters }}); + client = factory.client(); + await client.ready(); + + }, 'SDK with sets configured can only evaluate configured sets'); + + t.test(async (assert) => { + fetchMock.reset(); + assert.plan(8); + + let factory, client = []; + + fetchMock.get(baseUrls.sdk + '/mySegments/nicolas%40split.io', { status: 200, body: { 'mySegments': [] } }); + // Receive split change with 1 split belonging to set_1 & set_2 and one belonging to set_3 + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=-1', function () { + return { status: 200, body: splitChange2}; + }); + + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1602796638344', async function () { + // stored feature flags before update + assert.deepEqual(client.getTreatmentsByFlagSet('set_1'), {workm: 'on'}, 'all flags can be evaluated'); + assert.deepEqual(client.getTreatmentsByFlagSet('set_2'), {workm: 'on'}, 'all flags can be evaluated'); + assert.deepEqual(client.getTreatmentsByFlagSet('set_3'), { workm_set_3: 'on' }, 'all flags can be evaluated'); + assert.deepEqual(client.getTreatmentsWithConfigByFlagSet('set_1'), { workm: { treatment: 'on', config: null } }, 'all flags can be evaluated'); + assert.deepEqual(client.getTreatmentsWithConfigByFlagSet('set_2'), { workm: { treatment: 'on', config: null } }, 'all flags can be evaluated'); + assert.deepEqual(client.getTreatmentsWithConfigByFlagSet('set_3'), { workm_set_3: { treatment: 'on', config: null } }, 'all flags can be evaluated'); + assert.deepEqual(client.getTreatmentsByFlagSets(['set_1','set_2','set_3']), { workm: 'on', workm_set_3: 'on' }, 'all flags can be evaluated'); + assert.deepEqual(client.getTreatmentsWithConfigByFlagSets(['set_1','set_2','set_3']), { workm: { treatment: 'on', config: null }, workm_set_3: { treatment: 'on', config: null } }, 'all flags can be evaluated'); + await client.destroy(); + assert.end(); + + // send split change + return { status: 200, body: splitChange1}; + }); + + factory = SplitFactory(baseConfig); + client = factory.client(); + await client.ready(); + + }, 'SDK with no sets configured can evaluate any set'); + +} diff --git a/src/__tests__/browserSuites/push-flag-sets.spec.js b/src/__tests__/browserSuites/push-flag-sets.spec.js new file mode 100644 index 000000000..d2d7b2a6f --- /dev/null +++ b/src/__tests__/browserSuites/push-flag-sets.spec.js @@ -0,0 +1,262 @@ +import { SplitFactory } from '../..'; +import EventSourceMock, { setMockListener } from '../testUtils/eventSourceMock'; +window.EventSource = EventSourceMock; + +import notification1 from '../mocks/message.SPLIT_UPDATE.FS.1.json'; +import notification2 from '../mocks/message.SPLIT_UPDATE.FS.2.json'; +import notification3 from '../mocks/message.SPLIT_UPDATE.FS.3.json'; +import notification4None from '../mocks/message.SPLIT_UPDATE.FS.4None.json'; +import notification4 from '../mocks/message.SPLIT_UPDATE.FS.4.json'; +import notification5 from '../mocks/message.SPLIT_UPDATE.FS.5.json'; +import notificationKill from '../mocks/message.SPLIT_UPDATE.FS.kill.json'; + +import authPushEnabled from '../mocks/auth.pushEnabled.nicolas@split.io.json'; + +const baseUrls = { + sdk: 'https://sdk.baseurl', + auth: 'https://auth.baseurl/api' +}; + +const baseConfig = { + core: { + authorizationKey: '', + key: 'nicolas@split.io' + }, + urls: baseUrls, +}; + +const MILLIS_FIRST_SPLIT_UPDATE_EVENT = 100; +const MILLIS_SECOND_SPLIT_UPDATE_EVENT = 200; +const MILLIS_THIRD_SPLIT_UPDATE_EVENT = 300; +const MILLIS_FOURTH_SPLIT_UPDATE_EVENT = 400; + +export function testFlagSets(fetchMock, t) { + fetchMock.reset(); + + fetchMock.get(baseUrls.sdk + '/mySegments/nicolas%40split.io', { status: 200, body: { 'mySegments': [] } }); + + fetchMock.get(baseUrls.auth + '/v2/auth?users=nicolas%40split.io', function () { + return { status: 200, body: authPushEnabled }; + }); + fetchMock.get(baseUrls.sdk + '/splitChanges?since=-1', function () { + return { status: 200, body: { splits: [], since: -1, till: 0}}; + }); + fetchMock.get(baseUrls.sdk + '/splitChanges?since=0', function () { + return { status: 200, body: { splits: [], since: 0, till: 1 }}; + }); + fetchMock.get(baseUrls.sdk + '/splitChanges?since=-1&sets=set_1,set_2', function () { + return { status: 200, body: { splits: [], since: -1, till: 0 }}; + }); + fetchMock.get(baseUrls.sdk + '/splitChanges?since=0&sets=set_1,set_2', function () { + return { status: 200, body: { splits: [], since: 0, till: 1 }}; + }); + + + const configWithSets = { + ...baseConfig, + sync: { + splitFilters: [{type: 'bySet', values: ['set_1', 'set_2']}] + } + }; + + t.test(async (assert) => { + + assert.plan(3); + let splitio, client, manager = []; + + setMockListener((eventSourceInstance) => { + eventSourceInstance.emitOpen(); + + setTimeout(() => { + assert.deepEqual(manager.splits(), [], '1 - initialized without flags'); + client.once(client.Event.SDK_UPDATE, async () => { + assert.equal(manager.splits().length, 1, '1 - update is processed and the flag is stored'); + await client.destroy(); + assert.equal(eventSourceInstance.readyState, EventSourceMock.CLOSED, '1 - streaming is closed after destroy'); + assert.end(); + }); + eventSourceInstance.emitMessage(notification1); + }, MILLIS_FIRST_SPLIT_UPDATE_EVENT); + + }); + + splitio = SplitFactory(baseConfig); + client = splitio.client(); + manager = splitio.manager(); + + }, 'SDK with no sets configured does not exclude updates'); + + t.test(async (assert) => { + + assert.plan(5); + let splitio, client, manager = []; + + setMockListener((eventSourceInstance) => { + eventSourceInstance.emitOpen(); + + setTimeout(() => { + assert.deepEqual(manager.splits(), [], '2 - initialized without flags'); + // Receive a SPLIT_UPDATE with "sets":["set_1", "set_2"] + client.once(client.Event.SDK_UPDATE, () => { + assert.deepEqual(manager.split('workm').sets, ['set_1', 'set_2'], '2 - update is processed and the flag is stored'); + }); + eventSourceInstance.emitMessage(notification2); + }, MILLIS_FIRST_SPLIT_UPDATE_EVENT); + + setTimeout(() => { + // Receive a SPLIT_UPDATE with "sets":["set_1"] + client.once(client.Event.SDK_UPDATE, () => { + assert.deepEqual(manager.split('workm').sets, ['set_1'], '2 - update is processed and the flag is updated'); + }); + eventSourceInstance.emitMessage(notification3); + }, MILLIS_SECOND_SPLIT_UPDATE_EVENT); + + setTimeout(() => { + // Receive a SPLIT_UPDATE with "sets":[] + client.once(client.Event.SDK_UPDATE, async () => { + assert.deepEqual(manager.splits().length, 0, '2 - update is processed and the flag is removed'); + await client.destroy(); + assert.equal(eventSourceInstance.readyState, EventSourceMock.CLOSED, '2 - streaming is closed after destroy'); + assert.end(); + }); + eventSourceInstance.emitMessage(notification4None); + }, MILLIS_THIRD_SPLIT_UPDATE_EVENT); + + }); + + splitio = SplitFactory(configWithSets); + client = splitio.client(); + manager = splitio.manager(); + + }, 'SDK with sets configured deletes flag when change with empty sets is received'); + + t.test(async (assert) => { + + assert.plan(6); + let splitio, client, manager = []; + + setMockListener((eventSourceInstance) => { + eventSourceInstance.emitOpen(); + + setTimeout(() => { + assert.deepEqual(manager.splits(), [], '3 - initialized without flags'); + // Receive a SPLIT_UPDATE with "sets":["set_1", "set_2"] + client.once(client.Event.SDK_UPDATE, () => { + assert.deepEqual(manager.split('workm').sets, ['set_1', 'set_2'], '3 - update is processed and the flag is stored'); + }); + eventSourceInstance.emitMessage(notification2); + }, MILLIS_FIRST_SPLIT_UPDATE_EVENT); + + setTimeout(() => { + // Receive a SPLIT_UPDATE with "sets":["set_1"] + client.once(client.Event.SDK_UPDATE, () => { + assert.deepEqual(manager.split('workm').sets, ['set_1'], '3 - update is processed and the flag is updated'); + }); + eventSourceInstance.emitMessage(notification3); + }, MILLIS_SECOND_SPLIT_UPDATE_EVENT); + + setTimeout(() => { + // Receive a SPLIT_UPDATE with "sets":["set_3"] + client.once(client.Event.SDK_UPDATE, () => { + assert.deepEqual(manager.splits().length, 0, '3 - update is processed and the flag is removed'); + }); + eventSourceInstance.emitMessage(notification4); + }, MILLIS_THIRD_SPLIT_UPDATE_EVENT); + + setTimeout(() => { + // Receive a SPLIT_UPDATE with "sets":["set_3", "set_4"] + client.once(client.Event.SDK_UPDATE, async () => { + assert.deepEqual(manager.splits().length, 0, '3 - update is processed and flag is not added to the storage'); + await client.destroy(); + assert.equal(eventSourceInstance.readyState, EventSourceMock.CLOSED, '3 - streaming is closed after destroy'); + assert.end(); + }); + eventSourceInstance.emitMessage(notification5); + }, MILLIS_FOURTH_SPLIT_UPDATE_EVENT); + + }); + + splitio = SplitFactory(configWithSets); + client = splitio.client(); + manager = splitio.manager(); + + }, 'SDK with sets configured deletes flag when change with non-matching sets is received'); + + t.test(async (assert) => { + + assert.plan(5); + + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=2&sets=set_1,set_2', function () { + assert.pass('4 - A fetch is triggered due to the SPLIT_KILL'); + return { status: 200, body: { splits: [], since: 2, till: 3 }}; + }); + + let splitio, client, manager = []; + + setMockListener((eventSourceInstance) => { + eventSourceInstance.emitOpen(); + + setTimeout(() => { + assert.deepEqual(manager.splits(), [], '4 - initialized without flags'); + // Receive a SPLIT_UPDATE with "sets":["set_1", "set_2"] + client.once(client.Event.SDK_UPDATE, () => { + assert.equal(manager.split('workm').killed, false, '4 - update is processed and the flag is stored'); + }); + eventSourceInstance.emitMessage(notification2); + }, MILLIS_FIRST_SPLIT_UPDATE_EVENT); + + setTimeout(() => { + // Receive a SPLIT_KILL for flag + client.once(client.Event.SDK_UPDATE, async () => { + assert.equal(manager.split('workm').killed, true, '4 - update is processed and the flag is updated'); + await client.destroy(); + assert.equal(eventSourceInstance.readyState, EventSourceMock.CLOSED, '4 - streaming is closed after destroy'); + assert.end(); + }); + eventSourceInstance.emitMessage(notificationKill); + }, MILLIS_SECOND_SPLIT_UPDATE_EVENT); + + }); + + splitio = SplitFactory(configWithSets); + client = splitio.client(); + manager = splitio.manager(); + + }, 'SDK with sets configured updates flag when a SPLIT_KILL is received'); + + t.test(async (assert) => { + + assert.plan(4); + + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1&sets=set_1,set_2', function () { + assert.pass('5 - A fetch is triggered due to the SPLIT_KILL'); + return { status: 200, body: { splits: [], since: 1, till: 5 }}; + }); + + let splitio, client, manager = []; + + setMockListener((eventSourceInstance) => { + eventSourceInstance.emitOpen(); + + setTimeout(() => { + assert.deepEqual(manager.splits(), [], '5 - initialized without flags'); + + // Receive a SPLIT_KILL for flag + client.once(client.Event.SDK_UPDATE, async () => { + assert.deepEqual(manager.splits(), [], '5 - storage is not modified since flag is not present. '); + await client.destroy(); + assert.equal(eventSourceInstance.readyState, EventSourceMock.CLOSED, '5 - streaming is closed after destroy'); + assert.end(); + }); + eventSourceInstance.emitMessage(notificationKill); + }, MILLIS_FIRST_SPLIT_UPDATE_EVENT); + + }); + + splitio = SplitFactory(configWithSets); + client = splitio.client(); + manager = splitio.manager(); + + }, 'SDK with sets configured does not update flag when a SPLIT_KILL is received for a non-existing flag'); + +} diff --git a/src/__tests__/browserSuites/telemetry.spec.js b/src/__tests__/browserSuites/telemetry.spec.js index d58e56923..5d0c8f576 100644 --- a/src/__tests__/browserSuites/telemetry.spec.js +++ b/src/__tests__/browserSuites/telemetry.spec.js @@ -20,147 +20,222 @@ const config = { streamingEnabled: false }; -export default async function telemetryBrowserSuite(fetchMock, assert) { - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=-1', 500); - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=-1', { status: 200, body: splitChangesMock1 }); - fetchMock.getOnce(baseUrls.sdk + '/mySegments/user-key', 500); - fetchMock.getOnce(baseUrls.sdk + '/mySegments/user-key', { status: 200, body: { 'mySegments': [ 'one_segment'] } }); - - // We need to handle all requests properly - fetchMock.postOnce(baseUrls.events + '/testImpressions/bulk', 200); - fetchMock.postOnce(baseUrls.events + '/testImpressions/count', 200); - fetchMock.postOnce(baseUrls.events + '/events/bulk', 200); - +const SplitFactoryForTest = (config) => { // Overwrite Math.random to instantiate factory with telemetry const originalMathRandom = Math.random; Math.random = () => 0.001; - const splitio = SplitFactory(config, ({ settings }) => { - assert.equal(settings.scheduler.telemetryRefreshRate, 60000); + const factory = SplitFactory(config, ({ settings }) => { settings.scheduler.telemetryRefreshRate = 1000; // set below minimum to validate matrics/usage requests }); Math.random = originalMathRandom; // restore + return factory; +}; - const client = splitio.client(); - - const finish = (function* () { - yield; - client.destroy(); - assert.end(); - })(); - - let lastSync; - - // 1st metrics/usage call due to telemetryRefreshRate set in 1 second - fetchMock.postOnce(baseUrls.telemetry + '/v1/metrics/usage', (url, opts) => { - const data = JSON.parse(opts.body); - - // Validate last successful sync - assert.deepEqual(Object.keys(data.lS), ['ms', 'sp', 'te'], 'Successful splitChanges, mySegments and metrics/config requests'); - lastSync = data.lS; delete data.lS; - - // Validate http and method latencies - const getLatencyCount = buckets => buckets ? buckets.reduce((accum, entry) => accum + entry, 0) : 0; - assert.equal(getLatencyCount(data.hL.sp), 2, 'Two latency metrics for splitChanges GET request'); - assert.equal(getLatencyCount(data.hL.ms), 2, 'Two latency metrics for mySegments GET request'); - assert.equal(getLatencyCount(data.hL.te), 1, 'One latency metric for telemetry config POST request'); - assert.equal(getLatencyCount(data.mL.t), 2, 'Two latency metrics for getTreatment (one not ready usage'); - assert.equal(getLatencyCount(data.mL.ts), 1, 'One latency metric for getTreatments'); - assert.equal(getLatencyCount(data.mL.tc), 1, 'One latency metric for getTreatmentWithConfig'); - assert.equal(getLatencyCount(data.mL.tcs), 1, 'One latency metric for getTreatmentsWithConfig'); - assert.equal(getLatencyCount(data.mL.tr), 1, 'One latency metric for track'); - delete data.hL; delete data.mL; - - // @TODO check if iDe value is correct - assert.deepEqual(data, { - mE: {}, hE: { sp: { 500: 1 }, ms: { 500: 1 } }, tR: 0, aR: 0, iQ: 4, iDe: 1, iDr: 0, spC: 31, seC: 1, skC: 1, eQ: 1, eD: 0, sE: [], t: [], ufs: { sp: 0, ms: 0 } - }, 'metrics/usage JSON payload should be the expected'); - - finish.next(); - return 200; - }); - - // 2nd metrics/usage call due to destroy - fetchMock.postOnce(baseUrls.telemetry + '/v1/metrics/usage', (url, opts) => { - const data = JSON.parse(opts.body); - - assert.deepEqual(data.lS, lastSync, 'last successful sync hasn\'t change'); - delete data.lS; - - assert.true(data.sL > 0, 'sessionLengthMs must be defined'); - delete data.sL; - - // @TODO check if iDe value is correct - assert.deepEqual(data, { - mL: {}, mE: {}, hE: {}, hL: {}, // errors and latencies were popped - tR: 0, aR: 0, iQ: 4, iDe: 1, iDr: 0, spC: 31, seC: 1, skC: 1, eQ: 1, eD: 0, sE: [], t: [], ufs: { sp: 0, ms: 0 } - }, '2nd metrics/usage JSON payload should be the expected'); - return 200; - }); - - fetchMock.postOnce(baseUrls.telemetry + '/v1/metrics/config', (url, opts) => { - const data = JSON.parse(opts.body); - - assert.true(data.tR > 0, 'timeUntilReady is larger than 0'); - delete data.tR; // delete to validate other properties - - assert.deepEqual(data, { - oM: 0, st: 'memory', aF: 1, rF: 0, sE: false, - rR: { sp: 99999, ms: 60, im: 300, ev: 60, te: 1 } /* override featuresRefreshRate */, - uO: { s: true, e: true, a: false, st: false, t: true } /* override sdk, events and telemetry URLs */, - iQ: 30000, eQ: 500, iM: 0, iL: false, hP: false, nR: 1 /* 1 non ready usage */, t: [], i: [], uC: 2 /* Default GRANTED */, - fsT: 0, fsI: 0 - }, 'metrics/config JSON payload should be the expected'); +export default async function telemetryBrowserSuite(fetchMock, t) { + + t.test(async (assert) => { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=-1', 500); + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=-1', { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(baseUrls.sdk + '/mySegments/user-key', 500); + fetchMock.getOnce(baseUrls.sdk + '/mySegments/user-key', { status: 200, body: { 'mySegments': [ 'one_segment'] } }); + + // We need to handle all requests properly + fetchMock.postOnce(baseUrls.events + '/testImpressions/bulk', 200); + fetchMock.postOnce(baseUrls.events + '/testImpressions/count', 200); + fetchMock.postOnce(baseUrls.events + '/events/bulk', 200); + + const splitio = SplitFactoryForTest(config); + const client = splitio.client(); + + const finish = (function* () { + yield; + client.destroy(); + assert.end(); + })(); + + let lastSync; + + // 1st metrics/usage call due to telemetryRefreshRate set in 1 second + fetchMock.postOnce(baseUrls.telemetry + '/v1/metrics/usage', (url, opts) => { + const data = JSON.parse(opts.body); + + // Validate last successful sync + assert.deepEqual(Object.keys(data.lS), ['ms', 'sp', 'te'], 'Successful splitChanges, mySegments and metrics/config requests'); + lastSync = data.lS; delete data.lS; + + // Validate http and method latencies + const getLatencyCount = buckets => buckets ? buckets.reduce((accum, entry) => accum + entry, 0) : 0; + assert.equal(getLatencyCount(data.hL.sp), 2, 'Two latency metrics for splitChanges GET request'); + assert.equal(getLatencyCount(data.hL.ms), 2, 'Two latency metrics for mySegments GET request'); + assert.equal(getLatencyCount(data.hL.te), 1, 'One latency metric for telemetry config POST request'); + assert.equal(getLatencyCount(data.mL.t), 2, 'Two latency metrics for getTreatment (one not ready usage'); + assert.equal(getLatencyCount(data.mL.ts), 1, 'One latency metric for getTreatments'); + assert.equal(getLatencyCount(data.mL.tc), 1, 'One latency metric for getTreatmentWithConfig'); + assert.equal(getLatencyCount(data.mL.tcs), 1, 'One latency metric for getTreatmentsWithConfig'); + assert.equal(getLatencyCount(data.mL.tr), 1, 'One latency metric for track'); + delete data.hL; delete data.mL; + + // @TODO check if iDe value is correct + assert.deepEqual(data, { + mE: {}, hE: { sp: { 500: 1 }, ms: { 500: 1 } }, tR: 0, aR: 0, iQ: 4, iDe: 1, iDr: 0, spC: 31, seC: 1, skC: 1, eQ: 1, eD: 0, sE: [], t: [], ufs: { sp: 0, ms: 0 } + }, 'metrics/usage JSON payload should be the expected'); + + finish.next(); + return 200; + }); + + // 2nd metrics/usage call due to destroy + fetchMock.postOnce(baseUrls.telemetry + '/v1/metrics/usage', (url, opts) => { + const data = JSON.parse(opts.body); + + assert.deepEqual(data.lS, lastSync, 'last successful sync hasn\'t change'); + delete data.lS; + + assert.true(data.sL > 0, 'sessionLengthMs must be defined'); + delete data.sL; + + // @TODO check if iDe value is correct + assert.deepEqual(data, { + mL: {}, mE: {}, hE: {}, hL: {}, // errors and latencies were popped + tR: 0, aR: 0, iQ: 4, iDe: 1, iDr: 0, spC: 31, seC: 1, skC: 1, eQ: 1, eD: 0, sE: [], t: [], ufs: { sp: 0, ms: 0 } + }, '2nd metrics/usage JSON payload should be the expected'); + return 200; + }); + + fetchMock.postOnce(baseUrls.telemetry + '/v1/metrics/config', (url, opts) => { + const data = JSON.parse(opts.body); + + assert.true(data.tR > 0, 'timeUntilReady is larger than 0'); + delete data.tR; // delete to validate other properties + + assert.deepEqual(data, { + oM: 0, st: 'memory', aF: 1, rF: 0, sE: false, + rR: { sp: 99999, ms: 60, im: 300, ev: 60, te: 1 } /* override featuresRefreshRate */, + uO: { s: true, e: true, a: false, st: false, t: true } /* override sdk, events and telemetry URLs */, + iQ: 30000, eQ: 500, iM: 0, iL: false, hP: false, nR: 1 /* 1 non ready usage */, t: [], i: [], uC: 2 /* Default GRANTED */, + fsT: 0, fsI: 0 + }, 'metrics/config JSON payload should be the expected'); + + finish.next(); + + return 200; + }); + + assert.equal(client.getTreatment('always_on'), 'control', 'Non ready usage.'); + + await client.ready(); + + // treatments and results are only validated so we know for sure when the function was actually running to compare the metrics. + assert.equal(client.getTreatment('always_on'), 'on', 'Evaluation was correct.'); + assert.equal(client.getTreatment('always_on', () => { }), 'control', 'We should return control with invalid input.'); + + assert.deepEqual(client.getTreatmentWithConfig('split_with_config'), { + treatment: 'o.n', + config: '{"color":"brown","dimensions":{"height":12,"width":14},"text":{"inner":"click me"}}' + }, 'Evaluation with config was correct.'); + assert.deepEqual(client.getTreatmentWithConfig('split_with_config', () => { }), { + treatment: 'control', + config: null + }, 'Evaluation with config returned control state for invalid input.'); + + assert.deepEqual(client.getTreatments(['always_on', 'always_off']), { always_on: 'on', always_off: 'off' }, 'Evaluations were correct.'); + assert.deepEqual(client.getTreatments(['always_on', 'always_off', null], () => { }), { always_on: 'control', always_off: 'control' }, 'We should return map of controls with invalid input.'); + + assert.deepEqual(client.getTreatmentsWithConfig(['split_with_config', 'always_on', null]), + { + split_with_config: { + treatment: 'o.n', + config: '{"color":"brown","dimensions":{"height":12,"width":14},"text":{"inner":"click me"}}' + }, + always_on: { + treatment: 'on', + config: null + } + } + , 'Evaluations with config were correct.'); + assert.deepEqual(client.getTreatmentsWithConfig(['split_with_config', 'always_on', null], () => { }), + { + split_with_config: { + treatment: 'control', + config: null + }, + always_on: { + treatment: 'control', + config: null + } + }, + 'Evaluations with config returned control states for invalid input.'); - finish.next(); + assert.equal(client.track('someTT', 'someEvent'), true, 'Event was queued'); + assert.equal(client.track('someTT', null), false, 'Invalid input.'); - return 200; }); - assert.equal(client.getTreatment('always_on'), 'control', 'Non ready usage.'); - - await client.ready(); - - // treatments and results are only validated so we know for sure when the function was actually running to compare the metrics. - assert.equal(client.getTreatment('always_on'), 'on', 'Evaluation was correct.'); - assert.equal(client.getTreatment('always_on', () => { }), 'control', 'We should return control with invalid input.'); - - assert.deepEqual(client.getTreatmentWithConfig('split_with_config'), { - treatment: 'o.n', - config: '{"color":"brown","dimensions":{"height":12,"width":14},"text":{"inner":"click me"}}' - }, 'Evaluation with config was correct.'); - assert.deepEqual(client.getTreatmentWithConfig('split_with_config', () => { }), { - treatment: 'control', - config: null - }, 'Evaluation with config returned control state for invalid input.'); - - assert.deepEqual(client.getTreatments(['always_on', 'always_off']), { always_on: 'on', always_off: 'off' }, 'Evaluations were correct.'); - assert.deepEqual(client.getTreatments(['always_on', 'always_off', null], () => { }), { always_on: 'control', always_off: 'control' }, 'We should return map of controls with invalid input.'); - - assert.deepEqual(client.getTreatmentsWithConfig(['split_with_config', 'always_on', null]), - { - split_with_config: { - treatment: 'o.n', - config: '{"color":"brown","dimensions":{"height":12,"width":14},"text":{"inner":"click me"}}' - }, - always_on: { - treatment: 'on', - config: null - } - } - , 'Evaluations with config were correct.'); - assert.deepEqual(client.getTreatmentsWithConfig(['split_with_config', 'always_on', null], () => { }), - { - split_with_config: { - treatment: 'control', - config: null - }, - always_on: { - treatment: 'control', - config: null - } + // Flag sets + const baseConfig = { + core: { + authorizationKey: '', + key: 'nicolas@split.io' }, - 'Evaluations with config returned control states for invalid input.'); - - assert.equal(client.track('someTT', 'someEvent'), true, 'Event was queued'); - assert.equal(client.track('someTT', null), false, 'Invalid input.'); + urls: baseUrls, + scheduler: { + telemetryRefreshRate: 60 + }, + streamingEnabled: false + }; + + t.test(async (assert) => { + let factory; + const splitFilters = [{ type: 'bySet', values: ['a', '_b', 'a', 'a', 'c', 'd', '_d'] }]; + + fetchMock.get(baseUrls.sdk + '/mySegments/nicolas%40split.io', { status: 200, body: { 'mySegments': [] } }); + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=-1&sets=a,c,d', { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); + fetchMock.postOnce(baseUrls.telemetry + '/v1/metrics/usage', 200); + fetchMock.postOnce(baseUrls.telemetry + '/v1/metrics/config', (url, opts) => { + const data = JSON.parse(opts.body); + + assert.true(data.tR > 0, 'timeUntilReady is larger than 0'); + assert.equal(data.fsT, 7, 'unique flag sets total should be 7'); + assert.equal(data.fsI, 4, 'flagset ignored should be 4'); + factory.client().destroy().then(() => { + assert.end(); + }); + + return 200; + }); + + factory = SplitFactoryForTest({...baseConfig, sync: {splitFilters}}); + + }, 'SDK with sets configured has sets information in config POST'); + + t.test(async (assert) => { + assert.plan(8); + let factory; + const splitFilters = [{ type: 'bySet', values: ['a', 'b'] }]; + + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=-1&sets=a,c,d', { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); + fetchMock.postOnce(baseUrls.telemetry + '/v1/metrics/usage', (url, opts) => { + const data = JSON.parse(opts.body); + + assert.deepEqual(data.mL.tf, [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], 'Latencies stats'); + assert.deepEqual(data.mL.tfs, [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], 'Latencies stats'); + assert.deepEqual(data.mL.tcf, [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], 'Latencies stats'); + assert.deepEqual(data.mL.tcfs, [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], 'Latencies stats'); + + factory.client().destroy().then(() => { + assert.end(); + }); + + return 200; + }); + fetchMock.postOnce(baseUrls.telemetry + '/v1/metrics/usage', 200); + + factory = SplitFactoryForTest({...baseConfig, sync: {splitFilters}}); + const client = factory.client(); + assert.deepEqual(client.getTreatmentsByFlagSet('a'),[]); + assert.deepEqual(client.getTreatmentsByFlagSets(['a']),[]); + assert.deepEqual(client.getTreatmentsWithConfigByFlagSet('a'),[]); + assert.deepEqual(client.getTreatmentsWithConfigByFlagSets(['a']),[]); + + }, 'SDK with sets configured has evaluation by sets telemetry in stats POST'); } diff --git a/src/__tests__/mocks/message.SPLIT_UPDATE.FS.1.json b/src/__tests__/mocks/message.SPLIT_UPDATE.FS.1.json new file mode 100644 index 000000000..a8d69166b --- /dev/null +++ b/src/__tests__/mocks/message.SPLIT_UPDATE.FS.1.json @@ -0,0 +1,4 @@ +{ + "type": "message", + "data": "{\"id\":\"m2T85LA4fQ:0:0\",\"clientId\":\"pri:NzIyNjY1MzI4\",\"timestamp\":1694548198907,\"encoding\":\"json\",\"channel\":\"NzM2MDI5Mzc0_MTgyNTg1MTgwNg==_splits\",\"data\":\"{\\\"type\\\":\\\"SPLIT_UPDATE\\\",\\\"changeNumber\\\":2,\\\"pcn\\\":1,\\\"c\\\":0,\\\"d\\\":\\\"eyJ0cmFmZmljVHlwZU5hbWUiOiJ1c2VyIiwiaWQiOiJkNDMxY2RkMC1iMGJlLTExZWEtOGE4MC0xNjYwYWRhOWNlMzkiLCJuYW1lIjoibWF1cm9famF2YSIsInRyYWZmaWNBbGxvY2F0aW9uIjoxMDAsInRyYWZmaWNBbGxvY2F0aW9uU2VlZCI6LTkyMzkxNDkxLCJzZWVkIjotMTc2OTM3NzYwNCwic3RhdHVzIjoiQUNUSVZFIiwia2lsbGVkIjpmYWxzZSwiZGVmYXVsdFRyZWF0bWVudCI6Im9mZiIsImNoYW5nZU51bWJlciI6MTYwMjc5OTYzODM0NCwiYWxnbyI6MiwiY29uZmlndXJhdGlvbnMiOnt9LCJzZXRzIjpbXSwiY29uZGl0aW9ucyI6W3siY29uZGl0aW9uVHlwZSI6IldISVRFTElTVCIsIm1hdGNoZXJHcm91cCI6eyJjb21iaW5lciI6IkFORCIsIm1hdGNoZXJzIjpbeyJtYXRjaGVyVHlwZSI6IldISVRFTElTVCIsIm5lZ2F0ZSI6ZmFsc2UsIndoaXRlbGlzdE1hdGNoZXJEYXRhIjp7IndoaXRlbGlzdCI6WyJhZG1pbiIsIm1hdXJvIiwibmljbyJdfX1dfSwicGFydGl0aW9ucyI6W3sidHJlYXRtZW50Ijoib2ZmIiwic2l6ZSI6MTAwfV0sImxhYmVsIjoid2hpdGVsaXN0ZWQifSx7ImNvbmRpdGlvblR5cGUiOiJST0xMT1VUIiwibWF0Y2hlckdyb3VwIjp7ImNvbWJpbmVyIjoiQU5EIiwibWF0Y2hlcnMiOlt7ImtleVNlbGVjdG9yIjp7InRyYWZmaWNUeXBlIjoidXNlciJ9LCJtYXRjaGVyVHlwZSI6IklOX1NFR01FTlQiLCJuZWdhdGUiOmZhbHNlLCJ1c2VyRGVmaW5lZFNlZ21lbnRNYXRjaGVyRGF0YSI6eyJzZWdtZW50TmFtZSI6Im1hdXItMiJ9fV19LCJwYXJ0aXRpb25zIjpbeyJ0cmVhdG1lbnQiOiJvbiIsInNpemUiOjB9LHsidHJlYXRtZW50Ijoib2ZmIiwic2l6ZSI6MTAwfSx7InRyZWF0bWVudCI6IlY0Iiwic2l6ZSI6MH0seyJ0cmVhdG1lbnQiOiJ2NSIsInNpemUiOjB9XSwibGFiZWwiOiJpbiBzZWdtZW50IG1hdXItMiJ9LHsiY29uZGl0aW9uVHlwZSI6IlJPTExPVVQiLCJtYXRjaGVyR3JvdXAiOnsiY29tYmluZXIiOiJBTkQiLCJtYXRjaGVycyI6W3sia2V5U2VsZWN0b3IiOnsidHJhZmZpY1R5cGUiOiJ1c2VyIn0sIm1hdGNoZXJUeXBlIjoiQUxMX0tFWVMiLCJuZWdhdGUiOmZhbHNlfV19LCJwYXJ0aXRpb25zIjpbeyJ0cmVhdG1lbnQiOiJvbiIsInNpemUiOjB9LHsidHJlYXRtZW50Ijoib2ZmIiwic2l6ZSI6MTAwfSx7InRyZWF0bWVudCI6IlY0Iiwic2l6ZSI6MH0seyJ0cmVhdG1lbnQiOiJ2NSIsInNpemUiOjB9XSwibGFiZWwiOiJkZWZhdWx0IHJ1bGUifV19\\\"}\"}" +} diff --git a/src/__tests__/mocks/message.SPLIT_UPDATE.FS.2.json b/src/__tests__/mocks/message.SPLIT_UPDATE.FS.2.json new file mode 100644 index 000000000..2df299899 --- /dev/null +++ b/src/__tests__/mocks/message.SPLIT_UPDATE.FS.2.json @@ -0,0 +1,4 @@ +{ + "type": "message", + "data": "{\"id\":\"m2T85LA4fQ:0:0\",\"clientId\":\"pri:NzIyNjY1MzI4\",\"timestamp\":1694548363040,\"encoding\":\"json\",\"channel\":\"NzM2MDI5Mzc0_MTgyNTg1MTgwNg==_splits\",\"data\":\"{\\\"type\\\":\\\"SPLIT_UPDATE\\\",\\\"changeNumber\\\":2,\\\"pcn\\\":1,\\\"c\\\":0,\\\"d\\\":\\\"eyJ0cmFmZmljVHlwZU5hbWUiOiJjbGllbnQiLCJuYW1lIjoid29ya20iLCJ0cmFmZmljQWxsb2NhdGlvbiI6MTAwLCJ0cmFmZmljQWxsb2NhdGlvblNlZWQiOjE0NzM5MjIyNCwic2VlZCI6NTI0NDE3MTA1LCJzdGF0dXMiOiJBQ1RJVkUiLCJraWxsZWQiOmZhbHNlLCJkZWZhdWx0VHJlYXRtZW50Ijoib24iLCJjaGFuZ2VOdW1iZXIiOjIsImFsZ28iOjIsImNvbmZpZ3VyYXRpb25zIjp7fSwic2V0cyI6WyJzZXRfMSIsInNldF8yIl0sImNvbmRpdGlvbnMiOlt7ImNvbmRpdGlvblR5cGUiOiJST0xMT1VUIiwibWF0Y2hlckdyb3VwIjp7ImNvbWJpbmVyIjoiQU5EIiwibWF0Y2hlcnMiOlt7ImtleVNlbGVjdG9yIjp7InRyYWZmaWNUeXBlIjoiY2xpZW50IiwiYXR0cmlidXRlIjpudWxsfSwibWF0Y2hlclR5cGUiOiJJTl9TRUdNRU5UIiwibmVnYXRlIjpmYWxzZSwidXNlckRlZmluZWRTZWdtZW50TWF0Y2hlckRhdGEiOnsic2VnbWVudE5hbWUiOiJuZXdfc2VnbWVudCJ9LCJ3aGl0ZWxpc3RNYXRjaGVyRGF0YSI6bnVsbCwidW5hcnlOdW1lcmljTWF0Y2hlckRhdGEiOm51bGwsImJldHdlZW5NYXRjaGVyRGF0YSI6bnVsbCwiYm9vbGVhbk1hdGNoZXJEYXRhIjpudWxsLCJkZXBlbmRlbmN5TWF0Y2hlckRhdGEiOm51bGwsInN0cmluZ01hdGNoZXJEYXRhIjpudWxsfV19LCJwYXJ0aXRpb25zIjpbeyJ0cmVhdG1lbnQiOiJvbiIsInNpemUiOjB9LHsidHJlYXRtZW50Ijoib2ZmIiwic2l6ZSI6MH0seyJ0cmVhdG1lbnQiOiJmcmVlIiwic2l6ZSI6MTAwfSx7InRyZWF0bWVudCI6ImNvbnRhIiwic2l6ZSI6MH1dLCJsYWJlbCI6ImluIHNlZ21lbnQgbmV3X3NlZ21lbnQifSx7ImNvbmRpdGlvblR5cGUiOiJST0xMT1VUIiwibWF0Y2hlckdyb3VwIjp7ImNvbWJpbmVyIjoiQU5EIiwibWF0Y2hlcnMiOlt7ImtleVNlbGVjdG9yIjp7InRyYWZmaWNUeXBlIjoiY2xpZW50IiwiYXR0cmlidXRlIjpudWxsfSwibWF0Y2hlclR5cGUiOiJBTExfS0VZUyIsIm5lZ2F0ZSI6ZmFsc2UsInVzZXJEZWZpbmVkU2VnbWVudE1hdGNoZXJEYXRhIjpudWxsLCJ3aGl0ZWxpc3RNYXRjaGVyRGF0YSI6bnVsbCwidW5hcnlOdW1lcmljTWF0Y2hlckRhdGEiOm51bGwsImJldHdlZW5NYXRjaGVyRGF0YSI6bnVsbCwiYm9vbGVhbk1hdGNoZXJEYXRhIjpudWxsLCJkZXBlbmRlbmN5TWF0Y2hlckRhdGEiOm51bGwsInN0cmluZ01hdGNoZXJEYXRhIjpudWxsfV19LCJwYXJ0aXRpb25zIjpbeyJ0cmVhdG1lbnQiOiJvbiIsInNpemUiOjEwMH0seyJ0cmVhdG1lbnQiOiJvZmYiLCJzaXplIjowfSx7InRyZWF0bWVudCI6ImZyZWUiLCJzaXplIjowfSx7InRyZWF0bWVudCI6ImNvbnRhIiwic2l6ZSI6MH1dLCJsYWJlbCI6ImRlZmF1bHQgcnVsZSJ9XX0\\\"}\"}" +} diff --git a/src/__tests__/mocks/message.SPLIT_UPDATE.FS.3.json b/src/__tests__/mocks/message.SPLIT_UPDATE.FS.3.json new file mode 100644 index 000000000..d7e1aee32 --- /dev/null +++ b/src/__tests__/mocks/message.SPLIT_UPDATE.FS.3.json @@ -0,0 +1,4 @@ +{ + "type": "message", + "data": "{\"id\":\"m2T85LA4fQ:0:0\",\"clientId\":\"pri:NzIyNjY1MzI4\",\"timestamp\":1694548363039,\"encoding\":\"json\",\"channel\":\"NzM2MDI5Mzc0_MTgyNTg1MTgwNg==_splits\",\"data\":\"{\\\"type\\\":\\\"SPLIT_UPDATE\\\",\\\"changeNumber\\\":3,\\\"pcn\\\":2,\\\"c\\\":0,\\\"d\\\":\\\"eyJ0cmFmZmljVHlwZU5hbWUiOiJjbGllbnQiLCJuYW1lIjoid29ya20iLCJ0cmFmZmljQWxsb2NhdGlvbiI6MTAwLCJ0cmFmZmljQWxsb2NhdGlvblNlZWQiOjE0NzM5MjIyNCwic2VlZCI6NTI0NDE3MTA1LCJzdGF0dXMiOiJBQ1RJVkUiLCJraWxsZWQiOmZhbHNlLCJkZWZhdWx0VHJlYXRtZW50Ijoib24iLCJjaGFuZ2VOdW1iZXIiOjMsImFsZ28iOjIsImNvbmZpZ3VyYXRpb25zIjp7fSwic2V0cyI6WyJzZXRfMSJdLCJjb25kaXRpb25zIjpbeyJjb25kaXRpb25UeXBlIjoiUk9MTE9VVCIsIm1hdGNoZXJHcm91cCI6eyJjb21iaW5lciI6IkFORCIsIm1hdGNoZXJzIjpbeyJrZXlTZWxlY3RvciI6eyJ0cmFmZmljVHlwZSI6ImNsaWVudCIsImF0dHJpYnV0ZSI6bnVsbH0sIm1hdGNoZXJUeXBlIjoiSU5fU0VHTUVOVCIsIm5lZ2F0ZSI6ZmFsc2UsInVzZXJEZWZpbmVkU2VnbWVudE1hdGNoZXJEYXRhIjp7InNlZ21lbnROYW1lIjoibmV3X3NlZ21lbnQifSwid2hpdGVsaXN0TWF0Y2hlckRhdGEiOm51bGwsInVuYXJ5TnVtZXJpY01hdGNoZXJEYXRhIjpudWxsLCJiZXR3ZWVuTWF0Y2hlckRhdGEiOm51bGwsImJvb2xlYW5NYXRjaGVyRGF0YSI6bnVsbCwiZGVwZW5kZW5jeU1hdGNoZXJEYXRhIjpudWxsLCJzdHJpbmdNYXRjaGVyRGF0YSI6bnVsbH1dfSwicGFydGl0aW9ucyI6W3sidHJlYXRtZW50Ijoib24iLCJzaXplIjowfSx7InRyZWF0bWVudCI6Im9mZiIsInNpemUiOjB9LHsidHJlYXRtZW50IjoiZnJlZSIsInNpemUiOjEwMH0seyJ0cmVhdG1lbnQiOiJjb250YSIsInNpemUiOjB9XSwibGFiZWwiOiJpbiBzZWdtZW50IG5ld19zZWdtZW50In0seyJjb25kaXRpb25UeXBlIjoiUk9MTE9VVCIsIm1hdGNoZXJHcm91cCI6eyJjb21iaW5lciI6IkFORCIsIm1hdGNoZXJzIjpbeyJrZXlTZWxlY3RvciI6eyJ0cmFmZmljVHlwZSI6ImNsaWVudCIsImF0dHJpYnV0ZSI6bnVsbH0sIm1hdGNoZXJUeXBlIjoiQUxMX0tFWVMiLCJuZWdhdGUiOmZhbHNlLCJ1c2VyRGVmaW5lZFNlZ21lbnRNYXRjaGVyRGF0YSI6bnVsbCwid2hpdGVsaXN0TWF0Y2hlckRhdGEiOm51bGwsInVuYXJ5TnVtZXJpY01hdGNoZXJEYXRhIjpudWxsLCJiZXR3ZWVuTWF0Y2hlckRhdGEiOm51bGwsImJvb2xlYW5NYXRjaGVyRGF0YSI6bnVsbCwiZGVwZW5kZW5jeU1hdGNoZXJEYXRhIjpudWxsLCJzdHJpbmdNYXRjaGVyRGF0YSI6bnVsbH1dfSwicGFydGl0aW9ucyI6W3sidHJlYXRtZW50Ijoib24iLCJzaXplIjoxMDB9LHsidHJlYXRtZW50Ijoib2ZmIiwic2l6ZSI6MH0seyJ0cmVhdG1lbnQiOiJmcmVlIiwic2l6ZSI6MH0seyJ0cmVhdG1lbnQiOiJjb250YSIsInNpemUiOjB9XSwibGFiZWwiOiJkZWZhdWx0IHJ1bGUifV19\\\"}\"}" +} diff --git a/src/__tests__/mocks/message.SPLIT_UPDATE.FS.4.json b/src/__tests__/mocks/message.SPLIT_UPDATE.FS.4.json new file mode 100644 index 000000000..0806febf2 --- /dev/null +++ b/src/__tests__/mocks/message.SPLIT_UPDATE.FS.4.json @@ -0,0 +1,4 @@ +{ + "type": "message", + "data": "{\"id\":\"m2T85LA4fQ:0:0\",\"clientId\":\"pri:NzIyNjY1MzI4\",\"timestamp\":1694548363039,\"encoding\":\"json\",\"channel\":\"NzM2MDI5Mzc0_MTgyNTg1MTgwNg==_splits\",\"data\":\"{\\\"type\\\":\\\"SPLIT_UPDATE\\\",\\\"changeNumber\\\":4,\\\"pcn\\\":3,\\\"c\\\":0,\\\"d\\\":\\\"eyJ0cmFmZmljVHlwZU5hbWUiOiJjbGllbnQiLCJuYW1lIjoid29ya20iLCJ0cmFmZmljQWxsb2NhdGlvbiI6MTAwLCJ0cmFmZmljQWxsb2NhdGlvblNlZWQiOjE0NzM5MjIyNCwic2VlZCI6NTI0NDE3MTA1LCJzdGF0dXMiOiJBQ1RJVkUiLCJraWxsZWQiOmZhbHNlLCJkZWZhdWx0VHJlYXRtZW50Ijoib24iLCJjaGFuZ2VOdW1iZXIiOjQsImFsZ28iOjIsImNvbmZpZ3VyYXRpb25zIjp7fSwic2V0cyI6WyJzZXRfMyJdLCJjb25kaXRpb25zIjpbeyJjb25kaXRpb25UeXBlIjoiUk9MTE9VVCIsIm1hdGNoZXJHcm91cCI6eyJjb21iaW5lciI6IkFORCIsIm1hdGNoZXJzIjpbeyJrZXlTZWxlY3RvciI6eyJ0cmFmZmljVHlwZSI6ImNsaWVudCIsImF0dHJpYnV0ZSI6bnVsbH0sIm1hdGNoZXJUeXBlIjoiSU5fU0VHTUVOVCIsIm5lZ2F0ZSI6ZmFsc2UsInVzZXJEZWZpbmVkU2VnbWVudE1hdGNoZXJEYXRhIjp7InNlZ21lbnROYW1lIjoibmV3X3NlZ21lbnQifSwid2hpdGVsaXN0TWF0Y2hlckRhdGEiOm51bGwsInVuYXJ5TnVtZXJpY01hdGNoZXJEYXRhIjpudWxsLCJiZXR3ZWVuTWF0Y2hlckRhdGEiOm51bGwsImJvb2xlYW5NYXRjaGVyRGF0YSI6bnVsbCwiZGVwZW5kZW5jeU1hdGNoZXJEYXRhIjpudWxsLCJzdHJpbmdNYXRjaGVyRGF0YSI6bnVsbH1dfSwicGFydGl0aW9ucyI6W3sidHJlYXRtZW50Ijoib24iLCJzaXplIjowfSx7InRyZWF0bWVudCI6Im9mZiIsInNpemUiOjB9LHsidHJlYXRtZW50IjoiZnJlZSIsInNpemUiOjEwMH0seyJ0cmVhdG1lbnQiOiJjb250YSIsInNpemUiOjB9XSwibGFiZWwiOiJpbiBzZWdtZW50IG5ld19zZWdtZW50In0seyJjb25kaXRpb25UeXBlIjoiUk9MTE9VVCIsIm1hdGNoZXJHcm91cCI6eyJjb21iaW5lciI6IkFORCIsIm1hdGNoZXJzIjpbeyJrZXlTZWxlY3RvciI6eyJ0cmFmZmljVHlwZSI6ImNsaWVudCIsImF0dHJpYnV0ZSI6bnVsbH0sIm1hdGNoZXJUeXBlIjoiQUxMX0tFWVMiLCJuZWdhdGUiOmZhbHNlLCJ1c2VyRGVmaW5lZFNlZ21lbnRNYXRjaGVyRGF0YSI6bnVsbCwid2hpdGVsaXN0TWF0Y2hlckRhdGEiOm51bGwsInVuYXJ5TnVtZXJpY01hdGNoZXJEYXRhIjpudWxsLCJiZXR3ZWVuTWF0Y2hlckRhdGEiOm51bGwsImJvb2xlYW5NYXRjaGVyRGF0YSI6bnVsbCwiZGVwZW5kZW5jeU1hdGNoZXJEYXRhIjpudWxsLCJzdHJpbmdNYXRjaGVyRGF0YSI6bnVsbH1dfSwicGFydGl0aW9ucyI6W3sidHJlYXRtZW50Ijoib24iLCJzaXplIjoxMDB9LHsidHJlYXRtZW50Ijoib2ZmIiwic2l6ZSI6MH0seyJ0cmVhdG1lbnQiOiJmcmVlIiwic2l6ZSI6MH0seyJ0cmVhdG1lbnQiOiJjb250YSIsInNpemUiOjB9XSwibGFiZWwiOiJkZWZhdWx0IHJ1bGUifV19\\\"}\"}" +} diff --git a/src/__tests__/mocks/message.SPLIT_UPDATE.FS.4None.json b/src/__tests__/mocks/message.SPLIT_UPDATE.FS.4None.json new file mode 100644 index 000000000..ef90c8f9a --- /dev/null +++ b/src/__tests__/mocks/message.SPLIT_UPDATE.FS.4None.json @@ -0,0 +1,4 @@ +{ + "type": "message", + "data": "{\"id\":\"m2T85LA4fQ:0:0\",\"clientId\":\"pri:NzIyNjY1MzI4\",\"timestamp\":1694548363039,\"encoding\":\"json\",\"channel\":\"NzM2MDI5Mzc0_MTgyNTg1MTgwNg==_splits\",\"data\":\"{\\\"type\\\":\\\"SPLIT_UPDATE\\\",\\\"changeNumber\\\":4,\\\"pcn\\\":3,\\\"c\\\":0,\\\"d\\\":\\\"eyJ0cmFmZmljVHlwZU5hbWUiOiJjbGllbnQiLCJuYW1lIjoid29ya20iLCJ0cmFmZmljQWxsb2NhdGlvbiI6MTAwLCJ0cmFmZmljQWxsb2NhdGlvblNlZWQiOjE0NzM5MjIyNCwic2VlZCI6NTI0NDE3MTA1LCJzdGF0dXMiOiJBQ1RJVkUiLCJraWxsZWQiOmZhbHNlLCJkZWZhdWx0VHJlYXRtZW50Ijoib24iLCJjaGFuZ2VOdW1iZXIiOjUsImFsZ28iOjIsImNvbmZpZ3VyYXRpb25zIjp7fSwic2V0cyI6W10sImNvbmRpdGlvbnMiOlt7ImNvbmRpdGlvblR5cGUiOiJST0xMT1VUIiwibWF0Y2hlckdyb3VwIjp7ImNvbWJpbmVyIjoiQU5EIiwibWF0Y2hlcnMiOlt7ImtleVNlbGVjdG9yIjp7InRyYWZmaWNUeXBlIjoiY2xpZW50IiwiYXR0cmlidXRlIjpudWxsfSwibWF0Y2hlclR5cGUiOiJJTl9TRUdNRU5UIiwibmVnYXRlIjpmYWxzZSwidXNlckRlZmluZWRTZWdtZW50TWF0Y2hlckRhdGEiOnsic2VnbWVudE5hbWUiOiJuZXdfc2VnbWVudCJ9LCJ3aGl0ZWxpc3RNYXRjaGVyRGF0YSI6bnVsbCwidW5hcnlOdW1lcmljTWF0Y2hlckRhdGEiOm51bGwsImJldHdlZW5NYXRjaGVyRGF0YSI6bnVsbCwiYm9vbGVhbk1hdGNoZXJEYXRhIjpudWxsLCJkZXBlbmRlbmN5TWF0Y2hlckRhdGEiOm51bGwsInN0cmluZ01hdGNoZXJEYXRhIjpudWxsfV19LCJwYXJ0aXRpb25zIjpbeyJ0cmVhdG1lbnQiOiJvbiIsInNpemUiOjB9LHsidHJlYXRtZW50Ijoib2ZmIiwic2l6ZSI6MH0seyJ0cmVhdG1lbnQiOiJmcmVlIiwic2l6ZSI6MTAwfSx7InRyZWF0bWVudCI6ImNvbnRhIiwic2l6ZSI6MH1dLCJsYWJlbCI6ImluIHNlZ21lbnQgbmV3X3NlZ21lbnQifSx7ImNvbmRpdGlvblR5cGUiOiJST0xMT1VUIiwibWF0Y2hlckdyb3VwIjp7ImNvbWJpbmVyIjoiQU5EIiwibWF0Y2hlcnMiOlt7ImtleVNlbGVjdG9yIjp7InRyYWZmaWNUeXBlIjoiY2xpZW50IiwiYXR0cmlidXRlIjpudWxsfSwibWF0Y2hlclR5cGUiOiJBTExfS0VZUyIsIm5lZ2F0ZSI6ZmFsc2UsInVzZXJEZWZpbmVkU2VnbWVudE1hdGNoZXJEYXRhIjpudWxsLCJ3aGl0ZWxpc3RNYXRjaGVyRGF0YSI6bnVsbCwidW5hcnlOdW1lcmljTWF0Y2hlckRhdGEiOm51bGwsImJldHdlZW5NYXRjaGVyRGF0YSI6bnVsbCwiYm9vbGVhbk1hdGNoZXJEYXRhIjpudWxsLCJkZXBlbmRlbmN5TWF0Y2hlckRhdGEiOm51bGwsInN0cmluZ01hdGNoZXJEYXRhIjpudWxsfV19LCJwYXJ0aXRpb25zIjpbeyJ0cmVhdG1lbnQiOiJvbiIsInNpemUiOjEwMH0seyJ0cmVhdG1lbnQiOiJvZmYiLCJzaXplIjowfSx7InRyZWF0bWVudCI6ImZyZWUiLCJzaXplIjowfSx7InRyZWF0bWVudCI6ImNvbnRhIiwic2l6ZSI6MH1dLCJsYWJlbCI6ImRlZmF1bHQgcnVsZSJ9XX0\\\"}\"}" +} diff --git a/src/__tests__/mocks/message.SPLIT_UPDATE.FS.5.json b/src/__tests__/mocks/message.SPLIT_UPDATE.FS.5.json new file mode 100644 index 000000000..c03ffddc1 --- /dev/null +++ b/src/__tests__/mocks/message.SPLIT_UPDATE.FS.5.json @@ -0,0 +1,4 @@ +{ + "type": "message", + "data": "{\"id\":\"m2T85LA4fQ:0:0\",\"clientId\":\"pri:NzIyNjY1MzI4\",\"timestamp\":1694548665831,\"encoding\":\"json\",\"channel\":\"NzM2MDI5Mzc0_MTgyNTg1MTgwNg==_splits\",\"data\":\"{\\\"type\\\":\\\"SPLIT_UPDATE\\\",\\\"changeNumber\\\":5,\\\"pcn\\\":4,\\\"c\\\":0,\\\"d\\\":\\\"eyJ0cmFmZmljVHlwZU5hbWUiOiJjbGllbnQiLCJuYW1lIjoid29ya20iLCJ0cmFmZmljQWxsb2NhdGlvbiI6MTAwLCJ0cmFmZmljQWxsb2NhdGlvblNlZWQiOjE0NzM5MjIyNCwic2VlZCI6NTI0NDE3MTA1LCJzdGF0dXMiOiJBQ1RJVkUiLCJraWxsZWQiOmZhbHNlLCJkZWZhdWx0VHJlYXRtZW50Ijoib24iLCJjaGFuZ2VOdW1iZXIiOjUsImFsZ28iOjIsImNvbmZpZ3VyYXRpb25zIjp7fSwic2V0cyI6WyJzZXRfMyIsInNldF80Il0sImNvbmRpdGlvbnMiOlt7ImNvbmRpdGlvblR5cGUiOiJST0xMT1VUIiwibWF0Y2hlckdyb3VwIjp7ImNvbWJpbmVyIjoiQU5EIiwibWF0Y2hlcnMiOlt7ImtleVNlbGVjdG9yIjp7InRyYWZmaWNUeXBlIjoiY2xpZW50IiwiYXR0cmlidXRlIjpudWxsfSwibWF0Y2hlclR5cGUiOiJJTl9TRUdNRU5UIiwibmVnYXRlIjpmYWxzZSwidXNlckRlZmluZWRTZWdtZW50TWF0Y2hlckRhdGEiOnsic2VnbWVudE5hbWUiOiJuZXdfc2VnbWVudCJ9LCJ3aGl0ZWxpc3RNYXRjaGVyRGF0YSI6bnVsbCwidW5hcnlOdW1lcmljTWF0Y2hlckRhdGEiOm51bGwsImJldHdlZW5NYXRjaGVyRGF0YSI6bnVsbCwiYm9vbGVhbk1hdGNoZXJEYXRhIjpudWxsLCJkZXBlbmRlbmN5TWF0Y2hlckRhdGEiOm51bGwsInN0cmluZ01hdGNoZXJEYXRhIjpudWxsfV19LCJwYXJ0aXRpb25zIjpbeyJ0cmVhdG1lbnQiOiJvbiIsInNpemUiOjB9LHsidHJlYXRtZW50Ijoib2ZmIiwic2l6ZSI6MH0seyJ0cmVhdG1lbnQiOiJmcmVlIiwic2l6ZSI6MTAwfSx7InRyZWF0bWVudCI6ImNvbnRhIiwic2l6ZSI6MH1dLCJsYWJlbCI6ImluIHNlZ21lbnQgbmV3X3NlZ21lbnQifSx7ImNvbmRpdGlvblR5cGUiOiJST0xMT1VUIiwibWF0Y2hlckdyb3VwIjp7ImNvbWJpbmVyIjoiQU5EIiwibWF0Y2hlcnMiOlt7ImtleVNlbGVjdG9yIjp7InRyYWZmaWNUeXBlIjoiY2xpZW50IiwiYXR0cmlidXRlIjpudWxsfSwibWF0Y2hlclR5cGUiOiJBTExfS0VZUyIsIm5lZ2F0ZSI6ZmFsc2UsInVzZXJEZWZpbmVkU2VnbWVudE1hdGNoZXJEYXRhIjpudWxsLCJ3aGl0ZWxpc3RNYXRjaGVyRGF0YSI6bnVsbCwidW5hcnlOdW1lcmljTWF0Y2hlckRhdGEiOm51bGwsImJldHdlZW5NYXRjaGVyRGF0YSI6bnVsbCwiYm9vbGVhbk1hdGNoZXJEYXRhIjpudWxsLCJkZXBlbmRlbmN5TWF0Y2hlckRhdGEiOm51bGwsInN0cmluZ01hdGNoZXJEYXRhIjpudWxsfV19LCJwYXJ0aXRpb25zIjpbeyJ0cmVhdG1lbnQiOiJvbiIsInNpemUiOjEwMH0seyJ0cmVhdG1lbnQiOiJvZmYiLCJzaXplIjowfSx7InRyZWF0bWVudCI6ImZyZWUiLCJzaXplIjowfSx7InRyZWF0bWVudCI6ImNvbnRhIiwic2l6ZSI6MH1dLCJsYWJlbCI6ImRlZmF1bHQgcnVsZSJ9XX0\\\"}\"}" +} diff --git a/src/__tests__/mocks/message.SPLIT_UPDATE.FS.kill.json b/src/__tests__/mocks/message.SPLIT_UPDATE.FS.kill.json new file mode 100644 index 000000000..b00248517 --- /dev/null +++ b/src/__tests__/mocks/message.SPLIT_UPDATE.FS.kill.json @@ -0,0 +1,4 @@ +{ + "type": "message", + "data": "{\"id\":\"-OT-rGuSwz:0:0\",\"clientId\":\"NDEzMTY5Mzg0MA==:NDIxNjU0NTUyNw==\",\"timestamp\":1694549324214,\"encoding\":\"json\",\"channel\":\"NzM2MDI5Mzc0_MTgyNTg1MTgwNg==_splits\",\"data\":\"{\\\"type\\\":\\\"SPLIT_KILL\\\",\\\"changeNumber\\\":5,\\\"defaultTreatment\\\":\\\"off\\\",\\\"splitName\\\":\\\"workm\\\"}\"}" +} diff --git a/src/__tests__/mocks/splitchanges.since.-1.till.1602796638344.json b/src/__tests__/mocks/splitchanges.since.-1.till.1602796638344.json new file mode 100644 index 000000000..1c69f3b8a --- /dev/null +++ b/src/__tests__/mocks/splitchanges.since.-1.till.1602796638344.json @@ -0,0 +1,150 @@ +{ + "splits": [ + { + "trafficTypeName": "client", + "name": "workm", + "trafficAllocation": 100, + "trafficAllocationSeed": 147392224, + "seed": 524417105, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "on", + "changeNumber": 1602796638344, + "algo": 2, + "configurations": {}, + "sets": ["set_1", "set_2"], + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { "trafficType": "client", "attribute": null }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "new_segment" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { "treatment": "on", "size": 0 }, + { "treatment": "off", "size": 0 }, + { "treatment": "free", "size": 100 }, + { "treatment": "conta", "size": 0 } + ], + "label": "in segment new_segment" + }, + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { "trafficType": "client", "attribute": null }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { "treatment": "on", "size": 100 }, + { "treatment": "off", "size": 0 }, + { "treatment": "free", "size": 0 }, + { "treatment": "conta", "size": 0 } + ], + "label": "default rule" + } + ] + }, + { + "trafficTypeName": "client", + "name": "workm_set_3", + "trafficAllocation": 100, + "trafficAllocationSeed": 147392224, + "seed": 524417105, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "on", + "changeNumber": 1602796638344, + "algo": 2, + "configurations": {}, + "sets": ["set_3"], + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { "trafficType": "client", "attribute": null }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "new_segment" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { "treatment": "on", "size": 0 }, + { "treatment": "off", "size": 0 }, + { "treatment": "free", "size": 100 }, + { "treatment": "conta", "size": 0 } + ], + "label": "in segment new_segment" + }, + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { "trafficType": "client", "attribute": null }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { "treatment": "on", "size": 100 }, + { "treatment": "off", "size": 0 }, + { "treatment": "free", "size": 0 }, + { "treatment": "conta", "size": 0 } + ], + "label": "default rule" + } + ] + } + ], + "since": -1, + "till": 1602796638344 +} diff --git a/src/__tests__/mocks/splitchanges.since.1602796638344.till.1602797638344.json b/src/__tests__/mocks/splitchanges.since.1602796638344.till.1602797638344.json new file mode 100644 index 000000000..5ec998208 --- /dev/null +++ b/src/__tests__/mocks/splitchanges.since.1602796638344.till.1602797638344.json @@ -0,0 +1,78 @@ +{ + "splits": [ + { + "trafficTypeName": "client", + "name": "workm", + "trafficAllocation": 100, + "trafficAllocationSeed": 147392224, + "seed": 524417105, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "on", + "changeNumber": 1602797638344, + "algo": 2, + "configurations": {}, + "sets": ["set_1"], + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { "trafficType": "client", "attribute": null }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "new_segment" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { "treatment": "on", "size": 0 }, + { "treatment": "off", "size": 0 }, + { "treatment": "free", "size": 100 }, + { "treatment": "conta", "size": 0 } + ], + "label": "in segment new_segment" + }, + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { "trafficType": "client", "attribute": null }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { "treatment": "on", "size": 100 }, + { "treatment": "off", "size": 0 }, + { "treatment": "free", "size": 0 }, + { "treatment": "conta", "size": 0 } + ], + "label": "default rule" + } + ] + } + ], + "since": 1602796638344, + "till": 1602797638344 +} diff --git a/src/__tests__/mocks/splitchanges.since.1602797638344.till.1602798638344.json b/src/__tests__/mocks/splitchanges.since.1602797638344.till.1602798638344.json new file mode 100644 index 000000000..9384d1c17 --- /dev/null +++ b/src/__tests__/mocks/splitchanges.since.1602797638344.till.1602798638344.json @@ -0,0 +1,78 @@ +{ + "splits": [ + { + "trafficTypeName": "client", + "name": "workm", + "trafficAllocation": 100, + "trafficAllocationSeed": 147392224, + "seed": 524417105, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "on", + "changeNumber": 1602798638344, + "algo": 2, + "configurations": {}, + "sets": ["set_3"], + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { "trafficType": "client", "attribute": null }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "new_segment" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { "treatment": "on", "size": 0 }, + { "treatment": "off", "size": 0 }, + { "treatment": "free", "size": 100 }, + { "treatment": "conta", "size": 0 } + ], + "label": "in segment new_segment" + }, + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { "trafficType": "client", "attribute": null }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { "treatment": "on", "size": 100 }, + { "treatment": "off", "size": 0 }, + { "treatment": "free", "size": 0 }, + { "treatment": "conta", "size": 0 } + ], + "label": "default rule" + } + ] + } + ], + "since": 1602797638344, + "till": 1602798638344 +} diff --git a/src/__tests__/nodeSuites/fetch-specific-splits.spec.js b/src/__tests__/nodeSuites/fetch-specific-splits.spec.js index a8a92b4de..6470b0a32 100644 --- a/src/__tests__/nodeSuites/fetch-specific-splits.spec.js +++ b/src/__tests__/nodeSuites/fetch-specific-splits.spec.js @@ -13,7 +13,7 @@ const baseConfig = { export default function fetchSpecificSplits(fetchMock, assert) { - assert.plan(splitFilters.length); + assert.plan(splitFilters.length+1); for (let i = 0; i < splitFilters.length; i++) { const urls = { sdk: 'https://sdkurl' + i }; @@ -43,4 +43,32 @@ export default function fetchSpecificSplits(fetchMock, assert) { } } + + // Flag sets + assert.test(async (t) => { + + const splitFilters = [{ type: 'bySet', values: ['set_x ', 'set_x', 'set_3', 'set_2', 'set_3', 'set_ww', 'invalid+', '_invalid', '4_valid'] }]; + const baseUrls = { sdk: 'https://sdk.baseurl' }; + + const config = { + ...baseConfig, + urls: baseUrls, + sync: { + splitFilters + } + }; + + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=-1&sets=4_valid,set_2,set_3,set_ww,set_x', async function () { + t.pass('flag set query correctly formed'); + return { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }; + }); + + const factory = SplitFactory(config); + const client = factory.client(); + + client.ready().then(async () => { + await client.destroy(); + t.end(); + }); + }, 'FlagSets config'); } diff --git a/src/__tests__/nodeSuites/flag-sets.spec.js b/src/__tests__/nodeSuites/flag-sets.spec.js new file mode 100644 index 000000000..db857ad6a --- /dev/null +++ b/src/__tests__/nodeSuites/flag-sets.spec.js @@ -0,0 +1,215 @@ +import { SplitFactory } from '../..'; +import { mockSegmentChanges } from '../testUtils'; + + +import splitChange2 from '../mocks/splitChanges.since.-1.till.1602796638344.json'; +import splitChange1 from '../mocks/splitchanges.since.1602796638344.till.1602797638344.json'; +import splitChange0 from '../mocks/splitchanges.since.1602797638344.till.1602798638344.json'; + +const baseUrls = { sdk: 'https://sdk.baseurl' }; + +const baseConfig = { + core: { + authorizationKey: '', + }, + urls: baseUrls, + scheduler: { featuresRefreshRate: 0.01 }, + streamingEnabled: false, +}; + +const key = 'emma'; + +export default function flagSets(fetchMock, t) { + mockSegmentChanges(fetchMock, new RegExp(baseUrls.sdk + '/segmentChanges/*'), ['some_key']); + + t.test(async (assert) => { + assert.plan(7); + let factory, manager, client = []; + + // Receive split change with 1 split belonging to set_1 & set_2 and one belonging to set_3 + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=-1&sets=set_1,set_2', function () { + return { status: 200, body: splitChange2}; + }); + + // Receive split change with 1 split belonging to set_1 only + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1602796638344&sets=set_1,set_2', function () { + // stored feature flags before update + const storedFlags = manager.splits(); + assert.true(storedFlags.length === 1, 'only one feature flag should be added'); + assert.true(storedFlags[0].name === 'workm'); + assert.deepEqual(storedFlags[0].sets, ['set_1','set_2']); + + // send split change + return { status: 200, body: splitChange1}; + }); + + // Receive split change with 1 split belonging to set_3 only + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1602797638344&sets=set_1,set_2', function () { + // stored feature flags before update + const storedFlags = manager.splits(); + assert.true(storedFlags.length === 1); + assert.true(storedFlags[0].name === 'workm'); + assert.deepEqual(storedFlags[0].sets, ['set_1'], 'the feature flag should be updated'); + + // send split change + return { status: 200, body: splitChange0}; + }); + + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1602798638344&sets=set_1,set_2', async function () { + // stored feature flags before update + const storedFlags = manager.splits(); + assert.true(storedFlags.length === 0, 'the feature flag should be removed'); + await client.destroy(); + client = []; + assert.end(); + + return { status: 200, body: {} }; + }); + + // Initialize a factory with polling and sets set_1 & set_2 configured. + const splitFilters = [{ type: 'bySet', values: ['set_1','set_2'] }]; + factory = SplitFactory({ ...baseConfig, sync: { splitFilters }}); + client = factory.client(); + await client.ready(); + manager = factory.manager(); + + }, 'Polling - SDK with sets configured updates flags according to sets'); + + t.test(async (assert) => { + assert.plan(15); + + let factory, manager, client = []; + + // Receive split change with 1 split belonging to set_1 & set_2 and one belonging to set_3 + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=-1', function () { + return { status: 200, body: splitChange2}; + }); + + // Receive split change with 1 split belonging to set_1 only + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1602796638344', function () { + // stored feature flags before update + const storedFlags = manager.splits(); + assert.true(storedFlags.length === 2, 'every feature flag should be added'); + assert.true(storedFlags[0].name === 'workm'); + assert.true(storedFlags[1].name === 'workm_set_3'); + assert.deepEqual(storedFlags[0].sets, ['set_1','set_2']); + assert.deepEqual(storedFlags[1].sets, ['set_3']); + + // send split change + return { status: 200, body: splitChange1}; + }); + + // Receive split change with 1 split belonging to set_3 only + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1602797638344', function () { + // stored feature flags before update + const storedFlags = manager.splits(); + assert.true(storedFlags.length === 2); + assert.true(storedFlags[0].name === 'workm'); + assert.true(storedFlags[1].name === 'workm_set_3'); + assert.deepEqual(storedFlags[0].sets, ['set_1'], 'the feature flag should be updated'); + assert.deepEqual(storedFlags[1].sets, ['set_3'], 'the feature flag should remain as it was'); + + // send split change + return { status: 200, body: splitChange0}; + }); + + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1602798638344', async function () { + // stored feature flags before update + const storedFlags = manager.splits(); + assert.true(storedFlags.length === 2); + assert.true(storedFlags[0].name === 'workm'); + assert.true(storedFlags[1].name === 'workm_set_3'); + assert.deepEqual(storedFlags[0].sets, ['set_3'], 'the feature flag should be updated'); + assert.deepEqual(storedFlags[1].sets, ['set_3'], 'the feature flag should remain as it was'); + await client.destroy(); + assert.end(); + return { status: 200, body: {} }; + }); + + // Initialize a factory with polling and no sets configured. + factory = SplitFactory(baseConfig); + client = factory.client(); + await client.ready(); + manager = factory.manager(); + + }, 'Poling - SDK with no sets configured does not take sets into account when updating flags'); + + // EVALUATION + + t.test(async (assert) => { + fetchMock.reset(); + assert.plan(8); + + let factory, client = []; + + mockSegmentChanges(fetchMock, new RegExp(baseUrls.sdk + '/segmentChanges/*'), []); + fetchMock.post('*', 200); + // Receive split change with 1 split belonging to set_1 & set_2 and one belonging to set_3 + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=-1&sets=set_1', function () { + return { status: 200, body: splitChange2}; + }); + + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1602796638344&sets=set_1', async function () { + // stored feature flags before update + assert.deepEqual(client.getTreatmentsByFlagSet(key, 'set_1'), {workm: 'on'}, 'only the flag in set_1 can be evaluated'); + assert.deepEqual(client.getTreatmentsByFlagSet(key, 'set_2'), {}, 'only the flag in set_1 can be evaluated'); + assert.deepEqual(client.getTreatmentsByFlagSet(key, 'set_3'), {}, 'only the flag in set_1 can be evaluated'); + assert.deepEqual(client.getTreatmentsWithConfigByFlagSet(key, 'set_1'), { workm: { treatment: 'on', config: null } }, 'only the flag in set_1 can be evaluated'); + assert.deepEqual(client.getTreatmentsWithConfigByFlagSet(key, 'set_2'), {}, 'only the flag in set_1 can be evaluated'); + assert.deepEqual(client.getTreatmentsWithConfigByFlagSet(key, 'set_3'), {}, 'only the flag in set_1 can be evaluated'); + assert.deepEqual(client.getTreatmentsByFlagSets(key, ['set_1','set_2','set_3']), {workm: 'on'}, 'only the flag in set_1 can be evaluated'); + assert.deepEqual(client.getTreatmentsWithConfigByFlagSets(key, ['set_1','set_2','set_3']), { workm: { treatment: 'on', config: null } }, 'only the flag in set_1 can be evaluated'); + await client.destroy(); + assert.end(); + + // send split change + return { status: 200, body: splitChange1}; + }); + + // Initialize a factory with set_1 configured. + const splitFilters = [{ type: 'bySet', values: ['set_1'] }]; + factory = SplitFactory({ ...baseConfig, sync: { splitFilters }}); + client = factory.client(); + await client.ready(); + + }, 'SDK with sets configured can only evaluate configured sets'); + + t.test(async (assert) => { + fetchMock.reset(); + assert.plan(8); + + let factory, client = []; + + mockSegmentChanges(fetchMock, new RegExp(baseUrls.sdk + '/segmentChanges/*'), []); + fetchMock.post('*', 200); + + // Receive split change with 1 split belonging to set_1 & set_2 and one belonging to set_3 + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=-1', function () { + return { status: 200, body: splitChange2}; + }); + + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1602796638344', async function () { + // stored feature flags before update + assert.deepEqual(client.getTreatmentsByFlagSet(key, 'set_1'), {workm: 'on'}, 'all flags can be evaluated'); + assert.deepEqual(client.getTreatmentsByFlagSet(key, 'set_2'), {workm: 'on'}, 'all flags can be evaluated'); + assert.deepEqual(client.getTreatmentsByFlagSet(key, 'set_3'), { workm_set_3: 'on' }, 'all flags can be evaluated'); + assert.deepEqual(client.getTreatmentsWithConfigByFlagSet(key, 'set_1'), { workm: { treatment: 'on', config: null } }, 'all flags can be evaluated'); + assert.deepEqual(client.getTreatmentsWithConfigByFlagSet(key, 'set_2'), { workm: { treatment: 'on', config: null } }, 'all flags can be evaluated'); + assert.deepEqual(client.getTreatmentsWithConfigByFlagSet(key, 'set_3'), { workm_set_3: { treatment: 'on', config: null } }, 'all flags can be evaluated'); + assert.deepEqual(client.getTreatmentsByFlagSets(key, ['set_1','set_2','set_3']), { workm: 'on', workm_set_3: 'on' }, 'all flags can be evaluated'); + assert.deepEqual(client.getTreatmentsWithConfigByFlagSets(key, ['set_1','set_2','set_3']), { workm: { treatment: 'on', config: null }, workm_set_3: { treatment: 'on', config: null } }, 'all flags can be evaluated'); + await client.destroy(); + assert.end(); + + // send split change + return { status: 200, body: splitChange1}; + }); + + + factory = SplitFactory(baseConfig); + client = factory.client(); + await client.ready(); + + }, 'SDK with no sets configured can evaluate any set'); + +} diff --git a/src/__tests__/nodeSuites/push-flag-sets.spec.js b/src/__tests__/nodeSuites/push-flag-sets.spec.js new file mode 100644 index 000000000..d504a5084 --- /dev/null +++ b/src/__tests__/nodeSuites/push-flag-sets.spec.js @@ -0,0 +1,267 @@ +import { SplitFactory } from '../..'; +import EventSourceMock, { setMockListener } from '../testUtils/eventSourceMock'; +import { __setEventSource } from '../../platform/getEventSource/node'; +import { mockSegmentChanges } from '../testUtils'; + +import notification1 from '../mocks/message.SPLIT_UPDATE.FS.1.json'; +import notification2 from '../mocks/message.SPLIT_UPDATE.FS.2.json'; +import notification3 from '../mocks/message.SPLIT_UPDATE.FS.3.json'; +import notification4None from '../mocks/message.SPLIT_UPDATE.FS.4None.json'; +import notification4 from '../mocks/message.SPLIT_UPDATE.FS.4.json'; +import notification5 from '../mocks/message.SPLIT_UPDATE.FS.5.json'; +import notificationKill from '../mocks/message.SPLIT_UPDATE.FS.kill.json'; + +import authPushEnabled from '../mocks/auth.pushEnabled.node.json'; + +const baseUrls = { + sdk: 'https://sdk.baseurl', + auth: 'https://auth.baseurl/api' +}; + +const baseConfig = { + core: { + authorizationKey: '', + }, + urls: baseUrls, +}; + +const MILLIS_FIRST_SPLIT_UPDATE_EVENT = 100; +const MILLIS_SECOND_SPLIT_UPDATE_EVENT = 200; +const MILLIS_THIRD_SPLIT_UPDATE_EVENT = 300; +const MILLIS_FOURTH_SPLIT_UPDATE_EVENT = 400; + +export function testFlagSets(fetchMock, t) { + fetchMock.reset(); + __setEventSource(EventSourceMock); + + mockSegmentChanges(fetchMock, new RegExp(baseUrls.sdk + '/segmentChanges/*'), ['some-key']); + fetchMock.post('*', 200); + + fetchMock.get(baseUrls.auth + '/v2/auth', function (url, opts) { + if (!opts.headers['Authorization']) t.fail('`/v2/auth` request must include `Authorization` header'); + t.pass('auth success'); + return { status: 200, body: authPushEnabled }; + }); + fetchMock.get(baseUrls.sdk + '/splitChanges?since=-1', function () { + return { status: 200, body: { splits: [], since: -1, till: 0}}; + }); + fetchMock.get(baseUrls.sdk + '/splitChanges?since=0', function () { + return { status: 200, body: { splits: [], since: 0, till: 1 }}; + }); + fetchMock.get(baseUrls.sdk + '/splitChanges?since=-1&sets=set_1,set_2', function () { + return { status: 200, body: { splits: [], since: -1, till: 0 }}; + }); + fetchMock.get(baseUrls.sdk + '/splitChanges?since=0&sets=set_1,set_2', function () { + return { status: 200, body: { splits: [], since: 0, till: 1 }}; + }); + + const configWithSets = { + ...baseConfig, + sync: { + splitFilters: [{type: 'bySet', values: ['set_1', 'set_2']}] + } + }; + + t.test(async (assert) => { + + assert.plan(3); + let splitio, client, manager = []; + + setMockListener((eventSourceInstance) => { + eventSourceInstance.emitOpen(); + + setTimeout(() => { + assert.deepEqual(manager.splits(), [], '1 - initialized without flags'); + client.once(client.Event.SDK_UPDATE, async () => { + assert.equal(manager.splits().length, 1, '1 - update is processed and the flag is stored'); + await client.destroy(); + assert.equal(eventSourceInstance.readyState, EventSourceMock.CLOSED, '1 - streaming is closed after destroy'); + assert.end(); + }); + eventSourceInstance.emitMessage(notification1); + }, MILLIS_FIRST_SPLIT_UPDATE_EVENT); + + }); + + splitio = SplitFactory(baseConfig); + client = splitio.client(); + manager = splitio.manager(); + + }, 'SDK with no sets configured does not exclude updates'); + + t.test(async (assert) => { + + assert.plan(5); + let splitio, client, manager = []; + + setMockListener((eventSourceInstance) => { + eventSourceInstance.emitOpen(); + + setTimeout(() => { + assert.deepEqual(manager.splits(), [], '2 - initialized without flags'); + // Receive a SPLIT_UPDATE with "sets":["set_1", "set_2"] + client.once(client.Event.SDK_UPDATE, () => { + assert.deepEqual(manager.split('workm').sets, ['set_1', 'set_2'], '2 - update is processed and the flag is stored'); + }); + eventSourceInstance.emitMessage(notification2); + }, MILLIS_FIRST_SPLIT_UPDATE_EVENT); + + setTimeout(() => { + // Receive a SPLIT_UPDATE with "sets":["set_1"] + client.once(client.Event.SDK_UPDATE, () => { + assert.deepEqual(manager.split('workm').sets, ['set_1'], '2 - update is processed and the flag is updated'); + }); + eventSourceInstance.emitMessage(notification3); + }, MILLIS_SECOND_SPLIT_UPDATE_EVENT); + + setTimeout(() => { + // Receive a SPLIT_UPDATE with "sets":[] + client.once(client.Event.SDK_UPDATE, async () => { + assert.deepEqual(manager.splits().length, 0, '2 - update is processed and the flag is removed'); + await client.destroy(); + assert.equal(eventSourceInstance.readyState, EventSourceMock.CLOSED, '2 - streaming is closed after destroy'); + assert.end(); + }); + eventSourceInstance.emitMessage(notification4None); + }, MILLIS_THIRD_SPLIT_UPDATE_EVENT); + + }); + + splitio = SplitFactory(configWithSets); + client = splitio.client(); + manager = splitio.manager(); + + }, 'SDK with sets configured deletes flag when change with empty sets is received'); + + t.test(async (assert) => { + + assert.plan(6); + let splitio, client, manager = []; + + setMockListener((eventSourceInstance) => { + eventSourceInstance.emitOpen(); + + setTimeout(() => { + assert.deepEqual(manager.splits(), [], '3 - initialized without flags'); + // Receive a SPLIT_UPDATE with "sets":["set_1", "set_2"] + client.once(client.Event.SDK_UPDATE, () => { + assert.deepEqual(manager.split('workm').sets, ['set_1', 'set_2'], '3 - update is processed and the flag is stored'); + }); + eventSourceInstance.emitMessage(notification2); + }, MILLIS_FIRST_SPLIT_UPDATE_EVENT); + + setTimeout(() => { + // Receive a SPLIT_UPDATE with "sets":["set_1"] + client.once(client.Event.SDK_UPDATE, () => { + assert.deepEqual(manager.split('workm').sets, ['set_1'], '3 - update is processed and the flag is updated'); + }); + eventSourceInstance.emitMessage(notification3); + }, MILLIS_SECOND_SPLIT_UPDATE_EVENT); + + setTimeout(() => { + // Receive a SPLIT_UPDATE with "sets":["set_3"] + client.once(client.Event.SDK_UPDATE, () => { + assert.deepEqual(manager.splits().length, 0, '3 - update is processed and the flag is removed'); + }); + eventSourceInstance.emitMessage(notification4); + }, MILLIS_THIRD_SPLIT_UPDATE_EVENT); + + setTimeout(() => { + // Receive a SPLIT_UPDATE with "sets":["set_3", "set_4"] + client.once(client.Event.SDK_UPDATE, async () => { + assert.deepEqual(manager.splits().length, 0, '3 - update is processed and flag is not added to the storage'); + await client.destroy(); + assert.equal(eventSourceInstance.readyState, EventSourceMock.CLOSED, '3 - streaming is closed after destroy'); + assert.end(); + }); + eventSourceInstance.emitMessage(notification5); + }, MILLIS_FOURTH_SPLIT_UPDATE_EVENT); + + }); + + splitio = SplitFactory(configWithSets); + client = splitio.client(); + manager = splitio.manager(); + + }, 'SDK with sets configured deletes flag when change with non-matching sets is received'); + + t.test(async (assert) => { + + assert.plan(5); + + let splitio, client, manager = []; + + setMockListener((eventSourceInstance) => { + + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=2&sets=set_1,set_2', async function () { + assert.pass('4 - A fetch is triggered due to the SPLIT_KILL'); + await client.destroy(); + assert.equal(eventSourceInstance.readyState, EventSourceMock.CLOSED, '4 - streaming is closed after destroy'); + assert.end(); + return { status: 200, body: { splits: [], since: 2, till: 3 }}; + }); + + eventSourceInstance.emitOpen(); + + setTimeout(() => { + assert.deepEqual(manager.splits(), [], '4 - initialized without flags'); + // Receive a SPLIT_UPDATE with "sets":["set_1", "set_2"] + client.once(client.Event.SDK_UPDATE, () => { + assert.equal(manager.split('workm').killed, false, '4 - update is processed and the flag is stored'); + }); + eventSourceInstance.emitMessage(notification2); + }, MILLIS_FIRST_SPLIT_UPDATE_EVENT); + + setTimeout(() => { + // Receive a SPLIT_KILL for flag + client.once(client.Event.SDK_UPDATE, async () => { + assert.equal(manager.split('workm').killed, true, '4 - update is processed and the flag is updated'); + + }); + eventSourceInstance.emitMessage(notificationKill); + }, MILLIS_SECOND_SPLIT_UPDATE_EVENT); + + }); + + splitio = SplitFactory(configWithSets); + client = splitio.client(); + manager = splitio.manager(); + await client.ready(); + + }, 'SDK with sets configured updates flag when a SPLIT_KILL is received'); + + t.test(async (assert) => { + assert.plan(4); + + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1&sets=set_1,set_2', function () { + assert.pass('5 - A fetch is triggered due to the SPLIT_KILL'); + return { status: 200, body: { splits: [], since: 1, till: 5 }}; + }); + + let splitio, client, manager = []; + + setMockListener((eventSourceInstance) => { + eventSourceInstance.emitOpen(); + + setTimeout(() => { + assert.deepEqual(manager.splits(), [], '5 - initialized without flags'); + + // Receive a SPLIT_KILL for flag + client.once(client.Event.SDK_UPDATE, async () => { + assert.deepEqual(manager.splits(), [], '5 - storage is not modified since flag is not present. '); + await client.destroy(); + assert.equal(eventSourceInstance.readyState, EventSourceMock.CLOSED, '5 - streaming is closed after destroy'); + assert.end(); + }); + eventSourceInstance.emitMessage(notificationKill); + }, MILLIS_FIRST_SPLIT_UPDATE_EVENT); + + }); + + splitio = SplitFactory(configWithSets); + client = splitio.client(); + manager = splitio.manager(); + await client.ready(); + + }, 'SDK with sets configured does not update flag when a SPLIT_KILL is received for a non-existing flag'); +} diff --git a/src/__tests__/online/browser.spec.js b/src/__tests__/online/browser.spec.js index a96dfe9b7..00218fb53 100644 --- a/src/__tests__/online/browser.spec.js +++ b/src/__tests__/online/browser.spec.js @@ -19,6 +19,7 @@ import readyPromiseSuite from '../browserSuites/ready-promise.spec'; import fetchSpecificSplits from '../browserSuites/fetch-specific-splits.spec'; import userConsent from '../browserSuites/user-consent.spec'; import singleSync from '../browserSuites/single-sync.spec'; +import flagSets from '../browserSuites/flag-sets.spec'; import { settingsFactory } from '../../settings'; import splitChangesMock1 from '../mocks/splitchanges.since.-1.json'; @@ -132,6 +133,8 @@ tape('## E2E CI Tests ##', function (assert) { assert.test('E2E / Fetch specific splits', fetchSpecificSplits.bind(null, fetchMock)); /* Validate single sync */ assert.test('E2E / Single sync', singleSync.bind(null, fetchMock)); + /* Validate flag sets */ + assert.test('E2E / flag sets', flagSets.bind(null, fetchMock)); //If we change the mocks, we need to clear localstorage. Cleaning up after testing ensures "fresh data". localStorage.clear(); diff --git a/src/__tests__/online/node.spec.js b/src/__tests__/online/node.spec.js index 81304cce2..9cb9eb873 100644 --- a/src/__tests__/online/node.spec.js +++ b/src/__tests__/online/node.spec.js @@ -19,6 +19,7 @@ import fetchSpecificSplits from '../nodeSuites/fetch-specific-splits.spec'; import splitChangesMock1 from '../mocks/splitchanges.since.-1.json'; import splitChangesMock2 from '../mocks/splitchanges.since.1457552620999.json'; +import flagSets from '../nodeSuites/flag-sets.spec'; const config = { core: { @@ -83,5 +84,8 @@ tape('## Node JS - E2E CI Tests ##', async function (assert) { /* Validate fetching specific splits */ assert.test('E2E / Fetch specific splits', fetchSpecificSplits.bind(null, fetchMock)); + /* Validate flag sets */ + assert.test('E2E / Flag sets', flagSets.bind(null, fetchMock)); + assert.end(); }); diff --git a/src/__tests__/push/browser.spec.js b/src/__tests__/push/browser.spec.js index f4d510371..2ff84728d 100644 --- a/src/__tests__/push/browser.spec.js +++ b/src/__tests__/push/browser.spec.js @@ -7,6 +7,7 @@ import { testSynchronizationRetries } from '../browserSuites/push-synchronizatio import { testFallbacking } from '../browserSuites/push-fallbacking.spec'; import { testRefreshToken } from '../browserSuites/push-refresh-token.spec'; import { testSplitKillOnReadyFromCache } from '../browserSuites/push-corner-cases.spec'; +import { testFlagSets } from '../browserSuites/push-flag-sets.spec'; fetchMock.config.overwriteRoutes = false; Math.random = () => 0.5; // SDKs without telemetry @@ -38,5 +39,8 @@ tape('## Browser JS - E2E CI Tests for PUSH ##', function (assert) { // Corner cases assert.test('E2E / PUSH corner case: SPLIT_KILL notification must not emit SDK_READY if the SDK is ready from cache', testSplitKillOnReadyFromCache.bind(null, fetchMock)); + // Validate flag sets + assert.test('E2E / PUSH flag sets', testFlagSets.bind(null, fetchMock)); + assert.end(); }); diff --git a/src/__tests__/push/node.spec.js b/src/__tests__/push/node.spec.js index b72949c9e..6345e15db 100644 --- a/src/__tests__/push/node.spec.js +++ b/src/__tests__/push/node.spec.js @@ -6,6 +6,7 @@ import { testSynchronization } from '../nodeSuites/push-synchronization.spec'; import { testSynchronizationRetries } from '../nodeSuites/push-synchronization-retries.spec'; import { testFallbacking } from '../nodeSuites/push-fallbacking.spec'; import { testRefreshToken } from '../nodeSuites/push-refresh-token.spec'; +import { testFlagSets } from '../nodeSuites/push-flag-sets.spec'; fetchMock.config.overwriteRoutes = false; @@ -36,5 +37,8 @@ tape('## Node JS - E2E CI Tests for PUSH ##', async function (assert) { assert.test('E2E / PUSH refresh token and connection delay', testRefreshToken.bind(null, fetchMock)); + /* Validate flag sets */ + assert.test('E2E / PUSH flag sets', testFlagSets.bind(null, fetchMock)); + assert.end(); }); From b9fab93c34f1dfddf26d52ca7833122b55978ab6 Mon Sep 17 00:00:00 2001 From: Emmanuel Zamora Date: Mon, 9 Oct 2023 18:57:40 -0300 Subject: [PATCH 05/38] [SDKS-7567] Add type definitions --- types/splitio.d.ts | 70 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/types/splitio.d.ts b/types/splitio.d.ts index 38f9c2edc..621a11815 100644 --- a/types/splitio.d.ts +++ b/types/splitio.d.ts @@ -1370,7 +1370,7 @@ declare namespace SplitIO { /** * Returns a TreatmentWithConfig value, which an object with both treatment and config string for the given feature. * For usage on the Browser as we defined the key on the settings. - * @function getTreatment + * @function getTreatmentWithConfig * @param {string} featureFlagName - The string that represents the feature flag we want to get the treatment. * @param {Attributes=} attributes - An object of type Attributes defining the attributes for the given key. * @returns {TreatmentWithConfig} The TreatmentWithConfig, the object containing the treatment string and the @@ -1417,6 +1417,74 @@ declare namespace SplitIO { * @returns {TreatmentsWithConfig} The map with all the TreatmentWithConfig objects */ getTreatmentsWithConfig(featureFlagNames: string[], attributes?: Attributes): TreatmentsWithConfig, + /** + * Returns a Treatments value, which is an object map with the treatments for the feature flags related to the given flagSet. + * @function getTreatmentsByFlagSet + * @param {string} key - The string key representing the consumer. + * @param {string} flagSet - The flagSet name we want to get the treatments. + * @param {Attributes=} attributes - An object of type Attributes defining the attributes for the given key. + * @returns {Treatments} The map with all the TreatmentWithConfig objects + */ + getTreatmentsByFlagSet(key: SplitKey, flagSet: string, attributes?: Attributes): Treatments, + /** + * Returns a Treatments value, which is an object map with the treatments for the feature flags related to the given flag set. + * @function getTreatmentsByFlagSet + * @param {string} flagSet - The flag set name we want to get the treatments. + * @param {Attributes=} attributes - An object of type Attributes defining the attributes for the given key. + * @returns {Treatments} The map with all the TreatmentWithConfig objects + */ + getTreatmentsByFlagSet(key: SplitKey, flagSet: string, attributes?: Attributes): Treatments, + /** + * Returns a TreatmentsWithConfig value, which is an object map with the TreatmentWithConfig (an object with both treatment and config string) for the feature flags related to the given flagSets. + * @function getTreatmentsWithConfigByFlagSet + * @param {string} key - The string key representing the consumer. + * @param {string} flagSet - The flagSet name we want to get the treatments. + * @param {Attributes=} attributes - An object of type Attributes defining the attributes for the given key. + * @returns {Treatments} The map with all the TreatmentWithConfig objects + */ + getTreatmentsWithConfigByFlagSet(key: SplitKey, flagSet: string, attributes?: Attributes): TreatmentsWithConfig, + /** + * Returns a TreatmentsWithConfig value, which is an object map with the TreatmentWithConfig (an object with both treatment and config string) for the feature flags related to the given flag set. + * @function getTreatmentsWithConfigByFlagSet + * @param {string} flagSet - The flag set name we want to get the treatments. + * @param {Attributes=} attributes - An object of type Attributes defining the attributes for the given key. + * @returns {Treatments} The map with all the TreatmentWithConfig objects + */ + getTreatmentsWithConfigByFlagSet(flagSet: string, attributes?: Attributes): TreatmentsWithConfig, + /** + * Returns a Returns a Treatments value, which is an object with both treatment and config string for to the feature flags related to the given flagSets. + * @function getTreatmentsByFlagSets + * @param {string} key - The string key representing the consumer. + * @param {Array} flagSets - An array of the flagSet names we want to get the treatments. + * @param {Attributes=} attributes - An object of type Attributes defining the attributes for the given key. + * @returns {Treatments} The map with all the TreatmentWithConfig objects + */ + getTreatmentsByFlagSets(key: SplitKey, flagSets: string[], attributes?: Attributes): Treatments, + /** + * Returns a Returns a Treatments value, which is an object with both treatment and config string for to the feature flags related to the given flag sets. + * @function getTreatmentsByFlagSets + * @param {Array} flagSets - An array of the flag set names we want to get the treatments. + * @param {Attributes=} attributes - An object of type Attributes defining the attributes for the given key. + * @returns {Treatments} The map with all the TreatmentWithConfig objects + */ + getTreatmentsByFlagSets(flagSets: string[], attributes?: Attributes): Treatments, + /** + * Returns a TreatmentsWithConfig value, which is an object map with the TreatmentWithConfig (an object with both treatment and config string) for the feature flags related to the given flagSets. + * @function getTreatmentsWithConfigByFlagSets + * @param {string} key - The string key representing the consumer. + * @param {Array} flagSets - An array of the flagSet names we want to get the treatments. + * @param {Attributes=} attributes - An object of type Attributes defining the attributes for the given key. + * @returns {Treatments} The map with all the TreatmentWithConfig objects + */ + getTreatmentsWithConfigByFlagSets(key: SplitKey, flagSets: string[], attributes?: Attributes): TreatmentsWithConfig, + /** + * Returns a TreatmentsWithConfig value, which is an object map with the TreatmentWithConfig (an object with both treatment and config string) for the feature flags related to the given flag sets. + * @function getTreatmentsWithConfigByFlagSets + * @param {Array} flagSets - An array of the flag set names we want to get the treatments. + * @param {Attributes=} attributes - An object of type Attributes defining the attributes for the given key. + * @returns {Treatments} The map with all the TreatmentWithConfig objects + */ + getTreatmentsWithConfigByFlagSets(flagSets: string[], attributes?: Attributes): TreatmentsWithConfig, /** * Tracks an event to be fed to the results product on Split user interface. * For usage on NodeJS as we don't have only one key. From 933bcf2bff91b0ea051f8a17e06bd4c0fe9115da Mon Sep 17 00:00:00 2001 From: Emmanuel Zamora Date: Tue, 10 Oct 2023 14:48:15 -0300 Subject: [PATCH 06/38] Fix typo in splitChanges file --- src/__tests__/browserSuites/flag-sets.spec.js | 2 +- src/__tests__/nodeSuites/flag-sets.spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/__tests__/browserSuites/flag-sets.spec.js b/src/__tests__/browserSuites/flag-sets.spec.js index cae4fddc1..a821943aa 100644 --- a/src/__tests__/browserSuites/flag-sets.spec.js +++ b/src/__tests__/browserSuites/flag-sets.spec.js @@ -1,6 +1,6 @@ import { SplitFactory } from '../..'; -import splitChange2 from '../mocks/splitChanges.since.-1.till.1602796638344.json'; +import splitChange2 from '../mocks/splitchanges.since.-1.till.1602796638344.json'; import splitChange1 from '../mocks/splitchanges.since.1602796638344.till.1602797638344.json'; import splitChange0 from '../mocks/splitchanges.since.1602797638344.till.1602798638344.json'; diff --git a/src/__tests__/nodeSuites/flag-sets.spec.js b/src/__tests__/nodeSuites/flag-sets.spec.js index db857ad6a..e43f6be76 100644 --- a/src/__tests__/nodeSuites/flag-sets.spec.js +++ b/src/__tests__/nodeSuites/flag-sets.spec.js @@ -2,7 +2,7 @@ import { SplitFactory } from '../..'; import { mockSegmentChanges } from '../testUtils'; -import splitChange2 from '../mocks/splitChanges.since.-1.till.1602796638344.json'; +import splitChange2 from '../mocks/splitchanges.since.-1.till.1602796638344.json'; import splitChange1 from '../mocks/splitchanges.since.1602796638344.till.1602797638344.json'; import splitChange0 from '../mocks/splitchanges.since.1602797638344.till.1602798638344.json'; From 03bff12f72c80635ee6aec9d0cff59c64b180bd5 Mon Sep 17 00:00:00 2001 From: Emmanuel Zamora Date: Tue, 10 Oct 2023 14:54:51 -0300 Subject: [PATCH 07/38] Fix flag sets typo --- types/splitio.d.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/types/splitio.d.ts b/types/splitio.d.ts index 621a11815..1a0cc6f9d 100644 --- a/types/splitio.d.ts +++ b/types/splitio.d.ts @@ -1418,10 +1418,10 @@ declare namespace SplitIO { */ getTreatmentsWithConfig(featureFlagNames: string[], attributes?: Attributes): TreatmentsWithConfig, /** - * Returns a Treatments value, which is an object map with the treatments for the feature flags related to the given flagSet. + * Returns a Treatments value, which is an object map with the treatments for the feature flags related to the given flag set. * @function getTreatmentsByFlagSet * @param {string} key - The string key representing the consumer. - * @param {string} flagSet - The flagSet name we want to get the treatments. + * @param {string} flagSet - The flag set name we want to get the treatments. * @param {Attributes=} attributes - An object of type Attributes defining the attributes for the given key. * @returns {Treatments} The map with all the TreatmentWithConfig objects */ @@ -1433,12 +1433,12 @@ declare namespace SplitIO { * @param {Attributes=} attributes - An object of type Attributes defining the attributes for the given key. * @returns {Treatments} The map with all the TreatmentWithConfig objects */ - getTreatmentsByFlagSet(key: SplitKey, flagSet: string, attributes?: Attributes): Treatments, + getTreatmentsByFlagSet(flagSet: string, attributes?: Attributes): Treatments, /** - * Returns a TreatmentsWithConfig value, which is an object map with the TreatmentWithConfig (an object with both treatment and config string) for the feature flags related to the given flagSets. + * Returns a TreatmentsWithConfig value, which is an object map with the TreatmentWithConfig (an object with both treatment and config string) for the feature flags related to the given flag sets. * @function getTreatmentsWithConfigByFlagSet * @param {string} key - The string key representing the consumer. - * @param {string} flagSet - The flagSet name we want to get the treatments. + * @param {string} flagSet - The flag set name we want to get the treatments. * @param {Attributes=} attributes - An object of type Attributes defining the attributes for the given key. * @returns {Treatments} The map with all the TreatmentWithConfig objects */ @@ -1452,10 +1452,10 @@ declare namespace SplitIO { */ getTreatmentsWithConfigByFlagSet(flagSet: string, attributes?: Attributes): TreatmentsWithConfig, /** - * Returns a Returns a Treatments value, which is an object with both treatment and config string for to the feature flags related to the given flagSets. + * Returns a Returns a Treatments value, which is an object with both treatment and config string for to the feature flags related to the given flag sets. * @function getTreatmentsByFlagSets * @param {string} key - The string key representing the consumer. - * @param {Array} flagSets - An array of the flagSet names we want to get the treatments. + * @param {Array} flagSets - An array of the flag set names we want to get the treatments. * @param {Attributes=} attributes - An object of type Attributes defining the attributes for the given key. * @returns {Treatments} The map with all the TreatmentWithConfig objects */ @@ -1469,10 +1469,10 @@ declare namespace SplitIO { */ getTreatmentsByFlagSets(flagSets: string[], attributes?: Attributes): Treatments, /** - * Returns a TreatmentsWithConfig value, which is an object map with the TreatmentWithConfig (an object with both treatment and config string) for the feature flags related to the given flagSets. + * Returns a TreatmentsWithConfig value, which is an object map with the TreatmentWithConfig (an object with both treatment and config string) for the feature flags related to the given flag sets. * @function getTreatmentsWithConfigByFlagSets * @param {string} key - The string key representing the consumer. - * @param {Array} flagSets - An array of the flagSet names we want to get the treatments. + * @param {Array} flagSets - An array of the flag set names we want to get the treatments. * @param {Attributes=} attributes - An object of type Attributes defining the attributes for the given key. * @returns {Treatments} The map with all the TreatmentWithConfig objects */ From 602fc7f73284103b03c75eccf6d287269dab79aa Mon Sep 17 00:00:00 2001 From: Emmanuel Zamora Date: Wed, 18 Oct 2023 14:43:22 -0300 Subject: [PATCH 08/38] Integration tests improvements --- .../browserSuites/fetch-specific-splits.spec.js | 6 ++++-- src/__tests__/browserSuites/flag-sets.spec.js | 2 -- .../browserSuites/push-flag-sets.spec.js | 10 ---------- src/__tests__/browserSuites/telemetry.spec.js | 16 +--------------- .../nodeSuites/fetch-specific-splits.spec.js | 7 +++++-- src/__tests__/nodeSuites/flag-sets.spec.js | 4 ---- src/__tests__/nodeSuites/push-flag-sets.spec.js | 11 ----------- src/__tests__/online/browser.spec.js | 5 +++-- src/__tests__/online/node.spec.js | 3 ++- 9 files changed, 15 insertions(+), 49 deletions(-) diff --git a/src/__tests__/browserSuites/fetch-specific-splits.spec.js b/src/__tests__/browserSuites/fetch-specific-splits.spec.js index a15f68b42..fd0534746 100644 --- a/src/__tests__/browserSuites/fetch-specific-splits.spec.js +++ b/src/__tests__/browserSuites/fetch-specific-splits.spec.js @@ -13,9 +13,9 @@ const baseConfig = { streamingEnabled: false, }; -export default function fetchSpecificSplits(fetchMock, assert) { +export function fetchSpecificSplits(fetchMock, assert) { - assert.plan(splitFilters.length+1); + assert.plan(splitFilters.length); for (let i = 0; i < splitFilters.length; i++) { const urls = { sdk: 'https://sdkurl' + i }; @@ -46,7 +46,9 @@ export default function fetchSpecificSplits(fetchMock, assert) { } } +} +export function fetchSpecificSplitsForFlagSets(fetchMock, assert) { // Flag sets assert.test(async (t) => { diff --git a/src/__tests__/browserSuites/flag-sets.spec.js b/src/__tests__/browserSuites/flag-sets.spec.js index a821943aa..37673d617 100644 --- a/src/__tests__/browserSuites/flag-sets.spec.js +++ b/src/__tests__/browserSuites/flag-sets.spec.js @@ -131,7 +131,6 @@ export default function flagSets(fetchMock, t) { t.test(async (assert) => { fetchMock.reset(); - assert.plan(8); let factory, client = []; @@ -168,7 +167,6 @@ export default function flagSets(fetchMock, t) { t.test(async (assert) => { fetchMock.reset(); - assert.plan(8); let factory, client = []; diff --git a/src/__tests__/browserSuites/push-flag-sets.spec.js b/src/__tests__/browserSuites/push-flag-sets.spec.js index d2d7b2a6f..bc97cbb5a 100644 --- a/src/__tests__/browserSuites/push-flag-sets.spec.js +++ b/src/__tests__/browserSuites/push-flag-sets.spec.js @@ -61,7 +61,6 @@ export function testFlagSets(fetchMock, t) { t.test(async (assert) => { - assert.plan(3); let splitio, client, manager = []; setMockListener((eventSourceInstance) => { @@ -72,7 +71,6 @@ export function testFlagSets(fetchMock, t) { client.once(client.Event.SDK_UPDATE, async () => { assert.equal(manager.splits().length, 1, '1 - update is processed and the flag is stored'); await client.destroy(); - assert.equal(eventSourceInstance.readyState, EventSourceMock.CLOSED, '1 - streaming is closed after destroy'); assert.end(); }); eventSourceInstance.emitMessage(notification1); @@ -88,7 +86,6 @@ export function testFlagSets(fetchMock, t) { t.test(async (assert) => { - assert.plan(5); let splitio, client, manager = []; setMockListener((eventSourceInstance) => { @@ -116,7 +113,6 @@ export function testFlagSets(fetchMock, t) { client.once(client.Event.SDK_UPDATE, async () => { assert.deepEqual(manager.splits().length, 0, '2 - update is processed and the flag is removed'); await client.destroy(); - assert.equal(eventSourceInstance.readyState, EventSourceMock.CLOSED, '2 - streaming is closed after destroy'); assert.end(); }); eventSourceInstance.emitMessage(notification4None); @@ -132,7 +128,6 @@ export function testFlagSets(fetchMock, t) { t.test(async (assert) => { - assert.plan(6); let splitio, client, manager = []; setMockListener((eventSourceInstance) => { @@ -168,7 +163,6 @@ export function testFlagSets(fetchMock, t) { client.once(client.Event.SDK_UPDATE, async () => { assert.deepEqual(manager.splits().length, 0, '3 - update is processed and flag is not added to the storage'); await client.destroy(); - assert.equal(eventSourceInstance.readyState, EventSourceMock.CLOSED, '3 - streaming is closed after destroy'); assert.end(); }); eventSourceInstance.emitMessage(notification5); @@ -184,7 +178,6 @@ export function testFlagSets(fetchMock, t) { t.test(async (assert) => { - assert.plan(5); fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=2&sets=set_1,set_2', function () { assert.pass('4 - A fetch is triggered due to the SPLIT_KILL'); @@ -210,7 +203,6 @@ export function testFlagSets(fetchMock, t) { client.once(client.Event.SDK_UPDATE, async () => { assert.equal(manager.split('workm').killed, true, '4 - update is processed and the flag is updated'); await client.destroy(); - assert.equal(eventSourceInstance.readyState, EventSourceMock.CLOSED, '4 - streaming is closed after destroy'); assert.end(); }); eventSourceInstance.emitMessage(notificationKill); @@ -226,7 +218,6 @@ export function testFlagSets(fetchMock, t) { t.test(async (assert) => { - assert.plan(4); fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1&sets=set_1,set_2', function () { assert.pass('5 - A fetch is triggered due to the SPLIT_KILL'); @@ -245,7 +236,6 @@ export function testFlagSets(fetchMock, t) { client.once(client.Event.SDK_UPDATE, async () => { assert.deepEqual(manager.splits(), [], '5 - storage is not modified since flag is not present. '); await client.destroy(); - assert.equal(eventSourceInstance.readyState, EventSourceMock.CLOSED, '5 - streaming is closed after destroy'); assert.end(); }); eventSourceInstance.emitMessage(notificationKill); diff --git a/src/__tests__/browserSuites/telemetry.spec.js b/src/__tests__/browserSuites/telemetry.spec.js index 5d0c8f576..13eefa03b 100644 --- a/src/__tests__/browserSuites/telemetry.spec.js +++ b/src/__tests__/browserSuites/telemetry.spec.js @@ -189,30 +189,16 @@ export default async function telemetryBrowserSuite(fetchMock, t) { fetchMock.get(baseUrls.sdk + '/mySegments/nicolas%40split.io', { status: 200, body: { 'mySegments': [] } }); fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=-1&sets=a,c,d', { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); - fetchMock.postOnce(baseUrls.telemetry + '/v1/metrics/usage', 200); fetchMock.postOnce(baseUrls.telemetry + '/v1/metrics/config', (url, opts) => { const data = JSON.parse(opts.body); assert.true(data.tR > 0, 'timeUntilReady is larger than 0'); assert.equal(data.fsT, 7, 'unique flag sets total should be 7'); assert.equal(data.fsI, 4, 'flagset ignored should be 4'); - factory.client().destroy().then(() => { - assert.end(); - }); return 200; }); - factory = SplitFactoryForTest({...baseConfig, sync: {splitFilters}}); - - }, 'SDK with sets configured has sets information in config POST'); - - t.test(async (assert) => { - assert.plan(8); - let factory; - const splitFilters = [{ type: 'bySet', values: ['a', 'b'] }]; - - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=-1&sets=a,c,d', { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); fetchMock.postOnce(baseUrls.telemetry + '/v1/metrics/usage', (url, opts) => { const data = JSON.parse(opts.body); @@ -236,6 +222,6 @@ export default async function telemetryBrowserSuite(fetchMock, t) { assert.deepEqual(client.getTreatmentsWithConfigByFlagSet('a'),[]); assert.deepEqual(client.getTreatmentsWithConfigByFlagSets(['a']),[]); - }, 'SDK with sets configured has evaluation by sets telemetry in stats POST'); + }, 'SDK with sets configured has sets information in config POST and evaluation by sets telemetry in stats POST'); } diff --git a/src/__tests__/nodeSuites/fetch-specific-splits.spec.js b/src/__tests__/nodeSuites/fetch-specific-splits.spec.js index 6470b0a32..6c6849108 100644 --- a/src/__tests__/nodeSuites/fetch-specific-splits.spec.js +++ b/src/__tests__/nodeSuites/fetch-specific-splits.spec.js @@ -11,9 +11,9 @@ const baseConfig = { streamingEnabled: false, }; -export default function fetchSpecificSplits(fetchMock, assert) { +export function fetchSpecificSplits(fetchMock, assert) { - assert.plan(splitFilters.length+1); + assert.plan(splitFilters.length); for (let i = 0; i < splitFilters.length; i++) { const urls = { sdk: 'https://sdkurl' + i }; @@ -43,6 +43,9 @@ export default function fetchSpecificSplits(fetchMock, assert) { } } +} + +export function fetchSpecificSplitsForFlagSets(fetchMock, assert) { // Flag sets assert.test(async (t) => { diff --git a/src/__tests__/nodeSuites/flag-sets.spec.js b/src/__tests__/nodeSuites/flag-sets.spec.js index e43f6be76..c0c9d0b2e 100644 --- a/src/__tests__/nodeSuites/flag-sets.spec.js +++ b/src/__tests__/nodeSuites/flag-sets.spec.js @@ -23,7 +23,6 @@ export default function flagSets(fetchMock, t) { mockSegmentChanges(fetchMock, new RegExp(baseUrls.sdk + '/segmentChanges/*'), ['some_key']); t.test(async (assert) => { - assert.plan(7); let factory, manager, client = []; // Receive split change with 1 split belonging to set_1 & set_2 and one belonging to set_3 @@ -76,7 +75,6 @@ export default function flagSets(fetchMock, t) { }, 'Polling - SDK with sets configured updates flags according to sets'); t.test(async (assert) => { - assert.plan(15); let factory, manager, client = []; @@ -138,7 +136,6 @@ export default function flagSets(fetchMock, t) { t.test(async (assert) => { fetchMock.reset(); - assert.plan(8); let factory, client = []; @@ -176,7 +173,6 @@ export default function flagSets(fetchMock, t) { t.test(async (assert) => { fetchMock.reset(); - assert.plan(8); let factory, client = []; diff --git a/src/__tests__/nodeSuites/push-flag-sets.spec.js b/src/__tests__/nodeSuites/push-flag-sets.spec.js index d504a5084..75b828ca9 100644 --- a/src/__tests__/nodeSuites/push-flag-sets.spec.js +++ b/src/__tests__/nodeSuites/push-flag-sets.spec.js @@ -64,7 +64,6 @@ export function testFlagSets(fetchMock, t) { t.test(async (assert) => { - assert.plan(3); let splitio, client, manager = []; setMockListener((eventSourceInstance) => { @@ -75,7 +74,6 @@ export function testFlagSets(fetchMock, t) { client.once(client.Event.SDK_UPDATE, async () => { assert.equal(manager.splits().length, 1, '1 - update is processed and the flag is stored'); await client.destroy(); - assert.equal(eventSourceInstance.readyState, EventSourceMock.CLOSED, '1 - streaming is closed after destroy'); assert.end(); }); eventSourceInstance.emitMessage(notification1); @@ -91,7 +89,6 @@ export function testFlagSets(fetchMock, t) { t.test(async (assert) => { - assert.plan(5); let splitio, client, manager = []; setMockListener((eventSourceInstance) => { @@ -119,7 +116,6 @@ export function testFlagSets(fetchMock, t) { client.once(client.Event.SDK_UPDATE, async () => { assert.deepEqual(manager.splits().length, 0, '2 - update is processed and the flag is removed'); await client.destroy(); - assert.equal(eventSourceInstance.readyState, EventSourceMock.CLOSED, '2 - streaming is closed after destroy'); assert.end(); }); eventSourceInstance.emitMessage(notification4None); @@ -135,7 +131,6 @@ export function testFlagSets(fetchMock, t) { t.test(async (assert) => { - assert.plan(6); let splitio, client, manager = []; setMockListener((eventSourceInstance) => { @@ -171,7 +166,6 @@ export function testFlagSets(fetchMock, t) { client.once(client.Event.SDK_UPDATE, async () => { assert.deepEqual(manager.splits().length, 0, '3 - update is processed and flag is not added to the storage'); await client.destroy(); - assert.equal(eventSourceInstance.readyState, EventSourceMock.CLOSED, '3 - streaming is closed after destroy'); assert.end(); }); eventSourceInstance.emitMessage(notification5); @@ -187,8 +181,6 @@ export function testFlagSets(fetchMock, t) { t.test(async (assert) => { - assert.plan(5); - let splitio, client, manager = []; setMockListener((eventSourceInstance) => { @@ -196,7 +188,6 @@ export function testFlagSets(fetchMock, t) { fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=2&sets=set_1,set_2', async function () { assert.pass('4 - A fetch is triggered due to the SPLIT_KILL'); await client.destroy(); - assert.equal(eventSourceInstance.readyState, EventSourceMock.CLOSED, '4 - streaming is closed after destroy'); assert.end(); return { status: 200, body: { splits: [], since: 2, till: 3 }}; }); @@ -231,7 +222,6 @@ export function testFlagSets(fetchMock, t) { }, 'SDK with sets configured updates flag when a SPLIT_KILL is received'); t.test(async (assert) => { - assert.plan(4); fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1&sets=set_1,set_2', function () { assert.pass('5 - A fetch is triggered due to the SPLIT_KILL'); @@ -250,7 +240,6 @@ export function testFlagSets(fetchMock, t) { client.once(client.Event.SDK_UPDATE, async () => { assert.deepEqual(manager.splits(), [], '5 - storage is not modified since flag is not present. '); await client.destroy(); - assert.equal(eventSourceInstance.readyState, EventSourceMock.CLOSED, '5 - streaming is closed after destroy'); assert.end(); }); eventSourceInstance.emitMessage(notificationKill); diff --git a/src/__tests__/online/browser.spec.js b/src/__tests__/online/browser.spec.js index 00218fb53..b873d31c8 100644 --- a/src/__tests__/online/browser.spec.js +++ b/src/__tests__/online/browser.spec.js @@ -16,7 +16,7 @@ import ignoreIpAddressesSettingSuite from '../browserSuites/ignore-ip-addresses- import useBeaconApiSuite from '../browserSuites/use-beacon-api.spec'; import useBeaconDebugApiSuite from '../browserSuites/use-beacon-api.debug.spec'; import readyPromiseSuite from '../browserSuites/ready-promise.spec'; -import fetchSpecificSplits from '../browserSuites/fetch-specific-splits.spec'; +import { fetchSpecificSplits, fetchSpecificSplitsForFlagSets } from '../browserSuites/fetch-specific-splits.spec'; import userConsent from '../browserSuites/user-consent.spec'; import singleSync from '../browserSuites/single-sync.spec'; import flagSets from '../browserSuites/flag-sets.spec'; @@ -131,10 +131,11 @@ tape('## E2E CI Tests ##', function (assert) { assert.test('E2E / Ready promise', readyPromiseSuite.bind(null, fetchMock)); /* Validate fetching specific splits */ assert.test('E2E / Fetch specific splits', fetchSpecificSplits.bind(null, fetchMock)); + assert.test('E2E / Fetch specific splits for flag sets', fetchSpecificSplitsForFlagSets.bind(null, fetchMock)); /* Validate single sync */ assert.test('E2E / Single sync', singleSync.bind(null, fetchMock)); /* Validate flag sets */ - assert.test('E2E / flag sets', flagSets.bind(null, fetchMock)); + assert.test('E2E / Flag sets', flagSets.bind(null, fetchMock)); //If we change the mocks, we need to clear localstorage. Cleaning up after testing ensures "fresh data". localStorage.clear(); diff --git a/src/__tests__/online/node.spec.js b/src/__tests__/online/node.spec.js index 9cb9eb873..f5a71b07d 100644 --- a/src/__tests__/online/node.spec.js +++ b/src/__tests__/online/node.spec.js @@ -15,7 +15,7 @@ import managerSuite from '../nodeSuites/manager.spec'; import ipAddressesSetting from '../nodeSuites/ip-addresses-setting.spec'; import ipAddressesSettingDebug from '../nodeSuites/ip-addresses-setting.debug.spec'; import readyPromiseSuite from '../nodeSuites/ready-promise.spec'; -import fetchSpecificSplits from '../nodeSuites/fetch-specific-splits.spec'; +import { fetchSpecificSplits, fetchSpecificSplitsForFlagSets } from '../nodeSuites/fetch-specific-splits.spec'; import splitChangesMock1 from '../mocks/splitchanges.since.-1.json'; import splitChangesMock2 from '../mocks/splitchanges.since.1457552620999.json'; @@ -83,6 +83,7 @@ tape('## Node JS - E2E CI Tests ##', async function (assert) { /* Validate fetching specific splits */ assert.test('E2E / Fetch specific splits', fetchSpecificSplits.bind(null, fetchMock)); + assert.test('E2E / Fetch specific splits for flag sets', fetchSpecificSplitsForFlagSets.bind(null, fetchMock)); /* Validate flag sets */ assert.test('E2E / Flag sets', flagSets.bind(null, fetchMock)); From 7f95c920c663fbd75d0998f7cfd2af3e2c0efe7a Mon Sep 17 00:00:00 2001 From: Emmanuel Zamora Date: Fri, 20 Oct 2023 18:02:40 -0300 Subject: [PATCH 09/38] Update commons version --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index a0db522dc..541b77b4c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "10.23.2-rc.0", "license": "Apache-2.0", "dependencies": { - "@splitsoftware/splitio-commons": "1.10.0", + "@splitsoftware/splitio-commons": "1.10.1-rc.0", "@types/google.analytics": "0.0.40", "@types/ioredis": "^4.28.0", "bloom-filters": "^3.0.0", @@ -874,9 +874,9 @@ "dev": true }, "node_modules/@splitsoftware/splitio-commons": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.10.0.tgz", - "integrity": "sha512-se4jrBr0wMYJCeOKOx8xfICNSyRM9fpKT/7BtPy5mJERqfDWS+hjMLH+PweBNgW6YJeG1c4F0j9lBUE5CwgPjw==", + "version": "1.10.1-rc.0", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.10.1-rc.0.tgz", + "integrity": "sha512-bot9NEg0u/suJ4gSqIWfkiRLhTc3NL6Veq1GQiD5J+dh0x/STUwUfV3pnJ+cUuPPclC1teJxAqxMdmyFKSx+NA==", "dependencies": { "tslib": "^2.3.1" }, @@ -8443,9 +8443,9 @@ "dev": true }, "@splitsoftware/splitio-commons": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.10.0.tgz", - "integrity": "sha512-se4jrBr0wMYJCeOKOx8xfICNSyRM9fpKT/7BtPy5mJERqfDWS+hjMLH+PweBNgW6YJeG1c4F0j9lBUE5CwgPjw==", + "version": "1.10.1-rc.0", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.10.1-rc.0.tgz", + "integrity": "sha512-bot9NEg0u/suJ4gSqIWfkiRLhTc3NL6Veq1GQiD5J+dh0x/STUwUfV3pnJ+cUuPPclC1teJxAqxMdmyFKSx+NA==", "requires": { "tslib": "^2.3.1" } diff --git a/package.json b/package.json index ff0a367b7..e8b73637f 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "node": ">=6" }, "dependencies": { - "@splitsoftware/splitio-commons": "1.10.0", + "@splitsoftware/splitio-commons": "1.10.1-rc.0", "@types/google.analytics": "0.0.40", "@types/ioredis": "^4.28.0", "bloom-filters": "^3.0.0", From 97b4da93ac7f9949001a7593f2aafb4ec9f00fb5 Mon Sep 17 00:00:00 2001 From: Emmanuel Zamora Date: Fri, 20 Oct 2023 19:46:48 -0300 Subject: [PATCH 10/38] Fix missing post --- src/__tests__/browserSuites/flag-sets.spec.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/__tests__/browserSuites/flag-sets.spec.js b/src/__tests__/browserSuites/flag-sets.spec.js index 37673d617..a2b471435 100644 --- a/src/__tests__/browserSuites/flag-sets.spec.js +++ b/src/__tests__/browserSuites/flag-sets.spec.js @@ -131,6 +131,7 @@ export default function flagSets(fetchMock, t) { t.test(async (assert) => { fetchMock.reset(); + fetchMock.post('*', 200); let factory, client = []; @@ -167,6 +168,7 @@ export default function flagSets(fetchMock, t) { t.test(async (assert) => { fetchMock.reset(); + fetchMock.post('*', 200); let factory, client = []; From 1912e9ffa638b06dc657d7fb859750fc0e17cc13 Mon Sep 17 00:00:00 2001 From: Emmanuel Zamora Date: Tue, 24 Oct 2023 16:12:42 -0300 Subject: [PATCH 11/38] Update rc version --- package-lock.json | 4 ++-- package.json | 2 +- src/settings/defaults/version.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index aba86cf4a..a1a61cbbd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@splitsoftware/splitio", - "version": "10.23.2-rc.0", + "version": "10.23.2-rc.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@splitsoftware/splitio", - "version": "10.23.2-rc.0", + "version": "10.23.2-rc.1", "license": "Apache-2.0", "dependencies": { "@splitsoftware/splitio-commons": "1.10.1-rc.0", diff --git a/package.json b/package.json index e8b73637f..b46ded5b1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio", - "version": "10.23.2-rc.0", + "version": "10.23.2-rc.1", "description": "Split SDK", "files": [ "README.md", diff --git a/src/settings/defaults/version.js b/src/settings/defaults/version.js index 99f181e5a..8ae735d54 100644 --- a/src/settings/defaults/version.js +++ b/src/settings/defaults/version.js @@ -1 +1 @@ -export const packageVersion = '10.23.2-rc.0'; +export const packageVersion = '10.23.2-rc.1'; From fffc43d542c229d6028956a67902f66fe52673be Mon Sep 17 00:00:00 2001 From: Emmanuel Zamora Date: Tue, 24 Oct 2023 17:18:01 -0300 Subject: [PATCH 12/38] modify git ci to upload stage assets --- .github/workflows/ci-cd.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 701c3518d..0cc8aebaf 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -58,7 +58,7 @@ jobs: run: BUILD_BRANCH=$(echo "${GITHUB_REF#refs/heads/}") npm run build - name: Store assets - if: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/development' || github.ref == 'refs/heads/master') }} + if: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/sdks-7437' || github.ref == 'refs/heads/master') }} uses: actions/upload-artifact@v3 with: name: assets @@ -69,7 +69,7 @@ jobs: name: Upload assets runs-on: ubuntu-20.04 needs: build - if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/development' }} + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/sdks-7437' }} strategy: matrix: environment: From e522327cd18e4329ad14d3a72351c4cb3938be73 Mon Sep 17 00:00:00 2001 From: Emmanuel Zamora Date: Thu, 26 Oct 2023 17:58:46 -0300 Subject: [PATCH 13/38] Add async type definitions and ts-tests --- package-lock.json | 4 +-- package.json | 2 +- src/settings/defaults/version.js | 2 +- ts-tests/index.ts | 50 ++++++++++++++++++++++++++- types/splitio.d.ts | 58 +++++++++++++++++++++++++++----- 5 files changed, 102 insertions(+), 14 deletions(-) diff --git a/package-lock.json b/package-lock.json index a1a61cbbd..e483ef807 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@splitsoftware/splitio", - "version": "10.23.2-rc.1", + "version": "10.23.2-rc.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@splitsoftware/splitio", - "version": "10.23.2-rc.1", + "version": "10.23.2-rc.2", "license": "Apache-2.0", "dependencies": { "@splitsoftware/splitio-commons": "1.10.1-rc.0", diff --git a/package.json b/package.json index b46ded5b1..89b563a84 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio", - "version": "10.23.2-rc.1", + "version": "10.23.2-rc.2", "description": "Split SDK", "files": [ "README.md", diff --git a/src/settings/defaults/version.js b/src/settings/defaults/version.js index 8ae735d54..f8bc3cf93 100644 --- a/src/settings/defaults/version.js +++ b/src/settings/defaults/version.js @@ -1 +1 @@ -export const packageVersion = '10.23.2-rc.1'; +export const packageVersion = '10.23.2-rc.2'; diff --git a/ts-tests/index.ts b/ts-tests/index.ts index 3c39b01f8..85f16136f 100644 --- a/ts-tests/index.ts +++ b/ts-tests/index.ts @@ -272,6 +272,34 @@ treatmentsWithConfig = client.getTreatmentsWithConfig(['mySplit']); treatmentsWithConfig = client.getTreatmentsWithConfig(splitKey, ['mySplit'], attributes); treatmentsWithConfig = client.getTreatmentsWithConfig(['mySplit'], attributes); +// We can call getTreatmentsByFlagSet without a key. +treatments = client.getTreatmentsByFlagSet(splitKey, 'set_a'); +treatments = client.getTreatmentsByFlagSet('set_a'); +// Attributes parameter is optional. +treatments = client.getTreatmentsByFlagSet(splitKey, 'set_a', attributes); +treatments = client.getTreatmentsByFlagSet('set_a', attributes); + +// We can call getTreatmentsByFlagSets without a key. +treatments = client.getTreatmentsByFlagSets(splitKey, ['set_a']); +treatments = client.getTreatmentsByFlagSets(['set_a']); +// Attributes parameter is optional. +treatments = client.getTreatmentsByFlagSets(splitKey, ['set_a'], attributes); +treatments = client.getTreatmentsByFlagSets(['set_a'], attributes); + +// We can call getTreatmentsWithConfigByFlagSet without a key. +treatmentsWithConfig = client.getTreatmentsWithConfigByFlagSet(splitKey, 'set_a'); +treatmentsWithConfig = client.getTreatmentsWithConfigByFlagSet('set_a'); +// Attributes parameter is optional. +treatmentsWithConfig = client.getTreatmentsWithConfigByFlagSet(splitKey, 'set_a', attributes); +treatmentsWithConfig = client.getTreatmentsWithConfigByFlagSet('set_a', attributes); + +// We can call getTreatmentsWithConfigByFlagSets without a key. +treatmentsWithConfig = client.getTreatmentsWithConfigByFlagSets(splitKey, ['set_a']); +treatmentsWithConfig = client.getTreatmentsWithConfigByFlagSets(['set_a']); +// Attributes parameter is optional. +treatmentsWithConfig = client.getTreatmentsWithConfigByFlagSets(splitKey, ['set_a'], attributes); +treatmentsWithConfig = client.getTreatmentsWithConfigByFlagSets(['set_a'], attributes); + // We can call track with or without a key. Traffic type can also be binded to the client. tracked = client.track(splitKey, 'myTrafficType', 'myEventType'); // all params tracked = client.track('myTrafficType', 'myEventType'); // key binded, tt provided. @@ -328,6 +356,26 @@ asyncTreatmentsWithConfig = asyncClient.getTreatmentsWithConfig(splitKey, ['mySp // Attributes parameter is optional asyncTreatmentsWithConfig = asyncClient.getTreatmentsWithConfig(splitKey, ['mySplit'], attributes); +// We can call getTreatmentsByFlagSet +asyncTreatments = asyncClient.getTreatmentsByFlagSet(splitKey, 'set_a'); +// Attributes parameter is optional +asyncTreatments = asyncClient.getTreatmentsByFlagSet(splitKey, 'set_a', attributes); + +// We can call getTreatmentsByFlagSets +asyncTreatments = asyncClient.getTreatmentsByFlagSets(splitKey, ['set_a']); +// Attributes parameter is optional +asyncTreatments = asyncClient.getTreatmentsByFlagSets(splitKey, ['set_a'], attributes); + +// We can call getTreatmentsWithConfigByFlagSet +asyncTreatmentsWithConfig = asyncClient.getTreatmentsWithConfigByFlagSet(splitKey, 'set_a'); +// Attributes parameter is optional +asyncTreatmentsWithConfig = asyncClient.getTreatmentsWithConfigByFlagSet(splitKey, 'set_a', attributes); + +// We can call getTreatmentsByFlagSets but always with a key. +asyncTreatmentsWithConfig = asyncClient.getTreatmentsWithConfigByFlagSets(splitKey, ['set_a']); +// Attributes parameter is optional +asyncTreatmentsWithConfig = asyncClient.getTreatmentsWithConfigByFlagSets(splitKey, ['set_a'], attributes); + // We can call track only with a key. trackPromise = asyncClient.track(splitKey, 'myTrafficType', 'myEventType'); // all required params // Value parameter is optional. @@ -441,7 +489,7 @@ userConsent = BrowserSDK.UserConsent.Status.UNKNOWN; /**** Tests for fully crowded settings interfaces ****/ // Split filters -let splitFilters: SplitIO.SplitFilter[] = [{ type: 'byName', values: ['my_split_1', 'my_split_1'] }, { type: 'byPrefix', values: ['my_split', 'test_split_'] }] +let splitFilters: SplitIO.SplitFilter[] = [{ type: 'bySet', values: ['set_a', 'set_b'] }, { type: 'byName', values: ['my_split_1', 'my_split_1'] }, { type: 'byPrefix', values: ['my_split', 'test_split_'] }] // Browser integrations let fieldsObjectSample: UniversalAnalytics.FieldsObject = { hitType: 'event', eventAction: 'action' }; diff --git a/types/splitio.d.ts b/types/splitio.d.ts index eaeca4afc..9de7d7a7b 100644 --- a/types/splitio.d.ts +++ b/types/splitio.d.ts @@ -911,7 +911,7 @@ declare namespace SplitIO { * * @typedef {string} SplitFilterType */ - type SplitFilterType = 'byName' | 'byPrefix'; + type SplitFilterType = 'bySet' | 'byName' | 'byPrefix'; /** * Defines a feature flag filter, described by a type and list of values. */ @@ -1428,7 +1428,7 @@ declare namespace SplitIO { * @param {string} key - The string key representing the consumer. * @param {string} flagSet - The flag set name we want to get the treatments. * @param {Attributes=} attributes - An object of type Attributes defining the attributes for the given key. - * @returns {Treatments} The map with all the TreatmentWithConfig objects + * @returns {Treatments} The map with all the Treatments objects */ getTreatmentsByFlagSet(key: SplitKey, flagSet: string, attributes?: Attributes): Treatments, /** @@ -1436,7 +1436,7 @@ declare namespace SplitIO { * @function getTreatmentsByFlagSet * @param {string} flagSet - The flag set name we want to get the treatments. * @param {Attributes=} attributes - An object of type Attributes defining the attributes for the given key. - * @returns {Treatments} The map with all the TreatmentWithConfig objects + * @returns {Treatments} The map with all the Treatments objects */ getTreatmentsByFlagSet(flagSet: string, attributes?: Attributes): Treatments, /** @@ -1445,7 +1445,7 @@ declare namespace SplitIO { * @param {string} key - The string key representing the consumer. * @param {string} flagSet - The flag set name we want to get the treatments. * @param {Attributes=} attributes - An object of type Attributes defining the attributes for the given key. - * @returns {Treatments} The map with all the TreatmentWithConfig objects + * @returns {TreatmentsWithConfig} The map with all the TreatmentWithConfig objects */ getTreatmentsWithConfigByFlagSet(key: SplitKey, flagSet: string, attributes?: Attributes): TreatmentsWithConfig, /** @@ -1453,7 +1453,7 @@ declare namespace SplitIO { * @function getTreatmentsWithConfigByFlagSet * @param {string} flagSet - The flag set name we want to get the treatments. * @param {Attributes=} attributes - An object of type Attributes defining the attributes for the given key. - * @returns {Treatments} The map with all the TreatmentWithConfig objects + * @returns {TreatmentsWithConfig} The map with all the TreatmentWithConfig objects */ getTreatmentsWithConfigByFlagSet(flagSet: string, attributes?: Attributes): TreatmentsWithConfig, /** @@ -1462,7 +1462,7 @@ declare namespace SplitIO { * @param {string} key - The string key representing the consumer. * @param {Array} flagSets - An array of the flag set names we want to get the treatments. * @param {Attributes=} attributes - An object of type Attributes defining the attributes for the given key. - * @returns {Treatments} The map with all the TreatmentWithConfig objects + * @returns {Treatments} The map with all the Treatments objects */ getTreatmentsByFlagSets(key: SplitKey, flagSets: string[], attributes?: Attributes): Treatments, /** @@ -1470,7 +1470,7 @@ declare namespace SplitIO { * @function getTreatmentsByFlagSets * @param {Array} flagSets - An array of the flag set names we want to get the treatments. * @param {Attributes=} attributes - An object of type Attributes defining the attributes for the given key. - * @returns {Treatments} The map with all the TreatmentWithConfig objects + * @returns {Treatments} The map with all the Treatments objects */ getTreatmentsByFlagSets(flagSets: string[], attributes?: Attributes): Treatments, /** @@ -1479,7 +1479,7 @@ declare namespace SplitIO { * @param {string} key - The string key representing the consumer. * @param {Array} flagSets - An array of the flag set names we want to get the treatments. * @param {Attributes=} attributes - An object of type Attributes defining the attributes for the given key. - * @returns {Treatments} The map with all the TreatmentWithConfig objects + * @returns {TreatmentsWithConfig} The map with all the TreatmentWithConfig objects */ getTreatmentsWithConfigByFlagSets(key: SplitKey, flagSets: string[], attributes?: Attributes): TreatmentsWithConfig, /** @@ -1487,7 +1487,7 @@ declare namespace SplitIO { * @function getTreatmentsWithConfigByFlagSets * @param {Array} flagSets - An array of the flag set names we want to get the treatments. * @param {Attributes=} attributes - An object of type Attributes defining the attributes for the given key. - * @returns {Treatments} The map with all the TreatmentWithConfig objects + * @returns {TreatmentsWithConfig} The map with all the TreatmentWithConfig objects */ getTreatmentsWithConfigByFlagSets(flagSets: string[], attributes?: Attributes): TreatmentsWithConfig, /** @@ -1620,6 +1620,46 @@ declare namespace SplitIO { * @returns {AsyncTreatmentsWithConfig} TreatmentsWithConfig promise that resolves to the map of TreatmentsWithConfig objects. */ getTreatmentsWithConfig(key: SplitKey, featureFlagNames: string[], attributes?: Attributes): AsyncTreatmentsWithConfig, + /** + * Returns a Treatments value, which is an object map with the treatments for the feature flags related to the given flag set. + * For usage on NodeJS as we don't have only one key. + * @function getTreatmentsByFlagSet + * @param {string} key - The string key representing the consumer. + * @param {string} flagSet - The flag set name we want to get the treatments. + * @param {Attributes=} attributes - An object of type Attributes defining the attributes for the given key. + * @returns {AsyncTreatments} Treatments promise that resolves to the treatments object map. + */ + getTreatmentsByFlagSet(key: SplitKey, flagSet: string, attributes?: Attributes): AsyncTreatments, + /** + * Returns a TreatmentsWithConfig value, which is an object map with the TreatmentWithConfig (an object with both treatment and config string) for the feature flags related to the given flag set. + * For usage on NodeJS as we don't have only one key. + * @function getTreatmentsWithConfigByFlagSet + * @param {string} flagSet - The flag set name we want to get the treatments. + * @param {string} key - The string key representing the consumer. + * @param {Attributes=} attributes - An object of type Attributes defining the attributes for the given key. + * @returns {AsyncTreatmentsWithConfig} TreatmentsWithConfig promise that resolves to the TreatmentsWithConfig object. + */ + getTreatmentsWithConfigByFlagSet(key: SplitKey, flagSet: string, attributes?: Attributes): AsyncTreatmentsWithConfig, + /** + * Returns a Returns a Treatments value, which is an object with both treatment and config string for to the feature flags related to the given flag sets. + * For usage on NodeJS as we don't have only one key. + * @function getTreatmentsByFlagSets + * @param {string} key - The string key representing the consumer. + * @param {Array} flagSets - An array of the flag set names we want to get the treatments. + * @param {Attributes=} attributes - An object of type Attributes defining the attributes for the given key. + * @returns {AsyncTreatments} Treatments promise that resolves to the treatments object map. + */ + getTreatmentsByFlagSets(key: SplitKey, flagSets: string[], attributes?: Attributes): AsyncTreatments, + /** + * Returns a TreatmentsWithConfig value, which is an object map with the TreatmentWithConfig (an object with both treatment and config string) for the feature flags related to the given flag sets. + * For usage on NodeJS as we don't have only one key. + * @function getTreatmentsWithConfigByFlagSets + * @param {string} key - The string key representing the consumer. + * @param {Array} flagSets - An array of the flag set names we want to get the treatments. + * @param {Attributes=} attributes - An object of type Attributes defining the attributes for the given key. + * @returns {AsyncTreatmentsWithConfig} TreatmentsWithConfig promise that resolves to the TreatmentsWithConfig object. + */ + getTreatmentsWithConfigByFlagSets(key: SplitKey, flagSets: string[], attributes?: Attributes): AsyncTreatmentsWithConfig, /** * Tracks an event to be fed to the results product on Split user interface, and returns a promise to signal when the event was successfully queued (or not). * @function track From 768650056ded420037639d95320f050963791a0e Mon Sep 17 00:00:00 2001 From: Emmanuel Zamora Date: Wed, 1 Nov 2023 18:07:11 -0300 Subject: [PATCH 14/38] [SDKS-7657] update commons version & update changes file --- CHANGES.txt | 9 +++++- package-lock.json | 69 ++++++++++++++++++++++++---------------------- package.json | 4 +-- types/splitio.d.ts | 7 ++++- 4 files changed, 52 insertions(+), 37 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 6695ed5ec..34a9a64b5 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,6 +1,13 @@ 10.24.0 (October XX, 2023) + - Added support for Flag Sets on the SDK, which enables grouping feature flags and interacting with the group rather than individually (more details in our documentation): + - Added new variations of the get treatment methods to support evaluating flags in given flag set/s. + - getTreatmentsByFlagSet and getTreatmentsByFlagSets + - getTreatmentsWithConfigByFlagSets and getTreatmentsWithConfigByFlagSets + - Added a new optional Split Filter configuration option. This allows the SDK and Split services to only synchronize the flags in the specified flag sets, avoiding unused or unwanted flags from being synced on the SDK instance, bringing all the benefits from a reduced payload. + - Note: Only applicable when the SDK is in charge of the rollout data synchronization. When not applicable, the SDK will log a warning on init. + - Updated the following SDK manager methods to expose flag sets on flag views. - Added `defaultTreatment` property to the `SplitView` object returned by the `split` and `splits` methods of the SDK manager (Related to issue https://github.com/splitio/javascript-commons/issues/225). - - Updated @splitsoftware/splitio-commons package to version 1.10.0 that includes vulnerability fixes, and adds the `defaultTreatment` property to the `SplitView` object. + - Updated @splitsoftware/splitio-commons package to version 1.11.0 that includes vulnerability fixes, and adds the `defaultTreatment` property to the `SplitView` object. 10.23.1 (September 22, 2023) - Updated @splitsoftware/splitio-commons package to version 1.9.1. This update removes the handler for 'unload' DOM events, that can prevent browsers from being able to put pages in the back/forward cache for faster back and forward loads (Related to issue https://github.com/splitio/javascript-client/issues/759). diff --git a/package-lock.json b/package-lock.json index e483ef807..8e5030675 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,15 @@ { "name": "@splitsoftware/splitio", - "version": "10.23.2-rc.2", + "version": "10.23.2-rc.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@splitsoftware/splitio", - "version": "10.23.2-rc.2", + "version": "10.23.2-rc.3", "license": "Apache-2.0", "dependencies": { - "@splitsoftware/splitio-commons": "1.10.1-rc.0", + "@splitsoftware/splitio-commons": "1.10.1-rc.1", "@types/google.analytics": "0.0.40", "@types/ioredis": "^4.28.0", "bloom-filters": "^3.0.0", @@ -874,9 +874,9 @@ "dev": true }, "node_modules/@splitsoftware/splitio-commons": { - "version": "1.10.1-rc.0", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.10.1-rc.0.tgz", - "integrity": "sha512-bot9NEg0u/suJ4gSqIWfkiRLhTc3NL6Veq1GQiD5J+dh0x/STUwUfV3pnJ+cUuPPclC1teJxAqxMdmyFKSx+NA==", + "version": "1.10.1-rc.1", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.10.1-rc.1.tgz", + "integrity": "sha512-h2EI1aIYFI9L5IPlqHWNxGJh3ueenZqFRagxfrI6mYa+t0qX41ZZiPoQkDd4bA84NxCA0+kI6xICluJqCd0L8Q==", "dependencies": { "tslib": "^2.3.1" }, @@ -1717,20 +1717,23 @@ } }, "node_modules/browserify-sign": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", - "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.2.tgz", + "integrity": "sha512-1rudGyeYY42Dk6texmv7c4VcQ0EsvVbLwZkA+AQB7SxvXxmcD93jcHie8bzecJ+ChDlmAm2Qyu0+Ccg5uhZXCg==", "dev": true, "dependencies": { - "bn.js": "^5.1.1", - "browserify-rsa": "^4.0.1", + "bn.js": "^5.2.1", + "browserify-rsa": "^4.1.0", "create-hash": "^1.2.0", "create-hmac": "^1.1.7", - "elliptic": "^6.5.3", + "elliptic": "^6.5.4", "inherits": "^2.0.4", - "parse-asn1": "^5.1.5", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" + "parse-asn1": "^5.1.6", + "readable-stream": "^3.6.2", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 4" } }, "node_modules/browserify-zlib": { @@ -5640,9 +5643,9 @@ } }, "node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, "dependencies": { "inherits": "^2.0.3", @@ -8443,9 +8446,9 @@ "dev": true }, "@splitsoftware/splitio-commons": { - "version": "1.10.1-rc.0", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.10.1-rc.0.tgz", - "integrity": "sha512-bot9NEg0u/suJ4gSqIWfkiRLhTc3NL6Veq1GQiD5J+dh0x/STUwUfV3pnJ+cUuPPclC1teJxAqxMdmyFKSx+NA==", + "version": "1.10.1-rc.1", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.10.1-rc.1.tgz", + "integrity": "sha512-h2EI1aIYFI9L5IPlqHWNxGJh3ueenZqFRagxfrI6mYa+t0qX41ZZiPoQkDd4bA84NxCA0+kI6xICluJqCd0L8Q==", "requires": { "tslib": "^2.3.1" } @@ -9169,20 +9172,20 @@ } }, "browserify-sign": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", - "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.2.tgz", + "integrity": "sha512-1rudGyeYY42Dk6texmv7c4VcQ0EsvVbLwZkA+AQB7SxvXxmcD93jcHie8bzecJ+ChDlmAm2Qyu0+Ccg5uhZXCg==", "dev": true, "requires": { - "bn.js": "^5.1.1", - "browserify-rsa": "^4.0.1", + "bn.js": "^5.2.1", + "browserify-rsa": "^4.1.0", "create-hash": "^1.2.0", "create-hmac": "^1.1.7", - "elliptic": "^6.5.3", + "elliptic": "^6.5.4", "inherits": "^2.0.4", - "parse-asn1": "^5.1.5", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" + "parse-asn1": "^5.1.6", + "readable-stream": "^3.6.2", + "safe-buffer": "^5.2.1" } }, "browserify-zlib": { @@ -12185,9 +12188,9 @@ } }, "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, "requires": { "inherits": "^2.0.3", diff --git a/package.json b/package.json index 89b563a84..59bb835f4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio", - "version": "10.23.2-rc.2", + "version": "10.23.2-rc.3", "description": "Split SDK", "files": [ "README.md", @@ -40,7 +40,7 @@ "node": ">=6" }, "dependencies": { - "@splitsoftware/splitio-commons": "1.10.1-rc.0", + "@splitsoftware/splitio-commons": "1.10.1-rc.1", "@types/google.analytics": "0.0.40", "@types/ioredis": "^4.28.0", "bloom-filters": "^3.0.0", diff --git a/types/splitio.d.ts b/types/splitio.d.ts index 9de7d7a7b..fa08aefc2 100644 --- a/types/splitio.d.ts +++ b/types/splitio.d.ts @@ -664,6 +664,11 @@ declare namespace SplitIO { configs: { [treatmentName: string]: string }, + /** + * List of sets of the feature flag. + * @property {string[]} sets + */ + sets?: string[], /** * The default treatment of the feature flag. * @property {string} defaultTreatment @@ -1440,7 +1445,7 @@ declare namespace SplitIO { */ getTreatmentsByFlagSet(flagSet: string, attributes?: Attributes): Treatments, /** - * Returns a TreatmentsWithConfig value, which is an object map with the TreatmentWithConfig (an object with both treatment and config string) for the feature flags related to the given flag sets. + * Returns a TreatmentsWithConfig value, which is an object map with the TreatmentWithConfig (an object with both treatment and config string) for the feature flags related to the given flag set. * @function getTreatmentsWithConfigByFlagSet * @param {string} key - The string key representing the consumer. * @param {string} flagSet - The flag set name we want to get the treatments. From 8cda413ebe98289c36440feab00927e9e117d946 Mon Sep 17 00:00:00 2001 From: Emmanuel Zamora Date: Wed, 1 Nov 2023 18:10:41 -0300 Subject: [PATCH 15/38] fix ci --- src/settings/defaults/version.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/settings/defaults/version.js b/src/settings/defaults/version.js index f8bc3cf93..e44534448 100644 --- a/src/settings/defaults/version.js +++ b/src/settings/defaults/version.js @@ -1 +1 @@ -export const packageVersion = '10.23.2-rc.2'; +export const packageVersion = '10.23.2-rc.3'; From 18712fbc7a52e2012417ae604297cd937ff67455 Mon Sep 17 00:00:00 2001 From: Emmanuel Zamora Date: Thu, 2 Nov 2023 14:59:24 -0300 Subject: [PATCH 16/38] Fix tests and update commons rc version --- package-lock.json | 18 +++++++------- package.json | 4 ++-- .../fetch-specific-splits.spec.js | 14 +++++------ .../browserSuites/push-flag-sets.spec.js | 23 ++++++++++++++---- .../mocks/message.SPLIT_UPDATE.FS.6.json | 4 ++++ .../nodeSuites/fetch-specific-splits.spec.js | 19 +++++++-------- .../nodeSuites/push-flag-sets.spec.js | 24 ++++++++++++++----- 7 files changed, 66 insertions(+), 40 deletions(-) create mode 100644 src/__tests__/mocks/message.SPLIT_UPDATE.FS.6.json diff --git a/package-lock.json b/package-lock.json index 8e5030675..fe5cc31c9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,15 @@ { "name": "@splitsoftware/splitio", - "version": "10.23.2-rc.3", + "version": "10.23.2-rc.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@splitsoftware/splitio", - "version": "10.23.2-rc.3", + "version": "10.23.2-rc.4", "license": "Apache-2.0", "dependencies": { - "@splitsoftware/splitio-commons": "1.10.1-rc.1", + "@splitsoftware/splitio-commons": "1.10.1-rc.3", "@types/google.analytics": "0.0.40", "@types/ioredis": "^4.28.0", "bloom-filters": "^3.0.0", @@ -874,9 +874,9 @@ "dev": true }, "node_modules/@splitsoftware/splitio-commons": { - "version": "1.10.1-rc.1", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.10.1-rc.1.tgz", - "integrity": "sha512-h2EI1aIYFI9L5IPlqHWNxGJh3ueenZqFRagxfrI6mYa+t0qX41ZZiPoQkDd4bA84NxCA0+kI6xICluJqCd0L8Q==", + "version": "1.10.1-rc.3", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.10.1-rc.3.tgz", + "integrity": "sha512-eqJxAMtqFK7fXFKL8gMGfRsMBdxrYI9tIGUHHpY1NcyeKkn4OWqAOZMhX6z2qLdBArzHi34Li0Lb72o+Bh1Tqg==", "dependencies": { "tslib": "^2.3.1" }, @@ -8446,9 +8446,9 @@ "dev": true }, "@splitsoftware/splitio-commons": { - "version": "1.10.1-rc.1", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.10.1-rc.1.tgz", - "integrity": "sha512-h2EI1aIYFI9L5IPlqHWNxGJh3ueenZqFRagxfrI6mYa+t0qX41ZZiPoQkDd4bA84NxCA0+kI6xICluJqCd0L8Q==", + "version": "1.10.1-rc.3", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.10.1-rc.3.tgz", + "integrity": "sha512-eqJxAMtqFK7fXFKL8gMGfRsMBdxrYI9tIGUHHpY1NcyeKkn4OWqAOZMhX6z2qLdBArzHi34Li0Lb72o+Bh1Tqg==", "requires": { "tslib": "^2.3.1" } diff --git a/package.json b/package.json index 59bb835f4..95bb46421 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio", - "version": "10.23.2-rc.3", + "version": "10.23.2-rc.4", "description": "Split SDK", "files": [ "README.md", @@ -40,7 +40,7 @@ "node": ">=6" }, "dependencies": { - "@splitsoftware/splitio-commons": "1.10.1-rc.1", + "@splitsoftware/splitio-commons": "1.10.1-rc.3", "@types/google.analytics": "0.0.40", "@types/ioredis": "^4.28.0", "bloom-filters": "^3.0.0", diff --git a/src/__tests__/browserSuites/fetch-specific-splits.spec.js b/src/__tests__/browserSuites/fetch-specific-splits.spec.js index fd0534746..f8a17f365 100644 --- a/src/__tests__/browserSuites/fetch-specific-splits.spec.js +++ b/src/__tests__/browserSuites/fetch-specific-splits.spec.js @@ -66,16 +66,13 @@ export function fetchSpecificSplitsForFlagSets(fetchMock, assert) { const logSpy = sinon.spy(console, 'log'); + let factory; + const queryString = '&sets=4_valid,set_2,set_3,set_ww,set_x'; fetchMock.get(baseUrls.sdk + '/mySegments/nicolas%40split.io', { status: 200, body: { 'mySegments': [] } }); - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=-1&sets=4_valid,set_2,set_3,set_ww,set_x', function () { - t.pass('flag set query correctly formed'); - return { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }; - }); - const factory = SplitFactory(config); - - const client = factory.client(); - client.ready().then(() => { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=-1' + queryString, { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 }}); + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1457552620999' + queryString, async function () { + t.pass('flag set query correctly formed'); t.true(logSpy.calledWithExactly('[WARN] splitio => settings: bySet filter value "set_x " has extra whitespace, trimming.')); t.true(logSpy.calledWithExactly('[WARN] splitio => settings: you passed invalid+, flag set must adhere to the regular expressions /^[a-z0-9][_a-z0-9]{0,49}$/. This means a flag set must start with a letter or number, be in lowercase, alphanumeric and have a max length of 50 characters. invalid+ was discarded.')); t.true(logSpy.calledWithExactly('[WARN] splitio => settings: you passed _invalid, flag set must adhere to the regular expressions /^[a-z0-9][_a-z0-9]{0,49}$/. This means a flag set must start with a letter or number, be in lowercase, alphanumeric and have a max length of 50 characters. _invalid was discarded.')); @@ -84,5 +81,6 @@ export function fetchSpecificSplitsForFlagSets(fetchMock, assert) { t.end(); }); }); + factory = SplitFactory(config); }, 'FlagSets config'); } diff --git a/src/__tests__/browserSuites/push-flag-sets.spec.js b/src/__tests__/browserSuites/push-flag-sets.spec.js index bc97cbb5a..8ceb64bed 100644 --- a/src/__tests__/browserSuites/push-flag-sets.spec.js +++ b/src/__tests__/browserSuites/push-flag-sets.spec.js @@ -8,6 +8,7 @@ import notification3 from '../mocks/message.SPLIT_UPDATE.FS.3.json'; import notification4None from '../mocks/message.SPLIT_UPDATE.FS.4None.json'; import notification4 from '../mocks/message.SPLIT_UPDATE.FS.4.json'; import notification5 from '../mocks/message.SPLIT_UPDATE.FS.5.json'; +import notification6 from '../mocks/message.SPLIT_UPDATE.FS.6.json'; import notificationKill from '../mocks/message.SPLIT_UPDATE.FS.kill.json'; import authPushEnabled from '../mocks/auth.pushEnabled.nicolas@split.io.json'; @@ -29,6 +30,8 @@ const MILLIS_FIRST_SPLIT_UPDATE_EVENT = 100; const MILLIS_SECOND_SPLIT_UPDATE_EVENT = 200; const MILLIS_THIRD_SPLIT_UPDATE_EVENT = 300; const MILLIS_FOURTH_SPLIT_UPDATE_EVENT = 400; +const MILLIS_FIFTH_SPLIT_UPDATE_EVENT = 500; + export function testFlagSets(fetchMock, t) { fetchMock.reset(); @@ -160,14 +163,21 @@ export function testFlagSets(fetchMock, t) { setTimeout(() => { // Receive a SPLIT_UPDATE with "sets":["set_3", "set_4"] + // should not emit SPLIT_UPDATE + // Receive a SPLIT_UPDATE with "sets":["set_1", "set_2"] from next test notification client.once(client.Event.SDK_UPDATE, async () => { - assert.deepEqual(manager.splits().length, 0, '3 - update is processed and flag is not added to the storage'); + assert.deepEqual(manager.split('workm').sets, ['set_1', 'set_2'], '3 - update is processed and the flag is stored'); await client.destroy(); assert.end(); }); eventSourceInstance.emitMessage(notification5); }, MILLIS_FOURTH_SPLIT_UPDATE_EVENT); + setTimeout(() => { + // emit a SPLIT_UPDATE with "sets":["set_1", "set_2"] + eventSourceInstance.emitMessage(notification6); + }, MILLIS_FIFTH_SPLIT_UPDATE_EVENT); + }); splitio = SplitFactory(configWithSets); @@ -232,15 +242,18 @@ export function testFlagSets(fetchMock, t) { setTimeout(() => { assert.deepEqual(manager.splits(), [], '5 - initialized without flags'); - // Receive a SPLIT_KILL for flag + // Receive a SPLIT_KILL for flag that does not belongs to configured sets client.once(client.Event.SDK_UPDATE, async () => { - assert.deepEqual(manager.splits(), [], '5 - storage is not modified since flag is not present. '); - await client.destroy(); - assert.end(); + assert.fail('5 - SDK_UPDATE should not be emitted for flags that are not in configured sets '); }); eventSourceInstance.emitMessage(notificationKill); }, MILLIS_FIRST_SPLIT_UPDATE_EVENT); + setTimeout(async () => { + await client.destroy(); + assert.end(); + }, MILLIS_SECOND_SPLIT_UPDATE_EVENT); + }); splitio = SplitFactory(configWithSets); diff --git a/src/__tests__/mocks/message.SPLIT_UPDATE.FS.6.json b/src/__tests__/mocks/message.SPLIT_UPDATE.FS.6.json new file mode 100644 index 000000000..8b26192c8 --- /dev/null +++ b/src/__tests__/mocks/message.SPLIT_UPDATE.FS.6.json @@ -0,0 +1,4 @@ +{ + "type": "message", + "data": "{\"id\":\"m2T85LA4fQ:0:0\",\"clientId\":\"pri:NzIyNjY1MzI4\",\"timestamp\":1694548363040,\"encoding\":\"json\",\"channel\":\"NzM2MDI5Mzc0_MTgyNTg1MTgwNg==_splits\",\"data\":\"{\\\"type\\\":\\\"SPLIT_UPDATE\\\",\\\"changeNumber\\\":6,\\\"pcn\\\":5,\\\"c\\\":0,\\\"d\\\":\\\"eyJ0cmFmZmljVHlwZU5hbWUiOiJjbGllbnQiLCJuYW1lIjoid29ya20iLCJ0cmFmZmljQWxsb2NhdGlvbiI6MTAwLCJ0cmFmZmljQWxsb2NhdGlvblNlZWQiOjE0NzM5MjIyNCwic2VlZCI6NTI0NDE3MTA1LCJzdGF0dXMiOiJBQ1RJVkUiLCJraWxsZWQiOmZhbHNlLCJkZWZhdWx0VHJlYXRtZW50Ijoib24iLCJjaGFuZ2VOdW1iZXIiOjIsImFsZ28iOjIsImNvbmZpZ3VyYXRpb25zIjp7fSwic2V0cyI6WyJzZXRfMSIsInNldF8yIl0sImNvbmRpdGlvbnMiOlt7ImNvbmRpdGlvblR5cGUiOiJST0xMT1VUIiwibWF0Y2hlckdyb3VwIjp7ImNvbWJpbmVyIjoiQU5EIiwibWF0Y2hlcnMiOlt7ImtleVNlbGVjdG9yIjp7InRyYWZmaWNUeXBlIjoiY2xpZW50IiwiYXR0cmlidXRlIjpudWxsfSwibWF0Y2hlclR5cGUiOiJJTl9TRUdNRU5UIiwibmVnYXRlIjpmYWxzZSwidXNlckRlZmluZWRTZWdtZW50TWF0Y2hlckRhdGEiOnsic2VnbWVudE5hbWUiOiJuZXdfc2VnbWVudCJ9LCJ3aGl0ZWxpc3RNYXRjaGVyRGF0YSI6bnVsbCwidW5hcnlOdW1lcmljTWF0Y2hlckRhdGEiOm51bGwsImJldHdlZW5NYXRjaGVyRGF0YSI6bnVsbCwiYm9vbGVhbk1hdGNoZXJEYXRhIjpudWxsLCJkZXBlbmRlbmN5TWF0Y2hlckRhdGEiOm51bGwsInN0cmluZ01hdGNoZXJEYXRhIjpudWxsfV19LCJwYXJ0aXRpb25zIjpbeyJ0cmVhdG1lbnQiOiJvbiIsInNpemUiOjB9LHsidHJlYXRtZW50Ijoib2ZmIiwic2l6ZSI6MH0seyJ0cmVhdG1lbnQiOiJmcmVlIiwic2l6ZSI6MTAwfSx7InRyZWF0bWVudCI6ImNvbnRhIiwic2l6ZSI6MH1dLCJsYWJlbCI6ImluIHNlZ21lbnQgbmV3X3NlZ21lbnQifSx7ImNvbmRpdGlvblR5cGUiOiJST0xMT1VUIiwibWF0Y2hlckdyb3VwIjp7ImNvbWJpbmVyIjoiQU5EIiwibWF0Y2hlcnMiOlt7ImtleVNlbGVjdG9yIjp7InRyYWZmaWNUeXBlIjoiY2xpZW50IiwiYXR0cmlidXRlIjpudWxsfSwibWF0Y2hlclR5cGUiOiJBTExfS0VZUyIsIm5lZ2F0ZSI6ZmFsc2UsInVzZXJEZWZpbmVkU2VnbWVudE1hdGNoZXJEYXRhIjpudWxsLCJ3aGl0ZWxpc3RNYXRjaGVyRGF0YSI6bnVsbCwidW5hcnlOdW1lcmljTWF0Y2hlckRhdGEiOm51bGwsImJldHdlZW5NYXRjaGVyRGF0YSI6bnVsbCwiYm9vbGVhbk1hdGNoZXJEYXRhIjpudWxsLCJkZXBlbmRlbmN5TWF0Y2hlckRhdGEiOm51bGwsInN0cmluZ01hdGNoZXJEYXRhIjpudWxsfV19LCJwYXJ0aXRpb25zIjpbeyJ0cmVhdG1lbnQiOiJvbiIsInNpemUiOjEwMH0seyJ0cmVhdG1lbnQiOiJvZmYiLCJzaXplIjowfSx7InRyZWF0bWVudCI6ImZyZWUiLCJzaXplIjowfSx7InRyZWF0bWVudCI6ImNvbnRhIiwic2l6ZSI6MH1dLCJsYWJlbCI6ImRlZmF1bHQgcnVsZSJ9XX0\\\"}\"}" +} diff --git a/src/__tests__/nodeSuites/fetch-specific-splits.spec.js b/src/__tests__/nodeSuites/fetch-specific-splits.spec.js index 6c6849108..01a4f2347 100644 --- a/src/__tests__/nodeSuites/fetch-specific-splits.spec.js +++ b/src/__tests__/nodeSuites/fetch-specific-splits.spec.js @@ -61,17 +61,16 @@ export function fetchSpecificSplitsForFlagSets(fetchMock, assert) { } }; - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=-1&sets=4_valid,set_2,set_3,set_ww,set_x', async function () { - t.pass('flag set query correctly formed'); - return { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }; - }); - - const factory = SplitFactory(config); - const client = factory.client(); + let factory; + const queryString = '&sets=4_valid,set_2,set_3,set_ww,set_x'; - client.ready().then(async () => { - await client.destroy(); - t.end(); + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=-1' + queryString, { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 }}); + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1457552620999' + queryString, async function () { + t.pass('flag set query correctly formed'); + factory.client().destroy().then(() => { + t.end(); + }); }); + factory = SplitFactory(config); }, 'FlagSets config'); } diff --git a/src/__tests__/nodeSuites/push-flag-sets.spec.js b/src/__tests__/nodeSuites/push-flag-sets.spec.js index 75b828ca9..bcd5b92d2 100644 --- a/src/__tests__/nodeSuites/push-flag-sets.spec.js +++ b/src/__tests__/nodeSuites/push-flag-sets.spec.js @@ -9,6 +9,7 @@ import notification3 from '../mocks/message.SPLIT_UPDATE.FS.3.json'; import notification4None from '../mocks/message.SPLIT_UPDATE.FS.4None.json'; import notification4 from '../mocks/message.SPLIT_UPDATE.FS.4.json'; import notification5 from '../mocks/message.SPLIT_UPDATE.FS.5.json'; +import notification6 from '../mocks/message.SPLIT_UPDATE.FS.6.json'; import notificationKill from '../mocks/message.SPLIT_UPDATE.FS.kill.json'; import authPushEnabled from '../mocks/auth.pushEnabled.node.json'; @@ -29,6 +30,7 @@ const MILLIS_FIRST_SPLIT_UPDATE_EVENT = 100; const MILLIS_SECOND_SPLIT_UPDATE_EVENT = 200; const MILLIS_THIRD_SPLIT_UPDATE_EVENT = 300; const MILLIS_FOURTH_SPLIT_UPDATE_EVENT = 400; +const MILLIS_FIFTH_SPLIT_UPDATE_EVENT = 500; export function testFlagSets(fetchMock, t) { fetchMock.reset(); @@ -163,14 +165,21 @@ export function testFlagSets(fetchMock, t) { setTimeout(() => { // Receive a SPLIT_UPDATE with "sets":["set_3", "set_4"] + // should not emit SPLIT_UPDATE + // Receive a SPLIT_UPDATE with "sets":["set_1", "set_2"] from next test notification client.once(client.Event.SDK_UPDATE, async () => { - assert.deepEqual(manager.splits().length, 0, '3 - update is processed and flag is not added to the storage'); + assert.deepEqual(manager.split('workm').sets, ['set_1', 'set_2'], '3 - update is processed and the flag is stored'); await client.destroy(); assert.end(); }); eventSourceInstance.emitMessage(notification5); }, MILLIS_FOURTH_SPLIT_UPDATE_EVENT); + setTimeout(() => { + // emit a SPLIT_UPDATE with "sets":["set_1", "set_2"] + eventSourceInstance.emitMessage(notification6); + }, MILLIS_FIFTH_SPLIT_UPDATE_EVENT); + }); splitio = SplitFactory(configWithSets); @@ -236,15 +245,18 @@ export function testFlagSets(fetchMock, t) { setTimeout(() => { assert.deepEqual(manager.splits(), [], '5 - initialized without flags'); - // Receive a SPLIT_KILL for flag - client.once(client.Event.SDK_UPDATE, async () => { - assert.deepEqual(manager.splits(), [], '5 - storage is not modified since flag is not present. '); - await client.destroy(); - assert.end(); + // Receive a SPLIT_KILL for flag that does not belongs to configured sets + client.once(client.Event.SDK_UPDATE, () => { + assert.fail('5 - SDK_UPDATE should not be emitted for flags that are not in configured sets '); }); eventSourceInstance.emitMessage(notificationKill); }, MILLIS_FIRST_SPLIT_UPDATE_EVENT); + setTimeout(async () => { + await client.destroy(); + assert.end(); + }, MILLIS_SECOND_SPLIT_UPDATE_EVENT); + }); splitio = SplitFactory(configWithSets); From 4c1deff00da7d792a333d17690ff240cc9efa106 Mon Sep 17 00:00:00 2001 From: Emmanuel Zamora Date: Thu, 2 Nov 2023 15:05:52 -0300 Subject: [PATCH 17/38] fix lint --- src/settings/defaults/version.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/settings/defaults/version.js b/src/settings/defaults/version.js index e44534448..387ad6159 100644 --- a/src/settings/defaults/version.js +++ b/src/settings/defaults/version.js @@ -1 +1 @@ -export const packageVersion = '10.23.2-rc.3'; +export const packageVersion = '10.23.2-rc.4'; From da0f44051acf1fb92737257dcd876f36a88003e4 Mon Sep 17 00:00:00 2001 From: Emmanuel Zamora Date: Thu, 2 Nov 2023 15:41:24 -0300 Subject: [PATCH 18/38] Fix rc version --- package-lock.json | 4 ++-- package.json | 2 +- src/settings/defaults/version.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index fe5cc31c9..cf9a8a210 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@splitsoftware/splitio", - "version": "10.23.2-rc.4", + "version": "10.23.2-rc.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@splitsoftware/splitio", - "version": "10.23.2-rc.4", + "version": "10.23.2-rc.3", "license": "Apache-2.0", "dependencies": { "@splitsoftware/splitio-commons": "1.10.1-rc.3", diff --git a/package.json b/package.json index 95bb46421..b960f786e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio", - "version": "10.23.2-rc.4", + "version": "10.23.2-rc.3", "description": "Split SDK", "files": [ "README.md", diff --git a/src/settings/defaults/version.js b/src/settings/defaults/version.js index 387ad6159..e44534448 100644 --- a/src/settings/defaults/version.js +++ b/src/settings/defaults/version.js @@ -1 +1 @@ -export const packageVersion = '10.23.2-rc.4'; +export const packageVersion = '10.23.2-rc.3'; From 51cb6c9109c82e51edce555b94bf5665bbd1f44c Mon Sep 17 00:00:00 2001 From: Emmanuel Zamora Date: Thu, 2 Nov 2023 16:15:03 -0300 Subject: [PATCH 19/38] fix type definition --- types/splitio.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/splitio.d.ts b/types/splitio.d.ts index fa08aefc2..6a33f2c49 100644 --- a/types/splitio.d.ts +++ b/types/splitio.d.ts @@ -668,7 +668,7 @@ declare namespace SplitIO { * List of sets of the feature flag. * @property {string[]} sets */ - sets?: string[], + sets: string[], /** * The default treatment of the feature flag. * @property {string} defaultTreatment From 3585b64d09763c1148c0ccb3c10c47e198f2c123 Mon Sep 17 00:00:00 2001 From: Emmanuel Zamora Date: Thu, 2 Nov 2023 23:34:50 -0300 Subject: [PATCH 20/38] fix ts-tests --- ts-tests/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/ts-tests/index.ts b/ts-tests/index.ts index 85f16136f..9ea2f8478 100644 --- a/ts-tests/index.ts +++ b/ts-tests/index.ts @@ -124,6 +124,7 @@ splitView = { configs: { off: '{"dimensions":"{\"height\":20,\"width\":40}"}' }, + sets: ['set_a','set_b'], defaultTreatment: 'off' }; splitViews = [splitView]; From 8e90ebc4cd080befc307d1baee6e02eb57ae8160 Mon Sep 17 00:00:00 2001 From: Emmanuel Zamora Date: Thu, 2 Nov 2023 23:56:39 -0300 Subject: [PATCH 21/38] update changes file --- CHANGES.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 34a9a64b5..c706123c4 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,4 @@ -10.24.0 (October XX, 2023) +10.24.0 (November XX, 2023) - Added support for Flag Sets on the SDK, which enables grouping feature flags and interacting with the group rather than individually (more details in our documentation): - Added new variations of the get treatment methods to support evaluating flags in given flag set/s. - getTreatmentsByFlagSet and getTreatmentsByFlagSets From 83e5705d803e841691f54b42142c094f875d2220 Mon Sep 17 00:00:00 2001 From: Emmanuel Zamora Date: Fri, 3 Nov 2023 00:12:27 -0300 Subject: [PATCH 22/38] update rc version --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index cf9a8a210..fe5cc31c9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@splitsoftware/splitio", - "version": "10.23.2-rc.3", + "version": "10.23.2-rc.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@splitsoftware/splitio", - "version": "10.23.2-rc.3", + "version": "10.23.2-rc.4", "license": "Apache-2.0", "dependencies": { "@splitsoftware/splitio-commons": "1.10.1-rc.3", diff --git a/package.json b/package.json index b960f786e..95bb46421 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio", - "version": "10.23.2-rc.3", + "version": "10.23.2-rc.4", "description": "Split SDK", "files": [ "README.md", From ebb98d233a5d368c5afcfcb2cc512f85b33356be Mon Sep 17 00:00:00 2001 From: Emmanuel Zamora Date: Fri, 3 Nov 2023 00:18:44 -0300 Subject: [PATCH 23/38] fix lint --- src/settings/defaults/version.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/settings/defaults/version.js b/src/settings/defaults/version.js index e44534448..387ad6159 100644 --- a/src/settings/defaults/version.js +++ b/src/settings/defaults/version.js @@ -1 +1 @@ -export const packageVersion = '10.23.2-rc.3'; +export const packageVersion = '10.23.2-rc.4'; From c45244aa1385e69c54ce464676fe0c207490fc30 Mon Sep 17 00:00:00 2001 From: Nicolas Zelaya Date: Fri, 3 Nov 2023 12:31:08 -0300 Subject: [PATCH 24/38] New mock for flagsets --- src/__tests__/mocks/redis-commands-sets.txt | 45 +++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 src/__tests__/mocks/redis-commands-sets.txt diff --git a/src/__tests__/mocks/redis-commands-sets.txt b/src/__tests__/mocks/redis-commands-sets.txt new file mode 100644 index 000000000..cf425bce6 --- /dev/null +++ b/src/__tests__/mocks/redis-commands-sets.txt @@ -0,0 +1,45 @@ +FLUSHDB +DEL 'REDIS_NODE_UT.SPLITIO.segment.UT_SEGMENT' +SADD 'REDIS_NODE_UT.SPLITIO.segment.UT_SEGMENT' UT_Segment_member +SET 'REDIS_NODE_UT.SPLITIO.segment.UT_SEGMENT.till' 1492721958710 +DEL 'REDIS_NODE_UT.SPLITIO.segment.demo' +SADD 'REDIS_NODE_UT.SPLITIO.segment.demo' nico +SET 'REDIS_NODE_UT.SPLITIO.segment.demo.till' 1489607057740 +DEL 'REDIS_NODE_UT.SPLITIO.segment.qa' +SADD 'REDIS_NODE_UT.SPLITIO.segment.qa' qa-user +SET 'REDIS_NODE_UT.SPLITIO.segment.qa.till' 1488406856133 +DEL 'REDIS_NODE_UT.SPLITIO.segment.qc' +SADD 'REDIS_NODE_UT.SPLITIO.segment.qc' qc-user +SET 'REDIS_NODE_UT.SPLITIO.segment.qc.till' 1484051547045 +DEL 'REDIS_NODE_UT.SPLITIO.segments.registered' +SADD 'REDIS_NODE_UT.SPLITIO.segments.registered' qa demo qc UT_SEGMENT testing_traffic_type +SET 'REDIS_NODE_UT.SPLITIO.split.UT_IN_SEGMENT' '{"changeNumber":1492722104980,"trafficTypeName":"machine","name":"UT_IN_SEGMENT","seed":-202209840,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"","attribute":""},"matcherType":"IN_SEGMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":"UT_SEGMENT"},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100}],"label":"whitelisted segment"},{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"machine","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":100}],"label":"in segment all"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.UT_NOT_IN_SEGMENT' '{"changeNumber":1492722747908,"trafficTypeName":"machine","name":"UT_NOT_IN_SEGMENT","seed":-56653132,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"machine","attribute":""},"matcherType":"IN_SEGMENT","negate":true,"userDefinedSegmentMatcherData":{"segmentName":"UT_SEGMENT"},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"not in segment UT_SEGMENT"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.UT_NOT_SET_MATCHER' '{"changeNumber":1492723024413,"trafficTypeName":"machine","name":"UT_NOT_SET_MATCHER","seed":-93553840,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"machine","attribute":"permissions"},"matcherType":"CONTAINS_ANY_OF_SET","negate":true,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["create","delete","update"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"permissions does not contain any of [create, delete, ...]"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.UT_SET_MATCHER' '{"changeNumber":1492722926004,"trafficTypeName":"machine","name":"UT_SET_MATCHER","seed":-1995997836,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"machine","attribute":"permissions"},"matcherType":"CONTAINS_ANY_OF_SET","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["admin","premium","idol"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"permissions contains any of [admin, premium, ...]"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.always-off' '{"changeNumber":1491519038393,"trafficTypeName":"user","name":"always-off","seed":1952026922,"sets":["set_one","set_two"],"status":"ACTIVE","killed":false,"defaultTreatment":"on","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":100}],"label":"in segment all"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.always-on' '{"changeNumber":1487277320548,"trafficTypeName":"user","name":"always-on","seed":1684183541,"sets":["set_one"],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"in segment all"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.always-o.n-with-config' '{"changeNumber":1487277320548,"trafficTypeName":"user","name":"always-o.n-with-config","seed":1684183541,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"o.n","size":100},{"treatment":"off","size":0}],"label":"in segment all"}],"configurations":{"o.n":"{\"color\":\"brown\"}"}}' +SET 'REDIS_NODE_UT.SPLITIO.split.hierarchical_splits_testing_off' '{"changeNumber":1487277320548,"trafficTypeName":"user","name":"hierarchical_splits_testing_off","seed":1684183541,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}},{"keySelector":{"trafficType":"user","attribute":""},"dependencyMatcherData":{"split":"always-on","treatments":["off"]},"matcherType":"IN_SPLIT_TREATMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":null,"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"in segment all"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.hierarchical_splits_testing_on' '{"changeNumber":1487277320548,"trafficTypeName":"user","name":"hierarchical_splits_testing_on","seed":1684183541,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}},{"keySelector":{"trafficType":"user","attribute":""},"dependencyMatcherData":{"split":"always-on","treatments":["on"]},"matcherType":"IN_SPLIT_TREATMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":null,"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"in segment all"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.hierarchical_splits_testing_on_negated' '{"changeNumber":1487277320548,"trafficTypeName":"user","name":"hierarchical_splits_testing_on","seed":1684183541,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":true,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}},{"keySelector":{"trafficType":"user","attribute":""},"dependencyMatcherData":{"split":"always-on","treatments":["on"]},"matcherType":"IN_SPLIT_TREATMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":null,"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"in segment all"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.labels' '{"changeNumber":1492023661334,"trafficTypeName":"user","name":"labels","seed":-1240661267,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}},{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"IN_SEGMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":"demo"},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}},{"keySelector":{"trafficType":"user","attribute":"n"},"matcherType":"EQUAL_TO","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"NUMBER","value":123},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":100}],"label":"in segment all and in segment demo and n = 123"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.nico_not' '{"changeNumber":1489412422181,"trafficTypeName":"user","name":"nico_not","seed":-788702424,"sets":["set_two"],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"IN_SEGMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":"qa"},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":100}],"label":"in segment qa"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.not_part_of' '{"changeNumber":1492627582227,"trafficTypeName":"user","name":"not_part_of","seed":-1643575289,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":"setx"},"matcherType":"PART_OF_SET","negate":true,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["a","b","c"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":100}],"label":"setx not part of [a, b, ...]"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.not_set_contain_all' '{"changeNumber":1492626848560,"trafficTypeName":"user","name":"not_set_contain_all","seed":-1811083551,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":"setx"},"matcherType":"CONTAINS_ALL_OF_SET","negate":true,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["a","b","c"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"setx does not contain all of [a, b, ...]"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.not_set_contain_any' '{"changeNumber":1492627144346,"trafficTypeName":"user","name":"not_set_contain_any","seed":-430318598,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":"setx"},"matcherType":"CONTAINS_ANY_OF_SET","negate":true,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["a","b","c"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":100}],"label":"setx does not contain any of [a, b, ...]"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.not_set_is_equal_to' '{"changeNumber":1492629855568,"trafficTypeName":"user","name":"not_set_is_equal_to","seed":-1042120204,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":"setx"},"matcherType":"EQUAL_TO_SET","negate":true,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["a","b","c"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":100}],"label":"setx does not exactly match [a, b, ...]"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.on-if-in-segment-qa' '{"changeNumber":1484050906786,"trafficTypeName":"user","name":"on-if-in-segment-qa","seed":2023627546,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"IN_SEGMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":"qa"},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"in segment qa"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.on-if-in-segment-qc' '{"changeNumber":1484051573721,"trafficTypeName":"user","name":"on-if-in-segment-qc","seed":1996584605,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"IN_SEGMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":"qc"},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"in segment qc"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.part_of' '{"changeNumber":1492627833215,"trafficTypeName":"user","name":"part_of","seed":1985865328,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":"setx"},"matcherType":"PART_OF_SET","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["a","b","c"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"setx part of [a, b, ...]"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.set_contain_all' '{"changeNumber":1492627890510,"trafficTypeName":"user","name":"set_contain_all","seed":-1731963136,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":"setx"},"matcherType":"CONTAINS_ALL_OF_SET","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["a","b","c"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"setx contains all of [a, b, ...]"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.set_contain_any' '{"changeNumber":1492627933473,"trafficTypeName":"user","name":"set_contain_any","seed":-1267403715,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":"setx"},"matcherType":"CONTAINS_ANY_OF_SET","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["a","b","c"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"setx contains any of [a, b, ...]"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.string_matchers' '{"changeNumber":1492541823531,"trafficTypeName":"user","name":"string_matchers","seed":1037479690,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":"st"},"matcherType":"STARTS_WITH","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["a","b"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":100}],"label":"st starts with [a, b]"},{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":"en"},"matcherType":"ENDS_WITH","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["ends","another"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":100}],"label":"en ends with [ends, another]"},{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":"con"},"matcherType":"CONTAINS_STRING","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["a","b"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":100}],"label":"con contains [a, b]"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.testing_traffic_type' '{"changeNumber":1489607495199,"trafficTypeName":"machine","name":"testing_traffic_type","seed":1825443152,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"IN_SEGMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":"testing_traffic_type"},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":100}],"label":"in segment testing_traffic_type"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.testing_traffic_types' '{"changeNumber":1490974465415,"trafficTypeName":"machine","name":"testing_traffic_types","seed":475616886,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"on","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"","attribute":""},"matcherType":"WHITELIST","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["sarasa"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100}],"label":"whitelisted"},{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"","attribute":""},"matcherType":"WHITELIST","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["excluded"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"off","size":100}],"label":"whitelisted"},{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}},{"keySelector":{"trafficType":"machine","attribute":""},"matcherType":"IN_SEGMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":"testing_traffic_type"},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":100}],"label":"in segment all and in segment testing_traffic_type"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.traffic_allocation_testing' '{"changeNumber":1490974123779,"trafficTypeName":"user","name":"traffic_allocation_testing","seed":1716284102,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"in segment all"}]}' +SADD 'REDIS_NODE_UT.SPLITIO.flagSet.set_one' always-on +SADD 'REDIS_NODE_UT.SPLITIO.flagSet.set_one' always-off +SADD 'REDIS_NODE_UT.SPLITIO.flagSet.set_two' always-off +SADD 'REDIS_NODE_UT.SPLITIO.flagSet.set_two' nico_not +SET 'REDIS_NODE_UT.SPLITIO.splits.till' 1492723024413 From d13fdcc3544beb76cb204baae2852792192c6444 Mon Sep 17 00:00:00 2001 From: Nicolas Zelaya Date: Fri, 3 Nov 2023 12:31:38 -0300 Subject: [PATCH 25/38] Adding redis tests part1 for flagsets, this wont work without commons --- src/__tests__/consumer/node_redis.spec.js | 67 ++++++++++++++++++++++- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/src/__tests__/consumer/node_redis.spec.js b/src/__tests__/consumer/node_redis.spec.js index 41d1a15af..0af9822dc 100644 --- a/src/__tests__/consumer/node_redis.spec.js +++ b/src/__tests__/consumer/node_redis.spec.js @@ -56,18 +56,24 @@ const expectedImpressionCount = [ `hierarchical_splits_testing_on_negated::${truncateTimeFrame(timeFrame)}`, '1', ]; +const MOCKS = { + '': 'redis-commands', + 'flag_sets': 'redis-commands-sets' +}; + /** * Initialize redis server and run a cli bash command to load redis with data to do the proper tests */ -const initializeRedisServer = () => { +const initializeRedisServer = (mock = '') => { // Simply pass the port that you want a Redis server to listen on. const server = new RedisServer(redisPort); + const mockFileName = MOCKS[mock]; const promise = new Promise((resolve, reject) => { server .open() .then(() => { - exec(`cat ./src/__tests__/mocks/redis-commands.txt | redis-cli -p ${redisPort}`, err => { + exec(`cat ./src/__tests__/mocks/${mockFileName}.txt | redis-cli -p ${redisPort}`, err => { if (err) { reject(server); // node couldn't execute the command @@ -632,4 +638,61 @@ tape('NodeJS Redis', function (t) { server.close().then(assert.end); }); }); + + t.test('Getting treatments with flag sets', assert => { + initializeRedisServer('flag_sets') + .then(async (server) => { + const sdk = SplitFactory(config); + + const client = sdk.client(); + await client.ready(); + + // @TODO: Working to remove this crap, debugging RedisAdapter queueing and why it doesnt like the pipeline exec. + setTimeout(async function () { + + + assert.deepEqual( + await client.getTreatmentsByFlagSet('nico@test', 'set_one'), + { 'always-on': 'on', 'always-off': 'off' }, + 'Evaluations using Redis storage should be correct for a set.' + ); + + assert.deepEqual( + await client.getTreatmentsByFlagSet('nico@test', 'set_two'), + { 'always-off': 'off', 'nico_not': 'off' }, + 'Evaluations using Redis storage should be correct for a set.' + ); + + assert.deepEqual( + await client.getTreatmentsByFlagSet('nico@test', 'set_invalid'), + {}, + 'Evaluations using Redis storage should properly handle all invalid sets.' + ); + + assert.deepEqual( + await client.getTreatmentsByFlagSets('nico@test', ['set_two', 'set_one']), + { 'always-on': 'on', 'always-off': 'off', 'nico_not': 'off' }, + 'Evaluations using Redis storage should be correct for multiple sets.' + ); + + assert.deepEqual( + await client.getTreatmentsByFlagSets('nico@test', [243, null, 'set_two', 'set_one', 'invalid_set']), + { 'always-on': 'on', 'always-off': 'off', 'nico_not': 'off' }, + 'Evaluations using Redis storage should be correct for multiple sets, discarding invalids.' + ); + + assert.deepEqual( + await client.getTreatmentsByFlagSets('nico@test', [243, null, 'invalid_set']), + {}, + 'Evaluations using Redis storage should properly handle all invalid sets.' + ); + + await client.ready(); // promise already resolved + await client.destroy(); + + // close server connection + server.close().then(assert.end); + },1000); + }); + }); }); From bc2206304a8050824f052cdec159d3274d3ce15c Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Fri, 3 Nov 2023 13:04:50 -0300 Subject: [PATCH 26/38] add test to validate that NodeJS SDK times out when a client-side SDK key is provided --- package-lock.json | 26 +++++------ package.json | 2 +- src/__tests__/nodeSuites/readiness.spec.js | 51 ++++++++++++++++++++++ src/__tests__/online/node.spec.js | 4 ++ 4 files changed, 69 insertions(+), 14 deletions(-) create mode 100644 src/__tests__/nodeSuites/readiness.spec.js diff --git a/package-lock.json b/package-lock.json index fe5cc31c9..29d94c108 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "10.23.2-rc.4", "license": "Apache-2.0", "dependencies": { - "@splitsoftware/splitio-commons": "1.10.1-rc.3", + "@splitsoftware/splitio-commons": "1.10.1-rc.4", "@types/google.analytics": "0.0.40", "@types/ioredis": "^4.28.0", "bloom-filters": "^3.0.0", @@ -874,9 +874,9 @@ "dev": true }, "node_modules/@splitsoftware/splitio-commons": { - "version": "1.10.1-rc.3", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.10.1-rc.3.tgz", - "integrity": "sha512-eqJxAMtqFK7fXFKL8gMGfRsMBdxrYI9tIGUHHpY1NcyeKkn4OWqAOZMhX6z2qLdBArzHi34Li0Lb72o+Bh1Tqg==", + "version": "1.10.1-rc.4", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.10.1-rc.4.tgz", + "integrity": "sha512-4fYPz6cjW55N1Ah/BI+q1c06zc989chAQQ4tZwh/+MtSLgaZXLdhd/YGeaESwUqbZ3DCgf5bjyQZFDQnluCjiA==", "dependencies": { "tslib": "^2.3.1" }, @@ -7111,9 +7111,9 @@ } }, "node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "node_modules/tty-browserify": { "version": "0.0.1", @@ -8446,9 +8446,9 @@ "dev": true }, "@splitsoftware/splitio-commons": { - "version": "1.10.1-rc.3", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.10.1-rc.3.tgz", - "integrity": "sha512-eqJxAMtqFK7fXFKL8gMGfRsMBdxrYI9tIGUHHpY1NcyeKkn4OWqAOZMhX6z2qLdBArzHi34Li0Lb72o+Bh1Tqg==", + "version": "1.10.1-rc.4", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.10.1-rc.4.tgz", + "integrity": "sha512-4fYPz6cjW55N1Ah/BI+q1c06zc989chAQQ4tZwh/+MtSLgaZXLdhd/YGeaESwUqbZ3DCgf5bjyQZFDQnluCjiA==", "requires": { "tslib": "^2.3.1" } @@ -13296,9 +13296,9 @@ } }, "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "tty-browserify": { "version": "0.0.1", diff --git a/package.json b/package.json index 95bb46421..c67daf34f 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "node": ">=6" }, "dependencies": { - "@splitsoftware/splitio-commons": "1.10.1-rc.3", + "@splitsoftware/splitio-commons": "1.10.1-rc.4", "@types/google.analytics": "0.0.40", "@types/ioredis": "^4.28.0", "bloom-filters": "^3.0.0", diff --git a/src/__tests__/nodeSuites/readiness.spec.js b/src/__tests__/nodeSuites/readiness.spec.js new file mode 100644 index 000000000..622026102 --- /dev/null +++ b/src/__tests__/nodeSuites/readiness.spec.js @@ -0,0 +1,51 @@ +import { SplitFactory } from '../../'; + +import splitChangesMock1 from '../mocks/splitchanges.since.-1.json'; +import splitChangesMock2 from '../mocks/splitchanges.since.1457552620999.json'; + +const readyTimeout = 0.1; + +const baseConfig = { + core: { + authorizationKey: '', + }, + startup: { + readyTimeout, + }, + streamingEnabled: false +}; + +export default function (fetchMock, assert) { + + assert.test(t => { // Timeout test: we provide a client-side SDK key on server-side (403 error) + const testUrls = { + sdk: 'https://sdk.baseurl/readinessSuite1', + events: 'https://events.baseurl/readinessSuite1' + }; + + fetchMock.getOnce(testUrls.sdk + '/splitChanges?since=-1', { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(testUrls.sdk + '/splitChanges?since=1457552620999', { status: 200, body: splitChangesMock2 }); + fetchMock.get(new RegExp(testUrls.sdk + '/segmentChanges/*'), 403); + fetchMock.postOnce(testUrls.events + '/events/bulk', 200); + + const splitio = SplitFactory({ + ...baseConfig, urls: testUrls + }); + const client = splitio.client(); + + t.true(client.track('some_key', 'some_tt', 'some_event_type'), 'since client is not destroyed, client.track returns true'); + + client.once(client.Event.SDK_READY, () => { + t.fail('### IS READY - NOT TIMED OUT when it should.'); + t.end(); + }); + client.once(client.Event.SDK_READY_TIMED_OUT, () => { + t.pass('### SDK TIMED OUT - SegmentChanges requests with client-side SDK key should fail with 403. Timed out.'); + + t.false(client.track('some_key', 'some_tt', 'some_event_type'), 'since client is flagged as destroyed, client.track returns false'); + + client.destroy().then(() => { t.end(); }); + }); + }); + +} diff --git a/src/__tests__/online/node.spec.js b/src/__tests__/online/node.spec.js index f5a71b07d..dce02929c 100644 --- a/src/__tests__/online/node.spec.js +++ b/src/__tests__/online/node.spec.js @@ -14,6 +14,7 @@ import expectedTreatmentsSuite from '../nodeSuites/expected-treatments.spec'; import managerSuite from '../nodeSuites/manager.spec'; import ipAddressesSetting from '../nodeSuites/ip-addresses-setting.spec'; import ipAddressesSettingDebug from '../nodeSuites/ip-addresses-setting.debug.spec'; +import readinessSuite from '../nodeSuites/readiness.spec'; import readyPromiseSuite from '../nodeSuites/ready-promise.spec'; import { fetchSpecificSplits, fetchSpecificSplitsForFlagSets } from '../nodeSuites/fetch-specific-splits.spec'; @@ -78,6 +79,9 @@ tape('## Node JS - E2E CI Tests ##', async function (assert) { assert.test('E2E / IP Addresses Setting', ipAddressesSetting.bind(null, fetchMock)); assert.test('E2E / IP Addresses Setting Debug', ipAddressesSettingDebug.bind(null, fetchMock)); + /* Validate readiness */ + assert.test('E2E / Readiness', readinessSuite.bind(null, fetchMock)); + /* Validate readiness with ready promises */ assert.test('E2E / Ready promise', readyPromiseSuite.bind(null, key, fetchMock)); From eb43b58dbec63da543fd3beeb70926ff8cddb9b1 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Fri, 3 Nov 2023 13:07:45 -0300 Subject: [PATCH 27/38] add changelog entry --- CHANGES.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.txt b/CHANGES.txt index 4bdf958e1..0b8e3197b 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -8,6 +8,7 @@ - Updated the following SDK manager methods to expose flag sets on flag views. - Added `defaultTreatment` property to the `SplitView` object returned by the `split` and `splits` methods of the SDK manager (Related to issue https://github.com/splitio/javascript-commons/issues/225). - Updated @splitsoftware/splitio-commons package to version 1.11.0 that includes vulnerability fixes, and adds the `defaultTreatment` property to the `SplitView` object. + - Bugfixing - Fixed SDK key validation in NodeJS to ensure the SDK_READY_TIMED_OUT event is emitted when a client-side type SDK key is provided instead of a server-side one (Related to issue https://github.com/splitio/javascript-client/issues/768). 10.23.1 (September 22, 2023) - Updated @splitsoftware/splitio-commons package to version 1.9.1. This update removes the handler for 'unload' DOM events, that can prevent browsers from being able to put pages in the back/forward cache for faster back and forward loads (Related to issue https://github.com/splitio/javascript-client/issues/759). From bbad0a44ebccde3510a4837925d0d17b288dc80d Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Fri, 3 Nov 2023 13:24:58 -0300 Subject: [PATCH 28/38] validate ready promise rejection --- src/__tests__/nodeSuites/readiness.spec.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/__tests__/nodeSuites/readiness.spec.js b/src/__tests__/nodeSuites/readiness.spec.js index 622026102..199f29082 100644 --- a/src/__tests__/nodeSuites/readiness.spec.js +++ b/src/__tests__/nodeSuites/readiness.spec.js @@ -39,12 +39,19 @@ export default function (fetchMock, assert) { t.fail('### IS READY - NOT TIMED OUT when it should.'); t.end(); }); - client.once(client.Event.SDK_READY_TIMED_OUT, () => { + client.once(client.Event.SDK_READY_TIMED_OUT, async () => { t.pass('### SDK TIMED OUT - SegmentChanges requests with client-side SDK key should fail with 403. Timed out.'); t.false(client.track('some_key', 'some_tt', 'some_event_type'), 'since client is flagged as destroyed, client.track returns false'); - - client.destroy().then(() => { t.end(); }); + t.equal(client.getTreatment('hierarchical_splits_test'), 'control', 'since client is flagged as destroyed, client.getTreatment returns control'); + + // ready promise should reject + try { + await client.ready(); + } catch (e) { + await client.destroy(); + t.end(); + } }); }); From ddeef834f14f4613c41255306946fca47bb100b5 Mon Sep 17 00:00:00 2001 From: Nicolas Zelaya Date: Fri, 3 Nov 2023 16:37:18 -0300 Subject: [PATCH 29/38] add tests with configs and telemetry latency validations --- src/__tests__/consumer/node_redis.spec.js | 28 +++++++++++++++++++-- src/__tests__/mocks/redis-commands-sets.txt | 2 +- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/__tests__/consumer/node_redis.spec.js b/src/__tests__/consumer/node_redis.spec.js index 0af9822dc..72059e736 100644 --- a/src/__tests__/consumer/node_redis.spec.js +++ b/src/__tests__/consumer/node_redis.spec.js @@ -657,6 +657,12 @@ tape('NodeJS Redis', function (t) { 'Evaluations using Redis storage should be correct for a set.' ); + assert.deepEqual( + await client.getTreatmentsWithConfigByFlagSet('nico@test', 'set_one'), + { 'always-on': { treatment: 'on', config: null }, 'always-off': { treatment: 'off', config: null } }, + 'Evaluations with configs using Redis storage should be correct for a set.' + ); + assert.deepEqual( await client.getTreatmentsByFlagSet('nico@test', 'set_two'), { 'always-off': 'off', 'nico_not': 'off' }, @@ -675,6 +681,12 @@ tape('NodeJS Redis', function (t) { 'Evaluations using Redis storage should be correct for multiple sets.' ); + assert.deepEqual( + await client.getTreatmentsWithConfigByFlagSets('nico@test', ['set_two', 'set_one']), + { 'always-on': { treatment: 'on', config: null }, 'always-off': { treatment: 'off', config: null }, 'nico_not': { treatment: 'off', config: '{"text":"Gallardiola"}' } }, + 'Evaluations with configs using Redis storage should be correct for multiple sets.' + ); + assert.deepEqual( await client.getTreatmentsByFlagSets('nico@test', [243, null, 'set_two', 'set_one', 'invalid_set']), { 'always-on': 'on', 'always-off': 'off', 'nico_not': 'off' }, @@ -690,8 +702,20 @@ tape('NodeJS Redis', function (t) { await client.ready(); // promise already resolved await client.destroy(); - // close server connection - server.close().then(assert.end); + // Validate stored telemetry + exec(`echo "HLEN ${config.storage.prefix}.SPLITIO.telemetry.latencies \n HKEYS ${config.storage.prefix}.SPLITIO.telemetry.latencies" | redis-cli -p ${redisPort}`, (error, stdout) => { + if (error) assert.fail('Redis server should be reachable'); + + const [latenciesCount, ...latenciesForFlagSets] = stdout.split('\n').filter(line => line !== ''); + + assert.true(parseInt(latenciesCount) > 0, 'There are stored latencies'); + assert.true(latenciesForFlagSets.some(s => s.match(`nodejs-${version}/${HOSTNAME_VALUE}/${IP_VALUE}/treatmentsByFlagSet/`), 'The latency entry for treatmentsByFlagSet should be there.')); + assert.true(latenciesForFlagSets.some(s => s.match(`nodejs-${version}/${HOSTNAME_VALUE}/${IP_VALUE}/treatmentsByFlagSets/`), 'The latency entry for treatmentsByFlagSets should be there.')); + + // close server connection + server.close().then(assert.end); + }); + },1000); }); }); diff --git a/src/__tests__/mocks/redis-commands-sets.txt b/src/__tests__/mocks/redis-commands-sets.txt index cf425bce6..fa0397a52 100644 --- a/src/__tests__/mocks/redis-commands-sets.txt +++ b/src/__tests__/mocks/redis-commands-sets.txt @@ -24,7 +24,7 @@ SET 'REDIS_NODE_UT.SPLITIO.split.hierarchical_splits_testing_off' '{"changeN SET 'REDIS_NODE_UT.SPLITIO.split.hierarchical_splits_testing_on' '{"changeNumber":1487277320548,"trafficTypeName":"user","name":"hierarchical_splits_testing_on","seed":1684183541,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}},{"keySelector":{"trafficType":"user","attribute":""},"dependencyMatcherData":{"split":"always-on","treatments":["on"]},"matcherType":"IN_SPLIT_TREATMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":null,"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"in segment all"}]}' SET 'REDIS_NODE_UT.SPLITIO.split.hierarchical_splits_testing_on_negated' '{"changeNumber":1487277320548,"trafficTypeName":"user","name":"hierarchical_splits_testing_on","seed":1684183541,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":true,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}},{"keySelector":{"trafficType":"user","attribute":""},"dependencyMatcherData":{"split":"always-on","treatments":["on"]},"matcherType":"IN_SPLIT_TREATMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":null,"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"in segment all"}]}' SET 'REDIS_NODE_UT.SPLITIO.split.labels' '{"changeNumber":1492023661334,"trafficTypeName":"user","name":"labels","seed":-1240661267,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}},{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"IN_SEGMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":"demo"},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}},{"keySelector":{"trafficType":"user","attribute":"n"},"matcherType":"EQUAL_TO","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"NUMBER","value":123},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":100}],"label":"in segment all and in segment demo and n = 123"}]}' -SET 'REDIS_NODE_UT.SPLITIO.split.nico_not' '{"changeNumber":1489412422181,"trafficTypeName":"user","name":"nico_not","seed":-788702424,"sets":["set_two"],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"IN_SEGMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":"qa"},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":100}],"label":"in segment qa"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.nico_not' '{"changeNumber":1489412422181,"trafficTypeName":"user","name":"nico_not","seed":-788702424,"sets":["set_two"],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"IN_SEGMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":"qa"},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":100}],"label":"in segment qa"}],"configurations":{"off":"{\"text\":\"Gallardiola\"}"}}' SET 'REDIS_NODE_UT.SPLITIO.split.not_part_of' '{"changeNumber":1492627582227,"trafficTypeName":"user","name":"not_part_of","seed":-1643575289,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":"setx"},"matcherType":"PART_OF_SET","negate":true,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["a","b","c"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":100}],"label":"setx not part of [a, b, ...]"}]}' SET 'REDIS_NODE_UT.SPLITIO.split.not_set_contain_all' '{"changeNumber":1492626848560,"trafficTypeName":"user","name":"not_set_contain_all","seed":-1811083551,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":"setx"},"matcherType":"CONTAINS_ALL_OF_SET","negate":true,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["a","b","c"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"setx does not contain all of [a, b, ...]"}]}' SET 'REDIS_NODE_UT.SPLITIO.split.not_set_contain_any' '{"changeNumber":1492627144346,"trafficTypeName":"user","name":"not_set_contain_any","seed":-430318598,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":"setx"},"matcherType":"CONTAINS_ANY_OF_SET","negate":true,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["a","b","c"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":100}],"label":"setx does not contain any of [a, b, ...]"}]}' From 65572e9b70cc5712aeeb8965f2fd0108892724a4 Mon Sep 17 00:00:00 2001 From: Emmanuel Zamora Date: Wed, 8 Nov 2023 16:00:55 -0300 Subject: [PATCH 30/38] Release v10.24.0-beta --- CHANGES.txt | 2 +- package-lock.json | 4 ++-- package.json | 2 +- src/settings/defaults/version.js | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 4bdf958e1..989286d7c 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,4 @@ -10.24.0 (November XX, 2023) +10.24.0 (November 8, 2023) - Added support for Flag Sets on the SDK, which enables grouping feature flags and interacting with the group rather than individually (more details in our documentation): - Added new variations of the get treatment methods to support evaluating flags in given flag set/s. - getTreatmentsByFlagSet and getTreatmentsByFlagSets diff --git a/package-lock.json b/package-lock.json index fe5cc31c9..03895a6dc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@splitsoftware/splitio", - "version": "10.23.2-rc.4", + "version": "10.24.0-beta", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@splitsoftware/splitio", - "version": "10.23.2-rc.4", + "version": "10.24.0-beta", "license": "Apache-2.0", "dependencies": { "@splitsoftware/splitio-commons": "1.10.1-rc.3", diff --git a/package.json b/package.json index 95bb46421..a2b693e0e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio", - "version": "10.23.2-rc.4", + "version": "10.24.0-beta", "description": "Split SDK", "files": [ "README.md", diff --git a/src/settings/defaults/version.js b/src/settings/defaults/version.js index 387ad6159..77bd7d677 100644 --- a/src/settings/defaults/version.js +++ b/src/settings/defaults/version.js @@ -1 +1 @@ -export const packageVersion = '10.23.2-rc.4'; +export const packageVersion = '10.24.0-beta'; From 8040348381e22eb0db5137833ce691058fb7495c Mon Sep 17 00:00:00 2001 From: Emmanuel Zamora Date: Wed, 8 Nov 2023 16:02:30 -0300 Subject: [PATCH 31/38] fix changes file --- CHANGES.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 989286d7c..4bdf958e1 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,4 @@ -10.24.0 (November 8, 2023) +10.24.0 (November XX, 2023) - Added support for Flag Sets on the SDK, which enables grouping feature flags and interacting with the group rather than individually (more details in our documentation): - Added new variations of the get treatment methods to support evaluating flags in given flag set/s. - getTreatmentsByFlagSet and getTreatmentsByFlagSets From e58c8f970b1edf4db193cc0d5e699ac4d7c0f985 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Mon, 27 Nov 2023 15:50:25 -0300 Subject: [PATCH 32/38] Update E2E tests to validate flag sets support in consumer mode --- CHANGES.txt | 6 ++- package-lock.json | 14 +++---- package.json | 2 +- src/__tests__/consumer/node_redis.spec.js | 46 +++++++++++++++-------- src/__tests__/mocks/redis-commands.txt | 5 +++ types/splitio.d.ts | 2 - 6 files changed, 49 insertions(+), 26 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 4bdf958e1..3457d5246 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,7 @@ +10.25.0 (December XX, 2023) + - Added support for Flag Sets in "consumer" mode (Redis storage). + - Updated @splitsoftware/splitio-commons package to version 1.12.0 that includes flag sets support for "consumer" and "partial consumer" modes. + 10.24.0 (November XX, 2023) - Added support for Flag Sets on the SDK, which enables grouping feature flags and interacting with the group rather than individually (more details in our documentation): - Added new variations of the get treatment methods to support evaluating flags in given flag set/s. @@ -5,7 +9,7 @@ - getTreatmentsWithConfigByFlagSets and getTreatmentsWithConfigByFlagSets - Added a new optional Split Filter configuration option. This allows the SDK and Split services to only synchronize the flags in the specified flag sets, avoiding unused or unwanted flags from being synced on the SDK instance, bringing all the benefits from a reduced payload. - Note: Only applicable when the SDK is in charge of the rollout data synchronization. When not applicable, the SDK will log a warning on init. - - Updated the following SDK manager methods to expose flag sets on flag views. + - Added `sets` property to the `SplitView` object returned by the `split` and `splits` methods of the SDK manager to expose flag sets on flag views. - Added `defaultTreatment` property to the `SplitView` object returned by the `split` and `splits` methods of the SDK manager (Related to issue https://github.com/splitio/javascript-commons/issues/225). - Updated @splitsoftware/splitio-commons package to version 1.11.0 that includes vulnerability fixes, and adds the `defaultTreatment` property to the `SplitView` object. diff --git a/package-lock.json b/package-lock.json index 03895a6dc..15c084ac4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "10.24.0-beta", "license": "Apache-2.0", "dependencies": { - "@splitsoftware/splitio-commons": "1.10.1-rc.3", + "@splitsoftware/splitio-commons": "1.12.1-rc.1", "@types/google.analytics": "0.0.40", "@types/ioredis": "^4.28.0", "bloom-filters": "^3.0.0", @@ -874,9 +874,9 @@ "dev": true }, "node_modules/@splitsoftware/splitio-commons": { - "version": "1.10.1-rc.3", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.10.1-rc.3.tgz", - "integrity": "sha512-eqJxAMtqFK7fXFKL8gMGfRsMBdxrYI9tIGUHHpY1NcyeKkn4OWqAOZMhX6z2qLdBArzHi34Li0Lb72o+Bh1Tqg==", + "version": "1.12.1-rc.1", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.12.1-rc.1.tgz", + "integrity": "sha512-nmMFwCMAJ+WujVEfdf1TwVF/e8cqkjYUJTp0Mb+XWK7S0ZUItf4hzXhC1DOQRntBTH5CDxnL6hdkuACEWhfYEw==", "dependencies": { "tslib": "^2.3.1" }, @@ -8446,9 +8446,9 @@ "dev": true }, "@splitsoftware/splitio-commons": { - "version": "1.10.1-rc.3", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.10.1-rc.3.tgz", - "integrity": "sha512-eqJxAMtqFK7fXFKL8gMGfRsMBdxrYI9tIGUHHpY1NcyeKkn4OWqAOZMhX6z2qLdBArzHi34Li0Lb72o+Bh1Tqg==", + "version": "1.12.1-rc.1", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.12.1-rc.1.tgz", + "integrity": "sha512-nmMFwCMAJ+WujVEfdf1TwVF/e8cqkjYUJTp0Mb+XWK7S0ZUItf4hzXhC1DOQRntBTH5CDxnL6hdkuACEWhfYEw==", "requires": { "tslib": "^2.3.1" } diff --git a/package.json b/package.json index a2b693e0e..7f28565df 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "node": ">=6" }, "dependencies": { - "@splitsoftware/splitio-commons": "1.10.1-rc.3", + "@splitsoftware/splitio-commons": "1.12.1-rc.1", "@types/google.analytics": "0.0.40", "@types/ioredis": "^4.28.0", "bloom-filters": "^3.0.0", diff --git a/src/__tests__/consumer/node_redis.spec.js b/src/__tests__/consumer/node_redis.spec.js index 41d1a15af..1516eee46 100644 --- a/src/__tests__/consumer/node_redis.spec.js +++ b/src/__tests__/consumer/node_redis.spec.js @@ -20,9 +20,13 @@ import { truncateTimeFrame } from '@splitsoftware/splitio-commons/src/utils/time const IP_VALUE = ipFunction.address(); const HOSTNAME_VALUE = osFunction.hostname(); const NA = 'NA'; - const redisPort = '6385'; +const TOTAL_RAW_IMPRESSIONS = 16; +const TOTAL_RAW_IMPRESSIONS_IN_EVALUATIONS_WITH_FLAGSETS = 10; +const TOTAL_EVENTS = 2; +const DEDUPED_IMPRESSIONS = 3; + const config = { core: { authorizationKey: 'SOME SDK KEY' // in consumer mode, SDK key is only used to track and log warning regarding duplicated SDK instances @@ -92,6 +96,8 @@ tape('NodeJS Redis', function (t) { assert.equal(await client.getTreatment('UT_Segment_member', 'UT_IN_SEGMENT'), 'control', 'Evaluations using Redis storage should be control until connection is stablished.'); assert.equal(await client.getTreatment('other', 'UT_IN_SEGMENT'), 'control', 'Evaluations using Redis storage should be control until connection is stablished.'); + assert.deepEqual(await client.getTreatmentsWithConfigByFlagSets('other', ['set_a']), {}, 'Flag sets evaluations using Redis storage should be empty until connection is stablished.'); + await client.ready(); assert.equal(await client.getTreatment('UT_Segment_member', 'UT_IN_SEGMENT'), 'on', 'Evaluations using Redis storage should be correct.'); @@ -135,8 +141,18 @@ tape('NodeJS Redis', function (t) { assert.equal(typeof client.track('nicolas@split.io', 'user', 'test.redis.event', 18).then, 'function', 'Track calls should always return a promise on Redis mode.'); assert.equal(typeof client.track().then, 'function', 'Track calls should always return a promise on Redis mode, even when parameters are incorrect.'); - assert.true(await client.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was succesfully queued the promise will resolve to true'); - assert.false(await client.track(), 'If the event was NOT succesfully queued the promise will resolve to false'); + assert.true(await client.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was successfully queued the promise will resolve to true'); + assert.false(await client.track(), 'If the event was NOT successfully queued the promise will resolve to false'); + + // Evaluations with flag sets + assert.deepEqual(await client.getTreatmentsByFlagSet('other', 'set_a'), { with_set_a: 'on', with_sets_a_b: 'on' }, 'Evaluations with getTreatmentsByFlagSet should be correct.'); + assert.deepEqual(await client.getTreatmentsByFlagSets('other', ['set_a', 'set_b']), { with_set_a: 'on', with_set_b: 'on', with_sets_a_b: 'on' }, 'Evaluations with getTreatmentsByFlagSets should be correct.'); + assert.deepEqual(await client.getTreatmentsWithConfigByFlagSet('other', 'set_b'), { with_set_b: { treatment: 'on', config: '{}' }, with_sets_a_b: { treatment: 'on', config: null } }, 'Evaluations with getTreatmentsWithConfigByFlagSet should be correct.'); + assert.deepEqual(await client.getTreatmentsWithConfigByFlagSets('other', ['set_a', 'set_b']), { with_set_a: { treatment: 'on', config: null }, with_set_b: { treatment: 'on', config: '{}' }, with_sets_a_b: { treatment: 'on', config: null } }, 'Evaluations with getTreatmentsWithConfigByFlagSets should be correct.'); + + assert.deepEqual(await client.getTreatmentsByFlagSet('other'), {}, 'Evaluations without flag set should be empty.'); + assert.deepEqual(await client.getTreatmentsByFlagSets('other', []), {}, 'Evaluations without flag sets should be empty.'); + assert.deepEqual(await client.getTreatmentsByFlagSets('other', ['non_existent_set']), {}, 'Evaluations with non existent flag sets should be empty.'); await client.ready(); // promise already resolved await client.destroy(); @@ -146,7 +162,7 @@ tape('NodeJS Redis', function (t) { if (error) assert.fail('Redis server should be reachable'); const trackedImpressionsAndEvents = stdout.split('\n').filter(line => line !== '').map(line => parseInt(line)); - assert.deepEqual(trackedImpressionsAndEvents, [16, 2], 'Tracked impressions and events should be stored in Redis'); + assert.deepEqual(trackedImpressionsAndEvents, [TOTAL_RAW_IMPRESSIONS + TOTAL_RAW_IMPRESSIONS_IN_EVALUATIONS_WITH_FLAGSETS, TOTAL_EVENTS], 'Tracked impressions and events should be stored in Redis'); // Validate stored telemetry exec(`echo "HLEN ${config.storage.prefix}.SPLITIO.telemetry.latencies \n HLEN ${config.storage.prefix}.SPLITIO.telemetry.exceptions \n HGET ${config.storage.prefix}.SPLITIO.telemetry.init nodejs-${version}/${HOSTNAME_VALUE}/${IP_VALUE}" | redis-cli -p ${redisPort}`, (error, stdout) => { @@ -253,8 +269,8 @@ tape('NodeJS Redis', function (t) { assert.equal(typeof client.track('nicolas@split.io', 'user', 'test.redis.event', 18).then, 'function', 'Track calls should always return a promise on Redis mode.'); assert.equal(typeof client.track().then, 'function', 'Track calls should always return a promise on Redis mode, even when parameters are incorrect.'); - assert.true(await client.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was succesfully queued the promise will resolve to true'); - assert.false(await client.track(), 'If the event was NOT succesfully queued the promise will resolve to false'); + assert.true(await client.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was successfully queued the promise will resolve to true'); + assert.false(await client.track(), 'If the event was NOT successfully queued the promise will resolve to false'); await client.ready(); // promise already resolved await client.destroy(); @@ -269,7 +285,7 @@ tape('NodeJS Redis', function (t) { if (error) assert.fail('Redis server should be reachable'); const trackedImpressionsAndEvents = stdout.split('\n').filter(line => line !== '').map(line => parseInt(line)); - assert.deepEqual(trackedImpressionsAndEvents, [13, 2], 'Tracked impressions and events should be stored in Redis'); + assert.deepEqual(trackedImpressionsAndEvents, [TOTAL_RAW_IMPRESSIONS - DEDUPED_IMPRESSIONS, TOTAL_EVENTS], 'Tracked impressions and events should be stored in Redis'); // Validate stored telemetry exec(`echo "HLEN ${config.storage.prefix}.SPLITIO.telemetry.latencies \n HLEN ${config.storage.prefix}.SPLITIO.telemetry.exceptions \n HGET ${config.storage.prefix}.SPLITIO.telemetry.init nodejs-${version}/${HOSTNAME_VALUE}/${IP_VALUE}" | redis-cli -p ${redisPort}`, (error, stdout) => { @@ -349,8 +365,8 @@ tape('NodeJS Redis', function (t) { assert.equal(typeof client.track('nicolas@split.io', 'user', 'test.redis.event', 18).then, 'function', 'Track calls should always return a promise on Redis mode.'); assert.equal(typeof client.track().then, 'function', 'Track calls should always return a promise on Redis mode, even when parameters are incorrect.'); - assert.true(await client.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was succesfully queued the promise will resolve to true'); - assert.false(await client.track(), 'If the event was NOT succesfully queued the promise will resolve to false'); + assert.true(await client.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was successfully queued the promise will resolve to true'); + assert.false(await client.track(), 'If the event was NOT successfully queued the promise will resolve to false'); await client.ready(); // promise already resolved await client.destroy(); @@ -371,7 +387,7 @@ tape('NodeJS Redis', function (t) { if (error) assert.fail('Redis server should be reachable'); const trackedImpressionsAndEvents = stdout.split('\n').filter(line => line !== '').map(line => parseInt(line)); - assert.deepEqual(trackedImpressionsAndEvents, [0, 2], 'No impressions are stored in Redis in NONE impressions mode'); + assert.deepEqual(trackedImpressionsAndEvents, [0, TOTAL_EVENTS], 'No impressions are stored in Redis in NONE impressions mode'); // Validate stored telemetry exec(`echo "HLEN ${config.storage.prefix}.SPLITIO.telemetry.latencies \n HLEN ${config.storage.prefix}.SPLITIO.telemetry.exceptions \n HGET ${config.storage.prefix}.SPLITIO.telemetry.init nodejs-${version}/${HOSTNAME_VALUE}/${IP_VALUE}" | redis-cli -p ${redisPort}`, (error, stdout) => { @@ -409,7 +425,7 @@ tape('NodeJS Redis', function (t) { assert.equal(treatment, 'control', 'Evaluations using Redis storage should be control until Redis connection is stablished.'); }); client.track('nicolas@split.io', 'user', 'test.redis.event', 18).then(result => { - assert.true(result, 'If the event was succesfully queued the promise will resolve to true once Redis connection is stablished'); + assert.true(result, 'If the event was successfully queued the promise will resolve to true once Redis connection is stablished'); }); // SDK_READY_TIMED_OUT event must be emitted after 100 millis @@ -448,8 +464,8 @@ tape('NodeJS Redis', function (t) { // some asserts to test regular usage assert.equal(await client.getTreatment('UT_Segment_member', 'UT_IN_SEGMENT'), 'on', 'Evaluations using Redis storage should be correct.'); assert.equal(await client.getTreatment('other', 'UT_IN_SEGMENT'), 'off', 'Evaluations using Redis storage should be correct.'); - assert.true(await client.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was succesfully queued the promise will resolve to true'); - assert.false(await client.track(), 'If the event was NOT succesfully queued the promise will resolve to false'); + assert.true(await client.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was successfully queued the promise will resolve to true'); + assert.false(await client.track(), 'If the event was NOT successfully queued the promise will resolve to false'); await client.destroy(); assert.pass(); @@ -474,8 +490,8 @@ tape('NodeJS Redis', function (t) { // some asserts to test regular usage assert.equal(await client2.getTreatment('UT_Segment_member', 'UT_IN_SEGMENT'), 'on', 'Evaluations using Redis storage should be correct.'); assert.equal(await client2.getTreatment('other', 'UT_IN_SEGMENT'), 'off', 'Evaluations using Redis storage should be correct.'); - assert.true(await client2.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was succesfully queued the promise will resolve to true'); - assert.false(await client2.track(), 'If the event was NOT succesfully queued the promise will resolve to false'); + assert.true(await client2.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was successfully queued the promise will resolve to true'); + assert.false(await client2.track(), 'If the event was NOT successfully queued the promise will resolve to false'); await client2.destroy(); diff --git a/src/__tests__/mocks/redis-commands.txt b/src/__tests__/mocks/redis-commands.txt index 2e5daae42..00c31fa61 100644 --- a/src/__tests__/mocks/redis-commands.txt +++ b/src/__tests__/mocks/redis-commands.txt @@ -39,3 +39,8 @@ SET 'REDIS_NODE_UT.SPLITIO.split.testing_traffic_type' '{"changeNumber":1489 SET 'REDIS_NODE_UT.SPLITIO.split.testing_traffic_types' '{"changeNumber":1490974465415,"trafficTypeName":"machine","name":"testing_traffic_types","seed":475616886,"status":"ACTIVE","killed":false,"defaultTreatment":"on","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"","attribute":""},"matcherType":"WHITELIST","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["sarasa"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100}],"label":"whitelisted"},{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"","attribute":""},"matcherType":"WHITELIST","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["excluded"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"off","size":100}],"label":"whitelisted"},{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}},{"keySelector":{"trafficType":"machine","attribute":""},"matcherType":"IN_SEGMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":"testing_traffic_type"},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":100}],"label":"in segment all and in segment testing_traffic_type"}]}' SET 'REDIS_NODE_UT.SPLITIO.split.traffic_allocation_testing' '{"changeNumber":1490974123779,"trafficTypeName":"user","name":"traffic_allocation_testing","seed":1716284102,"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"in segment all"}]}' SET 'REDIS_NODE_UT.SPLITIO.splits.till' 1492723024413 +SADD 'REDIS_NODE_UT.SPLITIO.flagSet.set_a' with_set_a with_sets_a_b +SADD 'REDIS_NODE_UT.SPLITIO.flagSet.set_b' with_set_b with_sets_a_b +SET 'REDIS_NODE_UT.SPLITIO.split.with_set_a' '{"changeNumber":1487277320548,"trafficTypeName":"user","name":"with_set_a","seed":1684183541,"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"in segment all"}],"sets":["set_a"]}' +SET 'REDIS_NODE_UT.SPLITIO.split.with_set_b' '{"changeNumber":1487277320548,"trafficTypeName":"user","name":"with_set_b","seed":1684183541,"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"in segment all"}],"sets":["set_b"],"configurations":{"on":"{}"}}' +SET 'REDIS_NODE_UT.SPLITIO.split.with_sets_a_b' '{"changeNumber":1487277320548,"trafficTypeName":"user","name":"with_set_a","seed":1684183541,"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"in segment all"}],"sets":["set_a","set_b"]}' \ No newline at end of file diff --git a/types/splitio.d.ts b/types/splitio.d.ts index c01f999c7..6006ccb7b 100644 --- a/types/splitio.d.ts +++ b/types/splitio.d.ts @@ -223,8 +223,6 @@ interface ISharedSettings { * List of feature flag filters. These filters are used to fetch a subset of the feature flag definitions in your environment, in order to reduce the delay of the SDK to be ready. * This configuration is only meaningful when the SDK is working in "standalone" mode. * - * At the moment, only one type of feature flag filter is supported: by name. - * * Example: * `splitFilter: [ * { type: 'byName', values: ['my_feature_flag_1', 'my_feature_flag_2'] }, // will fetch feature flags named 'my_feature_flag_1' and 'my_feature_flag_2' From 3cd341f17c7e2bfc1ccdc846aced53f7c76e9edb Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Wed, 29 Nov 2023 17:25:42 -0300 Subject: [PATCH 33/38] Add manager method asserts for consumer mode with Redis --- CHANGES.txt | 14 +++++------ package-lock.json | 14 +++++------ package.json | 2 +- src/__tests__/consumer/node_redis.spec.js | 29 +++++++++++++++++++---- 4 files changed, 40 insertions(+), 19 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 182f96ac2..c817d63fb 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,11 +1,11 @@ 10.24.0 (December XX, 2023) - - Added support for Flag Sets on the SDK, which enables grouping feature flags and interacting with the group rather than individually (more details in our documentation): - - Added new variations of the get treatment methods to support evaluating flags in given flag set/s. - - getTreatmentsByFlagSet and getTreatmentsByFlagSets - - getTreatmentsWithConfigByFlagSets and getTreatmentsWithConfigByFlagSets - - Added a new optional Split Filter configuration option. This allows the SDK and Split services to only synchronize the flags in the specified flag sets, avoiding unused or unwanted flags from being synced on the SDK instance, bringing all the benefits from a reduced payload. - - Note: Only applicable when the SDK is in charge of the rollout data synchronization. When not applicable, the SDK will log a warning on init. - - Added `sets` property to the `SplitView` object returned by the `split` and `splits` methods of the SDK manager to expose flag sets on flag views. + - Added support for Flag Sets on the SDK, which enables grouping feature flags and interacting with the group rather than individually (more details in our documentation): + - Added new variations of the get treatment methods to support evaluating flags in given flag set/s. + - getTreatmentsByFlagSet and getTreatmentsByFlagSets + - getTreatmentsWithConfigByFlagSets and getTreatmentsWithConfigByFlagSets + - Added a new optional Split Filter configuration option. This allows the SDK and Split services to only synchronize the flags in the specified flag sets, avoiding unused or unwanted flags from being synced on the SDK instance, bringing all the benefits from a reduced payload. + - Note: Only applicable when the SDK is in charge of the rollout data synchronization. When not applicable, the SDK will log a warning on init. + - Added `sets` property to the `SplitView` object returned by the `split` and `splits` methods of the SDK manager to expose flag sets on flag views. - Added `defaultTreatment` property to the `SplitView` object returned by the `split` and `splits` methods of the SDK manager (Related to issue https://github.com/splitio/javascript-commons/issues/225). - Updated @splitsoftware/splitio-commons package to version 1.12.0 that includes vulnerability fixes, flag sets support, and adds the `defaultTreatment` property to the `SplitView` object. - Bugfixing - Fixed SDK key validation in NodeJS to ensure the SDK_READY_TIMED_OUT event is emitted when a client-side type SDK key is provided instead of a server-side one (Related to issue https://github.com/splitio/javascript-client/issues/768). diff --git a/package-lock.json b/package-lock.json index 250edddd1..2d7a1e6df 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "10.24.0-beta", "license": "Apache-2.0", "dependencies": { - "@splitsoftware/splitio-commons": "1.12.1-rc.1", + "@splitsoftware/splitio-commons": "1.12.1-rc.2", "@types/google.analytics": "0.0.40", "@types/ioredis": "^4.28.0", "bloom-filters": "^3.0.0", @@ -874,9 +874,9 @@ "dev": true }, "node_modules/@splitsoftware/splitio-commons": { - "version": "1.12.1-rc.1", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.12.1-rc.1.tgz", - "integrity": "sha512-nmMFwCMAJ+WujVEfdf1TwVF/e8cqkjYUJTp0Mb+XWK7S0ZUItf4hzXhC1DOQRntBTH5CDxnL6hdkuACEWhfYEw==", + "version": "1.12.1-rc.2", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.12.1-rc.2.tgz", + "integrity": "sha512-b8wVebAbTYDLKoUwWf7hwP+xlyVlbH3hX9+kPTbQWYeHkX2JNBnbEBSM5sLlYUpwbVW6mLEd59qwjwEsa9ZD5g==", "dependencies": { "tslib": "^2.3.1" }, @@ -8446,9 +8446,9 @@ "dev": true }, "@splitsoftware/splitio-commons": { - "version": "1.12.1-rc.1", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.12.1-rc.1.tgz", - "integrity": "sha512-nmMFwCMAJ+WujVEfdf1TwVF/e8cqkjYUJTp0Mb+XWK7S0ZUItf4hzXhC1DOQRntBTH5CDxnL6hdkuACEWhfYEw==", + "version": "1.12.1-rc.2", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.12.1-rc.2.tgz", + "integrity": "sha512-b8wVebAbTYDLKoUwWf7hwP+xlyVlbH3hX9+kPTbQWYeHkX2JNBnbEBSM5sLlYUpwbVW6mLEd59qwjwEsa9ZD5g==", "requires": { "tslib": "^2.3.1" } diff --git a/package.json b/package.json index 7f28565df..710d8f88a 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "node": ">=6" }, "dependencies": { - "@splitsoftware/splitio-commons": "1.12.1-rc.1", + "@splitsoftware/splitio-commons": "1.12.1-rc.2", "@types/google.analytics": "0.0.40", "@types/ioredis": "^4.28.0", "bloom-filters": "^3.0.0", diff --git a/src/__tests__/consumer/node_redis.spec.js b/src/__tests__/consumer/node_redis.spec.js index 1516eee46..6f96a48ea 100644 --- a/src/__tests__/consumer/node_redis.spec.js +++ b/src/__tests__/consumer/node_redis.spec.js @@ -60,6 +60,9 @@ const expectedImpressionCount = [ `hierarchical_splits_testing_on_negated::${truncateTimeFrame(timeFrame)}`, '1', ]; +const expectedSplitName = 'hierarchical_splits_testing_on'; +const expectedSplitView = { name: 'hierarchical_splits_testing_on', trafficType: 'user', killed: false, changeNumber: 1487277320548, treatments: ['on', 'off'], configs: {}, sets: [], defaultTreatment: 'off' }; + /** * Initialize redis server and run a cli bash command to load redis with data to do the proper tests */ @@ -93,13 +96,23 @@ tape('NodeJS Redis', function (t) { .then(async (server) => { const sdk = SplitFactory(config); const client = sdk.client(); + const manager = sdk.manager(); + + /** Evaluation, track and manager methods before SDK_READY */ + client.getTreatment('UT_Segment_member', 'UT_IN_SEGMENT').then(result => assert.equal(result, 'control', 'Evaluations using Redis storage should be control until connection is stablished.')); + client.getTreatment('other', 'UT_IN_SEGMENT').then(result => assert.equal(result, 'control', 'Evaluations using Redis storage should be control until connection is stablished.')); + client.getTreatmentsWithConfigByFlagSets('other', ['set_a']).then(result => assert.deepEqual(result, {}, 'Flag sets evaluations using Redis storage should be empty until connection is stablished.')); + + manager.names().then((result) => assert.deepEqual(result, [], 'manager `names` method returns an empty list of split names if called before SDK_READY or Redis operation fail')); + manager.split(expectedSplitName).then((result) => assert.deepEqual(result, null, 'manager `split` method returns a null split view if called before SDK_READY or Redis operation fail')); + manager.splits().then((result) => assert.deepEqual(result, [], 'manager `splits` method returns an empty list of split views if called before SDK_READY or Redis operation fail')); - assert.equal(await client.getTreatment('UT_Segment_member', 'UT_IN_SEGMENT'), 'control', 'Evaluations using Redis storage should be control until connection is stablished.'); - assert.equal(await client.getTreatment('other', 'UT_IN_SEGMENT'), 'control', 'Evaluations using Redis storage should be control until connection is stablished.'); - assert.deepEqual(await client.getTreatmentsWithConfigByFlagSets('other', ['set_a']), {}, 'Flag sets evaluations using Redis storage should be empty until connection is stablished.'); + client.track('nicolas@split.io', 'user', 'before.ready', 18).then((result) => assert.true(result, 'Redis adapter queue "rpush" operations until it is ready.')); await client.ready(); + /** Evaluation, track and manager methods on SDK_READY */ + assert.equal(await client.getTreatment('UT_Segment_member', 'UT_IN_SEGMENT'), 'on', 'Evaluations using Redis storage should be correct.'); assert.equal(await client.getTreatment('other', 'UT_IN_SEGMENT'), 'off', 'Evaluations using Redis storage should be correct.'); @@ -154,6 +167,15 @@ tape('NodeJS Redis', function (t) { assert.deepEqual(await client.getTreatmentsByFlagSets('other', []), {}, 'Evaluations without flag sets should be empty.'); assert.deepEqual(await client.getTreatmentsByFlagSets('other', ['non_existent_set']), {}, 'Evaluations with non existent flag sets should be empty.'); + // Manager methods + const splitNames = await manager.names(); + assert.equal(splitNames.length, 28, 'manager `names` method returns the list of split names asynchronously'); + assert.equal(splitNames.indexOf(expectedSplitName) > -1, true, 'list of split names should contain expected splits'); + assert.deepEqual(await manager.split(expectedSplitName), expectedSplitView, 'manager `split` method returns the split view of the given split name asynchronously'); + const splitViews = await manager.splits(); + assert.equal(splitViews.length, 28, 'manager `splits` method returns the list of split views asynchronously'); + assert.deepEqual(splitViews.find(splitView => splitView.name === expectedSplitName), expectedSplitView, 'manager `split` method returns the split view of the given split name asynchronously'); + await client.ready(); // promise already resolved await client.destroy(); @@ -266,7 +288,6 @@ tape('NodeJS Redis', function (t) { // this should be deduped assert.equal(await client.getTreatment('UT_Segment_member', 'hierarchical_splits_testing_on_negated'), 'off', 'Evaluations using Redis storage should be correct.'); - assert.equal(typeof client.track('nicolas@split.io', 'user', 'test.redis.event', 18).then, 'function', 'Track calls should always return a promise on Redis mode.'); assert.equal(typeof client.track().then, 'function', 'Track calls should always return a promise on Redis mode, even when parameters are incorrect.'); assert.true(await client.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was successfully queued the promise will resolve to true'); From 9fee387fdcd9ec929f325dcf858dbcf92040cfaf Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Thu, 30 Nov 2023 17:41:15 -0300 Subject: [PATCH 34/38] Updates --- CHANGES.txt | 11 +- src/__tests__/consumer/node_redis.spec.js | 162 +++++++++--------- ...-sets.txt => redis-commands-flag-sets.txt} | 0 types/splitio.d.ts | 2 - 4 files changed, 88 insertions(+), 87 deletions(-) rename src/__tests__/mocks/{redis-commands-sets.txt => redis-commands-flag-sets.txt} (100%) diff --git a/CHANGES.txt b/CHANGES.txt index 0b8e3197b..87e4076e3 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,14 +1,15 @@ -10.24.0 (November XX, 2023) - - Added support for Flag Sets on the SDK, which enables grouping feature flags and interacting with the group rather than individually (more details in our documentation): +10.24.0 (December XX, 2023) + - Added support for Flag Sets on the SDK, which enables grouping feature flags and interacting with the group rather than individually (more details in our documentation): - Added new variations of the get treatment methods to support evaluating flags in given flag set/s. - getTreatmentsByFlagSet and getTreatmentsByFlagSets - getTreatmentsWithConfigByFlagSets and getTreatmentsWithConfigByFlagSets - Added a new optional Split Filter configuration option. This allows the SDK and Split services to only synchronize the flags in the specified flag sets, avoiding unused or unwanted flags from being synced on the SDK instance, bringing all the benefits from a reduced payload. - Note: Only applicable when the SDK is in charge of the rollout data synchronization. When not applicable, the SDK will log a warning on init. - - Updated the following SDK manager methods to expose flag sets on flag views. + - Added `sets` property to the `SplitView` object returned by the `split` and `splits` methods of the SDK manager to expose flag sets on flag views. - Added `defaultTreatment` property to the `SplitView` object returned by the `split` and `splits` methods of the SDK manager (Related to issue https://github.com/splitio/javascript-commons/issues/225). - - Updated @splitsoftware/splitio-commons package to version 1.11.0 that includes vulnerability fixes, and adds the `defaultTreatment` property to the `SplitView` object. - - Bugfixing - Fixed SDK key validation in NodeJS to ensure the SDK_READY_TIMED_OUT event is emitted when a client-side type SDK key is provided instead of a server-side one (Related to issue https://github.com/splitio/javascript-client/issues/768). + - Updated @splitsoftware/splitio-commons package to version 1.12.0 that includes vulnerability fixes, flag sets support, and other improvements. + - Updated Redis adapter to handle timeouts and queueing of some missing commands: 'hincrby', 'popNRaw', and 'pipeline.exec'. + - Bugfixing - Fixed manager methods in consumer modes to return results in a promise when the SDK is not operational (not ready or destroyed). 10.23.1 (September 22, 2023) - Updated @splitsoftware/splitio-commons package to version 1.9.1. This update removes the handler for 'unload' DOM events, that can prevent browsers from being able to put pages in the back/forward cache for faster back and forward loads (Related to issue https://github.com/splitio/javascript-client/issues/759). diff --git a/src/__tests__/consumer/node_redis.spec.js b/src/__tests__/consumer/node_redis.spec.js index 72059e736..74f233da0 100644 --- a/src/__tests__/consumer/node_redis.spec.js +++ b/src/__tests__/consumer/node_redis.spec.js @@ -23,6 +23,10 @@ const NA = 'NA'; const redisPort = '6385'; +const TOTAL_RAW_IMPRESSIONS = 16; +const TOTAL_EVENTS = 2; +const DEDUPED_IMPRESSIONS = 3; + const config = { core: { authorizationKey: 'SOME SDK KEY' // in consumer mode, SDK key is only used to track and log warning regarding duplicated SDK instances @@ -58,7 +62,7 @@ const expectedImpressionCount = [ const MOCKS = { '': 'redis-commands', - 'flag_sets': 'redis-commands-sets' + 'flag_sets': 'redis-commands-flag-sets' }; /** @@ -141,8 +145,8 @@ tape('NodeJS Redis', function (t) { assert.equal(typeof client.track('nicolas@split.io', 'user', 'test.redis.event', 18).then, 'function', 'Track calls should always return a promise on Redis mode.'); assert.equal(typeof client.track().then, 'function', 'Track calls should always return a promise on Redis mode, even when parameters are incorrect.'); - assert.true(await client.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was succesfully queued the promise will resolve to true'); - assert.false(await client.track(), 'If the event was NOT succesfully queued the promise will resolve to false'); + assert.true(await client.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was successfully queued the promise will resolve to true'); + assert.false(await client.track(), 'If the event was NOT successfully queued the promise will resolve to false'); await client.ready(); // promise already resolved await client.destroy(); @@ -152,7 +156,7 @@ tape('NodeJS Redis', function (t) { if (error) assert.fail('Redis server should be reachable'); const trackedImpressionsAndEvents = stdout.split('\n').filter(line => line !== '').map(line => parseInt(line)); - assert.deepEqual(trackedImpressionsAndEvents, [16, 2], 'Tracked impressions and events should be stored in Redis'); + assert.deepEqual(trackedImpressionsAndEvents, [TOTAL_RAW_IMPRESSIONS, TOTAL_EVENTS], 'Tracked impressions and events should be stored in Redis'); // Validate stored telemetry exec(`echo "HLEN ${config.storage.prefix}.SPLITIO.telemetry.latencies \n HLEN ${config.storage.prefix}.SPLITIO.telemetry.exceptions \n HGET ${config.storage.prefix}.SPLITIO.telemetry.init nodejs-${version}/${HOSTNAME_VALUE}/${IP_VALUE}" | redis-cli -p ${redisPort}`, (error, stdout) => { @@ -259,8 +263,8 @@ tape('NodeJS Redis', function (t) { assert.equal(typeof client.track('nicolas@split.io', 'user', 'test.redis.event', 18).then, 'function', 'Track calls should always return a promise on Redis mode.'); assert.equal(typeof client.track().then, 'function', 'Track calls should always return a promise on Redis mode, even when parameters are incorrect.'); - assert.true(await client.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was succesfully queued the promise will resolve to true'); - assert.false(await client.track(), 'If the event was NOT succesfully queued the promise will resolve to false'); + assert.true(await client.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was successfully queued the promise will resolve to true'); + assert.false(await client.track(), 'If the event was NOT successfully queued the promise will resolve to false'); await client.ready(); // promise already resolved await client.destroy(); @@ -275,7 +279,7 @@ tape('NodeJS Redis', function (t) { if (error) assert.fail('Redis server should be reachable'); const trackedImpressionsAndEvents = stdout.split('\n').filter(line => line !== '').map(line => parseInt(line)); - assert.deepEqual(trackedImpressionsAndEvents, [13, 2], 'Tracked impressions and events should be stored in Redis'); + assert.deepEqual(trackedImpressionsAndEvents, [TOTAL_RAW_IMPRESSIONS - DEDUPED_IMPRESSIONS, TOTAL_EVENTS], 'Tracked impressions and events should be stored in Redis'); // Validate stored telemetry exec(`echo "HLEN ${config.storage.prefix}.SPLITIO.telemetry.latencies \n HLEN ${config.storage.prefix}.SPLITIO.telemetry.exceptions \n HGET ${config.storage.prefix}.SPLITIO.telemetry.init nodejs-${version}/${HOSTNAME_VALUE}/${IP_VALUE}" | redis-cli -p ${redisPort}`, (error, stdout) => { @@ -355,8 +359,8 @@ tape('NodeJS Redis', function (t) { assert.equal(typeof client.track('nicolas@split.io', 'user', 'test.redis.event', 18).then, 'function', 'Track calls should always return a promise on Redis mode.'); assert.equal(typeof client.track().then, 'function', 'Track calls should always return a promise on Redis mode, even when parameters are incorrect.'); - assert.true(await client.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was succesfully queued the promise will resolve to true'); - assert.false(await client.track(), 'If the event was NOT succesfully queued the promise will resolve to false'); + assert.true(await client.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was successfully queued the promise will resolve to true'); + assert.false(await client.track(), 'If the event was NOT successfully queued the promise will resolve to false'); await client.ready(); // promise already resolved await client.destroy(); @@ -377,7 +381,7 @@ tape('NodeJS Redis', function (t) { if (error) assert.fail('Redis server should be reachable'); const trackedImpressionsAndEvents = stdout.split('\n').filter(line => line !== '').map(line => parseInt(line)); - assert.deepEqual(trackedImpressionsAndEvents, [0, 2], 'No impressions are stored in Redis in NONE impressions mode'); + assert.deepEqual(trackedImpressionsAndEvents, [0, TOTAL_EVENTS], 'No impressions are stored in Redis in NONE impressions mode'); // Validate stored telemetry exec(`echo "HLEN ${config.storage.prefix}.SPLITIO.telemetry.latencies \n HLEN ${config.storage.prefix}.SPLITIO.telemetry.exceptions \n HGET ${config.storage.prefix}.SPLITIO.telemetry.init nodejs-${version}/${HOSTNAME_VALUE}/${IP_VALUE}" | redis-cli -p ${redisPort}`, (error, stdout) => { @@ -415,7 +419,7 @@ tape('NodeJS Redis', function (t) { assert.equal(treatment, 'control', 'Evaluations using Redis storage should be control until Redis connection is stablished.'); }); client.track('nicolas@split.io', 'user', 'test.redis.event', 18).then(result => { - assert.true(result, 'If the event was succesfully queued the promise will resolve to true once Redis connection is stablished'); + assert.true(result, 'If the event was successfully queued the promise will resolve to true once Redis connection is stablished'); }); // SDK_READY_TIMED_OUT event must be emitted after 100 millis @@ -454,8 +458,8 @@ tape('NodeJS Redis', function (t) { // some asserts to test regular usage assert.equal(await client.getTreatment('UT_Segment_member', 'UT_IN_SEGMENT'), 'on', 'Evaluations using Redis storage should be correct.'); assert.equal(await client.getTreatment('other', 'UT_IN_SEGMENT'), 'off', 'Evaluations using Redis storage should be correct.'); - assert.true(await client.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was succesfully queued the promise will resolve to true'); - assert.false(await client.track(), 'If the event was NOT succesfully queued the promise will resolve to false'); + assert.true(await client.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was successfully queued the promise will resolve to true'); + assert.false(await client.track(), 'If the event was NOT successfully queued the promise will resolve to false'); await client.destroy(); assert.pass(); @@ -480,8 +484,8 @@ tape('NodeJS Redis', function (t) { // some asserts to test regular usage assert.equal(await client2.getTreatment('UT_Segment_member', 'UT_IN_SEGMENT'), 'on', 'Evaluations using Redis storage should be correct.'); assert.equal(await client2.getTreatment('other', 'UT_IN_SEGMENT'), 'off', 'Evaluations using Redis storage should be correct.'); - assert.true(await client2.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was succesfully queued the promise will resolve to true'); - assert.false(await client2.track(), 'If the event was NOT succesfully queued the promise will resolve to false'); + assert.true(await client2.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was successfully queued the promise will resolve to true'); + assert.false(await client2.track(), 'If the event was NOT successfully queued the promise will resolve to false'); await client2.destroy(); @@ -645,78 +649,76 @@ tape('NodeJS Redis', function (t) { const sdk = SplitFactory(config); const client = sdk.client(); + + client.getTreatmentsWithConfigByFlagSets('other', ['set_one']).then(result => assert.deepEqual(result, {}, 'Flag sets evaluations using Redis storage should be empty until connection is ready.')); + await client.ready(); - // @TODO: Working to remove this crap, debugging RedisAdapter queueing and why it doesnt like the pipeline exec. - setTimeout(async function () { - - - assert.deepEqual( - await client.getTreatmentsByFlagSet('nico@test', 'set_one'), - { 'always-on': 'on', 'always-off': 'off' }, - 'Evaluations using Redis storage should be correct for a set.' - ); - - assert.deepEqual( - await client.getTreatmentsWithConfigByFlagSet('nico@test', 'set_one'), - { 'always-on': { treatment: 'on', config: null }, 'always-off': { treatment: 'off', config: null } }, - 'Evaluations with configs using Redis storage should be correct for a set.' - ); - - assert.deepEqual( - await client.getTreatmentsByFlagSet('nico@test', 'set_two'), - { 'always-off': 'off', 'nico_not': 'off' }, - 'Evaluations using Redis storage should be correct for a set.' - ); - - assert.deepEqual( - await client.getTreatmentsByFlagSet('nico@test', 'set_invalid'), - {}, - 'Evaluations using Redis storage should properly handle all invalid sets.' - ); - - assert.deepEqual( - await client.getTreatmentsByFlagSets('nico@test', ['set_two', 'set_one']), - { 'always-on': 'on', 'always-off': 'off', 'nico_not': 'off' }, - 'Evaluations using Redis storage should be correct for multiple sets.' - ); - - assert.deepEqual( - await client.getTreatmentsWithConfigByFlagSets('nico@test', ['set_two', 'set_one']), - { 'always-on': { treatment: 'on', config: null }, 'always-off': { treatment: 'off', config: null }, 'nico_not': { treatment: 'off', config: '{"text":"Gallardiola"}' } }, - 'Evaluations with configs using Redis storage should be correct for multiple sets.' - ); - - assert.deepEqual( - await client.getTreatmentsByFlagSets('nico@test', [243, null, 'set_two', 'set_one', 'invalid_set']), - { 'always-on': 'on', 'always-off': 'off', 'nico_not': 'off' }, - 'Evaluations using Redis storage should be correct for multiple sets, discarding invalids.' - ); - - assert.deepEqual( - await client.getTreatmentsByFlagSets('nico@test', [243, null, 'invalid_set']), - {}, - 'Evaluations using Redis storage should properly handle all invalid sets.' - ); - - await client.ready(); // promise already resolved - await client.destroy(); + assert.deepEqual( + await client.getTreatmentsByFlagSet('nico@test', 'set_one'), + { 'always-on': 'on', 'always-off': 'off' }, + 'Evaluations using Redis storage should be correct for a set.' + ); + + assert.deepEqual( + await client.getTreatmentsWithConfigByFlagSet('nico@test', 'set_one'), + { 'always-on': { treatment: 'on', config: null }, 'always-off': { treatment: 'off', config: null } }, + 'Evaluations with configs using Redis storage should be correct for a set.' + ); + + assert.deepEqual( + await client.getTreatmentsByFlagSet('nico@test', 'set_two'), + { 'always-off': 'off', 'nico_not': 'off' }, + 'Evaluations using Redis storage should be correct for a set.' + ); + + assert.deepEqual( + await client.getTreatmentsByFlagSet('nico@test', 'set_invalid'), + {}, + 'Evaluations using Redis storage should properly handle all invalid sets.' + ); + + assert.deepEqual( + await client.getTreatmentsByFlagSets('nico@test', ['set_two', 'set_one']), + { 'always-on': 'on', 'always-off': 'off', 'nico_not': 'off' }, + 'Evaluations using Redis storage should be correct for multiple sets.' + ); + + assert.deepEqual( + await client.getTreatmentsWithConfigByFlagSets('nico@test', ['set_two', 'set_one']), + { 'always-on': { treatment: 'on', config: null }, 'always-off': { treatment: 'off', config: null }, 'nico_not': { treatment: 'off', config: '{"text":"Gallardiola"}' } }, + 'Evaluations with configs using Redis storage should be correct for multiple sets.' + ); + + assert.deepEqual( + await client.getTreatmentsByFlagSets('nico@test', [243, null, 'set_two', 'set_one', 'invalid_set']), + { 'always-on': 'on', 'always-off': 'off', 'nico_not': 'off' }, + 'Evaluations using Redis storage should be correct for multiple sets, discarding invalids.' + ); + + assert.deepEqual( + await client.getTreatmentsByFlagSets('nico@test', [243, null, 'invalid_set']), + {}, + 'Evaluations using Redis storage should properly handle all invalid sets.' + ); - // Validate stored telemetry - exec(`echo "HLEN ${config.storage.prefix}.SPLITIO.telemetry.latencies \n HKEYS ${config.storage.prefix}.SPLITIO.telemetry.latencies" | redis-cli -p ${redisPort}`, (error, stdout) => { - if (error) assert.fail('Redis server should be reachable'); + await client.ready(); // promise already resolved + await client.destroy(); - const [latenciesCount, ...latenciesForFlagSets] = stdout.split('\n').filter(line => line !== ''); + // Validate stored telemetry + exec(`echo "HLEN ${config.storage.prefix}.SPLITIO.telemetry.latencies \n HKEYS ${config.storage.prefix}.SPLITIO.telemetry.latencies" | redis-cli -p ${redisPort}`, (error, stdout) => { + if (error) assert.fail('Redis server should be reachable'); - assert.true(parseInt(latenciesCount) > 0, 'There are stored latencies'); - assert.true(latenciesForFlagSets.some(s => s.match(`nodejs-${version}/${HOSTNAME_VALUE}/${IP_VALUE}/treatmentsByFlagSet/`), 'The latency entry for treatmentsByFlagSet should be there.')); - assert.true(latenciesForFlagSets.some(s => s.match(`nodejs-${version}/${HOSTNAME_VALUE}/${IP_VALUE}/treatmentsByFlagSets/`), 'The latency entry for treatmentsByFlagSets should be there.')); + const [latenciesCount, ...latenciesForFlagSets] = stdout.split('\n').filter(line => line !== ''); - // close server connection - server.close().then(assert.end); - }); + assert.true(parseInt(latenciesCount) > 0, 'There are stored latencies'); + assert.true(latenciesForFlagSets.some(s => s.match(`nodejs-${version}/${HOSTNAME_VALUE}/${IP_VALUE}/treatmentsByFlagSet/`), 'The latency entry for treatmentsByFlagSet should be there.')); + assert.true(latenciesForFlagSets.some(s => s.match(`nodejs-${version}/${HOSTNAME_VALUE}/${IP_VALUE}/treatmentsByFlagSets/`), 'The latency entry for treatmentsByFlagSets should be there.')); + + // close server connection + server.close().then(assert.end); + }); - },1000); }); }); }); diff --git a/src/__tests__/mocks/redis-commands-sets.txt b/src/__tests__/mocks/redis-commands-flag-sets.txt similarity index 100% rename from src/__tests__/mocks/redis-commands-sets.txt rename to src/__tests__/mocks/redis-commands-flag-sets.txt diff --git a/types/splitio.d.ts b/types/splitio.d.ts index c01f999c7..6006ccb7b 100644 --- a/types/splitio.d.ts +++ b/types/splitio.d.ts @@ -223,8 +223,6 @@ interface ISharedSettings { * List of feature flag filters. These filters are used to fetch a subset of the feature flag definitions in your environment, in order to reduce the delay of the SDK to be ready. * This configuration is only meaningful when the SDK is working in "standalone" mode. * - * At the moment, only one type of feature flag filter is supported: by name. - * * Example: * `splitFilter: [ * { type: 'byName', values: ['my_feature_flag_1', 'my_feature_flag_2'] }, // will fetch feature flags named 'my_feature_flag_1' and 'my_feature_flag_2' From 7c4eb1bbed99274811e976525b409e821c028e39 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Thu, 30 Nov 2023 17:46:14 -0300 Subject: [PATCH 35/38] Revert validation of flag sets evaluation, to merge with branch sdks-7658 --- src/__tests__/consumer/node_redis.spec.js | 15 ++------------- src/__tests__/mocks/redis-commands.txt | 5 ----- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/src/__tests__/consumer/node_redis.spec.js b/src/__tests__/consumer/node_redis.spec.js index 6f96a48ea..6c44deb67 100644 --- a/src/__tests__/consumer/node_redis.spec.js +++ b/src/__tests__/consumer/node_redis.spec.js @@ -20,10 +20,10 @@ import { truncateTimeFrame } from '@splitsoftware/splitio-commons/src/utils/time const IP_VALUE = ipFunction.address(); const HOSTNAME_VALUE = osFunction.hostname(); const NA = 'NA'; + const redisPort = '6385'; const TOTAL_RAW_IMPRESSIONS = 16; -const TOTAL_RAW_IMPRESSIONS_IN_EVALUATIONS_WITH_FLAGSETS = 10; const TOTAL_EVENTS = 2; const DEDUPED_IMPRESSIONS = 3; @@ -101,7 +101,6 @@ tape('NodeJS Redis', function (t) { /** Evaluation, track and manager methods before SDK_READY */ client.getTreatment('UT_Segment_member', 'UT_IN_SEGMENT').then(result => assert.equal(result, 'control', 'Evaluations using Redis storage should be control until connection is stablished.')); client.getTreatment('other', 'UT_IN_SEGMENT').then(result => assert.equal(result, 'control', 'Evaluations using Redis storage should be control until connection is stablished.')); - client.getTreatmentsWithConfigByFlagSets('other', ['set_a']).then(result => assert.deepEqual(result, {}, 'Flag sets evaluations using Redis storage should be empty until connection is stablished.')); manager.names().then((result) => assert.deepEqual(result, [], 'manager `names` method returns an empty list of split names if called before SDK_READY or Redis operation fail')); manager.split(expectedSplitName).then((result) => assert.deepEqual(result, null, 'manager `split` method returns a null split view if called before SDK_READY or Redis operation fail')); @@ -157,16 +156,6 @@ tape('NodeJS Redis', function (t) { assert.true(await client.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was successfully queued the promise will resolve to true'); assert.false(await client.track(), 'If the event was NOT successfully queued the promise will resolve to false'); - // Evaluations with flag sets - assert.deepEqual(await client.getTreatmentsByFlagSet('other', 'set_a'), { with_set_a: 'on', with_sets_a_b: 'on' }, 'Evaluations with getTreatmentsByFlagSet should be correct.'); - assert.deepEqual(await client.getTreatmentsByFlagSets('other', ['set_a', 'set_b']), { with_set_a: 'on', with_set_b: 'on', with_sets_a_b: 'on' }, 'Evaluations with getTreatmentsByFlagSets should be correct.'); - assert.deepEqual(await client.getTreatmentsWithConfigByFlagSet('other', 'set_b'), { with_set_b: { treatment: 'on', config: '{}' }, with_sets_a_b: { treatment: 'on', config: null } }, 'Evaluations with getTreatmentsWithConfigByFlagSet should be correct.'); - assert.deepEqual(await client.getTreatmentsWithConfigByFlagSets('other', ['set_a', 'set_b']), { with_set_a: { treatment: 'on', config: null }, with_set_b: { treatment: 'on', config: '{}' }, with_sets_a_b: { treatment: 'on', config: null } }, 'Evaluations with getTreatmentsWithConfigByFlagSets should be correct.'); - - assert.deepEqual(await client.getTreatmentsByFlagSet('other'), {}, 'Evaluations without flag set should be empty.'); - assert.deepEqual(await client.getTreatmentsByFlagSets('other', []), {}, 'Evaluations without flag sets should be empty.'); - assert.deepEqual(await client.getTreatmentsByFlagSets('other', ['non_existent_set']), {}, 'Evaluations with non existent flag sets should be empty.'); - // Manager methods const splitNames = await manager.names(); assert.equal(splitNames.length, 28, 'manager `names` method returns the list of split names asynchronously'); @@ -184,7 +173,7 @@ tape('NodeJS Redis', function (t) { if (error) assert.fail('Redis server should be reachable'); const trackedImpressionsAndEvents = stdout.split('\n').filter(line => line !== '').map(line => parseInt(line)); - assert.deepEqual(trackedImpressionsAndEvents, [TOTAL_RAW_IMPRESSIONS + TOTAL_RAW_IMPRESSIONS_IN_EVALUATIONS_WITH_FLAGSETS, TOTAL_EVENTS], 'Tracked impressions and events should be stored in Redis'); + assert.deepEqual(trackedImpressionsAndEvents, [TOTAL_RAW_IMPRESSIONS, TOTAL_EVENTS], 'Tracked impressions and events should be stored in Redis'); // Validate stored telemetry exec(`echo "HLEN ${config.storage.prefix}.SPLITIO.telemetry.latencies \n HLEN ${config.storage.prefix}.SPLITIO.telemetry.exceptions \n HGET ${config.storage.prefix}.SPLITIO.telemetry.init nodejs-${version}/${HOSTNAME_VALUE}/${IP_VALUE}" | redis-cli -p ${redisPort}`, (error, stdout) => { diff --git a/src/__tests__/mocks/redis-commands.txt b/src/__tests__/mocks/redis-commands.txt index 00c31fa61..2e5daae42 100644 --- a/src/__tests__/mocks/redis-commands.txt +++ b/src/__tests__/mocks/redis-commands.txt @@ -39,8 +39,3 @@ SET 'REDIS_NODE_UT.SPLITIO.split.testing_traffic_type' '{"changeNumber":1489 SET 'REDIS_NODE_UT.SPLITIO.split.testing_traffic_types' '{"changeNumber":1490974465415,"trafficTypeName":"machine","name":"testing_traffic_types","seed":475616886,"status":"ACTIVE","killed":false,"defaultTreatment":"on","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"","attribute":""},"matcherType":"WHITELIST","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["sarasa"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100}],"label":"whitelisted"},{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"","attribute":""},"matcherType":"WHITELIST","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["excluded"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"off","size":100}],"label":"whitelisted"},{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}},{"keySelector":{"trafficType":"machine","attribute":""},"matcherType":"IN_SEGMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":"testing_traffic_type"},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":100}],"label":"in segment all and in segment testing_traffic_type"}]}' SET 'REDIS_NODE_UT.SPLITIO.split.traffic_allocation_testing' '{"changeNumber":1490974123779,"trafficTypeName":"user","name":"traffic_allocation_testing","seed":1716284102,"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"in segment all"}]}' SET 'REDIS_NODE_UT.SPLITIO.splits.till' 1492723024413 -SADD 'REDIS_NODE_UT.SPLITIO.flagSet.set_a' with_set_a with_sets_a_b -SADD 'REDIS_NODE_UT.SPLITIO.flagSet.set_b' with_set_b with_sets_a_b -SET 'REDIS_NODE_UT.SPLITIO.split.with_set_a' '{"changeNumber":1487277320548,"trafficTypeName":"user","name":"with_set_a","seed":1684183541,"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"in segment all"}],"sets":["set_a"]}' -SET 'REDIS_NODE_UT.SPLITIO.split.with_set_b' '{"changeNumber":1487277320548,"trafficTypeName":"user","name":"with_set_b","seed":1684183541,"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"in segment all"}],"sets":["set_b"],"configurations":{"on":"{}"}}' -SET 'REDIS_NODE_UT.SPLITIO.split.with_sets_a_b' '{"changeNumber":1487277320548,"trafficTypeName":"user","name":"with_set_a","seed":1684183541,"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"in segment all"}],"sets":["set_a","set_b"]}' \ No newline at end of file From 550261fed07ca9320a191cdd6d05200c30098bca Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Thu, 30 Nov 2023 18:05:51 -0300 Subject: [PATCH 36/38] Fix tests and prepare rc --- .github/workflows/ci-cd.yml | 4 ++-- package-lock.json | 18 +++++++++--------- package.json | 4 ++-- src/__tests__/consumer/node_redis.spec.js | 6 +++--- src/settings/defaults/version.js | 2 +- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 0cc8aebaf..d27d27959 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -58,7 +58,7 @@ jobs: run: BUILD_BRANCH=$(echo "${GITHUB_REF#refs/heads/}") npm run build - name: Store assets - if: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/sdks-7437' || github.ref == 'refs/heads/master') }} + if: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/sdks-7658' || github.ref == 'refs/heads/master') }} uses: actions/upload-artifact@v3 with: name: assets @@ -69,7 +69,7 @@ jobs: name: Upload assets runs-on: ubuntu-20.04 needs: build - if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/sdks-7437' }} + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/sdks-7658' }} strategy: matrix: environment: diff --git a/package-lock.json b/package-lock.json index 2d7a1e6df..38fae3a86 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,15 @@ { "name": "@splitsoftware/splitio", - "version": "10.24.0-beta", + "version": "10.24.0-rc.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@splitsoftware/splitio", - "version": "10.24.0-beta", + "version": "10.24.0-rc.0", "license": "Apache-2.0", "dependencies": { - "@splitsoftware/splitio-commons": "1.12.1-rc.2", + "@splitsoftware/splitio-commons": "1.12.1-rc.3", "@types/google.analytics": "0.0.40", "@types/ioredis": "^4.28.0", "bloom-filters": "^3.0.0", @@ -874,9 +874,9 @@ "dev": true }, "node_modules/@splitsoftware/splitio-commons": { - "version": "1.12.1-rc.2", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.12.1-rc.2.tgz", - "integrity": "sha512-b8wVebAbTYDLKoUwWf7hwP+xlyVlbH3hX9+kPTbQWYeHkX2JNBnbEBSM5sLlYUpwbVW6mLEd59qwjwEsa9ZD5g==", + "version": "1.12.1-rc.3", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.12.1-rc.3.tgz", + "integrity": "sha512-fnygMKAWdnsjq7XltJV2RVgQZ0kmBgYQuX9mAkWRejuU4sFbcGnX7pLV8ZzI0OFi4hn0HMbl90Yx2moP4yRu/w==", "dependencies": { "tslib": "^2.3.1" }, @@ -8446,9 +8446,9 @@ "dev": true }, "@splitsoftware/splitio-commons": { - "version": "1.12.1-rc.2", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.12.1-rc.2.tgz", - "integrity": "sha512-b8wVebAbTYDLKoUwWf7hwP+xlyVlbH3hX9+kPTbQWYeHkX2JNBnbEBSM5sLlYUpwbVW6mLEd59qwjwEsa9ZD5g==", + "version": "1.12.1-rc.3", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.12.1-rc.3.tgz", + "integrity": "sha512-fnygMKAWdnsjq7XltJV2RVgQZ0kmBgYQuX9mAkWRejuU4sFbcGnX7pLV8ZzI0OFi4hn0HMbl90Yx2moP4yRu/w==", "requires": { "tslib": "^2.3.1" } diff --git a/package.json b/package.json index 710d8f88a..96db0bc95 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio", - "version": "10.24.0-beta", + "version": "10.24.0-rc.0", "description": "Split SDK", "files": [ "README.md", @@ -40,7 +40,7 @@ "node": ">=6" }, "dependencies": { - "@splitsoftware/splitio-commons": "1.12.1-rc.2", + "@splitsoftware/splitio-commons": "1.12.1-rc.3", "@types/google.analytics": "0.0.40", "@types/ioredis": "^4.28.0", "bloom-filters": "^3.0.0", diff --git a/src/__tests__/consumer/node_redis.spec.js b/src/__tests__/consumer/node_redis.spec.js index 090a3c067..cf9239ad6 100644 --- a/src/__tests__/consumer/node_redis.spec.js +++ b/src/__tests__/consumer/node_redis.spec.js @@ -156,7 +156,6 @@ tape('NodeJS Redis', function (t) { assert.equal(await client.getTreatment('UT_Segment_member', 'hierarchical_splits_testing_off'), 'off', 'Evaluations using Redis storage should be correct.'); assert.equal(await client.getTreatment('UT_Segment_member', 'hierarchical_splits_testing_on_negated'), 'off', 'Evaluations using Redis storage should be correct.'); - assert.equal(typeof client.track('nicolas@split.io', 'user', 'test.redis.event', 18).then, 'function', 'Track calls should always return a promise on Redis mode.'); assert.equal(typeof client.track().then, 'function', 'Track calls should always return a promise on Redis mode, even when parameters are incorrect.'); assert.true(await client.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was successfully queued the promise will resolve to true'); @@ -164,11 +163,11 @@ tape('NodeJS Redis', function (t) { // Manager methods const splitNames = await manager.names(); - assert.equal(splitNames.length, 28, 'manager `names` method returns the list of split names asynchronously'); + assert.equal(splitNames.length, 25, 'manager `names` method returns the list of split names asynchronously'); assert.equal(splitNames.indexOf(expectedSplitName) > -1, true, 'list of split names should contain expected splits'); assert.deepEqual(await manager.split(expectedSplitName), expectedSplitView, 'manager `split` method returns the split view of the given split name asynchronously'); const splitViews = await manager.splits(); - assert.equal(splitViews.length, 28, 'manager `splits` method returns the list of split views asynchronously'); + assert.equal(splitViews.length, 25, 'manager `splits` method returns the list of split views asynchronously'); assert.deepEqual(splitViews.find(splitView => splitView.name === expectedSplitName), expectedSplitView, 'manager `split` method returns the split view of the given split name asynchronously'); await client.ready(); // promise already resolved @@ -283,6 +282,7 @@ tape('NodeJS Redis', function (t) { // this should be deduped assert.equal(await client.getTreatment('UT_Segment_member', 'hierarchical_splits_testing_on_negated'), 'off', 'Evaluations using Redis storage should be correct.'); + assert.equal(typeof client.track('nicolas@split.io', 'user', 'test.redis.event', 18).then, 'function', 'Track calls should always return a promise on Redis mode.'); assert.equal(typeof client.track().then, 'function', 'Track calls should always return a promise on Redis mode, even when parameters are incorrect.'); assert.true(await client.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was successfully queued the promise will resolve to true'); diff --git a/src/settings/defaults/version.js b/src/settings/defaults/version.js index 77bd7d677..406da3c36 100644 --- a/src/settings/defaults/version.js +++ b/src/settings/defaults/version.js @@ -1 +1 @@ -export const packageVersion = '10.24.0-beta'; +export const packageVersion = '10.24.0-rc.0'; From 6118ecf87403fd6a22e874e21eb4fa108e96e1e3 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Fri, 1 Dec 2023 13:32:47 -0300 Subject: [PATCH 37/38] rc with log fixes --- package-lock.json | 18 +++++++++--------- package.json | 4 ++-- src/settings/defaults/version.js | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index 38fae3a86..905567f0d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,15 @@ { "name": "@splitsoftware/splitio", - "version": "10.24.0-rc.0", + "version": "10.24.0-rc.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@splitsoftware/splitio", - "version": "10.24.0-rc.0", + "version": "10.24.0-rc.1", "license": "Apache-2.0", "dependencies": { - "@splitsoftware/splitio-commons": "1.12.1-rc.3", + "@splitsoftware/splitio-commons": "1.12.1-rc.4", "@types/google.analytics": "0.0.40", "@types/ioredis": "^4.28.0", "bloom-filters": "^3.0.0", @@ -874,9 +874,9 @@ "dev": true }, "node_modules/@splitsoftware/splitio-commons": { - "version": "1.12.1-rc.3", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.12.1-rc.3.tgz", - "integrity": "sha512-fnygMKAWdnsjq7XltJV2RVgQZ0kmBgYQuX9mAkWRejuU4sFbcGnX7pLV8ZzI0OFi4hn0HMbl90Yx2moP4yRu/w==", + "version": "1.12.1-rc.4", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.12.1-rc.4.tgz", + "integrity": "sha512-tBn3+Vpiw7uKBPEM2H4FKXlA1Cax8/qQT6UgRY9ncDzWDkwuxubSgntopJ2f4Ax+/9BCuyDr/JKCHHc1oMUf4w==", "dependencies": { "tslib": "^2.3.1" }, @@ -8446,9 +8446,9 @@ "dev": true }, "@splitsoftware/splitio-commons": { - "version": "1.12.1-rc.3", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.12.1-rc.3.tgz", - "integrity": "sha512-fnygMKAWdnsjq7XltJV2RVgQZ0kmBgYQuX9mAkWRejuU4sFbcGnX7pLV8ZzI0OFi4hn0HMbl90Yx2moP4yRu/w==", + "version": "1.12.1-rc.4", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.12.1-rc.4.tgz", + "integrity": "sha512-tBn3+Vpiw7uKBPEM2H4FKXlA1Cax8/qQT6UgRY9ncDzWDkwuxubSgntopJ2f4Ax+/9BCuyDr/JKCHHc1oMUf4w==", "requires": { "tslib": "^2.3.1" } diff --git a/package.json b/package.json index 96db0bc95..ef87914da 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio", - "version": "10.24.0-rc.0", + "version": "10.24.0-rc.1", "description": "Split SDK", "files": [ "README.md", @@ -40,7 +40,7 @@ "node": ">=6" }, "dependencies": { - "@splitsoftware/splitio-commons": "1.12.1-rc.3", + "@splitsoftware/splitio-commons": "1.12.1-rc.4", "@types/google.analytics": "0.0.40", "@types/ioredis": "^4.28.0", "bloom-filters": "^3.0.0", diff --git a/src/settings/defaults/version.js b/src/settings/defaults/version.js index 406da3c36..3ceabbd20 100644 --- a/src/settings/defaults/version.js +++ b/src/settings/defaults/version.js @@ -1 +1 @@ -export const packageVersion = '10.24.0-rc.0'; +export const packageVersion = '10.24.0-rc.1'; From e65903210bfc7f500c0974b95d5971062f398f61 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Mon, 4 Dec 2023 13:06:18 -0300 Subject: [PATCH 38/38] Rollback ci-cd config --- .github/workflows/ci-cd.yml | 4 ++-- CHANGES.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index d27d27959..701c3518d 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -58,7 +58,7 @@ jobs: run: BUILD_BRANCH=$(echo "${GITHUB_REF#refs/heads/}") npm run build - name: Store assets - if: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/sdks-7658' || github.ref == 'refs/heads/master') }} + if: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/development' || github.ref == 'refs/heads/master') }} uses: actions/upload-artifact@v3 with: name: assets @@ -69,7 +69,7 @@ jobs: name: Upload assets runs-on: ubuntu-20.04 needs: build - if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/sdks-7658' }} + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/development' }} strategy: matrix: environment: diff --git a/CHANGES.txt b/CHANGES.txt index 9d367d7ca..660656038 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,4 @@ -10.24.0 (December XX, 2023) +10.24.0 (December 4, 2023) - Added support for Flag Sets on the SDK, which enables grouping feature flags and interacting with the group rather than individually (more details in our documentation): - Added new variations of the get treatment methods to support evaluating flags in given flag set/s. - getTreatmentsByFlagSet and getTreatmentsByFlagSets