Skip to content

Commit

Permalink
[SDKS-7564] Flag sets integration tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Emmanuel Zamora committed Oct 9, 2023
1 parent a2fa07a commit c510e36
Show file tree
Hide file tree
Showing 23 changed files with 1,585 additions and 147 deletions.
26 changes: 13 additions & 13 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
"node": ">=6"
},
"dependencies": {
"@splitsoftware/splitio-commons": "1.9.2-rc.0",
"@splitsoftware/splitio-commons": "1.9.2-rc.1",
"@types/google.analytics": "0.0.40",
"@types/ioredis": "^4.28.0",
"bloom-filters": "^3.0.0",
Expand Down
40 changes: 39 additions & 1 deletion src/__tests__/browserSuites/fetch-specific-splits.spec.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import sinon from 'sinon';
import { SplitFactory } from '../../';
import { splitFilters, queryStrings, groupedFilters } from '../mocks/fetchSpecificSplits';

Expand All @@ -14,7 +15,7 @@ const baseConfig = {

export default function fetchSpecificSplits(fetchMock, assert) {

assert.plan(splitFilters.length);
assert.plan(splitFilters.length+1);

for (let i = 0; i < splitFilters.length; i++) {
const urls = { sdk: 'https://sdkurl' + i };
Expand Down Expand Up @@ -45,4 +46,41 @@ export default function fetchSpecificSplits(fetchMock, assert) {
}

}

// Flag sets
assert.test(async (t) => {

const splitFilters = [{ type: 'bySet', values: ['set_x ', 'set_x', 'set_3', 'set_2', 'set_3', 'set_ww', 'invalid+', '_invalid', '4_valid'] }];
const baseUrls = { sdk: 'https://sdk.baseurl' };

const config = {
...baseConfig,
urls: baseUrls,
debug: 'WARN',
sync: {
splitFilters
}
};

const logSpy = sinon.spy(console, 'log');

fetchMock.get(baseUrls.sdk + '/mySegments/nicolas%40split.io', { status: 200, body: { 'mySegments': [] } });
fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=-1&sets=4_valid,set_2,set_3,set_ww,set_x', function () {
t.pass('flag set query correctly formed');
return { status: 200, body: { splits: [], since: 1457552620999, till: 1457552620999 } };
});

const factory = SplitFactory(config);

const client = factory.client();
client.ready().then(() => {
t.true(logSpy.calledWithExactly('[WARN] splitio => settings: bySet filter value "set_x " has extra whitespace, trimming.'));
t.true(logSpy.calledWithExactly('[WARN] splitio => settings: you passed invalid+, flag set must adhere to the regular expressions /^[a-z0-9][_a-z0-9]{0,49}$/. This means a flag set must start with a letter or number, be in lowercase, alphanumeric and have a max length of 50 characters. invalid+ was discarded.'));
t.true(logSpy.calledWithExactly('[WARN] splitio => settings: you passed _invalid, flag set must adhere to the regular expressions /^[a-z0-9][_a-z0-9]{0,49}$/. This means a flag set must start with a letter or number, be in lowercase, alphanumeric and have a max length of 50 characters. _invalid was discarded.'));
logSpy.restore();
factory.client().destroy().then(() => {
t.end();
});
});
}, 'FlagSets config');
}
204 changes: 204 additions & 0 deletions src/__tests__/browserSuites/flag-sets.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
import { SplitFactory } from '../..';

import splitChange2 from '../mocks/splitChanges.since.-1.till.1602796638344.json';
import splitChange1 from '../mocks/splitchanges.since.1602796638344.till.1602797638344.json';
import splitChange0 from '../mocks/splitchanges.since.1602797638344.till.1602798638344.json';

const baseUrls = { sdk: 'https://sdk.baseurl' };

const baseConfig = {
core: {
authorizationKey: '<fake-token>',
key: 'nicolas@split.io'
},
urls: baseUrls,
scheduler: { featuresRefreshRate: 0.01 },
streamingEnabled: false
};

