Skip to content

[Prerequisites] Add tests #869

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: rb_segments_baseline
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci-cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ jobs:
run: BUILD_BRANCH=$(echo "${GITHUB_REF#refs/heads/}") npm run build

- name: Store assets
if: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/development' || github.ref == 'refs/heads/master') }}
if: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/prerequisites' || github.ref == 'refs/heads/master') }}
uses: actions/upload-artifact@v4
with:
name: assets
Expand All @@ -68,7 +68,7 @@ jobs:
name: Upload assets
runs-on: ubuntu-latest
needs: build
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/development' }}
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/prerequisites' }}
strategy:
matrix:
environment:
Expand Down
5 changes: 5 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
11.3.0 (April XX, 2025)
- Added support for targeting rules based on rule-based segments.
- Added support for feature flag prerequisites.
- Updated @splitsoftware/splitio-commons package to version 2.3.0.

11.2.0 (March 28, 2025)
- Added a new optional argument to the client `getTreatment` methods to allow passing additional evaluation options, such as a map of properties to append to the generated impressions sent to Split backend. Read more in our docs.
- Added two new configuration options for the SDK's `LOCALSTORAGE` storage type to control the behavior of the persisted rollout plan cache in the browser:
Expand Down
18 changes: 9 additions & 9 deletions package-lock.json

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

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@splitsoftware/splitio",
"version": "11.2.0",
"version": "11.2.1-rc.0",
"description": "Split SDK",
"files": [
"README.md",
Expand Down Expand Up @@ -38,7 +38,7 @@
"node": ">=14.0.0"
},
"dependencies": {
"@splitsoftware/splitio-commons": "2.2.1-rc.1",
"@splitsoftware/splitio-commons": "2.2.1-rc.0",
"bloom-filters": "^3.0.4",
"ioredis": "^4.28.0",
"js-yaml": "^3.13.1",
Expand Down
8 changes: 6 additions & 2 deletions src/__tests__/browserSuites/evaluations.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ export default function (config, fetchMock, assert) {

};

const evaluationsWithRuleBasedSegments = async (splitio) => {
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: {} } });
Expand All @@ -372,24 +372,28 @@ export default function (config, fetchMock, assert) {
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('[email protected]');
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('[email protected]');
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();
};

Expand All @@ -408,7 +412,7 @@ export default function (config, fetchMock, assert) {
getTreatmentsWithConfigTests(client);
getTreatmentsWithInMemoryAttributes(client);

evaluationsWithRuleBasedSegments(splitio).then(() => {
evaluationsWithRuleBasedSegmentsAndPrerequisites(splitio).then(() => {
clientTABucket1.destroy();
client.destroy();
tested++;
Expand Down
3 changes: 2 additions & 1 deletion src/__tests__/browserSuites/manager.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ export default async function (settings, fetchMock, assert) {
'configs': mockSplits.ff.d[index].configurations || {},
'sets': mockSplits.ff.d[index].sets || [],
'defaultTreatment': mockSplits.ff.d[index].defaultTreatment,
'impressionsDisabled': false
'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.');
Expand Down
6 changes: 3 additions & 3 deletions src/__tests__/browserSuites/ready-from-cache.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -818,7 +818,7 @@ export default function (fetchMock, assert) {
});

await client.ready();
t.equal(manager.names().sort().length, 35, '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');
Expand All @@ -835,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, 35, '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();
Expand All @@ -856,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, 35, '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();
Expand Down
4 changes: 2 additions & 2 deletions src/__tests__/browserSuites/telemetry.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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: 35, 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();
Expand All @@ -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: 35, 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;
});
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/consumer/node_redis.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
55 changes: 53 additions & 2 deletions src/__tests__/mocks/splitchanges.since.-1.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,13 @@
"status": "ACTIVE",
"trafficTypeName": "user",
"excluded": {
"keys": [ "[email protected]", "[email protected]" ],
"segments": [ "segment_excluded_by_rbs" ]
"keys": [
"[email protected]",
"[email protected]"
],
"segments": [
"segment_excluded_by_rbs"
]
},
"conditions": [
{
Expand Down Expand Up @@ -1730,6 +1735,52 @@
"configurations": {},
"sets": [],
"impressionsDisabled": false
},
{
"orgId": null,
"environment": null,
"trafficTypeId": null,
"trafficTypeName": null,
"name": "always_on_if_prerequisite",
"seed": -790401604,
"status": "ACTIVE",
"killed": false,
"defaultTreatment": "off",
"prerequisites": [
{
"n": "rbs_test_flag",
"ts": [
"v1"
]
}
],
"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
}
]
}
]
}
],
"s": -1,
Expand Down
4 changes: 4 additions & 0 deletions src/__tests__/nodeSuites/evaluations.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,10 @@ export default async function (config, key, assert) {
assert.equal(client.getTreatment('[email protected]', 'rbs_test_flag_negated'), 'v1', 'excluded key');
assert.equal(client.getTreatment('[email protected]', '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('[email protected]', 'always_on_if_prerequisite'), 'on', 'prerequisite satisfied (key satisfies the rbs condition)');
assert.equal(client.getTreatment('[email protected]', 'always_on_if_prerequisite'), 'off', 'prerequisite not satisfied (key in excluded segment)');
};

const getTreatmentsTests = (client, sdkInstance) => {
Expand Down
3 changes: 2 additions & 1 deletion src/__tests__/nodeSuites/manager.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ export default async function (settings, fetchMock, assert) {
'configs': mockSplits.ff.d[index].configurations || {},
'sets': mockSplits.ff.d[index].sets || [],
'defaultTreatment': mockSplits.ff.d[index].defaultTreatment,
'impressionsDisabled': false
'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.');
Expand Down
4 changes: 2 additions & 2 deletions src/__tests__/nodeSuites/telemetry.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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: 35, seC: 4, skC: 4, 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();
Expand All @@ -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: 35, seC: 4, skC: 4, 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;
});
Expand Down
6 changes: 3 additions & 3 deletions src/__tests__/offline/browser.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
Loading