diff --git a/CHANGES.txt b/CHANGES.txt index 2545572b8..599c5d527 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,8 @@ +11.4.0 (May 28, 2025) + - Added support for rule-based segments. These segments determine membership at runtime by evaluating their configured rules against the user attributes provided to the SDK. + - Added support for feature flag prerequisites. This allows customers to define dependency conditions between flags, which are evaluated before any allowlists or targeting rules. + - Updated @splitsoftware/splitio-commons package to version 2.4.0. + 11.3.0 (May 16, 2025) - Updated @splitsoftware/splitio-commons package to version 2.3.0, which optimizes the Redis storage to: - Avoid lazy require of the `ioredis` dependency when the SDK is initialized, and diff --git a/karma/config.debug.js b/karma/config.debug.js index 6c461f901..8b3949072 100644 --- a/karma/config.debug.js +++ b/karma/config.debug.js @@ -3,8 +3,14 @@ const merge = require('lodash/merge'); module.exports = merge({}, require('./config'), { + customLaunchers: { + ChromeNoSandbox: { + base: 'Chrome', + flags: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-gpu'] + } + }, browsers: [ - 'Chrome' + 'ChromeNoSandbox' ], webpack: { mode: 'development' diff --git a/package-lock.json b/package-lock.json index 75e1bb986..d299f1608 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,15 @@ { "name": "@splitsoftware/splitio", - "version": "11.3.0", + "version": "11.4.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@splitsoftware/splitio", - "version": "11.3.0", + "version": "11.4.0", "license": "Apache-2.0", "dependencies": { - "@splitsoftware/splitio-commons": "2.3.0", + "@splitsoftware/splitio-commons": "2.4.0", "bloom-filters": "^3.0.4", "ioredis": "^4.28.0", "js-yaml": "^3.13.1", @@ -351,9 +351,9 @@ "dev": true }, "node_modules/@splitsoftware/splitio-commons": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.3.0.tgz", - "integrity": "sha512-TRxutKjGw2JtlYhGYFW5T/2srF76hLDn9pNlwkHx6R0qDK7CqkjhFx/KU6LRqsibnmlh2aMl1I+TxblDncMDyA==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.4.0.tgz", + "integrity": "sha512-VjrzXe7zDM5oi+VWfNNAu1DtcsZl1he8c/MeC4O2SiNRid+Nurzs0ROziHEcBt/4nnCI7vZMNdM4FCcnZHMccA==", "license": "Apache-2.0", "dependencies": { "@types/ioredis": "^4.28.0", @@ -7539,9 +7539,9 @@ "dev": true }, "@splitsoftware/splitio-commons": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.3.0.tgz", - "integrity": "sha512-TRxutKjGw2JtlYhGYFW5T/2srF76hLDn9pNlwkHx6R0qDK7CqkjhFx/KU6LRqsibnmlh2aMl1I+TxblDncMDyA==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.4.0.tgz", + "integrity": "sha512-VjrzXe7zDM5oi+VWfNNAu1DtcsZl1he8c/MeC4O2SiNRid+Nurzs0ROziHEcBt/4nnCI7vZMNdM4FCcnZHMccA==", "requires": { "@types/ioredis": "^4.28.0", "tslib": "^2.3.1" diff --git a/package.json b/package.json index 285f15601..c436bec33 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio", - "version": "11.3.0", + "version": "11.4.0", "description": "Split SDK", "files": [ "README.md", @@ -38,7 +38,7 @@ "node": ">=14.0.0" }, "dependencies": { - "@splitsoftware/splitio-commons": "2.3.0", + "@splitsoftware/splitio-commons": "2.4.0", "bloom-filters": "^3.0.4", "ioredis": "^4.28.0", "js-yaml": "^3.13.1", diff --git a/src/__tests__/browserSuites/evaluations-semver.spec.js b/src/__tests__/browserSuites/evaluations-semver.spec.js index 716c5467d..ce2831409 100644 --- a/src/__tests__/browserSuites/evaluations-semver.spec.js +++ b/src/__tests__/browserSuites/evaluations-semver.spec.js @@ -25,8 +25,7 @@ const config = { export default async function (fetchMock, assert) { - fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.2&since=-1', { status: 200, body: splitChangesMock1 }); - fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.2&since=1675259356568', { status: 200, body: { splits: [], since: 1675259356568, till: 1675259356568 } }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', { status: 200, body: splitChangesMock1 }); fetchMock.getOnce(config.urls.sdk + '/memberships/emi%40split.io', { status: 200, body: { ms: {} } }); fetchMock.getOnce(config.urls.sdk + '/memberships/2nd', { status: 200, body: { ms: {} } }); diff --git a/src/__tests__/browserSuites/evaluations.spec.js b/src/__tests__/browserSuites/evaluations.spec.js index b40b0b1d3..903e7c696 100644 --- a/src/__tests__/browserSuites/evaluations.spec.js +++ b/src/__tests__/browserSuites/evaluations.spec.js @@ -362,6 +362,41 @@ export default function (config, fetchMock, assert) { }; + const evaluationsWithRuleBasedSegmentsAndPrerequisites = async (splitio) => { + fetchMock.getOnce('https://sdk.split.io/api/memberships/emi%40split.io', { status: 200, body: { ms: { k: [{ n: 'segment_excluded_by_rbs' }] } } }); + fetchMock.getOnce('https://sdk.split.io/api/memberships/mauro%40split.io', { status: 200, body: { ms: {} } }); + fetchMock.getOnce('https://sdk.split.io/api/memberships/bilal%40split.io', { status: 200, body: { ms: {} } }); + fetchMock.getOnce('https://sdk.split.io/api/memberships/other_key', { status: 200, body: { ms: {} } }); + + const client1 = splitio.client('emi@split.io'); + await client1.ready(); + assert.equal(client1.getTreatment('rbs_test_flag'), 'v2', 'key in excluded segment'); + assert.equal(client1.getTreatment('rbs_test_flag_negated'), 'v1', 'key in excluded segment'); + assert.equal(client1.getTreatment('always_on_if_prerequisite'), 'off', 'prerequisite not satisfied (key in excluded segment)'); + await client1.destroy(); + + const client2 = splitio.client('mauro@split.io'); + await client2.ready(); + assert.equal(client2.getTreatment('rbs_test_flag'), 'v2', 'excluded key'); + assert.equal(client2.getTreatment('rbs_test_flag_negated'), 'v1', 'excluded key'); + assert.equal(client2.getTreatment('always_on_if_prerequisite'), 'off', 'prerequisite not satisfied (excluded key)'); + await client2.destroy(); + + const client3 = splitio.client('bilal@split.io'); + await client3.ready(); + assert.equal(client3.getTreatment('rbs_test_flag'), 'v1', 'key satisfies the rbs condition'); + assert.equal(client3.getTreatment('rbs_test_flag_negated'), 'v2', 'key satisfies the rbs condition'); + assert.equal(client3.getTreatment('always_on_if_prerequisite'), 'on', 'prerequisite satisfied (key satisfies the rbs condition)'); + await client3.destroy(); + + const client4 = splitio.client('other_key'); + await client4.ready(); + assert.equal(client4.getTreatment('rbs_test_flag'), 'v2', 'key not in segment'); + assert.equal(client4.getTreatment('rbs_test_flag_negated'), 'v1', 'key not in segment'); + assert.equal(client4.getTreatment('always_on_if_prerequisite'), 'off', 'prerequisite not satisfied (key not in segment)'); + await client4.destroy(); + }; + for (i; i < SDK_INSTANCES_TO_TEST; i++) { let splitio = SplitFactory(config); @@ -376,13 +411,16 @@ export default function (config, fetchMock, assert) { getTreatmentsTests(client); getTreatmentsWithConfigTests(client); getTreatmentsWithInMemoryAttributes(client); - clientTABucket1.destroy(); - client.destroy(); - tested++; - if (tested === SDK_INSTANCES_TO_TEST) { - assert.end(); - } + evaluationsWithRuleBasedSegmentsAndPrerequisites(splitio).then(() => { + clientTABucket1.destroy(); + client.destroy(); + tested++; + + if (tested === SDK_INSTANCES_TO_TEST) { + assert.end(); + } + }); }); } } diff --git a/src/__tests__/browserSuites/fetch-specific-splits.spec.js b/src/__tests__/browserSuites/fetch-specific-splits.spec.js index a9f6b383e..45a7af643 100644 --- a/src/__tests__/browserSuites/fetch-specific-splits.spec.js +++ b/src/__tests__/browserSuites/fetch-specific-splits.spec.js @@ -25,13 +25,13 @@ export function fetchSpecificSplits(fetchMock, assert) { const queryString = queryStrings[i] || ''; let factory; - fetchMock.getOnce(urls.sdk + '/splitChanges?s=1.2&since=-1' + queryString, { status: 200, body: { splits: [], since: -1, till: 1457552620999 } }); - fetchMock.getOnce(urls.sdk + '/splitChanges?s=1.2&since=1457552620999' + queryString, { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); - fetchMock.getOnce(urls.sdk + '/splitChanges?s=1.2&since=1457552620999' + queryString, function () { + fetchMock.getOnce(urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1' + queryString, { status: 200, body: { ff: { d: [], s: -1, t: 1457552620999 } } }); + fetchMock.getOnce(urls.sdk + '/splitChanges?s=1.3&since=1457552620999&rbSince=-1' + queryString, { status: 200, body: { ff: { d: [], s: 1457552620999, t: 1457552620999 } } }); + fetchMock.getOnce(urls.sdk + '/splitChanges?s=1.3&since=1457552620999&rbSince=-1' + queryString, function () { factory.client().destroy().then(() => { assert.pass(`splitFilters #${i}`); }); - return { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }; + return { status: 200, body: { ff: { d: [], s: 1457552620999, t: 1457552620999 } } }; }); fetchMock.get(urls.sdk + '/memberships/nicolas%40split.io', { status: 200, body: { 'ms': {} } }); @@ -70,8 +70,8 @@ export function fetchSpecificSplitsForFlagSets(fetchMock, assert) { const queryString = '&sets=4_valid,set_2,set_3,set_ww,set_x'; fetchMock.get(baseUrls.sdk + '/memberships/nicolas%40split.io', { status: 200, body: { 'ms': {} } }); - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.2&since=-1' + queryString, { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 }}); - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.2&since=1457552620999' + queryString, async function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1' + queryString, { status: 200, body: { ff: { d: [], s: 1457552620999, t: 1457552620999 } }}); + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=1457552620999&rbSince=-1' + queryString, async function () { t.pass('flag set query correctly formed'); t.true(logSpy.calledWithExactly('[WARN] splitio => settings: bySet filter value "set_x " has extra whitespace, trimming.')); t.true(logSpy.calledWithExactly('[WARN] splitio => settings: you passed invalid+, flag set must adhere to the regular expressions /^[a-z0-9][_a-z0-9]{0,49}$/. This means a flag set must start with a letter or number, be in lowercase, alphanumeric and have a max length of 50 characters. invalid+ was discarded.')); diff --git a/src/__tests__/browserSuites/flag-sets.spec.js b/src/__tests__/browserSuites/flag-sets.spec.js index 214c1a1fb..11fe92e20 100644 --- a/src/__tests__/browserSuites/flag-sets.spec.js +++ b/src/__tests__/browserSuites/flag-sets.spec.js @@ -24,12 +24,12 @@ export default function flagSets(fetchMock, t) { let manager; // Receive split change with 1 split belonging to set_1 & set_2 and one belonging to set_3 - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.2&since=-1&sets=set_1,set_2', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1&sets=set_1,set_2', function () { return { status: 200, body: splitChange2}; }); // Receive split change with 1 split belonging to set_1 only - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.2&since=1602796638344&sets=set_1,set_2', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=1602796638344&rbSince=-1&sets=set_1,set_2', function () { // stored feature flags before update const storedFlags = manager.splits(); assert.true(storedFlags.length === 1, 'only one feature flag should be added'); @@ -41,7 +41,7 @@ export default function flagSets(fetchMock, t) { }); // Receive split change with 1 split belonging to set_3 only - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.2&since=1602797638344&sets=set_1,set_2', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=1602797638344&rbSince=-1&sets=set_1,set_2', function () { // stored feature flags before update const storedFlags = manager.splits(); assert.true(storedFlags.length === 1); @@ -52,7 +52,7 @@ export default function flagSets(fetchMock, t) { return { status: 200, body: splitChange0}; }); - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.2&since=1602798638344&sets=set_1,set_2', async function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=1602798638344&rbSince=-1&sets=set_1,set_2', async function () { // stored feature flags before update const storedFlags = manager.splits(); assert.true(storedFlags.length === 0, 'the feature flag should be removed'); @@ -75,12 +75,12 @@ export default function flagSets(fetchMock, t) { let manager; // Receive split change with 1 split belonging to set_1 & set_2 and one belonging to set_3 - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.2&since=-1', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', function () { return { status: 200, body: splitChange2}; }); // Receive split change with 1 split belonging to set_1 only - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.2&since=1602796638344', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=1602796638344&rbSince=-1', function () { // stored feature flags before update const storedFlags = manager.splits(); assert.true(storedFlags.length === 2, 'every feature flag should be added'); @@ -94,7 +94,7 @@ export default function flagSets(fetchMock, t) { }); // Receive split change with 1 split belonging to set_3 only - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.2&since=1602797638344', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=1602797638344&rbSince=-1', function () { // stored feature flags before update const storedFlags = manager.splits(); assert.true(storedFlags.length === 2); @@ -107,7 +107,7 @@ export default function flagSets(fetchMock, t) { return { status: 200, body: splitChange0}; }); - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.2&since=1602798638344', async function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=1602798638344&rbSince=-1', async function () { // stored feature flags before update const storedFlags = manager.splits(); assert.true(storedFlags.length === 2); @@ -137,11 +137,11 @@ export default function flagSets(fetchMock, t) { fetchMock.get(baseUrls.sdk + '/memberships/nicolas%40split.io', { status: 200, body: { 'ms': {} } }); // Receive split change with 1 split belonging to set_1 & set_2 and one belonging to set_3 - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.2&since=-1&sets=set_1', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1&sets=set_1', function () { return { status: 200, body: splitChange2}; }); - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.2&since=1602796638344&sets=set_1', async function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=1602796638344&rbSince=-1&sets=set_1', async function () { // stored feature flags before update assert.deepEqual(client.getTreatmentsByFlagSet('set_1'), {workm: 'on'}, 'only the flag in set_1 can be evaluated'); assert.deepEqual(client.getTreatmentsByFlagSet('set_2'), {}, 'only the flag in set_1 can be evaluated'); @@ -174,11 +174,11 @@ export default function flagSets(fetchMock, t) { fetchMock.get(baseUrls.sdk + '/memberships/nicolas%40split.io', { status: 200, body: { 'ms': {} } }); // Receive split change with 1 split belonging to set_1 & set_2 and one belonging to set_3 - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.2&since=-1', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', function () { return { status: 200, body: splitChange2}; }); - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.2&since=1602796638344', async function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=1602796638344&rbSince=-1', async function () { // stored feature flags before update assert.deepEqual(client.getTreatmentsByFlagSet('set_1'), {workm: 'on'}, 'all flags can be evaluated'); assert.deepEqual(client.getTreatmentsByFlagSet('set_2'), {workm: 'on'}, 'all flags can be evaluated'); diff --git a/src/__tests__/browserSuites/ignore-ip-addresses-setting.spec.js b/src/__tests__/browserSuites/ignore-ip-addresses-setting.spec.js index 60108065a..599a95707 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 887d2f4fd..95806a6aa 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.none.spec.js b/src/__tests__/browserSuites/impressions.none.spec.js index e75a9ea87..c41c5c89c 100644 --- a/src/__tests__/browserSuites/impressions.none.spec.js +++ b/src/__tests__/browserSuites/impressions.none.spec.js @@ -1,7 +1,6 @@ import { SplitFactory } from '../..'; import { settingsFactory } from '../../settings/node'; 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 { NONE } from '@splitsoftware/splitio-commons/src/utils/constants'; import { truncateTimeFrame } from '@splitsoftware/splitio-commons/src/utils/time'; @@ -41,8 +40,7 @@ const config = { export default async function (fetchMock, assert) { // Mocking this specific route to make sure we only get the items we want to test from the handlers. - fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=-1'), { status: 200, body: splitChangesMock1 }); - fetchMock.get(url(settings, '/splitChanges?s=1.2&since=1457552620999'), { status: 200, body: splitChangesMock2 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), { status: 200, body: splitChangesMock1 }); fetchMock.get(url(settings, '/memberships/facundo%40split.io'), { status: 200, body: membershipsFacundo }); fetchMock.get(url(settings, '/memberships/emma%40split.io'), { status: 200, body: membershipsFacundo }); diff --git a/src/__tests__/browserSuites/impressions.spec.js b/src/__tests__/browserSuites/impressions.spec.js index f4c800468..cded6291a 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 9c64cc52c..8b76731dd 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 a07d1c162..37edd1587 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'; @@ -71,13 +72,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 dc8df88b9..1066c69e6 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,68 +206,68 @@ export function testFallback(fetchMock, assert) { }); - fetchMock.getOnce(url(settings, `/v2/auth?s=1.2&users=${encodeURIComponent(userKey)}`), function (url, opts) { + fetchMock.getOnce(url(settings, `/v2/auth?s=1.3&users=${encodeURIComponent(userKey)}`), function (url, opts) { if (!opts.headers['Authorization']) assert.fail('`/v2/auth` request must include `Authorization` header'); assert.pass('auth success'); return { status: 200, body: authPushEnabledNicolas }; }); // initial split and memberships sync - fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), { status: 200, body: 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: { ff: { d: [], s: 1457552649999, t: 1457552649999 } } }); fetchMock.getOnce(url(settings, '/memberships/nicolas%40split.io'), { status: 200, body: membershipsNicolasMock1 }); fetchMock.getOnce(url(settings, '/memberships/marcio%40split.io'), { status: 200, body: membershipsMarcio }); // continue fetches due to second fallback to polling - fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=1457552649999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552649999&rbSince=-1'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, MILLIS_STREAMING_PAUSED_CONTROL + settings.scheduler.featuresRefreshRate), 'fetch due to second fallback to polling'); - return { status: 200, body: { splits: [], since: 1457552649999, till: 1457552649999 } }; + return { status: 200, body: { ff: { d: [], s: 1457552649999, t: 1457552649999 } } }; }); fetchMock.getOnce(url(settings, '/memberships/nicolas%40split.io'), { status: 200, body: membershipsNicolasMock1 }); fetchMock.getOnce(url(settings, '/memberships/marcio%40split.io'), { status: 200, body: membershipsMarcio }); // split and segment sync due to streaming up (CONTROL event) - fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=1457552649999'), { status: 200, body: { splits: [], since: 1457552649999, till: 1457552649999 } }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552649999&rbSince=-1'), { status: 200, body: { ff: { d: [], s: 1457552649999, t: 1457552649999 } } }); fetchMock.getOnce(url(settings, '/memberships/nicolas%40split.io'), { status: 200, body: membershipsNicolasMock1 }); fetchMock.getOnce(url(settings, '/memberships/marcio%40split.io'), { status: 200, body: membershipsMarcio }); @@ -280,20 +280,20 @@ export function testFallback(fetchMock, assert) { fetchMock.getOnce(url(settings, '/memberships/marcio%40split.io'), { status: 200, body: membershipsMarcio }); // fetches due to third fallback to polling (STREAMING_PAUSED), two sync all (two STREAMING_RESET events) and fourth fallback (STREAMING_DISABLED) - fetchMock.get({ url: url(settings, '/splitChanges?s=1.2&since=1457552649999'), repeat: 4 }, { status: 200, body: { splits: [], since: 1457552649999, till: 1457552649999 } }); + fetchMock.get({ url: url(settings, '/splitChanges?s=1.3&since=1457552649999&rbSince=-1'), repeat: 4 }, { status: 200, body: { ff: { d: [], s: 1457552649999, t: 1457552649999 } } }); fetchMock.get({ url: url(settings, '/memberships/nicolas%40split.io'), repeat: 4 }, { status: 200, body: membershipsNicolasMock1 }); fetchMock.get({ url: url(settings, '/memberships/marcio%40split.io'), repeat: 4 }, { status: 200, body: membershipsMarcio }); // Periodic fetch due to polling (memberships is not fetched due to smart pausing) - fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=1457552649999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552649999&rbSince=-1'), function () { const lapse = Date.now() - start; - assert.true(nearlyEqual(lapse, MILLIS_STREAMING_DISABLED_CONTROL + settings.scheduler.featuresRefreshRate), 'fetch due to fourth fallback to polling'); - return { status: 200, body: splitChangesMock3 }; + assert.true(nearlyEqual(lapse, MILLIS_STREAMING_DISABLED_CONTROL + settings.scheduler.featuresRefreshRate, 100), 'fetch due to fourth fallback to polling'); + 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 } }; + assert.true(nearlyEqual(lapse, MILLIS_STREAMING_DISABLED_CONTROL + settings.scheduler.featuresRefreshRate * 2, 100), 'fetch due to fourth fallback to polling'); + return { status: 200, body: { ff: { d: [], s: 1457552669999, t: 1457552669999 } } }; }); fetchMock.get(new RegExp('.*'), function (url) { diff --git a/src/__tests__/browserSuites/push-flag-sets.spec.js b/src/__tests__/browserSuites/push-flag-sets.spec.js index df3d502f2..fe829be9b 100644 --- a/src/__tests__/browserSuites/push-flag-sets.spec.js +++ b/src/__tests__/browserSuites/push-flag-sets.spec.js @@ -38,20 +38,20 @@ export function testFlagSets(fetchMock, t) { fetchMock.get(baseUrls.sdk + '/memberships/nicolas%40split.io', { status: 200, body: { 'ms': {} } }); - fetchMock.get(baseUrls.auth + '/v2/auth?s=1.2&users=nicolas%40split.io', function () { + fetchMock.get(baseUrls.auth + '/v2/auth?s=1.3&users=nicolas%40split.io', function () { return { status: 200, body: authPushEnabled }; }); - fetchMock.get(baseUrls.sdk + '/splitChanges?s=1.2&since=-1', function () { - return { status: 200, body: { splits: [], since: -1, till: 0}}; + fetchMock.get(baseUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', function () { + return { status: 200, body: { ff: { d: [], s: -1, t: 0 }}}; }); - fetchMock.get(baseUrls.sdk + '/splitChanges?s=1.2&since=0', function () { - return { status: 200, body: { splits: [], since: 0, till: 1 }}; + fetchMock.get(baseUrls.sdk + '/splitChanges?s=1.3&since=0&rbSince=-1', function () { + return { status: 200, body: { ff: { d: [], s: 0, t: 1 } }}; }); - fetchMock.get(baseUrls.sdk + '/splitChanges?s=1.2&since=-1&sets=set_1,set_2', function () { - return { status: 200, body: { splits: [], since: -1, till: 0 }}; + fetchMock.get(baseUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1&sets=set_1,set_2', function () { + return { status: 200, body: { ff: { d: [], s: -1, t: 0 } }}; }); - fetchMock.get(baseUrls.sdk + '/splitChanges?s=1.2&since=0&sets=set_1,set_2', function () { - return { status: 200, body: { splits: [], since: 0, till: 1 }}; + fetchMock.get(baseUrls.sdk + '/splitChanges?s=1.3&since=0&rbSince=-1&sets=set_1,set_2', function () { + return { status: 200, body: { ff: { d: [], s: 0, t: 1 } }}; }); @@ -189,9 +189,9 @@ export function testFlagSets(fetchMock, t) { t.test(async (assert) => { - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.2&since=2&sets=set_1,set_2', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=2&rbSince=-1&sets=set_1,set_2', function () { assert.pass('4 - A fetch is triggered due to the SPLIT_KILL'); - return { status: 200, body: { splits: [], since: 2, till: 3 }}; + return { status: 200, body: { ff: { d: [], s: 2, t: 3 } }}; }); let splitio, client, manager = []; @@ -229,9 +229,9 @@ export function testFlagSets(fetchMock, t) { t.test(async (assert) => { - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.2&since=1&sets=set_1,set_2', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=1&rbSince=-1&sets=set_1,set_2', function () { assert.pass('5 - A fetch is triggered due to the SPLIT_KILL'); - return { status: 200, body: { splits: [], since: 1, till: 5 }}; + return { status: 200, body: { ff: { d: [], s: 1, t: 5 } }}; }); let splitio, client, manager = []; diff --git a/src/__tests__/browserSuites/push-initialization-nopush.spec.js b/src/__tests__/browserSuites/push-initialization-nopush.spec.js index 1df52e58b..40007b3e0 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 027ceceaa..77e0ad7ac 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 8e22592c9..6acf48d41 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 db0374aa7..c88f65e23 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 4086050fd..d9db7ff79 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: { ff: { d: [], s: 1457552650000, t: 1457552650000 } } }); fetchMock.get({ url: url(settings, '/memberships/key1'), repeat: 6 }, { status: 200, body: { ms: {} } }); fetchMock.get({ url: url(settings, '/memberships/key3'), repeat: 6 }, { status: 200, body: { ms: { k: [{ n: 'splitters' }] } } }); fetchMock.get({ url: url(settings, `/memberships/${bitmapTrueKey}`), repeat: 5 }, { status: 200, body: { ms: { k: [] } } }); diff --git a/src/__tests__/browserSuites/readiness.spec.js b/src/__tests__/browserSuites/readiness.spec.js index f81fafccd..d25c0dae1 100644 --- a/src/__tests__/browserSuites/readiness.spec.js +++ b/src/__tests__/browserSuites/readiness.spec.js @@ -1,7 +1,6 @@ import { SplitFactory } 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; @@ -269,7 +264,7 @@ export default function (fetchMock, assert) { }); setTimeout(() => { - t.equal(getMembershipsHits(), 1 * CLIENTS_COUNT -1, 'memberships should had been hit once per client on the first attempt (excluding client3), but it stopped syncing afterwards.'); + t.equal(getMembershipsHits(), 1 * CLIENTS_COUNT - 1, 'memberships should had been hit once per client on the first attempt (excluding client3), but it stopped syncing afterwards.'); }, 2500); // Now we will wait until it picks up Splits, using the SDK_UPDATE event. Features are refreshed every 3s, but segments every 1s. client.once(client.Event.SDK_UPDATE, () => { @@ -534,8 +529,8 @@ export default function (fetchMock, assert) { const getMembershipsHits = mockForSegmentsPauseTest(testUrls, false); // I'm having the first update of Splits come with segments. In this scenario it'll wait for memberships to download before being ready. - fetchMock.get({ url: testUrls.sdk + '/splitChanges?s=1.2&since=1457552669999', overwriteRoutes: true }, { status: 200, body: { ...splitChangesUpdateWithSegmentsMock, since: 1457552669999, till: 1457552679999 } }); - fetchMock.get(testUrls.sdk + '/splitChanges?s=1.2&since=1457552679999', { status: 200, body: { splits: [], since: 1457552679999, till: 1457552679999 } }); + fetchMock.get({ url: testUrls.sdk + '/splitChanges?s=1.3&since=1457552669999&rbSince=-1', overwriteRoutes: true }, { status: 200, body: { ff: { ...splitChangesUpdateWithSegmentsMock.ff, s: 1457552669999, t: 1457552679999 } } }); + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.3&since=1457552679999&rbSince=-1', { status: 200, body: { ff: { d: [], s: 1457552679999, t: 1457552679999 } } }); const start = Date.now(); const splitio = SplitFactory({ @@ -623,8 +618,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 289b34809..f88fc5163 100644 --- a/src/__tests__/browserSuites/ready-from-cache.spec.js +++ b/src/__tests__/browserSuites/ready-from-cache.spec.js @@ -84,8 +84,8 @@ const baseConfig = { streamingEnabled: false }; -const expectedHashNullFilter = 'db8943b4'; // for SDK key '', filter query null, and flags spec version '1.2' -const expectedHashWithFilter = '7ccd6b31'; // for SDK key '', filter query '&names=p1__split,p2__split', and flags spec version '1.2' +const expectedHashNullFilter = '193e6f3f'; // for SDK key '', filter query null, and flags spec version '1.3' +const expectedHashWithFilter = '2ce5cc38'; // for SDK key '', filter query '&names=p1__split,p2__split', and flags spec version '1.3' export default function (fetchMock, assert) { @@ -97,8 +97,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': {} } }); @@ -147,10 +146,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) }); @@ -254,11 +252,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) }); @@ -372,14 +369,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) }); @@ -486,7 +482,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: { ff: { d: [splitDeclarations.p1__split, splitDeclarations.p2__split], s: -1, t: 1457552620999 } } }, { delay: 10 }); // short delay to let emit SDK_READY_FROM_CACHE fetchMock.getOnce(testUrls.sdk + '/memberships/nicolas%40split.io', { status: 200, body: { ms: {} } }); localStorage.setItem('some_user_item', 'user_item'); @@ -536,7 +532,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: { ff: { d: [splitDeclarations.p1__split, splitDeclarations.p2__split], s: -1, t: 1457552620999 } } }, { delay: 10 }); // short delay to let emit SDK_READY_FROM_CACHE fetchMock.getOnce(testUrls.sdk + '/memberships/nicolas%40split.io', { status: 200, body: { ms: {} } }); const splitio = SplitFactory({ @@ -578,10 +574,10 @@ export default function (fetchMock, assert) { localStorage.clear(); t.plan(7); - fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.2&since=25&names=p2__split&prefixes=p1', { status: 200, body: { splits: [splitDeclarations.p1__split, splitDeclarations.p2__split], since: 25, till: 1457552620999 } }, { delay: 10 }); // short delay to let emit SDK_READY_FROM_CACHE + fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.3&since=25&rbSince=-1&names=p2__split&prefixes=p1', { status: 200, body: { ff: { d: [splitDeclarations.p1__split, splitDeclarations.p2__split], s: 25, t: 1457552620999 } } }, { delay: 10 }); // short delay to let emit SDK_READY_FROM_CACHE fetchMock.getOnce(testUrls.sdk + '/memberships/nicolas%40split.io', { status: 200, body: { ms: {} } }); - const expectedHash = getStorageHash({ ...baseConfig, sync: { __splitFiltersValidation: { queryString: '&names=p2__split&prefixes=p1' }, flagSpecVersion: '1.2' } }); + const expectedHash = getStorageHash({ ...baseConfig, sync: { __splitFiltersValidation: { queryString: '&names=p2__split&prefixes=p1' }, flagSpecVersion: '1.3' } }); localStorage.setItem('some_user_item', 'user_item'); localStorage.setItem('readyFromCache_6.SPLITIO.splits.till', 25); localStorage.setItem('readyFromCache_6.SPLITIO.split.p1__split', JSON.stringify(splitDeclarations.p1__split)); @@ -628,10 +624,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)); @@ -693,7 +689,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'); @@ -745,14 +741,14 @@ export default function (fetchMock, assert) { localStorage.clear(); t.plan(6); - fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.2&since=-1&names=no%20exist%20trim,no_exist,p3__split&prefixes=no%20exist%20trim,p2', { status: 200, body: { splits: [splitDeclarations.p2__split, splitDeclarations.p3__split], since: -1, till: 1457552620999 } }, { delay: 10 }); // short delay to let emit SDK_READY_FROM_CACHE + fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1&names=no%20exist%20trim,no_exist,p3__split&prefixes=no%20exist%20trim,p2', { status: 200, body: { ff: { d: [splitDeclarations.p2__split, splitDeclarations.p3__split], s: -1, t: 1457552620999 } } }, { delay: 10 }); // short delay to let emit SDK_READY_FROM_CACHE fetchMock.getOnce(testUrls.sdk + '/memberships/nicolas%40split.io', { status: 200, body: { ms: {} } }); localStorage.setItem('some_user_item', 'user_item'); localStorage.setItem('readyFromCache_9.SPLITIO.splits.till', 25); localStorage.setItem('readyFromCache_9.SPLITIO.split.p1__split', JSON.stringify(splitDeclarations.p1__split)); localStorage.setItem('readyFromCache_9.SPLITIO.split.p2__split', JSON.stringify(splitDeclarations.p2__split)); - localStorage.setItem('readyFromCache_9.SPLITIO.hash', getStorageHash({ ...baseConfig, sync: { __splitFiltersValidation: { queryString: '&names=p2__split&prefixes=p1' }, flagSpecVersion: '1.2' } })); + localStorage.setItem('readyFromCache_9.SPLITIO.hash', getStorageHash({ ...baseConfig, sync: { __splitFiltersValidation: { queryString: '&names=p2__split&prefixes=p1' }, flagSpecVersion: '1.3' } })); const splitio = SplitFactory({ ...baseConfig, @@ -776,7 +772,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(); }); }); @@ -807,8 +803,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); @@ -822,7 +818,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'); @@ -831,7 +827,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(); @@ -839,7 +835,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(); @@ -847,8 +843,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(); @@ -860,7 +856,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 cb13c8107..3371d1618 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 a2d7c16a9..57ef23cab 100644 --- a/src/__tests__/browserSuites/single-sync.spec.js +++ b/src/__tests__/browserSuites/single-sync.spec.js @@ -36,7 +36,7 @@ const settings = settingsFactory(config); export default function singleSync(fetchMock, assert) { - fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=-1'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), function () { assert.pass('first splitChanges fetch'); return { status: 200, body: splitChangesMock1 }; }); diff --git a/src/__tests__/browserSuites/telemetry.spec.js b/src/__tests__/browserSuites/telemetry.spec.js index 7f6fbc8bc..29facccf4 100644 --- a/src/__tests__/browserSuites/telemetry.spec.js +++ b/src/__tests__/browserSuites/telemetry.spec.js @@ -33,8 +33,8 @@ const SplitFactoryForTest = (config) => { export default async function telemetryBrowserSuite(fetchMock, t) { t.test(async (assert) => { - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.2&since=-1', 500); - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.2&since=-1', { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', 500); + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', { status: 200, body: splitChangesMock1 }); fetchMock.getOnce(baseUrls.sdk + '/memberships/user-key', 500); fetchMock.getOnce(baseUrls.sdk + '/memberships/user-key', { status: 200, body: { 'ms': { k: [{ n: 'one_segment' }] } } }); @@ -76,7 +76,7 @@ export default async function telemetryBrowserSuite(fetchMock, t) { // @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(); @@ -96,7 +96,7 @@ export default async function telemetryBrowserSuite(fetchMock, t) { // @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; }); @@ -188,7 +188,7 @@ export default async function telemetryBrowserSuite(fetchMock, t) { const splitFilters = [{ type: 'bySet', values: ['a', '_b', 'a', 'a', 'c', 'd', '_d'] }]; fetchMock.get(baseUrls.sdk + '/memberships/nicolas%40split.io', { status: 200, body: { 'ms': {} } }); - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.2&since=-1&sets=a,c,d', { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1&sets=a,c,d', { status: 200, body: { ff: { d: [], s: 1457552620999, t: 1457552620999 } } }); fetchMock.postOnce(baseUrls.telemetry + '/v1/metrics/config', (url, opts) => { const data = JSON.parse(opts.body); diff --git a/src/__tests__/browserSuites/use-beacon-api.debug.spec.js b/src/__tests__/browserSuites/use-beacon-api.debug.spec.js index 7de576e6c..bb0d1f4f5 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 bde426077..d8e51d164 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/node_redis.spec.js b/src/__tests__/consumer/node_redis.spec.js index 65b2306ba..00ff3467a 100644 --- a/src/__tests__/consumer/node_redis.spec.js +++ b/src/__tests__/consumer/node_redis.spec.js @@ -61,7 +61,7 @@ const expectedImpressionCount = [ ]; 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 MOCKS = { '': 'redis-commands', @@ -108,9 +108,9 @@ tape('Node.js Redis', function (t) { client.getTreatment('UT_Segment_member', 'UT_IN_SEGMENT').then(result => assert.equal(result, 'on', 'Evaluations using Redis storage should be control until connection is stablished.')); client.getTreatment('other', 'UT_IN_SEGMENT').then(result => assert.equal(result, 'off', 'Evaluations using Redis storage should be control until connection is stablished.')); - manager.names().then((result) => assert.equal(result.length, 26, 'manager `names` method returns an empty list of split names if called before SDK_READY or Redis operation fail')); + manager.names().then((result) => assert.equal(result.length, 28, 'manager `names` method returns an empty list of split names if called before SDK_READY or Redis operation fail')); manager.split(expectedSplitName).then((result) => assert.deepEqual(result, expectedSplitView, 'manager `split` method returns a null split view if called before SDK_READY or Redis operation fail')); - manager.splits().then((result) => assert.equal(result.length, 26, 'manager `splits` method returns an empty list of split views if called before SDK_READY or Redis operation fail')); + manager.splits().then((result) => assert.equal(result.length, 28, 'manager `splits` method returns an empty list of split views if called before SDK_READY or Redis operation fail')); client.track('nicolas@split.io', 'user', 'before.ready', 18).then((result) => assert.true(result, 'Redis adapter queue "rpush" operations until it is ready.')); @@ -158,6 +158,17 @@ tape('Node.js Redis', function (t) { assert.equal(await client.getTreatment('UT_Segment_member', 'hierarchical_splits_testing_on_negated'), 'off', 'Evaluations using Redis storage should be correct.'); assert.equal(await client.getTreatment('other_key', 'always-on-impressions-disabled-true'), 'on', 'Evaluations using Redis storage should be correct.'); + // Evaluations with rule-based segments + assert.equal(await client.getTreatment('emi@split.io', 'rbs_test_flag'), 'v2', 'key in excluded segment'); + assert.equal(await client.getTreatment('mauro@split.io', 'rbs_test_flag'), 'v2', 'excluded key'); + assert.equal(await client.getTreatment('bilal@split.io', 'rbs_test_flag'), 'v1', 'key satisfies the rbs condition'); + assert.equal(await client.getTreatment('other_key', 'rbs_test_flag'), 'v2', 'key not in segment'); + + assert.equal(await client.getTreatment('emi@split.io', 'rbs_test_flag_negated'), 'v1', 'key in excluded segment'); + assert.equal(await client.getTreatment('mauro@split.io', 'rbs_test_flag_negated'), 'v1', 'excluded key'); + assert.equal(await client.getTreatment('bilal@split.io', 'rbs_test_flag_negated'), 'v2', 'key satisfies the rbs condition'); + assert.equal(await client.getTreatment('other_key', 'rbs_test_flag_negated'), 'v1', 'key not in segment'); + assert.equal(typeof client.track().then, 'function', 'Track calls should always return a promise on Redis mode, even when parameters are incorrect.'); assert.true(await client.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was successfully queued the promise will resolve to true'); @@ -165,11 +176,11 @@ tape('Node.js Redis', 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'); await client.ready(); // promise already resolved @@ -189,7 +200,7 @@ tape('Node.js Redis', function (t) { if (error) assert.fail('Redis server should be reachable'); const trackedImpressionsAndEvents = stdout.split('\n').filter(line => line !== '').map(line => parseInt(line)); - assert.deepEqual(trackedImpressionsAndEvents, [TOTAL_RAW_IMPRESSIONS, TOTAL_EVENTS], 'Tracked impressions and events should be stored in Redis'); + assert.deepEqual(trackedImpressionsAndEvents, [TOTAL_RAW_IMPRESSIONS + 8 /* evaluations with rule-based segments */, TOTAL_EVENTS], 'Tracked impressions and events should be stored in Redis'); // Validate stored telemetry exec(`echo "HLEN ${config.storage.prefix}.SPLITIO.telemetry.latencies \n HLEN ${config.storage.prefix}.SPLITIO.telemetry.exceptions \n HGET ${config.storage.prefix}.SPLITIO.telemetry.init 'nodejs-${version}/${HOSTNAME_VALUE}/${IP_VALUE}'" | redis-cli -p ${redisPort}`, (error, stdout) => { diff --git a/src/__tests__/destroy/browser.spec.js b/src/__tests__/destroy/browser.spec.js index 88afc744d..7652d727f 100644 --- a/src/__tests__/destroy/browser.spec.js +++ b/src/__tests__/destroy/browser.spec.js @@ -18,8 +18,8 @@ const settings = settingsFactory({ streamingEnabled: false }); -fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=-1'), { status: 200, body: splitChangesMock1 }); -fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=1500492097547'), { status: 200, body: splitChangesMock2 }); +fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), { status: 200, body: splitChangesMock1 }); +fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1500492097547&rbSince=-1'), { status: 200, body: splitChangesMock2 }); fetchMock.getOnce(url(settings, '/memberships/ut1'), { status: 200, body: membershipsMock }); fetchMock.getOnce(url(settings, '/memberships/ut2'), { status: 200, body: membershipsMock }); fetchMock.getOnce(url(settings, '/memberships/ut3'), { status: 200, body: membershipsMock }); diff --git a/src/__tests__/destroy/node.spec.js b/src/__tests__/destroy/node.spec.js index ada8680d7..ce55ef506 100644 --- a/src/__tests__/destroy/node.spec.js +++ b/src/__tests__/destroy/node.spec.js @@ -17,8 +17,8 @@ import splitChangesMock1 from '../mocks/splitChanges.since.-1.till.1500492097547 import splitChangesMock2 from '../mocks/splitChanges.since.1500492097547.json'; import impressionsMock from '../mocks/impressions.json'; -fetchMock.get(url(settings, '/splitChanges?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); -fetchMock.get(url(settings, '/splitChanges?s=1.1&since=1500492097547'), { status: 200, body: splitChangesMock2 }); +fetchMock.get(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), { status: 200, body: splitChangesMock1 }); +fetchMock.get(url(settings, '/splitChanges?s=1.3&since=1500492097547&rbSince=-1'), { status: 200, body: splitChangesMock2 }); fetchMock.postOnce(url(settings, '/v1/metrics/config'), 200); tape('SDK destroy for Node.js', async function (assert) { diff --git a/src/__tests__/errorCatching/browser.spec.js b/src/__tests__/errorCatching/browser.spec.js index 4ad0c8222..4e422dc10 100644 --- a/src/__tests__/errorCatching/browser.spec.js +++ b/src/__tests__/errorCatching/browser.spec.js @@ -21,13 +21,13 @@ const settings = settingsFactory({ // prepare localstorage to emit SDK_READY_FROM_CACHE localStorage.clear(); localStorage.setItem('SPLITIO.splits.till', 25); -localStorage.setItem('SPLITIO.hash', getStorageHash({ core: { authorizationKey: '' }, sync: { __splitFiltersValidation: { queryString: null }, flagSpecVersion: '1.2' } })); +localStorage.setItem('SPLITIO.hash', getStorageHash({ core: { authorizationKey: '' }, sync: { __splitFiltersValidation: { queryString: null }, flagSpecVersion: '1.3' } })); -fetchMock.get(url(settings, '/splitChanges?s=1.2&since=25'), function () { +fetchMock.get(url(settings, '/splitChanges?s=1.3&since=25&rbSince=-1'), function () { return new Promise((res) => { setTimeout(() => res({ status: 200, body: splitChangesMock1 }), 1000); }); }); -fetchMock.get(url(settings, '/splitChanges?s=1.2&since=1500492097547'), { status: 200, body: splitChangesMock2 }); -fetchMock.get(url(settings, '/splitChanges?s=1.2&since=1500492297547'), { status: 200, body: splitChangesMock3 }); +fetchMock.get(url(settings, '/splitChanges?s=1.3&since=1500492097547&rbSince=-1'), { status: 200, body: splitChangesMock2 }); +fetchMock.get(url(settings, '/splitChanges?s=1.3&since=1500492297547&rbSince=-1'), { status: 200, body: splitChangesMock3 }); fetchMock.get(url(settings, '/memberships/nico%40split.io'), { status: 200, body: membershipsMock }); fetchMock.post('*', 200); diff --git a/src/__tests__/errorCatching/node.spec.js b/src/__tests__/errorCatching/node.spec.js index b7838685a..69ba50150 100644 --- a/src/__tests__/errorCatching/node.spec.js +++ b/src/__tests__/errorCatching/node.spec.js @@ -21,9 +21,9 @@ const settings = settingsFactory({ streamingEnabled: false }); -fetchMock.get(url(settings, '/splitChanges?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }, responseDelay); -fetchMock.get(url(settings, '/splitChanges?s=1.1&since=1500492097547'), { status: 200, body: splitChangesMock2 }, responseDelay); -fetchMock.get(url(settings, '/splitChanges?s=1.1&since=1500492297547'), { status: 200, body: splitChangesMock3 }, responseDelay); +fetchMock.get(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), { status: 200, body: splitChangesMock1 }, responseDelay); +fetchMock.get(url(settings, '/splitChanges?s=1.3&since=1500492097547&rbSince=-1'), { status: 200, body: splitChangesMock2 }, responseDelay); +fetchMock.get(url(settings, '/splitChanges?s=1.3&since=1500492297547&rbSince=-1'), { status: 200, body: splitChangesMock3 }, responseDelay); fetchMock.postOnce(url(settings, '/v1/metrics/config'), 200); // SDK_READY fetchMock.postOnce(url(settings, '/v1/metrics/usage'), 200); // SDK destroyed diff --git a/src/__tests__/mocks/message.RB_SEGMENT_UPDATE.1457552649999.json b/src/__tests__/mocks/message.RB_SEGMENT_UPDATE.1457552649999.json new file mode 100644 index 000000000..acad9194d --- /dev/null +++ b/src/__tests__/mocks/message.RB_SEGMENT_UPDATE.1457552649999.json @@ -0,0 +1,4 @@ +{ + "type": "message", + "data": "{\"id\":\"1111\",\"clientId\":\"pri:ODc1NjQyNzY1\",\"timestamp\":1588254699236,\"encoding\":\"json\",\"channel\":\"xxxx_xxxx_flags\",\"data\": \"{\\\"type\\\":\\\"RB_SEGMENT_UPDATE\\\",\\\"changeNumber\\\":1457552649999}\"}" +} \ No newline at end of file diff --git a/src/__tests__/mocks/message.RB_SEGMENT_UPDATE.C0.json b/src/__tests__/mocks/message.RB_SEGMENT_UPDATE.C0.json new file mode 100644 index 000000000..e1696abd2 --- /dev/null +++ b/src/__tests__/mocks/message.RB_SEGMENT_UPDATE.C0.json @@ -0,0 +1,4 @@ +{ + "type": "message", + "data": "{\"id\":\"1111\",\"clientId\":\"pri:ODc1NjQyNzY1\",\"timestamp\":1588254699236,\"encoding\":\"json\",\"channel\":\"xxxx_xxxx_flags\",\"data\":\"{\\\"type\\\":\\\"RB_SEGMENT_UPDATE\\\",\\\"changeNumber\\\":1684265694505,\\\"pcn\\\":1457552649999,\\\"c\\\":0,\\\"d\\\":\\\"eyJuYW1lIjoicmJzX3Rlc3QiLCJzdGF0dXMiOiJBQ1RJVkUiLCJ0cmFmZmljVHlwZU5hbWUiOiJ1c2VyIiwiZXhjbHVkZWQiOnsia2V5cyI6W10sInNlZ21lbnRzIjpbXX0sImNvbmRpdGlvbnMiOlt7Im1hdGNoZXJHcm91cCI6eyJjb21iaW5lciI6IkFORCIsIm1hdGNoZXJzIjpbeyJrZXlTZWxlY3RvciI6eyJ0cmFmZmljVHlwZSI6InVzZXIifSwibWF0Y2hlclR5cGUiOiJBTExfS0VZUyIsIm5lZ2F0ZSI6ZmFsc2V9XX19XX0=\\\"}\"}" +} diff --git a/src/__tests__/mocks/message.RB_SEGMENT_UPDATE.C1.json b/src/__tests__/mocks/message.RB_SEGMENT_UPDATE.C1.json new file mode 100644 index 000000000..2b09cfaa0 --- /dev/null +++ b/src/__tests__/mocks/message.RB_SEGMENT_UPDATE.C1.json @@ -0,0 +1,4 @@ +{ + "type": "message", + "data": "{\"id\":\"1111\",\"clientId\":\"pri:ODc1NjQyNzY1\",\"timestamp\":1588254699236,\"encoding\":\"json\",\"channel\":\"xxxx_xxxx_flags\",\"data\":\"{\\\"type\\\":\\\"RB_SEGMENT_UPDATE\\\",\\\"changeNumber\\\":1684265694506,\\\"pcn\\\":1684265694505,\\\"c\\\":1,\\\"d\\\":\\\"H4sIAAAAAAAA/0zOwWrDMBAE0H+Zs75At9CGUhpySSiUYoIij1MTSwraFdQY/XtRU5ccd3jDzoLoAmGRz3JSisJA1GkRWGyejq/vWxhodsMw+uN84/7OizDDgN9+Kj172AVXzgL72RkIL4FRf69q4FPsRx1TbMGC4NR/Mb/kVG6t51M4j5G5Pdw/w6zgrq+cD5zoNeWGH5asK+p/4y/d7Hant+3HAQaRF6eEHdwkrF2tXf0JAAD//9JucZnyAAAA\\\"}\"}" +} diff --git a/src/__tests__/mocks/message.RB_SEGMENT_UPDATE.C2.json b/src/__tests__/mocks/message.RB_SEGMENT_UPDATE.C2.json new file mode 100644 index 000000000..0addfbc45 --- /dev/null +++ b/src/__tests__/mocks/message.RB_SEGMENT_UPDATE.C2.json @@ -0,0 +1,4 @@ +{ + "type": "message", + "data": "{\"id\":\"1111\",\"clientId\":\"pri:ODc1NjQyNzY1\",\"timestamp\":1588254699236,\"encoding\":\"json\",\"channel\":\"xxxx_xxxx_flags\",\"data\":\"{\\\"type\\\":\\\"RB_SEGMENT_UPDATE\\\",\\\"changeNumber\\\":1684265694507,\\\"pcn\\\":1684265694506,\\\"c\\\":2,\\\"d\\\":\\\"eJxMzsFqwzAQBNB/mbO+QLfQhlIackkolGKCIo9TE0sK2hXUGP17UVOXHHd4w86C6AJhkc9yUorCQNRpEVhsno6v71sYaHbDMPrjfOP+zosww4Dffio9e9gFV84C+9kZCC+BUX+vauBT7EcdU2zBguDUfzG/5FRuredTOI+RuT3cP8Os4K6vnA+c6DXlhh+WrCvqf+Mv3ex2p7ftxwEGkRenhB3cJKxdrV39CQAA//8FrVMM\\\"}\"}" +} diff --git a/src/__tests__/mocks/redis-commands.txt b/src/__tests__/mocks/redis-commands.txt index e3ad7c35a..bb7628268 100644 --- a/src/__tests__/mocks/redis-commands.txt +++ b/src/__tests__/mocks/redis-commands.txt @@ -40,3 +40,8 @@ SET 'REDIS_NODE_UT.SPLITIO.split.testing_traffic_types' '{"changeNumber":149 SET 'REDIS_NODE_UT.SPLITIO.split.traffic_allocation_testing' '{"changeNumber":1490974123779,"trafficTypeName":"user","name":"traffic_allocation_testing","seed":1716284102,"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"in segment all"}]}' SET 'REDIS_NODE_UT.SPLITIO.splits.till' 1492723024413 SET 'REDIS_NODE_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 'REDIS_NODE_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 'REDIS_NODE_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 'REDIS_NODE_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 'REDIS_NODE_UT.SPLITIO.rbsegments.till' 1492723024413 +SADD 'REDIS_NODE_UT.SPLITIO.segment.segment_excluded_by_rbs' emi@split.io diff --git a/src/__tests__/mocks/splitChanges.since.-1.till.1500492097547.json b/src/__tests__/mocks/splitChanges.since.-1.till.1500492097547.json index 3e83720e6..72c0e5c7c 100644 --- a/src/__tests__/mocks/splitChanges.since.-1.till.1500492097547.json +++ b/src/__tests__/mocks/splitChanges.since.-1.till.1500492097547.json @@ -1,53 +1,55 @@ { - "splits": [ - { - "trafficTypeName": "account", - "name": "Single_Test", - "trafficAllocation": 100, - "trafficAllocationSeed": -1012499566, - "seed": -88385793, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "changeNumber": 1492734008466, - "algo": null, - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "ff": { + "d": [ + { + "trafficTypeName": "account", + "name": "Single_Test", + "trafficAllocation": 100, + "trafficAllocationSeed": -1012499566, + "seed": -88385793, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1492734008466, + "algo": null, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, { - "keySelector": { - "trafficType": "account", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null + "treatment": "off", + "size": 0 } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 - }, - { - "treatment": "off", - "size": 0 - } - ], - "label": "in segment all" - } - ] - } - ], - "since": -1, - "till": 1500492097547 + ], + "label": "in segment all" + } + ] + } + ], + "s": -1, + "t": 1500492097547 + } } diff --git a/src/__tests__/mocks/splitChanges.since.1500492097547.json b/src/__tests__/mocks/splitChanges.since.1500492097547.json index 3b4a4789b..b1a17c824 100644 --- a/src/__tests__/mocks/splitChanges.since.1500492097547.json +++ b/src/__tests__/mocks/splitChanges.since.1500492097547.json @@ -1,5 +1,7 @@ { - "splits": [], - "since": 1500492097547, - "till": 1500492097547 + "ff": { + "d": [], + "s": 1500492097547, + "t": 1500492097547 + } } diff --git a/src/__tests__/mocks/splitChanges.since.1500492097547.till.1500492297547.json b/src/__tests__/mocks/splitChanges.since.1500492097547.till.1500492297547.json index d53768f14..5c0ce4c57 100644 --- a/src/__tests__/mocks/splitChanges.since.1500492097547.till.1500492297547.json +++ b/src/__tests__/mocks/splitChanges.since.1500492097547.till.1500492297547.json @@ -1,53 +1,55 @@ { - "splits": [ - { - "trafficTypeName": "account", - "name": "Single_Test", - "trafficAllocation": 100, - "trafficAllocationSeed": -1012499566, - "seed": -88385793, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "changeNumber": 1492734008466, - "algo": null, - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "ff": { + "d": [ + { + "trafficTypeName": "account", + "name": "Single_Test", + "trafficAllocation": 100, + "trafficAllocationSeed": -1012499566, + "seed": -88385793, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1492734008466, + "algo": null, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, { - "keySelector": { - "trafficType": "account", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null + "treatment": "off", + "size": 100 } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 0 - }, - { - "treatment": "off", - "size": 100 - } - ], - "label": "in segment all" - } - ] - } - ], - "since": 1500492097547, - "till": 1500492297547 + ], + "label": "in segment all" + } + ] + } + ], + "s": 1500492097547, + "t": 1500492297547 + } } diff --git a/src/__tests__/mocks/splitChanges.since.1500492297547.json b/src/__tests__/mocks/splitChanges.since.1500492297547.json index 7536faca2..0169ce1c4 100644 --- a/src/__tests__/mocks/splitChanges.since.1500492297547.json +++ b/src/__tests__/mocks/splitChanges.since.1500492297547.json @@ -1,5 +1,7 @@ { - "splits": [], - "since": 1500492297547, - "till": 1500492297547 + "ff": { + "d": [], + "s": 1500492297547, + "t": 1500492297547 + } } diff --git a/src/__tests__/mocks/splitchanges.real.json b/src/__tests__/mocks/splitchanges.real.json index 8b4516745..5fb9163de 100644 --- a/src/__tests__/mocks/splitchanges.real.json +++ b/src/__tests__/mocks/splitchanges.real.json @@ -1,106 +1,108 @@ { - "splits": [ - { - "trafficTypeName": "user", - "name": "real_split", - "trafficAllocation": 100, - "trafficAllocationSeed": -1757484928, - "seed": 764645059, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "on", - "changeNumber": 1550099287313, - "algo": 2, - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "ff": { + "d": [ + { + "trafficTypeName": "user", + "name": "real_split", + "trafficAllocation": 100, + "trafficAllocationSeed": -1757484928, + "seed": 764645059, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "on", + "changeNumber": 1550099287313, + "algo": 2, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 50 + }, { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "treatment": "off", + "size": 50 } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 50 + ], + "label": "default rule" + } + ], + "configurations": {} + }, + { + "trafficTypeName": "user", + "name": "real_split_2", + "trafficAllocation": 100, + "trafficAllocationSeed": -1427479928, + "seed": 769174959, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "on", + "changeNumber": 1550099287990, + "algo": 2, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment": "off", - "size": 50 - } - ], - "label": "default rule" - } - ], - "configurations": {} - }, - { - "trafficTypeName": "user", - "name": "real_split_2", - "trafficAllocation": 100, - "trafficAllocationSeed": -1427479928, - "seed": 769174959, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "on", - "changeNumber": 1550099287990, - "algo": 2, - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 - }, - { - "treatment": "off", - "size": 0 - } - ], - "label": "default rule" + ], + "label": "default rule" + } + ], + "configurations": { + "on": "{\"color\":\"brown\",\"dimensions\":{\"height\":12,\"width\":14},\"text\":{\"inner\":\"click me\"}}" } - ], - "configurations": { - "on": "{\"color\":\"brown\",\"dimensions\":{\"height\":12,\"width\":14},\"text\":{\"inner\":\"click me\"}}" } - } - ], - "since": -1, - "till": 1457552620999 + ], + "s": -1, + "t": 1457552620999 + } } diff --git a/src/__tests__/mocks/splitchanges.real.updateWithSegments.json b/src/__tests__/mocks/splitchanges.real.updateWithSegments.json index 7a37877cc..99e015051 100644 --- a/src/__tests__/mocks/splitchanges.real.updateWithSegments.json +++ b/src/__tests__/mocks/splitchanges.real.updateWithSegments.json @@ -1,72 +1,74 @@ { - "splits": [ - { - "trafficTypeName": "user", - "name": "real_split", - "trafficAllocation": 100, - "trafficAllocationSeed": -1757484928, - "seed": 764645059, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "on", - "changeNumber": 1550099287313, - "algo": 2, - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "employees" + "ff": { + "d": [ + { + "trafficTypeName": "user", + "name": "real_split", + "trafficAllocation": 100, + "trafficAllocationSeed": -1757484928, + "seed": 764645059, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "on", + "changeNumber": 1550099287313, + "algo": 2, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "employees" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 }, { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "treatment": "off", + "size": 100 } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 0 - }, - { - "treatment": "off", - "size": 100 - } - ], - "label": "default rule" - } - ], - "configurations": {} - } - ], - "since": 1457552620999, - "till": 1457552649999 + ], + "label": "default rule" + } + ], + "configurations": {} + } + ], + "s": 1457552620999, + "t": 1457552649999 + } } diff --git a/src/__tests__/mocks/splitchanges.real.updateWithoutSegments.json b/src/__tests__/mocks/splitchanges.real.updateWithoutSegments.json index 523a0b790..865bae27b 100644 --- a/src/__tests__/mocks/splitchanges.real.updateWithoutSegments.json +++ b/src/__tests__/mocks/splitchanges.real.updateWithoutSegments.json @@ -1,56 +1,57 @@ { - "splits": [ - { - "trafficTypeName": "user", - "name": "real_split", - "trafficAllocation": 100, - "trafficAllocationSeed": -1757484928, - "seed": 764645059, - "status": "ARCHIVED", - "killed": false, - "defaultTreatment": "on", - "changeNumber": 1550099287313, - "algo": 2, - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "ff": { + "d": [ + { + "trafficTypeName": "user", + "name": "real_split", + "trafficAllocation": 100, + "trafficAllocationSeed": -1757484928, + "seed": 764645059, + "status": "ARCHIVED", + "killed": false, + "defaultTreatment": "on", + "changeNumber": 1550099287313, + "algo": 2, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 50 + }, { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "treatment": "off", + "size": 50 } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 50 - }, - { - "treatment": "off", - "size": 50 - } - ], - "label": "default rule" - } - ], - "configurations": {} - } - ], - "since": 1457552649999, - "till": 1457552669999 + ], + "label": "default rule" + } + ], + "configurations": {} + } + ], + "s": 1457552649999, + "t": 1457552669999 + } } - diff --git a/src/__tests__/mocks/splitchanges.real.withSegments.json b/src/__tests__/mocks/splitchanges.real.withSegments.json index a9d4149f4..ad766b311 100644 --- a/src/__tests__/mocks/splitchanges.real.withSegments.json +++ b/src/__tests__/mocks/splitchanges.real.withSegments.json @@ -1,123 +1,125 @@ { - "splits": [ - { - "trafficTypeName": "user", - "name": "real_split", - "trafficAllocation": 100, - "trafficAllocationSeed": -1757484928, - "seed": 764645059, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "on", - "changeNumber": 1550099287313, - "algo": 2, - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "employees" + "ff": { + "d": [ + { + "trafficTypeName": "user", + "name": "real_split", + "trafficAllocation": 100, + "trafficAllocationSeed": -1757484928, + "seed": 764645059, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "on", + "changeNumber": 1550099287313, + "algo": 2, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "employees" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 50 }, { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "treatment": "off", + "size": 50 } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 50 + ], + "label": "default rule" + } + ], + "configurations": {} + }, + { + "trafficTypeName": "user", + "name": "real_split_2", + "trafficAllocation": 100, + "trafficAllocationSeed": -1427479928, + "seed": 769174959, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "on", + "changeNumber": 1550099287990, + "algo": 2, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment": "off", - "size": 50 - } - ], - "label": "default rule" - } - ], - "configurations": {} - }, - { - "trafficTypeName": "user", - "name": "real_split_2", - "trafficAllocation": 100, - "trafficAllocationSeed": -1427479928, - "seed": 769174959, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "on", - "changeNumber": 1550099287990, - "algo": 2, - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 - }, - { - "treatment": "off", - "size": 0 - } - ], - "label": "default rule" + ], + "label": "default rule" + } + ], + "configurations": { + "on": "{\"color\":\"brown\",\"dimensions\":{\"height\":12,\"width\":14},\"text\":{\"inner\":\"click me\"}}" } - ], - "configurations": { - "on": "{\"color\":\"brown\",\"dimensions\":{\"height\":12,\"width\":14},\"text\":{\"inner\":\"click me\"}}" } - } - ], - "since": -1, - "till": 1457552620999 + ], + "s": -1, + "t": 1457552620999 + } } diff --git a/src/__tests__/mocks/splitchanges.since.-1.json b/src/__tests__/mocks/splitchanges.since.-1.json index c479eae1d..b08b71a3d 100644 --- a/src/__tests__/mocks/splitchanges.since.-1.json +++ b/src/__tests__/mocks/splitchanges.since.-1.json @@ -1,1552 +1,1794 @@ { - "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", + "trafficAllocation": 100, + "trafficAllocationSeed": 1828377380, + "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.-1.semver.json b/src/__tests__/mocks/splitchanges.since.-1.semver.json index ad3c486c5..876649937 100644 --- a/src/__tests__/mocks/splitchanges.since.-1.semver.json +++ b/src/__tests__/mocks/splitchanges.since.-1.semver.json @@ -1,514 +1,516 @@ { - "splits": [ - { - "trafficTypeName": "user", - "name": "semver_between", - "trafficAllocation": 100, - "trafficAllocationSeed": 1068038034, - "seed": -1053389887, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "changeNumber": 1675259356568, - "algo": 2, - "configurations": null, - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { - "trafficType": "user", - "attribute": "version" - }, - "matcherType": "BETWEEN_SEMVER", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "dependencyMatcherData": null, - "booleanMatcherData": null, - "stringMatcherData": null, - "betweenStringMatcherData": { - "start": "1.22.9", - "end": "2.1.0" + "ff": { + "d": [ + { + "trafficTypeName": "user", + "name": "semver_between", + "trafficAllocation": 100, + "trafficAllocationSeed": 1068038034, + "seed": -1053389887, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1675259356568, + "algo": 2, + "configurations": null, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "version" + }, + "matcherType": "BETWEEN_SEMVER", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "dependencyMatcherData": null, + "booleanMatcherData": null, + "stringMatcherData": null, + "betweenStringMatcherData": { + "start": "1.22.9", + "end": "2.1.0" + } } - } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 + ] }, - { - "treatment": "off", - "size": 0 - } - ], - "label": "between semver" - }, - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 } - ] + ], + "label": "between semver" }, - "partitions": [ - { - "treatment": "on", - "size": 0 + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment": "off", - "size": 100 - } - ], - "label": "default rule" - } - ] - }, - { - "trafficTypeName": "user", - "name": "semver_equalto", - "trafficAllocation": 100, - "trafficAllocationSeed": 1068038034, - "seed": -1053389887, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "changeNumber": 1675259356568, - "algo": 2, - "configurations": null, - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": "version" - }, - "matcherType": "EQUAL_TO_SEMVER", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "dependencyMatcherData": null, - "booleanMatcherData": null, - "stringMatcherData": "1.22.9" + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 + ], + "label": "default rule" + } + ] + }, + { + "trafficTypeName": "user", + "name": "semver_equalto", + "trafficAllocation": 100, + "trafficAllocationSeed": 1068038034, + "seed": -1053389887, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1675259356568, + "algo": 2, + "configurations": null, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "version" + }, + "matcherType": "EQUAL_TO_SEMVER", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "dependencyMatcherData": null, + "booleanMatcherData": null, + "stringMatcherData": "1.22.9" + } + ] }, - { - "treatment": "off", - "size": 0 - } - ], - "label": "equal to semver" - }, - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "partitions": [ + { + "treatment": "on", + "size": 100 + }, { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "treatment": "off", + "size": 0 } - ] + ], + "label": "equal to semver" }, - "partitions": [ - { - "treatment": "on", - "size": 0 + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment": "off", - "size": 100 - } - ], - "label": "default rule" - } - ] - }, - { - "trafficTypeName": "user", - "name": "semver_greater_or_equalto", - "trafficAllocation": 100, - "trafficAllocationSeed": 1068038034, - "seed": -1053389887, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "changeNumber": 1675259356568, - "algo": 2, - "configurations": null, - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "partitions": [ + { + "treatment": "on", + "size": 0 + }, { - "keySelector": { - "trafficType": "user", - "attribute": "version" - }, - "matcherType": "GREATER_THAN_OR_EQUAL_TO_SEMVER", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "dependencyMatcherData": null, - "booleanMatcherData": null, - "stringMatcherData": "1.22.9" + "treatment": "off", + "size": 100 } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 + ], + "label": "default rule" + } + ] + }, + { + "trafficTypeName": "user", + "name": "semver_greater_or_equalto", + "trafficAllocation": 100, + "trafficAllocationSeed": 1068038034, + "seed": -1053389887, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1675259356568, + "algo": 2, + "configurations": null, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "version" + }, + "matcherType": "GREATER_THAN_OR_EQUAL_TO_SEMVER", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "dependencyMatcherData": null, + "booleanMatcherData": null, + "stringMatcherData": "1.22.9" + } + ] }, - { - "treatment": "off", - "size": 0 - } - ], - "label": "greater than or equal to semver" - }, - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 } - ] + ], + "label": "greater than or equal to semver" }, - "partitions": [ - { - "treatment": "on", - "size": 0 + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment": "off", - "size": 100 - } - ], - "label": "default rule" - } - ] - }, - { - "trafficTypeName": "user", - "name": "semver_inlist", - "trafficAllocation": 100, - "trafficAllocationSeed": 1068038034, - "seed": -1053389887, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "changeNumber": 1675259356568, - "algo": 2, - "configurations": null, - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": "version" - }, - "matcherType": "IN_LIST_SEMVER", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist": [ - "1.22.9", - "2.1.0" - ] - }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "dependencyMatcherData": null, - "booleanMatcherData": null, - "stringMatcherData": null, - "betweenStringMatcherData": null + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 + ], + "label": "default rule" + } + ] + }, + { + "trafficTypeName": "user", + "name": "semver_inlist", + "trafficAllocation": 100, + "trafficAllocationSeed": 1068038034, + "seed": -1053389887, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1675259356568, + "algo": 2, + "configurations": null, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "version" + }, + "matcherType": "IN_LIST_SEMVER", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "1.22.9", + "2.1.0" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "dependencyMatcherData": null, + "booleanMatcherData": null, + "stringMatcherData": null, + "betweenStringMatcherData": null + } + ] }, - { - "treatment": "off", - "size": 0 - } - ], - "label": "in list semver" - }, - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "partitions": [ + { + "treatment": "on", + "size": 100 + }, { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "treatment": "off", + "size": 0 } - ] + ], + "label": "in list semver" }, - "partitions": [ - { - "treatment": "on", - "size": 0 + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment": "off", - "size": 100 - } - ], - "label": "default rule" - } - ] - }, - { - "trafficTypeName": "user", - "name": "semver_less_or_equalto", - "trafficAllocation": 100, - "trafficAllocationSeed": 1068038034, - "seed": -1053389887, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "changeNumber": 1675259356568, - "algo": 2, - "configurations": null, - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "partitions": [ + { + "treatment": "on", + "size": 0 + }, { - "keySelector": { - "trafficType": "user", - "attribute": "version" - }, - "matcherType": "LESS_THAN_OR_EQUAL_TO_SEMVER", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "dependencyMatcherData": null, - "booleanMatcherData": null, - "stringMatcherData": "1.22.9" + "treatment": "off", + "size": 100 } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 + ], + "label": "default rule" + } + ] + }, + { + "trafficTypeName": "user", + "name": "semver_less_or_equalto", + "trafficAllocation": 100, + "trafficAllocationSeed": 1068038034, + "seed": -1053389887, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1675259356568, + "algo": 2, + "configurations": null, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "version" + }, + "matcherType": "LESS_THAN_OR_EQUAL_TO_SEMVER", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "dependencyMatcherData": null, + "booleanMatcherData": null, + "stringMatcherData": "1.22.9" + } + ] }, - { - "treatment": "off", - "size": 0 - } - ], - "label": "less than or equal to semver" - }, - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 } - ] + ], + "label": "less than or equal to semver" }, - "partitions": [ - { - "treatment": "on", - "size": 0 + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment": "off", - "size": 100 - } - ], - "label": "default rule" - } - ] - }, - { - "trafficTypeName": "user", - "name": "flag_with_unsupported_matcher", - "trafficAllocation": 100, - "trafficAllocationSeed": 1068038034, - "seed": -1053389887, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "changeNumber": 1675259356568, - "algo": 2, - "configurations": null, - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "UNSUPPORTED_MATCHER", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "dependencyMatcherData": null, - "booleanMatcherData": null, - "stringMatcherData": "something" + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 0 + ], + "label": "default rule" + } + ] + }, + { + "trafficTypeName": "user", + "name": "flag_with_unsupported_matcher", + "trafficAllocation": 100, + "trafficAllocationSeed": 1068038034, + "seed": -1053389887, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1675259356568, + "algo": 2, + "configurations": null, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "UNSUPPORTED_MATCHER", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "dependencyMatcherData": null, + "booleanMatcherData": null, + "stringMatcherData": "something" + } + ] }, - { - "treatment": "off", - "size": 100 - } - ], - "label": "in segment my_custom_segment" - }, - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "partitions": [ + { + "treatment": "on", + "size": 0 + }, { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "treatment": "off", + "size": 100 } - ] + ], + "label": "in segment my_custom_segment" }, - "partitions": [ - { - "treatment": "on", - "size": 100 + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment": "off", - "size": 0 - } - ], - "label": "default rule" - } - ] - } - ], - "since": -1, - "till": 1675259356568 + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + } + ], + "label": "default rule" + } + ] + } + ], + "s": -1, + "t": 1675259356568 + } } diff --git a/src/__tests__/mocks/splitchanges.since.-1.till.1602796638344.json b/src/__tests__/mocks/splitchanges.since.-1.till.1602796638344.json index 1c69f3b8a..98a00df76 100644 --- a/src/__tests__/mocks/splitchanges.since.-1.till.1602796638344.json +++ b/src/__tests__/mocks/splitchanges.since.-1.till.1602796638344.json @@ -1,150 +1,217 @@ { - "splits": [ - { - "trafficTypeName": "client", - "name": "workm", - "trafficAllocation": 100, - "trafficAllocationSeed": 147392224, - "seed": 524417105, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "on", - "changeNumber": 1602796638344, - "algo": 2, - "configurations": {}, - "sets": ["set_1", "set_2"], - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { "trafficType": "client", "attribute": null }, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "new_segment" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "ff": { + "d": [ + { + "trafficTypeName": "client", + "name": "workm", + "trafficAllocation": 100, + "trafficAllocationSeed": 147392224, + "seed": 524417105, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "on", + "changeNumber": 1602796638344, + "algo": 2, + "configurations": {}, + "sets": [ + "set_1", + "set_2" + ], + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "client", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "new_segment" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 0 + }, + { + "treatment": "free", + "size": 100 + }, + { + "treatment": "conta", + "size": 0 } - ] + ], + "label": "in segment new_segment" }, - "partitions": [ - { "treatment": "on", "size": 0 }, - { "treatment": "off", "size": 0 }, - { "treatment": "free", "size": 100 }, - { "treatment": "conta", "size": 0 } - ], - "label": "in segment new_segment" - }, - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { "trafficType": "client", "attribute": null }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "client", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + }, + { + "treatment": "free", + "size": 0 + }, + { + "treatment": "conta", + "size": 0 } - ] - }, - "partitions": [ - { "treatment": "on", "size": 100 }, - { "treatment": "off", "size": 0 }, - { "treatment": "free", "size": 0 }, - { "treatment": "conta", "size": 0 } - ], - "label": "default rule" - } - ] - }, - { - "trafficTypeName": "client", - "name": "workm_set_3", - "trafficAllocation": 100, - "trafficAllocationSeed": 147392224, - "seed": 524417105, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "on", - "changeNumber": 1602796638344, - "algo": 2, - "configurations": {}, - "sets": ["set_3"], - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { "trafficType": "client", "attribute": null }, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "new_segment" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + ], + "label": "default rule" + } + ] + }, + { + "trafficTypeName": "client", + "name": "workm_set_3", + "trafficAllocation": 100, + "trafficAllocationSeed": 147392224, + "seed": 524417105, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "on", + "changeNumber": 1602796638344, + "algo": 2, + "configurations": {}, + "sets": [ + "set_3" + ], + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "client", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "new_segment" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 0 + }, + { + "treatment": "free", + "size": 100 + }, + { + "treatment": "conta", + "size": 0 } - ] + ], + "label": "in segment new_segment" }, - "partitions": [ - { "treatment": "on", "size": 0 }, - { "treatment": "off", "size": 0 }, - { "treatment": "free", "size": 100 }, - { "treatment": "conta", "size": 0 } - ], - "label": "in segment new_segment" - }, - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { "trafficType": "client", "attribute": null }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "client", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + }, + { + "treatment": "free", + "size": 0 + }, + { + "treatment": "conta", + "size": 0 } - ] - }, - "partitions": [ - { "treatment": "on", "size": 100 }, - { "treatment": "off", "size": 0 }, - { "treatment": "free", "size": 0 }, - { "treatment": "conta", "size": 0 } - ], - "label": "default rule" - } - ] - } - ], - "since": -1, - "till": 1602796638344 + ], + "label": "default rule" + } + ] + } + ], + "s": -1, + "t": 1602796638344 + } } diff --git a/src/__tests__/mocks/splitchanges.since.100.till.1457552649999.RB_SEGMENT_UPDATE.json b/src/__tests__/mocks/splitchanges.since.100.till.1457552649999.RB_SEGMENT_UPDATE.json new file mode 100644 index 000000000..a3fae4c63 --- /dev/null +++ b/src/__tests__/mocks/splitchanges.since.100.till.1457552649999.RB_SEGMENT_UPDATE.json @@ -0,0 +1,46 @@ +{ + "rbs": { + "s": 100, + "t": 1457552649999, + "d": [ + { + "changeNumber": 1457552649999, + "name": "test_rule_based_segment", + "status": "ACTIVE", + "trafficTypeName": "user", + "excluded": { + "keys": [ + "mauro@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" + ] + } + } + ] + } + } + ] + } + ] + } +} diff --git a/src/__tests__/mocks/splitchanges.since.1457552620999.json b/src/__tests__/mocks/splitchanges.since.1457552620999.json index d50f537f3..11d2c42c2 100644 --- a/src/__tests__/mocks/splitchanges.since.1457552620999.json +++ b/src/__tests__/mocks/splitchanges.since.1457552620999.json @@ -1,5 +1,7 @@ { - "splits": [], - "since": 1457552620999, - "till": 1457552620999 + "ff": { + "d": [], + "s": 1457552620999, + "t": 1457552620999 + } } diff --git a/src/__tests__/mocks/splitchanges.since.1457552620999.till.1457552649999.SPLIT_UPDATE.json b/src/__tests__/mocks/splitchanges.since.1457552620999.till.1457552649999.SPLIT_UPDATE.json index eefb6ea10..d1acf0e67 100644 --- a/src/__tests__/mocks/splitchanges.since.1457552620999.till.1457552649999.SPLIT_UPDATE.json +++ b/src/__tests__/mocks/splitchanges.since.1457552620999.till.1457552649999.SPLIT_UPDATE.json @@ -1,147 +1,149 @@ { - "splits": [ - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "qc_team", - "seed": -1984784937, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "no", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "ff": { + "d": [ + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "qc_team", + "seed": -1984784937, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "no", + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "tia@split.io", + "trevor@split.io" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] + }, + "partitions": [ { - "keySelector": null, - "matcherType": "WHITELIST", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist": [ - "tia@split.io", - "trevor@split.io" - ] - }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null + "treatment": "yes", + "size": 100 } ] }, - "partitions": [ - { - "treatment": "yes", - "size": 100 - } - ] - }, - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "employees" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "unaryStringMatcherData": null + } + ] + }, + "partitions": [ { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "employees" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "unaryStringMatcherData": null + "treatment": "yes", + "size": 100 + }, + { + "treatment": "no", + "size": 0 } ] - }, - "partitions": [ - { - "treatment": "yes", - "size": 100 + } + ], + "configurations": {} + }, + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "whitelist", + "seed": 104328192, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "not_allowed", + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "facundo@split.io" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] }, - { - "treatment": "no", - "size": 0 - } - ] - } - ], - "configurations": {} - }, - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "whitelist", - "seed": 104328192, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "not_allowed", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "partitions": [ { - "keySelector": null, - "matcherType": "WHITELIST", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist": [ - "facundo@split.io" - ] - }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null + "treatment": "allowed", + "size": 100 } ] }, - "partitions": [ - { - "treatment": "allowed", - "size": 100 - } - ] - }, - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "allowed", + "size": 100 + }, { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null + "treatment": "not_allowed", + "size": 0 } ] - }, - "partitions": [ - { - "treatment": "allowed", - "size": 100 - }, - { - "treatment": "not_allowed", - "size": 0 - } - ] - } - ] - } - ], - "since": 1457552620999, - "till": 1457552649999 + } + ] + } + ], + "s": 1457552620999, + "t": 1457552649999 + } } \ No newline at end of file diff --git a/src/__tests__/mocks/splitchanges.since.1457552649999.till.1457552650000.SPLIT_KILL.json b/src/__tests__/mocks/splitchanges.since.1457552649999.till.1457552650000.SPLIT_KILL.json index 098ac513d..7bd7ca290 100644 --- a/src/__tests__/mocks/splitchanges.since.1457552649999.till.1457552650000.SPLIT_KILL.json +++ b/src/__tests__/mocks/splitchanges.since.1457552649999.till.1457552650000.SPLIT_KILL.json @@ -1,75 +1,77 @@ { - "splits": [ - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "whitelist", - "seed": 104328192, - "status": "ACTIVE", - "killed": true, - "defaultTreatment": "not_allowed", - "changeNumber": 1457552650000, - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "ff": { + "d": [ + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "whitelist", + "seed": 104328192, + "status": "ACTIVE", + "killed": true, + "defaultTreatment": "not_allowed", + "changeNumber": 1457552650000, + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "facundo@split.io" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] + }, + "partitions": [ { - "keySelector": null, - "matcherType": "WHITELIST", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist": [ - "facundo@split.io" - ] - }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null + "treatment": "allowed", + "size": 100 } ] }, - "partitions": [ - { - "treatment": "allowed", - "size": 100 - } - ] - }, - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "allowed", + "size": 100 + }, { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null + "treatment": "not_allowed", + "size": 0 } ] - }, - "partitions": [ - { - "treatment": "allowed", - "size": 100 - }, - { - "treatment": "not_allowed", - "size": 0 - } - ] - } - ] - } - ], - "since": 1457552649999, - "till": 1457552650000 -} \ No newline at end of file + } + ] + } + ], + "s": 1457552649999, + "t": 1457552650000 + } +} diff --git a/src/__tests__/mocks/splitchanges.since.1457552650000.till.1457552650001.SPLIT_UPDATE.json b/src/__tests__/mocks/splitchanges.since.1457552650000.till.1457552650001.SPLIT_UPDATE.json index a151cfe5e..5cb0a1d0a 100644 --- a/src/__tests__/mocks/splitchanges.since.1457552650000.till.1457552650001.SPLIT_UPDATE.json +++ b/src/__tests__/mocks/splitchanges.since.1457552650000.till.1457552650001.SPLIT_UPDATE.json @@ -1,79 +1,81 @@ { - "splits": [ - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "qc_team", - "seed": -1984784937, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "no", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "ff": { + "d": [ + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "qc_team", + "seed": -1984784937, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "no", + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "tia@split.io", + "trevor@split.io" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] + }, + "partitions": [ { - "keySelector": null, - "matcherType": "WHITELIST", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist": [ - "tia@split.io", - "trevor@split.io" - ] - }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null + "treatment": "yes", + "size": 100 } ] }, - "partitions": [ - { - "treatment": "yes", - "size": 100 - } - ] - }, - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "new_segment" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "unaryStringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "yes", + "size": 100 + }, { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "new_segment" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "unaryStringMatcherData": null + "treatment": "no", + "size": 0 } ] - }, - "partitions": [ - { - "treatment": "yes", - "size": 100 - }, - { - "treatment": "no", - "size": 0 - } - ] - } - ], - "configurations": {} - } - ], - "since": 1457552650000, - "till": 1457552650001 -} \ No newline at end of file + } + ], + "configurations": {} + } + ], + "s": 1457552650000, + "t": 1457552650001 + } +} diff --git a/src/__tests__/mocks/splitchanges.since.1602796638344.till.1602797638344.json b/src/__tests__/mocks/splitchanges.since.1602796638344.till.1602797638344.json index 5ec998208..55d86bc9e 100644 --- a/src/__tests__/mocks/splitchanges.since.1602796638344.till.1602797638344.json +++ b/src/__tests__/mocks/splitchanges.since.1602796638344.till.1602797638344.json @@ -1,78 +1,112 @@ { - "splits": [ - { - "trafficTypeName": "client", - "name": "workm", - "trafficAllocation": 100, - "trafficAllocationSeed": 147392224, - "seed": 524417105, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "on", - "changeNumber": 1602797638344, - "algo": 2, - "configurations": {}, - "sets": ["set_1"], - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "ff": { + "d": [ + { + "trafficTypeName": "client", + "name": "workm", + "trafficAllocation": 100, + "trafficAllocationSeed": 147392224, + "seed": 524417105, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "on", + "changeNumber": 1602797638344, + "algo": 2, + "configurations": {}, + "sets": [ + "set_1" + ], + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "client", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "new_segment" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ { - "keySelector": { "trafficType": "client", "attribute": null }, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "new_segment" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 0 + }, + { + "treatment": "free", + "size": 100 + }, + { + "treatment": "conta", + "size": 0 } - ] + ], + "label": "in segment new_segment" }, - "partitions": [ - { "treatment": "on", "size": 0 }, - { "treatment": "off", "size": 0 }, - { "treatment": "free", "size": 100 }, - { "treatment": "conta", "size": 0 } - ], - "label": "in segment new_segment" - }, - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "client", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, { - "keySelector": { "trafficType": "client", "attribute": null }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "treatment": "off", + "size": 0 + }, + { + "treatment": "free", + "size": 0 + }, + { + "treatment": "conta", + "size": 0 } - ] - }, - "partitions": [ - { "treatment": "on", "size": 100 }, - { "treatment": "off", "size": 0 }, - { "treatment": "free", "size": 0 }, - { "treatment": "conta", "size": 0 } - ], - "label": "default rule" - } - ] - } - ], - "since": 1602796638344, - "till": 1602797638344 + ], + "label": "default rule" + } + ] + } + ], + "s": 1602796638344, + "t": 1602797638344 + } } diff --git a/src/__tests__/mocks/splitchanges.since.1602797638344.till.1602798638344.json b/src/__tests__/mocks/splitchanges.since.1602797638344.till.1602798638344.json index 9384d1c17..96d4580cb 100644 --- a/src/__tests__/mocks/splitchanges.since.1602797638344.till.1602798638344.json +++ b/src/__tests__/mocks/splitchanges.since.1602797638344.till.1602798638344.json @@ -1,78 +1,112 @@ { - "splits": [ - { - "trafficTypeName": "client", - "name": "workm", - "trafficAllocation": 100, - "trafficAllocationSeed": 147392224, - "seed": 524417105, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "on", - "changeNumber": 1602798638344, - "algo": 2, - "configurations": {}, - "sets": ["set_3"], - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "ff": { + "d": [ + { + "trafficTypeName": "client", + "name": "workm", + "trafficAllocation": 100, + "trafficAllocationSeed": 147392224, + "seed": 524417105, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "on", + "changeNumber": 1602798638344, + "algo": 2, + "configurations": {}, + "sets": [ + "set_3" + ], + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "client", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "new_segment" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ { - "keySelector": { "trafficType": "client", "attribute": null }, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "new_segment" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 0 + }, + { + "treatment": "free", + "size": 100 + }, + { + "treatment": "conta", + "size": 0 } - ] + ], + "label": "in segment new_segment" }, - "partitions": [ - { "treatment": "on", "size": 0 }, - { "treatment": "off", "size": 0 }, - { "treatment": "free", "size": 100 }, - { "treatment": "conta", "size": 0 } - ], - "label": "in segment new_segment" - }, - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "client", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, { - "keySelector": { "trafficType": "client", "attribute": null }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "treatment": "off", + "size": 0 + }, + { + "treatment": "free", + "size": 0 + }, + { + "treatment": "conta", + "size": 0 } - ] - }, - "partitions": [ - { "treatment": "on", "size": 100 }, - { "treatment": "off", "size": 0 }, - { "treatment": "free", "size": 0 }, - { "treatment": "conta", "size": 0 } - ], - "label": "default rule" - } - ] - } - ], - "since": 1602797638344, - "till": 1602798638344 + ], + "label": "default rule" + } + ] + } + ], + "s": 1602797638344, + "t": 1602798638344 + } } diff --git a/src/__tests__/mocks/splitchanges.since.1684265694505.till.1684265694506.SPLIT_UPDATE.json b/src/__tests__/mocks/splitchanges.since.1684265694505.till.1684265694506.SPLIT_UPDATE.json index 62c8361be..6e35e11b2 100644 --- a/src/__tests__/mocks/splitchanges.since.1684265694505.till.1684265694506.SPLIT_UPDATE.json +++ b/src/__tests__/mocks/splitchanges.since.1684265694505.till.1684265694506.SPLIT_UPDATE.json @@ -1,193 +1,195 @@ { - "splits": [ - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "qc_team", - "seed": -1984784937, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "no", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": null, - "matcherType": "WHITELIST", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist": [ - "tia@split.io", - "trevor@split.io" - ] - }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null - } - ] - }, - "partitions": [ - { - "treatment": "yes", - "size": 100 - } - ] - }, - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "new_segment" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "unaryStringMatcherData": null - } - ] - }, - "partitions": [ - { - "treatment": "yes", - "size": 100 - }, - { - "treatment": "no", - "size": 0 - } - ] - } - ], - "configurations": {} - }, - { - "trafficTypeName": null, - "name":"mauro_java", - "orgId": null, - "environment": null, - "trafficTypeId": null, - "seed":-1769377604, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "conditions": [ + "ff": { + "d": [ { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "qc_team", + "seed": -1984784937, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "no", + "conditions": [ { - "keySelector": null, - "matcherType": "WHITELIST", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist":[ - "admin", - "mauro", - "nico" + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "tia@split.io", + "trevor@split.io" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } ] }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null - } - ] - }, - "partitions": [ - { - "treatment": "v5", - "size": 100 - } - ] - }, - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "maur-2" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "unaryStringMatcherData": null - } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 0 - }, - { - "treatment": "off", - "size": 0 - }, - { - "treatment": "V4", - "size": 0 + "partitions": [ + { + "treatment": "yes", + "size": 100 + } + ] }, { - "treatment": "v5", - "size": 100 + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "new_segment" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "unaryStringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "yes", + "size": 100 + }, + { + "treatment": "no", + "size": 0 + } + ] } - ] + ], + "configurations": {} }, { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "trafficTypeName": null, + "name": "mauro_java", + "orgId": null, + "environment": null, + "trafficTypeId": null, + "seed": -1769377604, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "conditions": [ { - "keySelector": { - "trafficType":"user", - "attribute": null + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "admin", + "mauro", + "nico" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] }, - "matcherType":"ALL_KEYS", - "negate":false - } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 - }, - { - "treatment": "off", - "size": 0 + "partitions": [ + { + "treatment": "v5", + "size": 100 + } + ] }, { - "treatment": "V4", - "size": 0 + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "maur-2" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "unaryStringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 0 + }, + { + "treatment": "V4", + "size": 0 + }, + { + "treatment": "v5", + "size": 100 + } + ] }, { - "treatment": "v5", - "size": 0 + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + }, + { + "treatment": "V4", + "size": 0 + }, + { + "treatment": "v5", + "size": 0 + } + ] } ] } - ] + ], + "s": 1684265694505, + "t": 1684265694506 } - ], - "since": 1684265694505, - "till": 1684265694506 } diff --git a/src/__tests__/mocks/splitchanges.since.1684265694506.till.1684265694526.SPLIT_UPDATE.json b/src/__tests__/mocks/splitchanges.since.1684265694506.till.1684265694526.SPLIT_UPDATE.json index 74f142fb7..cf9cb3fd6 100644 --- a/src/__tests__/mocks/splitchanges.since.1684265694506.till.1684265694526.SPLIT_UPDATE.json +++ b/src/__tests__/mocks/splitchanges.since.1684265694506.till.1684265694526.SPLIT_UPDATE.json @@ -1,193 +1,195 @@ { - "splits": [ - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "qc_team", - "seed": -1984784937, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "no", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": null, - "matcherType": "WHITELIST", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist": [ - "tia@split.io", - "trevor@split.io" - ] - }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null - } - ] - }, - "partitions": [ - { - "treatment": "yes", - "size": 100 - } - ] - }, - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "new_segment" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "unaryStringMatcherData": null - } - ] - }, - "partitions": [ - { - "treatment": "yes", - "size": 100 - }, - { - "treatment": "no", - "size": 0 - } - ] - } - ], - "configurations": {} - }, - { - "trafficTypeName": null, - "name":"mauro_java", - "orgId": null, - "environment": null, - "trafficTypeId": null, - "seed":-1769377604, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "conditions": [ + "ff": { + "d": [ { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "qc_team", + "seed": -1984784937, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "no", + "conditions": [ { - "keySelector": null, - "matcherType": "WHITELIST", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist":[ - "admin", - "mauro", - "nico" + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "tia@split.io", + "trevor@split.io" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } ] }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null - } - ] - }, - "partitions": [ - { - "treatment": "v4", - "size": 100 - } - ] - }, - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "maur-2" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "unaryStringMatcherData": null - } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 0 - }, - { - "treatment": "off", - "size": 0 - }, - { - "treatment": "V4", - "size": 100 + "partitions": [ + { + "treatment": "yes", + "size": 100 + } + ] }, { - "treatment": "v5", - "size": 0 + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "new_segment" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "unaryStringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "yes", + "size": 100 + }, + { + "treatment": "no", + "size": 0 + } + ] } - ] + ], + "configurations": {} }, { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "trafficTypeName": null, + "name": "mauro_java", + "orgId": null, + "environment": null, + "trafficTypeId": null, + "seed": -1769377604, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "conditions": [ { - "keySelector": { - "trafficType":"user", - "attribute": null + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "admin", + "mauro", + "nico" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] }, - "matcherType":"ALL_KEYS", - "negate":false - } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 0 - }, - { - "treatment": "off", - "size": 0 + "partitions": [ + { + "treatment": "v4", + "size": 100 + } + ] }, { - "treatment": "V4", - "size": 0 + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "maur-2" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "unaryStringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 0 + }, + { + "treatment": "V4", + "size": 100 + }, + { + "treatment": "v5", + "size": 0 + } + ] }, { - "treatment": "v5", - "size": 100 + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 0 + }, + { + "treatment": "V4", + "size": 0 + }, + { + "treatment": "v5", + "size": 100 + } + ] } ] } - ] + ], + "s": 1684265694506, + "t": 1684265694526 } - ], - "since": 1684265694506, - "till": 1684265694526 -} +} \ No newline at end of file diff --git a/src/__tests__/mocks/splitchanges.since.1684265694526.till.1684265694546.SPLIT_UPDATE.json b/src/__tests__/mocks/splitchanges.since.1684265694526.till.1684265694546.SPLIT_UPDATE.json index 9e7ed4568..96b85bf17 100644 --- a/src/__tests__/mocks/splitchanges.since.1684265694526.till.1684265694546.SPLIT_UPDATE.json +++ b/src/__tests__/mocks/splitchanges.since.1684265694526.till.1684265694546.SPLIT_UPDATE.json @@ -1,193 +1,195 @@ { - "splits": [ - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "qc_team", - "seed": -1984784937, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "no", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": null, - "matcherType": "WHITELIST", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist": [ - "tia@split.io", - "trevor@split.io" - ] - }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null - } - ] - }, - "partitions": [ - { - "treatment": "yes", - "size": 100 - } - ] - }, - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "new_segment" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "unaryStringMatcherData": null - } - ] - }, - "partitions": [ - { - "treatment": "yes", - "size": 100 - }, - { - "treatment": "no", - "size": 0 - } - ] - } - ], - "configurations": {} - }, - { - "trafficTypeName": null, - "name":"mauro_java", - "orgId": null, - "environment": null, - "trafficTypeId": null, - "seed":-1769377604, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "conditions": [ + "ff": { + "d": [ { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "qc_team", + "seed": -1984784937, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "no", + "conditions": [ { - "keySelector": null, - "matcherType": "WHITELIST", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist":[ - "admin", - "mauro", - "nico" + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "tia@split.io", + "trevor@split.io" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } ] }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null - } - ] - }, - "partitions": [ - { - "treatment": "v4", - "size": 100 - } - ] - }, - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "maur-2" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "unaryStringMatcherData": null - } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 0 - }, - { - "treatment": "off", - "size": 0 - }, - { - "treatment": "V4", - "size": 100 + "partitions": [ + { + "treatment": "yes", + "size": 100 + } + ] }, { - "treatment": "v5", - "size": 0 + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "new_segment" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "unaryStringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "yes", + "size": 100 + }, + { + "treatment": "no", + "size": 0 + } + ] } - ] + ], + "configurations": {} }, { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "trafficTypeName": null, + "name": "mauro_java", + "orgId": null, + "environment": null, + "trafficTypeId": null, + "seed": -1769377604, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "conditions": [ { - "keySelector": { - "trafficType":"user", - "attribute": null + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "admin", + "mauro", + "nico" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] }, - "matcherType":"ALL_KEYS", - "negate":false - } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 0 - }, - { - "treatment": "off", - "size": 100 + "partitions": [ + { + "treatment": "v4", + "size": 100 + } + ] }, { - "treatment": "V4", - "size": 0 + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "maur-2" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "unaryStringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 0 + }, + { + "treatment": "V4", + "size": 100 + }, + { + "treatment": "v5", + "size": 0 + } + ] }, { - "treatment": "v5", - "size": 0 + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 + }, + { + "treatment": "V4", + "size": 0 + }, + { + "treatment": "v5", + "size": 0 + } + ] } ] } - ] + ], + "s": 1684265694526, + "t": 1684265694546 } - ], - "since": 1684265694526, - "till": 1684265694546 } diff --git a/src/__tests__/mocks/splitchanges.since.1684265694546.till.1684265694556.SPLIT_UPDATE.json b/src/__tests__/mocks/splitchanges.since.1684265694546.till.1684265694556.SPLIT_UPDATE.json index b96b031c3..b2a6c7a7f 100644 --- a/src/__tests__/mocks/splitchanges.since.1684265694546.till.1684265694556.SPLIT_UPDATE.json +++ b/src/__tests__/mocks/splitchanges.since.1684265694546.till.1684265694556.SPLIT_UPDATE.json @@ -1,193 +1,195 @@ { - "splits": [ - { - "orgId": null, - "environment": null, - "trafficTypeId": null, - "trafficTypeName": null, - "name": "qc_team", - "seed": -1984784937, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "no", - "conditions": [ - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": null, - "matcherType": "WHITELIST", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist": [ - "tia@split.io", - "trevor@split.io" - ] - }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null - } - ] - }, - "partitions": [ - { - "treatment": "yes", - "size": 100 - } - ] - }, - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "new_segment" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "unaryStringMatcherData": null - } - ] - }, - "partitions": [ - { - "treatment": "yes", - "size": 100 - }, - { - "treatment": "no", - "size": 0 - } - ] - } - ], - "configurations": {} - }, - { - "trafficTypeName": null, - "name":"mauro_java", - "orgId": null, - "environment": null, - "trafficTypeId": null, - "seed":-1769377604, - "status": "ARCHIVED", - "killed": false, - "defaultTreatment": "off", - "conditions": [ + "ff": { + "d": [ { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "qc_team", + "seed": -1984784937, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "no", + "conditions": [ { - "keySelector": null, - "matcherType": "WHITELIST", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": { - "whitelist":[ - "admin", - "mauro", - "nico" + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "tia@split.io", + "trevor@split.io" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } ] }, - "unaryNumericMatcherData": null, - "betweenMatcherData": null - } - ] - }, - "partitions": [ - { - "treatment": "v4", - "size": 100 - } - ] - }, - { - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "maur-2" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "unaryStringMatcherData": null - } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 0 - }, - { - "treatment": "off", - "size": 0 - }, - { - "treatment": "V4", - "size": 100 + "partitions": [ + { + "treatment": "yes", + "size": 100 + } + ] }, { - "treatment": "v5", - "size": 0 + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "new_segment" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "unaryStringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "yes", + "size": 100 + }, + { + "treatment": "no", + "size": 0 + } + ] } - ] + ], + "configurations": {} }, { - "matcherGroup": { - "combiner": "AND", - "matchers": [ + "trafficTypeName": null, + "name": "mauro_java", + "orgId": null, + "environment": null, + "trafficTypeId": null, + "seed": -1769377604, + "status": "ARCHIVED", + "killed": false, + "defaultTreatment": "off", + "conditions": [ { - "keySelector": { - "trafficType":"user", - "attribute": null + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "admin", + "mauro", + "nico" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null + } + ] }, - "matcherType":"ALL_KEYS", - "negate":false - } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 0 - }, - { - "treatment": "off", - "size": 100 + "partitions": [ + { + "treatment": "v4", + "size": 100 + } + ] }, { - "treatment": "V4", - "size": 0 + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "maur-2" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "unaryStringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 0 + }, + { + "treatment": "V4", + "size": 100 + }, + { + "treatment": "v5", + "size": 0 + } + ] }, { - "treatment": "v5", - "size": 0 + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 + }, + { + "treatment": "V4", + "size": 0 + }, + { + "treatment": "v5", + "size": 0 + } + ] } ] } - ] + ], + "s": 1684265694546, + "t": 1684265694556 } - ], - "since": 1684265694546, - "till": 1684265694556 } diff --git a/src/__tests__/nodeSuites/evaluations-semver.spec.js b/src/__tests__/nodeSuites/evaluations-semver.spec.js index 5885a2b16..cc716bb83 100644 --- a/src/__tests__/nodeSuites/evaluations-semver.spec.js +++ b/src/__tests__/nodeSuites/evaluations-semver.spec.js @@ -24,8 +24,8 @@ const config = { export default async function (fetchMock, assert) { - fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=-1', { status: 200, body: splitChangesMock1 }); - fetchMock.get(config.urls.sdk + '/splitChanges?s=1.1&since=1675259356568', { status: 200, body: { splits: [], since: 1675259356568, till: 1675259356568 } }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', { status: 200, body: splitChangesMock1 }); + fetchMock.get(config.urls.sdk + '/splitChanges?s=1.3&since=1675259356568&rbSince=-1', { status: 200, body: { ff: { d: [], s: 1675259356568, t: 1675259356568 } } }); const splitio = SplitFactory(config); const client = splitio.client(); diff --git a/src/__tests__/nodeSuites/evaluations.spec.js b/src/__tests__/nodeSuites/evaluations.spec.js index f5cf6aa53..ab6c20021 100644 --- a/src/__tests__/nodeSuites/evaluations.spec.js +++ b/src/__tests__/nodeSuites/evaluations.spec.js @@ -164,6 +164,21 @@ export default async function (config, key, assert) { assert.equal(client.getTreatment('aaaaaaklmnbv', 'ta_bucket1_test'), 'rollout_treatment'); // With a higher bucket it's ok to get default treatment assert.equal(client.getTreatment('nico_test', 'ta_bucket1_test'), 'default_treatment'); + + // Rule-based segments + assert.equal(client.getTreatment('emi@split.io', 'rbs_test_flag'), 'v2', 'key in excluded segment'); + assert.equal(client.getTreatment('mauro@split.io', 'rbs_test_flag'), 'v2', 'excluded key'); + assert.equal(client.getTreatment('bilal@split.io', 'rbs_test_flag'), 'v1', 'key satisfies the rbs condition'); + assert.equal(client.getTreatment('other_key', 'rbs_test_flag'), 'v2', 'key not in segment'); + + assert.equal(client.getTreatment('emi@split.io', 'rbs_test_flag_negated'), 'v1', 'key in excluded segment'); + assert.equal(client.getTreatment('mauro@split.io', 'rbs_test_flag_negated'), 'v1', 'excluded key'); + assert.equal(client.getTreatment('bilal@split.io', 'rbs_test_flag_negated'), 'v2', 'key satisfies the rbs condition'); + assert.equal(client.getTreatment('other_key', 'rbs_test_flag_negated'), 'v1', 'key not in segment'); + + // Prerequisites + assert.equal(client.getTreatment('bilal@split.io', 'always_on_if_prerequisite'), 'on', 'prerequisite satisfied (key satisfies the rbs condition)'); + assert.equal(client.getTreatment('emi@split.io', 'always_on_if_prerequisite'), 'off', 'prerequisite not satisfied (key in excluded segment)'); }; const getTreatmentsTests = (client, sdkInstance) => { diff --git a/src/__tests__/nodeSuites/expected-treatments.spec.js b/src/__tests__/nodeSuites/expected-treatments.spec.js index 552db56b3..0173777f1 100644 --- a/src/__tests__/nodeSuites/expected-treatments.spec.js +++ b/src/__tests__/nodeSuites/expected-treatments.spec.js @@ -6,7 +6,8 @@ import { url } from '../testUtils'; import splitChangesMockReal from '../mocks/splitchanges.real.json'; export default async function (config, settings, fetchMock, assert) { - fetchMock.get({ url: url(settings, '/splitChanges?s=1.1&since=-1'), overwriteRoutes: true }, { status: 200, body: splitChangesMockReal }); + fetchMock.getOnce({ url: url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), overwriteRoutes: true }, { status: 200, body: splitChangesMockReal }); + fetchMock.get(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), { status: 200, body: { ff: { d: [], s: 1457552620999, t: 1457552620999 } } }); const splitio = SplitFactory({ ...config, diff --git a/src/__tests__/nodeSuites/fetch-specific-splits.spec.js b/src/__tests__/nodeSuites/fetch-specific-splits.spec.js index 35d96d26d..971d4ec81 100644 --- a/src/__tests__/nodeSuites/fetch-specific-splits.spec.js +++ b/src/__tests__/nodeSuites/fetch-specific-splits.spec.js @@ -23,13 +23,13 @@ export function fetchSpecificSplits(fetchMock, assert) { const queryString = queryStrings[i] || ''; let factory; - fetchMock.getOnce(urls.sdk + '/splitChanges?s=1.1&since=-1' + queryString, { status: 200, body: { splits: [], since: -1, till: 1457552620999 } }); - fetchMock.getOnce(urls.sdk + '/splitChanges?s=1.1&since=1457552620999' + queryString, { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); - fetchMock.getOnce(urls.sdk + '/splitChanges?s=1.1&since=1457552620999' + queryString, function () { + fetchMock.getOnce(urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1' + queryString, { status: 200, body: { ff: { d: [], s: -1, t: 1457552620999 } } }); + fetchMock.getOnce(urls.sdk + '/splitChanges?s=1.3&since=1457552620999&rbSince=-1' + queryString, { status: 200, body: { ff: { d: [], s: 1457552620999, t: 1457552620999 } } }); + fetchMock.getOnce(urls.sdk + '/splitChanges?s=1.3&since=1457552620999&rbSince=-1' + queryString, function () { factory.client().destroy().then(() => { assert.pass(`splitFilters #${i}`); }); - return { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }; + return { status: 200, body: { ff: { d: [], s: 1457552620999, t: 1457552620999 } } }; }); factory = SplitFactory(config); @@ -64,8 +64,8 @@ export function fetchSpecificSplitsForFlagSets(fetchMock, assert) { let factory; const queryString = '&sets=4_valid,set_2,set_3,set_ww,set_x'; - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&since=-1' + queryString, { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 }}); - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&since=1457552620999' + queryString, async function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1' + queryString, { status: 200, body: { ff: { d: [], s: 1457552620999, t: 1457552620999 } }}); + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=1457552620999&rbSince=-1' + queryString, async function () { t.pass('flag set query correctly formed'); factory.client().destroy().then(() => { t.end(); diff --git a/src/__tests__/nodeSuites/flag-sets.spec.js b/src/__tests__/nodeSuites/flag-sets.spec.js index e8dd725e0..bacbd507a 100644 --- a/src/__tests__/nodeSuites/flag-sets.spec.js +++ b/src/__tests__/nodeSuites/flag-sets.spec.js @@ -26,12 +26,12 @@ export default function flagSets(fetchMock, t) { let factory, manager, client = []; // Receive split change with 1 split belonging to set_1 & set_2 and one belonging to set_3 - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&since=-1&sets=set_1,set_2', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1&sets=set_1,set_2', function () { return { status: 200, body: splitChange2}; }); // Receive split change with 1 split belonging to set_1 only - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&since=1602796638344&sets=set_1,set_2', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=1602796638344&rbSince=-1&sets=set_1,set_2', function () { // stored feature flags before update const storedFlags = manager.splits(); assert.true(storedFlags.length === 1, 'only one feature flag should be added'); @@ -43,7 +43,7 @@ export default function flagSets(fetchMock, t) { }); // Receive split change with 1 split belonging to set_3 only - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&since=1602797638344&sets=set_1,set_2', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=1602797638344&rbSince=-1&sets=set_1,set_2', function () { // stored feature flags before update const storedFlags = manager.splits(); assert.true(storedFlags.length === 1); @@ -54,7 +54,7 @@ export default function flagSets(fetchMock, t) { return { status: 200, body: splitChange0}; }); - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&since=1602798638344&sets=set_1,set_2', async function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=1602798638344&rbSince=-1&sets=set_1,set_2', async function () { // stored feature flags before update const storedFlags = manager.splits(); assert.true(storedFlags.length === 0, 'the feature flag should be removed'); @@ -79,12 +79,12 @@ export default function flagSets(fetchMock, t) { let factory, manager, client = []; // Receive split change with 1 split belonging to set_1 & set_2 and one belonging to set_3 - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&since=-1', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', function () { return { status: 200, body: splitChange2}; }); // Receive split change with 1 split belonging to set_1 only - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&since=1602796638344', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=1602796638344&rbSince=-1', function () { // stored feature flags before update const storedFlags = manager.splits(); assert.true(storedFlags.length === 2, 'every feature flag should be added'); @@ -98,7 +98,7 @@ export default function flagSets(fetchMock, t) { }); // Receive split change with 1 split belonging to set_3 only - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&since=1602797638344', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=1602797638344&rbSince=-1', function () { // stored feature flags before update const storedFlags = manager.splits(); assert.true(storedFlags.length === 2); @@ -111,7 +111,7 @@ export default function flagSets(fetchMock, t) { return { status: 200, body: splitChange0}; }); - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&since=1602798638344', async function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=1602798638344&rbSince=-1', async function () { // stored feature flags before update const storedFlags = manager.splits(); assert.true(storedFlags.length === 2); @@ -142,11 +142,11 @@ export default function flagSets(fetchMock, t) { mockSegmentChanges(fetchMock, new RegExp(baseUrls.sdk + '/segmentChanges/*'), []); fetchMock.post('*', 200); // Receive split change with 1 split belonging to set_1 & set_2 and one belonging to set_3 - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&since=-1&sets=set_1', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1&sets=set_1', function () { return { status: 200, body: splitChange2}; }); - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&since=1602796638344&sets=set_1', async function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=1602796638344&rbSince=-1&sets=set_1', async function () { // stored feature flags before update assert.deepEqual(client.getTreatmentsByFlagSet(key, 'set_1'), {workm: 'on'}, 'only the flag in set_1 can be evaluated'); assert.deepEqual(client.getTreatmentsByFlagSet(key, 'set_2'), {}, 'only the flag in set_1 can be evaluated'); @@ -180,11 +180,11 @@ export default function flagSets(fetchMock, t) { fetchMock.post('*', 200); // Receive split change with 1 split belonging to set_1 & set_2 and one belonging to set_3 - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&since=-1', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', function () { return { status: 200, body: splitChange2}; }); - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&since=1602796638344', async function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=1602796638344&rbSince=-1', async function () { // stored feature flags before update assert.deepEqual(client.getTreatmentsByFlagSet(key, 'set_1'), {workm: 'on'}, 'all flags can be evaluated'); assert.deepEqual(client.getTreatmentsByFlagSet(key, 'set_2'), {workm: 'on'}, 'all flags can be evaluated'); diff --git a/src/__tests__/nodeSuites/impressions.debug.spec.js b/src/__tests__/nodeSuites/impressions.debug.spec.js index f0f194f9b..8b77e218d 100644 --- a/src/__tests__/nodeSuites/impressions.debug.spec.js +++ b/src/__tests__/nodeSuites/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 { DEBUG } from '@splitsoftware/splitio-commons/src/utils/constants'; import { truncateTimeFrame } from '@splitsoftware/splitio-commons/src/utils/time'; import { url } from '../testUtils'; @@ -43,8 +42,7 @@ let truncatedTimeFrame; export default async function (key, fetchMock, assert) { // Mocking this specific route to make sure we only get the items we want to test from the handlers. - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); - fetchMock.get(url(settings, '/splitChanges?s=1.1&since=1457552620999'), { status: 200, body: splitChangesMock2 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), { status: 200, body: splitChangesMock1 }); fetchMock.get(new RegExp(`${url(settings, '/segmentChanges/')}.*`), { status: 200, body: { since: 10, till: 10, name: 'segmentName', added: [], removed: [] } }); const splitio = SplitFactory(config); diff --git a/src/__tests__/nodeSuites/impressions.none.spec.js b/src/__tests__/nodeSuites/impressions.none.spec.js index 004748e3b..df3256ab8 100644 --- a/src/__tests__/nodeSuites/impressions.none.spec.js +++ b/src/__tests__/nodeSuites/impressions.none.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 { NONE } from '@splitsoftware/splitio-commons/src/utils/constants'; import { truncateTimeFrame } from '@splitsoftware/splitio-commons/src/utils/time'; import { url } from '../testUtils'; @@ -39,8 +38,7 @@ const config = { export default async function (key, fetchMock, assert) { // Mocking this specific route to make sure we only get the items we want to test from the handlers. - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); - fetchMock.get(url(settings, '/splitChanges?s=1.1&since=1457552620999'), { status: 200, body: splitChangesMock2 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), { status: 200, body: splitChangesMock1 }); fetchMock.get(new RegExp(`${url(settings, '/segmentChanges/')}.*`), { status: 200, body: { since: 10, till: 10, name: 'segmentName', added: [], removed: [] } }); const splitio = SplitFactory(config); diff --git a/src/__tests__/nodeSuites/impressions.spec.js b/src/__tests__/nodeSuites/impressions.spec.js index 6489f3c99..3c883d34f 100644 --- a/src/__tests__/nodeSuites/impressions.spec.js +++ b/src/__tests__/nodeSuites/impressions.spec.js @@ -37,8 +37,8 @@ let truncatedTimeFrame; export default async function (key, fetchMock, assert) { // Mocking this specific route to make sure we only get the items we want to test from the handlers. - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); - fetchMock.get(url(settings, '/splitChanges?s=1.1&since=1457552620999'), { status: 200, body: splitChangesMock2 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.get(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=100'), { status: 200, body: splitChangesMock2 }); fetchMock.get(new RegExp(`${url(settings, '/segmentChanges/')}.*`), { status: 200, body: { since: 10, till: 10, name: 'segmentName', added: [], removed: [] } }); const splitio = SplitFactory(config); diff --git a/src/__tests__/nodeSuites/ip-addresses-setting.debug.spec.js b/src/__tests__/nodeSuites/ip-addresses-setting.debug.spec.js index 674873ad4..a93e6865e 100644 --- a/src/__tests__/nodeSuites/ip-addresses-setting.debug.spec.js +++ b/src/__tests__/nodeSuites/ip-addresses-setting.debug.spec.js @@ -77,8 +77,7 @@ export default function ipAddressesSettingAssertions(fetchMock, assert) { })(); // Mock GET endpoints to run client normally - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), { status: 200, body: splitChangesMock1 }); fetchMock.get(new RegExp(`${url(settings, '/segmentChanges/')}.*`), { status: 200, body: { since: 10, till: 10, name: 'segmentName', added: [], removed: [] } }); // Mock and assert POST endpoints diff --git a/src/__tests__/nodeSuites/ip-addresses-setting.spec.js b/src/__tests__/nodeSuites/ip-addresses-setting.spec.js index 2fbf92f12..18038de82 100644 --- a/src/__tests__/nodeSuites/ip-addresses-setting.spec.js +++ b/src/__tests__/nodeSuites/ip-addresses-setting.spec.js @@ -121,8 +121,7 @@ export default function ipAddressesSettingAssertions(fetchMock, assert) { })(); // Mock GET endpoints to run client normally - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), { status: 200, body: splitChangesMock1 }); fetchMock.get(new RegExp(`${url(settings, '/segmentChanges/')}.*`), { status: 200, body: { since: 10, till: 10, name: 'segmentName', added: [], removed: [] } }); // Mock and assert POST endpoints diff --git a/src/__tests__/nodeSuites/lazy-init.spec.js b/src/__tests__/nodeSuites/lazy-init.spec.js index 9cb5f9735..c4d94c1d2 100644 --- a/src/__tests__/nodeSuites/lazy-init.spec.js +++ b/src/__tests__/nodeSuites/lazy-init.spec.js @@ -29,11 +29,11 @@ export default function (settings, fetchMock, t) { assert.equal(client.track('user-1', 'user', 'my_event'), true, 'We should track the event'); } - fetchMock.getOnce('https://not-called/api/splitChanges?s=1.1&since=-1', { status: 200, body: { splits: [], since: -1, till: 1457552620999 } }); - fetchMock.getOnce('https://not-called/api/splitChanges?s=1.1&since=1457552620999', { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); + fetchMock.getOnce('https://not-called/api/splitChanges?s=1.3&since=-1&rbSince=-1', { status: 200, body: { ff: { d: [], s: -1, t: 1457552620999 } } }); + fetchMock.getOnce('https://not-called/api/splitChanges?s=1.3&since=1457552620999&rbSince=-1', { status: 200, body: { ff: { d: [], s: 1457552620999, t: 1457552620999 } } }); fetchMock.postOnce('https://not-called/api/testImpressions/bulk', 200); fetchMock.postOnce('https://not-called/api/events/bulk', 200); - fetchMock.get('https://not-called/api/v2/auth?s=1.1', 200); + fetchMock.get('https://not-called/api/v2/auth?s=1.3', 200); // Validate that init and destroy are idempotent for (let i = 0; i < 3; i++) { splitio.init(); splitio.init(); splitio.destroy(); splitio.destroy(); } @@ -85,13 +85,13 @@ export default function (settings, fetchMock, t) { assert.equal(otherClient.track('user', 'my_event'), true, 'We should track the event'); } - fetchMock.getOnce('https://not-called/api/splitChanges?s=1.2&since=-1', { status: 200, body: { splits: [], since: -1, till: 1457552620999 } }); - fetchMock.getOnce('https://not-called/api/splitChanges?s=1.2&since=1457552620999', { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); + fetchMock.getOnce('https://not-called/api/splitChanges?s=1.3&since=-1&rbSince=-1', { status: 200, body: { ff: { d: [], s: -1, t: 1457552620999 } } }); + fetchMock.getOnce('https://not-called/api/splitChanges?s=1.3&since=1457552620999&rbSince=-1', { status: 200, body: { ff: { d: [], s: 1457552620999, t: 1457552620999 } } }); fetchMock.getOnce('https://not-called/api/memberships/user-99', { status: 200, body: {} }); fetchMock.getOnce('https://not-called/api/memberships/other-user', { status: 200, body: {} }); fetchMock.postOnce('https://not-called/api/testImpressions/bulk', 200); fetchMock.postOnce('https://not-called/api/events/bulk', 200); - fetchMock.get('https://not-called/api/v2/auth?s=1.2&users=user-99', 200); + fetchMock.get('https://not-called/api/v2/auth?s=1.3&users=user-99', 200); // Validate that init and destroy are idempotent for (let i = 0; i < 3; i++) { splitio.init(); splitio.init(); splitio.destroy(); splitio.destroy(); } diff --git a/src/__tests__/nodeSuites/manager.spec.js b/src/__tests__/nodeSuites/manager.spec.js index 869d65e3b..5b60434bb 100644 --- a/src/__tests__/nodeSuites/manager.spec.js +++ b/src/__tests__/nodeSuites/manager.spec.js @@ -4,7 +4,7 @@ import map from 'lodash/map'; import { url } from '../testUtils'; export default async function (settings, fetchMock, assert) { - fetchMock.get({ url: url(settings, '/splitChanges?s=1.1&since=-1'), overwriteRoutes: true }, { status: 200, body: splitChangesMockReal }); + fetchMock.get({ url: url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), overwriteRoutes: true }, { status: 200, body: splitChangesMockReal }); const mockSplits = splitChangesMockReal; @@ -28,27 +28,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__/nodeSuites/proxy-fallback.spec.js b/src/__tests__/nodeSuites/proxy-fallback.spec.js new file mode 100644 index 000000000..de2616a0d --- /dev/null +++ b/src/__tests__/nodeSuites/proxy-fallback.spec.js @@ -0,0 +1,135 @@ +import { SplitFactory } from '../../'; +import splitChangesMockReal from '../mocks/splitchanges.real.json'; +import authPushDisabled from '../mocks/auth.pushDisabled.json'; +import { nearlyEqual, url } from '../testUtils'; + +const config = { + core: { + authorizationKey: '' + }, + scheduler: { + featuresRefreshRate: 0.5, + }, + urls: { + sdk: 'https://outdated-proxy/api', + auth: 'https://outdated-proxy/api', + } +}; + +// Response mocks +const outdatedProxyFailResponse = { + status: 400 +}; +const outdatedProxyInitialResponse = { + status: 200, body: { + splits: splitChangesMockReal.ff.d, + since: splitChangesMockReal.ff.s, + till: splitChangesMockReal.ff.t + } +}; +const outdatedProxyNextResponse = { + status: 200, body: { + splits: [], + since: 1457552620999, + till: 1457552620999 + } +}; +const fixedProxyInitialResponse = { + status: 200, body: splitChangesMockReal +}; +const fixedProxyNextResponse = { + status: 200, body: { + ff: { + d: [], + s: 1457552620999, + t: 1457552620999 + } + } +}; + +export async function proxyFallbackSuite(fetchMock, assert) { + const originalDateNow = Date.now; + const start = originalDateNow(); + + fetchMock.getOnce(url(config, '/v2/auth?s=1.2'), { status: 200, body: authPushDisabled }); + + // Outdated Proxy responds with 400 when spec 1.3 is provided + fetchMock.getOnce(url(config, '/splitChanges?s=1.3&since=-1&rbSince=-1'), outdatedProxyFailResponse); + + // Fallback to spec 1.2 + fetchMock.getOnce(url(config, '/splitChanges?s=1.2&since=-1'), () => { + assert.true(nearlyEqual(originalDateNow(), start), 'Initial fallback to spec 1.2'); + return outdatedProxyInitialResponse; + }); + fetchMock.getOnce(url(config, '/splitChanges?s=1.2&since=1457552620999'), () => { + assert.true(nearlyEqual(originalDateNow(), start), 'Initial fallback to spec 1.2'); + return outdatedProxyNextResponse; + }); + + // Polling with fallback to spec 1.2 + fetchMock.getOnce(url(config, '/splitChanges?s=1.2&since=1457552620999'), () => { + assert.true(nearlyEqual(originalDateNow() - start, config.scheduler.featuresRefreshRate * 1000), 'First polling with fallback to spec 1.2'); + + // Mock Date.now() to return a +24h timestamp to force a proxy recheck + let lastTimestamp = originalDateNow(); + Date.now = () => lastTimestamp += 25 * 60 * 60 * 1000; + + return outdatedProxyNextResponse; + }); + + // Polling with proxy recheck using spec 1.3, but fail again + fetchMock.getOnce(url(config, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), () => { + assert.true(nearlyEqual(originalDateNow() - start, config.scheduler.featuresRefreshRate * 2000), 'Second polling with recheck'); + + return outdatedProxyFailResponse; + }); + // Fallback to spec 1.2 + fetchMock.getOnce(url(config, '/splitChanges?s=1.2&since=1457552620999'), () => { + assert.true(nearlyEqual(originalDateNow() - start, config.scheduler.featuresRefreshRate * 2000), 'Second polling with fallback to spec 1.2'); + + return outdatedProxyNextResponse; + }); + + // Polling with proxy recheck using spec 1.3. This time succeeds + fetchMock.getOnce(url(config, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), () => { + assert.true(nearlyEqual(originalDateNow() - start, config.scheduler.featuresRefreshRate * 3000), 'Third polling with recheck'); + + return fixedProxyNextResponse; + }); + // Proxy recovery: refetch with clear cache + fetchMock.getOnce(url(config, '/splitChanges?s=1.3&since=-1&rbSince=-1'), () => { + assert.true(nearlyEqual(originalDateNow() - start, config.scheduler.featuresRefreshRate * 3000), 'Proxy recovery: refetch with clear cache'); + + return fixedProxyInitialResponse; + }); + + // Polling with spec 1.3 + fetchMock.getOnce(url(config, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), () => { + assert.true(nearlyEqual(originalDateNow() - start, config.scheduler.featuresRefreshRate * 4000), 'Fourth polling with spec 1.3'); + + return fixedProxyNextResponse; + }); + + const splitio = SplitFactory(config); + const client = splitio.client(); + const manager = splitio.manager(); + + client.once(client.Event.SDK_READY, () => { + assert.equal(manager.splits().length, splitChangesMockReal.ff.d.length, 'SDK IS READY as it should. The manager.splits() method should return all feature flags.'); + }); + + client.once(client.Event.SDK_READY_TIMED_OUT, () => { + assert.fail('SDK TIMED OUT - It should not in this scenario'); + assert.end(); + }); + + client.once(client.Event.SDK_UPDATE, () => { + assert.true(nearlyEqual(originalDateNow() - start, config.scheduler.featuresRefreshRate * 3000), 'Proxy recovery: refetch with clear cache and spec 1.3 trigger an SDK_UPDATE event'); + assert.equal(manager.splits().length, splitChangesMockReal.ff.d.length, 'Validate that the SDK is operational after proxy recovery'); + }); + + // Wait for 4 feature refreshes + await new Promise(resolve => setTimeout(resolve, config.scheduler.featuresRefreshRate * 1000 * 4 + 200)); + await client.destroy(); + assert.end(); +} diff --git a/src/__tests__/nodeSuites/push-fallback.spec.js b/src/__tests__/nodeSuites/push-fallback.spec.js index 7789236a1..61065c7d3 100644 --- a/src/__tests__/nodeSuites/push-fallback.spec.js +++ b/src/__tests__/nodeSuites/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 occupancy0ControlPriMessage from '../mocks/message.OCCUPANCY.0.control_pri.1586987434550.json'; import occupancy1ControlPriMessage from '../mocks/message.OCCUPANCY.1.control_pri.1586987434450.json'; @@ -190,57 +190,57 @@ export function testFallback(fetchMock, assert) { }); - fetchMock.get({ url: url(settings, '/v2/auth?s=1.1'), repeat: 3 /* initial + 2 STREAMING_RESET */ }, function (url, opts) { + fetchMock.get({ url: url(settings, '/v2/auth?s=1.3'), repeat: 3 /* initial + 2 STREAMING_RESET */ }, function (url, opts) { if (!opts.headers['Authorization']) assert.fail('`/v2/auth` request must include `Authorization` header'); assert.pass('auth success'); return { status: 200, body: authPushEnabled }; }); // initial split and segment sync - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), { status: 200, body: splitChangesMockReal1 }); fetchMock.getOnce(url(settings, '/segmentChanges/employees?since=-1'), { status: 200, body: { since: -1, till: 1457552620999, name: 'employees', added: [key], removed: [] } }); // extra retry due to double request (greedy fetch until since === till) fetchMock.getOnce(url(settings, '/segmentChanges/employees?since=1457552620999'), { status: 200, body: { since: 1457552620999, till: 1457552620999, name: 'employees', added: [], removed: [] } }); // split and segment sync after SSE opened - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), { status: 200, body: { ff: { d: [], s: 1457552620999, t: 1457552620999 } } }); fetchMock.getOnce(url(settings, '/segmentChanges/employees?since=1457552620999'), { status: 200, body: { since: 1457552620999, till: 1457552620999, name: 'employees', added: [], removed: [] } }); // fetches due to first fallback to polling - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), { status: 200, body: { ff: { d: [], s: 1457552620999, t: 1457552620999 } } }); fetchMock.getOnce(url(settings, '/segmentChanges/employees?since=1457552620999'), { status: 200, body: { since: 1457552620999, till: 1457552620999, name: 'employees', added: [], removed: [] } }); - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, MILLIS_STREAMING_DOWN_OCCUPANCY + settings.scheduler.featuresRefreshRate), 'fetch due to first fallback to polling'); - return { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }; + return { status: 200, body: { ff: { d: [], s: 1457552620999, t: 1457552620999 } } }; }); fetchMock.getOnce(url(settings, '/segmentChanges/employees?since=1457552620999'), { status: 200, body: { since: 1457552620999, till: 1457552620999, name: 'employees', added: [], removed: [] } }); // split and segment sync due to streaming up (OCCUPANCY event) - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), { status: 200, body: { ff: { d: [], s: 1457552620999, t: 1457552620999 } } }); fetchMock.getOnce(url(settings, '/segmentChanges/employees?since=1457552620999'), { status: 200, body: { since: 1457552620999, till: 1457552621999, name: 'employees', added: ['other_key'], removed: [] } }); // extra retry due to double request (greedy fetch until since === till) fetchMock.getOnce(url(settings, '/segmentChanges/employees?since=1457552621999'), { status: 200, body: { since: 1457552621999, till: 1457552621999, name: 'employees', added: [], removed: [] } }); // fetch due to SPLIT_UPDATE event - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=-1'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, MILLIS_SPLIT_UPDATE_EVENT_DURING_PUSH), 'sync due to SPLIT_UPDATE event'); - return { status: 200, body: splitChangesMock2 }; + return { status: 200, body: splitChangesMockReal2 }; }); // fetches due to second fallback to polling - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552649999'), { status: 200, body: { splits: [], since: 1457552649999, till: 1457552649999 } }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552649999&rbSince=-1'), { status: 200, body: { ff: { d: [], s: 1457552649999, t: 1457552649999 } } }); fetchMock.getOnce(url(settings, '/segmentChanges/employees?since=1457552621999'), { status: 200, body: { since: 1457552621999, till: 1457552621999, name: 'employees', added: [], removed: [] } }); - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552649999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552649999&rbSince=-1'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, MILLIS_STREAMING_PAUSED_CONTROL + settings.scheduler.featuresRefreshRate), 'fetch due to second fallback to polling'); - return { status: 200, body: { splits: [], since: 1457552649999, till: 1457552649999 } }; + return { status: 200, body: { ff: { d: [], s: 1457552649999, t: 1457552649999 } } }; }); fetchMock.getOnce(url(settings, '/segmentChanges/employees?since=1457552621999'), { status: 200, body: { since: 1457552621999, till: 1457552621999, name: 'employees', added: [], removed: [] } }); // split and segment sync due to streaming up (CONTROL event) - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552649999'), { status: 200, body: { splits: [], since: 1457552649999, till: 1457552649999 } }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552649999&rbSince=-1'), { status: 200, body: { ff: { d: [], s: 1457552649999, t: 1457552649999 } } }); fetchMock.getOnce(url(settings, '/segmentChanges/employees?since=1457552621999'), { status: 200, body: { since: 1457552621999, till: 1457552621999, name: 'employees', added: [], removed: [] } }); // fetch due to SEGMENT_UPDATE event @@ -249,22 +249,22 @@ export function testFallback(fetchMock, assert) { fetchMock.getOnce(url(settings, '/segmentChanges/employees?since=1457552650000'), { status: 200, body: { since: 1457552650000, till: 1457552650000, name: 'employees', added: [], removed: [] } }); // Fetches due to third fallback to polling (second STREAMING_PAUSED event) and two syncAll ( SSE opened twice due to two STREAMING_RESET events) - fetchMock.get({ url: url(settings, '/splitChanges?s=1.1&since=1457552649999'), repeat: 3 }, { status: 200, body: { splits: [], since: 1457552649999, till: 1457552649999 } }); + fetchMock.get({ url: url(settings, '/splitChanges?s=1.3&since=1457552649999&rbSince=-1'), repeat: 3 }, { status: 200, body: { ff: { d: [], s: 1457552649999, t: 1457552649999 } } }); fetchMock.get({ url: url(settings, '/segmentChanges/employees?since=1457552650000'), repeat: 3 }, { status: 200, body: { since: 1457552650000, till: 1457552650000, name: 'employees', added: [], removed: [] } }); // fetches due to fourth fallback to polling due to STREAMING_DISABLED event - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552649999'), { status: 200, body: { splits: [], since: 1457552649999, till: 1457552649999 } }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552649999&rbSince=-1'), { status: 200, body: { ff: { d: [], s: 1457552649999, t: 1457552649999 } } }); fetchMock.getOnce(url(settings, '/segmentChanges/employees?since=1457552650000'), { status: 200, body: { since: 1457552650000, till: 1457552650000, name: 'employees', added: [], removed: [] } }); - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552649999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552649999&rbSince=-1'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, MILLIS_STREAMING_DISABLED_CONTROL + settings.scheduler.featuresRefreshRate), 'fetch due to fourth fallback to polling'); - return { status: 200, body: splitChangesMock3 }; + return { status: 200, body: splitChangesMockReal3 }; }); fetchMock.getOnce(url(settings, '/segmentChanges/employees?since=1457552650000'), { status: 200, body: { since: 1457552650000, till: 1457552650000, name: 'employees', added: [], removed: [] } }); - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552669999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552669999&rbSince=-1'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, MILLIS_STREAMING_DISABLED_CONTROL + settings.scheduler.featuresRefreshRate * 2), 'fetch due to third fallback to polling'); - return { status: 200, body: { splits: [], since: 1457552669999, till: 1457552669999 } }; + return { status: 200, body: { ff: { d: [], s: 1457552669999, t: 1457552669999 } } }; }); fetchMock.getOnce(url(settings, '/segmentChanges/employees?since=1457552650000'), { status: 200, body: { since: 1457552650000, till: 1457552650000, name: 'employees', added: [], removed: [] } }); diff --git a/src/__tests__/nodeSuites/push-flag-sets.spec.js b/src/__tests__/nodeSuites/push-flag-sets.spec.js index d8beb05ac..e8a6fa6d5 100644 --- a/src/__tests__/nodeSuites/push-flag-sets.spec.js +++ b/src/__tests__/nodeSuites/push-flag-sets.spec.js @@ -39,22 +39,22 @@ export function testFlagSets(fetchMock, t) { mockSegmentChanges(fetchMock, new RegExp(baseUrls.sdk + '/segmentChanges/*'), ['some-key']); fetchMock.post('*', 200); - fetchMock.get(baseUrls.auth + '/v2/auth?s=1.1', function (url, opts) { + fetchMock.get(baseUrls.auth + '/v2/auth?s=1.3', function (url, opts) { if (!opts.headers['Authorization']) t.fail('`/v2/auth` request must include `Authorization` header'); t.pass('auth success'); return { status: 200, body: authPushEnabled }; }); - fetchMock.get(baseUrls.sdk + '/splitChanges?s=1.1&since=-1', function () { - return { status: 200, body: { splits: [], since: -1, till: 0}}; + fetchMock.get(baseUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', function () { + return { status: 200, body: { ff: { d: [], s: -1, t: 0 }}}; }); - fetchMock.get(baseUrls.sdk + '/splitChanges?s=1.1&since=0', function () { - return { status: 200, body: { splits: [], since: 0, till: 1 }}; + fetchMock.get(baseUrls.sdk + '/splitChanges?s=1.3&since=0&rbSince=-1', function () { + return { status: 200, body: { ff: { d: [], s: 0, t: 1 } }}; }); - fetchMock.get(baseUrls.sdk + '/splitChanges?s=1.1&since=-1&sets=set_1,set_2', function () { - return { status: 200, body: { splits: [], since: -1, till: 0 }}; + fetchMock.get(baseUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1&sets=set_1,set_2', function () { + return { status: 200, body: { ff: { d: [], s: -1, t: 0 } }}; }); - fetchMock.get(baseUrls.sdk + '/splitChanges?s=1.1&since=0&sets=set_1,set_2', function () { - return { status: 200, body: { splits: [], since: 0, till: 1 }}; + fetchMock.get(baseUrls.sdk + '/splitChanges?s=1.3&since=0&rbSince=-1&sets=set_1,set_2', function () { + return { status: 200, body: { ff: { d: [], s: 0, t: 1 } }}; }); const configWithSets = { @@ -194,11 +194,11 @@ export function testFlagSets(fetchMock, t) { setMockListener((eventSourceInstance) => { - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&since=2&sets=set_1,set_2', async function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=2&rbSince=-1&sets=set_1,set_2', async function () { assert.pass('4 - A fetch is triggered due to the SPLIT_KILL'); await client.destroy(); assert.end(); - return { status: 200, body: { splits: [], since: 2, till: 3 }}; + return { status: 200, body: { ff: { d: [], s: 2, t: 3 } }}; }); eventSourceInstance.emitOpen(); @@ -232,9 +232,9 @@ export function testFlagSets(fetchMock, t) { t.test(async (assert) => { - fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.1&since=1&sets=set_1,set_2', function () { + fetchMock.getOnce(baseUrls.sdk + '/splitChanges?s=1.3&since=1&rbSince=-1&sets=set_1,set_2', function () { assert.pass('5 - A fetch is triggered due to the SPLIT_KILL'); - return { status: 200, body: { splits: [], since: 1, till: 5 }}; + return { status: 200, body: { ff: { d: [], s: 1, t: 5 } }}; }); let splitio, client, manager = []; diff --git a/src/__tests__/nodeSuites/push-initialization-nopush.spec.js b/src/__tests__/nodeSuites/push-initialization-nopush.spec.js index b79db1f1f..e06397592 100644 --- a/src/__tests__/nodeSuites/push-initialization-nopush.spec.js +++ b/src/__tests__/nodeSuites/push-initialization-nopush.spec.js @@ -42,7 +42,7 @@ function testInitializationFail(fetchMock, assert, fallbackToPolling) { fetchMock.get(new RegExp(`${url(settings, '/segmentChanges/')}.*`), { status: 200, body: { since: 10, till: 10, name: 'segmentName', added: [], removed: [] } }); - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), function () { const lapse = Date.now() - start; // using a higher error margin for Travis, due to a lower performance than local execution assert.true(nearlyEqual(lapse, 0), 'initial sync'); @@ -50,7 +50,7 @@ function testInitializationFail(fetchMock, assert, fallbackToPolling) { }); if (fallbackToPolling) { - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=100'), function () { assert.true(ready, 'client ready'); const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, 0, 100), 'polling (first fetch)'); @@ -58,7 +58,7 @@ function testInitializationFail(fetchMock, assert, fallbackToPolling) { }); } - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=100'), function () { assert.true(ready, 'client ready'); const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, settings.scheduler.featuresRefreshRate, 100), 'polling (second fetch)'); @@ -80,7 +80,7 @@ function testInitializationFail(fetchMock, assert, fallbackToPolling) { export function testAuthWithPushDisabled(fetchMock, assert) { assert.plan(6); - fetchMock.getOnce('https://auth.push-initialization-nopush/api/v2/auth?s=1.1', function (url, opts) { + fetchMock.getOnce('https://auth.push-initialization-nopush/api/v2/auth?s=1.3', function (url, opts) { if (!opts.headers['Authorization']) assert.fail('`/v2/auth` request must include `Authorization` header'); assert.pass('auth'); return { status: 200, body: authPushDisabled }; @@ -93,7 +93,7 @@ export function testAuthWithPushDisabled(fetchMock, assert) { export function testAuthWith401(fetchMock, assert) { assert.plan(6); - fetchMock.getOnce(url(settings, '/v2/auth?s=1.1'), function (url, opts) { + fetchMock.getOnce(url(settings, '/v2/auth?s=1.3'), function (url, opts) { if (!opts.headers['Authorization']) assert.fail('`/v2/auth` request must include `Authorization` header'); assert.pass('auth'); return { status: 401, body: authInvalidCredentials }; @@ -106,7 +106,7 @@ export function testAuthWith401(fetchMock, assert) { export function testAuthWith400(fetchMock, assert) { assert.plan(6); - fetchMock.getOnce(url(settings, '/v2/auth?s=1.1'), function (url, opts) { + fetchMock.getOnce(url(settings, '/v2/auth?s=1.3'), function (url, opts) { if (!opts.headers['Authorization']) assert.fail('`/v2/auth` request must include `Authorization` header'); assert.pass('auth'); return { status: 400, body: authNoUserSpecified }; @@ -131,7 +131,7 @@ export function testSSEWithNonRetryableError(fetchMock, assert) { assert.plan(7); // Auth successes - fetchMock.getOnce(url(settings, '/v2/auth?s=1.1'), function (url, opts) { + fetchMock.getOnce(url(settings, '/v2/auth?s=1.3'), function (url, opts) { if (!opts.headers['Authorization']) assert.fail('`/v2/auth` request must include `Authorization` header'); assert.pass('auth successes'); return { status: 200, body: authPushEnabled }; diff --git a/src/__tests__/nodeSuites/push-initialization-retries.spec.js b/src/__tests__/nodeSuites/push-initialization-retries.spec.js index 88cc30af8..94fc9d7c0 100644 --- a/src/__tests__/nodeSuites/push-initialization-retries.spec.js +++ b/src/__tests__/nodeSuites/push-initialization-retries.spec.js @@ -45,13 +45,13 @@ export function testPushRetriesDueToAuthErrors(fetchMock, assert) { let start, splitio, client, ready = false; - fetchMock.getOnce(url(settings, '/v2/auth?s=1.1'), function (url, opts) { + fetchMock.getOnce(url(settings, '/v2/auth?s=1.3'), function (url, opts) { if (!opts.headers['Authorization']) assert.fail('`/v2/auth` request must include `Authorization` header'); assert.pass('first auth attempt'); return { status: 200, body: authPushBadToken }; }); - fetchMock.getOnce(url(settings, '/v2/auth?s=1.1'), { throws: new TypeError('Network error') }); - fetchMock.getOnce(url(settings, '/v2/auth?s=1.1'), function (url, opts) { + fetchMock.getOnce(url(settings, '/v2/auth?s=1.3'), { throws: new TypeError('Network error') }); + fetchMock.getOnce(url(settings, '/v2/auth?s=1.3'), function (url, opts) { if (!opts.headers['Authorization']) assert.fail('`/v2/auth` request must include `Authorization` header'); const lapse = Date.now() - start; const expected = (settings.scheduler.pushRetryBackoffBase * Math.pow(2, 0) + settings.scheduler.pushRetryBackoffBase * Math.pow(2, 1)); @@ -60,23 +60,23 @@ export function testPushRetriesDueToAuthErrors(fetchMock, assert) { }); fetchMock.get(new RegExp(`${url(settings, '/segmentChanges/')}.*`), { status: 200, body: { since: 10, till: 10, name: 'segmentName', added: [], removed: [] } }); - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, 0), 'initial sync'); return { status: 200, body: splitChangesMock1 }; }); - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=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.1&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.1&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(() => { @@ -122,30 +122,30 @@ export function testPushRetriesDueToSseErrors(fetchMock, assert) { sseattempts++; }); - fetchMock.get({ url: url(settings, '/v2/auth?s=1.1'), repeat: 3 /* 3 push attempts */ }, function (url, opts) { + fetchMock.get({ url: url(settings, '/v2/auth?s=1.3'), repeat: 3 /* 3 push attempts */ }, function (url, opts) { if (!opts.headers['Authorization']) assert.fail('`/v2/auth` request must include `Authorization` header'); assert.pass('auth success'); return { status: 200, body: authPushEnabled }; }); fetchMock.get(new RegExp(`${url(settings, '/segmentChanges/')}.*`), { status: 200, body: { since: 10, till: 10, name: 'segmentName', added: [], removed: [] } }); - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), function () { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, 0), 'initial sync'); return { status: 200, body: splitChangesMock1 }; }); - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=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.1&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.1&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(() => { @@ -181,10 +181,10 @@ export function testSdkDestroyWhileAuthSuccess(fetchMock, assert) { let splitio, client, ready = false; - fetchMock.getOnce(url(settings, '/v2/auth?s=1.1'), { status: 200, body: authPushEnabled }, { delay: 100 }); + fetchMock.getOnce(url(settings, '/v2/auth?s=1.3'), { status: 200, body: authPushEnabled }, { delay: 100 }); fetchMock.get(new RegExp(`${url(settings, '/segmentChanges/')}.*`), { status: 200, body: { since: 10, till: 10, name: 'segmentName', added: [], removed: [] } }); - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), { status: 200, body: splitChangesMock1 }); setTimeout(() => { client.destroy().then(() => { @@ -219,12 +219,12 @@ export function testSdkDestroyWhileAuthRetries(fetchMock, assert) { let splitio, client, ready = false; - fetchMock.getOnce(url(settings, '/v2/auth?s=1.1'), { status: 200, body: authPushBadToken }); - fetchMock.getOnce(url(settings, '/v2/auth?s=1.1'), { throws: new TypeError('Network error') }, { delay: 100 }); + fetchMock.getOnce(url(settings, '/v2/auth?s=1.3'), { status: 200, body: authPushBadToken }); + fetchMock.getOnce(url(settings, '/v2/auth?s=1.3'), { throws: new TypeError('Network error') }, { delay: 100 }); fetchMock.get(new RegExp(`${url(settings, '/segmentChanges/')}.*`), { status: 200, body: { since: 10, till: 10, name: 'segmentName', added: [], removed: [] } }); - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), { status: 200, body: splitChangesMock2 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=100'), { status: 200, body: splitChangesMock2 }); fetchMock.get(new RegExp('.*'), function (url) { assert.fail('unexpected GET request with url: ' + url); diff --git a/src/__tests__/nodeSuites/push-refresh-token.spec.js b/src/__tests__/nodeSuites/push-refresh-token.spec.js index b3246a2c4..148bc45b7 100644 --- a/src/__tests__/nodeSuites/push-refresh-token.spec.js +++ b/src/__tests__/nodeSuites/push-refresh-token.spec.js @@ -77,20 +77,20 @@ export function testRefreshToken(fetchMock, assert) { }); // initial split sync - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), { status: 200, body: splitChangesMock1 }); // first auth - fetchMock.getOnce(url(settings, '/v2/auth?s=1.1'), function (url, opts) { + fetchMock.getOnce(url(settings, '/v2/auth?s=1.3'), function (url, opts) { if (!opts.headers['Authorization']) assert.fail('`/v2/auth` request must include `Authorization` header'); assert.pass('auth success'); return { status: 200, body: authPushEnabled }; }); // split sync after SSE opened - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), { status: 200, body: splitChangesMock2 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=100'), { status: 200, body: splitChangesMock2 }); // re-auth due to refresh token, with connDelay of 0.5 seconds - fetchMock.getOnce(url(settings, '/v2/auth?s=1.1'), function (url, opts) { + fetchMock.getOnce(url(settings, '/v2/auth?s=1.3'), function (url, opts) { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, MILLIS_REFRESH_TOKEN), 'reauthentication for token refresh'); if (!opts.headers['Authorization']) assert.fail('`/v2/auth` request must include `Authorization` header'); @@ -98,14 +98,14 @@ export function testRefreshToken(fetchMock, assert) { }); // split sync after SSE reopened - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=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 } } }; }); // second re-auth due to refresh token, this time responding with pushEnabled false - fetchMock.getOnce(url(settings, '/v2/auth?s=1.1'), function (url, opts) { + fetchMock.getOnce(url(settings, '/v2/auth?s=1.3'), function (url, opts) { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, MILLIS_REFRESH_TOKEN * 2), 'second reauthentication for token refresh'); if (!opts.headers['Authorization']) assert.fail('`/v2/auth` request must include `Authorization` header'); @@ -113,7 +113,7 @@ export function testRefreshToken(fetchMock, assert) { }); // split sync after SSE closed due to push disabled - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=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__/nodeSuites/push-synchronization-retries.spec.js b/src/__tests__/nodeSuites/push-synchronization-retries.spec.js index e1154e9d6..227b7fdde 100644 --- a/src/__tests__/nodeSuites/push-synchronization-retries.spec.js +++ b/src/__tests__/nodeSuites/push-synchronization-retries.spec.js @@ -126,14 +126,14 @@ export function testSynchronizationRetries(fetchMock, assert) { }); // initial auth - fetchMock.getOnce(url(settings, '/v2/auth?s=1.1'), function (url, opts) { + fetchMock.getOnce(url(settings, '/v2/auth?s=1.3'), function (url, opts) { if (!opts.headers['Authorization']) assert.fail('`/v2/auth` request must include `Authorization` header'); assert.pass('auth success'); return { status: 200, body: authPushEnabled }; }); // initial split and segment sync - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), { status: 200, body: splitChangesMock1 }); fetchMock.getOnce(url(settings, '/segmentChanges/splitters?since=-1'), { status: 200, body: { since: -1, till: 1457552620999, name: 'splitters', added: [key], removed: [] } } ); @@ -143,7 +143,7 @@ export function testSynchronizationRetries(fetchMock, assert) { ); // split and segment sync after SSE opened - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=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 }; @@ -153,9 +153,9 @@ export function testSynchronizationRetries(fetchMock, assert) { ); // fetch due to SPLIT_UPDATE event - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), { throws: new TypeError('Network error') }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=100'), { throws: new TypeError('Network error') }); // fetch retry for SPLIT_UPDATE event, due to previous fail - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=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 }; @@ -182,18 +182,18 @@ export function testSynchronizationRetries(fetchMock, assert) { fetchMock.getOnce(url(settings, '/segmentChanges/splitters?since=1457552640000'), { status: 200, body: { since: 1457552640000, till: 1457552640000, name: 'splitters', added: [], removed: [] } }); // fetch due to SPLIT_KILL event - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552649999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552649999&rbSince=100'), function () { assert.equal(client.getTreatment(key, 'whitelist'), 'not_allowed', 'evaluation with split killed immediately, before fetch is done'); const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, MILLIS_SPLIT_KILL_EVENT), 'sync due to SPLIT_KILL event'); - return { status: 200, body: { since: 1457552649999, till: 1457552649999, splits: [] } }; // returning old state + return { status: 200, body: { ff: { d: [], s: 1457552649999, t: 1457552649999 } } }; // returning old state }); // first fetch retry for SPLIT_KILL event, due to previous unexpected response (response till minor than SPLIT_KILL changeNumber) - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552649999'), { status: 200, body: '{ "since": 1457552620999, "til' }); // invalid JSON + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552649999&rbSince=100'), { status: 200, body: '{ "since": 1457552620999, "til' }); // invalid JSON // second fetch retry for SPLIT_KILL event - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552649999'), { throws: new TypeError('Network error') }); + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552649999&rbSince=100'), { throws: new TypeError('Network error') }); // third fetch retry for SPLIT_KILL event - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552649999'), function () { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552649999&rbSince=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'); @@ -208,7 +208,7 @@ export function testSynchronizationRetries(fetchMock, assert) { return { status: 408, body: 'request timeout' }; }); - mockSegmentChanges(fetchMock, new RegExp(`${url(settings, '/segmentChanges')}/(employees|developers)`), [key]); + mockSegmentChanges(fetchMock, new RegExp(`${url(settings, '/segmentChanges')}/(employees|developers|segment_excluded_by_rbs)`), [key]); fetchMock.get(new RegExp('.*'), function (url) { assert.fail('unexpected GET request with url: ' + url); diff --git a/src/__tests__/nodeSuites/push-synchronization.spec.js b/src/__tests__/nodeSuites/push-synchronization.spec.js index 9f9cb9775..a97ad5937 100644 --- a/src/__tests__/nodeSuites/push-synchronization.spec.js +++ b/src/__tests__/nodeSuites/push-synchronization.spec.js @@ -7,6 +7,7 @@ import splitChangesMock6 from '../mocks/splitchanges.since.1684265694505.till.16 import splitChangesMock7 from '../mocks/splitchanges.since.1684265694506.till.1684265694526.SPLIT_UPDATE.json'; import splitChangesMock8 from '../mocks/splitchanges.since.1684265694526.till.1684265694546.SPLIT_UPDATE.json'; import splitChangesMock9 from '../mocks/splitchanges.since.1684265694546.till.1684265694556.SPLIT_UPDATE.json'; +import splitChangesMock10 from '../mocks/splitchanges.since.100.till.1457552649999.RB_SEGMENT_UPDATE.json'; import splitUpdateMessage from '../mocks/message.SPLIT_UPDATE.1457552649999.json'; import oldSplitUpdateMessage from '../mocks/message.SPLIT_UPDATE.1457552620999.json'; @@ -19,6 +20,11 @@ import iffuSplitUpdateMessageZeroPCN from '../mocks/message.SPLIT_UPDATE.IFFU.16 import iffuSplitUpdateMessageMissingPCN from '../mocks/message.SPLIT_UPDATE.IFFU.1684265694545.json'; import iffuSplitUpdateMessageArchivedFF from '../mocks/message.SPLIT_UPDATE.IFFU.1684265694555.json'; +import rbsUpdateMessage from '../mocks/message.RB_SEGMENT_UPDATE.1457552649999.json'; +import iffuRbsUpdateNoCompressionMessage from '../mocks/message.RB_SEGMENT_UPDATE.C0.json'; +import iffuRbsUpdateGZipMessage from '../mocks/message.RB_SEGMENT_UPDATE.C1.json'; +import iffuRbsUpdateZLibMessage from '../mocks/message.RB_SEGMENT_UPDATE.C2.json'; + import authPushEnabled from '../mocks/auth.pushEnabled.node.json'; import { nearlyEqual, mockSegmentChanges, url, hasNoCacheHeader } from '../testUtils'; @@ -59,7 +65,11 @@ const MILLIS_IFFU_UPDATE_EVENT_WITH_OLD_CHANGENUMBER = 900; const MILLIS_IFFU_UPDATE_EVENT_WITH_ZERO_PCN = 1000; const MILLIS_IFFU_UPDATE_EVENT_WITH_MISSING_PCN = 1100; const MILLIS_IFFU_UPDATE_EVENT_WITH_ARCHIVED = 1200; -const MILLIS_DESTROY = 1300; +const MILLIS_FIRST_RB_SEGMENT_UPDATE_EVENT = 1300; +const MILLIS_IFFU_RB_SEGMENT_UPDATE_C0_EVENT = 1400; +const MILLIS_IFFU_RB_SEGMENT_UPDATE_C1_EVENT = 1500; +const MILLIS_IFFU_RB_SEGMENT_UPDATE_C2_EVENT = 1600; +const MILLIS_DESTROY = 1700; /** * Sequence of calls: @@ -76,9 +86,13 @@ const MILLIS_DESTROY = 1300; * 1.0 secs: SPLIT_UPDATE IFFU event with pcn = 0 -> /splitChanges * 1.1 secs: SPLIT_UPDATE IFFU event with previous change number !== current change number -> /splitChanges * 1.2 secs: SPLIT_UPDATE IFFU event with ARCHIVED feature flag in notification and Base64 encoded + zLib (c==2) -> /splitChanges + * 1.3 secs: RB_SEGMENT_UPDATE event -> /splitChanges + * 1.4 secs: RB_SEGMENT_UPDATE IFFU event with no compression + * 1.5 secs: RB_SEGMENT_UPDATE IFFU event with Gzip compression + * 1.6 secs: RB_SEGMENT_UPDATE IFFU event with ZLib compression */ export function testSynchronization(fetchMock, assert) { - assert.plan(49); + assert.plan(53); fetchMock.reset(); __setEventSource(EventSourceMock); @@ -204,25 +218,57 @@ export function testSynchronization(fetchMock, assert) { eventSourceInstance.emitMessage(iffuSplitUpdateMessageArchivedFF); }, MILLIS_IFFU_UPDATE_EVENT_WITH_ARCHIVED); // send a SPLIT_UPDATE event with pcn = 0 after 1.1 seconds + setTimeout(() => { + client.once(client.Event.SDK_UPDATE, () => { + const lapse = Date.now() - start; + assert.true(nearlyEqual(lapse, MILLIS_FIRST_RB_SEGMENT_UPDATE_EVENT), 'SDK_UPDATE due to RB_SEGMENT_UPDATE event'); + }); + eventSourceInstance.emitMessage(rbsUpdateMessage); + }, MILLIS_FIRST_RB_SEGMENT_UPDATE_EVENT); // send a RB_SEGMENT_UPDATE event with a new changeNumber + + setTimeout(() => { + client.once(client.Event.SDK_UPDATE, () => { + const lapse = Date.now() - start; + assert.true(nearlyEqual(lapse, MILLIS_IFFU_RB_SEGMENT_UPDATE_C0_EVENT), 'SDK_UPDATE due to RB_SEGMENT_UPDATE IFFU event with no compression'); + }); + eventSourceInstance.emitMessage(iffuRbsUpdateNoCompressionMessage); + }, MILLIS_IFFU_RB_SEGMENT_UPDATE_C0_EVENT); // send a IFFU RB_SEGMENT_UPDATE event + + setTimeout(() => { + client.once(client.Event.SDK_UPDATE, () => { + const lapse = Date.now() - start; + assert.true(nearlyEqual(lapse, MILLIS_IFFU_RB_SEGMENT_UPDATE_C1_EVENT), 'SDK_UPDATE due to RB_SEGMENT_UPDATE IFFU event with GZip compression'); + }); + eventSourceInstance.emitMessage(iffuRbsUpdateGZipMessage); + }, MILLIS_IFFU_RB_SEGMENT_UPDATE_C1_EVENT); // send a IFFU RB_SEGMENT_UPDATE event + + setTimeout(() => { + client.once(client.Event.SDK_UPDATE, () => { + const lapse = Date.now() - start; + assert.true(nearlyEqual(lapse, MILLIS_IFFU_RB_SEGMENT_UPDATE_C2_EVENT), 'SDK_UPDATE due to RB_SEGMENT_UPDATE IFFU event with ZLib compression'); + }); + eventSourceInstance.emitMessage(iffuRbsUpdateZLibMessage); + }, MILLIS_IFFU_RB_SEGMENT_UPDATE_C2_EVENT); // send a IFFU RB_SEGMENT_UPDATE event + setTimeout(() => { client.destroy().then(() => { assert.equal(client.getTreatment(key, 'whitelist'), 'control', 'evaluation returns control if client is destroyed'); - // @TODO SDK_UPDATE should be emitted 9 times, but currently it is being emitted twice on SPLIT_KILL - assert.equal(sdkUpdateCount, 10, 'SDK_UPDATE should be emitted 10 times'); + // @TODO SDK_UPDATE should be emitted 13 times, but currently it is being emitted twice on SPLIT_KILL + assert.equal(sdkUpdateCount, 14, 'SDK_UPDATE should be emitted 14 times'); assert.end(); }); - }, MILLIS_DESTROY); // destroy client after 1.3 second + }, MILLIS_DESTROY); // destroy client }); // initial auth - fetchMock.getOnce(url(settings, '/v2/auth?s=1.1'), function (url, opts) { + fetchMock.getOnce(url(settings, '/v2/auth?s=1.3'), function (url, opts) { if (!opts.headers['Authorization']) assert.fail('`/v2/auth` request must include `Authorization` header'); assert.pass('auth success'); return { status: 200, body: authPushEnabled }; }); // initial split and segment sync - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=-1'), function (url, opts) { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), function (url, opts) { const lapse = Date.now() - start; assert.true(nearlyEqual(lapse, 0), 'initial sync'); if (hasNoCacheHeader(opts)) assert.fail('request must not include `Cache-Control` header'); @@ -239,7 +285,7 @@ export function testSynchronization(fetchMock, assert) { }); // split and segment sync after SSE opened - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function (url, opts) { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=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'); @@ -251,7 +297,7 @@ export function testSynchronization(fetchMock, assert) { }); // fetch due to SPLIT_UPDATE event - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552620999'), function (url, opts) { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=100'), function (url, opts) { if (!hasNoCacheHeader(opts)) assert.fail('request must include `Cache-Control` header'); return { status: 200, body: splitChangesMock3 }; }); @@ -268,14 +314,14 @@ export function testSynchronization(fetchMock, assert) { }); // fetch due to SPLIT_KILL event - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552649999'), function (url, opts) { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552649999&rbSince=100'), function (url, opts) { if (!hasNoCacheHeader(opts)) assert.fail('request must include `Cache-Control` header'); assert.equal(client.getTreatment(key, 'whitelist'), 'not_allowed', 'evaluation with split killed immediately, before fetch is done'); return { status: 200, body: splitChangesMock4 }; }); // fetch due to SPLIT_UPDATE event, with an update that involves a new segment - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1457552650000'), function (url, opts) { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1457552650000&rbSince=100'), function (url, opts) { if (!hasNoCacheHeader(opts)) assert.fail('request must include `Cache-Control` header'); return { status: 200, body: splitChangesMock5 }; }); @@ -286,30 +332,34 @@ export function testSynchronization(fetchMock, assert) { }); // fetch feature flags due to IFFU SPLIT_UPDATE event with wrong compress code - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1684265694505'), function (url, opts) { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1684265694505&rbSince=100'), function (url, opts) { if (!hasNoCacheHeader(opts)) assert.fail('request must include `Cache-Control` header'); return { status: 200, body: splitChangesMock6 }; }); // fetch feature flags due to IFFU SPLIT_UPDATE event with previous change number = 0 - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1684265694506'), function (url, opts) { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1684265694506&rbSince=100'), function (url, opts) { if (!hasNoCacheHeader(opts)) assert.fail('request must include `Cache-Control` header'); return { status: 200, body: splitChangesMock7 }; }); // fetch feature flags due to IFFU SPLIT_UPDATE event with previous change number !== current change number - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1684265694526'), function (url, opts) { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1684265694526&rbSince=100'), function (url, opts) { if (!hasNoCacheHeader(opts)) assert.fail('request must include `Cache-Control` header'); return { status: 200, body: splitChangesMock8 }; }); // fetch feature flags due to IFFU SPLIT_UPDATE event with ARCHIVED feature flag - fetchMock.getOnce(url(settings, '/splitChanges?s=1.1&since=1684265694546'), function (url, opts) { + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1684265694546&rbSince=100'), function (url, opts) { if (!hasNoCacheHeader(opts)) assert.fail('request must include `Cache-Control` header'); return { status: 200, body: splitChangesMock9 }; }); + // fetch due to RB_SEGMENTS_UPDATE event + fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=1684265694556&rbSince=100'), { status: 200, body: splitChangesMock10 }); + mockSegmentChanges(fetchMock, new RegExp(`${url(settings, '/segmentChanges')}/employees`), [key]); + mockSegmentChanges(fetchMock, new RegExp(`${url(settings, '/segmentChanges')}/segment_excluded_by_rbs`), []); // Special case: empty segment with -1 till mockSegmentChanges(fetchMock, new RegExp(`${url(settings, '/segmentChanges')}/developers`), [], -1); mockSegmentChanges(fetchMock, { url: new RegExp(`${url(settings, '/segmentChanges')}/new_segment`), repeat: 2 }, [otherUserKey]); diff --git a/src/__tests__/nodeSuites/readiness.spec.js b/src/__tests__/nodeSuites/readiness.spec.js index 33e377eb3..03372d16f 100644 --- a/src/__tests__/nodeSuites/readiness.spec.js +++ b/src/__tests__/nodeSuites/readiness.spec.js @@ -23,8 +23,8 @@ export default function (fetchMock, assert) { events: 'https://events.baseurl/readinessSuite1' }; - fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.1&since=-1', { status: 200, body: splitChangesMock1 }); - fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.1&since=1457552620999', { status: 200, body: splitChangesMock2 }); + fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.3&since=1457552620999&rbSince=-1', { status: 200, body: splitChangesMock2 }); fetchMock.get(new RegExp(testUrls.sdk + '/segmentChanges/*'), 403); fetchMock.postOnce(testUrls.events + '/events/bulk', 200); diff --git a/src/__tests__/nodeSuites/ready-promise.spec.js b/src/__tests__/nodeSuites/ready-promise.spec.js index e5811b302..7d0f35619 100644 --- a/src/__tests__/nodeSuites/ready-promise.spec.js +++ b/src/__tests__/nodeSuites/ready-promise.spec.js @@ -56,8 +56,8 @@ export default function readyPromiseAssertions(key, fetchMock, assert) { }; // /splitChanges takes longer than 'requestTimeoutBeforeReady' in both attempts - fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); - fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); fetchMock.postOnce(config.urls.events + '/testImpressions/bulk', 200); fetchMock.postOnce(config.urls.events + '/testImpressions/count', 200); @@ -103,8 +103,8 @@ export default function readyPromiseAssertions(key, fetchMock, assert) { }; // /splitChanges takes longer than 'requestTimeoutBeforeReady' only for the first attempt - fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); - fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); mockSegmentChanges(fetchMock, new RegExp(config.urls.sdk + '/segmentChanges/*'), ['some_key']); fetchMock.postOnce(config.urls.events + '/testImpressions/bulk', 200); fetchMock.postOnce(config.urls.events + '/testImpressions/count', 200); @@ -153,8 +153,8 @@ export default function readyPromiseAssertions(key, fetchMock, assert) { }; // /splitChanges takes longer than 'requestTimeoutBeforeReady' only for the first attempt - fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); - fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); mockSegmentChanges(fetchMock, new RegExp(config.urls.sdk + '/segmentChanges/*'), ['some_key']); fetchMock.postOnce(config.urls.events + '/testImpressions/bulk', 200); fetchMock.postOnce(config.urls.events + '/testImpressions/count', 200); @@ -222,9 +222,9 @@ export default function readyPromiseAssertions(key, fetchMock, assert) { config.scheduler.featuresRefreshRate) - config.startup.readyTimeout) + refreshTimeMillis; // /splitChanges takes longer than 'requestTimeoutBeforeReady' in both initial attempts - fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); - fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); - fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: refreshTimeMillis }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', splitChangesMock1, { delay: refreshTimeMillis }); mockSegmentChanges(fetchMock, new RegExp(config.urls.sdk + '/segmentChanges/*'), ['some_key']); fetchMock.postOnce(config.urls.events + '/testImpressions/bulk', 200); fetchMock.postOnce(config.urls.events + '/testImpressions/count', 200); @@ -281,7 +281,7 @@ export default function readyPromiseAssertions(key, fetchMock, assert) { }; // /splitChanges takes longer than 'requestTimeoutBeforeReady' - fetchMock.get(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.get(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); mockSegmentChanges(fetchMock, new RegExp(config.urls.sdk + '/segmentChanges/*'), ['some_key']); fetchMock.postOnce(config.urls.events + '/testImpressions/bulk', 200); fetchMock.postOnce(config.urls.events + '/testImpressions/count', 200); @@ -331,7 +331,7 @@ export default function readyPromiseAssertions(key, fetchMock, assert) { }; // /splitChanges takes less than 'requestTimeoutBeforeReady' - fetchMock.get(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); + fetchMock.get(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); mockSegmentChanges(fetchMock, new RegExp(config.urls.sdk + '/segmentChanges/*'), ['some_key']); fetchMock.postOnce(config.urls.events + '/testImpressions/bulk', 200); fetchMock.postOnce(config.urls.events + '/testImpressions/count', 200); @@ -378,8 +378,8 @@ export default function readyPromiseAssertions(key, fetchMock, assert) { }; // /splitChanges takes longer than 'requestTimeoutBeforeReady' only for the first attempt - fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); - fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); mockSegmentChanges(fetchMock, new RegExp(config.urls.sdk + '/segmentChanges/*'), ['some_key']); fetchMock.postOnce(config.urls.events + '/testImpressions/bulk', 200); fetchMock.postOnce(config.urls.events + '/testImpressions/count', 200); @@ -467,8 +467,8 @@ export default function readyPromiseAssertions(key, fetchMock, assert) { }; // /splitChanges takes longer than 'requestTimeoutBeforeReady' only for the first attempt - fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); - fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) - 20 }); mockSegmentChanges(fetchMock, new RegExp(config.urls.sdk + '/segmentChanges/*'), ['some_key']); fetchMock.postOnce(config.urls.events + '/testImpressions/bulk', 200); fetchMock.postOnce(config.urls.events + '/testImpressions/count', 200); @@ -538,7 +538,7 @@ export default function readyPromiseAssertions(key, fetchMock, assert) { }; // /splitChanges takes longer than 'requestTimeoutBeforeReady' - fetchMock.get(config.urls.sdk + '/splitChanges?s=1.1&since=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); + fetchMock.get(config.urls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', splitChangesMock1, { delay: fromSecondsToMillis(config.startup.requestTimeoutBeforeReady) + 20 }); mockSegmentChanges(fetchMock, new RegExp(config.urls.sdk + '/segmentChanges/*'), ['some_key']); fetchMock.postOnce(config.urls.events + '/testImpressions/bulk', 200); fetchMock.postOnce(config.urls.events + '/testImpressions/count', 200); diff --git a/src/__tests__/nodeSuites/telemetry.spec.js b/src/__tests__/nodeSuites/telemetry.spec.js index 751e762e2..85f7cdf67 100644 --- a/src/__tests__/nodeSuites/telemetry.spec.js +++ b/src/__tests__/nodeSuites/telemetry.spec.js @@ -22,8 +22,8 @@ const config = { export default async function telemetryNodejsSuite(key, fetchMock, assert) { - fetchMock.getOnce(url(config, '/splitChanges?s=1.1&since=-1'), 500); // record http exception - fetchMock.getOnce(url(config, '/splitChanges?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(url(config, '/splitChanges?s=1.3&since=-1&rbSince=-1'), 500); // record http exception + fetchMock.getOnce(url(config, '/splitChanges?s=1.3&since=-1&rbSince=-1'), { status: 200, body: splitChangesMock1 }); mockSegmentChanges(fetchMock, new RegExp(config.urls.sdk + '/segmentChanges/*'), ['some_key']); fetchMock.postOnce(url(config, '/testImpressions/bulk'), 200); @@ -55,7 +55,7 @@ export default async function telemetryNodejsSuite(key, fetchMock, assert) { // Validate http and method latencies const getLatencyCount = buckets => buckets ? buckets.reduce((accum, entry) => accum + entry, 0) : 0; assert.equal(getLatencyCount(data.hL.sp), 2, 'Two latency metrics for splitChanges GET request'); - assert.equal(getLatencyCount(data.hL.se), 6, 'Six latency metrics for segmentChanges GET request'); + assert.equal(getLatencyCount(data.hL.se), 8, 'Six latency metrics for segmentChanges GET request'); assert.equal(getLatencyCount(data.hL.te), 1, 'One latency metric for telemetry config POST request'); assert.equal(getLatencyCount(data.mL.t), 2, 'Two latency metrics for getTreatment (one not ready usage'); assert.equal(getLatencyCount(data.mL.ts), 1, 'One latency metric for getTreatments'); @@ -66,7 +66,7 @@ export default async function telemetryNodejsSuite(key, fetchMock, assert) { // @TODO check if iDe value is correct assert.deepEqual(data, { - mE: {}, hE: { sp: { 500: 1 } }, tR: 0, aR: 0, iQ: 4, iDe: 1, iDr: 0, spC: 33, seC: 3, skC: 3, eQ: 1, eD: 0, sE: [], t: [], ufs: {} + mE: {}, hE: { sp: { 500: 1 } }, tR: 0, aR: 0, iQ: 4, iDe: 1, iDr: 0, spC: 36, seC: 4, skC: 4, eQ: 1, eD: 0, sE: [], t: [], ufs: {} }, 'metrics/usage JSON payload should be the expected'); finish.next(); @@ -85,7 +85,7 @@ export default async function telemetryNodejsSuite(key, 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: 3, skC: 3, eQ: 1, eD: 0, sE: [], t: [], ufs: {} + tR: 0, aR: 0, iQ: 4, iDe: 1, iDr: 0, spC: 36, seC: 4, skC: 4, eQ: 1, eD: 0, sE: [], t: [], ufs: {} }, '2nd metrics/usage JSON payload should be the expected'); return 200; }); diff --git a/src/__tests__/offline/browser.spec.js b/src/__tests__/offline/browser.spec.js index 4411914cf..8507e080b 100644 --- a/src/__tests__/offline/browser.spec.js +++ b/src/__tests__/offline/browser.spec.js @@ -168,10 +168,10 @@ tape('Browser offline mode', function (assert) { // Manager tests const expectedSplitView1 = { - name: 'testing_split', trafficType: 'localhost', killed: false, changeNumber: 0, treatments: ['on'], configs: {}, defaultTreatment: 'control', sets: [], impressionsDisabled: false + name: 'testing_split', trafficType: 'localhost', killed: false, changeNumber: 0, treatments: ['on'], configs: {}, defaultTreatment: 'control', sets: [], impressionsDisabled: false, prerequisites: [] }; const expectedSplitView2 = { - name: 'testing_split_with_config', trafficType: 'localhost', killed: false, changeNumber: 0, treatments: ['off'], configs: { off: '{ "color": "blue" }' }, defaultTreatment: 'control', sets: [], impressionsDisabled: false + name: 'testing_split_with_config', trafficType: 'localhost', killed: false, changeNumber: 0, treatments: ['off'], configs: { off: '{ "color": "blue" }' }, defaultTreatment: 'control', sets: [], impressionsDisabled: false, prerequisites: [] }; assert.deepEqual(manager.names(), ['testing_split', 'testing_split_with_config']); assert.deepEqual(manager.split('testing_split'), expectedSplitView1); @@ -282,7 +282,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: {}, defaultTreatment: 'control', sets: [], impressionsDisabled: false + name: 'testing_split_with_config', trafficType: 'localhost', killed: false, changeNumber: 0, treatments: ['nope'], configs: {}, defaultTreatment: 'control', sets: [], 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__/offline/node.spec.js b/src/__tests__/offline/node.spec.js index cbc6e40d3..8c9698615 100644 --- a/src/__tests__/offline/node.spec.js +++ b/src/__tests__/offline/node.spec.js @@ -250,17 +250,17 @@ function ManagerDotSplitTests(assert) { const expectedView1 = { name: 'testing_split', changeNumber: 0, killed: false, trafficType: 'localhost', treatments: ['on'], configs: {}, defaultTreatment: 'control', - sets: [], impressionsDisabled: false + sets: [], impressionsDisabled: false, prerequisites: [] }; const expectedView2 = { name: 'testing_split2', changeNumber: 0, killed: false, trafficType: 'localhost', treatments: ['off'], configs: {}, defaultTreatment: 'control', - sets: [], impressionsDisabled: false + sets: [], impressionsDisabled: false, prerequisites: [] }; const expectedView3 = { name: 'testing_split3', changeNumber: 0, killed: false, trafficType: 'localhost', treatments: ['custom_treatment'], configs: {}, defaultTreatment: 'control', - sets: [], impressionsDisabled: false + sets: [], impressionsDisabled: false, prerequisites: [] }; assert.deepEqual(manager.split('testing_split'), expectedView1); @@ -295,7 +295,8 @@ function ManagerDotYamlTests(mockFileName, assert) { configs: {}, sets: [], defaultTreatment: 'control', - impressionsDisabled: false + impressionsDisabled: false, + prerequisites: [] }; const expectedView2 = { name: 'testing_split_only_wl', @@ -306,7 +307,8 @@ function ManagerDotYamlTests(mockFileName, assert) { configs: {}, sets: [], defaultTreatment: 'control', - impressionsDisabled: false + impressionsDisabled: false, + prerequisites: [] }; const expectedView3 = { name: 'testing_split_with_wl', @@ -320,7 +322,8 @@ function ManagerDotYamlTests(mockFileName, assert) { }, sets: [], defaultTreatment: 'control', - impressionsDisabled: false + impressionsDisabled: false, + prerequisites: [] }; const expectedView4 = { name: 'testing_split_off_with_config', @@ -333,7 +336,8 @@ function ManagerDotYamlTests(mockFileName, assert) { }, sets: [], defaultTreatment: 'control', - impressionsDisabled: false + impressionsDisabled: false, + prerequisites: [] }; assert.deepEqual(manager.split('testing_split_on'), expectedView1); @@ -414,15 +418,15 @@ function MultipleInstancesTests(assert) { const expectedView1 = { name: 'testing_split', changeNumber: 0, killed: false, trafficType: 'localhost', - treatments: ['on'], configs: {}, sets: [], impressionsDisabled: false + treatments: ['on'], configs: {}, sets: [], impressionsDisabled: false, prerequisites: [] }; const expectedView2 = { name: 'testing_split2', changeNumber: 0, killed: false, trafficType: 'localhost', - treatments: ['off'], configs: {}, sets: [], impressionsDisabled: false + treatments: ['off'], configs: {}, sets: [], impressionsDisabled: false, prerequisites: [] }; const expectedView3 = { name: 'testing_split3', changeNumber: 0, killed: false, trafficType: 'localhost', - treatments: ['custom_treatment'], configs: {}, sets: [], impressionsDisabled: false + treatments: ['custom_treatment'], configs: {}, sets: [], impressionsDisabled: false, prerequisites: [] }; assert.deepEqual(manager.split('testing_split'), expectedView1); diff --git a/src/__tests__/online/browser.spec.js b/src/__tests__/online/browser.spec.js index 05fd60de2..3f2051374 100644 --- a/src/__tests__/online/browser.spec.js +++ b/src/__tests__/online/browser.spec.js @@ -87,8 +87,8 @@ tape('## E2E CI Tests ##', function (assert) { //If we change the mocks, we need to clear localstorage. Cleaning up after testing ensures "fresh data". localStorage.clear(); - fetchMock.get(url(settings, '/splitChanges?s=1.2&since=-1'), { status: 200, body: splitChangesMock1 }); - fetchMock.get(url(settings, '/splitChanges?s=1.2&since=1457552620999'), { status: 200, body: splitChangesMock2 }); + fetchMock.get(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.get(url(settings, '/splitChanges?s=1.3&since=1457552620999&rbSince=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/__tests__/online/node.spec.js b/src/__tests__/online/node.spec.js index 7b697f0a7..c0f64b939 100644 --- a/src/__tests__/online/node.spec.js +++ b/src/__tests__/online/node.spec.js @@ -23,6 +23,7 @@ import readyPromiseSuite from '../nodeSuites/ready-promise.spec'; import { fetchSpecificSplits, fetchSpecificSplitsForFlagSets } from '../nodeSuites/fetch-specific-splits.spec'; import flagSets from '../nodeSuites/flag-sets.spec'; import lazyInitSuite from '../nodeSuites/lazy-init.spec'; +import { proxyFallbackSuite } from '../nodeSuites/proxy-fallback.spec'; const config = { core: { @@ -39,17 +40,16 @@ const config = { const settings = settingsFactory(config); const key = 'facundo@split.io'; -fetchMock.get(url(settings, '/splitChanges?s=1.1&since=-1'), { status: 200, body: splitChangesMock1 }); -fetchMock.get(url(settings, '/splitChanges?s=1.1&since=1457552620999'), { status: 200, body: splitChangesMock2 }); -fetchMock.get(new RegExp(`${url(settings, '/segmentChanges')}/*`), { - status: 200, body: { - 'name': 'segment', - 'added': [], - 'removed': [], - 'since': 1, - 'till': 1 - } -}); +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 }); + +const emptySegmentResponse = { added: [], removed: [], since: 1, till: 1 }; +fetchMock.get(new RegExp(`${url(settings, '/segmentChanges/employees')}*`), { status: 200, body: emptySegmentResponse }); +fetchMock.get(new RegExp(`${url(settings, '/segmentChanges/splitters')}*`), { status: 200, body: emptySegmentResponse }); +fetchMock.get(new RegExp(`${url(settings, '/segmentChanges/developers')}*`), { status: 200, body: emptySegmentResponse }); +fetchMock.get(url(settings, '/segmentChanges/segment_excluded_by_rbs?since=-1'), { status: 200, body: { added: ['emi@split.io'], removed: [], since: -1, till: 1 } }); +fetchMock.get(url(settings, '/segmentChanges/segment_excluded_by_rbs?since=1'), { status: 200, body: { added: [], removed: [], since: 1, till: 1 } }); + fetchMock.post(url(settings, '/testImpressions/bulk'), 200); fetchMock.post(url(settings, '/testImpressions/count'), 200); fetchMock.post(url(settings, '/v1/metrics/config'), 200); @@ -97,5 +97,8 @@ tape('## Node.js - E2E CI Tests ##', async function (assert) { assert.test('E2E / SplitFactory with lazy init', lazyInitSuite.bind(null, settings, fetchMock)); + // @TODO remove when dropping support for Split Proxy v5.10.0 or below + assert.test('E2E / Proxy fallback', proxyFallbackSuite.bind(null, fetchMock)); + assert.end(); }); diff --git a/src/__tests__/push/browser.spec.js b/src/__tests__/push/browser.spec.js index 0d8dbeee1..99a38fbad 100644 --- a/src/__tests__/push/browser.spec.js +++ b/src/__tests__/push/browser.spec.js @@ -14,13 +14,13 @@ Math.random = () => 0.5; // SDKs without telemetry tape('## Browser JS - E2E CI Tests for PUSH ##', function (assert) { - // Non-recoverable issues on inizialization + // Non-recoverable issues on initialization assert.test('E2E / PUSH initialization: auth with push disabled', testAuthWithPushDisabled.bind(null, fetchMock)); assert.test('E2E / PUSH initialization: auth with 401', testAuthWith401.bind(null, fetchMock)); assert.test('E2E / PUSH initialization: fallback to polling if EventSource is not available', testNoEventSource.bind(null, fetchMock)); assert.test('E2E / PUSH initialization: sse with non-recoverable Ably error', testSSEWithNonRetryableError.bind(null, fetchMock)); - // Recoverable issues on inizialization + // Recoverable issues on initialization assert.test('E2E / PUSH initialization: auth failures and then success', testPushRetriesDueToAuthErrors.bind(null, fetchMock)); assert.test('E2E / PUSH initialization: SSE connection failures and then success', testPushRetriesDueToSseErrors.bind(null, fetchMock)); diff --git a/src/__tests__/push/node.spec.js b/src/__tests__/push/node.spec.js index 3922685ea..6a2957d58 100644 --- a/src/__tests__/push/node.spec.js +++ b/src/__tests__/push/node.spec.js @@ -15,14 +15,14 @@ fetchMock.post('https://telemetry.split.io/api/v1/metrics/usage', 200); tape('## Node.js - E2E CI Tests for PUSH ##', async function (assert) { - // Non-recoverable issues on inizialization + // Non-recoverable issues on initialization assert.test('E2E / PUSH initialization: auth with push disabled', testAuthWithPushDisabled.bind(null, fetchMock)); assert.test('E2E / PUSH initialization: auth with 401', testAuthWith401.bind(null, fetchMock)); assert.test('E2E / PUSH initialization: auth with 400', testAuthWith400.bind(null, fetchMock)); assert.test('E2E / PUSH initialization: fallback to polling if EventSource is not available', testNoEventSource.bind(null, fetchMock)); assert.test('E2E / PUSH initialization: sse with non-recoverable Ably error', testSSEWithNonRetryableError.bind(null, fetchMock)); - // Recoverable issues on inizialization + // Recoverable issues on initialization assert.test('E2E / PUSH initialization: auth failures and then success', testPushRetriesDueToAuthErrors.bind(null, fetchMock)); assert.test('E2E / PUSH initialization: SSE connection failures and then success', testPushRetriesDueToSseErrors.bind(null, fetchMock)); diff --git a/src/settings/defaults/version.js b/src/settings/defaults/version.js index 8132c030a..7c83e39ba 100644 --- a/src/settings/defaults/version.js +++ b/src/settings/defaults/version.js @@ -1 +1 @@ -export const packageVersion = '11.3.0'; +export const packageVersion = '11.4.0'; diff --git a/src/settings/node.js b/src/settings/node.js index 5fe7d5535..bf559ba7d 100644 --- a/src/settings/node.js +++ b/src/settings/node.js @@ -5,15 +5,11 @@ import { defaults } from './defaults/node'; import { validateStorage } from './storage/node'; import { validateRuntime } from './runtime/node'; -const FLAG_SPEC_VERSION = '1.1'; - const params = { defaults, runtime: validateRuntime, storage: validateStorage, logger: validateLogger, - flagSpec: () => FLAG_SPEC_VERSION - // In Node.js the SDK ignores `config.integrations`, so a validator for integrations is not required }; export function settingsFactory(config) { diff --git a/ts-tests/index.ts b/ts-tests/index.ts index 588f5ec15..52ecade7e 100644 --- a/ts-tests/index.ts +++ b/ts-tests/index.ts @@ -143,7 +143,8 @@ splitView = { }, sets: ['set_a', 'set_b'], defaultTreatment: 'off', - impressionsDisabled: false + impressionsDisabled: false, + prerequisites: [{ flagName: 'flag1', treatments: ['on'] }] }; splitViews = [splitView];