diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 470b1d5..eade2ac 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -25,7 +25,7 @@ jobs: uses: actions/checkout@v4 - name: Setup Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: 'lts/*' cache: 'npm' diff --git a/.github/workflows/sonar-scan.yml b/.github/workflows/sonar-scan.yml index f10bda2..e19a6b9 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 c0c9895..fe57fc2 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,8 @@ +1.3.0 (May 22, 2025) + - Added support for targeting rules based on rule-based segments. + - Added support for feature flag prerequisites. + - Updated @splitsoftware/splitio-commons package to version 2.4.0. + 1.2.0 (March 31, 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 `InLocalStorage` module to control the behavior of the persisted rollout plan cache in the browser: @@ -8,10 +13,10 @@ 1.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. - - Bugfixing - Updated @splitsoftware/splitio-commons package to version 2.1.0, which properly handles rejected promises when using targeting rules with segment matchers in consumer modes (e.g., Redis and Pluggable storages). + - Bugfix - Updated @splitsoftware/splitio-commons package to version 2.1.0, which properly handles rejected promises when using targeting rules with segment matchers in consumer modes (e.g., Redis and Pluggable storages). 1.0.1 (November 11, 2024) - - Bugfixing - Revert removal of TypeScript `SplitIO` namespace at `/types/splitio.d.ts` to allow explicit imports of types from the Browser SDK package. E.g., `import type { IClientSideSettings } from '@splitsoftware/splitio-browserjs/types/splitio';`. + - Bugfix - Revert removal of TypeScript `SplitIO` namespace at `/types/splitio.d.ts` to allow explicit imports of types from the Browser SDK package. E.g., `import type { IClientSideSettings } from '@splitsoftware/splitio-browserjs/types/splitio';`. 1.0.0 (November 1, 2024) - Added support for targeting rules based on large segments. @@ -33,8 +38,8 @@ 0.14.1 (June 14, 2024) - Updated @splitsoftware/splitio-commons package to version 1.16.0 that includes some vulnerability and bug fixes. - - Bugfixing - Restored some input validation error logs that were removed in version 0.12.0. The logs inform the user when the `getTreatment(s)` methods are called with an invalid value as feature flag name or flag set name. - - Bugfixing - Fixed localhost mode to emit SDK_UPDATE when mocked feature flags are updated in the `features` object map of the config object (Related to issue https://github.com/splitio/javascript-browser-client/issues/119). + - Bugfix - Restored some input validation error logs that were removed in version 0.12.0. The logs inform the user when the `getTreatment(s)` methods are called with an invalid value as feature flag name or flag set name. + - Bugfix - Fixed localhost mode to emit SDK_UPDATE when mocked feature flags are updated in the `features` object map of the config object (Related to issue https://github.com/splitio/javascript-browser-client/issues/119). 0.14.0 (May 6, 2024) - Updated @splitsoftware/splitio-commons package to version 1.14.0 that includes minor updates: @@ -44,7 +49,7 @@ 0.13.2 (March 26, 2024) - Updated some transitive dependencies for vulnerability fixes. - - Bugfixing - Added tslib as an explicit dependency to avoid issues with some package managers that don't resolve it automatically as a transitive dependency from @splitsoftware/splitio-commons (Related to issue https://github.com/splitio/javascript-client/issues/795). + - Bugfix - Added tslib as an explicit dependency to avoid issues with some package managers that don't resolve it automatically as a transitive dependency from @splitsoftware/splitio-commons (Related to issue https://github.com/splitio/javascript-client/issues/795). 0.13.1 (January 18, 2024) - Updated @splitsoftware/splitio-commons package to version 1.13.1 and some transitive dependencies for vulnerability fixes. @@ -53,7 +58,7 @@ - Added support for Flag Sets in "consumer" and "partial consumer" modes (pluggable storage). - Updated SDK cache for browsers using localStorage, to clear cached feature flag definitions before initiating the synchronization process if the cache was previously synchronized with a different SDK key (i.e., a different environment) or different Split Filter criteria, to avoid using invalid cached data when the SDK is ready from cache. - Updated @splitsoftware/splitio-commons package to version 1.12.1 that includes flag sets support for consumer modes, and other improvements. - - Bugfixing - Fixed manager methods in consumer modes to return results in a promise when the SDK is not operational (not ready or destroyed). + - Bugfix - Fixed manager methods in consumer modes to return results in a promise when the SDK is not operational (not ready or destroyed). 0.12.0 (November 3, 2023) - Added support for Flag Sets on the SDK, which enables grouping feature flags and interacting with the group rather than individually (more details in our documentation): @@ -80,7 +85,7 @@ - Updated some transitive dependencies for vulnerability fixes. - Updated @splitsoftware/splitio-commons package to version 1.8.3 that includes: - Updated SDK_READY_TIMED_OUT event to be emitted immediately when a connection error occurs using pluggable storage (i.e., when the wrapper `connect` promise is rejected) in consumer and partial consumer modes. - - Bugfixing - The `destroy` method has been updated to immediately flag the SDK client as destroyed, to prevent unexpected behaviors when `getTreatment` and `track` methods are called synchronously after `destroy` method is called. + - Bugfix - The `destroy` method has been updated to immediately flag the SDK client as destroyed, to prevent unexpected behaviors when `getTreatment` and `track` methods are called synchronously after `destroy` method is called. 0.9.5 (May 15, 2023) - Updated @splitsoftware/splitio-commons package to version 1.8.2 that includes minor improvements. @@ -90,7 +95,7 @@ 0.9.4 (May 4, 2023) - Updated some transitive dependencies for vulnerability fixes. - - Bugfixing - Updated `unfetch` package as a runtime dependency, required when using the "full" import (`import { SplitFactory } from '@splitsoftware/splitio-browserjs/full'`). + - Bugfix - Updated `unfetch` package as a runtime dependency, required when using the "full" import (`import { SplitFactory } from '@splitsoftware/splitio-browserjs/full'`). 0.9.3 (March 20, 2023) - Updated @splitsoftware/splitio-commons package to version 1.8.1 that includes minor improvements. @@ -98,10 +103,10 @@ 0.9.2 (December 16, 2022) - Updated some transitive dependencies for vulnerability fixes. - - Bugfixing - Upgrade @splitsoftware/splitio-commons package to version 1.7.3 which includes a memory leak fix for localhost mode (Related to issue https://github.com/splitio/javascript-commons/issues/181) among other improvements. + - Bugfix - Upgrade @splitsoftware/splitio-commons package to version 1.7.3 which includes a memory leak fix for localhost mode (Related to issue https://github.com/splitio/javascript-commons/issues/181) among other improvements. 0.9.1 (October 14, 2022) - - Bugfixing - Upgrade @splitsoftware/splitio-commons package to version 1.7.2, that handles `Navigator.sendBeacon` API exceptions in the browser, and fallback to regular Fetch/XHR transport in case of error. + - Bugfix - Upgrade @splitsoftware/splitio-commons package to version 1.7.2, that handles `Navigator.sendBeacon` API exceptions in the browser, and fallback to regular Fetch/XHR transport in case of error. 0.9.0 (October 5, 2022) - Added a new impressions mode for the SDK called NONE, to be used in factory when there is no desire to capture impressions on an SDK factory to feed Split's analytics engine. Running NONE mode, the SDK will only capture unique keys evaluated for a particular feature flag instead of full blown impressions. @@ -118,23 +123,23 @@ - Added a new config option to control the tasks that listen or poll for updates on feature flags and segments, via the new config `sync.enabled`. Running online, Split will always pull the most recent updates upon initialization, this only affects updates fetching on a running instance. Useful when a consistent session experience is a must or to save resources when updates are not being used. - Updated telemetry logic to track the anonymous config for user consent flag set to declined or unknown. - Updated submitters logic, to avoid duplicating the post of impressions to Split cloud when the SDK is destroyed while its periodic post of impressions is running. - - Bugfixing - Updated submitters logic, to avoid dropping impressions and events that are being tracked while POST request is pending. + - Bugfix - Updated submitters logic, to avoid dropping impressions and events that are being tracked while POST request is pending. 0.6.0 (May 24, 2022) - Added `scheduler.telemetryRefreshRate` property to SDK configuration. - Updated SDK telemetry storage, metrics and updater to be more effective and send less often. - - Bugfixing - Updated default values for `scheduler.impressionsRefreshRate` config parameter: 300s for OPTIMIZED impression mode and 60s for DEBUG impression mode (previously it was 60s for both). + - Bugfix - Updated default values for `scheduler.impressionsRefreshRate` config parameter: 300s for OPTIMIZED impression mode and 60s for DEBUG impression mode (previously it was 60s for both). 0.5.0 (April 7, 2022) - Added user consent feature to allow delaying or disabling the data tracking from SDK until user consent is explicitly granted or declined. Read more in our docs. - Added `scheduler.impressionsQueueSize` property to SDK configuration to limit the amount of impressions tracked in memory. Read more in our docs. - Updated format for MySegments keys in LocalStorage, keeping backwards compatibility (issue https://github.com/splitio/javascript-client/issues/638). - Updated some dependencies for vulnerability fixes. - - Bugfixing - Updated internal isObject utility function, to avoid unexpected behaviors on frameworks and libraries that uses multiple VM contexts, like NuxtJS dev server. - - Bugfixing - Fixed validation of `core.key` SDK configuration param, to parse it into a string and log a warning when passing a number (Related to issue https://github.com/splitio/react-native-client/issues/19). - - Bugfixing - Fixed validation of `sync.impressionsMode` SDK configuration param, to avoid an exception on SplitFactory instantiation when passing a non-string value. - - Bugfixing - Fixed streaming synchronization issue when using multiple clients. - - Bugfixing - Fixed issue with internal Map ponyfill that results in logger not working properly on IE11 browser. + - Bugfix - Updated internal isObject utility function, to avoid unexpected behaviors on frameworks and libraries that uses multiple VM contexts, like NuxtJS dev server. + - Bugfix - Fixed validation of `core.key` SDK configuration param, to parse it into a string and log a warning when passing a number (Related to issue https://github.com/splitio/react-native-client/issues/19). + - Bugfix - Fixed validation of `sync.impressionsMode` SDK configuration param, to avoid an exception on SplitFactory instantiation when passing a non-string value. + - Bugfix - Fixed streaming synchronization issue when using multiple clients. + - Bugfix - Fixed issue with internal Map ponyfill that results in logger not working properly on IE11 browser. 0.4.1 (February 22, 2022) - Updated karma and some transitive dependencies for vulnerability fixes. @@ -150,7 +155,7 @@ - Updated @splitsoftware/splitio-commons dependency to version 1.1.0, which includes: - Updated multiple modules due to general polishing and improvements, including the replacement of default exports with named exports, to avoid runtime errors with some particular configurations of Webpack projects. - - Bugfixing - Fixed issue returning dynamic configs if treatment name contains a dot ("."). + - Bugfix - Fixed issue returning dynamic configs if treatment name contains a dot ("."). 0.2.0 (October 20, 2021) - Added localhost mode support (Read more in our docs here: https://help.split.io/hc/en-us/articles/360058730852-Browser-SDK#localhost-mode). diff --git a/package-lock.json b/package-lock.json index feb5856..b486a2f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,15 @@ { "name": "@splitsoftware/splitio-browserjs", - "version": "1.2.0", + "version": "1.2.1-rc.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@splitsoftware/splitio-browserjs", - "version": "1.2.0", + "version": "1.2.1-rc.0", "license": "Apache-2.0", "dependencies": { - "@splitsoftware/splitio-commons": "2.2.0", + "@splitsoftware/splitio-commons": "2.3.1-rc.0", "tslib": "^2.3.1", "unfetch": "^4.2.0" }, @@ -1396,9 +1396,9 @@ "dev": true }, "node_modules/@splitsoftware/splitio-commons": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.2.0.tgz", - "integrity": "sha512-ywWDh2fM4/EqJ1AByjXM13gAal+z/WSGiBQ5OZmjpL/iqFLENy3yo/GwsxR/ataOi27XbRQTeQbE/eD7HVnWiA==", + "version": "2.3.1-rc.0", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.3.1-rc.0.tgz", + "integrity": "sha512-+IQdkvy9FPD/r2v40vew8wpVFMPbFWmYXaQ1uhTgytax3nLI0TokoSjEBizEfE7B6xcQ22AZ4ogAqDnIvQBryw==", "license": "Apache-2.0", "dependencies": { "@types/ioredis": "^4.28.0", @@ -10411,9 +10411,9 @@ "dev": true }, "@splitsoftware/splitio-commons": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.2.0.tgz", - "integrity": "sha512-ywWDh2fM4/EqJ1AByjXM13gAal+z/WSGiBQ5OZmjpL/iqFLENy3yo/GwsxR/ataOi27XbRQTeQbE/eD7HVnWiA==", + "version": "2.3.1-rc.0", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.3.1-rc.0.tgz", + "integrity": "sha512-+IQdkvy9FPD/r2v40vew8wpVFMPbFWmYXaQ1uhTgytax3nLI0TokoSjEBizEfE7B6xcQ22AZ4ogAqDnIvQBryw==", "requires": { "@types/ioredis": "^4.28.0", "tslib": "^2.3.1" diff --git a/package.json b/package.json index a9a01de..63c6c4f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-browserjs", - "version": "1.2.0", + "version": "1.2.1-rc.0", "description": "Split SDK for JavaScript on Browser", "main": "cjs/index.js", "module": "esm/index.js", @@ -59,7 +59,7 @@ "bugs": "https://github.com/splitio/javascript-browser-client/issues", "homepage": "https://github.com/splitio/javascript-browser-client#readme", "dependencies": { - "@splitsoftware/splitio-commons": "2.2.0", + "@splitsoftware/splitio-commons": "2.3.1-rc.0", "tslib": "^2.3.1", "unfetch": "^4.2.0" }, diff --git a/src/__tests__/browserSuites/fetch-specific-splits.spec.js b/src/__tests__/browserSuites/fetch-specific-splits.spec.js index 6f7a590..577bbd4 100644 --- a/src/__tests__/browserSuites/fetch-specific-splits.spec.js +++ b/src/__tests__/browserSuites/fetch-specific-splits.spec.js @@ -24,13 +24,13 @@ export default 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: { splits: [], since: -1, till: 1457552620999 } }); + fetchMock.getOnce(urls.sdk + '/splitChanges?s=1.3&since=1457552620999&rbSince=-1' + queryString, { status: 200, body: { splits: [], since: 1457552620999, till: 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': {} } }); diff --git a/src/__tests__/browserSuites/ignore-ip-addresses-setting.spec.js b/src/__tests__/browserSuites/ignore-ip-addresses-setting.spec.js index 6010806..599a957 100644 --- a/src/__tests__/browserSuites/ignore-ip-addresses-setting.spec.js +++ b/src/__tests__/browserSuites/ignore-ip-addresses-setting.spec.js @@ -101,8 +101,7 @@ 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, `/memberships/${encodeURIComponent(config.core.key)}`), { status: 200, body: { ms: {} } }); // Init Split client diff --git a/src/__tests__/browserSuites/impressions.debug.spec.js b/src/__tests__/browserSuites/impressions.debug.spec.js index ce40a0f..3ca81a7 100644 --- a/src/__tests__/browserSuites/impressions.debug.spec.js +++ b/src/__tests__/browserSuites/impressions.debug.spec.js @@ -1,7 +1,6 @@ import { SplitFactory } from '../../'; import { settingsFactory } from '../../settings'; import splitChangesMock1 from '../mocks/splitchanges.since.-1.json'; -import splitChangesMock2 from '../mocks/splitchanges.since.1457552620999.json'; import membershipsFacundo from '../mocks/memberships.facundo@split.io.json'; import { DEBUG } from '@splitsoftware/splitio-commons/src/utils/constants'; import { truncateTimeFrame } from '@splitsoftware/splitio-commons/src/utils/time'; @@ -24,8 +23,7 @@ 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, '/memberships/facundo%40split.io'), { status: 200, body: membershipsFacundo }); const splitio = SplitFactory({ diff --git a/src/__tests__/browserSuites/impressions.spec.js b/src/__tests__/browserSuites/impressions.spec.js index d271beb..a6caa47 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=100'), { status: 200, body: splitChangesMock2 }); fetchMock.get(url(settings, '/memberships/facundo%40split.io'), { status: 200, body: membershipsFacundo }); const splitio = SplitFactory({ diff --git a/src/__tests__/browserSuites/manager.spec.js b/src/__tests__/browserSuites/manager.spec.js index 3f6b0c5..8b76731 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,27 +29,28 @@ 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, - 'impressionsDisabled': false + '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, + 'prerequisites': [] }); assert.equal(manager.split('non_existent'), null, 'Trying to get a manager.split() of a Split that does not exist returns null.'); 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 fe3e778..a211839 100644 --- a/src/__tests__/browserSuites/push-corner-cases.spec.js +++ b/src/__tests__/browserSuites/push-corner-cases.spec.js @@ -1,5 +1,6 @@ import { getStorageHash } from '@splitsoftware/splitio-commons/src/storages/KeyBuilder'; import splitChangesMock1 from '../mocks/splitchanges.since.-1.json'; +import splitChangesMock2 from '../mocks/splitchanges.since.1457552620999.json'; import splitKillMessage from '../mocks/message.SPLIT_KILL.1457552650000.json'; import authPushEnabledNicolas from '../mocks/auth.pushEnabled.nicolas@split.io.json'; import { nearlyEqual, url } from '../testUtils'; @@ -70,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=1457552620999&rbSince=100'), { status: 200, body: splitChangesMock2 }, { 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 dc8df88..f85223a 100644 --- a/src/__tests__/browserSuites/push-fallback.spec.js +++ b/src/__tests__/browserSuites/push-fallback.spec.js @@ -2,9 +2,9 @@ * Validate the handling of OCCUPANCY and CONTROL events */ -import splitChangesMock1 from '../mocks/splitchanges.real.withSegments.json'; // since: -1, till: 1457552620999 (for initial fetch) -import splitChangesMock2 from '../mocks/splitchanges.real.updateWithSegments.json'; // since: 1457552620999, till: 1457552649999 (for SPLIT_UPDATE event) -import splitChangesMock3 from '../mocks/splitchanges.real.updateWithoutSegments.json'; // since: 1457552649999, till: 1457552669999 (for second polling fetch) +import splitChangesMockReal1 from '../mocks/splitchanges.real.withSegments.json'; // since: -1, till: 1457552620999 (for initial fetch) +import splitChangesMockReal2 from '../mocks/splitchanges.real.updateWithSegments.json'; // since: 1457552620999, till: 1457552649999 (for SPLIT_UPDATE event) +import splitChangesMockReal3 from '../mocks/splitchanges.real.updateWithoutSegments.json'; // since: 1457552649999, till: 1457552669999 (for second polling fetch) import membershipsNicolasMock1 from '../mocks/memberships.nicolas@split.io.json'; import membershipsNicolasMock2 from '../mocks/memberships.nicolas@split.io.mock2.json'; import membershipsMarcio from '../mocks/memberships.marcio@split.io.json'; @@ -206,59 +206,59 @@ 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: splitChangesMockReal1 }); 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 }; + return { status: 200, body: splitChangesMockReal2 }; }); // 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: { splits: [], since: 1457552649999, till: 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 } }; @@ -267,7 +267,7 @@ export function testFallback(fetchMock, assert) { 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: { splits: [], since: 1457552649999, till: 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: { splits: [], since: 1457552649999, till: 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'); - return { status: 200, body: splitChangesMock3 }; + return { status: 200, body: splitChangesMockReal3 }; }); - 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 } }; + return { status: 200, body: { ff: { d: [], s: 1457552669999, t: 1457552669999 } } }; }); fetchMock.get(new RegExp('.*'), function (url) { diff --git a/src/__tests__/browserSuites/push-initialization-nopush.spec.js b/src/__tests__/browserSuites/push-initialization-nopush.spec.js index 1df52e5..40007b3 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=100'), 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=100'), 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 027cece..77e0ad7 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=100'), 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=100'), 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=100'), 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=100'), 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=100'), 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=100'), 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=100'), { 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 8e22592..6acf48d 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=100'), { 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=100'), 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=100'), 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 db0374a..c88f65e 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=100'), 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=100'), { 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=100'), 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=100'), 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=100'), { 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=100'), { 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=100'), 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 4086050..09055b2 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=100'), 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=100'), 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=100'), 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=100'), 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=100'), { status: 200, body: { splits: [], since: 1457552650000, till: 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 6829965..5fa7648 100644 --- a/src/__tests__/browserSuites/readiness.spec.js +++ b/src/__tests__/browserSuites/readiness.spec.js @@ -1,7 +1,6 @@ import { SplitFactory, InLocalStorage } from '../../'; 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'; // mocks for memberships readiness tests @@ -38,13 +37,12 @@ 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 }); const splitio = SplitFactory({ ...baseConfig, urls: testUrls @@ -67,13 +65,12 @@ 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 }); const splitio = SplitFactory({ ...baseConfig, urls: testUrls }); const client = splitio.client(); @@ -95,16 +92,15 @@ 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 }); const splitio = SplitFactory({ ...baseConfig, urls: testUrls }); const client = splitio.client(); @@ -122,26 +118,25 @@ export default function (fetchMock, assert) { /************** Now we will validate the intelligent memberships pausing, which requires lots of code. Related code below. **************/ localStorage.clear(); const membershipsEndpointDelay = 450; - function mockForSegmentsPauseTest(testUrls, startWithSegments = false) { + function mockForSegmentsPauseTest(testUrls, startWithSegments) { let membershipsHits = 0; fetchMock.get(new RegExp(`${testUrls.sdk}/memberships/nicolas\\d?%40split.io`), function () { // Mock any memberships call, so we can test with multiple clients. membershipsHits++; 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 } }); - 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 } } }); + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.3&since=1457552669999&rbSince=-1', { status: 200, body: { ff: { d: [], s: 1457552669999, 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: { ff: splitChangesUpdateWithSegmentsMock.ff } }); + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.3&since=1457552649999&rbSince=-1', { status: 200, body: { ff: splitChangesUpdateWithoutSegmentsMock.ff } }); + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.3&since=1457552669999&rbSince=-1', { status: 200, body: { ff: { d: [], s: 1457552669999, t: 1457552669999 } } }); } return () => membershipsHits; @@ -268,7 +263,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, () => { @@ -530,8 +525,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({ @@ -617,8 +612,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 2afb8cf..cc123e6 100644 --- a/src/__tests__/browserSuites/ready-from-cache.spec.js +++ b/src/__tests__/browserSuites/ready-from-cache.spec.js @@ -83,8 +83,8 @@ const baseConfig = { streamingEnabled: false }; -const expectedHashNullFilter = '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) { @@ -96,8 +96,7 @@ export default function (fetchMock, assert) { localStorage.clear(); 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 + '/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': {} } }); @@ -145,10 +144,9 @@ export default function (fetchMock, assert) { localStorage.clear(); t.plan(12 * 2 + 3); - fetchMock.get(testUrls.sdk + '/splitChanges?s=1.2&since=25', () => { - 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', () => { return new Promise(res => { setTimeout(() => res({ status: 200, body: membershipsNicolas, headers: {} }), 400); }); // First client gets segments before splits. No segment cache loading (yet) }); @@ -251,11 +249,10 @@ export default function (fetchMock, assert) { localStorage.clear(); t.plan(12 * 2 + 5); - fetchMock.get(testUrls.sdk + '/splitChanges?s=1.2&since=25', () => { + 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', () => { return new Promise(res => { setTimeout(() => res({ status: 200, body: membershipsNicolas, headers: {} }), 400); }); // First client gets segments before splits. No segment cache loading (yet) }); @@ -368,14 +365,13 @@ export default function (fetchMock, assert) { }; localStorage.clear(); - fetchMock.get(testUrls.sdk + '/splitChanges?s=1.2&since=-1', () => { + 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.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', () => { 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) }); @@ -481,7 +477,7 @@ export default function (fetchMock, assert) { localStorage.clear(); 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: { 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 + '/memberships/nicolas%40split.io', { status: 200, body: { ms: {} } }); localStorage.setItem('some_user_item', 'user_item'); @@ -530,7 +526,7 @@ export default function (fetchMock, assert) { localStorage.clear(); 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: { 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 + '/memberships/nicolas%40split.io', { status: 200, body: { ms: {} } }); const splitio = SplitFactory({ @@ -571,10 +567,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: { 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 + '/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)); @@ -620,10 +616,10 @@ export default function (fetchMock, assert) { localStorage.clear(); 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)); @@ -684,7 +680,7 @@ export default function (fetchMock, assert) { localStorage.clear(); 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'); @@ -735,14 +731,14 @@ export default function (fetchMock, assert) { localStorage.clear(); t.plan(7); - 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, @@ -769,7 +765,7 @@ 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(); }); }); @@ -800,8 +796,8 @@ export default function (fetchMock, assert) { 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.2&since=-1', { status: 200, body: splitChangesMock1 }); - fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.2&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=100', { status: 200, body: splitChangesMock2 }); fetchMock.get(testUrls.sdk + '/memberships/nicolas%40split.io', { status: 200, body: { ms: {} } }); let splitio = SplitFactory(clearOnInitConfig); @@ -815,7 +811,7 @@ export default function (fetchMock, assert) { }); await client.ready(); - t.equal(manager.names().sort().length, 33, 'active splits should be present for evaluation'); + t.equal(manager.names().sort().length, 36, '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'); @@ -824,7 +820,7 @@ export default function (fetchMock, assert) { // 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.2&since=1457552620999', { status: 200, body: splitChangesMock2 }); + fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.3&since=1457552620999&rbSince=-1', { status: 200, body: splitChangesMock2 }); splitio = SplitFactory(clearOnInitConfig); client = splitio.client(); @@ -832,7 +828,7 @@ export default function (fetchMock, assert) { 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.equal(manager.names().sort().length, 36, '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(); @@ -840,8 +836,8 @@ export default function (fetchMock, assert) { // 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.2&since=-1', { status: 200, body: splitChangesMock1 }); - fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.2&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 }); splitio = SplitFactory(clearOnInitConfig); client = splitio.client(); @@ -853,7 +849,7 @@ export default function (fetchMock, assert) { 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.equal(manager.names().sort().length, 36, '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(); diff --git a/src/__tests__/browserSuites/ready-promise.spec.js b/src/__tests__/browserSuites/ready-promise.spec.js index 0f420d6..9b38460 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=100', { 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 a2d7c16..57ef23c 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 4e91211..74f98d0 100644 --- a/src/__tests__/browserSuites/telemetry.spec.js +++ b/src/__tests__/browserSuites/telemetry.spec.js @@ -21,8 +21,8 @@ const config = { }; export default async function telemetryBrowserSuite(fetchMock, 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' }] } } }); @@ -71,7 +71,7 @@ export default async function telemetryBrowserSuite(fetchMock, assert) { // @TODO check if iDe value is correct assert.deepEqual(data, { - mE: {}, hE: { sp: { 500: 1 }, ms: { 500: 1 } }, tR: 0, aR: 0, iQ: 4, iDe: 1, iDr: 0, spC: 33, seC: 1, skC: 1, eQ: 1, eD: 0, sE: [], t: [], ufs: {} + mE: {}, hE: { sp: { 500: 1 }, ms: { 500: 1 } }, tR: 0, aR: 0, iQ: 4, iDe: 1, iDr: 0, spC: 36, seC: 1, skC: 1, eQ: 1, eD: 0, sE: [], t: [], ufs: {} }, 'metrics/usage JSON payload should be the expected'); finish.next(); @@ -91,7 +91,7 @@ export default async function telemetryBrowserSuite(fetchMock, assert) { // @TODO check if iDe value is correct assert.deepEqual(data, { mL: {}, mE: {}, hE: {}, hL: {}, // errors and latencies were popped - tR: 0, aR: 0, iQ: 4, iDe: 1, iDr: 0, spC: 33, seC: 1, skC: 1, eQ: 1, eD: 0, sE: [], t: [], ufs: {} + tR: 0, aR: 0, iQ: 4, iDe: 1, iDr: 0, spC: 36, seC: 1, skC: 1, eQ: 1, eD: 0, sE: [], t: [], ufs: {} }, '2nd metrics/usage JSON payload should be the expected'); return 200; }); diff --git a/src/__tests__/browserSuites/use-beacon-api.debug.spec.js b/src/__tests__/browserSuites/use-beacon-api.debug.spec.js index 7de576e..bb0d1f4 100644 --- a/src/__tests__/browserSuites/use-beacon-api.debug.spec.js +++ b/src/__tests__/browserSuites/use-beacon-api.debug.spec.js @@ -66,8 +66,7 @@ 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, '/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 bde4260..d8e51d1 100644 --- a/src/__tests__/browserSuites/use-beacon-api.spec.js +++ b/src/__tests__/browserSuites/use-beacon-api.spec.js @@ -78,8 +78,7 @@ 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, '/memberships/facundo%40split.io'), { status: 200, body: membershipsFacundo }); // Init and run Split client diff --git a/src/__tests__/consumer/browser_consumer.spec.js b/src/__tests__/consumer/browser_consumer.spec.js index 1c90f3a..d77be2b 100644 --- a/src/__tests__/consumer/browser_consumer.spec.js +++ b/src/__tests__/consumer/browser_consumer.spec.js @@ -10,7 +10,7 @@ import { version } from '../../../package.json'; import { SplitFactory, PluggableStorage } from '../../'; const expectedSplitName = 'hierarchical_splits_testing_on'; -const expectedSplitView = { name: 'hierarchical_splits_testing_on', trafficType: 'user', killed: false, changeNumber: 1487277320548, treatments: ['on', 'off'], configs: {}, sets: [], defaultTreatment: 'off', impressionsDisabled: false }; +const expectedSplitView = { name: 'hierarchical_splits_testing_on', trafficType: 'user', killed: false, changeNumber: 1487277320548, treatments: ['on', 'off'], configs: {}, sets: [], defaultTreatment: 'off', impressionsDisabled: false, prerequisites: [] }; const wrapperPrefix = 'PLUGGABLE_STORAGE_UT'; const wrapperInstance = inMemoryWrapperFactory(); @@ -118,6 +118,19 @@ tape('Browser Consumer mode with pluggable storage', function (t) { assert.equal(await client.getTreatment('hierarchical_splits_testing_on_negated'), 'off', 'Evaluations using pluggable storage should be correct.'); assert.equal(await client.getTreatment('always-on-impressions-disabled-true'), 'on', 'Evaluations using pluggable storage should be correct.'); + // Evaluations with rule-based segments + const clientInRBS = sdk.client('bilal@split.io'); + await clientInRBS.ready(); + + assert.equal(await otherClient.getTreatment('rbs_test_flag'), 'v2', 'key in excluded segment'); + assert.equal(await clientInRBS.getTreatment('rbs_test_flag'), 'v1', 'key satisfies the rbs condition'); + assert.equal(await client.getTreatment('rbs_test_flag'), 'v2', 'key not in segment'); + + assert.equal(await otherClient.getTreatment('rbs_test_flag_negated'), 'v1', 'key in excluded segment'); + assert.equal(await clientInRBS.getTreatment('rbs_test_flag_negated'), 'v2', 'key satisfies the rbs condition'); + assert.equal(await client.getTreatment('rbs_test_flag_negated'), 'v1', 'key not in segment'); + + // Track calls assert.equal(typeof client.track('user', 'test.event', 18).then, 'function', 'Track calls should always return a promise on Consumer mode.'); assert.equal(typeof client.track().then, 'function', 'Track calls should always return a promise on Consumer mode, even when parameters are incorrect.'); @@ -126,11 +139,11 @@ tape('Browser Consumer mode with pluggable storage', function (t) { // Manager methods const splitNames = await manager.names(); - assert.equal(splitNames.length, 26, 'manager `names` method returns the list of split names asynchronously'); + assert.equal(splitNames.length, 28, 'manager `names` method returns the list of split names asynchronously'); assert.equal(splitNames.indexOf(expectedSplitName) > -1, true, 'list of split names should contain expected splits'); assert.deepEqual(await manager.split(expectedSplitName), expectedSplitView, 'manager `split` method returns the split view of the given split name asynchronously'); const splitViews = await manager.splits(); - assert.equal(splitViews.length, 26, 'manager `splits` method returns the list of split views asynchronously'); + assert.equal(splitViews.length, 28, 'manager `splits` method returns the list of split views asynchronously'); assert.deepEqual(splitViews.find(splitView => splitView.name === expectedSplitName), expectedSplitView, 'manager `split` method returns the split view of the given split name asynchronously'); // New shared client created @@ -143,16 +156,14 @@ tape('Browser Consumer mode with pluggable storage', function (t) { assert.equal((await newClient.getTreatment('UT_IN_SEGMENT')), 'off', '`getTreatment` evaluation using pluggable storage should be correct.'); await client.ready(); // promise already resolved - await newClient.destroy(); - await otherClient.destroy(); - await client.destroy(); + await sdk.destroy(); assert.equal(disconnectSpy.callCount, 1, 'Wrapper disconnect method should be called only once, when the main client is destroyed'); // Validate stored impressions and events const trackedImpressions = await wrapperInstance.popItems('PLUGGABLE_STORAGE_UT.SPLITIO.impressions', await wrapperInstance.getItemsCount('PLUGGABLE_STORAGE_UT.SPLITIO.impressions')); const trackedEvents = await wrapperInstance.popItems('PLUGGABLE_STORAGE_UT.SPLITIO.events', await wrapperInstance.getItemsCount('PLUGGABLE_STORAGE_UT.SPLITIO.events')); - assert.equal(trackedImpressions.length, TOTAL_RAW_IMPRESSIONS, 'Tracked impressions should be present in the external storage'); + assert.equal(trackedImpressions.length, TOTAL_RAW_IMPRESSIONS + 6 /* evaluations with rule-based segments */, 'Tracked impressions should be present in the external storage'); assert.equal(trackedEvents.length, TOTAL_EVENTS, 'Tracked events should be present in the external storage'); // Validate stored telemetry @@ -165,7 +176,7 @@ tape('Browser Consumer mode with pluggable storage', function (t) { // Assert impressionsListener setTimeout(() => { - assert.equal(impressions.length, TOTAL_RAW_IMPRESSIONS + 1 /* One evaluation with impressionsDisabled true */, 'Each evaluation has its corresponding impression'); + assert.equal(impressions.length, TOTAL_RAW_IMPRESSIONS + 6 /* evaluations with rule-based segments */ + 1 /* One evaluation with impressionsDisabled true */, 'Each evaluation has its corresponding impression'); assert.equal(impressions[0].impression.label, SDK_NOT_READY, 'The first impression is control with label "sdk not ready"'); assert.end(); diff --git a/src/__tests__/consumer/browser_consumer_partial.spec.js b/src/__tests__/consumer/browser_consumer_partial.spec.js index cb35cef..25d3411 100644 --- a/src/__tests__/consumer/browser_consumer_partial.spec.js +++ b/src/__tests__/consumer/browser_consumer_partial.spec.js @@ -10,7 +10,7 @@ import { applyOperations } from './wrapper-commands'; import { SplitFactory, PluggableStorage } from '../../'; const expectedSplitName = 'hierarchical_splits_testing_on'; -const expectedSplitView = { name: 'hierarchical_splits_testing_on', trafficType: 'user', killed: false, changeNumber: 1487277320548, treatments: ['on', 'off'], configs: {}, sets: [], defaultTreatment: 'off', impressionsDisabled: false }; +const expectedSplitView = { name: 'hierarchical_splits_testing_on', trafficType: 'user', killed: false, changeNumber: 1487277320548, treatments: ['on', 'off'], configs: {}, sets: [], defaultTreatment: 'off', impressionsDisabled: false, prerequisites: [] }; const wrapperPrefix = 'PLUGGABLE_STORAGE_UT'; const wrapperInstance = inMemoryWrapperFactory(); @@ -161,11 +161,11 @@ tape('Browser Consumer Partial mode with pluggable storage', function (t) { // Manager methods const splitNames = await manager.names(); - assert.equal(splitNames.length, 26, 'manager `names` method returns the list of split names asynchronously'); + assert.equal(splitNames.length, 28, 'manager `names` method returns the list of split names asynchronously'); assert.equal(splitNames.indexOf(expectedSplitName) > -1, true, 'list of split names should contain expected splits'); assert.deepEqual(await manager.split(expectedSplitName), expectedSplitView, 'manager `split` method returns the split view of the given split name asynchronously'); const splitViews = await manager.splits(); - assert.equal(splitViews.length, 26, 'manager `splits` method returns the list of split views asynchronously'); + assert.equal(splitViews.length, 28, 'manager `splits` method returns the list of split views asynchronously'); assert.deepEqual(splitViews.find(splitView => splitView.name === expectedSplitName), expectedSplitView, 'manager `split` method returns the split view of the given split name asynchronously'); // New shared client created diff --git a/src/__tests__/consumer/wrapper-commands.js b/src/__tests__/consumer/wrapper-commands.js index f7095a3..6d3f05d 100644 --- a/src/__tests__/consumer/wrapper-commands.js +++ b/src/__tests__/consumer/wrapper-commands.js @@ -46,7 +46,12 @@ const OPERATIONS = [ ['set', 'PLUGGABLE_STORAGE_UT.SPLITIO.split.testing_traffic_types', '{"changeNumber":1490974465415,"trafficTypeName":"machine","name":"testing_traffic_types","seed":475616886,"status":"ACTIVE","killed":false,"defaultTreatment":"on","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"","attribute":""},"matcherType":"WHITELIST","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["sarasa"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100}],"label":"whitelisted"},{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"","attribute":""},"matcherType":"WHITELIST","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["excluded"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"off","size":100}],"label":"whitelisted"},{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}},{"keySelector":{"trafficType":"machine","attribute":""},"matcherType":"IN_SEGMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":"testing_traffic_type"},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":100}],"label":"in segment all and in segment testing_traffic_type"}]}'], ['set', 'PLUGGABLE_STORAGE_UT.SPLITIO.split.traffic_allocation_testing', '{"changeNumber":1490974123779,"trafficTypeName":"user","name":"traffic_allocation_testing","seed":1716284102,"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"in segment all"}]}'], ['set', 'PLUGGABLE_STORAGE_UT.SPLITIO.splits.till', '1492723024413'], - ['set', 'PLUGGABLE_STORAGE_UT.SPLITIO.split.always-on-impressions-disabled-true', '{"changeNumber":1487277320548,"trafficTypeName":"user","name":"always-on-impressions-disabled-true","impressionsDisabled":true,"seed":1684183541,"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"in segment all"}]}'] + ['set', 'PLUGGABLE_STORAGE_UT.SPLITIO.split.always-on-impressions-disabled-true', '{"changeNumber":1487277320548,"trafficTypeName":"user","name":"always-on-impressions-disabled-true","impressionsDisabled":true,"seed":1684183541,"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"in segment all"}]}'], + ['set', 'PLUGGABLE_STORAGE_UT.SPLITIO.split.rbs_test_flag', '{"changeNumber":10,"trafficTypeName":"user","name":"rbs_test_flag","trafficAllocation":100,"trafficAllocationSeed":1828377380,"seed":-286617921,"status":"ACTIVE","killed":false,"defaultTreatment":"off","algo":2,"conditions":[{"conditionType":"ROLLOUT","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user"},"matcherType":"IN_RULE_BASED_SEGMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":"test_rule_based_segment"}}]},"partitions":[{"treatment":"v1","size":100},{"treatment":"v2","size":0}],"label":"in rule based segment test_rule_based_segment"},{"conditionType":"ROLLOUT","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user"},"matcherType":"ALL_KEYS","negate":false}]},"partitions":[{"treatment":"v1","size":0},{"treatment":"v2","size":100}],"label":"default rule"}],"configurations":{},"sets":[],"impressionsDisabled":false}'], + ['set', 'PLUGGABLE_STORAGE_UT.SPLITIO.split.rbs_test_flag_negated', '{"changeNumber":10,"trafficTypeName":"user","name":"rbs_test_flag_negated","trafficAllocation":100,"trafficAllocationSeed":1828377380,"seed":-286617921,"status":"ACTIVE","killed":false,"defaultTreatment":"off","algo":2,"conditions":[{"conditionType":"ROLLOUT","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user"},"matcherType":"IN_RULE_BASED_SEGMENT","negate":true,"userDefinedSegmentMatcherData":{"segmentName":"test_rule_based_segment"}}]},"partitions":[{"treatment":"v1","size":100},{"treatment":"v2","size":0}],"label":"not in rule based segment test_rule_based_segment"},{"conditionType":"ROLLOUT","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user"},"matcherType":"ALL_KEYS","negate":false}]},"partitions":[{"treatment":"v1","size":0},{"treatment":"v2","size":100}],"label":"default rule"}],"configurations":{},"sets":[],"impressionsDisabled":false}'], + ['set', 'PLUGGABLE_STORAGE_UT.SPLITIO.rbsegment.test_rule_based_segment', '{"changeNumber":5,"name":"test_rule_based_segment","status":"ACTIVE","trafficTypeName":"user","excluded":{"keys":["mauro@split.io","gaston@split.io"],"segments":[{"type":"standard","name":"segment_excluded_by_rbs"}]},"conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user"},"matcherType":"ENDS_WITH","negate":false,"whitelistMatcherData":{"whitelist":["@split.io"]}}]}}]}'], + ['set', 'PLUGGABLE_STORAGE_UT.SPLITIO.rbsegments.till', '1492723024413'], + ['addItems', 'PLUGGABLE_STORAGE_UT.SPLITIO.segment.segment_excluded_by_rbs', ['emi@split.io']], ]; export const OPERATIONS_FLAG_SETS = [ diff --git a/src/__tests__/destroy/browser.spec.js b/src/__tests__/destroy/browser.spec.js index 04f17cb..0b1237d 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__/errorCatching/browser.spec.js b/src/__tests__/errorCatching/browser.spec.js index 768f6c8..38248e3 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'), () => { 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__/mocks/splitChanges.since.-1.till.1500492097547.json b/src/__tests__/mocks/splitChanges.since.-1.till.1500492097547.json index 3e83720..72c0e5c 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 3b4a478..b1a17c8 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 d53768f..5c0ce4c 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 7536fac..0169ce1 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 f29d2cc..5fb9163 100644 --- a/src/__tests__/mocks/splitchanges.real.json +++ b/src/__tests__/mocks/splitchanges.real.json @@ -1,108 +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": {}, - "sets": [] - }, - { - "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\"}}" - }, - "sets": [] - } - ], - "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 7a37877..99e0150 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 523a0b7..865bae2 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 a9d4149..ad766b3 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 c479eae..1cf6278 100644 --- a/src/__tests__/mocks/splitchanges.since.-1.json +++ b/src/__tests__/mocks/splitchanges.since.-1.json @@ -1,1552 +1,1792 @@ { - "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": [ + "rbs": { + "s": -1, + "t": 100, + "d": [ + { + "changeNumber": 5, + "name": "test_rule_based_segment", + "status": "ACTIVE", + "trafficTypeName": "user", + "excluded": { + "keys": [ + "mauro@split.io", + "gaston@split.io" + ], + "segments": [ { - "treatment": "yes", - "size": 100 + "type": "standard", + "name": "segment_excluded_by_rbs" } ] }, - { - "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": [ - { - "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": [ - { - "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 + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user" + }, + "matcherType": "ENDS_WITH", + "negate": false, + "whitelistMatcherData": { + "whitelist": [ + "@split.io" + ] + } + } + ] } - ] - }, - { - "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_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_LARGE_SEGMENT", + "negate": false, + "userDefinedLargeSegmentMatcherData": { + "largeSegmentName": "splitters" + }, + "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 + "treatment": "yes", + "size": 100 } ] - }, - "partitions": [ - { - "treatment": "allowed", - "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": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null + "treatment": "yes", + "size": 100 } ] }, - "partitions": [ - { - "treatment": "allowed", - "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": "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": "yes", + "size": 0 + }, + { + "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": "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 + "treatment": "allowed", + "size": 100 } ] }, - "partitions": [ - { - "treatment": "on", - "size": 100 + { + "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 - } - ] - } - ] - }, - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "developers", - "seed": 1461592538, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "developers" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null + "treatment": "allowed", + "size": 0 + }, + { + "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": [ + } + ] + }, + { + "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": [ { - "keySelector": { - "trafficType": "user", - "attribute": "attr" - }, - "matcherType": "BETWEEN", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": { - "dataType": null, - "start": 10, - "end": 20 + "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 + } } + ] + }, + "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_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": "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_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": "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_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": "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_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": "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_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": "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_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": "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_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": "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_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": "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_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": "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_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": "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": "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_always_on", - "treatments": [ - "on", "partial" - ] - }, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null + "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": [ + { + "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": [ + { + "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": [ + { + "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": [ + { + "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": "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": "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": "IN_SPLIT_TREATMENT", - "negate": false, - "dependencyMatcherData": { - "split": "hierarchical_dep_hierarchical", - "treatments": [ - "on", "partial" - ] - }, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null + "treatment": "off", + "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": [ + } + ] + }, + { + "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": "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": [ + } + ] + }, + { + "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": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null + "treatment": "rollout_treatment", + "size": 100 } ] - }, - "partitions": [ - { - "treatment": "off", - "size": 100 - } - ] - } - ] - }, - { - "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": [ + } + ] + }, + { + "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 + } + ] + }, + "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": "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 + } + ] + }, + "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\"}}" } - ] - }, - { - "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": [ + }, + { + "changeNumber": 10, + "trafficTypeName": "user", + "name": "rbs_test_flag", + "trafficAllocation": 100, + "trafficAllocationSeed": 1828377380, + "seed": -286617921, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "algo": 2, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user" + }, + "matcherType": "IN_RULE_BASED_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "test_rule_based_segment" + } + } + ] + }, + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null + "treatment": "v1", + "size": 100 + }, + { + "treatment": "v2", + "size": 0 } - ] + ], + "label": "in rule based segment test_rule_based_segment" }, - "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": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user" + }, + "matcherType": "ALL_KEYS", + "negate": false + } + ] + }, + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": "group" - }, - "matcherType": "WHITELIST", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist": [ - "value_without_config" - ] - }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null + "treatment": "v1", + "size": 0 + }, + { + "treatment": "v2", + "size": 100 } - ] + ], + "label": "default rule" + } + ], + "configurations": {}, + "sets": [], + "impressionsDisabled": false + }, + { + "changeNumber": 10, + "trafficTypeName": "user", + "name": "rbs_test_flag_negated", + "trafficAllocation": 100, + "trafficAllocationSeed": 1828377380, + "seed": -286617921, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "algo": 2, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user" + }, + "matcherType": "IN_RULE_BASED_SEGMENT", + "negate": true, + "userDefinedSegmentMatcherData": { + "segmentName": "test_rule_based_segment" + } + } + ] + }, + "partitions": [ + { + "treatment": "v1", + "size": 100 + }, + { + "treatment": "v2", + "size": 0 + } + ], + "label": "not in rule based segment test_rule_based_segment" }, - "partitions": [ - { - "treatment": "o.n", - "size": 0 + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user" + }, + "matcherType": "ALL_KEYS", + "negate": false + } + ] }, - { - "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": "v1", + "size": 0 + }, + { + "treatment": "v2", + "size": 100 } + ], + "label": "default rule" + } + ], + "configurations": {}, + "sets": [], + "impressionsDisabled": false + }, + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "always_on_if_prerequisite", + "seed": -790401604, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "prerequisites": [ + { + "n": "rbs_test_flag", + "ts": [ + "v1" ] - }, - "partitions": [ - { - "treatment": "o.n", - "size": 100 + } + ], + "conditions": [ + { + "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" - } - ], - "configurations": { - "o.n": "{\"color\":\"brown\",\"dimensions\":{\"height\":12,\"width\":14},\"text\":{\"inner\":\"click me\"}}" + "partitions": [ + { + "treatment": "on", + "size": 100 + } + ] + } + ] } - } - ], - "since": -1, - "till": 1457552620999 + ], + "s": -1, + "t": 1457552620999 + } } diff --git a/src/__tests__/mocks/splitchanges.since.1457552620999.json b/src/__tests__/mocks/splitchanges.since.1457552620999.json index d50f537..11d2c42 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 eefb6ea..e3e1e05 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 -} \ No newline at end of file + } + ] + } + ], + "s": 1457552620999, + "t": 1457552649999 + } +} 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 098ac51..7bd7ca2 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__/offline/browser.spec.js b/src/__tests__/offline/browser.spec.js index dcca73c..95ed166 100644 --- a/src/__tests__/offline/browser.spec.js +++ b/src/__tests__/offline/browser.spec.js @@ -178,10 +178,10 @@ tape('Browser offline mode', function (assert) { // Manager tests const expectedSplitView1 = { - name: 'testing_split', trafficType: 'localhost', killed: false, changeNumber: 0, treatments: ['on'], configs: {}, sets: [], defaultTreatment: 'control', impressionsDisabled: false + name: 'testing_split', trafficType: 'localhost', killed: false, changeNumber: 0, treatments: ['on'], configs: {}, sets: [], defaultTreatment: 'control', impressionsDisabled: false, prerequisites: [] }; const expectedSplitView2 = { - name: 'testing_split_with_config', trafficType: 'localhost', killed: false, changeNumber: 0, treatments: ['off'], configs: { off: '{ "color": "blue" }' }, sets: [], defaultTreatment: 'control', impressionsDisabled: false + name: 'testing_split_with_config', trafficType: 'localhost', killed: false, changeNumber: 0, treatments: ['off'], configs: { off: '{ "color": "blue" }' }, sets: [], defaultTreatment: 'control', impressionsDisabled: false, prerequisites: [] }; assert.deepEqual(manager.names(), ['testing_split', 'testing_split_with_config']); assert.deepEqual(manager.split('testing_split'), expectedSplitView1); @@ -292,7 +292,7 @@ tape('Browser offline mode', function (assert) { // Manager tests const expectedSplitView3 = { - name: 'testing_split_with_config', trafficType: 'localhost', killed: false, changeNumber: 0, treatments: ['nope'], configs: {}, sets: [], defaultTreatment: 'control', impressionsDisabled: false + name: 'testing_split_with_config', trafficType: 'localhost', killed: false, changeNumber: 0, treatments: ['nope'], configs: {}, sets: [], defaultTreatment: 'control', impressionsDisabled: false, prerequisites: [] }; assert.deepEqual(manager.names(), ['testing_split', 'testing_split_2', 'testing_split_3', 'testing_split_with_config']); assert.deepEqual(manager.split('testing_split'), expectedSplitView1); diff --git a/src/__tests__/online/browser.spec.js b/src/__tests__/online/browser.spec.js index 7974446..ef0841c 100644 --- a/src/__tests__/online/browser.spec.js +++ b/src/__tests__/online/browser.spec.js @@ -84,8 +84,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=100'), { 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/settings/defaults.ts b/src/settings/defaults.ts index 6d9393b..fa47f6c 100644 --- a/src/settings/defaults.ts +++ b/src/settings/defaults.ts @@ -2,7 +2,7 @@ import type SplitIO from '@splitsoftware/splitio-commons/types/splitio'; import { LogLevels, isLogLevelString } from '@splitsoftware/splitio-commons/src/logger/index'; import { CONSENT_GRANTED } from '@splitsoftware/splitio-commons/src/utils/constants'; -const packageVersion = '1.2.0'; +const packageVersion = '1.2.1-rc.0'; /** * In browser, the default debug level, can be set via the `localStorage.splitio_debug` item. diff --git a/ts-tests/index.ts b/ts-tests/index.ts index c66a7b5..7601ec4 100644 --- a/ts-tests/index.ts +++ b/ts-tests/index.ts @@ -153,7 +153,8 @@ splitView = { }, sets: ['set_a', 'set_b'], defaultTreatment: 'off', - impressionsDisabled: false + impressionsDisabled: false, + prerequisites: [{ flagName: 'flag1', treatments: ['on'] }] }; splitViews = [splitView];