export default function flagSets(fetchMock, t) {
fetchMock.get(baseUrls.sdk + '/mySegments/nicolas%40split.io', { status: 200, body: { 'mySegments': [] } });

t.test(async (assert) => {
let factory;
let manager;

// Receive split change with 1 split belonging to set_1 & set_2 and one belonging to set_3
fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=-1&sets=set_1,set_2', function () {
return { status: 200, body: splitChange2};
});

// Receive split change with 1 split belonging to set_1 only
fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1602796638344&sets=set_1,set_2', function () {
// stored feature flags before update
const storedFlags = manager.splits();
assert.true(storedFlags.length === 1, 'only one feature flag should be added');
assert.true(storedFlags[0].name === 'workm');
assert.deepEqual(storedFlags[0].sets, ['set_1','set_2']);

// send split change
return { status: 200, body: splitChange1};
});

// Receive split change with 1 split belonging to set_3 only
fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1602797638344&sets=set_1,set_2', function () {
// stored feature flags before update
const storedFlags = manager.splits();
assert.true(storedFlags.length === 1);
assert.true(storedFlags[0].name === 'workm');
assert.deepEqual(storedFlags[0].sets, ['set_1'], 'the feature flag should be updated');

// send split change
return { status: 200, body: splitChange0};
});

fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1602798638344&sets=set_1,set_2', async function () {
// stored feature flags before update
const storedFlags = manager.splits();
assert.true(storedFlags.length === 0, 'the feature flag should be removed');
await factory.client().destroy();
assert.end();

return { status: 200, body: {} };
});

// Initialize a factory with polling and sets set_1 & set_2 configured.
const splitFilters = [{ type: 'bySet', values: ['set_1','set_2'] }];
factory = SplitFactory({ ...baseConfig, sync: { splitFilters }});
await factory.client().ready();
manager = factory.manager();

}, 'Polling - SDK with sets configured updates flags according to sets');

t.test(async (assert) => {
let factory;
let manager;

// Receive split change with 1 split belonging to set_1 & set_2 and one belonging to set_3
fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=-1', function () {
return { status: 200, body: splitChange2};
});

// Receive split change with 1 split belonging to set_1 only
fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1602796638344', function () {
// stored feature flags before update
const storedFlags = manager.splits();
assert.true(storedFlags.length === 2, 'every feature flag should be added');
assert.true(storedFlags[0].name === 'workm');
assert.true(storedFlags[1].name === 'workm_set_3');
assert.deepEqual(storedFlags[0].sets, ['set_1','set_2']);
assert.deepEqual(storedFlags[1].sets, ['set_3']);

// send split change
return { status: 200, body: splitChange1};
});

// Receive split change with 1 split belonging to set_3 only
fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1602797638344', function () {
// stored feature flags before update
const storedFlags = manager.splits();
assert.true(storedFlags.length === 2);
assert.true(storedFlags[0].name === 'workm');
assert.true(storedFlags[1].name === 'workm_set_3');
assert.deepEqual(storedFlags[0].sets, ['set_1'], 'the feature flag should be updated');
assert.deepEqual(storedFlags[1].sets, ['set_3'], 'the feature flag should remain as it was');

// send split change
return { status: 200, body: splitChange0};
});

fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1602798638344', async function () {
// stored feature flags before update
const storedFlags = manager.splits();
assert.true(storedFlags.length === 2);
assert.true(storedFlags[0].name === 'workm');
assert.true(storedFlags[1].name === 'workm_set_3');
assert.deepEqual(storedFlags[0].sets, ['set_3'], 'the feature flag should be updated');
assert.deepEqual(storedFlags[1].sets, ['set_3'], 'the feature flag should remain as it was');
await factory.client().destroy();
assert.end();
return { status: 200, body: {} };
});

// Initialize a factory with polling and no sets configured.
factory = SplitFactory(baseConfig);
await factory.client().ready();
manager = factory.manager();

}, 'Poling - SDK with no sets configured does not take sets into account when updating flags');

// EVALUATION

t.test(async (assert) => {
fetchMock.reset();
assert.plan(8);

let factory, client = [];

fetchMock.get(baseUrls.sdk + '/mySegments/nicolas%40split.io', { status: 200, body: { 'mySegments': [] } });
// Receive split change with 1 split belonging to set_1 & set_2 and one belonging to set_3
fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=-1&sets=set_1', function () {
return { status: 200, body: splitChange2};
});

fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1602796638344&sets=set_1', async function () {
// stored feature flags before update
assert.deepEqual(client.getTreatmentsByFlagSet('set_1'), {workm: 'on'}, 'only the flag in set_1 can be evaluated');
assert.deepEqual(client.getTreatmentsByFlagSet('set_2'), {}, 'only the flag in set_1 can be evaluated');
assert.deepEqual(client.getTreatmentsByFlagSet('set_3'), {}, 'only the flag in set_1 can be evaluated');
assert.deepEqual(client.getTreatmentsWithConfigByFlagSet('set_1'), { workm: { treatment: 'on', config: null } }, 'only the flag in set_1 can be evaluated');
assert.deepEqual(client.getTreatmentsWithConfigByFlagSet('set_2'), {}, 'only the flag in set_1 can be evaluated');
assert.deepEqual(client.getTreatmentsWithConfigByFlagSet('set_3'), {}, 'only the flag in set_1 can be evaluated');
assert.deepEqual(client.getTreatmentsByFlagSets(['set_1','set_2','set_3']), {workm: 'on'}, 'only the flag in set_1 can be evaluated');
assert.deepEqual(client.getTreatmentsWithConfigByFlagSets(['set_1','set_2','set_3']), { workm: { treatment: 'on', config: null } }, 'only the flag in set_1 can be evaluated');
await client.destroy();
assert.end();

// send split change
return { status: 200, body: splitChange1};
});

// Initialize a factory with set_1 configured.
const splitFilters = [{ type: 'bySet', values: ['set_1'] }];
factory = SplitFactory({ ...baseConfig, sync: { splitFilters }});
client = factory.client();
await client.ready();

}, 'SDK with sets configured can only evaluate configured sets');

t.test(async (assert) => {
fetchMock.reset();
assert.plan(8);

let factory, client = [];

fetchMock.get(baseUrls.sdk + '/mySegments/nicolas%40split.io', { status: 200, body: { 'mySegments': [] } });
// Receive split change with 1 split belonging to set_1 & set_2 and one belonging to set_3
fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=-1', function () {
return { status: 200, body: splitChange2};
});

fetchMock.getOnce(baseUrls.sdk + '/splitChanges?since=1602796638344', async function () {
// stored feature flags before update
assert.deepEqual(client.getTreatmentsByFlagSet('set_1'), {workm: 'on'}, 'all flags can be evaluated');
assert.deepEqual(client.getTreatmentsByFlagSet('set_2'), {workm: 'on'}, 'all flags can be evaluated');
assert.deepEqual(client.getTreatmentsByFlagSet('set_3'), { workm_set_3: 'on' }, 'all flags can be evaluated');
assert.deepEqual(client.getTreatmentsWithConfigByFlagSet('set_1'), { workm: { treatment: 'on', config: null } }, 'all flags can be evaluated');
assert.deepEqual(client.getTreatmentsWithConfigByFlagSet('set_2'), { workm: { treatment: 'on', config: null } }, 'all flags can be evaluated');
assert.deepEqual(client.getTreatmentsWithConfigByFlagSet('set_3'), { workm_set_3: { treatment: 'on', config: null } }, 'all flags can be evaluated');
assert.deepEqual(client.getTreatmentsByFlagSets(['set_1','set_2','set_3']), { workm: 'on', workm_set_3: 'on' }, 'all flags can be evaluated');
assert.deepEqual(client.getTreatmentsWithConfigByFlagSets(['set_1','set_2','set_3']), { workm: { treatment: 'on', config: null }, workm_set_3: { treatment: 'on', config: null } }, 'all flags can be evaluated');
await client.destroy();
assert.end();

// send split change
return { status: 200, body: splitChange1};
});

factory = SplitFactory(baseConfig);
client = factory.client();
await client.ready();

}, 'SDK with no sets configured can evaluate any set');

}
Loading

0 comments on commit c510e36

Please sign in to comment.