diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 7621d4b33..15766ad41 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -66,7 +66,7 @@ jobs: upload-stage: name: Upload assets - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest needs: build if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/development' }} strategy: @@ -105,7 +105,7 @@ jobs: upload-prod: name: Upload assets - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest needs: build if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} strategy: diff --git a/.github/workflows/sonar-scan.yml b/.github/workflows/sonar-scan.yml index a38349dbf..f0d8a0621 100644 --- a/.github/workflows/sonar-scan.yml +++ b/.github/workflows/sonar-scan.yml @@ -20,7 +20,7 @@ jobs: fetch-depth: 0 - name: Set up Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: 'lts/*' cache: 'npm' diff --git a/CHANGES.txt b/CHANGES.txt index 070dc76c4..fcfcb6abf 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,11 @@ +11.2.0 (March 28, 2025) + - Added a new optional argument to the client `getTreatment` methods to allow passing additional evaluation options, such as a map of properties to append to the generated impressions sent to Split backend. Read more in our docs. + - Added two new configuration options for the SDK's `LOCALSTORAGE` storage type to control the behavior of the persisted rollout plan cache in the browser: + - `storage.expirationDays` to specify the validity period of the rollout plan cache in days. + - `storage.clearOnInit` to clear the rollout plan cache on SDK initialization. + - Updated SDK_READY_FROM_CACHE event when using the `LOCALSTORAGE` storage type to be emitted alongside the SDK_READY event if it has not already been emitted. + - Updated @splitsoftware/splitio-commons package to version 2.2.0. + 11.1.0 (January 17, 2025) - Added support for the new impressions tracking toggle available on feature flags, both respecting the setting and including the new field being returned on `SplitView` type objects. Read more in our docs. - Updated @splitsoftware/splitio-commons package to version 2.1.0. diff --git a/package-lock.json b/package-lock.json index 4e32e9351..7a0413c7d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,15 @@ { "name": "@splitsoftware/splitio", - "version": "11.1.0", + "version": "11.2.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@splitsoftware/splitio", - "version": "11.1.0", + "version": "11.2.0", "license": "Apache-2.0", "dependencies": { - "@splitsoftware/splitio-commons": "2.1.0", + "@splitsoftware/splitio-commons": "2.2.1-rc.1", "bloom-filters": "^3.0.4", "ioredis": "^4.28.0", "js-yaml": "^3.13.1", @@ -351,9 +351,10 @@ "dev": true }, "node_modules/@splitsoftware/splitio-commons": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.1.0.tgz", - "integrity": "sha512-7SJRBia0Pi72s76drH8kG2cVnCqkjMHMJQWJSFnG+rE/UOx9AROmuviOkY6tv6qYPJFqFQQGHGX6lXjxZhYzkw==", + "version": "2.2.1-rc.1", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.2.1-rc.1.tgz", + "integrity": "sha512-qbrfJK0p03KsBBE7f4bBJJhtSYeBppVdOUzTjG5rpZTnoYqnLvjEpkP9ciUrDIz2B9W/ylgXFV3I1j6wp4PeHQ==", + "license": "Apache-2.0", "dependencies": { "@types/ioredis": "^4.28.0", "tslib": "^2.3.1" @@ -6364,10 +6365,11 @@ } }, "node_modules/tar-fs": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", - "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.2.tgz", + "integrity": "sha512-EsaAXwxmx8UB7FRKqeozqEPop69DXcmYwTQwXvyAPF352HJsPdkVhvTaDPYqfNgruveJIJy3TA2l+2zj8LJIJA==", "dev": true, + "license": "MIT", "dependencies": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", @@ -7537,9 +7539,9 @@ "dev": true }, "@splitsoftware/splitio-commons": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.1.0.tgz", - "integrity": "sha512-7SJRBia0Pi72s76drH8kG2cVnCqkjMHMJQWJSFnG+rE/UOx9AROmuviOkY6tv6qYPJFqFQQGHGX6lXjxZhYzkw==", + "version": "2.2.1-rc.1", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.2.1-rc.1.tgz", + "integrity": "sha512-qbrfJK0p03KsBBE7f4bBJJhtSYeBppVdOUzTjG5rpZTnoYqnLvjEpkP9ciUrDIz2B9W/ylgXFV3I1j6wp4PeHQ==", "requires": { "@types/ioredis": "^4.28.0", "tslib": "^2.3.1" @@ -12221,9 +12223,9 @@ } }, "tar-fs": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", - "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.2.tgz", + "integrity": "sha512-EsaAXwxmx8UB7FRKqeozqEPop69DXcmYwTQwXvyAPF352HJsPdkVhvTaDPYqfNgruveJIJy3TA2l+2zj8LJIJA==", "dev": true, "requires": { "chownr": "^1.1.1", diff --git a/package.json b/package.json index efc19f4d8..a286def50 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio", - "version": "11.1.0", + "version": "11.2.0", "description": "Split SDK", "files": [ "README.md", @@ -38,7 +38,7 @@ "node": ">=14.0.0" }, "dependencies": { - "@splitsoftware/splitio-commons": "2.1.0", + "@splitsoftware/splitio-commons": "2.2.1-rc.1", "bloom-filters": "^3.0.4", "ioredis": "^4.28.0", "js-yaml": "^3.13.1", diff --git a/src/__tests__/browserSuites/evaluations-semver.spec.js b/src/__tests__/browserSuites/evaluations-semver.spec.js index 716c5467d..d049ddbf4 100644 --- a/src/__tests__/browserSuites/evaluations-semver.spec.js +++ b/src/__tests__/browserSuites/evaluations-semver.spec.js @@ -25,8 +25,8 @@ const config = { export default async function (fetchMock, assert) { - fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.2&since=-1', { status: 200, body: splitChangesMock1 }); - fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.2&since=1675259356568', { status: 200, body: { splits: [], since: 1675259356568, till: 1675259356568 } }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.3&since=1675259356568&rbSince=-1', { status: 200, body: { ff: { d: [], s: 1675259356568, t: 1675259356568 } } }); fetchMock.getOnce(config.urls.sdk + '/memberships/emi%40split.io', { status: 200, body: { ms: {} } }); fetchMock.getOnce(config.urls.sdk + '/memberships/2nd', { status: 200, body: { ms: {} } }); diff --git a/src/__tests__/browserSuites/fetch-specific-splits.spec.js b/src/__tests__/browserSuites/fetch-specific-splits.spec.js index a9f6b383e..45a7af643 100644 --- a/src/__tests__/browserSuites/fetch-specific-splits.spec.js +++ b/src/__tests__/browserSuites/fetch-specific-splits.spec.js @@ -25,13 +25,13 @@ export function fetchSpecificSplits(fetchMock, assert) { const queryString = queryStrings[i] || ''; let factory; - fetchMock.getOnce(urls.sdk + '/splitChanges?s=1.2&since=-1' + queryString, { status: 200, body: { splits: [], since: -1, till: 1457552620999 } }); - fetchMock.getOnce(urls.sdk + '/splitChanges?s=1.2&since=1457552620999' + queryString, { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); - fetchMock.getOnce(urls.sdk + '/splitChanges?s=1.2&since=1457552620999' + queryString, function () { + fetchMock.getOnce(urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1' + queryString, { status: 200, body: { ff: { d: [], s: -1, t: 1457552620999 } } }); + fetchMock.getOnce(urls.sdk + '/splitChanges?s=1.3&since=1457552620999&rbSince=-1' + queryString, { status: 200, body: { ff: { d: [], s: 1457552620999, t: 1457552620999 } } }); + fetchMock.getOnce(urls.sdk + '/splitChanges?s=1.3&since=1457552620999&rbSince=-1' + queryString, function () { factory.client().destroy().then(() => { assert.pass(`splitFilters #${i}`); }); - return { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }; + return { status: 200, body: { ff: { d: [], s: 1457552620999, t: 1457552620999 } } }; }); fetchMock.get(urls.sdk + '/memberships/nicolas%40split.io', { status: 200, body: { 'ms': {} } }); @@ -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 + '/memberships/nicolas%40split.io', { status: 200, body: { 'ms': {} } }); - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.2&since=-1' + queryString, { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 }}); - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.2&since=1457552620999' + queryString, async function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1' + queryString, { status: 200, body: { ff: { d: [], s: 1457552620999, t: 1457552620999 } }}); + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=1457552620999&rbSince=-1' + 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 214c1a1fb..11fe92e20 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?s=1.2&since=-1&sets=set_1,set_2', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-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?s=1.2&since=1602796638344&sets=set_1,set_2', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=1602796638344&rbSince=-1&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?s=1.2&since=1602797638344&sets=set_1,set_2', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=1602797638344&rbSince=-1&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?s=1.2&since=1602798638344&sets=set_1,set_2', async function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=1602798638344&rbSince=-1&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?s=1.2&since=-1', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', function () { return { status: 200, body: splitChange2}; }); // Receive split change with 1 split belonging to set_1 only - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.2&since=1602796638344', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=1602796638344&rbSince=-1', 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?s=1.2&since=1602797638344', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=1602797638344&rbSince=-1', 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?s=1.2&since=1602798638344', async function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=1602798638344&rbSince=-1', 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 + '/memberships/nicolas%40split.io', { status: 200, body: { 'ms': {} } }); // Receive split change with 1 split belonging to set_1 & set_2 and one belonging to set_3 - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.2&since=-1&sets=set_1', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1&sets=set_1', function () { return { status: 200, body: splitChange2}; }); - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.2&since=1602796638344&sets=set_1', async function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=1602796638344&rbSince=-1&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 + '/memberships/nicolas%40split.io', { status: 200, body: { 'ms': {} } }); // Receive split change with 1 split belonging to set_1 & set_2 and one belonging to set_3 - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.2&since=-1', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', function () { return { status: 200, body: splitChange2}; }); - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.2&since=1602796638344', async function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=1602796638344&rbSince=-1', 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 60108065a..2d262cb14 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?s=1.2&since=-1'), { status: 200, body: splitChangesMock1 }); - fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=1457552620999'), { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), { status: 200, body: { ff: { d: [], s: 1457552620999, t: 1457552620999 } } }); fetchMock.getOnce(url(settings, `/memberships/${encodeURIComponent(config.core.key)}`), { status: 200, body: { ms: {} } }); // Init Split client diff --git a/src/__tests__/browserSuites/impressions-listener.spec.js b/src/__tests__/browserSuites/impressions-listener.spec.js index ca47995c2..9907eab87 100644 --- a/src/__tests__/browserSuites/impressions-listener.spec.js +++ b/src/__tests__/browserSuites/impressions-listener.spec.js @@ -46,7 +46,7 @@ export default function (assert) { const testAttrs = { is_test: true }; // Impression listener is shared across all client instances and does not get affected by configurations. - client.getTreatment('hierarchical_splits_test'); + client.getTreatment('hierarchical_splits_test', undefined, { properties: { prop1: 'prop-value' } }); client2.getTreatment('qc_team'); client2.getTreatmentWithConfig('qc_team'); // Validate that the impression is the same. client3.getTreatment('qc_team', testAttrs); @@ -58,7 +58,8 @@ export default function (assert) { treatment: 'no', bucketingKey: 'impr_bucketing_2', label: 'default rule', - pt: undefined + pt: undefined, + properties: undefined }; assert.equal(listener.logImpression.callCount, 4, 'Impression listener logImpression method should be called after we call client.getTreatment, once per each impression generated.'); @@ -71,7 +72,7 @@ export default function (assert) { bucketingKey: undefined, label: 'expected label', changeNumber: 2828282828, - pt: undefined + properties: '{"prop1":"prop-value"}' }, attributes: undefined, ...metaData diff --git a/src/__tests__/browserSuites/impressions.debug.spec.js b/src/__tests__/browserSuites/impressions.debug.spec.js index 5f90b7004..f8b704311 100644 --- a/src/__tests__/browserSuites/impressions.debug.spec.js +++ b/src/__tests__/browserSuites/impressions.debug.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?s=1.2&since=-1'), { status: 200, body: splitChangesMock1 }); - fetchMock.get(url(settings, '/splitChanges?s=1.2&since=1457552620999'), { status: 200, body: splitChangesMock2 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.get(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), { status: 200, body: splitChangesMock2 }); fetchMock.get(url(settings, '/memberships/facundo%40split.io'), { status: 200, body: membershipsFacundo }); const splitio = SplitFactory({ @@ -63,6 +63,14 @@ export default function (fetchMock, assert) { k: 'facundo@split.io', t: 'o.n', m: data[0].i[1].m, c: 828282828282, r: 'another expected label', pt: data[0].i[0].m, }, { k: 'facundo@split.io', t: 'o.n', m: data[0].i[2].m, c: 828282828282, r: 'another expected label', pt: data[0].i[1].m + }, { + k: 'facundo@split.io', t: 'o.n', m: data[0].i[3].m, c: 828282828282, r: 'another expected label', properties: '{"prop1":"value1"}' + }, { + k: 'facundo@split.io', t: 'o.n', m: data[0].i[4].m, c: 828282828282, r: 'another expected label', properties: '{"prop1":"value2"}' + }, { + k: 'facundo@split.io', t: 'o.n', m: data[0].i[5].m, c: 828282828282, r: 'another expected label', properties: '{"prop1":"value3"}' + }, { + k: 'facundo@split.io', t: 'o.n', m: data[0].i[6].m, c: 828282828282, r: 'another expected label', properties: '{"prop1":"value4"}' }] }]); @@ -75,7 +83,7 @@ export default function (fetchMock, assert) { fetchMock.postOnce(url(settings, '/testImpressions/count'), (url, opts) => { assert.deepEqual(JSON.parse(opts.body), { - pf: [{ f: 'always_on_track_impressions_false', m: truncatedTimeFrame, rc: 1 }] + pf: [{ f: 'always_on_impressions_disabled_true', m: truncatedTimeFrame, rc: 1 }] }, 'We should generate impression count for the feature with track impressions disabled.'); return 200; @@ -83,7 +91,7 @@ export default function (fetchMock, assert) { fetchMock.postOnce(url(settings, '/v1/keys/cs'), (url, opts) => { assert.deepEqual(JSON.parse(opts.body), { - keys: [{ fs: ['always_on_track_impressions_false'], k: 'facundo@split.io' }] + keys: [{ fs: ['always_on_impressions_disabled_true'], k: 'facundo@split.io' }] }, 'We should track unique keys for the feature with track impressions disabled.'); return 200; @@ -95,6 +103,12 @@ export default function (fetchMock, assert) { client.getTreatment('split_with_config'); client.getTreatment('split_with_config'); client.getTreatment('split_with_config'); - assert.equal(client.getTreatment('always_on_track_impressions_false'), 'on'); + assert.equal(client.getTreatment('always_on_impressions_disabled_true'), 'on'); + + // with properties + assert.equal(client.getTreatment('split_with_config', undefined, { properties: { prop1: 'value1' } }), 'o.n'); + assert.equal(client.getTreatments(['split_with_config'], undefined, { properties: { prop1: 'value2' } }).split_with_config, 'o.n'); + assert.equal(client.getTreatmentWithConfig('split_with_config', undefined, { properties: { prop1: 'value3' } }).treatment, 'o.n'); + assert.equal(client.getTreatmentsWithConfig(['split_with_config'], undefined, { properties: { prop1: 'value4' } }).split_with_config.treatment, 'o.n'); }); } diff --git a/src/__tests__/browserSuites/impressions.none.spec.js b/src/__tests__/browserSuites/impressions.none.spec.js index 2294c9695..60cdcacd3 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?s=1.2&since=-1'), { status: 200, body: splitChangesMock1 }); - fetchMock.get(url(settings, '/splitChanges?s=1.2&since=1457552620999'), { status: 200, body: splitChangesMock2 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.get(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), { status: 200, body: splitChangesMock2 }); fetchMock.get(url(settings, '/memberships/facundo%40split.io'), { status: 200, body: membershipsFacundo }); fetchMock.get(url(settings, '/memberships/emma%40split.io'), { status: 200, body: membershipsFacundo }); @@ -59,7 +59,7 @@ export default async function (fetchMock, assert) { { f: 'split_with_config', m: truncatedTimeFrame, rc: 2 }, { f: 'always_off', m: truncatedTimeFrame, rc: 4 }, { f: 'always_on', m: truncatedTimeFrame, rc: 2 }, - { f: 'always_on_track_impressions_false', m: truncatedTimeFrame, rc: 1 } + { f: 'always_on_impressions_disabled_true', m: truncatedTimeFrame, rc: 1 } ] }); return 200; @@ -76,7 +76,7 @@ export default async function (fetchMock, assert) { }, { k: 'emma@split.io', - fs: ['always_off', 'always_on', 'always_on_track_impressions_false'] + fs: ['always_off', 'always_on', 'always_on_impressions_disabled_true'] } ] }, 'We performed evaluations for two keys, so we should have 2 item total.'); @@ -94,7 +94,7 @@ export default async function (fetchMock, assert) { client.getTreatment('always_on'); client.getTreatment('always_off'); client.getTreatment('split_with_config'); - sharedClient.getTreatment('always_on_track_impressions_false'); + sharedClient.getTreatment('always_on_impressions_disabled_true'); client.destroy().then(() => { assert.end(); diff --git a/src/__tests__/browserSuites/impressions.spec.js b/src/__tests__/browserSuites/impressions.spec.js index 33e60106a..12f02a198 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?s=1.2&since=-1'), { status: 200, body: splitChangesMock1 }); - fetchMock.get(url(settings, '/splitChanges?s=1.2&since=1457552620999'), { status: 200, body: splitChangesMock2 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.get(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), { status: 200, body: splitChangesMock2 }); fetchMock.get(url(settings, '/memberships/facundo%40split.io'), { status: 200, body: membershipsFacundo }); const splitio = SplitFactory({ @@ -47,32 +47,21 @@ export default function (fetchMock, assert) { const client = splitio.client(); const assertPayload = req => { - const resp = JSON.parse(req.body); - - assert.equal(resp.length, 2, 'We performed evaluations for 3 features, but one with `impressionsDisabled` true, so we should have 2 items total'); - - const dependencyChildImpr = resp.filter(e => e.f === 'hierarchical_splits_test')[0]; - const splitWithConfigImpr = resp.filter(e => e.f === 'split_with_config')[0]; - const alwaysOnWithTrackImpressionsFalse = resp.filter(e => e.f === 'always_on_track_impressions_false'); - - assert.true(dependencyChildImpr, 'Split we wanted to evaluate should be present on the impressions.'); - assert.false(resp.some(e => e.f === 'hierarchical_dep_always_on'), 'Parent split evaluations should not result in impressions.'); - assert.false(resp.some(e => e.f === 'hierarchical_dep_hierarchical'), 'No matter how deep is the chain.'); - assert.true(splitWithConfigImpr, 'Split evaluated with config should have generated an impression too.'); - assert.false(Object.prototype.hasOwnProperty.call(splitWithConfigImpr.i[0], 'configuration'), 'Impressions do not change with configuration evaluations.'); - assert.false(Object.prototype.hasOwnProperty.call(splitWithConfigImpr.i[0], 'config'), 'Impressions do not change with configuration evaluations.'); - assert.equal(alwaysOnWithTrackImpressionsFalse.length, 0); - - const { - k, - r, - t - } = dependencyChildImpr.i[0]; - - assert.equal(k, 'facundo@split.io', 'Present impression should have the correct key.'); - // The label present on the mock. - assert.equal(r, 'expected label', 'Present impression should have the correct label.'); - assert.equal(t, 'on', 'Present impression should have the correct treatment.'); + const reqBody = JSON.parse(req.body); + + assert.deepEqual(reqBody, [{ + f: 'hierarchical_splits_test', + i: [{ + k: 'facundo@split.io', t: 'on', m: reqBody[0].i[0].m, c: 2828282828, r: 'expected label' + }] + }, { + f: 'split_with_config', + i: [{ + k: 'facundo@split.io', t: 'o.n', m: reqBody[1].i[0].m, c: 828282828282, r: 'another expected label' + }, { + k: 'facundo@split.io', t: 'o.n', m: reqBody[1].i[1].m, c: 828282828282, r: 'another expected label', properties: '{"some":"value2"}' + }] + }], 'We performed evaluations for 3 features, but one with `impressionsDisabled` true, so we should have 2 items total'); }; fetchMock.postOnce(url(settings, '/testImpressions/bulk'), (url, req) => { @@ -93,28 +82,23 @@ export default function (fetchMock, assert) { return 200; }); - fetchMock.postOnce(url(settings, '/testImpressions/count'), (url, opts) => { - const data = JSON.parse(opts.body); + fetchMock.postOnce(url(settings, '/testImpressions/count'), (url, req) => { + const reqBody = JSON.parse(req.body); - assert.equal(data.pf.length, 2, 'We should generate impressions count for 2 features.'); - - // finding these validate the feature names collection too - const splitWithConfigImpr = data.pf.filter(e => e.f === 'split_with_config')[0]; - const alwaysOnWithTrackImpressionsFalse = data.pf.filter(e => e.f === 'always_on_track_impressions_false')[0]; - - assert.equal(splitWithConfigImpr.rc, 2); - assert.equal(typeof splitWithConfigImpr.m, 'number'); - assert.equal(splitWithConfigImpr.m, truncatedTimeFrame); - assert.equal(alwaysOnWithTrackImpressionsFalse.rc, 1); - assert.equal(typeof alwaysOnWithTrackImpressionsFalse.m, 'number'); - assert.equal(alwaysOnWithTrackImpressionsFalse.m, truncatedTimeFrame); + assert.deepEqual(reqBody, { + pf: [{ + f: 'split_with_config', m: truncatedTimeFrame, rc: 2 + }, { + f: 'always_on_impressions_disabled_true', m: truncatedTimeFrame, rc: 1 + }] + }, 'We should generate impressions count for 2 features.'); return 200; }); fetchMock.postOnce(url(settings, '/v1/keys/cs'), (url, opts) => { assert.deepEqual(JSON.parse(opts.body), { - keys: [{ fs: [ 'always_on_track_impressions_false' ], k: 'facundo@split.io' }] + keys: [{ fs: ['always_on_impressions_disabled_true'], k: 'facundo@split.io' }] }, 'We should only track unique keys for features flags with track impressions disabled.'); return 200; @@ -129,9 +113,12 @@ export default function (fetchMock, assert) { config: '{"color":"brown","dimensions":{"height":12,"width":14},"text":{"inner":"click me"}}' }, 'We should get an evaluation as always.'); client.getTreatmentWithConfig('split_with_config'); - client.getTreatmentWithConfig('split_with_config'); + client.getTreatmentWithConfig('split_with_config', undefined, { properties: { /* empty properties are ignored */ } }); + + // Impression should not be tracked (passed properties will not be submitted) + assert.equal(client.getTreatment('always_on_impressions_disabled_true'), 'on', undefined, { properties: { some: 'value1' } }); - // Impression should not be tracked - assert.equal(client.getTreatment('always_on_track_impressions_false'), 'on'); + // Tracked impression with properties should be handled in DEBUG mode (doesn't increase `rc` count but adds an impression) + assert.equal(client.getTreatment('split_with_config', undefined, { properties: { some: 'value2' } }), 'o.n'); }); } diff --git a/src/__tests__/browserSuites/manager.spec.js b/src/__tests__/browserSuites/manager.spec.js index 9c64cc52c..b595ffcce 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?s=1.2&since=-1'), overwriteRoutes: true }, { status: 200, body: splitChangesMockReal }); + fetchMock.getOnce({ url: url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), overwriteRoutes: true }, { status: 200, body: splitChangesMockReal }); const mockSplits = splitChangesMockReal; @@ -29,19 +29,19 @@ export default async function (settings, fetchMock, assert) { const splitNames = manager.names(); - assert.equal(splitNames.length, mockSplits.splits.length, 'The manager.splits() method should return all split names on the factory storage.'); - assert.deepEqual(splitNames, map(mockSplits.splits, split => split.name), 'The manager.splits() method should return all split names on the factory storage.'); + assert.equal(splitNames.length, mockSplits.ff.d.length, 'The manager.splits() method should return all split names on the factory storage.'); + assert.deepEqual(splitNames, map(mockSplits.ff.d, split => split.name), 'The manager.splits() method should return all split names on the factory storage.'); const splitObj = manager.split(splitNames[0]); const expectedSplitObj = index => ({ - 'trafficType': mockSplits.splits[index].trafficTypeName, - 'name': mockSplits.splits[index].name, - 'killed': mockSplits.splits[index].killed, - 'changeNumber': mockSplits.splits[index].changeNumber, - 'treatments': map(mockSplits.splits[index].conditions[0].partitions, partition => partition.treatment), - 'configs': mockSplits.splits[index].configurations || {}, - 'sets': mockSplits.splits[index].sets || [], - 'defaultTreatment': mockSplits.splits[index].defaultTreatment, + 'trafficType': mockSplits.ff.d[index].trafficTypeName, + 'name': mockSplits.ff.d[index].name, + 'killed': mockSplits.ff.d[index].killed, + 'changeNumber': mockSplits.ff.d[index].changeNumber, + 'treatments': map(mockSplits.ff.d[index].conditions[0].partitions, partition => partition.treatment), + 'configs': mockSplits.ff.d[index].configurations || {}, + 'sets': mockSplits.ff.d[index].sets || [], + 'defaultTreatment': mockSplits.ff.d[index].defaultTreatment, 'impressionsDisabled': false }); @@ -49,7 +49,7 @@ export default async function (settings, fetchMock, assert) { assert.deepEqual(splitObj, expectedSplitObj(0), 'If we ask for an existent one we receive the expected split view.'); const splitObjects = manager.splits(); - assert.equal(splitObjects.length, mockSplits.splits.length, 'The manager.splits() returns the full collection of split views.'); + assert.equal(splitObjects.length, mockSplits.ff.d.length, 'The manager.splits() returns the full collection of split views.'); assert.deepEqual(splitObjects[0], expectedSplitObj(0), 'And the split views should match the items of the collection in split view format.'); assert.deepEqual(splitObjects[1], expectedSplitObj(1), 'And the split views should match the items of the collection in split view format.'); diff --git a/src/__tests__/browserSuites/push-corner-cases.spec.js b/src/__tests__/browserSuites/push-corner-cases.spec.js index a07d1c162..d25170307 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?s=1.2&users=${encodeURIComponent(userKey)}`), { status: 200, body: authPushEnabledNicolas }); + fetchMock.getOnce(url(settings, `/v2/auth?s=1.3&users=${encodeURIComponent(userKey)}`), { status: 200, body: authPushEnabledNicolas }); // 2 memberships requests: initial sync and after SSE opened fetchMock.get({ url: url(settings, '/memberships/nicolas%40split.io'), repeat: 2 }, { status: 200, body: { ms: {} } }); // 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?s=1.2&since=25'), { status: 200, body: splitChangesMock1 }, { delay: MILLIS_SPLIT_CHANGES_RESPONSE, /* delay response */ }); - fetchMock.getOnce(url(settings, `/splitChanges?s=1.2&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.3&since=25&rbSince=-1'), { status: 200, body: splitChangesMock1 }, { delay: MILLIS_SPLIT_CHANGES_RESPONSE, /* delay response */ }); + fetchMock.getOnce(url(settings, `/splitChanges?s=1.3&since=${splitChangesMock1.ff.t}&rbSince=-1`), { status: 200, body: { ff: { d: [], s: splitChangesMock1.ff.t, t: splitChangesMock1.ff.t } } }, { 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-fallback.spec.js b/src/__tests__/browserSuites/push-fallback.spec.js index dc8df88b9..5c3e0a4de 100644 --- a/src/__tests__/browserSuites/push-fallback.spec.js +++ b/src/__tests__/browserSuites/push-fallback.spec.js @@ -206,68 +206,68 @@ export function testFallback(fetchMock, assert) { }); - fetchMock.getOnce(url(settings, `/v2/auth?s=1.2&users=${encodeURIComponent(userKey)}`), function (url, opts) { + fetchMock.getOnce(url(settings, `/v2/auth?s=1.3&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 memberships sync - fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), { status: 200, body: splitChangesMock1 }); fetchMock.getOnce(url(settings, '/memberships/nicolas%40split.io'), { status: 200, body: membershipsNicolasMock1 }); // split and segment sync after SSE opened - fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=1457552620999'), { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), { status: 200, body: { ff: { d: [], s: 1457552620999, t: 1457552620999 } } }); fetchMock.getOnce(url(settings, '/memberships/nicolas%40split.io'), { status: 200, body: membershipsNicolasMock1 }); // fetches due to first fallback to polling - fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=1457552620999'), { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), { status: 200, body: { ff: { d: [], s: 1457552620999, t: 1457552620999 } } }); fetchMock.getOnce(url(settings, '/memberships/nicolas%40split.io'), { status: 200, body: membershipsNicolasMock1 }); - fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), 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 } }; + return { status: 200, body: { ff: { d: [], s: 1457552620999, t: 1457552620999 } } }; }); fetchMock.getOnce(url(settings, '/memberships/nicolas%40split.io'), { status: 200, body: membershipsNicolasMock1 }); // split and segment sync due to streaming up (OCCUPANCY event) - fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=1457552620999'), { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), { status: 200, body: { ff: { d: [], s: 1457552620999, t: 1457552620999 } } }); fetchMock.getOnce(url(settings, '/memberships/nicolas%40split.io'), { status: 200, body: membershipsNicolasMock1 }); // creating of second client during streaming: initial memberships sync, reauth and syncAll due to new client fetchMock.getOnce(url(settings, '/memberships/marcio%40split.io'), { status: 200, body: membershipsMarcio }); - fetchMock.get({ url: url(settings, `/v2/auth?s=1.2&users=${encodeURIComponent(userKey)}&users=${encodeURIComponent(secondUserKey)}`), repeat: 3 /* initial + 2 STREAMING_RESET */ }, (url, opts) => { + fetchMock.get({ url: url(settings, `/v2/auth?s=1.3&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?s=1.2&since=1457552620999'), { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), { status: 200, body: { ff: { d: [], s: 1457552620999, t: 1457552620999 } } }); fetchMock.getOnce(url(settings, '/memberships/nicolas%40split.io'), { status: 200, body: membershipsNicolasMock1 }); fetchMock.getOnce(url(settings, '/memberships/marcio%40split.io'), { status: 200, body: membershipsMarcio }); // fetch due to SPLIT_UPDATE event - fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), 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?s=1.2&since=1457552649999'), { status: 200, body: { splits: [], since: 1457552649999, till: 1457552649999 } }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552649999&rbSince=-1'), { status: 200, body: { ff: { d: [], s: 1457552649999, t: 1457552649999 } } }); fetchMock.getOnce(url(settings, '/memberships/nicolas%40split.io'), { status: 200, body: membershipsNicolasMock1 }); fetchMock.getOnce(url(settings, '/memberships/marcio%40split.io'), { status: 200, body: membershipsMarcio }); // continue fetches due to second fallback to polling - fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=1457552649999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552649999&rbSince=-1'), 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 } }; + return { status: 200, body: { ff: { d: [], s: 1457552649999, t: 1457552649999 } } }; }); fetchMock.getOnce(url(settings, '/memberships/nicolas%40split.io'), { status: 200, body: membershipsNicolasMock1 }); fetchMock.getOnce(url(settings, '/memberships/marcio%40split.io'), { status: 200, body: membershipsMarcio }); // split and segment sync due to streaming up (CONTROL event) - fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=1457552649999'), { status: 200, body: { splits: [], since: 1457552649999, till: 1457552649999 } }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552649999&rbSince=-1'), { status: 200, body: { ff: { d: [], s: 1457552649999, t: 1457552649999 } } }); fetchMock.getOnce(url(settings, '/memberships/nicolas%40split.io'), { status: 200, body: membershipsNicolasMock1 }); fetchMock.getOnce(url(settings, '/memberships/marcio%40split.io'), { status: 200, body: membershipsMarcio }); @@ -280,20 +280,20 @@ export function testFallback(fetchMock, assert) { fetchMock.getOnce(url(settings, '/memberships/marcio%40split.io'), { status: 200, body: membershipsMarcio }); // 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?s=1.2&since=1457552649999'), repeat: 4 }, { status: 200, body: { splits: [], since: 1457552649999, till: 1457552649999 } }); + fetchMock.get({ url: url(settings, '/splitChanges?s=1.3&since=1457552649999&rbSince=-1'), repeat: 4 }, { status: 200, body: { ff: { d: [], s: 1457552649999, t: 1457552649999 } } }); fetchMock.get({ url: url(settings, '/memberships/nicolas%40split.io'), repeat: 4 }, { status: 200, body: membershipsNicolasMock1 }); fetchMock.get({ url: url(settings, '/memberships/marcio%40split.io'), repeat: 4 }, { status: 200, body: membershipsMarcio }); // Periodic fetch due to polling (memberships is not fetched due to smart pausing) - fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=1457552649999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552649999&rbSince=-1'), function () { const lapse = Date.now() - start; - assert.true(nearlyEqual(lapse, MILLIS_STREAMING_DISABLED_CONTROL + settings.scheduler.featuresRefreshRate), 'fetch due to fourth fallback to polling'); + assert.true(nearlyEqual(lapse, MILLIS_STREAMING_DISABLED_CONTROL + settings.scheduler.featuresRefreshRate, 100), 'fetch due to fourth fallback to polling'); return { status: 200, body: splitChangesMock3 }; }); - fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=1457552669999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552669999&rbSince=-1'), 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 } }; + assert.true(nearlyEqual(lapse, MILLIS_STREAMING_DISABLED_CONTROL + settings.scheduler.featuresRefreshRate * 2, 100), 'fetch due to fourth fallback to polling'); + return { status: 200, body: { ff: { d: [], s: 1457552669999, t: 1457552669999 } } }; }); fetchMock.get(new RegExp('.*'), function (url) { diff --git a/src/__tests__/browserSuites/push-flag-sets.spec.js b/src/__tests__/browserSuites/push-flag-sets.spec.js index df3d502f2..fe829be9b 100644 --- a/src/__tests__/browserSuites/push-flag-sets.spec.js +++ b/src/__tests__/browserSuites/push-flag-sets.spec.js @@ -38,20 +38,20 @@ export function testFlagSets(fetchMock, t) { fetchMock.get(baseUrls.sdk + '/memberships/nicolas%40split.io', { status: 200, body: { 'ms': {} } }); - fetchMock.get(baseUrls.auth + '/v2/auth?s=1.2&users=nicolas%40split.io', function () { + fetchMock.get(baseUrls.auth + '/v2/auth?s=1.3&users=nicolas%40split.io', function () { return { status: 200, body: authPushEnabled }; }); - fetchMock.get(baseUrls.sdk + '/splitChanges?s=1.2&since=-1', function () { - return { status: 200, body: { splits: [], since: -1, till: 0}}; + fetchMock.get(baseUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', function () { + return { status: 200, body: { ff: { d: [], s: -1, t: 0 }}}; }); - fetchMock.get(baseUrls.sdk + '/splitChanges?s=1.2&since=0', function () { - return { status: 200, body: { splits: [], since: 0, till: 1 }}; + fetchMock.get(baseUrls.sdk + '/splitChanges?s=1.3&since=0&rbSince=-1', function () { + return { status: 200, body: { ff: { d: [], s: 0, t: 1 } }}; }); - fetchMock.get(baseUrls.sdk + '/splitChanges?s=1.2&since=-1&sets=set_1,set_2', function () { - return { status: 200, body: { splits: [], since: -1, till: 0 }}; + fetchMock.get(baseUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1&sets=set_1,set_2', function () { + return { status: 200, body: { ff: { d: [], s: -1, t: 0 } }}; }); - fetchMock.get(baseUrls.sdk + '/splitChanges?s=1.2&since=0&sets=set_1,set_2', function () { - return { status: 200, body: { splits: [], since: 0, till: 1 }}; + fetchMock.get(baseUrls.sdk + '/splitChanges?s=1.3&since=0&rbSince=-1&sets=set_1,set_2', function () { + return { status: 200, body: { ff: { d: [], s: 0, t: 1 } }}; }); @@ -189,9 +189,9 @@ export function testFlagSets(fetchMock, t) { t.test(async (assert) => { - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.2&since=2&sets=set_1,set_2', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=2&rbSince=-1&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 }}; + return { status: 200, body: { ff: { d: [], s: 2, t: 3 } }}; }); let splitio, client, manager = []; @@ -229,9 +229,9 @@ export function testFlagSets(fetchMock, t) { t.test(async (assert) => { - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.2&since=1&sets=set_1,set_2', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=1&rbSince=-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 }}; + return { status: 200, body: { ff: { d: [], s: 1, t: 5 } }}; }); let splitio, client, manager = []; diff --git a/src/__tests__/browserSuites/push-initialization-nopush.spec.js b/src/__tests__/browserSuites/push-initialization-nopush.spec.js index 1df52e58b..cb76abd2d 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, '/memberships/nicolas%40split.io'), { status: 200, body: membershipsNicolas }); - fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=-1'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-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?s=1.2&since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), 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?s=1.2&since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), 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?s=1.2&users=${encodeURIComponent(userKey)}`, function (url, opts) { + fetchMock.getOnce(`https://auth.push-initialization-nopush/api/v2/auth?s=1.3&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?s=1.2&users=${encodeURIComponent(userKey)}`), function (url, opts) { + fetchMock.getOnce(url(settings, `/v2/auth?s=1.3&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?s=1.2&users=${encodeURIComponent(userKey)}`), function (url, opts) { + fetchMock.getOnce(url(settings, `/v2/auth?s=1.3&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 027ceceaa..ebe12112f 100644 --- a/src/__tests__/browserSuites/push-initialization-retries.spec.js +++ b/src/__tests__/browserSuites/push-initialization-retries.spec.js @@ -51,13 +51,13 @@ export function testPushRetriesDueToAuthErrors(fetchMock, assert) { let start, splitio, client, ready = false; - fetchMock.getOnce(url(settings, `/v2/auth?s=1.2&users=${encodeURIComponent(userKey)}`), function (url, opts) { + fetchMock.getOnce(url(settings, `/v2/auth?s=1.3&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?s=1.2&users=${encodeURIComponent(userKey)}`), { throws: new TypeError('Network error') }); - fetchMock.getOnce(url(settings, `/v2/auth?s=1.2&users=${encodeURIComponent(userKey)}`), function (url, opts) { + fetchMock.getOnce(url(settings, `/v2/auth?s=1.3&users=${encodeURIComponent(userKey)}`), { throws: new TypeError('Network error') }); + fetchMock.getOnce(url(settings, `/v2/auth?s=1.3&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)); @@ -66,23 +66,23 @@ export function testPushRetriesDueToAuthErrors(fetchMock, assert) { }); fetchMock.get({ url: url(settings, '/memberships/nicolas%40split.io'), repeat: 4 }, { status: 200, body: membershipsNicolasMock }); - fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=-1'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, 0), 'initial sync'); return { status: 200, body: splitChangesMock1 }; }); - fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), 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?s=1.2&since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, settings.scheduler.featuresRefreshRate), 'polling'); return { status: 200, body: splitChangesMock2 }; }); - fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), 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(() => { @@ -130,30 +130,30 @@ export function testPushRetriesDueToSseErrors(fetchMock, assert) { sseattempts++; }); - fetchMock.get({ url: url(settings, `/v2/auth?s=1.2&users=${encodeURIComponent(userKey)}`), repeat: 3 /* 3 push attempts */ }, function (url, opts) { + fetchMock.get({ url: url(settings, `/v2/auth?s=1.3&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, '/memberships/nicolas%40split.io'), repeat: 4 }, { status: 200, body: membershipsNicolasMock }); - fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=-1'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, 0), 'initial sync'); return { status: 200, body: splitChangesMock1 }; }); - fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), 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?s=1.2&since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, settings.scheduler.featuresRefreshRate), 'polling'); return { status: 200, body: splitChangesMock2 }; }); - fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), 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?s=1.2&users=${encodeURIComponent(userKey)}`), { status: 200, body: authPushEnabledNicolas }, { delay: 100 }); + fetchMock.getOnce(url(settings, `/v2/auth?s=1.3&users=${encodeURIComponent(userKey)}`), { status: 200, body: authPushEnabledNicolas }, { delay: 100 }); fetchMock.getOnce(url(settings, '/memberships/nicolas%40split.io'), { status: 200, body: membershipsNicolasMock }); - fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-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?s=1.2&users=${encodeURIComponent(userKey)}`), { status: 200, body: { ...authPushEnabledNicolas, connDelay: 0.1 } }); + fetchMock.getOnce(url(settings, `/v2/auth?s=1.3&users=${encodeURIComponent(userKey)}`), { status: 200, body: { ...authPushEnabledNicolas, connDelay: 0.1 } }); fetchMock.getOnce(url(settings, '/memberships/nicolas%40split.io'), { status: 200, body: membershipsNicolasMock }); - fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-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?s=1.2&users=${encodeURIComponent(userKey)}`), { status: 200, body: authPushBadToken }); - fetchMock.getOnce(url(settings, `/v2/auth?s=1.2&users=${encodeURIComponent(userKey)}`), { throws: new TypeError('Network error') }, { delay: 100 }); + fetchMock.getOnce(url(settings, `/v2/auth?s=1.3&users=${encodeURIComponent(userKey)}`), { status: 200, body: authPushBadToken }); + fetchMock.getOnce(url(settings, `/v2/auth?s=1.3&users=${encodeURIComponent(userKey)}`), { throws: new TypeError('Network error') }, { delay: 100 }); fetchMock.get({ url: url(settings, '/memberships/nicolas%40split.io'), repeat: 2 }, { status: 200, body: membershipsNicolasMock }); - fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=-1'), { status: 200, body: splitChangesMock1 }); - fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=1457552620999'), { status: 200, body: splitChangesMock2 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), { 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 8e22592c9..24634a49e 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?s=1.2&since=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), { status: 200, body: splitChangesMock1 }); fetchMock.getOnce(url(settings, '/memberships/nicolas%40split.io'), { status: 200, body: membershipsNicolasMock1 }); // first auth - fetchMock.getOnce(url(settings, `/v2/auth?s=1.2&users=${encodeURIComponent(userKey)}`), function (url, opts) { + fetchMock.getOnce(url(settings, `/v2/auth?s=1.3&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?s=1.2&since=1457552620999'), { status: 200, body: splitChangesMock2 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), { status: 200, body: splitChangesMock2 }); fetchMock.getOnce(url(settings, '/memberships/nicolas%40split.io'), { status: 200, body: membershipsNicolasMock1 }); // re-auth due to refresh token, with connDelay of 0.5 seconds - fetchMock.getOnce(url(settings, `/v2/auth?s=1.2&users=${encodeURIComponent(userKey)}`), function (url, opts) { + fetchMock.getOnce(url(settings, `/v2/auth?s=1.3&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,15 +100,15 @@ export function testRefreshToken(fetchMock, assert) { }); // sync after SSE reopened - fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), 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 } }; + return { status: 200, body: { ff: { d: [], s: 1457552620999, t: 1457552620999 } } }; }); fetchMock.getOnce(url(settings, '/memberships/nicolas%40split.io'), { status: 200, body: membershipsNicolasMock1 }); // second re-auth due to refresh token, this time responding with pushEnabled false - fetchMock.getOnce(url(settings, `/v2/auth?s=1.2&users=${encodeURIComponent(userKey)}`), function (url, opts) { + fetchMock.getOnce(url(settings, `/v2/auth?s=1.3&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?s=1.2&since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), 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 db0374aa7..88472f643 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?s=1.2&users=${encodeURIComponent(userKey)}&users=${encodeURIComponent(otherUserKeySync)}`), function (url, opts) { + fetchMock.getOnce(url(settings, `/v2/auth?s=1.3&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 memberships sync - fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), { status: 200, body: splitChangesMock1 }); fetchMock.getOnce(url(settings, '/memberships/nicolas%40split.io'), { status: 200, body: membershipsNicolasMock1 }); fetchMock.get({ url: url(settings, '/memberships/marcio%40split.io'), repeat: 3 }, { status: 200, body: membershipsMarcio }); // split and segment sync after SSE opened - fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), 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, '/memberships/nicolas%40split.io'), { status: 200, body: membershipsNicolasMock1 }); // fetch due to SPLIT_UPDATE event - fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=1457552620999'), { status: 200, body: splitChangesMock2 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), { 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?s=1.2&since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), 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?s=1.2&since=1457552649999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552649999&rbSince=-1'), 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 + return { status: 200, body: { ff: { d: [], s: 1457552649999, t: 1457552649999 } } }; // 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?s=1.2&since=1457552649999'), { throws: new TypeError('Network error') }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552649999&rbSince=-1'), { throws: new TypeError('Network error') }); // second fetch retry for SPLIT_KILL event - fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=1457552649999'), { status: 200, body: '{ "since": 1457552620999, "til' }); // invalid JSON response + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552649999&rbSince=-1'), { status: 200, body: '{ "since": 1457552620999, "til' }); // invalid JSON response // third fetch retry for SPLIT_KILL event - fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=1457552649999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552649999&rbSince=-1'), 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 4086050fd..6ae6fca48 100644 --- a/src/__tests__/browserSuites/push-synchronization.spec.js +++ b/src/__tests__/browserSuites/push-synchronization.spec.js @@ -247,7 +247,7 @@ export function testSynchronization(fetchMock, assert) { // initial auth let authParams = `users=${encodeURIComponent(userKey)}`; - fetchMock.getOnce(url(settings, `/v2/auth?s=1.2&${authParams}`), function (url, opts) { + fetchMock.getOnce(url(settings, `/v2/auth?s=1.3&${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 }; @@ -255,7 +255,7 @@ export function testSynchronization(fetchMock, assert) { // reauth due to new client authParams += `&users=${encodeURIComponent(otherUserKey)}`; - fetchMock.getOnce(url(settings, `/v2/auth?s=1.2&${authParams}`), function (url, opts) { + fetchMock.getOnce(url(settings, `/v2/auth?s=1.3&${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 }; @@ -263,10 +263,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?s=1.2&${authParams}`), { status: 200, body: authPushEnabledNicolasAndMarcio }); + fetchMock.getOnce(url(settings, `/v2/auth?s=1.3&${authParams}`), { status: 200, body: authPushEnabledNicolasAndMarcio }); // initial sync - fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=-1'), function (url, opts) { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-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'); @@ -278,7 +278,7 @@ export function testSynchronization(fetchMock, assert) { }); // sync all after SSE opened - fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=1457552620999'), function (url, opts) { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), 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'); @@ -290,13 +290,13 @@ export function testSynchronization(fetchMock, assert) { }); // fetch due to SPLIT_UPDATE event - fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=1457552620999'), function (url, opts) { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), function (url, opts) { if (!hasNoCacheHeader(opts)) assert.fail('request must include `Cache-Control` header'); return { status: 200, body: splitChangesMock3 }; }); // fetch due to SPLIT_KILL event - fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=1457552649999'), function (url, opts) { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552649999&rbSince=-1'), 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 }; @@ -309,11 +309,11 @@ export function testSynchronization(fetchMock, assert) { }); // sync all after second SSE opened - fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=1457552650000'), function (url, opts) { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552650000&rbSince=-1'), 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'); - return { status: 200, body: { splits: [], since: 1457552650000, till: 1457552650000 } }; + return { status: 200, body: { ff: { d: [], s: 1457552650000, t: 1457552650000 } } }; }); fetchMock.get({ url: url(settings, '/memberships/nicolas%40split.io'), repeat: 2 }, function (url, opts) { if (hasNoCacheHeader(opts)) assert.fail('request must not include `Cache-Control` header'); @@ -337,7 +337,7 @@ export function testSynchronization(fetchMock, assert) { fetchMock.getOnce(url(settings, '/memberships/nicolas%40split.io'), { status: 200, body: { ms: { k: [{ n: 'developers' }, { n: 'engineers' }] }, ls: { k: [{ n: 'employees' }, { n: 'splitters' }], cn: 1457552650000 } } }); // target changeNumber // initial fetch of memberships for other clients + sync all after third SSE opened + 3 unbounded fetch for MEMBERSHIPS_MS_UPDATE + 1 unbounded fetch for MEMBERSHIPS_LS_UPDATE - fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=1457552650000'), { status: 200, body: { splits: [], since: 1457552650000, till: 1457552650000 } }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552650000&rbSince=-1'), { status: 200, body: { ff: { d: [], s: 1457552650000, t: 1457552650000 } } }); fetchMock.get({ url: url(settings, '/memberships/key1'), repeat: 6 }, { status: 200, body: { ms: {} } }); fetchMock.get({ url: url(settings, '/memberships/key3'), repeat: 6 }, { status: 200, body: { ms: { k: [{ n: 'splitters' }] } } }); fetchMock.get({ url: url(settings, `/memberships/${bitmapTrueKey}`), repeat: 5 }, { status: 200, body: { ms: { k: [] } } }); diff --git a/src/__tests__/browserSuites/readiness.spec.js b/src/__tests__/browserSuites/readiness.spec.js index f81fafccd..fa7d7ad85 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?s=1.2&since=-1', function () { + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', function () { return new Promise((res) => { setTimeout(() => { res({ status: 200, body: splitChangesMock1, headers: {} }); }, requestTimeoutBeforeReady * 1000 + 50); }); }); fetchMock.get(testUrls.sdk + '/memberships/nicolas%40split.io', function () { return new Promise((res) => { setTimeout(() => { res({ status: 200, body: membershipsNicolas, headers: {} }); }, requestTimeoutBeforeReady * 1000 - 50); }); }); - fetchMock.get(testUrls.sdk + '/splitChanges?s=1.2&since=1457552620999', { status: 200, body: splitChangesMock2 }); + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.3&since=1457552620999&rbSince=-1', { 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?s=1.2&since=-1', function () { + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', function () { return new Promise((res) => { setTimeout(() => { res({ status: 200, body: splitChangesMock1, headers: {} }); }, requestTimeoutBeforeReady * 1000 - 50); }); }); fetchMock.get(testUrls.sdk + '/memberships/nicolas%40split.io', function () { return new Promise((res) => { setTimeout(() => { res({ status: 200, body: membershipsNicolas, headers: {} }); }, requestTimeoutBeforeReady * 1000 + 50); }); }); - fetchMock.get(testUrls.sdk + '/splitChanges?s=1.2&since=1457552620999', { status: 200, body: splitChangesMock2 }); + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.3&since=1457552620999&rbSince=-1', { 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?s=1.2&since=-1', function () { + fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', function () { return new Promise((res) => { setTimeout(() => { res({ status: 200, body: splitChangesMock1, headers: {} }); }, requestTimeoutBeforeReady * 1000 + 50); }); }); - fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.2&since=-1', function () { + fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-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 + '/memberships/nicolas%40split.io', function () { return new Promise((res) => { setTimeout(() => { res({ status: 200, body: membershipsNicolas, headers: {} }); }, requestTimeoutBeforeReady * 1000 - 50); }); }); - fetchMock.get(testUrls.sdk + '/splitChanges?s=1.2&since=1457552620999', { status: 200, body: splitChangesMock2 }); + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.3&since=1457552620999&rbSince=-1', { 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: { ms: {} } }); }, membershipsEndpointDelay); }); }); // Now mock the no more updates state - fetchMock.get(testUrls.sdk + '/splitChanges?s=1.2&since=1457552669999', { status: 200, body: { splits: [], since: 1457552669999, till: 1457552669999 } }); + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.3&since=1457552669999&rbSince=-1', { status: 200, body: { ff: { d: [], s: 1457552669999, t: 1457552669999 } } }); if (startWithSegments) { // Adjust since and till so the order is inverted. - fetchMock.get(testUrls.sdk + '/splitChanges?s=1.2&since=-1', { status: 200, body: splitChangesStartWithSegmentsMock }); - fetchMock.get(testUrls.sdk + '/splitChanges?s=1.2&since=1457552620999', { status: 200, body: { ...splitChangesUpdateWithoutSegmentsMock, since: 1457552620999, till: 1457552649999 } }); - fetchMock.get(testUrls.sdk + '/splitChanges?s=1.2&since=1457552649999', { status: 200, body: { ...splitChangesUpdateWithSegmentsMock, since: 1457552649999, till: 1457552669999 } }); + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', { status: 200, body: splitChangesStartWithSegmentsMock }); + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.3&since=1457552620999&rbSince=-1', { status: 200, body: { ff: { ...splitChangesUpdateWithoutSegmentsMock.ff, s: 1457552620999, t: 1457552649999 } } }); + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.3&since=1457552649999&rbSince=-1', { status: 200, body: { ff: { ...splitChangesUpdateWithSegmentsMock.ff, s: 1457552649999, t: 1457552669999 } } }); } else { - fetchMock.get(testUrls.sdk + '/splitChanges?s=1.2&since=-1', { status: 200, body: splitChangesStartWithoutSegmentsMock }); - fetchMock.get(testUrls.sdk + '/splitChanges?s=1.2&since=1457552620999', { status: 200, body: splitChangesUpdateWithSegmentsMock }); - fetchMock.get(testUrls.sdk + '/splitChanges?s=1.2&since=1457552649999', { status: 200, body: splitChangesUpdateWithoutSegmentsMock }); + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', { status: 200, body: splitChangesStartWithoutSegmentsMock }); + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.3&since=1457552620999&rbSince=-1', { status: 200, body: splitChangesUpdateWithSegmentsMock }); + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.3&since=1457552649999&rbSince=-1', { status: 200, body: splitChangesUpdateWithoutSegmentsMock }); } return () => membershipsHits; @@ -269,7 +269,7 @@ export default function (fetchMock, assert) { }); setTimeout(() => { - t.equal(getMembershipsHits(), 1 * CLIENTS_COUNT -1, 'memberships should had been hit once per client on the first attempt (excluding client3), but it stopped syncing afterwards.'); + t.equal(getMembershipsHits(), 1 * CLIENTS_COUNT - 1, 'memberships should had been hit once per client on the first attempt (excluding client3), but it stopped syncing afterwards.'); }, 2500); // Now we will wait until it picks up Splits, using the SDK_UPDATE event. Features are refreshed every 3s, but segments every 1s. client.once(client.Event.SDK_UPDATE, () => { @@ -534,8 +534,8 @@ export default function (fetchMock, assert) { const getMembershipsHits = mockForSegmentsPauseTest(testUrls, false); // I'm having the first update of Splits come with segments. In this scenario it'll wait for memberships to download before being ready. - fetchMock.get({ url: testUrls.sdk + '/splitChanges?s=1.2&since=1457552669999', overwriteRoutes: true }, { status: 200, body: { ...splitChangesUpdateWithSegmentsMock, since: 1457552669999, till: 1457552679999 } }); - fetchMock.get(testUrls.sdk + '/splitChanges?s=1.2&since=1457552679999', { status: 200, body: { splits: [], since: 1457552679999, till: 1457552679999 } }); + fetchMock.get({ url: testUrls.sdk + '/splitChanges?s=1.3&since=1457552669999&rbSince=-1', overwriteRoutes: true }, { status: 200, body: { ff: { ...splitChangesUpdateWithSegmentsMock.ff, s: 1457552669999, t: 1457552679999 } } }); + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.3&since=1457552679999&rbSince=-1', { status: 200, body: { ff: { d: [], s: 1457552679999, t: 1457552679999 } } }); const start = Date.now(); const splitio = SplitFactory({ @@ -623,8 +623,8 @@ export default function (fetchMock, assert) { }; const getMembershipsHits = mockForSegmentsPauseTest(testUrls, false); // I'm having the first update of Splits come without segments. In this scenario it'll NOT wait for memberships to download before being ready. - fetchMock.get({ url: testUrls.sdk + '/splitChanges?s=1.2&since=1457552669999', overwriteRoutes: true }, { status: 200, body: { ...splitChangesUpdateWithoutSegmentsMock, since: 1457552669999, till: 1457552679999 } }); - fetchMock.get(testUrls.sdk + '/splitChanges?s=1.2&since=1457552679999', { status: 200, body: { splits: [], since: 1457552679999, till: 1457552679999 } }); + fetchMock.get({ url: testUrls.sdk + '/splitChanges?s=1.3&since=1457552669999&rbSince=-1', overwriteRoutes: true }, { status: 200, body: { ff: { ...splitChangesUpdateWithoutSegmentsMock.ff, s: 1457552669999, t: 1457552679999 } } }); + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.3&since=1457552679999&rbSince=-1', { status: 200, body: { ff: { d: [], s: 1457552679999, t: 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 0e9cdd90f..ef1decb6a 100644 --- a/src/__tests__/browserSuites/ready-from-cache.spec.js +++ b/src/__tests__/browserSuites/ready-from-cache.spec.js @@ -1,3 +1,5 @@ +import sinon from 'sinon'; +import { nearlyEqual } from '../testUtils'; import { getStorageHash } from '@splitsoftware/splitio-commons/src/storages/KeyBuilder'; import { SplitFactory } from '../../'; @@ -5,7 +7,6 @@ import splitChangesMock1 from '../mocks/splitchanges.since.-1.json'; import splitChangesMock2 from '../mocks/splitchanges.since.1457552620999.json'; import membershipsNicolas from '../mocks/memberships.nicolas@split.io.json'; -import { nearlyEqual } from '../testUtils'; const DEFAULT_CACHE_EXPIRATION_IN_MILLIS = 864000000; // 10 days @@ -83,8 +84,8 @@ const baseConfig = { streamingEnabled: false }; -const expectedHashNullFilter = 'db8943b4'; // for SDK key '', filter query null, and flags spec version '1.2' -const expectedHashWithFilter = '7ccd6b31'; // for SDK key '', filter query '&names=p1__split,p2__split', and flags spec version '1.2' +const expectedHashNullFilter = '193e6f3f'; // for SDK key '', filter query null, and flags spec version '1.3' +const expectedHashWithFilter = '2ce5cc38'; // for SDK key '', filter query '&names=p1__split,p2__split', and flags spec version '1.3' export default function (fetchMock, assert) { @@ -94,10 +95,10 @@ export default function (fetchMock, assert) { events: 'https://events.baseurl/readyFromCacheEmpty' }; localStorage.clear(); - t.plan(3); + t.plan(4); - fetchMock.get(testUrls.sdk + '/splitChanges?s=1.2&since=-1', { status: 200, body: splitChangesMock1 }); - fetchMock.get(testUrls.sdk + '/splitChanges?s=1.2&since=1457552620999', { status: 200, body: splitChangesMock2 }); + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', { status: 200, body: splitChangesMock1 }); + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.3&since=1457552620999&rbSince=-1', { status: 200, body: splitChangesMock2 }); fetchMock.get(testUrls.sdk + '/memberships/nicolas%40split.io', { status: 200, body: membershipsNicolas }); fetchMock.get(testUrls.sdk + '/memberships/nicolas2%40split.io', { status: 200, body: { 'ms': {} } }); fetchMock.get(testUrls.sdk + '/memberships/nicolas3%40split.io', { status: 200, body: { 'ms': {} } }); @@ -123,18 +124,17 @@ export default function (fetchMock, assert) { t.end(); }); client.once(client.Event.SDK_READY_FROM_CACHE, () => { - t.fail('It should not emit SDK_READY_FROM_CACHE if there is no cache.'); - t.end(); + t.true(client.__getStatus().isReady, 'Client should emit SDK_READY_FROM_CACHE alongside SDK_READY'); }); client.on(client.Event.SDK_READY, () => { - t.pass('It should emit SDK_READY alone, since there was no cache.'); + t.true(client.__getStatus().isReadyFromCache, 'Client should emit SDK_READY and it should be ready from cache'); }); client2.on(client.Event.SDK_READY, () => { - t.pass('It should emit SDK_READY alone, since there was no cache.'); + t.true(client2.__getStatus().isReadyFromCache, 'Non-default client should emit SDK_READY and it should be ready from cache'); }); client3.on(client.Event.SDK_READY, () => { - t.pass('It should emit SDK_READY alone, since there was no cache.'); + t.true(client2.__getStatus().isReadyFromCache, 'Non-default client should emit SDK_READY and it should be ready from cache'); }); }); @@ -147,17 +147,17 @@ export default function (fetchMock, assert) { localStorage.clear(); t.plan(12 * 2 + 3); - fetchMock.get(testUrls.sdk + '/splitChanges?s=1.2&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?s=1.3&since=25&rbSince=-1', () => { + return new Promise(res => { setTimeout(() => res({ status: 200, body: { ff: { ...splitChangesMock1.ff, s: 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?s=1.2&since=1457552620999', { status: 200, body: splitChangesMock2 }); - fetchMock.get(testUrls.sdk + '/memberships/nicolas%40split.io', function () { + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.3&since=1457552620999&rbSince=-1', { status: 200, body: splitChangesMock2 }); + fetchMock.get(testUrls.sdk + '/memberships/nicolas%40split.io', () => { return new Promise(res => { setTimeout(() => res({ status: 200, body: membershipsNicolas, headers: {} }), 400); }); // First client gets segments before splits. No segment cache loading (yet) }); - fetchMock.get(testUrls.sdk + '/memberships/nicolas2%40split.io', function () { + fetchMock.get(testUrls.sdk + '/memberships/nicolas2%40split.io', () => { return new Promise(res => { setTimeout(() => res({ status: 200, body: { 'ms': {} }, headers: {} }), 700); }); // Second client gets segments after 700ms }); - fetchMock.get(testUrls.sdk + '/memberships/nicolas3%40split.io', function () { + fetchMock.get(testUrls.sdk + '/memberships/nicolas3%40split.io', () => { return new Promise(res => { setTimeout(() => res({ status: 200, body: { 'ms': {} }, headers: {} }), 1000); }); // Third client memberships will come after 1s }); fetchMock.postOnce(testUrls.events + '/testImpressions/bulk', 200); @@ -179,7 +179,6 @@ export default function (fetchMock, assert) { readyTimeout: 0.85 }, urls: testUrls, - debug: true }); const client = splitio.client(); const client2 = splitio.client('nicolas2@split.io'); @@ -247,7 +246,7 @@ export default function (fetchMock, assert) { }); }); - assert.test(t => { // Testing when we start with cached data and not expired (lastUpdate item higher than expirationTimestamp) + assert.test(t => { // Testing when we start with cached data and not expired (lastUpdate timestamp higher than default (10) expirationDays ago) const testUrls = { sdk: 'https://sdk.baseurl/readyFromCacheWithData3', events: 'https://events.baseurl/readyFromCacheWithData3' @@ -255,18 +254,18 @@ export default function (fetchMock, assert) { localStorage.clear(); t.plan(12 * 2 + 5); - fetchMock.get(testUrls.sdk + '/splitChanges?s=1.2&since=25', function () { + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.3&since=25&rbSince=-1', () => { 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. + return new Promise(res => { setTimeout(() => res({ status: 200, body: { ff: { ...splitChangesMock1.ff, s: 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?s=1.2&since=1457552620999', { status: 200, body: splitChangesMock2 }); - fetchMock.get(testUrls.sdk + '/memberships/nicolas%40split.io', function () { + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.3&since=1457552620999&rbSince=-1', { status: 200, body: splitChangesMock2 }); + fetchMock.get(testUrls.sdk + '/memberships/nicolas%40split.io', () => { return new Promise(res => { setTimeout(() => res({ status: 200, body: membershipsNicolas, headers: {} }), 400); }); // First client gets segments before splits. No segment cache loading (yet) }); - fetchMock.get(testUrls.sdk + '/memberships/nicolas2%40split.io', function () { + fetchMock.get(testUrls.sdk + '/memberships/nicolas2%40split.io', () => { return new Promise(res => { setTimeout(() => res({ status: 200, body: { 'ms': {} }, headers: {} }), 700); }); // Second client gets segments after 700ms }); - fetchMock.get(testUrls.sdk + '/memberships/nicolas3%40split.io', function () { + fetchMock.get(testUrls.sdk + '/memberships/nicolas3%40split.io', () => { return new Promise(res => { setTimeout(() => res({ status: 200, body: { 'ms': {} }, headers: {} }), 1000); }); // Third client memberships will come after 1s }); fetchMock.get(testUrls.sdk + '/memberships/nicolas4%40split.io', { 'ms': {} }); @@ -290,7 +289,6 @@ export default function (fetchMock, assert) { readyTimeout: 0.85 }, urls: testUrls, - debug: true }); const client = splitio.client(); const client2 = splitio.client('nicolas2@split.io'); @@ -365,35 +363,38 @@ export default function (fetchMock, assert) { }); }); - assert.test(t => { // Testing when we start with cached data but expired (lastUpdate item lower than expirationTimestamp) + assert.test(t => { // Testing when we start with cached data but expired (lastUpdate timestamp lower than custom (1) expirationDays ago) + const CLIENT_READY_MS = 400, CLIENT2_READY_MS = 700, CLIENT3_READY_MS = 1000; + const testUrls = { sdk: 'https://sdk.baseurl/readyFromCacheWithData4', events: 'https://events.baseurl/readyFromCacheWithData4' }; localStorage.clear(); - fetchMock.get(testUrls.sdk + '/splitChanges?s=1.2&since=-1', function () { + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', () => { 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'); + t.true(nearlyEqual(parseInt(localStorage.getItem('readyFromCache_4.SPLITIO.lastClear'), 10), Date.now()), 'storage lastClear timestamp must be updated'); + t.equal(localStorage.length, 3, 'feature flags cache data must be cleaned from localStorage'); return { status: 200, body: splitChangesMock1 }; }); - fetchMock.get(testUrls.sdk + '/splitChanges?s=1.2&since=1457552620999', { status: 200, body: splitChangesMock2 }); - fetchMock.get(testUrls.sdk + '/memberships/nicolas%40split.io', function () { - return new Promise(res => { setTimeout(() => res({ status: 200, body: membershipsNicolas, headers: {} }), 400); }); // First client gets segments before splits. No segment cache loading (yet) + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.3&since=1457552620999&rbSince=-1', { status: 200, body: splitChangesMock2 }); + fetchMock.get(testUrls.sdk + '/memberships/nicolas%40split.io', () => { + return new Promise(res => { setTimeout(() => res({ status: 200, body: membershipsNicolas, headers: {} }), CLIENT_READY_MS); }); // First client gets segments before splits. No segment cache loading (yet) }); - fetchMock.get(testUrls.sdk + '/memberships/nicolas2%40split.io', function () { - return new Promise(res => { setTimeout(() => res({ status: 200, body: { 'ms': {} }, headers: {} }), 700); }); // Second client gets segments after 700ms + fetchMock.get(testUrls.sdk + '/memberships/nicolas2%40split.io', () => { + return new Promise(res => { setTimeout(() => res({ status: 200, body: { 'ms': {} }, headers: {} }), CLIENT2_READY_MS); }); // Second client gets segments after 700ms }); - fetchMock.get(testUrls.sdk + '/memberships/nicolas3%40split.io', function () { - return new Promise(res => { setTimeout(() => res({ status: 200, body: { 'ms': {} }, headers: {} }), 1000); }); // Third client memberships will come after 1s + fetchMock.get(testUrls.sdk + '/memberships/nicolas3%40split.io', () => { + return new Promise(res => { setTimeout(() => res({ status: 200, body: { 'ms': {} }, headers: {} }), CLIENT3_READY_MS); }); // Third client memberships will come after 1s }); fetchMock.postOnce(testUrls.events + '/testImpressions/bulk', 200); fetchMock.postOnce(testUrls.events + '/testImpressions/count', 200); localStorage.setItem('some_user_item', 'user_item'); localStorage.setItem('readyFromCache_4.SPLITIO.splits.till', 25); - localStorage.setItem('readyFromCache_4.SPLITIO.splits.lastUpdated', Date.now() - DEFAULT_CACHE_EXPIRATION_IN_MILLIS - 1); // -1 to ensure having an expired lastUpdated item + localStorage.setItem('readyFromCache_4.SPLITIO.splits.lastUpdated', Date.now() - DEFAULT_CACHE_EXPIRATION_IN_MILLIS / 10 - 1); // -1 to ensure having an expired lastUpdated item localStorage.setItem('readyFromCache_4.SPLITIO.split.always_on', alwaysOnSplitInverted); localStorage.setItem('readyFromCache_4.SPLITIO.hash', expectedHashNullFilter); @@ -402,13 +403,13 @@ export default function (fetchMock, assert) { ...baseConfig, storage: { type: 'LOCALSTORAGE', - prefix: 'readyFromCache_4' + prefix: 'readyFromCache_4', + expirationDays: 1, }, startup: { readyTimeout: 0.85 }, urls: testUrls, - debug: true }); const client = splitio.client(); const client2 = splitio.client('nicolas2@split.io'); @@ -423,37 +424,34 @@ export default function (fetchMock, assert) { }); client.once(client.Event.SDK_READY_FROM_CACHE, () => { - t.fail('It should not emit SDK_READY_FROM_CACHE if there is expired cache.'); - t.end(); + t.true(nearlyEqual(Date.now() - startTime, CLIENT_READY_MS), 'It should emit SDK_READY_FROM_CACHE alongside SDK_READY'); }); client2.once(client2.Event.SDK_READY_FROM_CACHE, () => { - t.fail('It should not emit SDK_READY_FROM_CACHE if there is expired cache.'); - t.end(); + t.true(nearlyEqual(Date.now() - startTime, CLIENT2_READY_MS), 'It should emit SDK_READY_FROM_CACHE alongside SDK_READY'); }); client3.once(client3.Event.SDK_READY_FROM_CACHE, () => { - t.fail('It should not emit SDK_READY_FROM_CACHE if there is expired cache.'); - t.end(); + t.true(nearlyEqual(Date.now() - startTime, CLIENT3_READY_MS), 'It should emit SDK_READY_FROM_CACHE alongside SDK_READY'); }); client.on(client.Event.SDK_READY, () => { - t.true(Date.now() - startTime >= 400, 'It should emit SDK_READY after syncing with the cloud.'); + t.true(nearlyEqual(Date.now() - startTime, CLIENT_READY_MS), 'It should emit SDK_READY after syncing with the cloud.'); t.equal(client.getTreatment('always_on'), 'on', 'It should evaluate treatments with updated data after syncing with the cloud.'); }); client.ready().then(() => { - t.true(Date.now() - startTime >= 400, 'It should resolve ready promise after syncing with the cloud.'); + t.true(nearlyEqual(Date.now() - startTime, CLIENT_READY_MS), 'It should resolve ready promise after syncing with the cloud.'); t.equal(client.getTreatment('always_on'), 'on', 'It should evaluate treatments with updated data after syncing with the cloud.'); }); client2.on(client2.Event.SDK_READY, () => { - t.true(Date.now() - startTime >= 700, 'It should emit SDK_READY after syncing with the cloud.'); + t.true(nearlyEqual(Date.now() - startTime, CLIENT2_READY_MS), 'It should emit SDK_READY after syncing with the cloud.'); t.equal(client2.getTreatment('always_on'), 'on', 'It should evaluate treatments with updated data after syncing with the cloud.'); }); client2.ready().then(() => { - t.true(Date.now() - startTime >= 700, 'It should resolve ready promise after syncing with the cloud.'); + t.true(nearlyEqual(Date.now() - startTime, CLIENT2_READY_MS), 'It should resolve ready promise after syncing with the cloud.'); t.equal(client2.getTreatment('always_on'), 'on', 'It should evaluate treatments with updated data after syncing with the cloud.'); }); client3.on(client3.Event.SDK_READY, () => { client3.ready().then(() => { - t.true(Date.now() - startTime >= 1000, 'It should resolve ready promise after syncing with the cloud.'); + t.true(nearlyEqual(Date.now() - startTime, CLIENT3_READY_MS), 'It should resolve ready promise after syncing with the cloud.'); t.equal(client3.getTreatment('always_on'), 'on', 'It should evaluate treatments with updated data after syncing with the cloud.'); // Last cb: destroy clients and check that localstorage has the expected items @@ -486,9 +484,9 @@ export default function (fetchMock, assert) { events: 'https://events.baseurl/readyFromCache_5' }; localStorage.clear(); - t.plan(7); + t.plan(8); - fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.2&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.3&since=-1&rbSince=-1&names=p1__split,p2__split', { status: 200, body: { ff: { d: [splitDeclarations.p1__split, splitDeclarations.p2__split], s: -1, t: 1457552620999 } } }, { delay: 10 }); // short delay to let emit SDK_READY_FROM_CACHE fetchMock.getOnce(testUrls.sdk + '/memberships/nicolas%40split.io', { status: 200, body: { ms: {} } }); localStorage.setItem('some_user_item', 'user_item'); @@ -507,14 +505,12 @@ export default function (fetchMock, assert) { sync: { splitFilters: [{ type: 'byName', values: ['p2__split', 'p1__split'] }, { type: 'byName', values: ['p2__split', null] }] }, - debug: true }); const client = splitio.client(); const manager = splitio.manager(); client.once(client.Event.SDK_READY_FROM_CACHE, () => { - t.fail('It should not emit SDK_READY_FROM_CACHE because localStorage is cleaned and there isn\'t cached feature flags'); - t.end(); + t.true(client.__getStatus().isReady, 'Client should emit SDK_READY_FROM_CACHE alongside SDK_READY'); }); client.once(client.Event.SDK_READY, () => { @@ -538,9 +534,9 @@ export default function (fetchMock, assert) { events: 'https://events.baseurl/readyFromCache_5B' }; localStorage.clear(); - t.plan(5); + t.plan(6); - fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.2&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.3&since=-1&rbSince=-1&names=p1__split,p2__split', { status: 200, body: { ff: { d: [splitDeclarations.p1__split, splitDeclarations.p2__split], s: -1, t: 1457552620999 } } }, { delay: 10 }); // short delay to let emit SDK_READY_FROM_CACHE fetchMock.getOnce(testUrls.sdk + '/memberships/nicolas%40split.io', { status: 200, body: { ms: {} } }); const splitio = SplitFactory({ @@ -553,14 +549,12 @@ export default function (fetchMock, assert) { sync: { splitFilters: [{ type: 'byName', values: ['p2__split', 'p1__split'] }, { type: 'byName', values: ['p2__split', null] }] }, - debug: true }); const client = splitio.client(); const manager = splitio.manager(); client.once(client.Event.SDK_READY_FROM_CACHE, () => { - t.fail('It should not emit SDK_READY_FROM_CACHE if cache is empty.'); - t.end(); + t.true(client.__getStatus().isReady, 'Client should emit SDK_READY_FROM_CACHE alongside SDK_READY'); }); client.once(client.Event.SDK_READY, () => { @@ -584,10 +578,10 @@ export default function (fetchMock, assert) { localStorage.clear(); t.plan(7); - fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.2&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.3&since=25&rbSince=-1&names=p2__split&prefixes=p1', { status: 200, body: { ff: { d: [splitDeclarations.p1__split, splitDeclarations.p2__split], s: 25, t: 1457552620999 } } }, { delay: 10 }); // short delay to let emit SDK_READY_FROM_CACHE fetchMock.getOnce(testUrls.sdk + '/memberships/nicolas%40split.io', { status: 200, body: { ms: {} } }); - const expectedHash = getStorageHash({ ...baseConfig, sync: { __splitFiltersValidation: { queryString: '&names=p2__split&prefixes=p1' }, flagSpecVersion: '1.2' } }); + const expectedHash = getStorageHash({ ...baseConfig, sync: { __splitFiltersValidation: { queryString: '&names=p2__split&prefixes=p1' }, flagSpecVersion: '1.3' } }); localStorage.setItem('some_user_item', 'user_item'); localStorage.setItem('readyFromCache_6.SPLITIO.splits.till', 25); localStorage.setItem('readyFromCache_6.SPLITIO.split.p1__split', JSON.stringify(splitDeclarations.p1__split)); @@ -604,7 +598,6 @@ export default function (fetchMock, assert) { sync: { splitFilters: [{ type: 'byName', values: [undefined, true, 'p2__split'] }, { type: 'byPrefix', values: ['p1'] }, { type: 'byName', values: ['p2__split'] }] }, - debug: true }); const client = splitio.client(); const manager = splitio.manager(); @@ -633,12 +626,12 @@ export default function (fetchMock, assert) { events: 'https://events.baseurl/readyFromCache_7' }; localStorage.clear(); - t.plan(6); + t.plan(7); - fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.2&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.3&since=-1&rbSince=-1&prefixes=p1,p2', { status: 200, body: { ff: { d: [splitDeclarations.p1__split, splitDeclarations.p2__split], s: -1, t: 1457552620999 } } }, { delay: 10 }); // short delay to let emit SDK_READY_FROM_CACHE fetchMock.getOnce(testUrls.sdk + '/memberships/nicolas%40split.io', { status: 200, body: { ms: {} } }); - const expectedHash = getStorageHash({ ...baseConfig, sync: { __splitFiltersValidation: { queryString: '&prefixes=p1,p2' }, flagSpecVersion: '1.2' } }); + const expectedHash = getStorageHash({ ...baseConfig, sync: { __splitFiltersValidation: { queryString: '&prefixes=p1,p2' }, flagSpecVersion: '1.3' } }); localStorage.setItem('some_user_item', 'user_item'); localStorage.setItem('readyFromCache_7.SPLITIO.splits.till', 25); localStorage.setItem('readyFromCache_7.SPLITIO.split.p1__split', JSON.stringify(splitDeclarations.p1__split)); @@ -650,20 +643,19 @@ export default function (fetchMock, assert) { ...baseConfig, storage: { type: 'LOCALSTORAGE', - prefix: 'readyFromCache_7' + prefix: 'readyFromCache_7', + expirationDays: 0, // invalid value, will use default (10) }, urls: testUrls, sync: { splitFilters: [{ type: 'byPrefix', values: ['p2'] }, { type: 'byPrefix', values: ['p1', ''] }, { type: '', values: [] }, {}, { type: 'byPrefix' }] }, - debug: true }); const client = splitio.client(); const manager = splitio.manager(); client.once(client.Event.SDK_READY_FROM_CACHE, () => { - t.fail('It should not emit SDK_READY_FROM_CACHE if cache has expired.'); - t.end(); + t.true(client.__getStatus().isReady, 'Client should emit SDK_READY_FROM_CACHE alongside SDK_READY'); }); client.once(client.Event.SDK_READY, () => { @@ -699,9 +691,9 @@ export default function (fetchMock, assert) { events: 'https://events.baseurl/readyFromCache_8' }; localStorage.clear(); - t.plan(7); + t.plan(8); - fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.2&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.3&since=-1&rbSince=-1', { status: 200, body: { ff: { d: [splitDeclarations.p1__split, splitDeclarations.p2__split, splitDeclarations.p3__split], s: -1, t: 1457552620999 } } }, { delay: 10 }); // short delay to let emit SDK_READY_FROM_CACHE fetchMock.getOnce(testUrls.sdk + '/memberships/nicolas%40split.io', { status: 200, body: { ms: {} } }); localStorage.setItem('some_user_item', 'user_item'); @@ -719,14 +711,12 @@ export default function (fetchMock, assert) { }, urls: testUrls, sync: syncParam, - debug: true }); const client = splitio.client(); const manager = splitio.manager(); client.once(client.Event.SDK_READY_FROM_CACHE, () => { - t.fail('It should not emit SDK_READY_FROM_CACHE because all feature flags were removed from cache since the filter query changed.'); - t.end(); + t.true(client.__getStatus().isReady, 'Client should emit SDK_READY_FROM_CACHE alongside SDK_READY'); }); client.once(client.Event.SDK_READY, () => { @@ -755,14 +745,14 @@ export default function (fetchMock, assert) { localStorage.clear(); t.plan(6); - fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.2&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.3&since=-1&rbSince=-1&names=no%20exist%20trim,no_exist,p3__split&prefixes=no%20exist%20trim,p2', { status: 200, body: { ff: { d: [splitDeclarations.p2__split, splitDeclarations.p3__split], s: -1, t: 1457552620999 } } }, { delay: 10 }); // short delay to let emit SDK_READY_FROM_CACHE fetchMock.getOnce(testUrls.sdk + '/memberships/nicolas%40split.io', { status: 200, body: { ms: {} } }); localStorage.setItem('some_user_item', 'user_item'); localStorage.setItem('readyFromCache_9.SPLITIO.splits.till', 25); localStorage.setItem('readyFromCache_9.SPLITIO.split.p1__split', JSON.stringify(splitDeclarations.p1__split)); localStorage.setItem('readyFromCache_9.SPLITIO.split.p2__split', JSON.stringify(splitDeclarations.p2__split)); - localStorage.setItem('readyFromCache_9.SPLITIO.hash', getStorageHash({ ...baseConfig, sync: { __splitFiltersValidation: { queryString: '&names=p2__split&prefixes=p1' }, flagSpecVersion: '1.2' } })); + localStorage.setItem('readyFromCache_9.SPLITIO.hash', getStorageHash({ ...baseConfig, sync: { __splitFiltersValidation: { queryString: '&names=p2__split&prefixes=p1' }, flagSpecVersion: '1.3' } })); const splitio = SplitFactory({ ...baseConfig, @@ -774,7 +764,6 @@ export default function (fetchMock, assert) { sync: { splitFilters: [{ type: 'byName', values: ['p3__split'] }, { type: 'byPrefix', values: [' p2', ' p2', ' p2', ' p2', 'no exist trim '] }, { type: 'byName', values: ['no_exist', ' no exist trim '] }] }, - debug: true }); const client = splitio.client(); const manager = splitio.manager(); @@ -787,10 +776,97 @@ export default function (fetchMock, assert) { t.equal(localStorage.getItem('readyFromCache_9.SPLITIO.splits.till'), '1457552620999', 'splits.till must correspond to the till of the last successfully fetched Splits'); t.equal(localStorage.getItem('readyFromCache_9.SPLITIO.split.p2__split'), JSON.stringify(splitDeclarations.p2__split), 'feature flag declarations must be cached'); t.equal(localStorage.getItem('readyFromCache_9.SPLITIO.split.p3__split'), JSON.stringify(splitDeclarations.p3__split), 'feature flag declarations must be cached'); - t.equal(localStorage.getItem('readyFromCache_9.SPLITIO.hash'), getStorageHash({ ...baseConfig, sync: { __splitFiltersValidation: { queryString: '&names=no%20exist%20trim,no_exist,p3__split&prefixes=no%20exist%20trim,p2' }, flagSpecVersion: '1.2' } }), 'Storage hash must correspond to the split filter query and SDK key'); + t.equal(localStorage.getItem('readyFromCache_9.SPLITIO.hash'), getStorageHash({ ...baseConfig, sync: { __splitFiltersValidation: { queryString: '&names=no%20exist%20trim,no_exist,p3__split&prefixes=no%20exist%20trim,p2' }, flagSpecVersion: '1.3' } }), 'Storage hash must correspond to the split filter query and SDK key'); t.end(); }); }); }); + assert.test(async t => { // Testing clearOnInit config true + sinon.spy(console, 'log'); + + const testUrls = { + sdk: 'https://sdk.baseurl/readyFromCache_10', + events: 'https://events.baseurl/readyFromCache_10' + }; + const clearOnInitConfig = { + ...baseConfig, + storage: { + type: 'LOCALSTORAGE', + prefix: 'readyFromCache_10', + clearOnInit: true + }, + urls: testUrls, + debug: true + }; + + // Start with cached data but without lastClear item (JS SDK below 11.1.0) -> cache cleanup + localStorage.clear(); + localStorage.setItem('readyFromCache_10.SPLITIO.splits.till', 25); + localStorage.setItem('readyFromCache_10.SPLITIO.split.p1__split', JSON.stringify(splitDeclarations.p1__split)); + localStorage.setItem('readyFromCache_10.SPLITIO.split.p2__split', JSON.stringify(splitDeclarations.p2__split)); + localStorage.setItem('readyFromCache_10.SPLITIO.hash', expectedHashNullFilter); + + fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.3&since=1457552620999&rbSince=-1', { status: 200, body: splitChangesMock2 }); + fetchMock.get(testUrls.sdk + '/memberships/nicolas%40split.io', { status: 200, body: { ms: {} } }); + + let splitio = SplitFactory(clearOnInitConfig); + let client = splitio.client(); + let manager = splitio.manager(); + + t.true(console.log.calledWithMatch('clearOnInit was set and cache was not cleared in the last 24 hours. Cleaning up cache'), 'It should log a message about cleaning up cache'); + + client.once(client.Event.SDK_READY_FROM_CACHE, () => { + t.true(client.__getStatus().isReady, 'Client should emit SDK_READY_FROM_CACHE alongside SDK_READY, because clearOnInit is true'); + }); + + await client.ready(); + t.equal(manager.names().sort().length, 33, 'active splits should be present for evaluation'); + + await splitio.destroy(); + t.equal(localStorage.getItem('readyFromCache_10.SPLITIO.splits.till'), '1457552620999', 'splits.till must correspond to the till of the last successfully fetched Splits'); + t.equal(localStorage.getItem('readyFromCache_10.SPLITIO.hash'), expectedHashNullFilter, 'Storage hash must not be changed'); + t.true(nearlyEqual(parseInt(localStorage.getItem('readyFromCache_10.SPLITIO.lastClear')), Date.now()), 'lastClear timestamp must be set'); + + // Start again with cached data and lastClear item within the last 24 hours -> no cache cleanup + console.log.resetHistory(); + fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.3&since=1457552620999&rbSince=-1', { status: 200, body: splitChangesMock2 }); + + splitio = SplitFactory(clearOnInitConfig); + client = splitio.client(); + manager = splitio.manager(); + + await new Promise(res => client.once(client.Event.SDK_READY_FROM_CACHE, res)); + + t.equal(manager.names().sort().length, 33, 'active splits should be present for evaluation'); + t.false(console.log.calledWithMatch('clearOnInit was set and cache was not cleared in the last 24 hours. Cleaning up cache'), 'It should log a message about cleaning up cache'); + + await splitio.destroy(); + + // Start again with cached data and lastClear item older than 24 hours -> cache cleanup + console.log.resetHistory(); + localStorage.setItem('readyFromCache_10.SPLITIO.lastClear', Date.now() - 25 * 60 * 60 * 1000); // 25 hours ago + fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.3&since=1457552620999&rbSince=-1', { status: 200, body: splitChangesMock2 }); + + splitio = SplitFactory(clearOnInitConfig); + client = splitio.client(); + manager = splitio.manager(); + + client.once(client.Event.SDK_READY_FROM_CACHE, () => { + t.true(client.__getStatus().isReady, 'Client should emit SDK_READY_FROM_CACHE alongside SDK_READY, because clearOnInit is true'); + }); + + await new Promise(res => client.once(client.Event.SDK_READY, res)); + + t.equal(manager.names().sort().length, 33, 'active splits should be present for evaluation'); + t.true(console.log.calledWithMatch('clearOnInit was set and cache was not cleared in the last 24 hours. Cleaning up cache'), 'It should log a message about cleaning up cache'); + + await splitio.destroy(); + + console.log.restore(); + t.end(); + }); + } diff --git a/src/__tests__/browserSuites/ready-promise.spec.js b/src/__tests__/browserSuites/ready-promise.spec.js index cb13c8107..a8176e628 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?s=1.2&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); - fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.2&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); fetchMock.get(config.urls.sdk + '/memberships/facundo%40split.io', membershipsFacundo, { 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?s=1.2&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); - fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.2&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); fetchMock.get(config.urls.sdk + '/memberships/facundo%40split.io', membershipsFacundo, { 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?s=1.2&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); - fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.2&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); fetchMock.get(config.urls.sdk + '/memberships/facundo%40split.io', membershipsFacundo, { 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?s=1.2&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); - fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.2&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); - fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.2&since=-1', splitChangesMock1, { delay: refreshTimeMillis }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', splitChangesMock1, { delay: refreshTimeMillis }); // main client endpoint configured to fetch segments before request timeout fetchMock.get(config.urls.sdk + '/memberships/facundo%40split.io', membershipsFacundo, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); - fetchMock.get(config.urls.sdk + '/splitChanges?s=1.2&since=1457552620999', { splits: [], since: 1457552620999, till: 1457552620999 }); + fetchMock.get(config.urls.sdk + '/splitChanges?s=1.3&since=1457552620999&rbSince=-1', { ff: { d: [], s: 1457552620999, t: 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 + '/memberships/nicolas%40split.io', membershipsFacundo); // 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?s=1.2&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.get(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); fetchMock.get(config.urls.sdk + '/memberships/facundo%40split.io', membershipsFacundo, { 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 /memberships take less than 'requestTimeoutBeforeReady' - fetchMock.get(config.urls.sdk + '/splitChanges?s=1.2&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); + fetchMock.get(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); fetchMock.get(config.urls.sdk + '/memberships/facundo%40split.io', membershipsFacundo, { 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?s=1.2&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); - fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.2&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); fetchMock.get(config.urls.sdk + '/memberships/facundo%40split.io', membershipsFacundo, { 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?s=1.2&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); - fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.2&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); fetchMock.get(config.urls.sdk + '/memberships/facundo%40split.io', membershipsFacundo, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); fetchMock.get(config.urls.sdk + '/memberships/nicolas%40split.io', membershipsFacundo); fetchMock.get(config.urls.sdk + '/memberships/emiliano%40split.io', membershipsFacundo); @@ -581,7 +581,7 @@ export default function readyPromiseAssertions(fetchMock, assert) { }; // /splitChanges takes longer than 'requestTimeoutBeforeReady' - fetchMock.get(config.urls.sdk + '/splitChanges?s=1.2&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.get(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); fetchMock.get(config.urls.sdk + '/memberships/facundo%40split.io', membershipsFacundo, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); fetchMock.get(config.urls.sdk + '/memberships/nicolas%40split.io', membershipsFacundo, { 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 a2d7c16a9..57ef23cab 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?s=1.2&since=-1'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-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 7f6fbc8bc..f9995f5ff 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?s=1.2&since=-1', 500); - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.2&since=-1', { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', 500); + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', { status: 200, body: splitChangesMock1 }); fetchMock.getOnce(baseUrls.sdk + '/memberships/user-key', 500); fetchMock.getOnce(baseUrls.sdk + '/memberships/user-key', { status: 200, body: { 'ms': { k: [{ n: '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 + '/memberships/nicolas%40split.io', { status: 200, body: { 'ms': {} } }); - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.2&since=-1&sets=a,c,d', { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1&sets=a,c,d', { status: 200, body: { ff: { d: [], s: 1457552620999, t: 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 7de576e6c..b82d5a733 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?s=1.2&since=-1'), { status: 200, body: splitChangesMock1 }); - fetchMock.get(url(settings, '/splitChanges?s=1.2&since=1457552620999'), { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); + fetchMock.get(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.get(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), { status: 200, body: { ff: { d: [], s: 1457552620999, t: 1457552620999 } } }); fetchMock.get(url(settings, '/memberships/facundo%40split.io'), { status: 200, body: membershipsFacundo }); // 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 bde426077..84d573f1a 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?s=1.2&since=-1'), { status: 200, body: splitChangesMock1 }); - fetchMock.get(url(settings, '/splitChanges?s=1.2&since=1457552620999'), { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); + fetchMock.get(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.get(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), { status: 200, body: { ff: { d: [], s: 1457552620999, t: 1457552620999 } } }); fetchMock.get(url(settings, '/memberships/facundo%40split.io'), { status: 200, body: membershipsFacundo }); // Init and run Split client diff --git a/src/__tests__/consumer/node_redis.spec.js b/src/__tests__/consumer/node_redis.spec.js index a2317ce48..7bc0c93b1 100644 --- a/src/__tests__/consumer/node_redis.spec.js +++ b/src/__tests__/consumer/node_redis.spec.js @@ -23,7 +23,7 @@ const NA = 'NA'; const redisPort = '6385'; -const TOTAL_RAW_IMPRESSIONS = 16; +const TOTAL_RAW_IMPRESSIONS = 17; const TOTAL_EVENTS = 2; const DEDUPED_IMPRESSIONS = 3; @@ -118,7 +118,8 @@ tape('Node.js Redis', function (t) { /** Evaluation, track and manager methods on SDK_READY */ - assert.equal(await client.getTreatment('UT_Segment_member', 'UT_IN_SEGMENT'), 'on', 'Evaluations using Redis storage should be correct.'); + assert.equal(await client.getTreatment('UT_Segment_member', 'UT_IN_SEGMENT', undefined, { properties: { /* empty properties are ignored */ } }), 'on', 'Evaluations using Redis storage should be correct.'); + assert.equal(await client.getTreatment('UT_Segment_member', 'UT_IN_SEGMENT', undefined, { properties: { some: 'value1' } }), 'on', 'Evaluations using Redis storage should be correct.'); assert.equal(await client.getTreatment('other', 'UT_IN_SEGMENT'), 'off', 'Evaluations using Redis storage should be correct.'); assert.equal(await client.getTreatment('UT_Segment_member', 'UT_NOT_IN_SEGMENT'), 'off', 'Evaluations using Redis storage should be correct.'); @@ -223,7 +224,9 @@ tape('Node.js Redis', function (t) { // this should be deduped assert.equal(await client.getTreatment('other', 'UT_IN_SEGMENT'), 'off', 'Evaluations using Redis storage should be correct.'); // this should be deduped - assert.equal(await client.getTreatment('other', 'UT_IN_SEGMENT'), 'off', 'Evaluations using Redis storage should be correct.'); + assert.equal(await client.getTreatment('other', 'UT_IN_SEGMENT', undefined, { properties: { /* empty properties are ignored */ } }), 'off', 'Evaluations using Redis storage should be correct.'); + // this should not be deduped because of properties + assert.equal(await client.getTreatment('other', 'UT_IN_SEGMENT', undefined, { properties: { some: 'value1' } }), 'off', 'Evaluations using Redis storage should be correct.'); assert.equal(await client.getTreatment('UT_Segment_member', 'UT_NOT_IN_SEGMENT'), 'off', 'Evaluations using Redis storage should be correct.'); assert.equal(await client.getTreatment('other', 'UT_NOT_IN_SEGMENT'), 'on', 'Evaluations using Redis storage should be correct.'); diff --git a/src/__tests__/destroy/browser.spec.js b/src/__tests__/destroy/browser.spec.js index 88afc744d..7652d727f 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?s=1.2&since=-1'), { status: 200, body: splitChangesMock1 }); -fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=1500492097547'), { status: 200, body: splitChangesMock2 }); +fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), { status: 200, body: splitChangesMock1 }); +fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1500492097547&rbSince=-1'), { status: 200, body: splitChangesMock2 }); fetchMock.getOnce(url(settings, '/memberships/ut1'), { status: 200, body: membershipsMock }); fetchMock.getOnce(url(settings, '/memberships/ut2'), { status: 200, body: membershipsMock }); fetchMock.getOnce(url(settings, '/memberships/ut3'), { status: 200, body: membershipsMock }); diff --git a/src/__tests__/destroy/node.spec.js b/src/__tests__/destroy/node.spec.js index ada8680d7..ce55ef506 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?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); -fetchMock.get(url(settings, '/splitChanges?s=1.1&since=1500492097547'), { status: 200, body: splitChangesMock2 }); +fetchMock.get(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), { status: 200, body: splitChangesMock1 }); +fetchMock.get(url(settings, '/splitChanges?s=1.3&since=1500492097547&rbSince=-1'), { status: 200, body: splitChangesMock2 }); fetchMock.postOnce(url(settings, '/v1/metrics/config'), 200); tape('SDK destroy for Node.js', async function (assert) { diff --git a/src/__tests__/errorCatching/browser.spec.js b/src/__tests__/errorCatching/browser.spec.js index 4ad0c8222..4e422dc10 100644 --- a/src/__tests__/errorCatching/browser.spec.js +++ b/src/__tests__/errorCatching/browser.spec.js @@ -21,13 +21,13 @@ const settings = settingsFactory({ // prepare localstorage to emit SDK_READY_FROM_CACHE localStorage.clear(); localStorage.setItem('SPLITIO.splits.till', 25); -localStorage.setItem('SPLITIO.hash', getStorageHash({ core: { authorizationKey: '' }, sync: { __splitFiltersValidation: { queryString: null }, flagSpecVersion: '1.2' } })); +localStorage.setItem('SPLITIO.hash', getStorageHash({ core: { authorizationKey: '' }, sync: { __splitFiltersValidation: { queryString: null }, flagSpecVersion: '1.3' } })); -fetchMock.get(url(settings, '/splitChanges?s=1.2&since=25'), function () { +fetchMock.get(url(settings, '/splitChanges?s=1.3&since=25&rbSince=-1'), function () { return new Promise((res) => { setTimeout(() => res({ status: 200, body: splitChangesMock1 }), 1000); }); }); -fetchMock.get(url(settings, '/splitChanges?s=1.2&since=1500492097547'), { status: 200, body: splitChangesMock2 }); -fetchMock.get(url(settings, '/splitChanges?s=1.2&since=1500492297547'), { status: 200, body: splitChangesMock3 }); +fetchMock.get(url(settings, '/splitChanges?s=1.3&since=1500492097547&rbSince=-1'), { status: 200, body: splitChangesMock2 }); +fetchMock.get(url(settings, '/splitChanges?s=1.3&since=1500492297547&rbSince=-1'), { status: 200, body: splitChangesMock3 }); fetchMock.get(url(settings, '/memberships/nico%40split.io'), { status: 200, body: membershipsMock }); fetchMock.post('*', 200); diff --git a/src/__tests__/errorCatching/node.spec.js b/src/__tests__/errorCatching/node.spec.js index b7838685a..69ba50150 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?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.get(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), { status: 200, body: splitChangesMock1 }, responseDelay); +fetchMock.get(url(settings, '/splitChanges?s=1.3&since=1500492097547&rbSince=-1'), { status: 200, body: splitChangesMock2 }, responseDelay); +fetchMock.get(url(settings, '/splitChanges?s=1.3&since=1500492297547&rbSince=-1'), { 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__/mocks/splitChanges.since.-1.till.1500492097547.json b/src/__tests__/mocks/splitChanges.since.-1.till.1500492097547.json index 3e83720e6..72c0e5c7c 100644 --- a/src/__tests__/mocks/splitChanges.since.-1.till.1500492097547.json +++ b/src/__tests__/mocks/splitChanges.since.-1.till.1500492097547.json @@ -1,53 +1,55 @@ { - "splits": [ - { - "trafficTypeName": "account", - "name": "Single_Test", - "trafficAllocation": 100, - "trafficAllocationSeed": -1012499566, - "seed": -88385793, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "changeNumber": 1492734008466, - "algo": null, - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "ff": { + "d": [ + { + "trafficTypeName": "account", + "name": "Single_Test", + "trafficAllocation": 100, + "trafficAllocationSeed": -1012499566, + "seed": -88385793, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1492734008466, + "algo": null, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, { - "keySelector": { - "trafficType": "account", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null + "treatment": "off", + "size": 0 } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 - }, - { - "treatment": "off", - "size": 0 - } - ], - "label": "in segment all" - } - ] - } - ], - "since": -1, - "till": 1500492097547 + ], + "label": "in segment all" + } + ] + } + ], + "s": -1, + "t": 1500492097547 + } } diff --git a/src/__tests__/mocks/splitChanges.since.1500492097547.json b/src/__tests__/mocks/splitChanges.since.1500492097547.json index 3b4a4789b..b1a17c824 100644 --- a/src/__tests__/mocks/splitChanges.since.1500492097547.json +++ b/src/__tests__/mocks/splitChanges.since.1500492097547.json @@ -1,5 +1,7 @@ { - "splits": [], - "since": 1500492097547, - "till": 1500492097547 + "ff": { + "d": [], + "s": 1500492097547, + "t": 1500492097547 + } } diff --git a/src/__tests__/mocks/splitChanges.since.1500492097547.till.1500492297547.json b/src/__tests__/mocks/splitChanges.since.1500492097547.till.1500492297547.json index d53768f14..5c0ce4c57 100644 --- a/src/__tests__/mocks/splitChanges.since.1500492097547.till.1500492297547.json +++ b/src/__tests__/mocks/splitChanges.since.1500492097547.till.1500492297547.json @@ -1,53 +1,55 @@ { - "splits": [ - { - "trafficTypeName": "account", - "name": "Single_Test", - "trafficAllocation": 100, - "trafficAllocationSeed": -1012499566, - "seed": -88385793, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "changeNumber": 1492734008466, - "algo": null, - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "ff": { + "d": [ + { + "trafficTypeName": "account", + "name": "Single_Test", + "trafficAllocation": 100, + "trafficAllocationSeed": -1012499566, + "seed": -88385793, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1492734008466, + "algo": null, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, { - "keySelector": { - "trafficType": "account", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null + "treatment": "off", + "size": 100 } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 0 - }, - { - "treatment": "off", - "size": 100 - } - ], - "label": "in segment all" - } - ] - } - ], - "since": 1500492097547, - "till": 1500492297547 + ], + "label": "in segment all" + } + ] + } + ], + "s": 1500492097547, + "t": 1500492297547 + } } diff --git a/src/__tests__/mocks/splitChanges.since.1500492297547.json b/src/__tests__/mocks/splitChanges.since.1500492297547.json index 7536faca2..0169ce1c4 100644 --- a/src/__tests__/mocks/splitChanges.since.1500492297547.json +++ b/src/__tests__/mocks/splitChanges.since.1500492297547.json @@ -1,5 +1,7 @@ { - "splits": [], - "since": 1500492297547, - "till": 1500492297547 + "ff": { + "d": [], + "s": 1500492297547, + "t": 1500492297547 + } } diff --git a/src/__tests__/mocks/splitchanges.real.json b/src/__tests__/mocks/splitchanges.real.json index 8b4516745..5fb9163de 100644 --- a/src/__tests__/mocks/splitchanges.real.json +++ b/src/__tests__/mocks/splitchanges.real.json @@ -1,106 +1,108 @@ { - "splits": [ - { - "trafficTypeName": "user", - "name": "real_split", - "trafficAllocation": 100, - "trafficAllocationSeed": -1757484928, - "seed": 764645059, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "on", - "changeNumber": 1550099287313, - "algo": 2, - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "ff": { + "d": [ + { + "trafficTypeName": "user", + "name": "real_split", + "trafficAllocation": 100, + "trafficAllocationSeed": -1757484928, + "seed": 764645059, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "on", + "changeNumber": 1550099287313, + "algo": 2, + "conditions": [ + { + "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": 50 + }, { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "treatment": "off", + "size": 50 } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 50 + ], + "label": "default rule" + } + ], + "configurations": {} + }, + { + "trafficTypeName": "user", + "name": "real_split_2", + "trafficAllocation": 100, + "trafficAllocationSeed": -1427479928, + "seed": 769174959, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "on", + "changeNumber": 1550099287990, + "algo": 2, + "conditions": [ + { + "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 + } + ] }, - { - "treatment": "off", - "size": 50 - } - ], - "label": "default rule" - } - ], - "configurations": {} - }, - { - "trafficTypeName": "user", - "name": "real_split_2", - "trafficAllocation": 100, - "trafficAllocationSeed": -1427479928, - "seed": 769174959, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "on", - "changeNumber": 1550099287990, - "algo": 2, - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 - }, - { - "treatment": "off", - "size": 0 - } - ], - "label": "default rule" + ], + "label": "default rule" + } + ], + "configurations": { + "on": "{\"color\":\"brown\",\"dimensions\":{\"height\":12,\"width\":14},\"text\":{\"inner\":\"click me\"}}" } - ], - "configurations": { - "on": "{\"color\":\"brown\",\"dimensions\":{\"height\":12,\"width\":14},\"text\":{\"inner\":\"click me\"}}" } - } - ], - "since": -1, - "till": 1457552620999 + ], + "s": -1, + "t": 1457552620999 + } } diff --git a/src/__tests__/mocks/splitchanges.real.updateWithSegments.json b/src/__tests__/mocks/splitchanges.real.updateWithSegments.json index 7a37877cc..99e015051 100644 --- a/src/__tests__/mocks/splitchanges.real.updateWithSegments.json +++ b/src/__tests__/mocks/splitchanges.real.updateWithSegments.json @@ -1,72 +1,74 @@ { - "splits": [ - { - "trafficTypeName": "user", - "name": "real_split", - "trafficAllocation": 100, - "trafficAllocationSeed": -1757484928, - "seed": 764645059, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "on", - "changeNumber": 1550099287313, - "algo": 2, - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "employees" + "ff": { + "d": [ + { + "trafficTypeName": "user", + "name": "real_split", + "trafficAllocation": 100, + "trafficAllocationSeed": -1757484928, + "seed": 764645059, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "on", + "changeNumber": 1550099287313, + "algo": 2, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "employees" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + { + "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 }, { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "treatment": "off", + "size": 100 } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 0 - }, - { - "treatment": "off", - "size": 100 - } - ], - "label": "default rule" - } - ], - "configurations": {} - } - ], - "since": 1457552620999, - "till": 1457552649999 + ], + "label": "default rule" + } + ], + "configurations": {} + } + ], + "s": 1457552620999, + "t": 1457552649999 + } } diff --git a/src/__tests__/mocks/splitchanges.real.updateWithoutSegments.json b/src/__tests__/mocks/splitchanges.real.updateWithoutSegments.json index 523a0b790..865bae27b 100644 --- a/src/__tests__/mocks/splitchanges.real.updateWithoutSegments.json +++ b/src/__tests__/mocks/splitchanges.real.updateWithoutSegments.json @@ -1,56 +1,57 @@ { - "splits": [ - { - "trafficTypeName": "user", - "name": "real_split", - "trafficAllocation": 100, - "trafficAllocationSeed": -1757484928, - "seed": 764645059, - "status": "ARCHIVED", - "killed": false, - "defaultTreatment": "on", - "changeNumber": 1550099287313, - "algo": 2, - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "ff": { + "d": [ + { + "trafficTypeName": "user", + "name": "real_split", + "trafficAllocation": 100, + "trafficAllocationSeed": -1757484928, + "seed": 764645059, + "status": "ARCHIVED", + "killed": false, + "defaultTreatment": "on", + "changeNumber": 1550099287313, + "algo": 2, + "conditions": [ + { + "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": 50 + }, { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "treatment": "off", + "size": 50 } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 50 - }, - { - "treatment": "off", - "size": 50 - } - ], - "label": "default rule" - } - ], - "configurations": {} - } - ], - "since": 1457552649999, - "till": 1457552669999 + ], + "label": "default rule" + } + ], + "configurations": {} + } + ], + "s": 1457552649999, + "t": 1457552669999 + } } - diff --git a/src/__tests__/mocks/splitchanges.real.withSegments.json b/src/__tests__/mocks/splitchanges.real.withSegments.json index a9d4149f4..ad766b311 100644 --- a/src/__tests__/mocks/splitchanges.real.withSegments.json +++ b/src/__tests__/mocks/splitchanges.real.withSegments.json @@ -1,123 +1,125 @@ { - "splits": [ - { - "trafficTypeName": "user", - "name": "real_split", - "trafficAllocation": 100, - "trafficAllocationSeed": -1757484928, - "seed": 764645059, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "on", - "changeNumber": 1550099287313, - "algo": 2, - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "employees" + "ff": { + "d": [ + { + "trafficTypeName": "user", + "name": "real_split", + "trafficAllocation": 100, + "trafficAllocationSeed": -1757484928, + "seed": 764645059, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "on", + "changeNumber": 1550099287313, + "algo": 2, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "employees" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + { + "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": 50 }, { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "treatment": "off", + "size": 50 } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 50 + ], + "label": "default rule" + } + ], + "configurations": {} + }, + { + "trafficTypeName": "user", + "name": "real_split_2", + "trafficAllocation": 100, + "trafficAllocationSeed": -1427479928, + "seed": 769174959, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "on", + "changeNumber": 1550099287990, + "algo": 2, + "conditions": [ + { + "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 + } + ] }, - { - "treatment": "off", - "size": 50 - } - ], - "label": "default rule" - } - ], - "configurations": {} - }, - { - "trafficTypeName": "user", - "name": "real_split_2", - "trafficAllocation": 100, - "trafficAllocationSeed": -1427479928, - "seed": 769174959, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "on", - "changeNumber": 1550099287990, - "algo": 2, - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 - }, - { - "treatment": "off", - "size": 0 - } - ], - "label": "default rule" + ], + "label": "default rule" + } + ], + "configurations": { + "on": "{\"color\":\"brown\",\"dimensions\":{\"height\":12,\"width\":14},\"text\":{\"inner\":\"click me\"}}" } - ], - "configurations": { - "on": "{\"color\":\"brown\",\"dimensions\":{\"height\":12,\"width\":14},\"text\":{\"inner\":\"click me\"}}" } - } - ], - "since": -1, - "till": 1457552620999 + ], + "s": -1, + "t": 1457552620999 + } } diff --git a/src/__tests__/mocks/splitchanges.since.-1.json b/src/__tests__/mocks/splitchanges.since.-1.json index 372b030b8..3c7422213 100644 --- a/src/__tests__/mocks/splitchanges.since.-1.json +++ b/src/__tests__/mocks/splitchanges.since.-1.json @@ -1,1552 +1,1557 @@ { - "splits": [ - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "in_large_segment", - "seed": -1984784937, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "no", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "IN_LARGE_SEGMENT", - "negate": false, - "userDefinedLargeSegmentMatcherData": { - "largeSegmentName": "harnessians" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "unaryStringMatcherData": null - } - ] - }, - "partitions": [ - { - "treatment": "yes", - "size": 100 - } - ] - }, - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "ff": { + "d": [ + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "in_large_segment", + "seed": -1984784937, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "no", + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "IN_LARGE_SEGMENT", + "negate": false, + "userDefinedLargeSegmentMatcherData": { + "largeSegmentName": "harnessians" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "unaryStringMatcherData": null + } + ] + }, + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "IN_LARGE_SEGMENT", - "negate": false, - "userDefinedLargeSegmentMatcherData": { - "largeSegmentName": "splitters" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "unaryStringMatcherData": null + "treatment": "yes", + "size": 100 } ] }, - "partitions": [ - { - "treatment": "yes", - "size": 100 - } - ] - } - ], - "configurations": {} - }, - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "qc_team", - "seed": -1984784937, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "no", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "IN_LARGE_SEGMENT", + "negate": false, + "userDefinedLargeSegmentMatcherData": { + "largeSegmentName": "splitters" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "unaryStringMatcherData": null + } + ] + }, + "partitions": [ { - "keySelector": null, - "matcherType": "WHITELIST", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist": [ - "tia@split.io", - "trevor@split.io" - ] - }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null + "treatment": "yes", + "size": 100 } ] - }, - "partitions": [ - { - "treatment": "yes", - "size": 100 - } - ] - }, - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + } + ], + "configurations": {} + }, + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "qc_team", + "seed": -1984784937, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "no", + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "tia@split.io", + "trevor@split.io" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] + }, + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "employees" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "unaryStringMatcherData": null + "treatment": "yes", + "size": 100 } ] }, - "partitions": [ - { - "treatment": "yes", - "size": 0 + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "employees" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "unaryStringMatcherData": null + } + ] }, - { - "treatment": "no", - "size": 100 - } - ] - } - ], - "configurations": {} - }, - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "whitelist", - "seed": 104328192, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "not_allowed", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "partitions": [ { - "keySelector": null, - "matcherType": "WHITELIST", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist": [ - "facundo@split.io" - ] - }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null - } - ] - }, - "partitions": [ - { - "treatment": "allowed", - "size": 100 - } - ] - }, - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "treatment": "yes", + "size": 0 + }, { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null + "treatment": "no", + "size": 100 } ] - }, - "partitions": [ - { - "treatment": "allowed", - "size": 0 + } + ], + "configurations": {} + }, + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "whitelist", + "seed": 104328192, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "not_allowed", + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "facundo@split.io" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] }, - { - "treatment": "not_allowed", - "size": 100 - } - ] - } - ] - }, - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "blacklist", - "seed": -1840071133, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "allowed", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "splitters" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null + "treatment": "allowed", + "size": 100 } ] }, - "partitions": [ - { - "treatment": "allowed", - "size": 0 + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] }, - { - "treatment": "not_allowed", - "size": 100 - } - ] - } - ] - }, - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "splitters", - "seed": 1061596048, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "splitters" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null - } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 - }, - { - "treatment": "off", - "size": 0 - } - ] - } - ] - }, - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "developers", - "seed": 1461592538, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "treatment": "allowed", + "size": 0 + }, { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "developers" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null + "treatment": "not_allowed", + "size": 100 } ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 + } + ] + }, + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "blacklist", + "seed": -1840071133, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "allowed", + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "splitters" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] }, - { - "treatment": "off", - "size": 0 - } - ] - } - ] - }, - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "employees_between_21_and_50_and_chrome", - "seed": -1073105888, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "splitters" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null + "treatment": "allowed", + "size": 0 }, { - "keySelector": { - "trafficType": "user", - "attribute": "age" - }, - "matcherType": "BETWEEN", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": { - "dataType": null, - "start": 21, - "end": 50 + "treatment": "not_allowed", + "size": 100 + } + ] + } + ] + }, + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "splitters", + "seed": 1061596048, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "splitters" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 }, { - "keySelector": { - "trafficType": "user", - "attribute": "agent" - }, - "matcherType": "WHITELIST", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist": [ - "chrome" - ] - }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null + "treatment": "off", + "size": 0 } ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 - } - ] - } - ] - }, - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "user_attr_gte_10_and_user_attr2_is_not_foo", - "seed": 481329258, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + } + ] + }, + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "developers", + "seed": 1461592538, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "developers" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] + }, + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": "attr" - }, - "matcherType": "GREATER_THAN_OR_EQUAL_TO", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": { - "dataType": null, - "value": 10 - }, - "betweenMatcherData": null + "treatment": "on", + "size": 100 }, { - "keySelector": { - "trafficType": "user", - "attribute": "attr2" - }, - "matcherType": "WHITELIST", - "negate": true, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist": [ - "foo" - ] - }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null + "treatment": "off", + "size": 0 } ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 - } - ] - } - ] - }, - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "user_account_in_whitelist", - "seed": -2122983143, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + } + ] + }, + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "employees_between_21_and_50_and_chrome", + "seed": -1073105888, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "splitters" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + }, + { + "keySelector": { + "trafficType": "user", + "attribute": "age" + }, + "matcherType": "BETWEEN", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": { + "dataType": null, + "start": 21, + "end": 50 + } + }, + { + "keySelector": { + "trafficType": "user", + "attribute": "agent" + }, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "chrome" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] + }, + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": "account" - }, - "matcherType": "WHITELIST", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist": [ - "key_1@split.io", - "key_2@split.io", - "key_3@split.io", - "key_4@split.io", - "key_5@split.io" - ] - }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null + "treatment": "on", + "size": 100 } ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 - } - ] - } - ] - }, - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "user_account_in_segment_employees", - "seed": 1107027749, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + } + ] + }, + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "user_attr_gte_10_and_user_attr2_is_not_foo", + "seed": 481329258, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "attr" + }, + "matcherType": "GREATER_THAN_OR_EQUAL_TO", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": { + "dataType": null, + "value": 10 + }, + "betweenMatcherData": null + }, + { + "keySelector": { + "trafficType": "user", + "attribute": "attr2" + }, + "matcherType": "WHITELIST", + "negate": true, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "foo" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] + }, + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": "account" - }, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "employees" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null + "treatment": "on", + "size": 100 } ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 - } - ] - } - ] - }, - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "user_account_in_segment_all", - "seed": -790401804, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + } + ] + }, + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "user_account_in_whitelist", + "seed": -2122983143, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "account" + }, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "key_1@split.io", + "key_2@split.io", + "key_3@split.io", + "key_4@split.io", + "key_5@split.io" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] + }, + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": "account" - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null + "treatment": "on", + "size": 100 } ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 - } - ] - } - ] - }, - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "user_account_in_segment_all_50_50", - "seed": 968686, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + } + ] + }, + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "user_account_in_segment_employees", + "seed": 1107027749, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "account" + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "employees" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] + }, + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null + "treatment": "on", + "size": 100 } ] - }, - "partitions": [ - { - "treatment": "lower", - "size": 50 + } + ] + }, + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "user_account_in_segment_all", + "seed": -790401804, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "account" + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] }, - { - "treatment": "higher", - "size": 50 - } - ] - } - ] - },{ - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "user_account_in_segment_all_50_50_2", - "seed": 96868, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null + "treatment": "on", + "size": 100 } ] - }, - "partitions": [ - { - "treatment": "lower", - "size": 50 + } + ] + }, + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "user_account_in_segment_all_50_50", + "seed": 968686, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] }, - { - "treatment": "higher", - "size": 50 - } - ] - } - ] - }, - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "user_attr_btw_datetime_1458240947021_and_1458246884077", - "seed": 622265394, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": "attr" - }, - "matcherType": "BETWEEN", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": { - "dataType": "DATETIME", - "start": 1458240947021, - "end": 1458246884077 - } + "treatment": "lower", + "size": 50 + }, + { + "treatment": "higher", + "size": 50 } ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 - } - ] - } - ] - }, - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "user_attr_btw_number_10_and_20", - "seed": 1870594950, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { - "trafficType": "user", - "attribute": "attr" - }, - "matcherType": "BETWEEN", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": { - "dataType": "NUMBER", - "start": 10, - "end": 20 + } + ] + }, + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "user_account_in_segment_all_50_50_2", + "seed": 96868, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null } + ] + }, + "partitions": [ + { + "treatment": "lower", + "size": 50 + }, + { + "treatment": "higher", + "size": 50 } ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 - } - ] - } - ] - }, - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "user_attr_btw_10_and_20", - "seed": -976719381, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { - "trafficType": "user", - "attribute": "attr" - }, - "matcherType": "BETWEEN", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": { - "dataType": null, - "start": 10, - "end": 20 + } + ] + }, + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "user_attr_btw_datetime_1458240947021_and_1458246884077", + "seed": 622265394, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "attr" + }, + "matcherType": "BETWEEN", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": { + "dataType": "DATETIME", + "start": 1458240947021, + "end": 1458246884077 + } } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 } ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 - } - ] - } - ] - }, - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "user_attr_lte_datetime_1458240947021", - "seed": 455590578, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + } + ] + }, + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "user_attr_btw_number_10_and_20", + "seed": 1870594950, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "attr" + }, + "matcherType": "BETWEEN", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": { + "dataType": "NUMBER", + "start": 10, + "end": 20 + } + } + ] + }, + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": "attr" - }, - "matcherType": "LESS_THAN_OR_EQUAL_TO", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": { - "dataType": "DATETIME", - "value": 1458240947021 - }, - "betweenMatcherData": null + "treatment": "on", + "size": 100 } ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 - } - ] - } - ] - }, - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "user_attr_lte_number_10", - "seed": 1895728928, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + } + ] + }, + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "user_attr_btw_10_and_20", + "seed": -976719381, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "attr" + }, + "matcherType": "BETWEEN", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": { + "dataType": null, + "start": 10, + "end": 20 + } + } + ] + }, + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": "attr" - }, - "matcherType": "LESS_THAN_OR_EQUAL_TO", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": { - "dataType": "NUMBER", - "value": 10 - }, - "betweenMatcherData": null + "treatment": "on", + "size": 100 } ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 - } - ] - } - ] - }, - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "user_attr_lte_10", - "seed": 773481472, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + } + ] + }, + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "user_attr_lte_datetime_1458240947021", + "seed": 455590578, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "attr" + }, + "matcherType": "LESS_THAN_OR_EQUAL_TO", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": { + "dataType": "DATETIME", + "value": 1458240947021 + }, + "betweenMatcherData": null + } + ] + }, + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": "attr" - }, - "matcherType": "LESS_THAN_OR_EQUAL_TO", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": { - "dataType": null, - "value": 10 - }, - "betweenMatcherData": null + "treatment": "on", + "size": 100 } ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 - } - ] - } - ] - }, - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "user_attr_gte_datetime_1458240947021", - "seed": 582849993, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + } + ] + }, + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "user_attr_lte_number_10", + "seed": 1895728928, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "attr" + }, + "matcherType": "LESS_THAN_OR_EQUAL_TO", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": { + "dataType": "NUMBER", + "value": 10 + }, + "betweenMatcherData": null + } + ] + }, + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": "attr" - }, - "matcherType": "GREATER_THAN_OR_EQUAL_TO", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": { - "dataType": "DATETIME", - "value": 1458240947021 - }, - "betweenMatcherData": null + "treatment": "on", + "size": 100 } ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 - } - ] - } - ] - }, - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "user_attr_gte_number_10", - "seed": -1710564342, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + } + ] + }, + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "user_attr_lte_10", + "seed": 773481472, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "attr" + }, + "matcherType": "LESS_THAN_OR_EQUAL_TO", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": { + "dataType": null, + "value": 10 + }, + "betweenMatcherData": null + } + ] + }, + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": "attr" - }, - "matcherType": "GREATER_THAN_OR_EQUAL_TO", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": { - "dataType": "NUMBER", - "value": 10 - }, - "betweenMatcherData": null + "treatment": "on", + "size": 100 } ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 - } - ] - } - ] - }, - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "user_attr_gte_10", - "seed": 2016359772, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + } + ] + }, + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "user_attr_gte_datetime_1458240947021", + "seed": 582849993, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "attr" + }, + "matcherType": "GREATER_THAN_OR_EQUAL_TO", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": { + "dataType": "DATETIME", + "value": 1458240947021 + }, + "betweenMatcherData": null + } + ] + }, + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": "attr" - }, - "matcherType": "GREATER_THAN_OR_EQUAL_TO", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": { - "dataType": null, - "value": 10 - }, - "betweenMatcherData": null + "treatment": "on", + "size": 100 } ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 - } - ] - } - ] - }, - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "user_attr_eq_datetime_1458240947021", - "seed": -1927656676, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + } + ] + }, + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "user_attr_gte_number_10", + "seed": -1710564342, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "attr" + }, + "matcherType": "GREATER_THAN_OR_EQUAL_TO", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": { + "dataType": "NUMBER", + "value": 10 + }, + "betweenMatcherData": null + } + ] + }, + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": "attr" - }, - "matcherType": "EQUAL_TO", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": { - "dataType": "DATETIME", - "value": 1458240947021 - }, - "betweenMatcherData": null + "treatment": "on", + "size": 100 } ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 - } - ] - } - ] - }, - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "user_attr_eq_number_ten", - "seed": 643770303, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + } + ] + }, + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "user_attr_gte_10", + "seed": 2016359772, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "attr" + }, + "matcherType": "GREATER_THAN_OR_EQUAL_TO", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": { + "dataType": null, + "value": 10 + }, + "betweenMatcherData": null + } + ] + }, + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": "attr" - }, - "matcherType": "EQUAL_TO", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": { - "dataType": "NUMBER", - "value": 10 - }, - "betweenMatcherData": null + "treatment": "on", + "size": 100 } ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 - } - ] - } - ] - }, - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "user_attr_eq_ten", - "seed": 1276593955, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + } + ] + }, + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "user_attr_eq_datetime_1458240947021", + "seed": -1927656676, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "attr" + }, + "matcherType": "EQUAL_TO", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": { + "dataType": "DATETIME", + "value": 1458240947021 + }, + "betweenMatcherData": null + } + ] + }, + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": "attr" - }, - "matcherType": "EQUAL_TO", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": { - "dataType": null, - "value": 10 - }, - "betweenMatcherData": null + "treatment": "on", + "size": 100 } ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 - } - ] - } - ] - }, - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "hierarchical_dep_always_on", - "seed": -790396804, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + } + ] + }, + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "user_attr_eq_number_ten", + "seed": 643770303, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "attr" + }, + "matcherType": "EQUAL_TO", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": { + "dataType": "NUMBER", + "value": 10 + }, + "betweenMatcherData": null + } + ] + }, + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null + "treatment": "on", + "size": 100 } ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 - } - ], - "label": "hierarchical dependency always on label" - } - ] - }, - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "hierarchical_dep_hierarchical", - "seed": 1276793945, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + } + ] + }, + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "user_attr_eq_ten", + "seed": 1276593955, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "attr" + }, + "matcherType": "EQUAL_TO", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": { + "dataType": null, + "value": 10 + }, + "betweenMatcherData": null + } + ] + }, + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "IN_SPLIT_TREATMENT", - "negate": false, - "dependencyMatcherData": { - "split": "hierarchical_dep_always_on", - "treatments": [ - "on", "partial" - ] - }, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null + "treatment": "on", + "size": 100 } ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 - } - ], - "label": "hierarchical dependency label" - } - ], - "configurations": {} - }, - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "hierarchical_splits_test", - "impressionsDisabled": false, - "seed": 1276793945, - "changeNumber": 2828282828, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + } + ] + }, + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "hierarchical_dep_always_on", + "seed": -790396804, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] + }, + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "IN_SPLIT_TREATMENT", - "negate": false, - "dependencyMatcherData": { - "split": "hierarchical_dep_hierarchical", - "treatments": [ - "on", "partial" - ] - }, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null + "treatment": "on", + "size": 100 } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 - } - ], - "label": "expected label" - } - ] - }, - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "always_on", - "seed": -790401604, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + ], + "label": "hierarchical dependency always on label" + } + ] + }, + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "hierarchical_dep_hierarchical", + "seed": 1276793945, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "IN_SPLIT_TREATMENT", + "negate": false, + "dependencyMatcherData": { + "split": "hierarchical_dep_always_on", + "treatments": [ + "on", + "partial" + ] + }, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] + }, + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null + "treatment": "on", + "size": 100 } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 - } - ] - } - ] - }, - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "always_off", - "seed": -790401604, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + ], + "label": "hierarchical dependency label" + } + ], + "configurations": {} + }, + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "hierarchical_splits_test", + "impressionsDisabled": false, + "seed": 1276793945, + "changeNumber": 2828282828, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "IN_SPLIT_TREATMENT", + "negate": false, + "dependencyMatcherData": { + "split": "hierarchical_dep_hierarchical", + "treatments": [ + "on", + "partial" + ] + }, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] + }, + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null + "treatment": "on", + "size": 100 + } + ], + "label": "expected label" + } + ] + }, + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "always_on", + "seed": -790401604, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 } ] - }, - "partitions": [ - { - "treatment": "off", - "size": 100 - } - ] - } - ] - }, - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "always_on_track_impressions_false", - "impressionsDisabled": true, - "seed": -790401604, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + } + ] + }, + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "always_off", + "seed": -790401604, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] + }, + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null + "treatment": "off", + "size": 100 } ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 - } - ] - } - ] - }, - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "ta_bucket1_test", - "algo": 2, - "seed": -1222652054, - "trafficAllocation": 1, - "trafficAllocationSeed": -1667452163, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "default_treatment", - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + } + ] + }, + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "always_on_impressions_disabled_true", + "impressionsDisabled": true, + "seed": -790401604, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] + }, + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null + "treatment": "on", + "size": 100 } ] - }, - "partitions": [ - { - "treatment": "rollout_treatment", - "size": 100 - } - ] - } - ] - }, - { - "trafficTypeName": null, - "name": "split_with_config", - "impressionsDisabled": false, - "algo": 2, - "seed": -1222652064, - "trafficAllocation": 100, - "changeNumber": 828282828282, - "trafficAllocationSeed": -1667492163, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "o.n", - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + } + ] + }, + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "ta_bucket1_test", + "algo": 2, + "seed": -1222652054, + "trafficAllocation": 1, + "trafficAllocationSeed": -1667452163, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "default_treatment", + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] + }, + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": "group" - }, - "matcherType": "WHITELIST", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist": [ - "value_without_config" - ] - }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null + "treatment": "rollout_treatment", + "size": 100 } ] - }, - "partitions": [ - { - "treatment": "o.n", - "size": 0 + } + ] + }, + { + "trafficTypeName": null, + "name": "split_with_config", + "impressionsDisabled": false, + "algo": 2, + "seed": -1222652064, + "trafficAllocation": 100, + "changeNumber": 828282828282, + "trafficAllocationSeed": -1667492163, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "o.n", + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "group" + }, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "value_without_config" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] }, - { - "treatment": "off", - "size": 100 - } - ] - }, - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null + "treatment": "o.n", + "size": 0 + }, + { + "treatment": "off", + "size": 100 } ] }, - "partitions": [ - { - "treatment": "o.n", - "size": 100 + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] }, - { - "treatment": "off", - "size": 0 - } - ], - "label": "another expected label" + "partitions": [ + { + "treatment": "o.n", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + } + ], + "label": "another expected label" + } + ], + "configurations": { + "o.n": "{\"color\":\"brown\",\"dimensions\":{\"height\":12,\"width\":14},\"text\":{\"inner\":\"click me\"}}" } - ], - "configurations": { - "o.n": "{\"color\":\"brown\",\"dimensions\":{\"height\":12,\"width\":14},\"text\":{\"inner\":\"click me\"}}" } - } - ], - "since": -1, - "till": 1457552620999 + ], + "s": -1, + "t": 1457552620999 + } } diff --git a/src/__tests__/mocks/splitchanges.since.-1.semver.json b/src/__tests__/mocks/splitchanges.since.-1.semver.json index ad3c486c5..876649937 100644 --- a/src/__tests__/mocks/splitchanges.since.-1.semver.json +++ b/src/__tests__/mocks/splitchanges.since.-1.semver.json @@ -1,514 +1,516 @@ { - "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" + "ff": { + "d": [ + { + "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": [ + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 } - ] + ], + "label": "between semver" }, - "partitions": [ - { - "treatment": "on", - "size": 0 + { + "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 + } + ] }, - { - "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": [ + "partitions": [ { - "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" + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 } - ] - }, - "partitions": [ - { - "treatment": "on", - "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" + } + ] }, - { - "treatment": "off", - "size": 0 - } - ], - "label": "equal to semver" - }, - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "partitions": [ + { + "treatment": "on", + "size": 100 + }, { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "treatment": "off", + "size": 0 } - ] + ], + "label": "equal to semver" }, - "partitions": [ - { - "treatment": "on", - "size": 0 + { + "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 + } + ] }, - { - "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": [ + "partitions": [ + { + "treatment": "on", + "size": 0 + }, { - "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" + "treatment": "off", + "size": 100 } - ] - }, - "partitions": [ - { - "treatment": "on", - "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" + } + ] }, - { - "treatment": "off", - "size": 0 - } - ], - "label": "greater than or equal to semver" - }, - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 } - ] + ], + "label": "greater than or equal to semver" }, - "partitions": [ - { - "treatment": "on", - "size": 0 + { + "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 + } + ] }, - { - "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": [ + "partitions": [ { - "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 + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 } - ] - }, - "partitions": [ - { - "treatment": "on", - "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 + } + ] }, - { - "treatment": "off", - "size": 0 - } - ], - "label": "in list semver" - }, - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "partitions": [ + { + "treatment": "on", + "size": 100 + }, { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "treatment": "off", + "size": 0 } - ] + ], + "label": "in list semver" }, - "partitions": [ - { - "treatment": "on", - "size": 0 + { + "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 + } + ] }, - { - "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": [ + "partitions": [ + { + "treatment": "on", + "size": 0 + }, { - "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" + "treatment": "off", + "size": 100 } - ] - }, - "partitions": [ - { - "treatment": "on", - "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" + } + ] }, - { - "treatment": "off", - "size": 0 - } - ], - "label": "less than or equal to semver" - }, - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 } - ] + ], + "label": "less than or equal to semver" }, - "partitions": [ - { - "treatment": "on", - "size": 0 + { + "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 + } + ] }, - { - "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": [ + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "UNSUPPORTED_MATCHER", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "dependencyMatcherData": null, - "booleanMatcherData": null, - "stringMatcherData": "something" + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 0 + ], + "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" + } + ] }, - { - "treatment": "off", - "size": 100 - } - ], - "label": "in segment my_custom_segment" - }, - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "partitions": [ + { + "treatment": "on", + "size": 0 + }, { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "treatment": "off", + "size": 100 } - ] + ], + "label": "in segment my_custom_segment" }, - "partitions": [ - { - "treatment": "on", - "size": 100 + { + "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 + } + ] }, - { - "treatment": "off", - "size": 0 - } - ], - "label": "default rule" - } - ] - } - ], - "since": -1, - "till": 1675259356568 + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + } + ], + "label": "default rule" + } + ] + } + ], + "s": -1, + "t": 1675259356568 + } } diff --git a/src/__tests__/mocks/splitchanges.since.-1.till.1602796638344.json b/src/__tests__/mocks/splitchanges.since.-1.till.1602796638344.json index 1c69f3b8a..98a00df76 100644 --- a/src/__tests__/mocks/splitchanges.since.-1.till.1602796638344.json +++ b/src/__tests__/mocks/splitchanges.since.-1.till.1602796638344.json @@ -1,150 +1,217 @@ { - "splits": [ - { - "trafficTypeName": "client", - "name": "workm", - "trafficAllocation": 100, - "trafficAllocationSeed": 147392224, - "seed": 524417105, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "on", - "changeNumber": 1602796638344, - "algo": 2, - "configurations": {}, - "sets": ["set_1", "set_2"], - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { "trafficType": "client", "attribute": null }, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "new_segment" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "ff": { + "d": [ + { + "trafficTypeName": "client", + "name": "workm", + "trafficAllocation": 100, + "trafficAllocationSeed": 147392224, + "seed": 524417105, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "on", + "changeNumber": 1602796638344, + "algo": 2, + "configurations": {}, + "sets": [ + "set_1", + "set_2" + ], + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "client", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "new_segment" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 0 + }, + { + "treatment": "free", + "size": 100 + }, + { + "treatment": "conta", + "size": 0 } - ] + ], + "label": "in segment new_segment" }, - "partitions": [ - { "treatment": "on", "size": 0 }, - { "treatment": "off", "size": 0 }, - { "treatment": "free", "size": 100 }, - { "treatment": "conta", "size": 0 } - ], - "label": "in segment new_segment" - }, - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { "trafficType": "client", "attribute": null }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "client", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + }, + { + "treatment": "free", + "size": 0 + }, + { + "treatment": "conta", + "size": 0 } - ] - }, - "partitions": [ - { "treatment": "on", "size": 100 }, - { "treatment": "off", "size": 0 }, - { "treatment": "free", "size": 0 }, - { "treatment": "conta", "size": 0 } - ], - "label": "default rule" - } - ] - }, - { - "trafficTypeName": "client", - "name": "workm_set_3", - "trafficAllocation": 100, - "trafficAllocationSeed": 147392224, - "seed": 524417105, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "on", - "changeNumber": 1602796638344, - "algo": 2, - "configurations": {}, - "sets": ["set_3"], - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { "trafficType": "client", "attribute": null }, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "new_segment" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + ], + "label": "default rule" + } + ] + }, + { + "trafficTypeName": "client", + "name": "workm_set_3", + "trafficAllocation": 100, + "trafficAllocationSeed": 147392224, + "seed": 524417105, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "on", + "changeNumber": 1602796638344, + "algo": 2, + "configurations": {}, + "sets": [ + "set_3" + ], + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "client", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "new_segment" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 0 + }, + { + "treatment": "free", + "size": 100 + }, + { + "treatment": "conta", + "size": 0 } - ] + ], + "label": "in segment new_segment" }, - "partitions": [ - { "treatment": "on", "size": 0 }, - { "treatment": "off", "size": 0 }, - { "treatment": "free", "size": 100 }, - { "treatment": "conta", "size": 0 } - ], - "label": "in segment new_segment" - }, - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { "trafficType": "client", "attribute": null }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "client", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + }, + { + "treatment": "free", + "size": 0 + }, + { + "treatment": "conta", + "size": 0 } - ] - }, - "partitions": [ - { "treatment": "on", "size": 100 }, - { "treatment": "off", "size": 0 }, - { "treatment": "free", "size": 0 }, - { "treatment": "conta", "size": 0 } - ], - "label": "default rule" - } - ] - } - ], - "since": -1, - "till": 1602796638344 + ], + "label": "default rule" + } + ] + } + ], + "s": -1, + "t": 1602796638344 + } } diff --git a/src/__tests__/mocks/splitchanges.since.1457552620999.json b/src/__tests__/mocks/splitchanges.since.1457552620999.json index d50f537f3..11d2c42c2 100644 --- a/src/__tests__/mocks/splitchanges.since.1457552620999.json +++ b/src/__tests__/mocks/splitchanges.since.1457552620999.json @@ -1,5 +1,7 @@ { - "splits": [], - "since": 1457552620999, - "till": 1457552620999 + "ff": { + "d": [], + "s": 1457552620999, + "t": 1457552620999 + } } diff --git a/src/__tests__/mocks/splitchanges.since.1457552620999.till.1457552649999.SPLIT_UPDATE.json b/src/__tests__/mocks/splitchanges.since.1457552620999.till.1457552649999.SPLIT_UPDATE.json index eefb6ea10..d1acf0e67 100644 --- a/src/__tests__/mocks/splitchanges.since.1457552620999.till.1457552649999.SPLIT_UPDATE.json +++ b/src/__tests__/mocks/splitchanges.since.1457552620999.till.1457552649999.SPLIT_UPDATE.json @@ -1,147 +1,149 @@ { - "splits": [ - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "qc_team", - "seed": -1984784937, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "no", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "ff": { + "d": [ + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "qc_team", + "seed": -1984784937, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "no", + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "tia@split.io", + "trevor@split.io" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] + }, + "partitions": [ { - "keySelector": null, - "matcherType": "WHITELIST", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist": [ - "tia@split.io", - "trevor@split.io" - ] - }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null + "treatment": "yes", + "size": 100 } ] }, - "partitions": [ - { - "treatment": "yes", - "size": 100 - } - ] - }, - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "employees" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "unaryStringMatcherData": null + } + ] + }, + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "employees" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "unaryStringMatcherData": null + "treatment": "yes", + "size": 100 + }, + { + "treatment": "no", + "size": 0 } ] - }, - "partitions": [ - { - "treatment": "yes", - "size": 100 + } + ], + "configurations": {} + }, + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "whitelist", + "seed": 104328192, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "not_allowed", + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "facundo@split.io" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] }, - { - "treatment": "no", - "size": 0 - } - ] - } - ], - "configurations": {} - }, - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "whitelist", - "seed": 104328192, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "not_allowed", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "partitions": [ { - "keySelector": null, - "matcherType": "WHITELIST", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist": [ - "facundo@split.io" - ] - }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null + "treatment": "allowed", + "size": 100 } ] }, - "partitions": [ - { - "treatment": "allowed", - "size": 100 - } - ] - }, - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "allowed", + "size": 100 + }, { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null + "treatment": "not_allowed", + "size": 0 } ] - }, - "partitions": [ - { - "treatment": "allowed", - "size": 100 - }, - { - "treatment": "not_allowed", - "size": 0 - } - ] - } - ] - } - ], - "since": 1457552620999, - "till": 1457552649999 + } + ] + } + ], + "s": 1457552620999, + "t": 1457552649999 + } } \ No newline at end of file diff --git a/src/__tests__/mocks/splitchanges.since.1457552649999.till.1457552650000.SPLIT_KILL.json b/src/__tests__/mocks/splitchanges.since.1457552649999.till.1457552650000.SPLIT_KILL.json index 098ac513d..7bd7ca290 100644 --- a/src/__tests__/mocks/splitchanges.since.1457552649999.till.1457552650000.SPLIT_KILL.json +++ b/src/__tests__/mocks/splitchanges.since.1457552649999.till.1457552650000.SPLIT_KILL.json @@ -1,75 +1,77 @@ { - "splits": [ - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "whitelist", - "seed": 104328192, - "status": "ACTIVE", - "killed": true, - "defaultTreatment": "not_allowed", - "changeNumber": 1457552650000, - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "ff": { + "d": [ + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "whitelist", + "seed": 104328192, + "status": "ACTIVE", + "killed": true, + "defaultTreatment": "not_allowed", + "changeNumber": 1457552650000, + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "facundo@split.io" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] + }, + "partitions": [ { - "keySelector": null, - "matcherType": "WHITELIST", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist": [ - "facundo@split.io" - ] - }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null + "treatment": "allowed", + "size": 100 } ] }, - "partitions": [ - { - "treatment": "allowed", - "size": 100 - } - ] - }, - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "allowed", + "size": 100 + }, { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null + "treatment": "not_allowed", + "size": 0 } ] - }, - "partitions": [ - { - "treatment": "allowed", - "size": 100 - }, - { - "treatment": "not_allowed", - "size": 0 - } - ] - } - ] - } - ], - "since": 1457552649999, - "till": 1457552650000 -} \ No newline at end of file + } + ] + } + ], + "s": 1457552649999, + "t": 1457552650000 + } +} diff --git a/src/__tests__/mocks/splitchanges.since.1457552650000.till.1457552650001.SPLIT_UPDATE.json b/src/__tests__/mocks/splitchanges.since.1457552650000.till.1457552650001.SPLIT_UPDATE.json index a151cfe5e..5cb0a1d0a 100644 --- a/src/__tests__/mocks/splitchanges.since.1457552650000.till.1457552650001.SPLIT_UPDATE.json +++ b/src/__tests__/mocks/splitchanges.since.1457552650000.till.1457552650001.SPLIT_UPDATE.json @@ -1,79 +1,81 @@ { - "splits": [ - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "qc_team", - "seed": -1984784937, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "no", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "ff": { + "d": [ + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "qc_team", + "seed": -1984784937, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "no", + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "tia@split.io", + "trevor@split.io" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] + }, + "partitions": [ { - "keySelector": null, - "matcherType": "WHITELIST", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist": [ - "tia@split.io", - "trevor@split.io" - ] - }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null + "treatment": "yes", + "size": 100 } ] }, - "partitions": [ - { - "treatment": "yes", - "size": 100 - } - ] - }, - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "new_segment" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "unaryStringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "yes", + "size": 100 + }, { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "new_segment" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "unaryStringMatcherData": null + "treatment": "no", + "size": 0 } ] - }, - "partitions": [ - { - "treatment": "yes", - "size": 100 - }, - { - "treatment": "no", - "size": 0 - } - ] - } - ], - "configurations": {} - } - ], - "since": 1457552650000, - "till": 1457552650001 -} \ No newline at end of file + } + ], + "configurations": {} + } + ], + "s": 1457552650000, + "t": 1457552650001 + } +} diff --git a/src/__tests__/mocks/splitchanges.since.1602796638344.till.1602797638344.json b/src/__tests__/mocks/splitchanges.since.1602796638344.till.1602797638344.json index 5ec998208..55d86bc9e 100644 --- a/src/__tests__/mocks/splitchanges.since.1602796638344.till.1602797638344.json +++ b/src/__tests__/mocks/splitchanges.since.1602796638344.till.1602797638344.json @@ -1,78 +1,112 @@ { - "splits": [ - { - "trafficTypeName": "client", - "name": "workm", - "trafficAllocation": 100, - "trafficAllocationSeed": 147392224, - "seed": 524417105, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "on", - "changeNumber": 1602797638344, - "algo": 2, - "configurations": {}, - "sets": ["set_1"], - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "ff": { + "d": [ + { + "trafficTypeName": "client", + "name": "workm", + "trafficAllocation": 100, + "trafficAllocationSeed": 147392224, + "seed": 524417105, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "on", + "changeNumber": 1602797638344, + "algo": 2, + "configurations": {}, + "sets": [ + "set_1" + ], + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "client", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "new_segment" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ { - "keySelector": { "trafficType": "client", "attribute": null }, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "new_segment" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 0 + }, + { + "treatment": "free", + "size": 100 + }, + { + "treatment": "conta", + "size": 0 } - ] + ], + "label": "in segment new_segment" }, - "partitions": [ - { "treatment": "on", "size": 0 }, - { "treatment": "off", "size": 0 }, - { "treatment": "free", "size": 100 }, - { "treatment": "conta", "size": 0 } - ], - "label": "in segment new_segment" - }, - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "client", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, { - "keySelector": { "trafficType": "client", "attribute": null }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "treatment": "off", + "size": 0 + }, + { + "treatment": "free", + "size": 0 + }, + { + "treatment": "conta", + "size": 0 } - ] - }, - "partitions": [ - { "treatment": "on", "size": 100 }, - { "treatment": "off", "size": 0 }, - { "treatment": "free", "size": 0 }, - { "treatment": "conta", "size": 0 } - ], - "label": "default rule" - } - ] - } - ], - "since": 1602796638344, - "till": 1602797638344 + ], + "label": "default rule" + } + ] + } + ], + "s": 1602796638344, + "t": 1602797638344 + } } diff --git a/src/__tests__/mocks/splitchanges.since.1602797638344.till.1602798638344.json b/src/__tests__/mocks/splitchanges.since.1602797638344.till.1602798638344.json index 9384d1c17..96d4580cb 100644 --- a/src/__tests__/mocks/splitchanges.since.1602797638344.till.1602798638344.json +++ b/src/__tests__/mocks/splitchanges.since.1602797638344.till.1602798638344.json @@ -1,78 +1,112 @@ { - "splits": [ - { - "trafficTypeName": "client", - "name": "workm", - "trafficAllocation": 100, - "trafficAllocationSeed": 147392224, - "seed": 524417105, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "on", - "changeNumber": 1602798638344, - "algo": 2, - "configurations": {}, - "sets": ["set_3"], - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "ff": { + "d": [ + { + "trafficTypeName": "client", + "name": "workm", + "trafficAllocation": 100, + "trafficAllocationSeed": 147392224, + "seed": 524417105, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "on", + "changeNumber": 1602798638344, + "algo": 2, + "configurations": {}, + "sets": [ + "set_3" + ], + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "client", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "new_segment" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ { - "keySelector": { "trafficType": "client", "attribute": null }, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "new_segment" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 0 + }, + { + "treatment": "free", + "size": 100 + }, + { + "treatment": "conta", + "size": 0 } - ] + ], + "label": "in segment new_segment" }, - "partitions": [ - { "treatment": "on", "size": 0 }, - { "treatment": "off", "size": 0 }, - { "treatment": "free", "size": 100 }, - { "treatment": "conta", "size": 0 } - ], - "label": "in segment new_segment" - }, - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "client", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, { - "keySelector": { "trafficType": "client", "attribute": null }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "treatment": "off", + "size": 0 + }, + { + "treatment": "free", + "size": 0 + }, + { + "treatment": "conta", + "size": 0 } - ] - }, - "partitions": [ - { "treatment": "on", "size": 100 }, - { "treatment": "off", "size": 0 }, - { "treatment": "free", "size": 0 }, - { "treatment": "conta", "size": 0 } - ], - "label": "default rule" - } - ] - } - ], - "since": 1602797638344, - "till": 1602798638344 + ], + "label": "default rule" + } + ] + } + ], + "s": 1602797638344, + "t": 1602798638344 + } } diff --git a/src/__tests__/mocks/splitchanges.since.1684265694505.till.1684265694506.SPLIT_UPDATE.json b/src/__tests__/mocks/splitchanges.since.1684265694505.till.1684265694506.SPLIT_UPDATE.json index 62c8361be..6e35e11b2 100644 --- a/src/__tests__/mocks/splitchanges.since.1684265694505.till.1684265694506.SPLIT_UPDATE.json +++ b/src/__tests__/mocks/splitchanges.since.1684265694505.till.1684265694506.SPLIT_UPDATE.json @@ -1,193 +1,195 @@ { - "splits": [ - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "qc_team", - "seed": -1984784937, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "no", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": null, - "matcherType": "WHITELIST", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist": [ - "tia@split.io", - "trevor@split.io" - ] - }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null - } - ] - }, - "partitions": [ - { - "treatment": "yes", - "size": 100 - } - ] - }, - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "new_segment" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "unaryStringMatcherData": null - } - ] - }, - "partitions": [ - { - "treatment": "yes", - "size": 100 - }, - { - "treatment": "no", - "size": 0 - } - ] - } - ], - "configurations": {} - }, - { - "trafficTypeName": null, - "name":"mauro_java", - "orgId": null, - "environment": null, - "trafficTypeId": null, - "seed":-1769377604, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "conditions": [ + "ff": { + "d": [ { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "qc_team", + "seed": -1984784937, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "no", + "conditions": [ { - "keySelector": null, - "matcherType": "WHITELIST", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist":[ - "admin", - "mauro", - "nico" + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "tia@split.io", + "trevor@split.io" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } ] }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null - } - ] - }, - "partitions": [ - { - "treatment": "v5", - "size": 100 - } - ] - }, - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "maur-2" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "unaryStringMatcherData": null - } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 0 - }, - { - "treatment": "off", - "size": 0 - }, - { - "treatment": "V4", - "size": 0 + "partitions": [ + { + "treatment": "yes", + "size": 100 + } + ] }, { - "treatment": "v5", - "size": 100 + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "new_segment" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "unaryStringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "yes", + "size": 100 + }, + { + "treatment": "no", + "size": 0 + } + ] } - ] + ], + "configurations": {} }, { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "trafficTypeName": null, + "name": "mauro_java", + "orgId": null, + "environment": null, + "trafficTypeId": null, + "seed": -1769377604, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "conditions": [ { - "keySelector": { - "trafficType":"user", - "attribute": null + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "admin", + "mauro", + "nico" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] }, - "matcherType":"ALL_KEYS", - "negate":false - } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 - }, - { - "treatment": "off", - "size": 0 + "partitions": [ + { + "treatment": "v5", + "size": 100 + } + ] }, { - "treatment": "V4", - "size": 0 + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "maur-2" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "unaryStringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 0 + }, + { + "treatment": "V4", + "size": 0 + }, + { + "treatment": "v5", + "size": 100 + } + ] }, { - "treatment": "v5", - "size": 0 + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + }, + { + "treatment": "V4", + "size": 0 + }, + { + "treatment": "v5", + "size": 0 + } + ] } ] } - ] + ], + "s": 1684265694505, + "t": 1684265694506 } - ], - "since": 1684265694505, - "till": 1684265694506 } diff --git a/src/__tests__/mocks/splitchanges.since.1684265694506.till.1684265694526.SPLIT_UPDATE.json b/src/__tests__/mocks/splitchanges.since.1684265694506.till.1684265694526.SPLIT_UPDATE.json index 74f142fb7..cf9cb3fd6 100644 --- a/src/__tests__/mocks/splitchanges.since.1684265694506.till.1684265694526.SPLIT_UPDATE.json +++ b/src/__tests__/mocks/splitchanges.since.1684265694506.till.1684265694526.SPLIT_UPDATE.json @@ -1,193 +1,195 @@ { - "splits": [ - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "qc_team", - "seed": -1984784937, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "no", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": null, - "matcherType": "WHITELIST", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist": [ - "tia@split.io", - "trevor@split.io" - ] - }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null - } - ] - }, - "partitions": [ - { - "treatment": "yes", - "size": 100 - } - ] - }, - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "new_segment" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "unaryStringMatcherData": null - } - ] - }, - "partitions": [ - { - "treatment": "yes", - "size": 100 - }, - { - "treatment": "no", - "size": 0 - } - ] - } - ], - "configurations": {} - }, - { - "trafficTypeName": null, - "name":"mauro_java", - "orgId": null, - "environment": null, - "trafficTypeId": null, - "seed":-1769377604, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "conditions": [ + "ff": { + "d": [ { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "qc_team", + "seed": -1984784937, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "no", + "conditions": [ { - "keySelector": null, - "matcherType": "WHITELIST", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist":[ - "admin", - "mauro", - "nico" + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "tia@split.io", + "trevor@split.io" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } ] }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null - } - ] - }, - "partitions": [ - { - "treatment": "v4", - "size": 100 - } - ] - }, - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "maur-2" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "unaryStringMatcherData": null - } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 0 - }, - { - "treatment": "off", - "size": 0 - }, - { - "treatment": "V4", - "size": 100 + "partitions": [ + { + "treatment": "yes", + "size": 100 + } + ] }, { - "treatment": "v5", - "size": 0 + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "new_segment" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "unaryStringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "yes", + "size": 100 + }, + { + "treatment": "no", + "size": 0 + } + ] } - ] + ], + "configurations": {} }, { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "trafficTypeName": null, + "name": "mauro_java", + "orgId": null, + "environment": null, + "trafficTypeId": null, + "seed": -1769377604, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "conditions": [ { - "keySelector": { - "trafficType":"user", - "attribute": null + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "admin", + "mauro", + "nico" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] }, - "matcherType":"ALL_KEYS", - "negate":false - } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 0 - }, - { - "treatment": "off", - "size": 0 + "partitions": [ + { + "treatment": "v4", + "size": 100 + } + ] }, { - "treatment": "V4", - "size": 0 + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "maur-2" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "unaryStringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 0 + }, + { + "treatment": "V4", + "size": 100 + }, + { + "treatment": "v5", + "size": 0 + } + ] }, { - "treatment": "v5", - "size": 100 + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 0 + }, + { + "treatment": "V4", + "size": 0 + }, + { + "treatment": "v5", + "size": 100 + } + ] } ] } - ] + ], + "s": 1684265694506, + "t": 1684265694526 } - ], - "since": 1684265694506, - "till": 1684265694526 -} +} \ No newline at end of file diff --git a/src/__tests__/mocks/splitchanges.since.1684265694526.till.1684265694546.SPLIT_UPDATE.json b/src/__tests__/mocks/splitchanges.since.1684265694526.till.1684265694546.SPLIT_UPDATE.json index 9e7ed4568..96b85bf17 100644 --- a/src/__tests__/mocks/splitchanges.since.1684265694526.till.1684265694546.SPLIT_UPDATE.json +++ b/src/__tests__/mocks/splitchanges.since.1684265694526.till.1684265694546.SPLIT_UPDATE.json @@ -1,193 +1,195 @@ { - "splits": [ - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "qc_team", - "seed": -1984784937, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "no", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": null, - "matcherType": "WHITELIST", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist": [ - "tia@split.io", - "trevor@split.io" - ] - }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null - } - ] - }, - "partitions": [ - { - "treatment": "yes", - "size": 100 - } - ] - }, - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "new_segment" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "unaryStringMatcherData": null - } - ] - }, - "partitions": [ - { - "treatment": "yes", - "size": 100 - }, - { - "treatment": "no", - "size": 0 - } - ] - } - ], - "configurations": {} - }, - { - "trafficTypeName": null, - "name":"mauro_java", - "orgId": null, - "environment": null, - "trafficTypeId": null, - "seed":-1769377604, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "conditions": [ + "ff": { + "d": [ { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "qc_team", + "seed": -1984784937, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "no", + "conditions": [ { - "keySelector": null, - "matcherType": "WHITELIST", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist":[ - "admin", - "mauro", - "nico" + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "tia@split.io", + "trevor@split.io" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } ] }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null - } - ] - }, - "partitions": [ - { - "treatment": "v4", - "size": 100 - } - ] - }, - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "maur-2" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "unaryStringMatcherData": null - } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 0 - }, - { - "treatment": "off", - "size": 0 - }, - { - "treatment": "V4", - "size": 100 + "partitions": [ + { + "treatment": "yes", + "size": 100 + } + ] }, { - "treatment": "v5", - "size": 0 + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "new_segment" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "unaryStringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "yes", + "size": 100 + }, + { + "treatment": "no", + "size": 0 + } + ] } - ] + ], + "configurations": {} }, { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "trafficTypeName": null, + "name": "mauro_java", + "orgId": null, + "environment": null, + "trafficTypeId": null, + "seed": -1769377604, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "conditions": [ { - "keySelector": { - "trafficType":"user", - "attribute": null + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "admin", + "mauro", + "nico" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] }, - "matcherType":"ALL_KEYS", - "negate":false - } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 0 - }, - { - "treatment": "off", - "size": 100 + "partitions": [ + { + "treatment": "v4", + "size": 100 + } + ] }, { - "treatment": "V4", - "size": 0 + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "maur-2" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "unaryStringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 0 + }, + { + "treatment": "V4", + "size": 100 + }, + { + "treatment": "v5", + "size": 0 + } + ] }, { - "treatment": "v5", - "size": 0 + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 + }, + { + "treatment": "V4", + "size": 0 + }, + { + "treatment": "v5", + "size": 0 + } + ] } ] } - ] + ], + "s": 1684265694526, + "t": 1684265694546 } - ], - "since": 1684265694526, - "till": 1684265694546 } diff --git a/src/__tests__/mocks/splitchanges.since.1684265694546.till.1684265694556.SPLIT_UPDATE.json b/src/__tests__/mocks/splitchanges.since.1684265694546.till.1684265694556.SPLIT_UPDATE.json index b96b031c3..b2a6c7a7f 100644 --- a/src/__tests__/mocks/splitchanges.since.1684265694546.till.1684265694556.SPLIT_UPDATE.json +++ b/src/__tests__/mocks/splitchanges.since.1684265694546.till.1684265694556.SPLIT_UPDATE.json @@ -1,193 +1,195 @@ { - "splits": [ - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "qc_team", - "seed": -1984784937, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "no", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": null, - "matcherType": "WHITELIST", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist": [ - "tia@split.io", - "trevor@split.io" - ] - }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null - } - ] - }, - "partitions": [ - { - "treatment": "yes", - "size": 100 - } - ] - }, - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "new_segment" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "unaryStringMatcherData": null - } - ] - }, - "partitions": [ - { - "treatment": "yes", - "size": 100 - }, - { - "treatment": "no", - "size": 0 - } - ] - } - ], - "configurations": {} - }, - { - "trafficTypeName": null, - "name":"mauro_java", - "orgId": null, - "environment": null, - "trafficTypeId": null, - "seed":-1769377604, - "status": "ARCHIVED", - "killed": false, - "defaultTreatment": "off", - "conditions": [ + "ff": { + "d": [ { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "qc_team", + "seed": -1984784937, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "no", + "conditions": [ { - "keySelector": null, - "matcherType": "WHITELIST", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist":[ - "admin", - "mauro", - "nico" + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "tia@split.io", + "trevor@split.io" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } ] }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null - } - ] - }, - "partitions": [ - { - "treatment": "v4", - "size": 100 - } - ] - }, - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "maur-2" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "unaryStringMatcherData": null - } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 0 - }, - { - "treatment": "off", - "size": 0 - }, - { - "treatment": "V4", - "size": 100 + "partitions": [ + { + "treatment": "yes", + "size": 100 + } + ] }, { - "treatment": "v5", - "size": 0 + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "new_segment" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "unaryStringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "yes", + "size": 100 + }, + { + "treatment": "no", + "size": 0 + } + ] } - ] + ], + "configurations": {} }, { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "trafficTypeName": null, + "name": "mauro_java", + "orgId": null, + "environment": null, + "trafficTypeId": null, + "seed": -1769377604, + "status": "ARCHIVED", + "killed": false, + "defaultTreatment": "off", + "conditions": [ { - "keySelector": { - "trafficType":"user", - "attribute": null + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "admin", + "mauro", + "nico" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] }, - "matcherType":"ALL_KEYS", - "negate":false - } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 0 - }, - { - "treatment": "off", - "size": 100 + "partitions": [ + { + "treatment": "v4", + "size": 100 + } + ] }, { - "treatment": "V4", - "size": 0 + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "maur-2" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "unaryStringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 0 + }, + { + "treatment": "V4", + "size": 100 + }, + { + "treatment": "v5", + "size": 0 + } + ] }, { - "treatment": "v5", - "size": 0 + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 + }, + { + "treatment": "V4", + "size": 0 + }, + { + "treatment": "v5", + "size": 0 + } + ] } ] } - ] + ], + "s": 1684265694546, + "t": 1684265694556 } - ], - "since": 1684265694546, - "till": 1684265694556 } diff --git a/src/__tests__/nodeSuites/evaluations-semver.spec.js b/src/__tests__/nodeSuites/evaluations-semver.spec.js index 5885a2b16..cc716bb83 100644 --- a/src/__tests__/nodeSuites/evaluations-semver.spec.js +++ b/src/__tests__/nodeSuites/evaluations-semver.spec.js @@ -24,8 +24,8 @@ const config = { 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 } }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', { status: 200, body: splitChangesMock1 }); + fetchMock.get(config.urls.sdk + '/splitChanges?s=1.3&since=1675259356568&rbSince=-1', { status: 200, body: { ff: { d: [], s: 1675259356568, t: 1675259356568 } } }); const splitio = SplitFactory(config); const client = splitio.client(); diff --git a/src/__tests__/nodeSuites/expected-treatments.spec.js b/src/__tests__/nodeSuites/expected-treatments.spec.js index 552db56b3..b4c2f6a18 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?s=1.1&since=-1'), overwriteRoutes: true }, { status: 200, body: splitChangesMockReal }); + fetchMock.get({ url: url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-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 35d96d26d..971d4ec81 100644 --- a/src/__tests__/nodeSuites/fetch-specific-splits.spec.js +++ b/src/__tests__/nodeSuites/fetch-specific-splits.spec.js @@ -23,13 +23,13 @@ export function fetchSpecificSplits(fetchMock, assert) { const queryString = queryStrings[i] || ''; let factory; - 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 () { + fetchMock.getOnce(urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1' + queryString, { status: 200, body: { ff: { d: [], s: -1, t: 1457552620999 } } }); + fetchMock.getOnce(urls.sdk + '/splitChanges?s=1.3&since=1457552620999&rbSince=-1' + queryString, { status: 200, body: { ff: { d: [], s: 1457552620999, t: 1457552620999 } } }); + fetchMock.getOnce(urls.sdk + '/splitChanges?s=1.3&since=1457552620999&rbSince=-1' + queryString, function () { factory.client().destroy().then(() => { assert.pass(`splitFilters #${i}`); }); - return { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }; + return { status: 200, body: { ff: { d: [], s: 1457552620999, t: 1457552620999 } } }; }); factory = SplitFactory(config); @@ -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?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 () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1' + queryString, { status: 200, body: { ff: { d: [], s: 1457552620999, t: 1457552620999 } }}); + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=1457552620999&rbSince=-1' + 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 e8dd725e0..bacbd507a 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?s=1.1&since=-1&sets=set_1,set_2', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-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?s=1.1&since=1602796638344&sets=set_1,set_2', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=1602796638344&rbSince=-1&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?s=1.1&since=1602797638344&sets=set_1,set_2', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=1602797638344&rbSince=-1&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?s=1.1&since=1602798638344&sets=set_1,set_2', async function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=1602798638344&rbSince=-1&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?s=1.1&since=-1', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', function () { return { status: 200, body: splitChange2}; }); // Receive split change with 1 split belonging to set_1 only - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&since=1602796638344', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=1602796638344&rbSince=-1', 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?s=1.1&since=1602797638344', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=1602797638344&rbSince=-1', 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?s=1.1&since=1602798638344', async function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=1602798638344&rbSince=-1', 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?s=1.1&since=-1&sets=set_1', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1&sets=set_1', function () { return { status: 200, body: splitChange2}; }); - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&since=1602796638344&sets=set_1', async function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=1602796638344&rbSince=-1&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?s=1.1&since=-1', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', function () { return { status: 200, body: splitChange2}; }); - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&since=1602796638344', async function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=1602796638344&rbSince=-1', 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-listener.spec.js b/src/__tests__/nodeSuites/impressions-listener.spec.js index 984c36d1e..5a09d532c 100644 --- a/src/__tests__/nodeSuites/impressions-listener.spec.js +++ b/src/__tests__/nodeSuites/impressions-listener.spec.js @@ -39,13 +39,13 @@ export default function (assert) { const testAttrs = { is_test: true }; // Generate one impression, depends on hierarchical_dep_hierarchical which depends on hierarchical_dep_always_on - client.getTreatment('nicolas@split.io', 'hierarchical_splits_test'); + client.getTreatment('nicolas@split.io', 'hierarchical_splits_test', undefined, { properties: { prop1: 'prop-value' } }); client.getTreatment({ matchingKey: 'marcio@split.io', bucketingKey: 'impr_bucketing_2' }, 'qc_team'); client.getTreatment('facundo@split.io', 'qc_team', testAttrs); client.getTreatment('facundo@split.io', 'qc_team', testAttrs); setTimeout(() => { - assert.true(listener.logImpression.callCount, 4, 'Impression listener logImpression method should be called after we call client.getTreatment, once per each impression generated.'); + assert.equal(listener.logImpression.callCount, 4, 'Impression listener logImpression method should be called after we call client.getTreatment, once per each impression generated.'); assert.true(listener.logImpression.getCall(0).calledWithExactly({ impression: { feature: 'hierarchical_splits_test', @@ -55,7 +55,7 @@ export default function (assert) { bucketingKey: undefined, label: 'expected label', changeNumber: 2828282828, - pt: undefined + properties: '{"prop1":"prop-value"}' }, attributes: undefined, ...metaData diff --git a/src/__tests__/nodeSuites/impressions.debug.spec.js b/src/__tests__/nodeSuites/impressions.debug.spec.js index 08faa13e3..3ddc05737 100644 --- a/src/__tests__/nodeSuites/impressions.debug.spec.js +++ b/src/__tests__/nodeSuites/impressions.debug.spec.js @@ -43,8 +43,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?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); - fetchMock.get(url(settings, '/splitChanges?s=1.1&since=1457552620999'), { status: 200, body: splitChangesMock2 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.get(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), { 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); @@ -63,6 +63,14 @@ export default async function (key, fetchMock, assert) { k: 'facundo@split.io', t: 'o.n', m: data[0].i[1].m, c: 828282828282, r: 'another expected label', b: 'test_buck_key', pt: data[0].i[0].m }, { k: 'facundo@split.io', t: 'o.n', m: data[0].i[2].m, c: 828282828282, r: 'another expected label', b: 'test_buck_key', pt: data[0].i[1].m + }, { + k: 'emi@split.io', t: 'o.n', m: data[0].i[3].m, c: 828282828282, r: 'another expected label', properties: '{"prop1":"value1"}' + }, { + k: 'emi@split.io', t: 'o.n', m: data[0].i[4].m, c: 828282828282, r: 'another expected label', properties: '{"prop1":"value2"}' + }, { + k: 'emi@split.io', t: 'o.n', m: data[0].i[5].m, c: 828282828282, r: 'another expected label', properties: '{"prop1":"value3"}' + }, { + k: 'emi@split.io', t: 'o.n', m: data[0].i[6].m, c: 828282828282, r: 'another expected label', properties: '{"prop1":"value4"}' }] }], 'We performed evaluations for one split, so we should have 1 item total.'); @@ -75,7 +83,7 @@ export default async function (key, fetchMock, assert) { fetchMock.postOnce(url(settings, '/testImpressions/count'), (url, opts) => { assert.deepEqual(JSON.parse(opts.body), { - pf: [{ f: 'always_on_track_impressions_false', m: truncatedTimeFrame, rc: 1 }] + pf: [{ f: 'always_on_impressions_disabled_true', m: truncatedTimeFrame, rc: 1 }] }, 'We should generate impression count for the feature with track impressions disabled.'); return 200; @@ -83,7 +91,7 @@ export default async function (key, fetchMock, assert) { fetchMock.postOnce(url(settings, '/v1/keys/ss'), (url, opts) => { assert.deepEqual(JSON.parse(opts.body), { - keys: [{ f: 'always_on_track_impressions_false', ks: ['other_key'] }] + keys: [{ f: 'always_on_impressions_disabled_true', ks: ['other_key'] }] }, 'We should track unique keys for the feature with track impressions disabled.'); return 200; @@ -97,5 +105,10 @@ export default async function (key, fetchMock, assert) { assert.equal(client.getTreatment({ matchingKey: key, bucketingKey: 'test_buck_key' }, 'split_with_config'), 'o.n'); assert.equal(client.getTreatment({ matchingKey: key, bucketingKey: 'test_buck_key' }, 'split_with_config'), 'o.n'); assert.equal(client.getTreatment({ matchingKey: key, bucketingKey: 'test_buck_key' }, 'split_with_config'), 'o.n'); - assert.equal(client.getTreatment({ matchingKey: 'other_key', bucketingKey: 'test_buck_key' }, 'always_on_track_impressions_false'), 'on'); + assert.equal(client.getTreatment({ matchingKey: 'other_key', bucketingKey: 'test_buck_key' }, 'always_on_impressions_disabled_true'), 'on'); + + assert.equal(client.getTreatment('emi@split.io', 'split_with_config', undefined, { properties: { prop1: 'value1' } }), 'o.n'); + assert.equal(client.getTreatments('emi@split.io', ['split_with_config'], undefined, { properties: { prop1: 'value2' } }).split_with_config, 'o.n'); + assert.equal(client.getTreatmentWithConfig('emi@split.io', 'split_with_config', undefined, { properties: { prop1: 'value3' } }).treatment, 'o.n'); + assert.equal(client.getTreatmentsWithConfig('emi@split.io', ['split_with_config'], undefined, { properties: { prop1: 'value4' } }).split_with_config.treatment, 'o.n'); } diff --git a/src/__tests__/nodeSuites/impressions.none.spec.js b/src/__tests__/nodeSuites/impressions.none.spec.js index 2e47f32e1..6a94a7a06 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?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); - fetchMock.get(url(settings, '/splitChanges?s=1.1&since=1457552620999'), { status: 200, body: splitChangesMock2 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.get(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), { 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); @@ -55,7 +55,7 @@ export default async function (key, fetchMock, assert) { { f: 'split_with_config', m: truncatedTimeFrame, rc: 3 }, { f: 'always_off', m: truncatedTimeFrame, rc: 3 }, { f: 'always_on', m: truncatedTimeFrame, rc: 5 }, - { f: 'always_on_track_impressions_false', m: truncatedTimeFrame, rc: 1 } + { f: 'always_on_impressions_disabled_true', m: truncatedTimeFrame, rc: 1 } ] }); return 200; @@ -79,7 +79,7 @@ export default async function (key, fetchMock, assert) { ks: ['emma@split.io', 'emi@split.io', 'nico@split.io'] }, { - f: 'always_on_track_impressions_false', + f: 'always_on_impressions_disabled_true', ks: ['emi@split.io'] } ] @@ -100,7 +100,7 @@ export default async function (key, fetchMock, assert) { client.getTreatment('nico@split.io', 'always_on'); client.getTreatment('emi@split.io', 'split_with_config'); client.getTreatment('emma@split.io', 'split_with_config'); - client.getTreatment('emi@split.io', 'always_on_track_impressions_false'); + client.getTreatment('emi@split.io', 'always_on_impressions_disabled_true'); client.destroy().then(() => { assert.end(); diff --git a/src/__tests__/nodeSuites/impressions.spec.js b/src/__tests__/nodeSuites/impressions.spec.js index e7615140c..78857c77b 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?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); - fetchMock.get(url(settings, '/splitChanges?s=1.1&since=1457552620999'), { status: 200, body: splitChangesMock2 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.get(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), { 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); @@ -55,12 +55,12 @@ export default async function (key, fetchMock, assert) { const dependencyChildImpr = data.filter(e => e.f === 'hierarchical_splits_test')[0]; const splitWithConfigImpr = data.filter(e => e.f === 'split_with_config')[0]; const notExistentSplitImpr = data.filter(e => e.f === 'not_existent_split')[0]; - const alwaysOnWithTrackImpressionsFalse = data.filter(e => e.f === 'always_on_track_impressions_false'); + const alwaysOnWithImpressionsDisabledTrue = data.filter(e => e.f === 'always_on_impressions_disabled_true'); assert.equal(notExistentSplitImpr.i.length, 1); // Only one, the split not found is filtered by the non existent Split check. - assert.equal(splitWithConfigImpr.i.length, 2); + assert.equal(splitWithConfigImpr.i.length, 3); assert.equal(dependencyChildImpr.i.length, 1); - assert.equal(alwaysOnWithTrackImpressionsFalse.length, 0); + assert.equal(alwaysOnWithImpressionsDisabledTrue.length, 0); assert.true(dependencyChildImpr, 'Split we wanted to evaluate should be present on the impressions.'); assert.false(data.some(e => e.f === 'hierarchical_dep_always_on'), 'Parent split evaluations should not result in impressions.'); @@ -75,6 +75,7 @@ export default async function (key, fetchMock, assert) { assert.equal(output.t, expected.treatment, 'Present impressions should have the correct treatment.'); assert.equal(output.r, expected.label, 'Present impressions should have the correct label.'); assert.equal(output.c, expected.changeNumber, 'Present impressions should have the correct changeNumber.'); + assert.equal(output.properties, expected.properties, 'Present impressions should have the correct properties.'); assert.true(output.m >= (performedWhenReady ? readyEvaluationsStart : evaluationsStart) && output.m <= evaluationsEnd, 'Present impressions should have the correct timestamp (test with error margin).'); } @@ -90,6 +91,10 @@ export default async function (key, fetchMock, assert) { keyName: 'facundo@split.io', label: 'another expected label', treatment: 'o.n', bucketingKey: 'test_buck_key', changeNumber: 828282828282 }); + validateImpressionData(splitWithConfigImpr.i[2], { + keyName: 'other_key', label: 'another expected label', treatment: 'o.n', + changeNumber: 828282828282, properties: '{"some":"value2"}' + }); // Not push impressions with a invalid key (aka matching key) assert.true( @@ -111,21 +116,21 @@ export default async function (key, fetchMock, assert) { // finding these validate the feature names collection too const splitWithConfigImpr = data.pf.filter(e => e.f === 'split_with_config')[0]; - const alwaysOnWithTrackImpressionsFalse = data.pf.filter(e => e.f === 'always_on_track_impressions_false')[0]; + const alwaysOnWithImpressionsDisabledTrue = data.pf.filter(e => e.f === 'always_on_impressions_disabled_true')[0]; assert.equal(splitWithConfigImpr.rc, 1); assert.equal(typeof splitWithConfigImpr.m, 'number'); assert.equal(splitWithConfigImpr.m, truncatedTimeFrame); - assert.equal(alwaysOnWithTrackImpressionsFalse.rc, 1); - assert.equal(typeof alwaysOnWithTrackImpressionsFalse.m, 'number'); - assert.equal(alwaysOnWithTrackImpressionsFalse.m, truncatedTimeFrame); + assert.equal(alwaysOnWithImpressionsDisabledTrue.rc, 1); + assert.equal(typeof alwaysOnWithImpressionsDisabledTrue.m, 'number'); + assert.equal(alwaysOnWithImpressionsDisabledTrue.m, truncatedTimeFrame); return 200; }); fetchMock.postOnce(url(settings, '/v1/keys/ss'), (url, opts) => { assert.deepEqual(JSON.parse(opts.body), { - keys: [{ f: 'always_on_track_impressions_false', ks: ['other_key'] }] + keys: [{ f: 'always_on_impressions_disabled_true', ks: ['other_key'] }] }, 'We should only track unique keys for features flags with track impressions disabled.'); return 200; @@ -153,8 +158,11 @@ export default async function (key, fetchMock, assert) { client.getTreatmentWithConfig({ matchingKey: key, bucketingKey: 'test_buck_key' }, 'split_with_config'); client.getTreatmentWithConfig({ matchingKey: 'different', bucketingKey: 'test_buck_key' }, 'split_with_config'); - // Impression should not be tracked - assert.equal(client.getTreatment('other_key', 'always_on_track_impressions_false'), 'on'); + // Impression should not be tracked (passed properties will not be submitted) + assert.equal(client.getTreatment('other_key', 'always_on_impressions_disabled_true'), 'on', undefined, { properties: { some: 'value1' } }); + + // Tracked impression with properties should be handled in DEBUG mode + assert.equal(client.getTreatment('other_key', 'split_with_config', undefined, { properties: { some: 'value2' } }), 'o.n'); evaluationsEnd = Date.now(); } diff --git a/src/__tests__/nodeSuites/ip-addresses-setting.debug.spec.js b/src/__tests__/nodeSuites/ip-addresses-setting.debug.spec.js index 674873ad4..2e9599e78 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?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, '/splitChanges?s=1.3&since=-1&rbSince=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), { status: 200, body: { ff: { d: [], s: 1457552620999, t: 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 2fbf92f12..99aea2916 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?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, '/splitChanges?s=1.3&since=-1&rbSince=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), { status: 200, body: { ff: { d: [], s: 1457552620999, t: 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/lazy-init.spec.js b/src/__tests__/nodeSuites/lazy-init.spec.js index 9cb5f9735..c4d94c1d2 100644 --- a/src/__tests__/nodeSuites/lazy-init.spec.js +++ b/src/__tests__/nodeSuites/lazy-init.spec.js @@ -29,11 +29,11 @@ export default function (settings, fetchMock, t) { assert.equal(client.track('user-1', 'user', 'my_event'), true, 'We should track the event'); } - fetchMock.getOnce('https://not-called/api/splitChanges?s=1.1&since=-1', { status: 200, body: { splits: [], since: -1, till: 1457552620999 } }); - fetchMock.getOnce('https://not-called/api/splitChanges?s=1.1&since=1457552620999', { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); + fetchMock.getOnce('https://not-called/api/splitChanges?s=1.3&since=-1&rbSince=-1', { status: 200, body: { ff: { d: [], s: -1, t: 1457552620999 } } }); + fetchMock.getOnce('https://not-called/api/splitChanges?s=1.3&since=1457552620999&rbSince=-1', { status: 200, body: { ff: { d: [], s: 1457552620999, t: 1457552620999 } } }); fetchMock.postOnce('https://not-called/api/testImpressions/bulk', 200); fetchMock.postOnce('https://not-called/api/events/bulk', 200); - fetchMock.get('https://not-called/api/v2/auth?s=1.1', 200); + fetchMock.get('https://not-called/api/v2/auth?s=1.3', 200); // Validate that init and destroy are idempotent for (let i = 0; i < 3; i++) { splitio.init(); splitio.init(); splitio.destroy(); splitio.destroy(); } @@ -85,13 +85,13 @@ export default function (settings, fetchMock, t) { assert.equal(otherClient.track('user', 'my_event'), true, 'We should track the event'); } - fetchMock.getOnce('https://not-called/api/splitChanges?s=1.2&since=-1', { status: 200, body: { splits: [], since: -1, till: 1457552620999 } }); - fetchMock.getOnce('https://not-called/api/splitChanges?s=1.2&since=1457552620999', { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); + fetchMock.getOnce('https://not-called/api/splitChanges?s=1.3&since=-1&rbSince=-1', { status: 200, body: { ff: { d: [], s: -1, t: 1457552620999 } } }); + fetchMock.getOnce('https://not-called/api/splitChanges?s=1.3&since=1457552620999&rbSince=-1', { status: 200, body: { ff: { d: [], s: 1457552620999, t: 1457552620999 } } }); fetchMock.getOnce('https://not-called/api/memberships/user-99', { status: 200, body: {} }); fetchMock.getOnce('https://not-called/api/memberships/other-user', { status: 200, body: {} }); fetchMock.postOnce('https://not-called/api/testImpressions/bulk', 200); fetchMock.postOnce('https://not-called/api/events/bulk', 200); - fetchMock.get('https://not-called/api/v2/auth?s=1.2&users=user-99', 200); + fetchMock.get('https://not-called/api/v2/auth?s=1.3&users=user-99', 200); // Validate that init and destroy are idempotent for (let i = 0; i < 3; i++) { splitio.init(); splitio.init(); splitio.destroy(); splitio.destroy(); } diff --git a/src/__tests__/nodeSuites/manager.spec.js b/src/__tests__/nodeSuites/manager.spec.js index 869d65e3b..663a4df59 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?s=1.1&since=-1'), overwriteRoutes: true }, { status: 200, body: splitChangesMockReal }); + fetchMock.get({ url: url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), overwriteRoutes: true }, { status: 200, body: splitChangesMockReal }); const mockSplits = splitChangesMockReal; @@ -28,19 +28,19 @@ export default async function (settings, fetchMock, assert) { const splitNames = manager.names(); - assert.equal(splitNames.length, mockSplits.splits.length, 'The manager.splits() method should return all split names on the factory storage.'); - assert.deepEqual(splitNames, map(mockSplits.splits, split => split.name), 'The manager.splits() method should return all split names on the factory storage.'); + assert.equal(splitNames.length, mockSplits.ff.d.length, 'The manager.splits() method should return all split names on the factory storage.'); + assert.deepEqual(splitNames, map(mockSplits.ff.d, split => split.name), 'The manager.splits() method should return all split names on the factory storage.'); const splitObj = manager.split(splitNames[0]); const expectedSplitObj = index => ({ - 'trafficType': mockSplits.splits[index].trafficTypeName, - 'name': mockSplits.splits[index].name, - 'killed': mockSplits.splits[index].killed, - 'changeNumber': mockSplits.splits[index].changeNumber, - 'treatments': map(mockSplits.splits[index].conditions[0].partitions, partition => partition.treatment), - 'configs': mockSplits.splits[index].configurations || {}, - 'sets': mockSplits.splits[index].sets || [], - 'defaultTreatment': mockSplits.splits[index].defaultTreatment, + 'trafficType': mockSplits.ff.d[index].trafficTypeName, + 'name': mockSplits.ff.d[index].name, + 'killed': mockSplits.ff.d[index].killed, + 'changeNumber': mockSplits.ff.d[index].changeNumber, + 'treatments': map(mockSplits.ff.d[index].conditions[0].partitions, partition => partition.treatment), + 'configs': mockSplits.ff.d[index].configurations || {}, + 'sets': mockSplits.ff.d[index].sets || [], + 'defaultTreatment': mockSplits.ff.d[index].defaultTreatment, 'impressionsDisabled': false }); @@ -48,7 +48,7 @@ export default async function (settings, fetchMock, assert) { assert.deepEqual(splitObj, expectedSplitObj(0), 'If we ask for an existent one we receive the expected split view.'); const splitObjects = manager.splits(); - assert.equal(splitObjects.length, mockSplits.splits.length, 'The manager.splits() returns the full collection of split views.'); + assert.equal(splitObjects.length, mockSplits.ff.d.length, 'The manager.splits() returns the full collection of split views.'); assert.deepEqual(splitObjects[0], expectedSplitObj(0), 'And the split views should match the items of the collection in split view format.'); assert.deepEqual(splitObjects[1], expectedSplitObj(1), 'And the split views should match the items of the collection in split view format.'); diff --git a/src/__tests__/nodeSuites/push-fallback.spec.js b/src/__tests__/nodeSuites/push-fallback.spec.js index 7789236a1..f534ea14f 100644 --- a/src/__tests__/nodeSuites/push-fallback.spec.js +++ b/src/__tests__/nodeSuites/push-fallback.spec.js @@ -190,57 +190,57 @@ export function testFallback(fetchMock, assert) { }); - fetchMock.get({ url: url(settings, '/v2/auth?s=1.1'), repeat: 3 /* initial + 2 STREAMING_RESET */ }, function (url, opts) { + fetchMock.get({ url: url(settings, '/v2/auth?s=1.3'), 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?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-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?s=1.1&since=1457552620999'), { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), { status: 200, body: { ff: { d: [], s: 1457552620999, t: 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?s=1.1&since=1457552620999'), { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), { status: 200, body: { ff: { d: [], s: 1457552620999, t: 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?s=1.1&since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), 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 } }; + return { status: 200, body: { ff: { d: [], s: 1457552620999, t: 1457552620999 } } }; }); 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?s=1.1&since=1457552620999'), { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), { status: 200, body: { ff: { d: [], s: 1457552620999, t: 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?s=1.1&since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), 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?s=1.1&since=1457552649999'), { status: 200, body: { splits: [], since: 1457552649999, till: 1457552649999 } }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552649999&rbSince=-1'), { status: 200, body: { ff: { d: [], s: 1457552649999, t: 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?s=1.1&since=1457552649999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552649999&rbSince=-1'), 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 } }; + return { status: 200, body: { ff: { d: [], s: 1457552649999, t: 1457552649999 } } }; }); 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?s=1.1&since=1457552649999'), { status: 200, body: { splits: [], since: 1457552649999, till: 1457552649999 } }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552649999&rbSince=-1'), { status: 200, body: { ff: { d: [], s: 1457552649999, t: 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,22 +249,22 @@ export function testFallback(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?s=1.1&since=1457552649999'), repeat: 3 }, { status: 200, body: { splits: [], since: 1457552649999, till: 1457552649999 } }); + fetchMock.get({ url: url(settings, '/splitChanges?s=1.3&since=1457552649999&rbSince=-1'), repeat: 3 }, { status: 200, body: { ff: { d: [], s: 1457552649999, t: 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?s=1.1&since=1457552649999'), { status: 200, body: { splits: [], since: 1457552649999, till: 1457552649999 } }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552649999&rbSince=-1'), { status: 200, body: { ff: { d: [], s: 1457552649999, t: 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?s=1.1&since=1457552649999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552649999&rbSince=-1'), 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?s=1.1&since=1457552669999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552669999&rbSince=-1'), 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 } }; + return { status: 200, body: { ff: { d: [], s: 1457552669999, t: 1457552669999 } } }; }); fetchMock.getOnce(url(settings, '/segmentChanges/employees?since=1457552650000'), { status: 200, body: { since: 1457552650000, till: 1457552650000, name: 'employees', added: [], removed: [] } }); diff --git a/src/__tests__/nodeSuites/push-flag-sets.spec.js b/src/__tests__/nodeSuites/push-flag-sets.spec.js index d8beb05ac..e8a6fa6d5 100644 --- a/src/__tests__/nodeSuites/push-flag-sets.spec.js +++ b/src/__tests__/nodeSuites/push-flag-sets.spec.js @@ -39,22 +39,22 @@ export function testFlagSets(fetchMock, t) { mockSegmentChanges(fetchMock, new RegExp(baseUrls.sdk + '/segmentChanges/*'), ['some-key']); fetchMock.post('*', 200); - fetchMock.get(baseUrls.auth + '/v2/auth?s=1.1', function (url, opts) { + fetchMock.get(baseUrls.auth + '/v2/auth?s=1.3', 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?s=1.1&since=-1', function () { - return { status: 200, body: { splits: [], since: -1, till: 0}}; + fetchMock.get(baseUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', function () { + return { status: 200, body: { ff: { d: [], s: -1, t: 0 }}}; }); - 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?s=1.3&since=0&rbSince=-1', function () { + return { status: 200, body: { ff: { d: [], s: 0, t: 1 } }}; }); - 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?s=1.3&since=-1&rbSince=-1&sets=set_1,set_2', function () { + return { status: 200, body: { ff: { d: [], s: -1, t: 0 } }}; }); - 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 }}; + fetchMock.get(baseUrls.sdk + '/splitChanges?s=1.3&since=0&rbSince=-1&sets=set_1,set_2', function () { + return { status: 200, body: { ff: { d: [], s: 0, t: 1 } }}; }); const configWithSets = { @@ -194,11 +194,11 @@ export function testFlagSets(fetchMock, t) { setMockListener((eventSourceInstance) => { - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&since=2&sets=set_1,set_2', async function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=2&rbSince=-1&sets=set_1,set_2', async function () { assert.pass('4 - A fetch is triggered due to the SPLIT_KILL'); await client.destroy(); assert.end(); - return { status: 200, body: { splits: [], since: 2, till: 3 }}; + return { status: 200, body: { ff: { d: [], s: 2, t: 3 } }}; }); eventSourceInstance.emitOpen(); @@ -232,9 +232,9 @@ export function testFlagSets(fetchMock, t) { t.test(async (assert) => { - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&since=1&sets=set_1,set_2', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=1&rbSince=-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 }}; + return { status: 200, body: { ff: { d: [], s: 1, t: 5 } }}; }); let splitio, client, manager = []; diff --git a/src/__tests__/nodeSuites/push-initialization-nopush.spec.js b/src/__tests__/nodeSuites/push-initialization-nopush.spec.js index b79db1f1f..d273279cd 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?s=1.1&since=-1'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-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?s=1.1&since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), function () { assert.true(ready, 'client ready'); const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, 0, 100), 'polling (first fetch)'); @@ -58,7 +58,7 @@ function testInitializationFail(fetchMock, assert, fallbackToPolling) { }); } - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), function () { assert.true(ready, 'client ready'); const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, settings.scheduler.featuresRefreshRate, 100), '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?s=1.1', function (url, opts) { + fetchMock.getOnce('https://auth.push-initialization-nopush/api/v2/auth?s=1.3', 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?s=1.1'), function (url, opts) { + fetchMock.getOnce(url(settings, '/v2/auth?s=1.3'), 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?s=1.1'), function (url, opts) { + fetchMock.getOnce(url(settings, '/v2/auth?s=1.3'), 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?s=1.1'), function (url, opts) { + fetchMock.getOnce(url(settings, '/v2/auth?s=1.3'), 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 88cc30af8..e81d7d050 100644 --- a/src/__tests__/nodeSuites/push-initialization-retries.spec.js +++ b/src/__tests__/nodeSuites/push-initialization-retries.spec.js @@ -45,13 +45,13 @@ export function testPushRetriesDueToAuthErrors(fetchMock, assert) { let start, splitio, client, ready = false; - fetchMock.getOnce(url(settings, '/v2/auth?s=1.1'), function (url, opts) { + fetchMock.getOnce(url(settings, '/v2/auth?s=1.3'), 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?s=1.1'), { throws: new TypeError('Network error') }); - fetchMock.getOnce(url(settings, '/v2/auth?s=1.1'), function (url, opts) { + fetchMock.getOnce(url(settings, '/v2/auth?s=1.3'), { throws: new TypeError('Network error') }); + fetchMock.getOnce(url(settings, '/v2/auth?s=1.3'), 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)); @@ -60,23 +60,23 @@ export function testPushRetriesDueToAuthErrors(fetchMock, assert) { }); fetchMock.get(new RegExp(`${url(settings, '/segmentChanges/')}.*`), { status: 200, body: { since: 10, till: 10, name: 'segmentName', added: [], removed: [] } }); - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, 0), 'initial sync'); return { status: 200, body: splitChangesMock1 }; }); - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), 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?s=1.1&since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, settings.scheduler.featuresRefreshRate), 'polling'); return { status: 200, body: splitChangesMock2 }; }); - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), 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(() => { @@ -122,30 +122,30 @@ export function testPushRetriesDueToSseErrors(fetchMock, assert) { sseattempts++; }); - fetchMock.get({ url: url(settings, '/v2/auth?s=1.1'), repeat: 3 /* 3 push attempts */ }, function (url, opts) { + fetchMock.get({ url: url(settings, '/v2/auth?s=1.3'), 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?s=1.1&since=-1'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, 0), 'initial sync'); return { status: 200, body: splitChangesMock1 }; }); - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), 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?s=1.1&since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, settings.scheduler.featuresRefreshRate), 'polling'); return { status: 200, body: splitChangesMock2 }; }); - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), 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?s=1.1'), { status: 200, body: authPushEnabled }, { delay: 100 }); + fetchMock.getOnce(url(settings, '/v2/auth?s=1.3'), { 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?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-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?s=1.1'), { status: 200, body: authPushBadToken }); - fetchMock.getOnce(url(settings, '/v2/auth?s=1.1'), { throws: new TypeError('Network error') }, { delay: 100 }); + fetchMock.getOnce(url(settings, '/v2/auth?s=1.3'), { status: 200, body: authPushBadToken }); + fetchMock.getOnce(url(settings, '/v2/auth?s=1.3'), { 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?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), { status: 200, body: splitChangesMock2 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), { 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 b3246a2c4..9ff9bfa5c 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?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), { status: 200, body: splitChangesMock1 }); // first auth - fetchMock.getOnce(url(settings, '/v2/auth?s=1.1'), function (url, opts) { + fetchMock.getOnce(url(settings, '/v2/auth?s=1.3'), 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?s=1.1&since=1457552620999'), { status: 200, body: splitChangesMock2 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), { status: 200, body: splitChangesMock2 }); // re-auth due to refresh token, with connDelay of 0.5 seconds - fetchMock.getOnce(url(settings, '/v2/auth?s=1.1'), function (url, opts) { + fetchMock.getOnce(url(settings, '/v2/auth?s=1.3'), 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?s=1.1&since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), 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 } }; + return { status: 200, body: { ff: { d: [], s: 1457552620999, t: 1457552620999 } } }; }); // second re-auth due to refresh token, this time responding with pushEnabled false - fetchMock.getOnce(url(settings, '/v2/auth?s=1.1'), function (url, opts) { + fetchMock.getOnce(url(settings, '/v2/auth?s=1.3'), 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?s=1.1&since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), 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 e1154e9d6..2a741dd6b 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?s=1.1'), function (url, opts) { + fetchMock.getOnce(url(settings, '/v2/auth?s=1.3'), 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?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-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?s=1.1&since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), 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?s=1.1&since=1457552620999'), { throws: new TypeError('Network error') }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), { throws: new TypeError('Network error') }); // fetch retry for SPLIT_UPDATE event, due to previous fail - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), 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?s=1.1&since=1457552649999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552649999&rbSince=-1'), 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 + return { status: 200, body: { ff: { d: [], s: 1457552649999, t: 1457552649999 } } }; // 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?s=1.1&since=1457552649999'), { status: 200, body: '{ "since": 1457552620999, "til' }); // invalid JSON + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552649999&rbSince=-1'), { status: 200, body: '{ "since": 1457552620999, "til' }); // invalid JSON // second fetch retry for SPLIT_KILL event - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552649999'), { throws: new TypeError('Network error') }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552649999&rbSince=-1'), { throws: new TypeError('Network error') }); // third fetch retry for SPLIT_KILL event - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552649999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552649999&rbSince=-1'), 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 9f9cb9775..d14bf6bae 100644 --- a/src/__tests__/nodeSuites/push-synchronization.spec.js +++ b/src/__tests__/nodeSuites/push-synchronization.spec.js @@ -215,14 +215,14 @@ export function testSynchronization(fetchMock, assert) { }); // initial auth - fetchMock.getOnce(url(settings, '/v2/auth?s=1.1'), function (url, opts) { + fetchMock.getOnce(url(settings, '/v2/auth?s=1.3'), 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?s=1.1&since=-1'), function (url, opts) { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-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'); @@ -239,7 +239,7 @@ export function testSynchronization(fetchMock, assert) { }); // split and segment sync after SSE opened - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function (url, opts) { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), 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'); @@ -251,7 +251,7 @@ export function testSynchronization(fetchMock, assert) { }); // fetch due to SPLIT_UPDATE event - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function (url, opts) { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), function (url, opts) { if (!hasNoCacheHeader(opts)) assert.fail('request must include `Cache-Control` header'); return { status: 200, body: splitChangesMock3 }; }); @@ -268,14 +268,14 @@ export function testSynchronization(fetchMock, assert) { }); // fetch due to SPLIT_KILL event - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552649999'), function (url, opts) { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552649999&rbSince=-1'), 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?s=1.1&since=1457552650000'), function (url, opts) { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552650000&rbSince=-1'), function (url, opts) { if (!hasNoCacheHeader(opts)) assert.fail('request must include `Cache-Control` header'); return { status: 200, body: splitChangesMock5 }; }); @@ -286,25 +286,25 @@ export function testSynchronization(fetchMock, assert) { }); // fetch feature flags due to IFFU SPLIT_UPDATE event with wrong compress code - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1684265694505'), function (url, opts) { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1684265694505&rbSince=-1'), 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?s=1.1&since=1684265694506'), function (url, opts) { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1684265694506&rbSince=-1'), 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?s=1.1&since=1684265694526'), function (url, opts) { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1684265694526&rbSince=-1'), 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?s=1.1&since=1684265694546'), function (url, opts) { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1684265694546&rbSince=-1'), 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 33e377eb3..03372d16f 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?s=1.1&since=-1', { status: 200, body: splitChangesMock1 }); - fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.1&since=1457552620999', { status: 200, body: splitChangesMock2 }); + fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.3&since=1457552620999&rbSince=-1', { 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 e5811b302..7d0f35619 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?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.3&since=-1&rbSince=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-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?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.3&since=-1&rbSince=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-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?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.3&since=-1&rbSince=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-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?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 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-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?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.get(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-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?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); + fetchMock.get(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-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?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.3&since=-1&rbSince=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-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?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.3&since=-1&rbSince=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-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?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.get(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-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 751e762e2..d27611150 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?s=1.1&since=-1'), 500); // record http exception - fetchMock.getOnce(url(config, '/splitChanges?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(url(config, '/splitChanges?s=1.3&since=-1&rbSince=-1'), 500); // record http exception + fetchMock.getOnce(url(config, '/splitChanges?s=1.3&since=-1&rbSince=-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 05fd60de2..51911c28b 100644 --- a/src/__tests__/online/browser.spec.js +++ b/src/__tests__/online/browser.spec.js @@ -87,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?s=1.2&since=-1'), { status: 200, body: splitChangesMock1 }); - fetchMock.get(url(settings, '/splitChanges?s=1.2&since=1457552620999'), { status: 200, body: splitChangesMock2 }); + fetchMock.get(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.get(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), { status: 200, body: splitChangesMock2 }); fetchMock.get(url(settings, '/memberships/facundo%40split.io'), { status: 200, body: membershipsFacundo }); fetchMock.get(url(settings, '/memberships/nicolas%40split.io'), { status: 200, body: membershipsNicolas }); fetchMock.get(url(settings, '/memberships/marcio%40split.io'), { status: 200, body: membershipsMarcio }); diff --git a/src/__tests__/online/node.spec.js b/src/__tests__/online/node.spec.js index 7b697f0a7..18b2c6283 100644 --- a/src/__tests__/online/node.spec.js +++ b/src/__tests__/online/node.spec.js @@ -39,8 +39,8 @@ const config = { const settings = settingsFactory(config); const key = 'facundo@split.io'; -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, '/splitChanges?s=1.3&since=-1&rbSince=-1'), { status: 200, body: splitChangesMock1 }); +fetchMock.get(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), { status: 200, body: splitChangesMock2 }); fetchMock.get(new RegExp(`${url(settings, '/segmentChanges')}/*`), { status: 200, body: { 'name': 'segment', diff --git a/src/factory/browser.js b/src/factory/browser.js index 8b71ed235..da9ca826c 100644 --- a/src/factory/browser.js +++ b/src/factory/browser.js @@ -20,10 +20,10 @@ const syncManagerOnlineCSFactory = syncManagerOnlineFactory(pollingManagerCSFact function getStorage(settings) { return settings.storage.type === STORAGE_LOCALSTORAGE ? - InLocalStorage(settings.storage) - : settings.storage.__originalType === STORAGE_LOCALSTORAGE ? - __InLocalStorageMockFactory - : InMemoryStorageCSFactory; + InLocalStorage(settings.storage) : + settings.storage.__originalType === STORAGE_LOCALSTORAGE ? + __InLocalStorageMockFactory : + InMemoryStorageCSFactory; } /** diff --git a/src/settings/defaults/version.js b/src/settings/defaults/version.js index eb209ae90..e116c4aa8 100644 --- a/src/settings/defaults/version.js +++ b/src/settings/defaults/version.js @@ -1 +1 @@ -export const packageVersion = '11.1.0'; +export const packageVersion = '11.2.0'; diff --git a/src/settings/node.js b/src/settings/node.js index 5fe7d5535..bf559ba7d 100644 --- a/src/settings/node.js +++ b/src/settings/node.js @@ -5,15 +5,11 @@ import { defaults } from './defaults/node'; import { validateStorage } from './storage/node'; import { validateRuntime } from './runtime/node'; -const FLAG_SPEC_VERSION = '1.1'; - const params = { defaults, runtime: validateRuntime, storage: validateStorage, logger: validateLogger, - flagSpec: () => FLAG_SPEC_VERSION - // In Node.js the SDK ignores `config.integrations`, so a validator for integrations is not required }; export function settingsFactory(config) { diff --git a/src/settings/storage/browser.js b/src/settings/storage/browser.js index 10a9e3eea..0165d45e3 100644 --- a/src/settings/storage/browser.js +++ b/src/settings/storage/browser.js @@ -10,7 +10,9 @@ export function validateStorage(settings) { storage: { type, options = {}, - prefix + prefix, + expirationDays, + clearOnInit } = { type: STORAGE_MEMORY }, } = settings; let __originalType; @@ -38,6 +40,8 @@ export function validateStorage(settings) { type, options, prefix, + expirationDays, + clearOnInit, __originalType }; } diff --git a/ts-tests/index.ts b/ts-tests/index.ts index 823e00771..588f5ec15 100644 --- a/ts-tests/index.ts +++ b/ts-tests/index.ts @@ -84,6 +84,14 @@ const attributes: SplitIO.Attributes = { attr6: [1, 2], attr7: true }; +const evaluationOptions: SplitIO.EvaluationOptions = { + properties: { + prop1: 1, + prop2: '2', + prop3: true, + prop4: null + } +}; const splitKeyObj: SplitIO.SplitKeyObject = { matchingKey: 'matchingKey', bucketingKey: 'bucketingKey' @@ -254,58 +262,74 @@ promise = SDK.destroy(); // We can call getTreatment with or without a key. treatment = client.getTreatment(splitKey, 'mySplit'); treatment = browserClient.getTreatment('mySplit'); -// Attributes parameter is optional on both signatures. +// Attributes and EvaluationOptions parameters are optional treatment = client.getTreatment(splitKey, 'mySplit', attributes); +treatment = client.getTreatment(splitKey, 'mySplit', undefined, evaluationOptions); treatment = browserClient.getTreatment('mySplit', attributes); +treatment = browserClient.getTreatment('mySplit', undefined, evaluationOptions); // We can call getTreatments with or without a key. treatments = client.getTreatments(splitKey, ['mySplit']); treatments = browserClient.getTreatments(['mySplit']); -// Attributes parameter is optional on both signatures. +// Attributes and EvaluationOptions parameters are optional treatments = client.getTreatments(splitKey, ['mySplit'], attributes); +treatments = client.getTreatments(splitKey, ['mySplit'], undefined, evaluationOptions); treatments = browserClient.getTreatments(['mySplit'], attributes); +treatments = browserClient.getTreatments(['mySplit'], undefined, evaluationOptions); // We can call getTreatmentWithConfig with or without a key. treatmentWithConfig = client.getTreatmentWithConfig(splitKey, 'mySplit'); treatmentWithConfig = browserClient.getTreatmentWithConfig('mySplit'); -// Attributes parameter is optional on both signatures. +// Attributes and EvaluationOptions parameters are optional treatmentWithConfig = client.getTreatmentWithConfig(splitKey, 'mySplit', attributes); +treatmentWithConfig = client.getTreatmentWithConfig(splitKey, 'mySplit', undefined, evaluationOptions); treatmentWithConfig = browserClient.getTreatmentWithConfig('mySplit', attributes); +treatmentWithConfig = browserClient.getTreatmentWithConfig('mySplit', undefined, evaluationOptions); // We can call getTreatmentsWithConfig with or without a key. treatmentsWithConfig = client.getTreatmentsWithConfig(splitKey, ['mySplit']); treatmentsWithConfig = browserClient.getTreatmentsWithConfig(['mySplit']); -// Attributes parameter is optional on both signatures. +// Attributes and EvaluationOptions parameters are optional treatmentsWithConfig = client.getTreatmentsWithConfig(splitKey, ['mySplit'], attributes); +treatmentsWithConfig = client.getTreatmentsWithConfig(splitKey, ['mySplit'], undefined, evaluationOptions); treatmentsWithConfig = browserClient.getTreatmentsWithConfig(['mySplit'], attributes); +treatmentsWithConfig = browserClient.getTreatmentsWithConfig(['mySplit'], undefined, evaluationOptions); // We can call getTreatmentsByFlagSet with or without a key. treatments = client.getTreatmentsByFlagSet(splitKey, 'set_a'); treatments = browserClient.getTreatmentsByFlagSet('set_a'); -// Attributes parameter is optional. +// Attributes and EvaluationOptions parameters are optional treatments = client.getTreatmentsByFlagSet(splitKey, 'set_a', attributes); +treatments = client.getTreatmentsByFlagSet(splitKey, 'set_a', undefined, evaluationOptions); treatments = browserClient.getTreatmentsByFlagSet('set_a', attributes); +treatments = browserClient.getTreatmentsByFlagSet('set_a', undefined, evaluationOptions); // We can call getTreatmentsByFlagSets with or without a key. treatments = client.getTreatmentsByFlagSets(splitKey, ['set_a']); treatments = browserClient.getTreatmentsByFlagSets(['set_a']); -// Attributes parameter is optional. +// Attributes and EvaluationOptions parameters are optional treatments = client.getTreatmentsByFlagSets(splitKey, ['set_a'], attributes); +treatments = client.getTreatmentsByFlagSets(splitKey, ['set_a'], undefined, evaluationOptions); treatments = browserClient.getTreatmentsByFlagSets(['set_a'], attributes); +treatments = browserClient.getTreatmentsByFlagSets(['set_a'], undefined, evaluationOptions); // We can call getTreatmentsWithConfigByFlagSet with or without a key. treatmentsWithConfig = client.getTreatmentsWithConfigByFlagSet(splitKey, 'set_a'); treatmentsWithConfig = browserClient.getTreatmentsWithConfigByFlagSet('set_a'); -// Attributes parameter is optional. +// Attributes and EvaluationOptions parameters are optional treatmentsWithConfig = client.getTreatmentsWithConfigByFlagSet(splitKey, 'set_a', attributes); +treatmentsWithConfig = client.getTreatmentsWithConfigByFlagSet(splitKey, 'set_a', undefined, evaluationOptions); treatmentsWithConfig = browserClient.getTreatmentsWithConfigByFlagSet('set_a', attributes); +treatmentsWithConfig = browserClient.getTreatmentsWithConfigByFlagSet('set_a', undefined, evaluationOptions); // We can call getTreatmentsWithConfigByFlagSets with or without a key. treatmentsWithConfig = client.getTreatmentsWithConfigByFlagSets(splitKey, ['set_a']); treatmentsWithConfig = browserClient.getTreatmentsWithConfigByFlagSets(['set_a']); -// Attributes parameter is optional. +// Attributes and EvaluationOptions parameters are optional treatmentsWithConfig = client.getTreatmentsWithConfigByFlagSets(splitKey, ['set_a'], attributes); +treatmentsWithConfig = client.getTreatmentsWithConfigByFlagSets(splitKey, ['set_a'], undefined, evaluationOptions); treatmentsWithConfig = browserClient.getTreatmentsWithConfigByFlagSets(['set_a'], attributes); +treatmentsWithConfig = browserClient.getTreatmentsWithConfigByFlagSets(['set_a'], undefined, evaluationOptions); // We can call track with or without a key. tracked = client.track(splitKey, 'myTrafficType', 'myEventType'); // all params @@ -343,43 +367,51 @@ promise = AsyncSDK.destroy(); // We can call getTreatment but always with a key. asyncTreatment = asyncClient.getTreatment(splitKey, 'mySplit'); -// Attributes parameter is optional +// Attributes and EvaluationOptions parameters are optional asyncTreatment = asyncClient.getTreatment(splitKey, 'mySplit', attributes); +asyncTreatment = asyncClient.getTreatment(splitKey, 'mySplit', undefined, evaluationOptions); // We can call getTreatments but always with a key. asyncTreatments = asyncClient.getTreatments(splitKey, ['mySplit']); -// Attributes parameter is optional +// Attributes and EvaluationOptions parameters are optional asyncTreatments = asyncClient.getTreatments(splitKey, ['mySplit'], attributes); +asyncTreatments = asyncClient.getTreatments(splitKey, ['mySplit'], undefined, evaluationOptions); // We can call getTreatmentWithConfig but always with a key. asyncTreatmentWithConfig = asyncClient.getTreatmentWithConfig(splitKey, 'mySplit'); -// Attributes parameter is optional +// Attributes and EvaluationOptions parameters are optional asyncTreatmentWithConfig = asyncClient.getTreatmentWithConfig(splitKey, 'mySplit', attributes); +asyncTreatmentWithConfig = asyncClient.getTreatmentWithConfig(splitKey, 'mySplit', undefined, evaluationOptions); // We can call getTreatments but always with a key. asyncTreatmentsWithConfig = asyncClient.getTreatmentsWithConfig(splitKey, ['mySplit']); -// Attributes parameter is optional +// Attributes and EvaluationOptions parameters are optional asyncTreatmentsWithConfig = asyncClient.getTreatmentsWithConfig(splitKey, ['mySplit'], attributes); +asyncTreatmentsWithConfig = asyncClient.getTreatmentsWithConfig(splitKey, ['mySplit'], undefined, evaluationOptions); // We can call getTreatmentsByFlagSet asyncTreatments = asyncClient.getTreatmentsByFlagSet(splitKey, 'set_a'); -// Attributes parameter is optional +// Attributes and EvaluationOptions parameters are optional asyncTreatments = asyncClient.getTreatmentsByFlagSet(splitKey, 'set_a', attributes); +asyncTreatments = asyncClient.getTreatmentsByFlagSet(splitKey, 'set_a', undefined, evaluationOptions); // We can call getTreatmentsByFlagSets asyncTreatments = asyncClient.getTreatmentsByFlagSets(splitKey, ['set_a']); -// Attributes parameter is optional +// Attributes and EvaluationOptions parameters are optional asyncTreatments = asyncClient.getTreatmentsByFlagSets(splitKey, ['set_a'], attributes); +asyncTreatments = asyncClient.getTreatmentsByFlagSets(splitKey, ['set_a'], undefined, evaluationOptions); // We can call getTreatmentsWithConfigByFlagSet asyncTreatmentsWithConfig = asyncClient.getTreatmentsWithConfigByFlagSet(splitKey, 'set_a'); -// Attributes parameter is optional +// Attributes and EvaluationOptions parameters are optional asyncTreatmentsWithConfig = asyncClient.getTreatmentsWithConfigByFlagSet(splitKey, 'set_a', attributes); +asyncTreatmentsWithConfig = asyncClient.getTreatmentsWithConfigByFlagSet(splitKey, 'set_a', undefined, evaluationOptions); // We can call getTreatmentsByFlagSets but always with a key. asyncTreatmentsWithConfig = asyncClient.getTreatmentsWithConfigByFlagSets(splitKey, ['set_a']); -// Attributes parameter is optional +// Attributes and EvaluationOptions parameters are optional asyncTreatmentsWithConfig = asyncClient.getTreatmentsWithConfigByFlagSets(splitKey, ['set_a'], attributes); +asyncTreatmentsWithConfig = asyncClient.getTreatmentsWithConfigByFlagSets(splitKey, ['set_a'], undefined, evaluationOptions); // We can call track only with a key. trackPromise = asyncClient.track(splitKey, 'myTrafficType', 'myEventType'); // all required params @@ -531,7 +563,9 @@ let fullBrowserSettings: SplitIO.IBrowserSettings = { features: mockedFeaturesMap, storage: { type: 'LOCALSTORAGE', - prefix: 'PREFIX' + prefix: 'PREFIX', + expirationDays: 1, + clearOnInit: true }, impressionListener: impressionListener, debug: true,