diff --git a/CHANGES.txt b/CHANGES.txt index 6a09d963d..8a9cf92c6 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,9 @@ +10.26.0 (May 6, 2024) + - Updated @splitsoftware/splitio-commons package to version 1.14.0 that includes minor updates: + - Added support for targeting rules based on semantic versions (https://semver.org/). + - Added special impression label "targeting rule type unsupported by sdk" when the matcher type is not supported by the SDK, which returns 'control' treatment. + - Updated Split API client to include the flags spec version query parameter for the `splitChanges` and `auth` endpoints. + 10.25.2 (March 26, 2024) - Updated some transitive dependencies for vulnerability fixes. - Bugfixing - Added tslib as an explicit dependency to avoid issues with some package managers that don't resolve it automatically as a transitive dependency from @splitsoftware/splitio-commons (Related to issue https://github.com/splitio/javascript-client/issues/795). diff --git a/package-lock.json b/package-lock.json index aac4cec71..d794787d8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,15 @@ { "name": "@splitsoftware/splitio", - "version": "10.25.2", + "version": "10.26.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@splitsoftware/splitio", - "version": "10.25.2", + "version": "10.26.0", "license": "Apache-2.0", "dependencies": { - "@splitsoftware/splitio-commons": "1.13.1", + "@splitsoftware/splitio-commons": "1.14.0", "@types/google.analytics": "0.0.40", "@types/ioredis": "^4.28.0", "bloom-filters": "^3.0.0", @@ -875,9 +875,9 @@ "dev": true }, "node_modules/@splitsoftware/splitio-commons": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.13.1.tgz", - "integrity": "sha512-xGu94sLx+tJb6PeM26vH8/LEElsaVbh2BjoLvL5twR4gKsVezie5ZtHhejWT1+iCVCtJuhjZxKwOm4HGYoVIHQ==", + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.14.0.tgz", + "integrity": "sha512-ANP0NRPAMehi4bUQsb19kP5W5NVuCYUKRsDC5Nl78xHIu6cskAej1rXkjsocLnWerz2rO0H9kMjRKZj9lVsvKA==", "dependencies": { "tslib": "^2.3.1" }, @@ -8447,9 +8447,9 @@ "dev": true }, "@splitsoftware/splitio-commons": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.13.1.tgz", - "integrity": "sha512-xGu94sLx+tJb6PeM26vH8/LEElsaVbh2BjoLvL5twR4gKsVezie5ZtHhejWT1+iCVCtJuhjZxKwOm4HGYoVIHQ==", + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.14.0.tgz", + "integrity": "sha512-ANP0NRPAMehi4bUQsb19kP5W5NVuCYUKRsDC5Nl78xHIu6cskAej1rXkjsocLnWerz2rO0H9kMjRKZj9lVsvKA==", "requires": { "tslib": "^2.3.1" } diff --git a/package.json b/package.json index 2dce53ec8..b301c0345 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio", - "version": "10.25.2", + "version": "10.26.0", "description": "Split SDK", "files": [ "README.md", @@ -40,7 +40,7 @@ "node": ">=6" }, "dependencies": { - "@splitsoftware/splitio-commons": "1.13.1", + "@splitsoftware/splitio-commons": "1.14.0", "@types/google.analytics": "0.0.40", "@types/ioredis": "^4.28.0", "bloom-filters": "^3.0.0", diff --git a/src/__tests__/browserSuites/evaluations-semver.spec.js b/src/__tests__/browserSuites/evaluations-semver.spec.js new file mode 100644 index 000000000..29d2540fe --- /dev/null +++ b/src/__tests__/browserSuites/evaluations-semver.spec.js @@ -0,0 +1,114 @@ +import sinon from 'sinon'; +import splitChangesMock1 from '../mocks/splitchanges.since.-1.semver.json'; + +import { SplitFactory } from '../../'; + +const listener = { + logImpression: sinon.stub() +}; + +const config = { + core: { + authorizationKey: '', + key: 'emi@split.io' + }, + urls: { + sdk: 'https://sdk.evaluation-semver/api', + events: 'https://events.evaluation-semver/api' + }, + sync: { + impressionsMode: 'DEBUG' + }, + impressionListener: listener, + streamingEnabled: false +}; + +export default async function (fetchMock, assert) { + + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=-1', { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=1675259356568', { status: 200, body: { splits: [], since: 1675259356568, till: 1675259356568 } }); + fetchMock.getOnce(config.urls.sdk + '/mySegments/emi%40split.io', { status: 200, body: { mySegments: [] } }); + fetchMock.getOnce(config.urls.sdk + '/mySegments/2nd', { status: 200, body: { mySegments: [] } }); + + const splitio = SplitFactory(config); + const client = splitio.client(); + + await client.ready(); + + // EQUAL_TO_SEMVER matcher + assert.equal(client.getTreatment('semver_equalto', { 'version': '1.22.9' }), 'on', 'the rule will return `on` if attribute `version` is equal to `1.22.9`'); + assert.equal(client.getTreatment('semver_equalto', { 'version': '1.22.9+build' }), 'off', 'build metadata is not ignored'); + assert.equal(client.getTreatment('semver_equalto', { 'version': '1.22.9-rc.0' }), 'off', 'the rule will return `off` if attribute `version` is not equal to `1.22.9`'); + assert.equal(client.getTreatment('semver_equalto', { 'version': null }), 'off', 'the rule will return `off` if attribute `version` is not the expected type'); + assert.equal(client.getTreatment('semver_equalto'), 'off', 'the rule will return `off` if attribute `version` is not provided'); + + // IN_LIST_SEMVER matcher + assert.equal(client.getTreatment('semver_inlist', { 'version': '2.1.0' }), 'on', 'the rule will return `on` if attribute `version` is in list (`1.22.9`, `2.1.0`)'); + assert.equal(client.getTreatment('semver_inlist', { 'version': '1.22.9' }), 'on', 'the rule will return `on` if attribute `version` is in list (`1.22.9`, `2.1.0`)'); + assert.equal(client.getTreatment('semver_inlist', { 'version': '1.22.9+build' }), 'off', 'build metadata is not ignored'); + assert.equal(client.getTreatment('semver_inlist', { 'version': '1.22.9-rc.0' }), 'off', 'the rule will return `off` if attribute `version` is not in list (`1.22.9`, `2.1.0`)'); + assert.equal(client.getTreatment('semver_inlist', { 'version': null }), 'off', 'the rule will return `off` if attribute `version` is not the expected type'); + + // GREATER_THAN_OR_EQUAL_TO_SEMVER matcher + assert.equal(client.getTreatments(['semver_greater_or_equalto'], { 'version': '1.23.9' }).semver_greater_or_equalto, 'on', 'the rule will return `on` if attribute `version` is greater than or equal to `1.22.9`'); + assert.equal(client.getTreatments(['semver_greater_or_equalto'], { 'version': '1.22.9' }).semver_greater_or_equalto, 'on', 'the rule will return `on` if attribute `version` is greater than or equal to `1.22.9`'); + assert.equal(client.getTreatments(['semver_greater_or_equalto'], { 'version': '1.22.9+build' }).semver_greater_or_equalto, 'on', 'build metadata is ignored'); + assert.equal(client.getTreatments(['semver_greater_or_equalto'], { 'version': '1.22.9-rc.0' }).semver_greater_or_equalto, 'off', 'the rule will return `off` if attribute `version` is not greater than or equal to `1.22.9`'); + assert.equal(client.getTreatments(['semver_greater_or_equalto'], { 'version': '1.21.9' }).semver_greater_or_equalto, 'off', 'the rule will return `off` if attribute `version` is not greater than or equal to `1.22.9`'); + assert.equal(client.getTreatments(['semver_greater_or_equalto'], { 'version': 'invalid' }).semver_greater_or_equalto, 'off', 'the rule will return `off` if attribute `version` is an invalid semver value'); + + // LESS_THAN_OR_EQUAL_TO_SEMVER matcher + assert.deepEqual(client.getTreatmentWithConfig('semver_less_or_equalto', { 'version': '1.22.11' }), { treatment: 'off', config: null }, 'the rule will return `off` if attribute `version` is not less than or equal to `1.22.9`'); + assert.deepEqual(client.getTreatmentWithConfig('semver_less_or_equalto', { 'version': '1.22.9' }), { treatment: 'on', config: null }, 'the rule will return `on` if attribute `version` is less than or equal to `1.22.9`'); + assert.deepEqual(client.getTreatmentWithConfig('semver_less_or_equalto', { 'version': '1.22.9+build' }), { treatment: 'on', config: null }, 'build metadata is ignored'); + assert.deepEqual(client.getTreatmentWithConfig('semver_less_or_equalto', { 'version': '1.22.9-rc.0' }), { treatment: 'on', config: null }, 'the rule will return `on` if attribute `version` is less than or equal to `1.22.9`'); + assert.deepEqual(client.getTreatmentWithConfig('semver_less_or_equalto', { 'version': '1.21.9' }), { treatment: 'on', config: null }, 'the rule will return `on` if attribute `version` is less than or equal to `1.22.9`'); + assert.deepEqual(client.getTreatmentWithConfig('semver_less_or_equalto', { 'version': {} }), { treatment: 'off', config: null }, 'the rule will return `off` if attribute `version` is not the expected type'); + + const client2 = splitio.client('2nd'); + await client2.ready(); + + // BETWEEN_SEMVER matcher + assert.deepEqual(client2.getTreatmentsWithConfig(['semver_between'], { 'version': '2.1.1' }).semver_between, { treatment: 'off', config: null }, 'the rule will return `off` if attribute `version` is not between `1.22.9` and `2.1.0`'); + assert.deepEqual(client2.getTreatmentsWithConfig(['semver_between'], { 'version': '2.1.0+build' }).semver_between, { treatment: 'on', config: null }, 'build metadata is ignored'); + assert.deepEqual(client2.getTreatmentsWithConfig(['semver_between'], { 'version': '1.25.0' }).semver_between, { treatment: 'on', config: null }, 'the rule will return `on` if attribute `version` is between `1.22.9` and `2.1.0`'); + assert.deepEqual(client2.getTreatmentsWithConfig(['semver_between'], { 'version': '1.22.9' }).semver_between, { treatment: 'on', config: null }, 'the rule will return `on` if attribute `version` is between `1.22.9` and `2.1.0`'); + assert.deepEqual(client2.getTreatmentsWithConfig(['semver_between'], { 'version': '1.22.9-rc.0' }).semver_between, { treatment: 'off', config: null }, 'the rule will return `off` if attribute `version` is not between `1.22.9` and `2.1.0`'); + assert.deepEqual(client2.getTreatmentsWithConfig(['semver_between'], { 'version': [] }).semver_between, { treatment: 'off', config: null }, 'the rule will return `off` if attribute `version` is not the expected type'); + + // Evaluation of a flag with unsupported matcher + assert.equal(client2.getTreatment('flag_with_unsupported_matcher'), 'control', 'evaluation of a flag with an unsupported matcher should return control'); + + let POSTED_IMPRESSIONS_COUNT; + + fetchMock.postOnce(config.urls.events + '/testImpressions/bulk', (_, opts) => { + + const payload = JSON.parse(opts.body); + + function validateImpressionData(featureFlagName, expectedImpressionCount, expectedOnCount, expectedLabel, expectedTreatment = 'on') { + const impressions = payload.find(e => e.f === featureFlagName).i; + + assert.equal(impressions.length, expectedImpressionCount, `We should have ${expectedImpressionCount} impressions for the feature flag ${featureFlagName}`); + assert.equal(impressions.filter((imp) => imp.r === expectedLabel && imp.t === expectedTreatment).length, expectedOnCount, `${expectedOnCount} impression with 'on' treatment and label ${expectedLabel}`); + } + + validateImpressionData('semver_equalto', 5, 1, 'equal to semver'); + validateImpressionData('semver_inlist', 5, 2, 'in list semver'); + validateImpressionData('semver_greater_or_equalto', 6, 3, 'greater than or equal to semver'); + validateImpressionData('semver_less_or_equalto', 6, 4, 'less than or equal to semver'); + validateImpressionData('semver_between', 6, 3, 'between semver'); + validateImpressionData('flag_with_unsupported_matcher', 1, 1, 'targeting rule type unsupported by sdk', 'control'); + + POSTED_IMPRESSIONS_COUNT = payload.reduce((acc, curr) => acc + curr.i.length, 0); + + return 200; + }); + + await Promise.all([client.destroy(), client2.destroy()]); + + setTimeout(() => { + assert.equal(listener.logImpression.callCount, POSTED_IMPRESSIONS_COUNT, 'Impression listener should be called once per each impression generated.'); + + assert.end(); + }, 0); +} diff --git a/src/__tests__/browserSuites/fetch-specific-splits.spec.js b/src/__tests__/browserSuites/fetch-specific-splits.spec.js index f8a17f365..fb64a4b82 100644 --- a/src/__tests__/browserSuites/fetch-specific-splits.spec.js +++ b/src/__tests__/browserSuites/fetch-specific-splits.spec.js @@ -25,9 +25,9 @@ export function fetchSpecificSplits(fetchMock, assert) { const queryString = queryStrings[i] || ''; let factory; - fetchMock.getOnce(urls.sdk + '/splitChanges?since=-1' + queryString, { status: 200, body: { splits: [], since: -1, till: 1457552620999 } }); - fetchMock.getOnce(urls.sdk + '/splitChanges?since=1457552620999' + queryString, { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); - fetchMock.getOnce(urls.sdk + '/splitChanges?since=1457552620999' + queryString, function () { + fetchMock.getOnce(urls.sdk + '/splitChanges?s=1.1&since=-1' + queryString, { status: 200, body: { splits: [], since: -1, till: 1457552620999 } }); + fetchMock.getOnce(urls.sdk + '/splitChanges?s=1.1&since=1457552620999' + queryString, { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); + fetchMock.getOnce(urls.sdk + '/splitChanges?s=1.1&since=1457552620999' + queryString, function () { factory.client().destroy().then(() => { assert.pass(`splitFilters #${i}`); }); @@ -70,8 +70,8 @@ export function fetchSpecificSplitsForFlagSets(fetchMock, assert) { 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' + queryString, { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 }}); - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1457552620999' + queryString, async function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&since=-1' + queryString, { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 }}); + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&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.')); diff --git a/src/__tests__/browserSuites/flag-sets.spec.js b/src/__tests__/browserSuites/flag-sets.spec.js index a2b471435..60396eb81 100644 --- a/src/__tests__/browserSuites/flag-sets.spec.js +++ b/src/__tests__/browserSuites/flag-sets.spec.js @@ -24,12 +24,12 @@ export default function flagSets(fetchMock, t) { 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 () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&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 () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&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'); @@ -41,7 +41,7 @@ export default function flagSets(fetchMock, t) { }); // Receive split change with 1 split belonging to set_3 only - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1602797638344&sets=set_1,set_2', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&since=1602797638344&sets=set_1,set_2', function () { // stored feature flags before update const storedFlags = manager.splits(); assert.true(storedFlags.length === 1); @@ -52,7 +52,7 @@ export default function flagSets(fetchMock, t) { return { status: 200, body: splitChange0}; }); - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1602798638344&sets=set_1,set_2', async function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&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'); @@ -75,12 +75,12 @@ export default function flagSets(fetchMock, t) { 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 () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&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 () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&since=1602796638344', function () { // stored feature flags before update const storedFlags = manager.splits(); assert.true(storedFlags.length === 2, 'every feature flag should be added'); @@ -94,7 +94,7 @@ export default function flagSets(fetchMock, t) { }); // Receive split change with 1 split belonging to set_3 only - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1602797638344', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&since=1602797638344', function () { // stored feature flags before update const storedFlags = manager.splits(); assert.true(storedFlags.length === 2); @@ -107,7 +107,7 @@ export default function flagSets(fetchMock, t) { return { status: 200, body: splitChange0}; }); - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1602798638344', async function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&since=1602798638344', async function () { // stored feature flags before update const storedFlags = manager.splits(); assert.true(storedFlags.length === 2); @@ -137,11 +137,11 @@ export default function flagSets(fetchMock, t) { 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 () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&since=-1&sets=set_1', function () { return { status: 200, body: splitChange2}; }); - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1602796638344&sets=set_1', async function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&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'); @@ -174,11 +174,11 @@ export default function flagSets(fetchMock, t) { 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 () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&since=-1', function () { return { status: 200, body: splitChange2}; }); - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1602796638344', async function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&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'); diff --git a/src/__tests__/browserSuites/ignore-ip-addresses-setting.spec.js b/src/__tests__/browserSuites/ignore-ip-addresses-setting.spec.js index 6266976fe..c2c391d52 100644 --- a/src/__tests__/browserSuites/ignore-ip-addresses-setting.spec.js +++ b/src/__tests__/browserSuites/ignore-ip-addresses-setting.spec.js @@ -101,8 +101,8 @@ export default function (fetchMock, assert) { // Mock GET endpoints before creating the client const settings = settingsFactory(config); - fetchMock.getOnce(url(settings, '/splitChanges?since=-1'), { status: 200, body: splitChangesMock1 }); - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); fetchMock.getOnce(url(settings, `/mySegments/${encodeURIComponent(config.core.key)}`), { status: 200, body: { mySegments: [] } }); // Init Split client diff --git a/src/__tests__/browserSuites/impressions.debug.spec.js b/src/__tests__/browserSuites/impressions.debug.spec.js index bbccfc9db..3a2eeb488 100644 --- a/src/__tests__/browserSuites/impressions.debug.spec.js +++ b/src/__tests__/browserSuites/impressions.debug.spec.js @@ -21,8 +21,8 @@ const settings = settingsFactory({ export default function (fetchMock, assert) { // Mocking this specific route to make sure we only get the items we want to test from the handlers. - fetchMock.getOnce(url(settings, '/splitChanges?since=-1'), { status: 200, body: splitChangesMock1 }); - fetchMock.get(url(settings, '/splitChanges?since=1457552620999'), { status: 200, body: splitChangesMock2 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.get(url(settings, '/splitChanges?s=1.1&since=1457552620999'), { status: 200, body: splitChangesMock2 }); fetchMock.get(url(settings, '/mySegments/facundo%40split.io'), { status: 200, body: mySegmentsFacundo }); const splitio = SplitFactory({ diff --git a/src/__tests__/browserSuites/impressions.none.spec.js b/src/__tests__/browserSuites/impressions.none.spec.js index 93bea3e1e..89aee3ab0 100644 --- a/src/__tests__/browserSuites/impressions.none.spec.js +++ b/src/__tests__/browserSuites/impressions.none.spec.js @@ -41,8 +41,8 @@ const config = { export default async function (fetchMock, assert) { // Mocking this specific route to make sure we only get the items we want to test from the handlers. - fetchMock.getOnce(url(settings, '/splitChanges?since=-1'), { status: 200, body: splitChangesMock1 }); - fetchMock.get(url(settings, '/splitChanges?since=1457552620999'), { status: 200, body: splitChangesMock2 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.get(url(settings, '/splitChanges?s=1.1&since=1457552620999'), { status: 200, body: splitChangesMock2 }); fetchMock.get(url(settings, '/mySegments/facundo%40split.io'), { status: 200, body: mySegmentsFacundo }); fetchMock.get(url(settings, '/mySegments/emma%40split.io'), { status: 200, body: mySegmentsFacundo }); diff --git a/src/__tests__/browserSuites/impressions.spec.js b/src/__tests__/browserSuites/impressions.spec.js index 6be91e666..42c85809b 100644 --- a/src/__tests__/browserSuites/impressions.spec.js +++ b/src/__tests__/browserSuites/impressions.spec.js @@ -24,8 +24,8 @@ let truncatedTimeFrame; export default function (fetchMock, assert) { // Mocking this specific route to make sure we only get the items we want to test from the handlers. - fetchMock.getOnce(url(settings, '/splitChanges?since=-1'), { status: 200, body: splitChangesMock1 }); - fetchMock.get(url(settings, '/splitChanges?since=1457552620999'), { status: 200, body: splitChangesMock2 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.get(url(settings, '/splitChanges?s=1.1&since=1457552620999'), { status: 200, body: splitChangesMock2 }); fetchMock.get(url(settings, '/mySegments/facundo%40split.io'), { status: 200, body: mySegmentsFacundo }); const splitio = SplitFactory({ diff --git a/src/__tests__/browserSuites/manager.spec.js b/src/__tests__/browserSuites/manager.spec.js index aba6c648a..15b250bab 100644 --- a/src/__tests__/browserSuites/manager.spec.js +++ b/src/__tests__/browserSuites/manager.spec.js @@ -4,7 +4,7 @@ import map from 'lodash/map'; import { url } from '../testUtils'; export default async function (settings, fetchMock, assert) { - fetchMock.getOnce({ url: url(settings, '/splitChanges?since=-1'), overwriteRoutes: true }, { status: 200, body: splitChangesMockReal }); + fetchMock.getOnce({ url: url(settings, '/splitChanges?s=1.1&since=-1'), overwriteRoutes: true }, { status: 200, body: splitChangesMockReal }); const mockSplits = splitChangesMockReal; diff --git a/src/__tests__/browserSuites/push-corner-cases.spec.js b/src/__tests__/browserSuites/push-corner-cases.spec.js index 7c6ec7db4..bc7e8bb09 100644 --- a/src/__tests__/browserSuites/push-corner-cases.spec.js +++ b/src/__tests__/browserSuites/push-corner-cases.spec.js @@ -71,13 +71,13 @@ export function testSplitKillOnReadyFromCache(fetchMock, assert) { }); // 1 auth request - fetchMock.getOnce(url(settings, `/v2/auth?users=${encodeURIComponent(userKey)}`), { status: 200, body: authPushEnabledNicolas }); + fetchMock.getOnce(url(settings, `/v2/auth?s=1.1&users=${encodeURIComponent(userKey)}`), { status: 200, body: authPushEnabledNicolas }); // 2 mySegments requests: initial sync and after SSE opened fetchMock.get({ url: url(settings, '/mySegments/nicolas%40split.io'), repeat: 2 }, { status: 200, body: { mySegments: [] } }); // 2 splitChanges request: initial sync and after SSE opened. Sync after SPLIT_KILL is not performed because SplitsSyncTask is "executing" - fetchMock.getOnce(url(settings, '/splitChanges?since=25'), { status: 200, body: splitChangesMock1 }, { delay: MILLIS_SPLIT_CHANGES_RESPONSE, /* delay response */ }); - fetchMock.getOnce(url(settings, `/splitChanges?since=${splitChangesMock1.till}`), { status: 200, body: { splits: [], since: splitChangesMock1.till, till: splitChangesMock1.till } }, { delay: MILLIS_SPLIT_CHANGES_RESPONSE - 100, /* delay response */ }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=25'), { status: 200, body: splitChangesMock1 }, { delay: MILLIS_SPLIT_CHANGES_RESPONSE, /* delay response */ }); + fetchMock.getOnce(url(settings, `/splitChanges?s=1.1&since=${splitChangesMock1.till}`), { status: 200, body: { splits: [], since: splitChangesMock1.till, till: splitChangesMock1.till } }, { delay: MILLIS_SPLIT_CHANGES_RESPONSE - 100, /* delay response */ }); fetchMock.get(new RegExp('.*'), function (url) { assert.fail('unexpected GET request with url: ' + url); diff --git a/src/__tests__/browserSuites/push-fallbacking.spec.js b/src/__tests__/browserSuites/push-fallbacking.spec.js index 88ece69b6..83832c0d6 100644 --- a/src/__tests__/browserSuites/push-fallbacking.spec.js +++ b/src/__tests__/browserSuites/push-fallbacking.spec.js @@ -207,24 +207,24 @@ export function testFallbacking(fetchMock, assert) { }); - fetchMock.getOnce(url(settings, `/v2/auth?users=${encodeURIComponent(userKey)}`), function (url, opts) { + fetchMock.getOnce(url(settings, `/v2/auth?s=1.1&users=${encodeURIComponent(userKey)}`), function (url, opts) { if (!opts.headers['Authorization']) assert.fail('`/v2/auth` request must include `Authorization` header'); assert.pass('auth success'); return { status: 200, body: authPushEnabledNicolas }; }); // initial split and mySegment sync - fetchMock.getOnce(url(settings, '/splitChanges?since=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); fetchMock.getOnce(url(settings, '/mySegments/nicolas%40split.io'), { status: 200, body: mySegmentsNicolasMock1 }); // split and segment sync after SSE opened - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); fetchMock.getOnce(url(settings, '/mySegments/nicolas%40split.io'), { status: 200, body: mySegmentsNicolasMock1 }); // fetches due to first fallback to polling - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); fetchMock.getOnce(url(settings, '/mySegments/nicolas%40split.io'), { status: 200, body: mySegmentsNicolasMock1 }); - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, MILLIS_STREAMING_DOWN_OCCUPANCY + settings.scheduler.featuresRefreshRate), 'fetch due to first fallback to polling'); return { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }; @@ -232,34 +232,34 @@ export function testFallbacking(fetchMock, assert) { fetchMock.getOnce(url(settings, '/mySegments/nicolas%40split.io'), { status: 200, body: mySegmentsNicolasMock1 }); // split and segment sync due to streaming up (OCCUPANCY event) - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); fetchMock.getOnce(url(settings, '/mySegments/nicolas%40split.io'), { status: 200, body: mySegmentsNicolasMock1 }); // creating of second client during streaming: initial mysegment sync, reauth and syncAll due to new client fetchMock.getOnce(url(settings, '/mySegments/marcio%40split.io'), { status: 200, body: mySegmentsMarcio }); - fetchMock.get({ url: url(settings, `/v2/auth?users=${encodeURIComponent(userKey)}&users=${encodeURIComponent(secondUserKey)}`), repeat: 3 /* initial + 2 STREAMING_RESET */ }, (url, opts) => { + fetchMock.get({ url: url(settings, `/v2/auth?s=1.1&users=${encodeURIComponent(userKey)}&users=${encodeURIComponent(secondUserKey)}`), repeat: 3 /* initial + 2 STREAMING_RESET */ }, (url, opts) => { if (!opts.headers['Authorization']) assert.fail('`/v2/auth` request must include `Authorization` header'); assert.pass('second auth success'); return { status: 200, body: authPushEnabledNicolasAndMarcio }; }); - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); fetchMock.getOnce(url(settings, '/mySegments/nicolas%40split.io'), { status: 200, body: mySegmentsNicolasMock1 }); fetchMock.getOnce(url(settings, '/mySegments/marcio%40split.io'), { status: 200, body: mySegmentsMarcio }); // fetch due to SPLIT_UPDATE event - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, MILLIS_SPLIT_UPDATE_EVENT_DURING_PUSH), 'sync due to SPLIT_UPDATE event'); return { status: 200, body: splitChangesMock2 }; }); // fetches due to second fallback to polling - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552649999'), { status: 200, body: { splits: [], since: 1457552649999, till: 1457552649999 } }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552649999'), { status: 200, body: { splits: [], since: 1457552649999, till: 1457552649999 } }); fetchMock.getOnce(url(settings, '/mySegments/nicolas%40split.io'), { status: 200, body: mySegmentsNicolasMock1 }); fetchMock.getOnce(url(settings, '/mySegments/marcio%40split.io'), { status: 200, body: mySegmentsMarcio }); // continue fetches due to second fallback to polling - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552649999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552649999'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, MILLIS_STREAMING_PAUSED_CONTROL + settings.scheduler.featuresRefreshRate), 'fetch due to second fallback to polling'); return { status: 200, body: { splits: [], since: 1457552649999, till: 1457552649999 } }; @@ -268,7 +268,7 @@ export function testFallbacking(fetchMock, assert) { fetchMock.getOnce(url(settings, '/mySegments/marcio%40split.io'), { status: 200, body: mySegmentsMarcio }); // split and segment sync due to streaming up (CONTROL event) - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552649999'), { status: 200, body: { splits: [], since: 1457552649999, till: 1457552649999 } }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552649999'), { status: 200, body: { splits: [], since: 1457552649999, till: 1457552649999 } }); fetchMock.getOnce(url(settings, '/mySegments/nicolas%40split.io'), { status: 200, body: mySegmentsNicolasMock1 }); fetchMock.getOnce(url(settings, '/mySegments/marcio%40split.io'), { status: 200, body: mySegmentsMarcio }); @@ -280,17 +280,17 @@ export function testFallbacking(fetchMock, assert) { }); // fetches due to third fallback to polling (STREAMING_PAUSED), two sync all (two STREAMING_RESET events) and fourth fallback (STREAMING_DISABLED) - fetchMock.get({ url: url(settings, '/splitChanges?since=1457552649999'), repeat: 4 }, { status: 200, body: { splits: [], since: 1457552649999, till: 1457552649999 } }); + fetchMock.get({ url: url(settings, '/splitChanges?s=1.1&since=1457552649999'), repeat: 4 }, { status: 200, body: { splits: [], since: 1457552649999, till: 1457552649999 } }); fetchMock.get({ url: url(settings, '/mySegments/nicolas%40split.io'), repeat: 4 }, { status: 200, body: mySegmentsNicolasMock1 }); fetchMock.get({ url: url(settings, '/mySegments/marcio%40split.io'), repeat: 4 }, { status: 200, body: mySegmentsMarcio }); // Periodic fetch due to polling (mySegments is not fetched due to smart pausing) - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552649999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552649999'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, MILLIS_STREAMING_DISABLED_CONTROL + settings.scheduler.featuresRefreshRate), 'fetch due to fourth fallback to polling'); return { status: 200, body: splitChangesMock3 }; }); - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552669999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552669999'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, MILLIS_STREAMING_DISABLED_CONTROL + settings.scheduler.featuresRefreshRate * 2), 'fetch due to fourth fallback to polling'); return { status: 200, body: { splits: [], since: 1457552669999, till: 1457552669999 } }; diff --git a/src/__tests__/browserSuites/push-flag-sets.spec.js b/src/__tests__/browserSuites/push-flag-sets.spec.js index 8ceb64bed..8898f9759 100644 --- a/src/__tests__/browserSuites/push-flag-sets.spec.js +++ b/src/__tests__/browserSuites/push-flag-sets.spec.js @@ -38,19 +38,19 @@ export function testFlagSets(fetchMock, t) { fetchMock.get(baseUrls.sdk + '/mySegments/nicolas%40split.io', { status: 200, body: { 'mySegments': [] } }); - fetchMock.get(baseUrls.auth + '/v2/auth?users=nicolas%40split.io', function () { + fetchMock.get(baseUrls.auth + '/v2/auth?s=1.1&users=nicolas%40split.io', function () { return { status: 200, body: authPushEnabled }; }); - fetchMock.get(baseUrls.sdk + '/splitChanges?since=-1', function () { + fetchMock.get(baseUrls.sdk + '/splitChanges?s=1.1&since=-1', function () { return { status: 200, body: { splits: [], since: -1, till: 0}}; }); - fetchMock.get(baseUrls.sdk + '/splitChanges?since=0', function () { + fetchMock.get(baseUrls.sdk + '/splitChanges?s=1.1&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 () { + fetchMock.get(baseUrls.sdk + '/splitChanges?s=1.1&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 () { + fetchMock.get(baseUrls.sdk + '/splitChanges?s=1.1&since=0&sets=set_1,set_2', function () { return { status: 200, body: { splits: [], since: 0, till: 1 }}; }); @@ -189,7 +189,7 @@ export function testFlagSets(fetchMock, t) { t.test(async (assert) => { - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=2&sets=set_1,set_2', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&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 }}; }); @@ -229,7 +229,7 @@ export function testFlagSets(fetchMock, t) { t.test(async (assert) => { - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1&sets=set_1,set_2', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&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 }}; }); diff --git a/src/__tests__/browserSuites/push-initialization-nopush.spec.js b/src/__tests__/browserSuites/push-initialization-nopush.spec.js index 6e0e0a4f7..568739970 100644 --- a/src/__tests__/browserSuites/push-initialization-nopush.spec.js +++ b/src/__tests__/browserSuites/push-initialization-nopush.spec.js @@ -46,14 +46,14 @@ function testInitializationFail(fetchMock, assert, fallbackToPolling) { let start, splitio, client, ready = false; fetchMock.get(url(settings, '/mySegments/nicolas%40split.io'), { status: 200, body: mySegmentsNicolas }); - fetchMock.getOnce(url(settings, '/splitChanges?since=-1'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, 0), 'initial sync'); return { status: 200, body: splitChangesMock1 }; }); if (fallbackToPolling) { - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function () { assert.true(ready, 'client ready'); const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, 0), 'polling (first fetch)'); @@ -61,7 +61,7 @@ function testInitializationFail(fetchMock, assert, fallbackToPolling) { }); } - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function () { assert.true(ready, 'client ready'); const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, settings.scheduler.featuresRefreshRate), 'polling (second fetch)'); @@ -83,7 +83,7 @@ function testInitializationFail(fetchMock, assert, fallbackToPolling) { export function testAuthWithPushDisabled(fetchMock, assert) { assert.plan(6); - fetchMock.getOnce(`https://auth.push-initialization-nopush/api/v2/auth?users=${encodeURIComponent(userKey)}`, function (url, opts) { + fetchMock.getOnce(`https://auth.push-initialization-nopush/api/v2/auth?s=1.1&users=${encodeURIComponent(userKey)}`, function (url, opts) { if (!opts.headers['Authorization']) assert.fail('`/v2/auth` request must include `Authorization` header'); assert.pass('auth'); return { status: 200, body: authPushDisabled }; @@ -96,7 +96,7 @@ export function testAuthWithPushDisabled(fetchMock, assert) { export function testAuthWith401(fetchMock, assert) { assert.plan(6); - fetchMock.getOnce(url(settings, `/v2/auth?users=${encodeURIComponent(userKey)}`), function (url, opts) { + fetchMock.getOnce(url(settings, `/v2/auth?s=1.1&users=${encodeURIComponent(userKey)}`), function (url, opts) { if (!opts.headers['Authorization']) assert.fail('`/v2/auth` request must include `Authorization` header'); assert.pass('auth'); return { status: 401, body: authInvalidCredentials }; @@ -122,7 +122,7 @@ export function testSSEWithNonRetryableError(fetchMock, assert) { assert.plan(7); // Auth successes - fetchMock.getOnce(url(settings, `/v2/auth?users=${encodeURIComponent(userKey)}`), function (url, opts) { + fetchMock.getOnce(url(settings, `/v2/auth?s=1.1&users=${encodeURIComponent(userKey)}`), function (url, opts) { if (!opts.headers['Authorization']) assert.fail('`/v2/auth` request must include `Authorization` header'); assert.pass('auth successes'); return { status: 200, body: authPushEnabledNicolas }; diff --git a/src/__tests__/browserSuites/push-initialization-retries.spec.js b/src/__tests__/browserSuites/push-initialization-retries.spec.js index 4d72ee15d..d907d21e8 100644 --- a/src/__tests__/browserSuites/push-initialization-retries.spec.js +++ b/src/__tests__/browserSuites/push-initialization-retries.spec.js @@ -51,38 +51,38 @@ export function testPushRetriesDueToAuthErrors(fetchMock, assert) { let start, splitio, client, ready = false; - fetchMock.getOnce(url(settings, `/v2/auth?users=${encodeURIComponent(userKey)}`), function (url, opts) { + fetchMock.getOnce(url(settings, `/v2/auth?s=1.1&users=${encodeURIComponent(userKey)}`), function (url, opts) { if (!opts.headers['Authorization']) assert.fail('`/v2/auth` request must include `Authorization` header'); assert.pass('first auth attempt'); return { status: 200, body: authPushBadToken }; }); - fetchMock.getOnce(url(settings, `/v2/auth?users=${encodeURIComponent(userKey)}`), { throws: new TypeError('Network error') }); - fetchMock.getOnce(url(settings, `/v2/auth?users=${encodeURIComponent(userKey)}`), function (url, opts) { + fetchMock.getOnce(url(settings, `/v2/auth?s=1.1&users=${encodeURIComponent(userKey)}`), { throws: new TypeError('Network error') }); + fetchMock.getOnce(url(settings, `/v2/auth?s=1.1&users=${encodeURIComponent(userKey)}`), function (url, opts) { if (!opts.headers['Authorization']) assert.fail('`/v2/auth` request must include `Authorization` header'); const lapse = Date.now() - start; const expected = (settings.scheduler.pushRetryBackoffBase * Math.pow(2, 0) + settings.scheduler.pushRetryBackoffBase * Math.pow(2, 1)); - assert.true(nearlyEqual(lapse, expected), 'third auth attempt (aproximately in 0.3 seconds from first attempt)'); + assert.true(nearlyEqual(lapse, expected), 'third auth attempt (approximately in 0.3 seconds from first attempt)'); return { status: 200, body: authPushDisabled }; }); fetchMock.get({ url: url(settings, '/mySegments/nicolas%40split.io'), repeat: 4 }, { status: 200, body: mySegmentsNicolasMock }); - fetchMock.getOnce(url(settings, '/splitChanges?since=-1'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, 0), 'initial sync'); return { status: 200, body: splitChangesMock1 }; }); - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function () { assert.true(ready, 'client ready before first polling fetch'); const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, 0), 'fallback to polling'); return { status: 200, body: splitChangesMock2 }; }); - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, settings.scheduler.featuresRefreshRate), 'polling'); return { status: 200, body: splitChangesMock2 }; }); - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, settings.scheduler.featuresRefreshRate * 2), 'keep polling since auth success buth with push disabled'); client.destroy().then(() => { @@ -124,36 +124,36 @@ export function testPushRetriesDueToSseErrors(fetchMock, assert) { } else { const lapse = Date.now() - start; - assert.true(nearlyEqual(lapse, expectedTimeToSSEsuccess), 'third auth attempt (aproximately in 0.3 seconds from first attempt)'); + assert.true(nearlyEqual(lapse, expectedTimeToSSEsuccess), 'third auth attempt (approximately in 0.3 seconds from first attempt)'); eventSourceInstance.emitOpen(); } sseattempts++; }); - fetchMock.get({ url: url(settings, `/v2/auth?users=${encodeURIComponent(userKey)}`), repeat: 3 /* 3 push attempts */ }, function (url, opts) { + fetchMock.get({ url: url(settings, `/v2/auth?s=1.1&users=${encodeURIComponent(userKey)}`), repeat: 3 /* 3 push attempts */ }, function (url, opts) { if (!opts.headers['Authorization']) assert.fail('`/v2/auth` request must include `Authorization` header'); assert.pass('auth success'); return { status: 200, body: authPushEnabledNicolas }; }); fetchMock.get({ url: url(settings, '/mySegments/nicolas%40split.io'), repeat: 4 }, { status: 200, body: mySegmentsNicolasMock }); - fetchMock.getOnce(url(settings, '/splitChanges?since=-1'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, 0), 'initial sync'); return { status: 200, body: splitChangesMock1 }; }); - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function () { assert.true(ready, 'client ready before first polling fetch'); const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, 0), 'fallback to polling'); return { status: 200, body: splitChangesMock2 }; }); - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, settings.scheduler.featuresRefreshRate), 'polling'); return { status: 200, body: splitChangesMock2 }; }); - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, expectedTimeToSSEsuccess), 'sync due to success SSE connection'); client.destroy().then(() => { @@ -189,10 +189,10 @@ export function testSdkDestroyWhileAuthSuccess(fetchMock, assert) { let splitio, client, ready = false; - fetchMock.getOnce(url(settings, `/v2/auth?users=${encodeURIComponent(userKey)}`), { status: 200, body: authPushEnabledNicolas }, { delay: 100 }); + fetchMock.getOnce(url(settings, `/v2/auth?s=1.1&users=${encodeURIComponent(userKey)}`), { status: 200, body: authPushEnabledNicolas }, { delay: 100 }); fetchMock.getOnce(url(settings, '/mySegments/nicolas%40split.io'), { status: 200, body: mySegmentsNicolasMock }); - fetchMock.getOnce(url(settings, '/splitChanges?since=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); setTimeout(() => { client.destroy().then(() => { @@ -224,9 +224,9 @@ export function testSdkDestroyWhileConnDelay(fetchMock, assert) { assert.fail('unexpected EventSource request with url: ' + eventSourceInstance.url); }); - fetchMock.getOnce(url(settings, `/v2/auth?users=${encodeURIComponent(userKey)}`), { status: 200, body: { ...authPushEnabledNicolas, connDelay: 0.1 } }); + fetchMock.getOnce(url(settings, `/v2/auth?s=1.1&users=${encodeURIComponent(userKey)}`), { status: 200, body: { ...authPushEnabledNicolas, connDelay: 0.1 } }); fetchMock.getOnce(url(settings, '/mySegments/nicolas%40split.io'), { status: 200, body: mySegmentsNicolasMock }); - fetchMock.getOnce(url(settings, '/splitChanges?since=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); const client = SplitFactory(config).client(); setTimeout(() => { @@ -255,12 +255,12 @@ export function testSdkDestroyWhileAuthRetries(fetchMock, assert) { let splitio, client, ready = false; - fetchMock.getOnce(url(settings, `/v2/auth?users=${encodeURIComponent(userKey)}`), { status: 200, body: authPushBadToken }); - fetchMock.getOnce(url(settings, `/v2/auth?users=${encodeURIComponent(userKey)}`), { throws: new TypeError('Network error') }, { delay: 100 }); + fetchMock.getOnce(url(settings, `/v2/auth?s=1.1&users=${encodeURIComponent(userKey)}`), { status: 200, body: authPushBadToken }); + fetchMock.getOnce(url(settings, `/v2/auth?s=1.1&users=${encodeURIComponent(userKey)}`), { throws: new TypeError('Network error') }, { delay: 100 }); fetchMock.get({ url: url(settings, '/mySegments/nicolas%40split.io'), repeat: 2 }, { status: 200, body: mySegmentsNicolasMock }); - fetchMock.getOnce(url(settings, '/splitChanges?since=-1'), { status: 200, body: splitChangesMock1 }); - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), { status: 200, body: splitChangesMock2 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), { status: 200, body: splitChangesMock2 }); fetchMock.get(new RegExp('.*'), function (url) { assert.fail('unexpected GET request with url: ' + url); diff --git a/src/__tests__/browserSuites/push-refresh-token.spec.js b/src/__tests__/browserSuites/push-refresh-token.spec.js index 8a226503b..cecfcb1fe 100644 --- a/src/__tests__/browserSuites/push-refresh-token.spec.js +++ b/src/__tests__/browserSuites/push-refresh-token.spec.js @@ -77,22 +77,22 @@ export function testRefreshToken(fetchMock, assert) { }); // initial sync - fetchMock.getOnce(url(settings, '/splitChanges?since=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); fetchMock.getOnce(url(settings, '/mySegments/nicolas%40split.io'), { status: 200, body: mySegmentsNicolasMock1 }); // first auth - fetchMock.getOnce(url(settings, `/v2/auth?users=${encodeURIComponent(userKey)}`), function (url, opts) { + fetchMock.getOnce(url(settings, `/v2/auth?s=1.1&users=${encodeURIComponent(userKey)}`), function (url, opts) { if (!opts.headers['Authorization']) assert.fail('`/v2/auth` request must include `Authorization` header'); assert.pass('auth success'); return { status: 200, body: authPushEnabledNicolas }; }); // sync after SSE opened - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), { status: 200, body: splitChangesMock2 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), { status: 200, body: splitChangesMock2 }); fetchMock.getOnce(url(settings, '/mySegments/nicolas%40split.io'), { status: 200, body: mySegmentsNicolasMock1 }); // re-auth due to refresh token, with connDelay of 0.5 seconds - fetchMock.getOnce(url(settings, `/v2/auth?users=${encodeURIComponent(userKey)}`), function (url, opts) { + fetchMock.getOnce(url(settings, `/v2/auth?s=1.1&users=${encodeURIComponent(userKey)}`), function (url, opts) { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, MILLIS_REFRESH_TOKEN), 'reauthentication for token refresh'); if (!opts.headers['Authorization']) assert.fail('`/v2/auth` request must include `Authorization` header'); @@ -100,7 +100,7 @@ export function testRefreshToken(fetchMock, assert) { }); // sync after SSE reopened - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, MILLIS_REFRESH_TOKEN + MILLIS_CONNDELAY), 'sync after SSE connection is reopened'); return { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }; @@ -108,7 +108,7 @@ export function testRefreshToken(fetchMock, assert) { fetchMock.getOnce(url(settings, '/mySegments/nicolas%40split.io'), { status: 200, body: mySegmentsNicolasMock1 }); // second re-auth due to refresh token, this time responding with pushEnabled false - fetchMock.getOnce(url(settings, `/v2/auth?users=${encodeURIComponent(userKey)}`), function (url, opts) { + fetchMock.getOnce(url(settings, `/v2/auth?s=1.1&users=${encodeURIComponent(userKey)}`), function (url, opts) { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, MILLIS_REFRESH_TOKEN * 2), 'second reauthentication for token refresh'); if (!opts.headers['Authorization']) assert.fail('`/v2/auth` request must include `Authorization` header'); @@ -116,7 +116,7 @@ export function testRefreshToken(fetchMock, assert) { }); // split sync after SSE closed due to push disabled - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, MILLIS_REFRESH_TOKEN * 2), 'sync after SSE connection is reopened a second time'); setTimeout(() => { diff --git a/src/__tests__/browserSuites/push-synchronization-retries.spec.js b/src/__tests__/browserSuites/push-synchronization-retries.spec.js index 03c79554f..c7031b7c9 100644 --- a/src/__tests__/browserSuites/push-synchronization-retries.spec.js +++ b/src/__tests__/browserSuites/push-synchronization-retries.spec.js @@ -135,19 +135,19 @@ export function testSynchronizationRetries(fetchMock, assert) { }); // initial auth - fetchMock.getOnce(url(settings, `/v2/auth?users=${encodeURIComponent(userKey)}&users=${encodeURIComponent(otherUserKeySync)}`), function (url, opts) { + fetchMock.getOnce(url(settings, `/v2/auth?s=1.1&users=${encodeURIComponent(userKey)}&users=${encodeURIComponent(otherUserKeySync)}`), function (url, opts) { if (!opts.headers['Authorization']) assert.fail('`/v2/auth` request must include `Authorization` header'); assert.pass('auth success'); return { status: 200, body: authPushEnabledNicolas }; }); // initial split and mySegments sync - fetchMock.getOnce(url(settings, '/splitChanges?since=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); fetchMock.getOnce(url(settings, '/mySegments/nicolas%40split.io'), { status: 200, body: mySegmentsNicolasMock1 }); fetchMock.get({ url: url(settings, '/mySegments/marcio%40split.io'), repeat: 2 }, { status: 200, body: mySegmentsMarcio }); // split and segment sync after SSE opened - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, MILLIS_SSE_OPEN), 'sync after SSE connection is opened'); return { status: 200, body: splitChangesMock2 }; @@ -155,9 +155,9 @@ export function testSynchronizationRetries(fetchMock, assert) { fetchMock.getOnce(url(settings, '/mySegments/nicolas%40split.io'), { status: 200, body: mySegmentsNicolasMock1 }); // fetch due to SPLIT_UPDATE event - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), { status: 200, body: splitChangesMock2 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), { status: 200, body: splitChangesMock2 }); // fetch retry for SPLIT_UPDATE event, due to previous unexpected response (response till minor than SPLIT_UPDATE changeNumber) - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, MILLIS_RETRY_FOR_FIRST_SPLIT_UPDATE_EVENT), 'fetch retry due to SPLIT_UPDATE event'); return { status: 200, body: splitChangesMock3 }; @@ -177,18 +177,18 @@ export function testSynchronizationRetries(fetchMock, assert) { }); // fetch due to SPLIT_KILL event - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552649999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552649999'), function () { assert.equal(client.getTreatment('whitelist'), 'not_allowed', 'evaluation with split killed immediately, before fetch is done'); const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, MILLIS_SPLIT_KILL_EVENT), 'sync due to SPLIT_KILL event'); return { status: 200, body: { since: 1457552649999, till: 1457552649999, splits: [] } }; // returning old state }); // first fetch retry for SPLIT_KILL event, due to previous unexpected response (response till minor than SPLIT_KILL changeNumber) - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552649999'), { throws: new TypeError('Network error') }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552649999'), { throws: new TypeError('Network error') }); // second fetch retry for SPLIT_KILL event - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552649999'), { status: 200, body: '{ "since": 1457552620999, "til' }); // invalid JSON response + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552649999'), { status: 200, body: '{ "since": 1457552620999, "til' }); // invalid JSON response // third fetch retry for SPLIT_KILL event - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552649999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552649999'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, MILLIS_THIRD_RETRY_FOR_SPLIT_KILL_EVENT), 'third fetch retry due to SPLIT_KILL event'); diff --git a/src/__tests__/browserSuites/push-synchronization.spec.js b/src/__tests__/browserSuites/push-synchronization.spec.js index 370abcb73..4ca030726 100644 --- a/src/__tests__/browserSuites/push-synchronization.spec.js +++ b/src/__tests__/browserSuites/push-synchronization.spec.js @@ -265,7 +265,7 @@ export function testSynchronization(fetchMock, assert) { // initial auth let authParams = `users=${encodeURIComponent(userKey)}`; - fetchMock.getOnce(url(settings, `/v2/auth?${authParams}`), function (url, opts) { + fetchMock.getOnce(url(settings, `/v2/auth?s=1.1&${authParams}`), function (url, opts) { if (!opts.headers['Authorization']) assert.fail('`/v2/auth` request must include `Authorization` header'); assert.pass('auth success'); return { status: 200, body: authPushEnabledNicolas }; @@ -273,7 +273,7 @@ export function testSynchronization(fetchMock, assert) { // reauth due to new client authParams += `&users=${encodeURIComponent(otherUserKey)}`; - fetchMock.getOnce(url(settings, `/v2/auth?${authParams}`), function (url, opts) { + fetchMock.getOnce(url(settings, `/v2/auth?s=1.1&${authParams}`), function (url, opts) { if (!opts.headers['Authorization']) assert.fail('`/v2/auth` request must include `Authorization` header'); assert.pass('second auth success'); return { status: 200, body: authPushEnabledNicolasAndMarcio }; @@ -281,10 +281,10 @@ export function testSynchronization(fetchMock, assert) { // reauth due to more clients authParams += `&users=${encodeURIComponent(keylistAddKey)}&users=${encodeURIComponent(keylistRemoveKey)}&users=${encodeURIComponent(bitmapTrueKey)}`; - fetchMock.getOnce(url(settings, `/v2/auth?${authParams}`), { status: 200, body: authPushEnabledNicolasAndMarcio }); + fetchMock.getOnce(url(settings, `/v2/auth?s=1.1&${authParams}`), { status: 200, body: authPushEnabledNicolasAndMarcio }); // initial split and mySegments sync - fetchMock.getOnce(url(settings, '/splitChanges?since=-1'), function (url, opts) { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), function (url, opts) { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, 0), 'initial sync'); if (hasNoCacheHeader(opts)) assert.fail('request must not include `Cache-Control` header'); @@ -296,7 +296,7 @@ export function testSynchronization(fetchMock, assert) { }); // split and segment sync after SSE opened - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), function (url, opts) { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function (url, opts) { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, MILLIS_SSE_OPEN), 'sync after SSE connection is opened'); if (hasNoCacheHeader(opts)) assert.fail('request must not include `Cache-Control` header'); @@ -308,7 +308,7 @@ export function testSynchronization(fetchMock, assert) { }); // fetch due to SPLIT_UPDATE event - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), function (url, opts) { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function (url, opts) { if (!hasNoCacheHeader(opts)) assert.fail('request must include `Cache-Control` header'); return { status: 200, body: splitChangesMock3 }; }); @@ -320,7 +320,7 @@ export function testSynchronization(fetchMock, assert) { }); // fetch due to SPLIT_KILL event - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552649999'), function (url, opts) { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552649999'), function (url, opts) { if (!hasNoCacheHeader(opts)) assert.fail('request must include `Cache-Control` header'); assert.equal(client.getTreatment('whitelist'), 'not_allowed', 'evaluation with split killed immediately, before fetch is done'); return { status: 200, body: splitChangesMock4 }; @@ -333,7 +333,7 @@ export function testSynchronization(fetchMock, assert) { }); // split and mySegment sync after second SSE opened - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552650000'), function (url, opts) { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552650000'), function (url, opts) { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, MILLIS_SECOND_SSE_OPEN), 'sync after second SSE connection is opened'); if (hasNoCacheHeader(opts)) assert.fail('request must not include `Cache-Control` header'); @@ -358,7 +358,7 @@ export function testSynchronization(fetchMock, assert) { }); // initial fetch of mySegments for other clients + sync after third SSE opened + 3 unbounded fetch requests - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552650000'), { status: 200, body: { splits: [], since: 1457552650000, till: 1457552650000 } }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552650000'), { status: 200, body: { splits: [], since: 1457552650000, till: 1457552650000 } }); fetchMock.get({ url: url(settings, '/mySegments/key1'), repeat: 5 }, { status: 200, body: { mySegments: [] } }); fetchMock.get({ url: url(settings, '/mySegments/key3'), repeat: 5 }, { status: 200, body: { mySegments: [{ name: 'splitters' }] } }); fetchMock.get({ url: url(settings, `/mySegments/${bitmapTrueKey}`), repeat: 5 }, { status: 200, body: { mySegments: [] } }); diff --git a/src/__tests__/browserSuites/readiness.spec.js b/src/__tests__/browserSuites/readiness.spec.js index c6897fafd..3b98d5132 100644 --- a/src/__tests__/browserSuites/readiness.spec.js +++ b/src/__tests__/browserSuites/readiness.spec.js @@ -38,13 +38,13 @@ export default function (fetchMock, assert) { sdk: 'https://sdk.baseurl/readinessSuite1', events: 'https://events.baseurl/readinessSuite1' }; - fetchMock.get(testUrls.sdk + '/splitChanges?since=-1', function () { + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.1&since=-1', function () { return new Promise((res) => { setTimeout(() => { res({ status: 200, body: splitChangesMock1, headers: {} }); }, requestTimeoutBeforeReady * 1000 + 50); }); }); fetchMock.get(testUrls.sdk + '/mySegments/nicolas%40split.io', function () { return new Promise((res) => { setTimeout(() => { res({ status: 200, body: mySegmentsNicolas, headers: {} }); }, requestTimeoutBeforeReady * 1000 - 50); }); }); - fetchMock.get(testUrls.sdk + '/splitChanges?since=1457552620999', { status: 200, body: splitChangesMock2 }); + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.1&since=1457552620999', { status: 200, body: splitChangesMock2 }); const splitio = SplitFactory({ ...baseConfig, urls: testUrls @@ -67,13 +67,13 @@ export default function (fetchMock, assert) { sdk: 'https://sdk.baseurl/readinessSuite2', events: 'https://events.baseurl/readinessSuite2' }; - fetchMock.get(testUrls.sdk + '/splitChanges?since=-1', function () { + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.1&since=-1', function () { return new Promise((res) => { setTimeout(() => { res({ status: 200, body: splitChangesMock1, headers: {} }); }, requestTimeoutBeforeReady * 1000 - 50); }); }); fetchMock.get(testUrls.sdk + '/mySegments/nicolas%40split.io', function () { return new Promise((res) => { setTimeout(() => { res({ status: 200, body: mySegmentsNicolas, headers: {} }); }, requestTimeoutBeforeReady * 1000 + 50); }); }); - fetchMock.get(testUrls.sdk + '/splitChanges?since=1457552620999', { status: 200, body: splitChangesMock2 }); + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.1&since=1457552620999', { status: 200, body: splitChangesMock2 }); const splitio = SplitFactory({ ...baseConfig, urls: testUrls }); const client = splitio.client(); @@ -95,16 +95,16 @@ export default function (fetchMock, assert) { events: 'https://events.baseurl/readinessSuite3' }; - fetchMock.getOnce(testUrls.sdk + '/splitChanges?since=-1', function () { + fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.1&since=-1', function () { return new Promise((res) => { setTimeout(() => { res({ status: 200, body: splitChangesMock1, headers: {} }); }, requestTimeoutBeforeReady * 1000 + 50); }); }); - fetchMock.getOnce(testUrls.sdk + '/splitChanges?since=-1', function () { + fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.1&since=-1', function () { return new Promise((res) => { setTimeout(() => { res({ status: 200, body: splitChangesMock1, headers: {} }); }, requestTimeoutBeforeReady * 1000 - 50); }); // Faster, it should get ready on the retry. }); fetchMock.get(testUrls.sdk + '/mySegments/nicolas%40split.io', function () { return new Promise((res) => { setTimeout(() => { res({ status: 200, body: mySegmentsNicolas, headers: {} }); }, requestTimeoutBeforeReady * 1000 - 50); }); }); - fetchMock.get(testUrls.sdk + '/splitChanges?since=1457552620999', { status: 200, body: splitChangesMock2 }); + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.1&since=1457552620999', { status: 200, body: splitChangesMock2 }); const splitio = SplitFactory({ ...baseConfig, urls: testUrls }); const client = splitio.client(); @@ -130,18 +130,18 @@ export default function (fetchMock, assert) { return new Promise((res) => { setTimeout(() => { res({ status: 200, body: { mySegments: [] } }); }, mySegmentsEndpointDelay); }); }); // Now mock the no more updates state - fetchMock.get(testUrls.sdk + '/splitChanges?since=1457552669999', { status: 200, body: { splits: [], since: 1457552669999, till: 1457552669999 } }); + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.1&since=1457552669999', { status: 200, body: { splits: [], since: 1457552669999, till: 1457552669999 } }); if (startWithSegments) { // Adjust since and till so the order is inverted. - fetchMock.get(testUrls.sdk + '/splitChanges?since=-1', { status: 200, body: splitChangesStartWithSegmentsMock }); - fetchMock.get(testUrls.sdk + '/splitChanges?since=1457552620999', { status: 200, body: { ...splitChangesUpdateWithoutSegmentsMock, since: 1457552620999, till: 1457552649999 } }); - fetchMock.get(testUrls.sdk + '/splitChanges?since=1457552649999', { status: 200, body: { ...splitChangesUpdateWithSegmentsMock, since: 1457552649999, till: 1457552669999 } }); + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.1&since=-1', { status: 200, body: splitChangesStartWithSegmentsMock }); + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.1&since=1457552620999', { status: 200, body: { ...splitChangesUpdateWithoutSegmentsMock, since: 1457552620999, till: 1457552649999 } }); + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.1&since=1457552649999', { status: 200, body: { ...splitChangesUpdateWithSegmentsMock, since: 1457552649999, till: 1457552669999 } }); } else { - fetchMock.get(testUrls.sdk + '/splitChanges?since=-1', { status: 200, body: splitChangesStartWithoutSegmentsMock }); - fetchMock.get(testUrls.sdk + '/splitChanges?since=1457552620999', { status: 200, body: splitChangesUpdateWithSegmentsMock }); - fetchMock.get(testUrls.sdk + '/splitChanges?since=1457552649999', { status: 200, body: splitChangesUpdateWithoutSegmentsMock }); + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.1&since=-1', { status: 200, body: splitChangesStartWithoutSegmentsMock }); + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.1&since=1457552620999', { status: 200, body: splitChangesUpdateWithSegmentsMock }); + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.1&since=1457552649999', { status: 200, body: splitChangesUpdateWithoutSegmentsMock }); } return () => mySegmentsHits; @@ -562,8 +562,8 @@ export default function (fetchMock, assert) { const getMySegmentsHits = mockForSegmentsPauseTest(testUrls, false); // I'm having the first update of Splits come with segments. In this scenario it'll wait for mySegments to download before being ready. - fetchMock.get({ url: testUrls.sdk + '/splitChanges?since=1457552669999', overwriteRoutes: true }, { status: 200, body: { ...splitChangesUpdateWithSegmentsMock, since: 1457552669999, till: 1457552679999 } }); - fetchMock.get(testUrls.sdk + '/splitChanges?since=1457552679999', { status: 200, body: { splits: [], since: 1457552679999, till: 1457552679999 } }); + fetchMock.get({ url: testUrls.sdk + '/splitChanges?s=1.1&since=1457552669999', overwriteRoutes: true }, { status: 200, body: { ...splitChangesUpdateWithSegmentsMock, since: 1457552669999, till: 1457552679999 } }); + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.1&since=1457552679999', { status: 200, body: { splits: [], since: 1457552679999, till: 1457552679999 } }); const start = Date.now(); const splitio = SplitFactory({ @@ -659,8 +659,8 @@ export default function (fetchMock, assert) { }; const getMySegmentsHits = mockForSegmentsPauseTest(testUrls, false); // I'm having the first update of Splits come without segments. In this scenario it'll NOT wait for mySegments to download before being ready. - fetchMock.get({ url: testUrls.sdk + '/splitChanges?since=1457552669999', overwriteRoutes: true }, { status: 200, body: { ...splitChangesUpdateWithoutSegmentsMock, since: 1457552669999, till: 1457552679999 } }); - fetchMock.get(testUrls.sdk + '/splitChanges?since=1457552679999', { status: 200, body: { splits: [], since: 1457552679999, till: 1457552679999 } }); + fetchMock.get({ url: testUrls.sdk + '/splitChanges?s=1.1&since=1457552669999', overwriteRoutes: true }, { status: 200, body: { ...splitChangesUpdateWithoutSegmentsMock, since: 1457552669999, till: 1457552679999 } }); + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.1&since=1457552679999', { status: 200, body: { splits: [], since: 1457552679999, till: 1457552679999 } }); const start = Date.now(); const splitio = SplitFactory({ diff --git a/src/__tests__/browserSuites/ready-from-cache.spec.js b/src/__tests__/browserSuites/ready-from-cache.spec.js index 9330b0bb8..828f16122 100644 --- a/src/__tests__/browserSuites/ready-from-cache.spec.js +++ b/src/__tests__/browserSuites/ready-from-cache.spec.js @@ -83,8 +83,8 @@ const baseConfig = { streamingEnabled: false }; -const expectedHashNullFilter = '9507ef4'; // for SDK key '' and filter query null -const expectedHashWithFilter = '6a596ded'; // for SDK key '' and filter query '&names=p1__split,p2__split' +const expectedHashNullFilter = '2a2c20bb'; // for SDK key '', filter query null, and flags spec version '1.1' +const expectedHashWithFilter = 'fdf7bd89'; // for SDK key '', filter query '&names=p1__split,p2__split', and flags spec version '1.1' export default function (fetchMock, assert) { @@ -96,8 +96,8 @@ export default function (fetchMock, assert) { localStorage.clear(); t.plan(3); - fetchMock.get(testUrls.sdk + '/splitChanges?since=-1', { status: 200, body: splitChangesMock1 }); - fetchMock.get(testUrls.sdk + '/splitChanges?since=1457552620999', { status: 200, body: splitChangesMock2 }); + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.1&since=-1', { status: 200, body: splitChangesMock1 }); + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.1&since=1457552620999', { status: 200, body: splitChangesMock2 }); fetchMock.get(testUrls.sdk + '/mySegments/nicolas%40split.io', { status: 200, body: mySegmentsNicolas }); fetchMock.get(testUrls.sdk + '/mySegments/nicolas2%40split.io', { status: 200, body: { 'mySegments': [] } }); fetchMock.get(testUrls.sdk + '/mySegments/nicolas3%40split.io', { status: 200, body: { 'mySegments': [] } }); @@ -147,10 +147,10 @@ export default function (fetchMock, assert) { localStorage.clear(); t.plan(12 * 2 + 3); - fetchMock.get(testUrls.sdk + '/splitChanges?since=25', function () { + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.1&since=25', function () { return new Promise(res => { setTimeout(() => res({ status: 200, body: { ...splitChangesMock1, since: 25 }, headers: {} }), 200); }); // 400ms is how long it'll take to reply with Splits, no SDK_READY should be emitted before that. }); - fetchMock.get(testUrls.sdk + '/splitChanges?since=1457552620999', { status: 200, body: splitChangesMock2 }); + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.1&since=1457552620999', { status: 200, body: splitChangesMock2 }); fetchMock.get(testUrls.sdk + '/mySegments/nicolas%40split.io', function () { return new Promise(res => { setTimeout(() => res({ status: 200, body: mySegmentsNicolas, headers: {} }), 400); }); // First client gets segments before splits. No segment cache loading (yet) }); @@ -255,11 +255,11 @@ export default function (fetchMock, assert) { localStorage.clear(); t.plan(12 * 2 + 5); - fetchMock.get(testUrls.sdk + '/splitChanges?since=25', function () { + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.1&since=25', function () { t.equal(localStorage.getItem('readyFromCache_3.SPLITIO.split.always_on'), alwaysOnSplitInverted, 'feature flags must not be cleaned from cache'); return new Promise(res => { setTimeout(() => res({ status: 200, body: { ...splitChangesMock1, since: 25 }, headers: {} }), 200); }); // 400ms is how long it'll take to reply with Splits, no SDK_READY should be emitted before that. }); - fetchMock.get(testUrls.sdk + '/splitChanges?since=1457552620999', { status: 200, body: splitChangesMock2 }); + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.1&since=1457552620999', { status: 200, body: splitChangesMock2 }); fetchMock.get(testUrls.sdk + '/mySegments/nicolas%40split.io', function () { return new Promise(res => { setTimeout(() => res({ status: 200, body: mySegmentsNicolas, headers: {} }), 400); }); // First client gets segments before splits. No segment cache loading (yet) }); @@ -372,13 +372,13 @@ export default function (fetchMock, assert) { }; localStorage.clear(); - fetchMock.get(testUrls.sdk + '/splitChanges?since=-1', function () { + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.1&since=-1', function () { t.equal(localStorage.getItem('some_user_item'), 'user_item', 'user items at localStorage must not be changed'); t.equal(localStorage.getItem('readyFromCache_4.SPLITIO.hash'), expectedHashNullFilter, 'storage hash must not be changed'); t.equal(localStorage.length, 2, 'feature flags cache data must be cleaned from localStorage'); return { status: 200, body: splitChangesMock1 }; }); - fetchMock.get(testUrls.sdk + '/splitChanges?since=1457552620999', { status: 200, body: splitChangesMock2 }); + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.1&since=1457552620999', { status: 200, body: splitChangesMock2 }); fetchMock.get(testUrls.sdk + '/mySegments/nicolas%40split.io', function () { return new Promise(res => { setTimeout(() => res({ status: 200, body: mySegmentsNicolas, headers: {} }), 400); }); // First client gets segments before splits. No segment cache loading (yet) }); @@ -488,8 +488,8 @@ export default function (fetchMock, assert) { localStorage.clear(); t.plan(7); - 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 } }); + fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.1&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?s=1.1&since=1457552620999&names=p1__split', { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); fetchMock.getOnce(testUrls.sdk + '/mySegments/nicolas%40split.io', { status: 200, body: { mySegments: [] } }); localStorage.setItem('some_user_item', 'user_item'); @@ -541,7 +541,7 @@ export default function (fetchMock, assert) { localStorage.clear(); t.plan(5); - 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?s=1.1&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 + '/mySegments/nicolas%40split.io', { status: 200, body: { mySegments: [] } }); const splitio = SplitFactory({ @@ -585,7 +585,7 @@ export default function (fetchMock, assert) { localStorage.clear(); 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 + '/splitChanges?s=1.1&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: [] } }); const expectedHash = getStorageHash({ ...baseConfig, sync: { __splitFiltersValidation: { queryString: '&names=p2__split&prefixes=p1' } } }); @@ -636,7 +636,7 @@ export default function (fetchMock, assert) { localStorage.clear(); t.plan(6); - fetchMock.getOnce(testUrls.sdk + '/splitChanges?since=-1&prefixes=p1,p2', { 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?s=1.1&since=-1&prefixes=p1,p2', { 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 + '/mySegments/nicolas%40split.io', { status: 200, body: { mySegments: [] } }); const expectedHash = getStorageHash({ ...baseConfig, sync: { __splitFiltersValidation: { queryString: '&prefixes=p1,p2' } } }); @@ -702,7 +702,7 @@ export default function (fetchMock, assert) { localStorage.clear(); t.plan(7); - fetchMock.getOnce(testUrls.sdk + '/splitChanges?since=-1', { status: 200, body: { splits: [splitDeclarations.p1__split, 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 + '/splitChanges?s=1.1&since=-1', { status: 200, body: { splits: [splitDeclarations.p1__split, 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'); @@ -756,7 +756,7 @@ export default function (fetchMock, assert) { localStorage.clear(); 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 + '/splitChanges?s=1.1&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: [] } }); localStorage.setItem('some_user_item', 'user_item'); diff --git a/src/__tests__/browserSuites/ready-promise.spec.js b/src/__tests__/browserSuites/ready-promise.spec.js index bdd3fc20f..56ac7922e 100644 --- a/src/__tests__/browserSuites/ready-promise.spec.js +++ b/src/__tests__/browserSuites/ready-promise.spec.js @@ -59,8 +59,8 @@ export default function readyPromiseAssertions(fetchMock, assert) { }; // /splitChanges takes longer than 'requestTimeoutBeforeReady' in both attempts - fetchMock.getOnce(config.urls.sdk + '/splitChanges?since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); - fetchMock.getOnce(config.urls.sdk + '/splitChanges?since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); fetchMock.get(config.urls.sdk + '/mySegments/facundo%40split.io', mySegmentsFacundo, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); fetchMock.postOnce(config.urls.events + '/testImpressions/bulk', 200); fetchMock.postOnce(config.urls.events + '/testImpressions/count', 200); @@ -107,8 +107,8 @@ export default function readyPromiseAssertions(fetchMock, assert) { }; // /splitChanges takes longer than 'requestTimeoutBeforeReady' only for the first attempt - fetchMock.getOnce(config.urls.sdk + '/splitChanges?since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); - fetchMock.getOnce(config.urls.sdk + '/splitChanges?since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); fetchMock.get(config.urls.sdk + '/mySegments/facundo%40split.io', mySegmentsFacundo, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); fetchMock.postOnce(config.urls.events + '/testImpressions/bulk', 200); fetchMock.postOnce(config.urls.events + '/testImpressions/count', 200); @@ -157,8 +157,8 @@ export default function readyPromiseAssertions(fetchMock, assert) { }; // /splitChanges takes longer than 'requestTimeoutBeforeReady' only for the first attempt - fetchMock.getOnce(config.urls.sdk + '/splitChanges?since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); - fetchMock.getOnce(config.urls.sdk + '/splitChanges?since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); fetchMock.get(config.urls.sdk + '/mySegments/facundo%40split.io', mySegmentsFacundo, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); fetchMock.postOnce(config.urls.events + '/testImpressions/bulk', 200); fetchMock.postOnce(config.urls.events + '/testImpressions/count', 200); @@ -226,12 +226,12 @@ export default function readyPromiseAssertions(fetchMock, assert) { config.scheduler.featuresRefreshRate) - config.startup.readyTimeout) + refreshTimeMillis; // /splitChanges takes longer than 'requestTimeoutBeforeReady' in both initial attempts - fetchMock.getOnce(config.urls.sdk + '/splitChanges?since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); - fetchMock.getOnce(config.urls.sdk + '/splitChanges?since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); - fetchMock.getOnce(config.urls.sdk + '/splitChanges?since=-1', splitChangesMock1, { delay: refreshTimeMillis }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: refreshTimeMillis }); // main client endpoint configured to fetch segments before request timeout fetchMock.get(config.urls.sdk + '/mySegments/facundo%40split.io', mySegmentsFacundo, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); - fetchMock.get(config.urls.sdk + '/splitChanges?since=1457552620999', { splits: [], since: 1457552620999, till: 1457552620999 }); + fetchMock.get(config.urls.sdk + '/splitChanges?s=1.1&since=1457552620999', { splits: [], since: 1457552620999, till: 1457552620999 }); // shared client endpoint configured to fetch segments immediately, in order to emit SDK_READY as soon as splits arrives fetchMock.get(config.urls.sdk + '/mySegments/nicolas%40split.io', mySegmentsFacundo); // shared client endpoint configured to emit SDK_READY_TIMED_OUT @@ -310,7 +310,7 @@ export default function readyPromiseAssertions(fetchMock, assert) { }; // /splitChanges takes longer than 'requestTimeoutBeforeReady' - fetchMock.get(config.urls.sdk + '/splitChanges?since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.get(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); fetchMock.get(config.urls.sdk + '/mySegments/facundo%40split.io', mySegmentsFacundo, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); fetchMock.postOnce(config.urls.events + '/testImpressions/bulk', 200); fetchMock.postOnce(config.urls.events + '/testImpressions/count', 200); @@ -360,7 +360,7 @@ export default function readyPromiseAssertions(fetchMock, assert) { }; // Both /splitChanges and /mySegments take less than 'requestTimeoutBeforeReady' - fetchMock.get(config.urls.sdk + '/splitChanges?since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); + fetchMock.get(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); fetchMock.get(config.urls.sdk + '/mySegments/facundo%40split.io', mySegmentsFacundo, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); fetchMock.postOnce(config.urls.events + '/testImpressions/bulk', 200); fetchMock.postOnce(config.urls.events + '/testImpressions/count', 200); @@ -407,8 +407,8 @@ export default function readyPromiseAssertions(fetchMock, assert) { }; // /splitChanges takes longer than 'requestTimeoutBeforeReady' only for the first attempt - fetchMock.getOnce(config.urls.sdk + '/splitChanges?since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); - fetchMock.getOnce(config.urls.sdk + '/splitChanges?since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); fetchMock.get(config.urls.sdk + '/mySegments/facundo%40split.io', mySegmentsFacundo, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); fetchMock.postOnce(config.urls.events + '/testImpressions/bulk', 200); fetchMock.postOnce(config.urls.events + '/testImpressions/count', 200); @@ -496,8 +496,8 @@ export default function readyPromiseAssertions(fetchMock, assert) { }; // /splitChanges takes longer than 'requestTimeoutBeforeReady' only for the first attempt - fetchMock.getOnce(config.urls.sdk + '/splitChanges?since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); - fetchMock.getOnce(config.urls.sdk + '/splitChanges?since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); fetchMock.get(config.urls.sdk + '/mySegments/facundo%40split.io', mySegmentsFacundo, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); fetchMock.get(config.urls.sdk + '/mySegments/nicolas%40split.io', mySegmentsFacundo); fetchMock.get(config.urls.sdk + '/mySegments/emiliano%40split.io', mySegmentsFacundo); @@ -581,7 +581,7 @@ export default function readyPromiseAssertions(fetchMock, assert) { }; // /splitChanges takes longer than 'requestTimeoutBeforeReady' - fetchMock.get(config.urls.sdk + '/splitChanges?since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.get(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); fetchMock.get(config.urls.sdk + '/mySegments/facundo%40split.io', mySegmentsFacundo, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); fetchMock.get(config.urls.sdk + '/mySegments/nicolas%40split.io', mySegmentsFacundo, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); fetchMock.postOnce(config.urls.events + '/testImpressions/bulk', 200); diff --git a/src/__tests__/browserSuites/single-sync.spec.js b/src/__tests__/browserSuites/single-sync.spec.js index f87783da1..19e46db52 100644 --- a/src/__tests__/browserSuites/single-sync.spec.js +++ b/src/__tests__/browserSuites/single-sync.spec.js @@ -36,7 +36,7 @@ const settings = settingsFactory(config); export default function singleSync(fetchMock, assert) { - fetchMock.getOnce(url(settings, '/splitChanges?since=-1'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), function () { assert.pass('first splitChanges fetch'); return { status: 200, body: splitChangesMock1 }; }); diff --git a/src/__tests__/browserSuites/telemetry.spec.js b/src/__tests__/browserSuites/telemetry.spec.js index 13eefa03b..d1f77d129 100644 --- a/src/__tests__/browserSuites/telemetry.spec.js +++ b/src/__tests__/browserSuites/telemetry.spec.js @@ -33,8 +33,8 @@ const SplitFactoryForTest = (config) => { 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 + '/splitChanges?s=1.1&since=-1', 500); + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&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'] } }); @@ -188,7 +188,7 @@ export default async function telemetryBrowserSuite(fetchMock, t) { 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.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&since=-1&sets=a,c,d', { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); fetchMock.postOnce(baseUrls.telemetry + '/v1/metrics/config', (url, opts) => { const data = JSON.parse(opts.body); diff --git a/src/__tests__/browserSuites/use-beacon-api.debug.spec.js b/src/__tests__/browserSuites/use-beacon-api.debug.spec.js index 9b692f2f2..2dede6e28 100644 --- a/src/__tests__/browserSuites/use-beacon-api.debug.spec.js +++ b/src/__tests__/browserSuites/use-beacon-api.debug.spec.js @@ -66,8 +66,8 @@ function beaconApiNotSendTestDebug(fetchMock, assert) { sendBeaconSpyDebug = sinon.spy(window.navigator, 'sendBeacon'); // Mocking this specific route to make sure we only get the items we want to test from the handlers. - fetchMock.get(url(settings, '/splitChanges?since=-1'), { status: 200, body: splitChangesMock1 }); - fetchMock.get(url(settings, '/splitChanges?since=1457552620999'), { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); + fetchMock.get(url(settings, '/splitChanges?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.get(url(settings, '/splitChanges?s=1.1&since=1457552620999'), { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); fetchMock.get(url(settings, '/mySegments/facundo%40split.io'), { status: 200, body: mySegmentsFacundo }); // Init and run Split client diff --git a/src/__tests__/browserSuites/use-beacon-api.spec.js b/src/__tests__/browserSuites/use-beacon-api.spec.js index 3c6ca3bc7..9065cfd61 100644 --- a/src/__tests__/browserSuites/use-beacon-api.spec.js +++ b/src/__tests__/browserSuites/use-beacon-api.spec.js @@ -78,8 +78,8 @@ function beaconApiNotSendTest(fetchMock, assert) { sendBeaconSpy = sinon.spy(window.navigator, 'sendBeacon'); // Mocking this specific route to make sure we only get the items we want to test from the handlers. - fetchMock.get(url(settings, '/splitChanges?since=-1'), { status: 200, body: splitChangesMock1 }); - fetchMock.get(url(settings, '/splitChanges?since=1457552620999'), { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); + fetchMock.get(url(settings, '/splitChanges?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.get(url(settings, '/splitChanges?s=1.1&since=1457552620999'), { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); fetchMock.get(url(settings, '/mySegments/facundo%40split.io'), { status: 200, body: mySegmentsFacundo }); // Init and run Split client diff --git a/src/__tests__/destroy/browser.spec.js b/src/__tests__/destroy/browser.spec.js index 26fd25c93..5cbf88fa5 100644 --- a/src/__tests__/destroy/browser.spec.js +++ b/src/__tests__/destroy/browser.spec.js @@ -18,8 +18,8 @@ const settings = settingsFactory({ streamingEnabled: false }); -fetchMock.getOnce(url(settings, '/splitChanges?since=-1'), { status: 200, body: splitChangesMock1 }); -fetchMock.getOnce(url(settings, '/splitChanges?since=1500492097547'), { status: 200, body: splitChangesMock2 }); +fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); +fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1500492097547'), { status: 200, body: splitChangesMock2 }); fetchMock.getOnce(url(settings, '/mySegments/ut1'), { status: 200, body: mySegmentsMock }); fetchMock.getOnce(url(settings, '/mySegments/ut2'), { status: 200, body: mySegmentsMock }); fetchMock.getOnce(url(settings, '/mySegments/ut3'), { status: 200, body: mySegmentsMock }); diff --git a/src/__tests__/destroy/node.spec.js b/src/__tests__/destroy/node.spec.js index 32b171f7b..7f9f9b541 100644 --- a/src/__tests__/destroy/node.spec.js +++ b/src/__tests__/destroy/node.spec.js @@ -17,8 +17,8 @@ import splitChangesMock1 from '../mocks/splitChanges.since.-1.till.1500492097547 import splitChangesMock2 from '../mocks/splitChanges.since.1500492097547.json'; import impressionsMock from '../mocks/impressions.json'; -fetchMock.get(url(settings, '/splitChanges?since=-1'), { status: 200, body: splitChangesMock1 }); -fetchMock.get(url(settings, '/splitChanges?since=1500492097547'), { status: 200, body: splitChangesMock2 }); +fetchMock.get(url(settings, '/splitChanges?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); +fetchMock.get(url(settings, '/splitChanges?s=1.1&since=1500492097547'), { status: 200, body: splitChangesMock2 }); fetchMock.postOnce(url(settings, '/v1/metrics/config'), 200); tape('SDK destroy for NodeJS', async function (assert) { diff --git a/src/__tests__/errorCatching/browser.spec.js b/src/__tests__/errorCatching/browser.spec.js index b006b6759..a35cdf5d8 100644 --- a/src/__tests__/errorCatching/browser.spec.js +++ b/src/__tests__/errorCatching/browser.spec.js @@ -23,11 +23,11 @@ localStorage.clear(); localStorage.setItem('SPLITIO.splits.till', 25); localStorage.setItem('SPLITIO.hash', getStorageHash({ core: { authorizationKey: '' }, sync: { __splitFiltersValidation: { queryString: null } } })); -fetchMock.get(url(settings, '/splitChanges?since=25'), function () { +fetchMock.get(url(settings, '/splitChanges?s=1.1&since=25'), function () { return new Promise((res) => { setTimeout(() => res({ status: 200, body: splitChangesMock1 }), 1000); }); }); -fetchMock.get(url(settings, '/splitChanges?since=1500492097547'), { status: 200, body: splitChangesMock2 }); -fetchMock.get(url(settings, '/splitChanges?since=1500492297547'), { status: 200, body: splitChangesMock3 }); +fetchMock.get(url(settings, '/splitChanges?s=1.1&since=1500492097547'), { status: 200, body: splitChangesMock2 }); +fetchMock.get(url(settings, '/splitChanges?s=1.1&since=1500492297547'), { status: 200, body: splitChangesMock3 }); fetchMock.get(url(settings, '/mySegments/nico%40split.io'), { status: 200, body: mySegmentsMock }); fetchMock.post('*', 200); diff --git a/src/__tests__/errorCatching/node.spec.js b/src/__tests__/errorCatching/node.spec.js index be626d307..b7838685a 100644 --- a/src/__tests__/errorCatching/node.spec.js +++ b/src/__tests__/errorCatching/node.spec.js @@ -21,9 +21,9 @@ const settings = settingsFactory({ streamingEnabled: false }); -fetchMock.get(url(settings, '/splitChanges?since=-1'), { status: 200, body: splitChangesMock1 }, responseDelay); -fetchMock.get(url(settings, '/splitChanges?since=1500492097547'), { status: 200, body: splitChangesMock2 }, responseDelay); -fetchMock.get(url(settings, '/splitChanges?since=1500492297547'), { status: 200, body: splitChangesMock3 }, responseDelay); +fetchMock.get(url(settings, '/splitChanges?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }, responseDelay); +fetchMock.get(url(settings, '/splitChanges?s=1.1&since=1500492097547'), { status: 200, body: splitChangesMock2 }, responseDelay); +fetchMock.get(url(settings, '/splitChanges?s=1.1&since=1500492297547'), { status: 200, body: splitChangesMock3 }, responseDelay); fetchMock.postOnce(url(settings, '/v1/metrics/config'), 200); // SDK_READY fetchMock.postOnce(url(settings, '/v1/metrics/usage'), 200); // SDK destroyed diff --git a/src/__tests__/gaIntegration/browser.spec.js b/src/__tests__/gaIntegration/browser.spec.js index 6d61aea16..c97a2922b 100644 --- a/src/__tests__/gaIntegration/browser.spec.js +++ b/src/__tests__/gaIntegration/browser.spec.js @@ -18,7 +18,7 @@ const settings = settingsFactory({ tape('## E2E CI Tests ##', function (assert) { - fetchMock.get(url(settings, '/splitChanges?since=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.get(url(settings, '/splitChanges?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); fetchMock.get(url(settings, '/mySegments/facundo%40split.io'), { status: 200, body: mySegmentsFacundo }); fetchMock.post(/\/v1\/metrics/, 200); // 0.1% sample rate diff --git a/src/__tests__/mocks/splitchanges.since.-1.semver.json b/src/__tests__/mocks/splitchanges.since.-1.semver.json new file mode 100644 index 000000000..ad3c486c5 --- /dev/null +++ b/src/__tests__/mocks/splitchanges.since.-1.semver.json @@ -0,0 +1,514 @@ +{ + "splits": [ + { + "trafficTypeName": "user", + "name": "semver_between", + "trafficAllocation": 100, + "trafficAllocationSeed": 1068038034, + "seed": -1053389887, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1675259356568, + "algo": 2, + "configurations": null, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "version" + }, + "matcherType": "BETWEEN_SEMVER", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "dependencyMatcherData": null, + "booleanMatcherData": null, + "stringMatcherData": null, + "betweenStringMatcherData": { + "start": "1.22.9", + "end": "2.1.0" + } + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + } + ], + "label": "between semver" + }, + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "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": 0 + }, + { + "treatment": "off", + "size": 100 + } + ], + "label": "default rule" + } + ] + }, + { + "trafficTypeName": "user", + "name": "semver_equalto", + "trafficAllocation": 100, + "trafficAllocationSeed": 1068038034, + "seed": -1053389887, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1675259356568, + "algo": 2, + "configurations": null, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "version" + }, + "matcherType": "EQUAL_TO_SEMVER", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "dependencyMatcherData": null, + "booleanMatcherData": null, + "stringMatcherData": "1.22.9" + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + } + ], + "label": "equal to semver" + }, + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "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": 0 + }, + { + "treatment": "off", + "size": 100 + } + ], + "label": "default rule" + } + ] + }, + { + "trafficTypeName": "user", + "name": "semver_greater_or_equalto", + "trafficAllocation": 100, + "trafficAllocationSeed": 1068038034, + "seed": -1053389887, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1675259356568, + "algo": 2, + "configurations": null, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "version" + }, + "matcherType": "GREATER_THAN_OR_EQUAL_TO_SEMVER", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "dependencyMatcherData": null, + "booleanMatcherData": null, + "stringMatcherData": "1.22.9" + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + } + ], + "label": "greater than or equal to semver" + }, + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "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": 0 + }, + { + "treatment": "off", + "size": 100 + } + ], + "label": "default rule" + } + ] + }, + { + "trafficTypeName": "user", + "name": "semver_inlist", + "trafficAllocation": 100, + "trafficAllocationSeed": 1068038034, + "seed": -1053389887, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1675259356568, + "algo": 2, + "configurations": null, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "version" + }, + "matcherType": "IN_LIST_SEMVER", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "1.22.9", + "2.1.0" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "dependencyMatcherData": null, + "booleanMatcherData": null, + "stringMatcherData": null, + "betweenStringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + } + ], + "label": "in list semver" + }, + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "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": 0 + }, + { + "treatment": "off", + "size": 100 + } + ], + "label": "default rule" + } + ] + }, + { + "trafficTypeName": "user", + "name": "semver_less_or_equalto", + "trafficAllocation": 100, + "trafficAllocationSeed": 1068038034, + "seed": -1053389887, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1675259356568, + "algo": 2, + "configurations": null, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "version" + }, + "matcherType": "LESS_THAN_OR_EQUAL_TO_SEMVER", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "dependencyMatcherData": null, + "booleanMatcherData": null, + "stringMatcherData": "1.22.9" + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + } + ], + "label": "less than or equal to semver" + }, + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "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": 0 + }, + { + "treatment": "off", + "size": 100 + } + ], + "label": "default rule" + } + ] + }, + { + "trafficTypeName": "user", + "name": "flag_with_unsupported_matcher", + "trafficAllocation": 100, + "trafficAllocationSeed": 1068038034, + "seed": -1053389887, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1675259356568, + "algo": 2, + "configurations": null, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "UNSUPPORTED_MATCHER", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "dependencyMatcherData": null, + "booleanMatcherData": null, + "stringMatcherData": "something" + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 + } + ], + "label": "in segment my_custom_segment" + }, + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "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 + } + ], + "label": "default rule" + } + ] + } + ], + "since": -1, + "till": 1675259356568 +} diff --git a/src/__tests__/nodeSuites/evaluations-semver.spec.js b/src/__tests__/nodeSuites/evaluations-semver.spec.js new file mode 100644 index 000000000..5885a2b16 --- /dev/null +++ b/src/__tests__/nodeSuites/evaluations-semver.spec.js @@ -0,0 +1,108 @@ +import sinon from 'sinon'; +import splitChangesMock1 from '../mocks/splitchanges.since.-1.semver.json'; + +import { SplitFactory } from '../../'; + +const listener = { + logImpression: sinon.stub() +}; + +const config = { + core: { + authorizationKey: '' + }, + urls: { + sdk: 'https://sdk.evaluation-semver/api', + events: 'https://events.evaluation-semver/api' + }, + sync: { + impressionsMode: 'DEBUG' + }, + impressionListener: listener, + streamingEnabled: false +}; + +export default async function (fetchMock, assert) { + + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=-1', { status: 200, body: splitChangesMock1 }); + fetchMock.get(config.urls.sdk + '/splitChanges?s=1.1&since=1675259356568', { status: 200, body: { splits: [], since: 1675259356568, till: 1675259356568 } }); + + const splitio = SplitFactory(config); + const client = splitio.client(); + + await client.ready(); + + // EQUAL_TO_SEMVER matcher + assert.equal(client.getTreatment('emi@split.io', 'semver_equalto', { 'version': '1.22.9' }), 'on', 'the rule will return `on` if attribute `version` is equal to `1.22.9`'); + assert.equal(client.getTreatment('emi@split.io', 'semver_equalto', { 'version': '1.22.9+build' }), 'off', 'build metadata is not ignored'); + assert.equal(client.getTreatment('emi@split.io', 'semver_equalto', { 'version': '1.22.9-rc.0' }), 'off', 'the rule will return `off` if attribute `version` is not equal to `1.22.9`'); + assert.equal(client.getTreatment('emi@split.io', 'semver_equalto', { 'version': null }), 'off', 'the rule will return `off` if attribute `version` is not the expected type'); + assert.equal(client.getTreatment('emi@split.io', 'semver_equalto'), 'off', 'the rule will return `off` if attribute `version` is not provided'); + + // IN_LIST_SEMVER matcher + assert.equal(client.getTreatment('emi@split.io', 'semver_inlist', { 'version': '2.1.0' }), 'on', 'the rule will return `on` if attribute `version` is in list (`1.22.9`, `2.1.0`)'); + assert.equal(client.getTreatment('emi@split.io', 'semver_inlist', { 'version': '1.22.9' }), 'on', 'the rule will return `on` if attribute `version` is in list (`1.22.9`, `2.1.0`)'); + assert.equal(client.getTreatment('emi@split.io', 'semver_inlist', { 'version': '1.22.9+build' }), 'off', 'build metadata is not ignored'); + assert.equal(client.getTreatment('emi@split.io', 'semver_inlist', { 'version': '1.22.9-rc.0' }), 'off', 'the rule will return `off` if attribute `version` is not in list (`1.22.9`, `2.1.0`)'); + assert.equal(client.getTreatment('emi@split.io', 'semver_inlist', { 'version': null }), 'off', 'the rule will return `off` if attribute `version` is not the expected type'); + + // GREATER_THAN_OR_EQUAL_TO_SEMVER matcher + assert.equal(client.getTreatments({ matchingKey: 'rulo@split.io', bucketingKey: 'some_bucket' }, ['semver_greater_or_equalto'], { 'version': '1.23.9' }).semver_greater_or_equalto, 'on', 'the rule will return `on` if attribute `version` is greater than or equal to `1.22.9`'); + assert.equal(client.getTreatments({ matchingKey: 'rulo@split.io', bucketingKey: 'some_bucket' }, ['semver_greater_or_equalto'], { 'version': '1.22.9' }).semver_greater_or_equalto, 'on', 'the rule will return `on` if attribute `version` is greater than or equal to `1.22.9`'); + assert.equal(client.getTreatments({ matchingKey: 'rulo@split.io', bucketingKey: 'some_bucket' }, ['semver_greater_or_equalto'], { 'version': '1.22.9+build' }).semver_greater_or_equalto, 'on', 'build metadata is ignored'); + assert.equal(client.getTreatments({ matchingKey: 'rulo@split.io', bucketingKey: 'some_bucket' }, ['semver_greater_or_equalto'], { 'version': '1.22.9-rc.0' }).semver_greater_or_equalto, 'off', 'the rule will return `off` if attribute `version` is not greater than or equal to `1.22.9`'); + assert.equal(client.getTreatments({ matchingKey: 'rulo@split.io', bucketingKey: 'some_bucket' }, ['semver_greater_or_equalto'], { 'version': '1.21.9' }).semver_greater_or_equalto, 'off', 'the rule will return `off` if attribute `version` is not greater than or equal to `1.22.9`'); + assert.equal(client.getTreatments({ matchingKey: 'rulo@split.io', bucketingKey: 'some_bucket' }, ['semver_greater_or_equalto'], { 'version': 'invalid' }).semver_greater_or_equalto, 'off', 'the rule will return `off` if attribute `version` is an invalid semver value'); + + // LESS_THAN_OR_EQUAL_TO_SEMVER matcher + assert.deepEqual(client.getTreatmentWithConfig('emi@split.io', 'semver_less_or_equalto', { 'version': '1.22.11' }), { treatment: 'off', config: null }, 'the rule will return `off` if attribute `version` is not less than or equal to `1.22.9`'); + assert.deepEqual(client.getTreatmentWithConfig('emi@split.io', 'semver_less_or_equalto', { 'version': '1.22.9' }), { treatment: 'on', config: null }, 'the rule will return `on` if attribute `version` is less than or equal to `1.22.9`'); + assert.deepEqual(client.getTreatmentWithConfig('emi@split.io', 'semver_less_or_equalto', { 'version': '1.22.9+build' }), { treatment: 'on', config: null }, 'build metadata is ignored'); + assert.deepEqual(client.getTreatmentWithConfig('emi@split.io', 'semver_less_or_equalto', { 'version': '1.22.9-rc.0' }), { treatment: 'on', config: null }, 'the rule will return `on` if attribute `version` is less than or equal to `1.22.9`'); + assert.deepEqual(client.getTreatmentWithConfig('emi@split.io', 'semver_less_or_equalto', { 'version': '1.21.9' }), { treatment: 'on', config: null }, 'the rule will return `on` if attribute `version` is less than or equal to `1.22.9`'); + assert.deepEqual(client.getTreatmentWithConfig('emi@split.io', 'semver_less_or_equalto', { 'version': {} }), { treatment: 'off', config: null }, 'the rule will return `off` if attribute `version` is not the expected type'); + + // BETWEEN_SEMVER matcher + assert.deepEqual(client.getTreatmentsWithConfig('emi@split.io', ['semver_between'], { 'version': '2.1.1' }).semver_between, { treatment: 'off', config: null }, 'the rule will return `off` if attribute `version` is not between `1.22.9` and `2.1.0`'); + assert.deepEqual(client.getTreatmentsWithConfig('emi@split.io', ['semver_between'], { 'version': '2.1.0+build' }).semver_between, { treatment: 'on', config: null }, 'build metadata is ignored'); + assert.deepEqual(client.getTreatmentsWithConfig('emi@split.io', ['semver_between'], { 'version': '1.25.0' }).semver_between, { treatment: 'on', config: null }, 'the rule will return `on` if attribute `version` is between `1.22.9` and `2.1.0`'); + assert.deepEqual(client.getTreatmentsWithConfig('emi@split.io', ['semver_between'], { 'version': '1.22.9' }).semver_between, { treatment: 'on', config: null }, 'the rule will return `on` if attribute `version` is between `1.22.9` and `2.1.0`'); + assert.deepEqual(client.getTreatmentsWithConfig('emi@split.io', ['semver_between'], { 'version': '1.22.9-rc.0' }).semver_between, { treatment: 'off', config: null }, 'the rule will return `off` if attribute `version` is not between `1.22.9` and `2.1.0`'); + assert.deepEqual(client.getTreatmentsWithConfig('emi@split.io', ['semver_between'], { 'version': [] }).semver_between, { treatment: 'off', config: null }, 'the rule will return `off` if attribute `version` is not the expected type'); + + // Evaluation of a flag with unsupported matcher + assert.equal(client.getTreatment('any-key', 'flag_with_unsupported_matcher'), 'control', 'evaluation of a flag with an unsupported matcher should return control'); + + let POSTED_IMPRESSIONS_COUNT; + + fetchMock.postOnce(config.urls.events + '/testImpressions/bulk', (_, opts) => { + + const payload = JSON.parse(opts.body); + + function validateImpressionData(featureFlagName, expectedImpressionCount, expectedOnCount, expectedLabel, expectedTreatment = 'on') { + const impressions = payload.find(e => e.f === featureFlagName).i; + + assert.equal(impressions.length, expectedImpressionCount, `We should have ${expectedImpressionCount} impressions for the feature flag ${featureFlagName}`); + assert.equal(impressions.filter((imp) => imp.r === expectedLabel && imp.t === expectedTreatment).length, expectedOnCount, `${expectedOnCount} impression with 'on' treatment and label ${expectedLabel}`); + } + + validateImpressionData('semver_equalto', 5, 1, 'equal to semver'); + validateImpressionData('semver_inlist', 5, 2, 'in list semver'); + validateImpressionData('semver_greater_or_equalto', 6, 3, 'greater than or equal to semver'); + validateImpressionData('semver_less_or_equalto', 6, 4, 'less than or equal to semver'); + validateImpressionData('semver_between', 6, 3, 'between semver'); + validateImpressionData('flag_with_unsupported_matcher', 1, 1, 'targeting rule type unsupported by sdk', 'control'); + + POSTED_IMPRESSIONS_COUNT = payload.reduce((acc, curr) => acc + curr.i.length, 0); + + return 200; + }); + + await client.destroy(); + + setTimeout(() => { + assert.equal(listener.logImpression.callCount, POSTED_IMPRESSIONS_COUNT, 'Impression listener should be called once per each impression generated.'); + + assert.end(); + }, 0); +} diff --git a/src/__tests__/nodeSuites/expected-treatments.spec.js b/src/__tests__/nodeSuites/expected-treatments.spec.js index 05b46ac87..552db56b3 100644 --- a/src/__tests__/nodeSuites/expected-treatments.spec.js +++ b/src/__tests__/nodeSuites/expected-treatments.spec.js @@ -6,7 +6,7 @@ import { url } from '../testUtils'; import splitChangesMockReal from '../mocks/splitchanges.real.json'; export default async function (config, settings, fetchMock, assert) { - fetchMock.get({ url: url(settings, '/splitChanges?since=-1'), overwriteRoutes: true }, { status: 200, body: splitChangesMockReal }); + fetchMock.get({ url: url(settings, '/splitChanges?s=1.1&since=-1'), overwriteRoutes: true }, { status: 200, body: splitChangesMockReal }); const splitio = SplitFactory({ ...config, diff --git a/src/__tests__/nodeSuites/fetch-specific-splits.spec.js b/src/__tests__/nodeSuites/fetch-specific-splits.spec.js index 01a4f2347..35d96d26d 100644 --- a/src/__tests__/nodeSuites/fetch-specific-splits.spec.js +++ b/src/__tests__/nodeSuites/fetch-specific-splits.spec.js @@ -23,9 +23,9 @@ export function fetchSpecificSplits(fetchMock, assert) { const queryString = queryStrings[i] || ''; let factory; - fetchMock.getOnce(urls.sdk + '/splitChanges?since=-1' + queryString, { status: 200, body: { splits: [], since: -1, till: 1457552620999 } }); - fetchMock.getOnce(urls.sdk + '/splitChanges?since=1457552620999' + queryString, { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); - fetchMock.getOnce(urls.sdk + '/splitChanges?since=1457552620999' + queryString, function () { + fetchMock.getOnce(urls.sdk + '/splitChanges?s=1.1&since=-1' + queryString, { status: 200, body: { splits: [], since: -1, till: 1457552620999 } }); + fetchMock.getOnce(urls.sdk + '/splitChanges?s=1.1&since=1457552620999' + queryString, { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); + fetchMock.getOnce(urls.sdk + '/splitChanges?s=1.1&since=1457552620999' + queryString, function () { factory.client().destroy().then(() => { assert.pass(`splitFilters #${i}`); }); @@ -64,8 +64,8 @@ export function fetchSpecificSplitsForFlagSets(fetchMock, assert) { let factory; const queryString = '&sets=4_valid,set_2,set_3,set_ww,set_x'; - 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 () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&since=-1' + queryString, { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 }}); + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&since=1457552620999' + queryString, async function () { t.pass('flag set query correctly formed'); factory.client().destroy().then(() => { t.end(); diff --git a/src/__tests__/nodeSuites/flag-sets.spec.js b/src/__tests__/nodeSuites/flag-sets.spec.js index c0c9d0b2e..e8dd725e0 100644 --- a/src/__tests__/nodeSuites/flag-sets.spec.js +++ b/src/__tests__/nodeSuites/flag-sets.spec.js @@ -26,12 +26,12 @@ export default function flagSets(fetchMock, t) { 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 () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&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 () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&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'); @@ -43,7 +43,7 @@ export default function flagSets(fetchMock, t) { }); // Receive split change with 1 split belonging to set_3 only - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1602797638344&sets=set_1,set_2', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&since=1602797638344&sets=set_1,set_2', function () { // stored feature flags before update const storedFlags = manager.splits(); assert.true(storedFlags.length === 1); @@ -54,7 +54,7 @@ export default function flagSets(fetchMock, t) { return { status: 200, body: splitChange0}; }); - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1602798638344&sets=set_1,set_2', async function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&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'); @@ -79,12 +79,12 @@ export default function flagSets(fetchMock, t) { 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 () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&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 () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&since=1602796638344', function () { // stored feature flags before update const storedFlags = manager.splits(); assert.true(storedFlags.length === 2, 'every feature flag should be added'); @@ -98,7 +98,7 @@ export default function flagSets(fetchMock, t) { }); // Receive split change with 1 split belonging to set_3 only - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1602797638344', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&since=1602797638344', function () { // stored feature flags before update const storedFlags = manager.splits(); assert.true(storedFlags.length === 2); @@ -111,7 +111,7 @@ export default function flagSets(fetchMock, t) { return { status: 200, body: splitChange0}; }); - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1602798638344', async function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&since=1602798638344', async function () { // stored feature flags before update const storedFlags = manager.splits(); assert.true(storedFlags.length === 2); @@ -142,11 +142,11 @@ export default function flagSets(fetchMock, t) { 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 () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&since=-1&sets=set_1', function () { return { status: 200, body: splitChange2}; }); - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1602796638344&sets=set_1', async function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&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'); @@ -180,11 +180,11 @@ export default function flagSets(fetchMock, t) { 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 () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&since=-1', function () { return { status: 200, body: splitChange2}; }); - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1602796638344', async function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&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'); diff --git a/src/__tests__/nodeSuites/impressions.debug.spec.js b/src/__tests__/nodeSuites/impressions.debug.spec.js index 0e166403e..9d09415c0 100644 --- a/src/__tests__/nodeSuites/impressions.debug.spec.js +++ b/src/__tests__/nodeSuites/impressions.debug.spec.js @@ -40,8 +40,8 @@ const config = { export default async function (key, fetchMock, assert) { // Mocking this specific route to make sure we only get the items we want to test from the handlers. - fetchMock.getOnce(url(settings, '/splitChanges?since=-1'), { status: 200, body: splitChangesMock1 }); - fetchMock.get(url(settings, '/splitChanges?since=1457552620999'), { status: 200, body: splitChangesMock2 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.get(url(settings, '/splitChanges?s=1.1&since=1457552620999'), { status: 200, body: splitChangesMock2 }); fetchMock.get(new RegExp(`${url(settings, '/segmentChanges/')}.*`), { status: 200, body: { since: 10, till: 10, name: 'segmentName', added: [], removed: [] } }); const splitio = SplitFactory(config); diff --git a/src/__tests__/nodeSuites/impressions.none.spec.js b/src/__tests__/nodeSuites/impressions.none.spec.js index 632fd77c9..4c726bca4 100644 --- a/src/__tests__/nodeSuites/impressions.none.spec.js +++ b/src/__tests__/nodeSuites/impressions.none.spec.js @@ -39,8 +39,8 @@ const config = { export default async function (key, fetchMock, assert) { // Mocking this specific route to make sure we only get the items we want to test from the handlers. - fetchMock.getOnce(url(settings, '/splitChanges?since=-1'), { status: 200, body: splitChangesMock1 }); - fetchMock.get(url(settings, '/splitChanges?since=1457552620999'), { status: 200, body: splitChangesMock2 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.get(url(settings, '/splitChanges?s=1.1&since=1457552620999'), { status: 200, body: splitChangesMock2 }); fetchMock.get(new RegExp(`${url(settings, '/segmentChanges/')}.*`), { status: 200, body: { since: 10, till: 10, name: 'segmentName', added: [], removed: [] } }); const splitio = SplitFactory(config); diff --git a/src/__tests__/nodeSuites/impressions.spec.js b/src/__tests__/nodeSuites/impressions.spec.js index 578e02a3e..b5fb102a7 100644 --- a/src/__tests__/nodeSuites/impressions.spec.js +++ b/src/__tests__/nodeSuites/impressions.spec.js @@ -37,8 +37,8 @@ let truncatedTimeFrame; export default async function (key, fetchMock, assert) { // Mocking this specific route to make sure we only get the items we want to test from the handlers. - fetchMock.getOnce(url(settings, '/splitChanges?since=-1'), { status: 200, body: splitChangesMock1 }); - fetchMock.get(url(settings, '/splitChanges?since=1457552620999'), { status: 200, body: splitChangesMock2 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.get(url(settings, '/splitChanges?s=1.1&since=1457552620999'), { status: 200, body: splitChangesMock2 }); fetchMock.get(new RegExp(`${url(settings, '/segmentChanges/')}.*`), { status: 200, body: { since: 10, till: 10, name: 'segmentName', added: [], removed: [] } }); const splitio = SplitFactory(config); diff --git a/src/__tests__/nodeSuites/ip-addresses-setting.debug.spec.js b/src/__tests__/nodeSuites/ip-addresses-setting.debug.spec.js index 61ae5f37c..0e3de4098 100644 --- a/src/__tests__/nodeSuites/ip-addresses-setting.debug.spec.js +++ b/src/__tests__/nodeSuites/ip-addresses-setting.debug.spec.js @@ -77,8 +77,8 @@ export default function ipAddressesSettingAssertions(fetchMock, assert) { })(); // Mock GET endpoints to run client normally - fetchMock.getOnce(url(settings, '/splitChanges?since=-1'), { status: 200, body: splitChangesMock1 }); - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); fetchMock.get(new RegExp(`${url(settings, '/segmentChanges/')}.*`), { status: 200, body: { since: 10, till: 10, name: 'segmentName', added: [], removed: [] } }); // Mock and assert POST endpoints diff --git a/src/__tests__/nodeSuites/ip-addresses-setting.spec.js b/src/__tests__/nodeSuites/ip-addresses-setting.spec.js index eb7a4292c..cde08e2ec 100644 --- a/src/__tests__/nodeSuites/ip-addresses-setting.spec.js +++ b/src/__tests__/nodeSuites/ip-addresses-setting.spec.js @@ -121,8 +121,8 @@ export default function ipAddressesSettingAssertions(fetchMock, assert) { })(); // Mock GET endpoints to run client normally - fetchMock.getOnce(url(settings, '/splitChanges?since=-1'), { status: 200, body: splitChangesMock1 }); - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); fetchMock.get(new RegExp(`${url(settings, '/segmentChanges/')}.*`), { status: 200, body: { since: 10, till: 10, name: 'segmentName', added: [], removed: [] } }); // Mock and assert POST endpoints diff --git a/src/__tests__/nodeSuites/manager.spec.js b/src/__tests__/nodeSuites/manager.spec.js index d74193c0b..00a707dc9 100644 --- a/src/__tests__/nodeSuites/manager.spec.js +++ b/src/__tests__/nodeSuites/manager.spec.js @@ -4,7 +4,7 @@ import map from 'lodash/map'; import { url } from '../testUtils'; export default async function (settings, fetchMock, assert) { - fetchMock.get({ url: url(settings, '/splitChanges?since=-1'), overwriteRoutes: true }, { status: 200, body: splitChangesMockReal }); + fetchMock.get({ url: url(settings, '/splitChanges?s=1.1&since=-1'), overwriteRoutes: true }, { status: 200, body: splitChangesMockReal }); const mockSplits = splitChangesMockReal; diff --git a/src/__tests__/nodeSuites/push-fallbacking.spec.js b/src/__tests__/nodeSuites/push-fallbacking.spec.js index 8285028ad..5f330634e 100644 --- a/src/__tests__/nodeSuites/push-fallbacking.spec.js +++ b/src/__tests__/nodeSuites/push-fallbacking.spec.js @@ -190,26 +190,26 @@ export function testFallbacking(fetchMock, assert) { }); - fetchMock.get({ url: url(settings, '/v2/auth'), repeat: 3 /* initial + 2 STREAMING_RESET */ }, function (url, opts) { + fetchMock.get({ url: url(settings, '/v2/auth?s=1.1'), repeat: 3 /* initial + 2 STREAMING_RESET */ }, function (url, opts) { if (!opts.headers['Authorization']) assert.fail('`/v2/auth` request must include `Authorization` header'); assert.pass('auth success'); return { status: 200, body: authPushEnabled }; }); // initial split and segment sync - fetchMock.getOnce(url(settings, '/splitChanges?since=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); fetchMock.getOnce(url(settings, '/segmentChanges/employees?since=-1'), { status: 200, body: { since: -1, till: 1457552620999, name: 'employees', added: [key], removed: [] } }); // extra retry due to double request (greedy fetch until since === till) fetchMock.getOnce(url(settings, '/segmentChanges/employees?since=1457552620999'), { status: 200, body: { since: 1457552620999, till: 1457552620999, name: 'employees', added: [], removed: [] } }); // split and segment sync after SSE opened - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); fetchMock.getOnce(url(settings, '/segmentChanges/employees?since=1457552620999'), { status: 200, body: { since: 1457552620999, till: 1457552620999, name: 'employees', added: [], removed: [] } }); // fetches due to first fallback to polling - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); fetchMock.getOnce(url(settings, '/segmentChanges/employees?since=1457552620999'), { status: 200, body: { since: 1457552620999, till: 1457552620999, name: 'employees', added: [], removed: [] } }); - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, MILLIS_STREAMING_DOWN_OCCUPANCY + settings.scheduler.featuresRefreshRate), 'fetch due to first fallback to polling'); return { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }; @@ -217,22 +217,22 @@ export function testFallbacking(fetchMock, assert) { fetchMock.getOnce(url(settings, '/segmentChanges/employees?since=1457552620999'), { status: 200, body: { since: 1457552620999, till: 1457552620999, name: 'employees', added: [], removed: [] } }); // split and segment sync due to streaming up (OCCUPANCY event) - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); fetchMock.getOnce(url(settings, '/segmentChanges/employees?since=1457552620999'), { status: 200, body: { since: 1457552620999, till: 1457552621999, name: 'employees', added: ['other_key'], removed: [] } }); // extra retry due to double request (greedy fetch until since === till) fetchMock.getOnce(url(settings, '/segmentChanges/employees?since=1457552621999'), { status: 200, body: { since: 1457552621999, till: 1457552621999, name: 'employees', added: [], removed: [] } }); // fetch due to SPLIT_UPDATE event - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, MILLIS_SPLIT_UPDATE_EVENT_DURING_PUSH), 'sync due to SPLIT_UPDATE event'); return { status: 200, body: splitChangesMock2 }; }); // fetches due to second fallback to polling - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552649999'), { status: 200, body: { splits: [], since: 1457552649999, till: 1457552649999 } }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552649999'), { status: 200, body: { splits: [], since: 1457552649999, till: 1457552649999 } }); fetchMock.getOnce(url(settings, '/segmentChanges/employees?since=1457552621999'), { status: 200, body: { since: 1457552621999, till: 1457552621999, name: 'employees', added: [], removed: [] } }); - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552649999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552649999'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, MILLIS_STREAMING_PAUSED_CONTROL + settings.scheduler.featuresRefreshRate), 'fetch due to second fallback to polling'); return { status: 200, body: { splits: [], since: 1457552649999, till: 1457552649999 } }; @@ -240,7 +240,7 @@ export function testFallbacking(fetchMock, assert) { fetchMock.getOnce(url(settings, '/segmentChanges/employees?since=1457552621999'), { status: 200, body: { since: 1457552621999, till: 1457552621999, name: 'employees', added: [], removed: [] } }); // split and segment sync due to streaming up (CONTROL event) - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552649999'), { status: 200, body: { splits: [], since: 1457552649999, till: 1457552649999 } }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552649999'), { status: 200, body: { splits: [], since: 1457552649999, till: 1457552649999 } }); fetchMock.getOnce(url(settings, '/segmentChanges/employees?since=1457552621999'), { status: 200, body: { since: 1457552621999, till: 1457552621999, name: 'employees', added: [], removed: [] } }); // fetch due to SEGMENT_UPDATE event @@ -249,19 +249,19 @@ export function testFallbacking(fetchMock, assert) { fetchMock.getOnce(url(settings, '/segmentChanges/employees?since=1457552650000'), { status: 200, body: { since: 1457552650000, till: 1457552650000, name: 'employees', added: [], removed: [] } }); // Fetches due to third fallback to polling (second STREAMING_PAUSED event) and two syncAll ( SSE opened twice due to two STREAMING_RESET events) - fetchMock.get({ url: url(settings, '/splitChanges?since=1457552649999'), repeat: 3 }, { status: 200, body: { splits: [], since: 1457552649999, till: 1457552649999 } }); + fetchMock.get({ url: url(settings, '/splitChanges?s=1.1&since=1457552649999'), repeat: 3 }, { status: 200, body: { splits: [], since: 1457552649999, till: 1457552649999 } }); fetchMock.get({ url: url(settings, '/segmentChanges/employees?since=1457552650000'), repeat: 3 }, { status: 200, body: { since: 1457552650000, till: 1457552650000, name: 'employees', added: [], removed: [] } }); // fetches due to fourth fallback to polling due to STREAMING_DISABLED event - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552649999'), { status: 200, body: { splits: [], since: 1457552649999, till: 1457552649999 } }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552649999'), { status: 200, body: { splits: [], since: 1457552649999, till: 1457552649999 } }); fetchMock.getOnce(url(settings, '/segmentChanges/employees?since=1457552650000'), { status: 200, body: { since: 1457552650000, till: 1457552650000, name: 'employees', added: [], removed: [] } }); - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552649999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552649999'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, MILLIS_STREAMING_DISABLED_CONTROL + settings.scheduler.featuresRefreshRate), 'fetch due to fourth fallback to polling'); return { status: 200, body: splitChangesMock3 }; }); fetchMock.getOnce(url(settings, '/segmentChanges/employees?since=1457552650000'), { status: 200, body: { since: 1457552650000, till: 1457552650000, name: 'employees', added: [], removed: [] } }); - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552669999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552669999'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, MILLIS_STREAMING_DISABLED_CONTROL + settings.scheduler.featuresRefreshRate * 2), 'fetch due to third fallback to polling'); return { status: 200, body: { splits: [], since: 1457552669999, till: 1457552669999 } }; diff --git a/src/__tests__/nodeSuites/push-flag-sets.spec.js b/src/__tests__/nodeSuites/push-flag-sets.spec.js index bcd5b92d2..d8beb05ac 100644 --- a/src/__tests__/nodeSuites/push-flag-sets.spec.js +++ b/src/__tests__/nodeSuites/push-flag-sets.spec.js @@ -39,21 +39,21 @@ export function testFlagSets(fetchMock, t) { mockSegmentChanges(fetchMock, new RegExp(baseUrls.sdk + '/segmentChanges/*'), ['some-key']); fetchMock.post('*', 200); - fetchMock.get(baseUrls.auth + '/v2/auth', function (url, opts) { + fetchMock.get(baseUrls.auth + '/v2/auth?s=1.1', 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 () { + fetchMock.get(baseUrls.sdk + '/splitChanges?s=1.1&since=-1', function () { return { status: 200, body: { splits: [], since: -1, till: 0}}; }); - fetchMock.get(baseUrls.sdk + '/splitChanges?since=0', function () { + fetchMock.get(baseUrls.sdk + '/splitChanges?s=1.1&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 () { + fetchMock.get(baseUrls.sdk + '/splitChanges?s=1.1&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 () { + fetchMock.get(baseUrls.sdk + '/splitChanges?s=1.1&since=0&sets=set_1,set_2', function () { return { status: 200, body: { splits: [], since: 0, till: 1 }}; }); @@ -194,7 +194,7 @@ export function testFlagSets(fetchMock, t) { setMockListener((eventSourceInstance) => { - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=2&sets=set_1,set_2', async function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&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.end(); @@ -232,7 +232,7 @@ export function testFlagSets(fetchMock, t) { t.test(async (assert) => { - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1&sets=set_1,set_2', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&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 }}; }); diff --git a/src/__tests__/nodeSuites/push-initialization-nopush.spec.js b/src/__tests__/nodeSuites/push-initialization-nopush.spec.js index b133729c8..6fc773caa 100644 --- a/src/__tests__/nodeSuites/push-initialization-nopush.spec.js +++ b/src/__tests__/nodeSuites/push-initialization-nopush.spec.js @@ -42,7 +42,7 @@ function testInitializationFail(fetchMock, assert, fallbackToPolling) { fetchMock.get(new RegExp(`${url(settings, '/segmentChanges/')}.*`), { status: 200, body: { since: 10, till: 10, name: 'segmentName', added: [], removed: [] } }); - fetchMock.getOnce(url(settings, '/splitChanges?since=-1'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), function () { const lapse = Date.now() - start; // using a higher error margin for Travis, due to a lower performance than local execution assert.true(nearlyEqual(lapse, 0), 'initial sync'); @@ -50,7 +50,7 @@ function testInitializationFail(fetchMock, assert, fallbackToPolling) { }); if (fallbackToPolling) { - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function () { assert.true(ready, 'client ready'); const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, 0), 'polling (first fetch)'); @@ -58,7 +58,7 @@ function testInitializationFail(fetchMock, assert, fallbackToPolling) { }); } - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function () { assert.true(ready, 'client ready'); const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, settings.scheduler.featuresRefreshRate), 'polling (second fetch)'); @@ -80,7 +80,7 @@ function testInitializationFail(fetchMock, assert, fallbackToPolling) { export function testAuthWithPushDisabled(fetchMock, assert) { assert.plan(6); - fetchMock.getOnce('https://auth.push-initialization-nopush/api/v2/auth', function (url, opts) { + fetchMock.getOnce('https://auth.push-initialization-nopush/api/v2/auth?s=1.1', function (url, opts) { if (!opts.headers['Authorization']) assert.fail('`/v2/auth` request must include `Authorization` header'); assert.pass('auth'); return { status: 200, body: authPushDisabled }; @@ -93,7 +93,7 @@ export function testAuthWithPushDisabled(fetchMock, assert) { export function testAuthWith401(fetchMock, assert) { assert.plan(6); - fetchMock.getOnce(url(settings, '/v2/auth'), function (url, opts) { + fetchMock.getOnce(url(settings, '/v2/auth?s=1.1'), function (url, opts) { if (!opts.headers['Authorization']) assert.fail('`/v2/auth` request must include `Authorization` header'); assert.pass('auth'); return { status: 401, body: authInvalidCredentials }; @@ -106,7 +106,7 @@ export function testAuthWith401(fetchMock, assert) { export function testAuthWith400(fetchMock, assert) { assert.plan(6); - fetchMock.getOnce(url(settings, '/v2/auth'), function (url, opts) { + fetchMock.getOnce(url(settings, '/v2/auth?s=1.1'), function (url, opts) { if (!opts.headers['Authorization']) assert.fail('`/v2/auth` request must include `Authorization` header'); assert.pass('auth'); return { status: 400, body: authNoUserSpecified }; @@ -131,7 +131,7 @@ export function testSSEWithNonRetryableError(fetchMock, assert) { assert.plan(7); // Auth successes - fetchMock.getOnce(url(settings, '/v2/auth'), function (url, opts) { + fetchMock.getOnce(url(settings, '/v2/auth?s=1.1'), function (url, opts) { if (!opts.headers['Authorization']) assert.fail('`/v2/auth` request must include `Authorization` header'); assert.pass('auth successes'); return { status: 200, body: authPushEnabled }; diff --git a/src/__tests__/nodeSuites/push-initialization-retries.spec.js b/src/__tests__/nodeSuites/push-initialization-retries.spec.js index 51b7b0ece..54c681f18 100644 --- a/src/__tests__/nodeSuites/push-initialization-retries.spec.js +++ b/src/__tests__/nodeSuites/push-initialization-retries.spec.js @@ -45,38 +45,38 @@ export function testPushRetriesDueToAuthErrors(fetchMock, assert) { let start, splitio, client, ready = false; - fetchMock.getOnce(url(settings, '/v2/auth'), function (url, opts) { + fetchMock.getOnce(url(settings, '/v2/auth?s=1.1'), function (url, opts) { if (!opts.headers['Authorization']) assert.fail('`/v2/auth` request must include `Authorization` header'); assert.pass('first auth attempt'); return { status: 200, body: authPushBadToken }; }); - fetchMock.getOnce(url(settings, '/v2/auth'), { throws: new TypeError('Network error') }); - fetchMock.getOnce(url(settings, '/v2/auth'), function (url, opts) { + fetchMock.getOnce(url(settings, '/v2/auth?s=1.1'), { throws: new TypeError('Network error') }); + fetchMock.getOnce(url(settings, '/v2/auth?s=1.1'), function (url, opts) { if (!opts.headers['Authorization']) assert.fail('`/v2/auth` request must include `Authorization` header'); const lapse = Date.now() - start; const expected = (settings.scheduler.pushRetryBackoffBase * Math.pow(2, 0) + settings.scheduler.pushRetryBackoffBase * Math.pow(2, 1)); - assert.true(nearlyEqual(lapse, expected), 'third auth attempt (aproximately in 0.3 seconds from first attempt)'); + assert.true(nearlyEqual(lapse, expected), 'third auth attempt (approximately in 0.3 seconds from first attempt)'); return { status: 200, body: authPushDisabled }; }); fetchMock.get(new RegExp(`${url(settings, '/segmentChanges/')}.*`), { status: 200, body: { since: 10, till: 10, name: 'segmentName', added: [], removed: [] } }); - fetchMock.getOnce(url(settings, '/splitChanges?since=-1'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, 0), 'initial sync'); return { status: 200, body: splitChangesMock1 }; }); - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function () { assert.true(ready, 'client ready before first polling fetch'); const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, 0), 'fallback to polling'); return { status: 200, body: splitChangesMock2 }; }); - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, settings.scheduler.featuresRefreshRate), 'polling'); return { status: 200, body: splitChangesMock2 }; }); - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, settings.scheduler.featuresRefreshRate * 2), 'keep polling since auth success buth with push disabled'); client.destroy().then(() => { @@ -116,36 +116,36 @@ export function testPushRetriesDueToSseErrors(fetchMock, assert) { } else { const lapse = Date.now() - start; - assert.true(nearlyEqual(lapse, expectedTimeToSSEsuccess), 'third auth attempt (aproximately in 0.3 seconds from first attempt)'); + assert.true(nearlyEqual(lapse, expectedTimeToSSEsuccess), 'third auth attempt (approximately in 0.3 seconds from first attempt)'); eventSourceInstance.emitOpen(); } sseattempts++; }); - fetchMock.get({ url: url(settings, '/v2/auth'), repeat: 3 /* 3 push attempts */ }, function (url, opts) { + fetchMock.get({ url: url(settings, '/v2/auth?s=1.1'), repeat: 3 /* 3 push attempts */ }, function (url, opts) { if (!opts.headers['Authorization']) assert.fail('`/v2/auth` request must include `Authorization` header'); assert.pass('auth success'); return { status: 200, body: authPushEnabled }; }); fetchMock.get(new RegExp(`${url(settings, '/segmentChanges/')}.*`), { status: 200, body: { since: 10, till: 10, name: 'segmentName', added: [], removed: [] } }); - fetchMock.getOnce(url(settings, '/splitChanges?since=-1'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, 0), 'initial sync'); return { status: 200, body: splitChangesMock1 }; }); - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function () { assert.true(ready, 'client ready before first polling fetch'); const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, 0), 'fallback to polling'); return { status: 200, body: splitChangesMock2 }; }); - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, settings.scheduler.featuresRefreshRate), 'polling'); return { status: 200, body: splitChangesMock2 }; }); - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, expectedTimeToSSEsuccess), 'sync due to success SSE connection'); client.destroy().then(() => { @@ -181,10 +181,10 @@ export function testSdkDestroyWhileAuthSuccess(fetchMock, assert) { let splitio, client, ready = false; - fetchMock.getOnce(url(settings, '/v2/auth'), { status: 200, body: authPushEnabled }, { delay: 100 }); + fetchMock.getOnce(url(settings, '/v2/auth?s=1.1'), { status: 200, body: authPushEnabled }, { delay: 100 }); fetchMock.get(new RegExp(`${url(settings, '/segmentChanges/')}.*`), { status: 200, body: { since: 10, till: 10, name: 'segmentName', added: [], removed: [] } }); - fetchMock.getOnce(url(settings, '/splitChanges?since=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); setTimeout(() => { client.destroy().then(() => { @@ -219,12 +219,12 @@ export function testSdkDestroyWhileAuthRetries(fetchMock, assert) { let splitio, client, ready = false; - fetchMock.getOnce(url(settings, '/v2/auth'), { status: 200, body: authPushBadToken }); - fetchMock.getOnce(url(settings, '/v2/auth'), { throws: new TypeError('Network error') }, { delay: 100 }); + fetchMock.getOnce(url(settings, '/v2/auth?s=1.1'), { status: 200, body: authPushBadToken }); + fetchMock.getOnce(url(settings, '/v2/auth?s=1.1'), { throws: new TypeError('Network error') }, { delay: 100 }); fetchMock.get(new RegExp(`${url(settings, '/segmentChanges/')}.*`), { status: 200, body: { since: 10, till: 10, name: 'segmentName', added: [], removed: [] } }); - fetchMock.getOnce(url(settings, '/splitChanges?since=-1'), { status: 200, body: splitChangesMock1 }); - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), { status: 200, body: splitChangesMock2 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), { status: 200, body: splitChangesMock2 }); fetchMock.get(new RegExp('.*'), function (url) { assert.fail('unexpected GET request with url: ' + url); diff --git a/src/__tests__/nodeSuites/push-refresh-token.spec.js b/src/__tests__/nodeSuites/push-refresh-token.spec.js index 644d2c07d..0cb2c5424 100644 --- a/src/__tests__/nodeSuites/push-refresh-token.spec.js +++ b/src/__tests__/nodeSuites/push-refresh-token.spec.js @@ -77,20 +77,20 @@ export function testRefreshToken(fetchMock, assert) { }); // initial split sync - fetchMock.getOnce(url(settings, '/splitChanges?since=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); // first auth - fetchMock.getOnce(url(settings, '/v2/auth'), function (url, opts) { + fetchMock.getOnce(url(settings, '/v2/auth?s=1.1'), function (url, opts) { if (!opts.headers['Authorization']) assert.fail('`/v2/auth` request must include `Authorization` header'); assert.pass('auth success'); return { status: 200, body: authPushEnabled }; }); // split sync after SSE opened - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), { status: 200, body: splitChangesMock2 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), { status: 200, body: splitChangesMock2 }); // re-auth due to refresh token, with connDelay of 0.5 seconds - fetchMock.getOnce(url(settings, '/v2/auth'), function (url, opts) { + fetchMock.getOnce(url(settings, '/v2/auth?s=1.1'), function (url, opts) { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, MILLIS_REFRESH_TOKEN), 'reauthentication for token refresh'); if (!opts.headers['Authorization']) assert.fail('`/v2/auth` request must include `Authorization` header'); @@ -98,14 +98,14 @@ export function testRefreshToken(fetchMock, assert) { }); // split sync after SSE reopened - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, MILLIS_REFRESH_TOKEN + MILLIS_CONNDELAY), 'sync after SSE connection is reopened'); return { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }; }); // second re-auth due to refresh token, this time responding with pushEnabled false - fetchMock.getOnce(url(settings, '/v2/auth'), function (url, opts) { + fetchMock.getOnce(url(settings, '/v2/auth?s=1.1'), function (url, opts) { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, MILLIS_REFRESH_TOKEN * 2), 'second reauthentication for token refresh'); if (!opts.headers['Authorization']) assert.fail('`/v2/auth` request must include `Authorization` header'); @@ -113,7 +113,7 @@ export function testRefreshToken(fetchMock, assert) { }); // split sync after SSE closed due to push disabled - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, MILLIS_REFRESH_TOKEN * 2), 'sync after SSE connection is reopened a second time'); setTimeout(() => { diff --git a/src/__tests__/nodeSuites/push-synchronization-retries.spec.js b/src/__tests__/nodeSuites/push-synchronization-retries.spec.js index 7eb6ff079..e1154e9d6 100644 --- a/src/__tests__/nodeSuites/push-synchronization-retries.spec.js +++ b/src/__tests__/nodeSuites/push-synchronization-retries.spec.js @@ -126,14 +126,14 @@ export function testSynchronizationRetries(fetchMock, assert) { }); // initial auth - fetchMock.getOnce(url(settings, '/v2/auth'), function (url, opts) { + fetchMock.getOnce(url(settings, '/v2/auth?s=1.1'), function (url, opts) { if (!opts.headers['Authorization']) assert.fail('`/v2/auth` request must include `Authorization` header'); assert.pass('auth success'); return { status: 200, body: authPushEnabled }; }); // initial split and segment sync - fetchMock.getOnce(url(settings, '/splitChanges?since=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); fetchMock.getOnce(url(settings, '/segmentChanges/splitters?since=-1'), { status: 200, body: { since: -1, till: 1457552620999, name: 'splitters', added: [key], removed: [] } } ); @@ -143,7 +143,7 @@ export function testSynchronizationRetries(fetchMock, assert) { ); // split and segment sync after SSE opened - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, MILLIS_SSE_OPEN), 'sync after SSE connection is opened'); return { status: 200, body: splitChangesMock2 }; @@ -153,9 +153,9 @@ export function testSynchronizationRetries(fetchMock, assert) { ); // fetch due to SPLIT_UPDATE event - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), { throws: new TypeError('Network error') }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), { throws: new TypeError('Network error') }); // fetch retry for SPLIT_UPDATE event, due to previous fail - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, MILLIS_RETRY_FOR_FIRST_SPLIT_UPDATE_EVENT), 'fetch retry due to SPLIT_UPDATE event'); return { status: 200, body: splitChangesMock3 }; @@ -182,18 +182,18 @@ export function testSynchronizationRetries(fetchMock, assert) { fetchMock.getOnce(url(settings, '/segmentChanges/splitters?since=1457552640000'), { status: 200, body: { since: 1457552640000, till: 1457552640000, name: 'splitters', added: [], removed: [] } }); // fetch due to SPLIT_KILL event - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552649999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552649999'), function () { assert.equal(client.getTreatment(key, 'whitelist'), 'not_allowed', 'evaluation with split killed immediately, before fetch is done'); const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, MILLIS_SPLIT_KILL_EVENT), 'sync due to SPLIT_KILL event'); return { status: 200, body: { since: 1457552649999, till: 1457552649999, splits: [] } }; // returning old state }); // first fetch retry for SPLIT_KILL event, due to previous unexpected response (response till minor than SPLIT_KILL changeNumber) - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552649999'), { status: 200, body: '{ "since": 1457552620999, "til' }); // invalid JSON + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552649999'), { status: 200, body: '{ "since": 1457552620999, "til' }); // invalid JSON // second fetch retry for SPLIT_KILL event - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552649999'), { throws: new TypeError('Network error') }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552649999'), { throws: new TypeError('Network error') }); // third fetch retry for SPLIT_KILL event - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552649999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552649999'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, MILLIS_THIRD_RETRY_FOR_SPLIT_KILL_EVENT), 'third fetch retry due to SPLIT_KILL event'); diff --git a/src/__tests__/nodeSuites/push-synchronization.spec.js b/src/__tests__/nodeSuites/push-synchronization.spec.js index d078b6cc9..e45f3370a 100644 --- a/src/__tests__/nodeSuites/push-synchronization.spec.js +++ b/src/__tests__/nodeSuites/push-synchronization.spec.js @@ -218,14 +218,14 @@ export function testSynchronization(fetchMock, assert) { }); // initial auth - fetchMock.getOnce(url(settings, '/v2/auth'), function (url, opts) { + fetchMock.getOnce(url(settings, '/v2/auth?s=1.1'), function (url, opts) { if (!opts.headers['Authorization']) assert.fail('`/v2/auth` request must include `Authorization` header'); assert.pass('auth success'); return { status: 200, body: authPushEnabled }; }); // initial split and segment sync - fetchMock.getOnce(url(settings, '/splitChanges?since=-1'), function (url, opts) { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), function (url, opts) { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, 0), 'initial sync'); if (hasNoCacheHeader(opts)) assert.fail('request must not include `Cache-Control` header'); @@ -242,7 +242,7 @@ export function testSynchronization(fetchMock, assert) { }); // split and segment sync after SSE opened - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), function (url, opts) { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function (url, opts) { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, MILLIS_SSE_OPEN), 'sync after SSE connection is opened'); if (hasNoCacheHeader(opts)) assert.fail('request must not include `Cache-Control` header'); @@ -254,7 +254,7 @@ export function testSynchronization(fetchMock, assert) { }); // fetch due to SPLIT_UPDATE event - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552620999'), function (url, opts) { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function (url, opts) { if (!hasNoCacheHeader(opts)) assert.fail('request must include `Cache-Control` header'); return { status: 200, body: splitChangesMock3 }; }); @@ -271,14 +271,14 @@ export function testSynchronization(fetchMock, assert) { }); // fetch due to SPLIT_KILL event - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552649999'), function (url, opts) { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552649999'), function (url, opts) { if (!hasNoCacheHeader(opts)) assert.fail('request must include `Cache-Control` header'); assert.equal(client.getTreatment(key, 'whitelist'), 'not_allowed', 'evaluation with split killed immediately, before fetch is done'); return { status: 200, body: splitChangesMock4 }; }); // fetch due to SPLIT_UPDATE event, with an update that involves a new segment - fetchMock.getOnce(url(settings, '/splitChanges?since=1457552650000'), function (url, opts) { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552650000'), function (url, opts) { if (!hasNoCacheHeader(opts)) assert.fail('request must include `Cache-Control` header'); return { status: 200, body: splitChangesMock5 }; }); @@ -289,25 +289,25 @@ export function testSynchronization(fetchMock, assert) { }); // fetch feature flags due to IFFU SPLIT_UPDATE event with wrong compress code - fetchMock.getOnce(url(settings, '/splitChanges?since=1684265694505'), function (url, opts) { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1684265694505'), function (url, opts) { if (!hasNoCacheHeader(opts)) assert.fail('request must include `Cache-Control` header'); return { status: 200, body: splitChangesMock6 }; }); // fetch feature flags due to IFFU SPLIT_UPDATE event with previous change number = 0 - fetchMock.getOnce(url(settings, '/splitChanges?since=1684265694506'), function (url, opts) { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1684265694506'), function (url, opts) { if (!hasNoCacheHeader(opts)) assert.fail('request must include `Cache-Control` header'); return { status: 200, body: splitChangesMock7 }; }); // fetch feature flags due to IFFU SPLIT_UPDATE event with previous change number !== current change number - fetchMock.getOnce(url(settings, '/splitChanges?since=1684265694526'), function (url, opts) { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1684265694526'), function (url, opts) { if (!hasNoCacheHeader(opts)) assert.fail('request must include `Cache-Control` header'); return { status: 200, body: splitChangesMock8 }; }); // fetch feature flags due to IFFU SPLIT_UPDATE event with ARCHIVED feature flag - fetchMock.getOnce(url(settings, '/splitChanges?since=1684265694546'), function (url, opts) { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1684265694546'), function (url, opts) { if (!hasNoCacheHeader(opts)) assert.fail('request must include `Cache-Control` header'); return { status: 200, body: splitChangesMock9 }; }); diff --git a/src/__tests__/nodeSuites/readiness.spec.js b/src/__tests__/nodeSuites/readiness.spec.js index 199f29082..519571ea3 100644 --- a/src/__tests__/nodeSuites/readiness.spec.js +++ b/src/__tests__/nodeSuites/readiness.spec.js @@ -23,8 +23,8 @@ export default function (fetchMock, assert) { 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.getOnce(testUrls.sdk + '/splitChanges?s=1.1&since=-1', { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.1&since=1457552620999', { status: 200, body: splitChangesMock2 }); fetchMock.get(new RegExp(testUrls.sdk + '/segmentChanges/*'), 403); fetchMock.postOnce(testUrls.events + '/events/bulk', 200); diff --git a/src/__tests__/nodeSuites/ready-promise.spec.js b/src/__tests__/nodeSuites/ready-promise.spec.js index 9a0edff4c..e5811b302 100644 --- a/src/__tests__/nodeSuites/ready-promise.spec.js +++ b/src/__tests__/nodeSuites/ready-promise.spec.js @@ -56,8 +56,8 @@ export default function readyPromiseAssertions(key, fetchMock, assert) { }; // /splitChanges takes longer than 'requestTimeoutBeforeReady' in both attempts - fetchMock.getOnce(config.urls.sdk + '/splitChanges?since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); - fetchMock.getOnce(config.urls.sdk + '/splitChanges?since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); fetchMock.postOnce(config.urls.events + '/testImpressions/bulk', 200); fetchMock.postOnce(config.urls.events + '/testImpressions/count', 200); @@ -103,8 +103,8 @@ export default function readyPromiseAssertions(key, fetchMock, assert) { }; // /splitChanges takes longer than 'requestTimeoutBeforeReady' only for the first attempt - fetchMock.getOnce(config.urls.sdk + '/splitChanges?since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); - fetchMock.getOnce(config.urls.sdk + '/splitChanges?since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); mockSegmentChanges(fetchMock, new RegExp(config.urls.sdk + '/segmentChanges/*'), ['some_key']); fetchMock.postOnce(config.urls.events + '/testImpressions/bulk', 200); fetchMock.postOnce(config.urls.events + '/testImpressions/count', 200); @@ -153,8 +153,8 @@ export default function readyPromiseAssertions(key, fetchMock, assert) { }; // /splitChanges takes longer than 'requestTimeoutBeforeReady' only for the first attempt - fetchMock.getOnce(config.urls.sdk + '/splitChanges?since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); - fetchMock.getOnce(config.urls.sdk + '/splitChanges?since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); mockSegmentChanges(fetchMock, new RegExp(config.urls.sdk + '/segmentChanges/*'), ['some_key']); fetchMock.postOnce(config.urls.events + '/testImpressions/bulk', 200); fetchMock.postOnce(config.urls.events + '/testImpressions/count', 200); @@ -222,9 +222,9 @@ export default function readyPromiseAssertions(key, fetchMock, assert) { config.scheduler.featuresRefreshRate) - config.startup.readyTimeout) + refreshTimeMillis; // /splitChanges takes longer than 'requestTimeoutBeforeReady' in both initial attempts - fetchMock.getOnce(config.urls.sdk + '/splitChanges?since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); - fetchMock.getOnce(config.urls.sdk + '/splitChanges?since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); - fetchMock.getOnce(config.urls.sdk + '/splitChanges?since=-1', splitChangesMock1, { delay: refreshTimeMillis }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: refreshTimeMillis }); mockSegmentChanges(fetchMock, new RegExp(config.urls.sdk + '/segmentChanges/*'), ['some_key']); fetchMock.postOnce(config.urls.events + '/testImpressions/bulk', 200); fetchMock.postOnce(config.urls.events + '/testImpressions/count', 200); @@ -281,7 +281,7 @@ export default function readyPromiseAssertions(key, fetchMock, assert) { }; // /splitChanges takes longer than 'requestTimeoutBeforeReady' - fetchMock.get(config.urls.sdk + '/splitChanges?since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.get(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); mockSegmentChanges(fetchMock, new RegExp(config.urls.sdk + '/segmentChanges/*'), ['some_key']); fetchMock.postOnce(config.urls.events + '/testImpressions/bulk', 200); fetchMock.postOnce(config.urls.events + '/testImpressions/count', 200); @@ -331,7 +331,7 @@ export default function readyPromiseAssertions(key, fetchMock, assert) { }; // /splitChanges takes less than 'requestTimeoutBeforeReady' - fetchMock.get(config.urls.sdk + '/splitChanges?since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); + fetchMock.get(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); mockSegmentChanges(fetchMock, new RegExp(config.urls.sdk + '/segmentChanges/*'), ['some_key']); fetchMock.postOnce(config.urls.events + '/testImpressions/bulk', 200); fetchMock.postOnce(config.urls.events + '/testImpressions/count', 200); @@ -378,8 +378,8 @@ export default function readyPromiseAssertions(key, fetchMock, assert) { }; // /splitChanges takes longer than 'requestTimeoutBeforeReady' only for the first attempt - fetchMock.getOnce(config.urls.sdk + '/splitChanges?since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); - fetchMock.getOnce(config.urls.sdk + '/splitChanges?since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); mockSegmentChanges(fetchMock, new RegExp(config.urls.sdk + '/segmentChanges/*'), ['some_key']); fetchMock.postOnce(config.urls.events + '/testImpressions/bulk', 200); fetchMock.postOnce(config.urls.events + '/testImpressions/count', 200); @@ -467,8 +467,8 @@ export default function readyPromiseAssertions(key, fetchMock, assert) { }; // /splitChanges takes longer than 'requestTimeoutBeforeReady' only for the first attempt - fetchMock.getOnce(config.urls.sdk + '/splitChanges?since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); - fetchMock.getOnce(config.urls.sdk + '/splitChanges?since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); mockSegmentChanges(fetchMock, new RegExp(config.urls.sdk + '/segmentChanges/*'), ['some_key']); fetchMock.postOnce(config.urls.events + '/testImpressions/bulk', 200); fetchMock.postOnce(config.urls.events + '/testImpressions/count', 200); @@ -538,7 +538,7 @@ export default function readyPromiseAssertions(key, fetchMock, assert) { }; // /splitChanges takes longer than 'requestTimeoutBeforeReady' - fetchMock.get(config.urls.sdk + '/splitChanges?since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.get(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); mockSegmentChanges(fetchMock, new RegExp(config.urls.sdk + '/segmentChanges/*'), ['some_key']); fetchMock.postOnce(config.urls.events + '/testImpressions/bulk', 200); fetchMock.postOnce(config.urls.events + '/testImpressions/count', 200); diff --git a/src/__tests__/nodeSuites/telemetry.spec.js b/src/__tests__/nodeSuites/telemetry.spec.js index 25e096c94..22824cd28 100644 --- a/src/__tests__/nodeSuites/telemetry.spec.js +++ b/src/__tests__/nodeSuites/telemetry.spec.js @@ -22,8 +22,8 @@ const config = { export default async function telemetryNodejsSuite(key, fetchMock, assert) { - fetchMock.getOnce(url(config, '/splitChanges?since=-1'), 500); // record http exception - fetchMock.getOnce(url(config, '/splitChanges?since=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(url(config, '/splitChanges?s=1.1&since=-1'), 500); // record http exception + fetchMock.getOnce(url(config, '/splitChanges?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); mockSegmentChanges(fetchMock, new RegExp(config.urls.sdk + '/segmentChanges/*'), ['some_key']); fetchMock.postOnce(url(config, '/testImpressions/bulk'), 200); diff --git a/src/__tests__/online/browser.spec.js b/src/__tests__/online/browser.spec.js index f93ec18a9..f862a3ea8 100644 --- a/src/__tests__/online/browser.spec.js +++ b/src/__tests__/online/browser.spec.js @@ -2,6 +2,7 @@ import tape from 'tape-catch'; import fetchMock from '../testUtils/fetchMock'; import { url } from '../testUtils'; import evaluationsSuite from '../browserSuites/evaluations.spec'; +import evaluationsSemverSuite from '../browserSuites/evaluations-semver.spec'; import impressionsSuite from '../browserSuites/impressions.spec'; import impressionsSuiteDebug from '../browserSuites/impressions.debug.spec'; import impressionsSuiteNone from '../browserSuites/impressions.none.spec'; @@ -86,8 +87,8 @@ tape('## E2E CI Tests ##', function (assert) { //If we change the mocks, we need to clear localstorage. Cleaning up after testing ensures "fresh data". localStorage.clear(); - fetchMock.get(url(settings, '/splitChanges?since=-1'), { status: 200, body: splitChangesMock1 }); - fetchMock.get(url(settings, '/splitChanges?since=1457552620999'), { status: 200, body: splitChangesMock2 }); + fetchMock.get(url(settings, '/splitChanges?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.get(url(settings, '/splitChanges?s=1.1&since=1457552620999'), { status: 200, body: splitChangesMock2 }); fetchMock.get(url(settings, '/mySegments/facundo%40split.io'), { status: 200, body: mySegmentsFacundo }); fetchMock.get(url(settings, '/mySegments/nicolas%40split.io'), { status: 200, body: mySegmentsNicolas }); fetchMock.get(url(settings, '/mySegments/marcio%40split.io'), { status: 200, body: mySegmentsMarcio }); @@ -100,6 +101,7 @@ tape('## E2E CI Tests ##', function (assert) { assert.test('E2E / In Memory', evaluationsSuite.bind(null, configInMemory, fetchMock)); assert.test('E2E / In Memory with Bucketing Key', evaluationsSuite.bind(null, configInMemoryWithBucketingKey, fetchMock)); assert.test('E2E / In LocalStorage with In Memory Fallback', evaluationsSuite.bind(null, configInLocalStorage, fetchMock)); + assert.test('E2E / In Memory - Semver', evaluationsSemverSuite.bind(null, fetchMock)); /* Check impressions */ assert.test('E2E / Impressions', impressionsSuite.bind(null, fetchMock)); assert.test('E2E / Impressions Debug Mode', impressionsSuiteDebug.bind(null, fetchMock)); diff --git a/src/__tests__/online/node.spec.js b/src/__tests__/online/node.spec.js index dce02929c..94ca9d830 100644 --- a/src/__tests__/online/node.spec.js +++ b/src/__tests__/online/node.spec.js @@ -4,6 +4,7 @@ import { url } from '../testUtils'; import { settingsFactory } from '../../settings/node'; import evaluationsSuite from '../nodeSuites/evaluations.spec'; +import evaluationsSemverSuite from '../nodeSuites/evaluations-semver.spec'; import eventsSuite from '../nodeSuites/events.spec'; import impressionsSuite from '../nodeSuites/impressions.spec'; import impressionsSuiteDebug from '../nodeSuites/impressions.debug.spec'; @@ -37,8 +38,8 @@ const config = { const settings = settingsFactory(config); const key = 'facundo@split.io'; -fetchMock.get(url(settings, '/splitChanges?since=-1'), { status: 200, body: splitChangesMock1 }); -fetchMock.get(url(settings, '/splitChanges?since=1457552620999'), { status: 200, body: splitChangesMock2 }); +fetchMock.get(url(settings, '/splitChanges?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); +fetchMock.get(url(settings, '/splitChanges?s=1.1&since=1457552620999'), { status: 200, body: splitChangesMock2 }); fetchMock.get(new RegExp(`${url(settings, '/segmentChanges')}/*`), { status: 200, body: { 'name': 'segment', @@ -56,6 +57,7 @@ fetchMock.post(url(settings, '/v1/metrics/usage'), 200); tape('## Node JS - E2E CI Tests ##', async function (assert) { /* Check client evaluations. */ assert.test('E2E / In Memory', evaluationsSuite.bind(null, config, key)); + assert.test('E2E / In Memory - Semver', evaluationsSemverSuite.bind(null, fetchMock)); /* Check impressions */ assert.test('E2E / Impressions', impressionsSuite.bind(null, key, fetchMock)); diff --git a/src/settings/defaults/version.js b/src/settings/defaults/version.js index f1a241e74..768a07529 100644 --- a/src/settings/defaults/version.js +++ b/src/settings/defaults/version.js @@ -1 +1 @@ -export const packageVersion = '10.25.2'; +export const packageVersion = '10.26.0';