From 451255ac91681a1603af12d1b9906b9949b355e1 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Fri, 12 Apr 2024 14:25:06 -0300 Subject: [PATCH 1/8] Update JS-commons and add E2E tests for Semver matchers --- package-lock.json | 14 +- package.json | 2 +- .../browserSuites/evaluations-semver.spec.js | 108 ++++ .../mocks/splitchanges.since.-1.semver.json | 514 ++++++++++++++++++ .../nodeSuites/evaluations-semver.spec.js | 102 ++++ src/__tests__/online/browser.spec.js | 2 + src/__tests__/online/node.spec.js | 2 + 7 files changed, 736 insertions(+), 8 deletions(-) create mode 100644 src/__tests__/browserSuites/evaluations-semver.spec.js create mode 100644 src/__tests__/mocks/splitchanges.since.-1.semver.json create mode 100644 src/__tests__/nodeSuites/evaluations-semver.spec.js diff --git a/package-lock.json b/package-lock.json index aac4cec71..dffb2c127 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "10.25.2", "license": "Apache-2.0", "dependencies": { - "@splitsoftware/splitio-commons": "1.13.1", + "@splitsoftware/splitio-commons": "1.13.2-rc.3", "@types/google.analytics": "0.0.40", "@types/ioredis": "^4.28.0", "bloom-filters": "^3.0.0", @@ -875,9 +875,9 @@ "dev": true }, "node_modules/@splitsoftware/splitio-commons": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.13.1.tgz", - "integrity": "sha512-xGu94sLx+tJb6PeM26vH8/LEElsaVbh2BjoLvL5twR4gKsVezie5ZtHhejWT1+iCVCtJuhjZxKwOm4HGYoVIHQ==", + "version": "1.13.2-rc.3", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.13.2-rc.3.tgz", + "integrity": "sha512-2AeRqIYqh9N/2prXv4TOJigJ4+1Jk5yi1Dqxo9yLbgF3ZeUpj+fdMfhoCHAL8mcqym9wjjLR5GR+53feYLtqjg==", "dependencies": { "tslib": "^2.3.1" }, @@ -8447,9 +8447,9 @@ "dev": true }, "@splitsoftware/splitio-commons": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.13.1.tgz", - "integrity": "sha512-xGu94sLx+tJb6PeM26vH8/LEElsaVbh2BjoLvL5twR4gKsVezie5ZtHhejWT1+iCVCtJuhjZxKwOm4HGYoVIHQ==", + "version": "1.13.2-rc.3", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.13.2-rc.3.tgz", + "integrity": "sha512-2AeRqIYqh9N/2prXv4TOJigJ4+1Jk5yi1Dqxo9yLbgF3ZeUpj+fdMfhoCHAL8mcqym9wjjLR5GR+53feYLtqjg==", "requires": { "tslib": "^2.3.1" } diff --git a/package.json b/package.json index 2dce53ec8..8fa0fd959 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "node": ">=6" }, "dependencies": { - "@splitsoftware/splitio-commons": "1.13.1", + "@splitsoftware/splitio-commons": "1.13.2-rc.3", "@types/google.analytics": "0.0.40", "@types/ioredis": "^4.28.0", "bloom-filters": "^3.0.0", diff --git a/src/__tests__/browserSuites/evaluations-semver.spec.js b/src/__tests__/browserSuites/evaluations-semver.spec.js new file mode 100644 index 000000000..1e2b9794f --- /dev/null +++ b/src/__tests__/browserSuites/evaluations-semver.spec.js @@ -0,0 +1,108 @@ +import sinon from 'sinon'; +import splitChangesMock1 from '../mocks/splitchanges.since.-1.semver.json'; + +import { SplitFactory } from '../../'; + +const listener = { + logImpression: sinon.stub() +}; + +const config = { + core: { + authorizationKey: '', + key: 'emi@split.io' + }, + urls: { + sdk: 'https://sdk.evaluation-semver/api', + events: 'https://events.evaluation-semver/api' + }, + sync: { + impressionsMode: 'DEBUG' + }, + impressionListener: listener, + streamingEnabled: false +}; + +export default async function (fetchMock, assert) { + + fetchMock.getOnce(config.urls.sdk + '/splitChanges?since=-1', { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(config.urls.sdk + '/splitChanges?since=1675259356568', { status: 200, body: { splits: [], since: 1675259356568, till: 1675259356568 } }); + fetchMock.getOnce(config.urls.sdk + '/mySegments/emi%40split.io', { status: 200, body: { mySegments: [] } }); + fetchMock.getOnce(config.urls.sdk + '/mySegments/2nd', { status: 200, body: { mySegments: [] } }); + + const splitio = SplitFactory(config); + const client = splitio.client(); + + await client.ready(); + + // EQUAL_TO_SEMVER matcher + assert.equal(client.getTreatment('semver_equalto', { 'version': '1.22.9' }), 'on', 'the rule will return `on` if attribute `version` is equal to `1.22.9`'); + assert.equal(client.getTreatment('semver_equalto', { 'version': '1.22.9+build' }), 'off', 'build metadata is not ignored'); + assert.equal(client.getTreatment('semver_equalto', { 'version': '1.22.9-rc.0' }), 'off', 'the rule will return `off` if attribute `version` is not equal to `1.22.9`'); + + // IN_LIST_SEMVER matcher + assert.equal(client.getTreatment('semver_inlist', { 'version': '2.1.0' }), 'on', 'the rule will return `on` if attribute `version` is in list (`1.22.9`, `2.1.0`)'); + assert.equal(client.getTreatment('semver_inlist', { 'version': '1.22.9' }), 'on', 'the rule will return `on` if attribute `version` is in list (`1.22.9`, `2.1.0`)'); + assert.equal(client.getTreatment('semver_inlist', { 'version': '1.22.9+build' }), 'off', 'build metadata is not ignored'); + assert.equal(client.getTreatment('semver_inlist', { 'version': '1.22.9-rc.0' }), 'off', 'the rule will return `off` if attribute `version` is not in list (`1.22.9`, `2.1.0`)'); + + // GREATER_THAN_OR_EQUAL_TO_SEMVER matcher + assert.equal(client.getTreatments(['semver_greater_or_equalto'], { 'version': '1.23.9' }).semver_greater_or_equalto, 'on', 'the rule will return `on` if attribute `version` is greater than or equal to `1.22.9`'); + assert.equal(client.getTreatments(['semver_greater_or_equalto'], { 'version': '1.22.9' }).semver_greater_or_equalto, 'on', 'the rule will return `on` if attribute `version` is greater than or equal to `1.22.9`'); + assert.equal(client.getTreatments(['semver_greater_or_equalto'], { 'version': '1.22.9+build' }).semver_greater_or_equalto, 'on', 'build metadata is ignored'); + assert.equal(client.getTreatments(['semver_greater_or_equalto'], { 'version': '1.22.9-rc.0' }).semver_greater_or_equalto, 'off', 'the rule will return `off` if attribute `version` is not greater than or equal to `1.22.9`'); + assert.equal(client.getTreatments(['semver_greater_or_equalto'], { 'version': '1.21.9' }).semver_greater_or_equalto, 'off', 'the rule will return `off` if attribute `version` is not greater than or equal to `1.22.9`'); + + // LESS_THAN_OR_EQUAL_TO_SEMVER matcher + assert.deepEqual(client.getTreatmentWithConfig('semver_less_or_equalto', { 'version': '1.22.11' }), { treatment: 'off', config: null }, 'the rule will return `off` if attribute `version` is not less than or equal to `1.22.9`'); + assert.deepEqual(client.getTreatmentWithConfig('semver_less_or_equalto', { 'version': '1.22.9' }), { treatment: 'on', config: null }, 'the rule will return `on` if attribute `version` is less than or equal to `1.22.9`'); + assert.deepEqual(client.getTreatmentWithConfig('semver_less_or_equalto', { 'version': '1.22.9+build' }), { treatment: 'on', config: null }, 'build metadata is ignored'); + assert.deepEqual(client.getTreatmentWithConfig('semver_less_or_equalto', { 'version': '1.22.9-rc.0' }), { treatment: 'on', config: null }, 'the rule will return `on` if attribute `version` is less than or equal to `1.22.9`'); + assert.deepEqual(client.getTreatmentWithConfig('semver_less_or_equalto', { 'version': '1.21.9' }), { treatment: 'on', config: null }, 'the rule will return `on` if attribute `version` is less than or equal to `1.22.9`'); + + const client2 = splitio.client('2nd'); + await client2.ready(); + + // BETWEEN_SEMVER matcher + assert.deepEqual(client2.getTreatmentsWithConfig(['semver_between'], { 'version': '2.1.1' }).semver_between, { treatment: 'off', config: null }, 'the rule will return `off` if attribute `version` is not between `1.22.9` and `2.1.0`'); + assert.deepEqual(client2.getTreatmentsWithConfig(['semver_between'], { 'version': '2.1.0+build' }).semver_between, { treatment: 'on', config: null }, 'build metadata is ignored'); + assert.deepEqual(client2.getTreatmentsWithConfig(['semver_between'], { 'version': '1.25.0' }).semver_between, { treatment: 'on', config: null }, 'the rule will return `on` if attribute `version` is between `1.22.9` and `2.1.0`'); + assert.deepEqual(client2.getTreatmentsWithConfig(['semver_between'], { 'version': '1.22.9' }).semver_between, { treatment: 'on', config: null }, 'the rule will return `on` if attribute `version` is between `1.22.9` and `2.1.0`'); + assert.deepEqual(client2.getTreatmentsWithConfig(['semver_between'], { 'version': '1.22.9-rc.0' }).semver_between, { treatment: 'off', config: null }, 'the rule will return `off` if attribute `version` is not between `1.22.9` and `2.1.0`'); + + // Evaluation of a flag with unsupported matcher + assert.equal(client2.getTreatment('flag_with_unsupported_matcher'), 'control', 'evaluation of a flag with an unsupported matcher should return control'); + + let POSTED_IMPRESSIONS_COUNT; + + fetchMock.postOnce(config.urls.events + '/testImpressions/bulk', (_, opts) => { + + const payload = JSON.parse(opts.body); + + function validateImpressionData(featureFlagName, expectedImpressionCount, expectedOnCount, expectedLabel, expectedTreatment = 'on') { + const impressions = payload.find(e => e.f === featureFlagName).i; + + assert.equal(impressions.length, expectedImpressionCount, `We should have ${expectedImpressionCount} impressions for the feature flag ${featureFlagName}`); + assert.equal(impressions.filter((imp) => imp.r === expectedLabel && imp.t === expectedTreatment).length, expectedOnCount, `${expectedOnCount} impression with 'on' treatment and label ${expectedLabel}`); + } + + validateImpressionData('semver_equalto', 3, 1, 'equal to semver'); + validateImpressionData('semver_inlist', 4, 2, 'in list semver'); + validateImpressionData('semver_greater_or_equalto', 5, 3, 'greater than or equal to semver'); + validateImpressionData('semver_less_or_equalto', 5, 4, 'less than or equal to semver'); + validateImpressionData('semver_between', 5, 3, 'between semver'); + validateImpressionData('flag_with_unsupported_matcher', 1, 1, 'unsupported matcher type', 'control'); + + POSTED_IMPRESSIONS_COUNT = payload.reduce((acc, curr) => acc + curr.i.length, 0); + + return 200; + }); + + await Promise.all([client.destroy(), client2.destroy()]); + + setTimeout(() => { + assert.equal(listener.logImpression.callCount, POSTED_IMPRESSIONS_COUNT, 'Impression listener should be called once per each impression generated.'); + + assert.end(); + }, 0); +} diff --git a/src/__tests__/mocks/splitchanges.since.-1.semver.json b/src/__tests__/mocks/splitchanges.since.-1.semver.json new file mode 100644 index 000000000..ad3c486c5 --- /dev/null +++ b/src/__tests__/mocks/splitchanges.since.-1.semver.json @@ -0,0 +1,514 @@ +{ + "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" + } + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + } + ], + "label": "between semver" + }, + { + "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": 0 + }, + { + "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": [ + { + "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" + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + } + ], + "label": "equal to semver" + }, + { + "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": 0 + }, + { + "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": [ + { + "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" + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + } + ], + "label": "greater than or equal to semver" + }, + { + "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": 0 + }, + { + "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": [ + { + "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 + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + } + ], + "label": "in list semver" + }, + { + "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": 0 + }, + { + "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": [ + { + "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" + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + } + ], + "label": "less than or equal to semver" + }, + { + "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": 0 + }, + { + "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": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "UNSUPPORTED_MATCHER", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "dependencyMatcherData": null, + "booleanMatcherData": null, + "stringMatcherData": "something" + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 + } + ], + "label": "in segment my_custom_segment" + }, + { + "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": 100 + }, + { + "treatment": "off", + "size": 0 + } + ], + "label": "default rule" + } + ] + } + ], + "since": -1, + "till": 1675259356568 +} diff --git a/src/__tests__/nodeSuites/evaluations-semver.spec.js b/src/__tests__/nodeSuites/evaluations-semver.spec.js new file mode 100644 index 000000000..8a4b6e224 --- /dev/null +++ b/src/__tests__/nodeSuites/evaluations-semver.spec.js @@ -0,0 +1,102 @@ +import sinon from 'sinon'; +import splitChangesMock1 from '../mocks/splitchanges.since.-1.semver.json'; + +import { SplitFactory } from '../../'; + +const listener = { + logImpression: sinon.stub() +}; + +const config = { + core: { + authorizationKey: '' + }, + urls: { + sdk: 'https://sdk.evaluation-semver/api', + events: 'https://events.evaluation-semver/api' + }, + sync: { + impressionsMode: 'DEBUG' + }, + impressionListener: listener, + streamingEnabled: false +}; + +export default async function (fetchMock, assert) { + + fetchMock.getOnce(config.urls.sdk + '/splitChanges?since=-1', { status: 200, body: splitChangesMock1 }); + fetchMock.get(config.urls.sdk + '/splitChanges?since=1675259356568', { status: 200, body: { splits: [], since: 1675259356568, till: 1675259356568 } }); + + const splitio = SplitFactory(config); + const client = splitio.client(); + + await client.ready(); + + // EQUAL_TO_SEMVER matcher + assert.equal(client.getTreatment('emi@split.io', 'semver_equalto', { 'version': '1.22.9' }), 'on', 'the rule will return `on` if attribute `version` is equal to `1.22.9`'); + assert.equal(client.getTreatment('emi@split.io', 'semver_equalto', { 'version': '1.22.9+build' }), 'off', 'build metadata is not ignored'); + assert.equal(client.getTreatment('emi@split.io', 'semver_equalto', { 'version': '1.22.9-rc.0' }), 'off', 'the rule will return `off` if attribute `version` is not equal to `1.22.9`'); + + // IN_LIST_SEMVER matcher + assert.equal(client.getTreatment('emi@split.io', 'semver_inlist', { 'version': '2.1.0' }), 'on', 'the rule will return `on` if attribute `version` is in list (`1.22.9`, `2.1.0`)'); + assert.equal(client.getTreatment('emi@split.io', 'semver_inlist', { 'version': '1.22.9' }), 'on', 'the rule will return `on` if attribute `version` is in list (`1.22.9`, `2.1.0`)'); + assert.equal(client.getTreatment('emi@split.io', 'semver_inlist', { 'version': '1.22.9+build' }), 'off', 'build metadata is not ignored'); + assert.equal(client.getTreatment('emi@split.io', 'semver_inlist', { 'version': '1.22.9-rc.0' }), 'off', 'the rule will return `off` if attribute `version` is not in list (`1.22.9`, `2.1.0`)'); + + // GREATER_THAN_OR_EQUAL_TO_SEMVER matcher + assert.equal(client.getTreatments({ matchingKey: 'rulo@split.io', bucketingKey: 'some_bucket' }, ['semver_greater_or_equalto'], { 'version': '1.23.9' }).semver_greater_or_equalto, 'on', 'the rule will return `on` if attribute `version` is greater than or equal to `1.22.9`'); + assert.equal(client.getTreatments({ matchingKey: 'rulo@split.io', bucketingKey: 'some_bucket' }, ['semver_greater_or_equalto'], { 'version': '1.22.9' }).semver_greater_or_equalto, 'on', 'the rule will return `on` if attribute `version` is greater than or equal to `1.22.9`'); + assert.equal(client.getTreatments({ matchingKey: 'rulo@split.io', bucketingKey: 'some_bucket' }, ['semver_greater_or_equalto'], { 'version': '1.22.9+build' }).semver_greater_or_equalto, 'on', 'build metadata is ignored'); + assert.equal(client.getTreatments({ matchingKey: 'rulo@split.io', bucketingKey: 'some_bucket' }, ['semver_greater_or_equalto'], { 'version': '1.22.9-rc.0' }).semver_greater_or_equalto, 'off', 'the rule will return `off` if attribute `version` is not greater than or equal to `1.22.9`'); + assert.equal(client.getTreatments({ matchingKey: 'rulo@split.io', bucketingKey: 'some_bucket' }, ['semver_greater_or_equalto'], { 'version': '1.21.9' }).semver_greater_or_equalto, 'off', 'the rule will return `off` if attribute `version` is not greater than or equal to `1.22.9`'); + + // LESS_THAN_OR_EQUAL_TO_SEMVER matcher + assert.deepEqual(client.getTreatmentWithConfig('emi@split.io', 'semver_less_or_equalto', { 'version': '1.22.11' }), { treatment: 'off', config: null }, 'the rule will return `off` if attribute `version` is not less than or equal to `1.22.9`'); + assert.deepEqual(client.getTreatmentWithConfig('emi@split.io', 'semver_less_or_equalto', { 'version': '1.22.9' }), { treatment: 'on', config: null }, 'the rule will return `on` if attribute `version` is less than or equal to `1.22.9`'); + assert.deepEqual(client.getTreatmentWithConfig('emi@split.io', 'semver_less_or_equalto', { 'version': '1.22.9+build' }), { treatment: 'on', config: null }, 'build metadata is ignored'); + assert.deepEqual(client.getTreatmentWithConfig('emi@split.io', 'semver_less_or_equalto', { 'version': '1.22.9-rc.0' }), { treatment: 'on', config: null }, 'the rule will return `on` if attribute `version` is less than or equal to `1.22.9`'); + assert.deepEqual(client.getTreatmentWithConfig('emi@split.io', 'semver_less_or_equalto', { 'version': '1.21.9' }), { treatment: 'on', config: null }, 'the rule will return `on` if attribute `version` is less than or equal to `1.22.9`'); + + // BETWEEN_SEMVER matcher + assert.deepEqual(client.getTreatmentsWithConfig('emi@split.io', ['semver_between'], { 'version': '2.1.1' }).semver_between, { treatment: 'off', config: null }, 'the rule will return `off` if attribute `version` is not between `1.22.9` and `2.1.0`'); + assert.deepEqual(client.getTreatmentsWithConfig('emi@split.io', ['semver_between'], { 'version': '2.1.0+build' }).semver_between, { treatment: 'on', config: null }, 'build metadata is ignored'); + assert.deepEqual(client.getTreatmentsWithConfig('emi@split.io', ['semver_between'], { 'version': '1.25.0' }).semver_between, { treatment: 'on', config: null }, 'the rule will return `on` if attribute `version` is between `1.22.9` and `2.1.0`'); + assert.deepEqual(client.getTreatmentsWithConfig('emi@split.io', ['semver_between'], { 'version': '1.22.9' }).semver_between, { treatment: 'on', config: null }, 'the rule will return `on` if attribute `version` is between `1.22.9` and `2.1.0`'); + assert.deepEqual(client.getTreatmentsWithConfig('emi@split.io', ['semver_between'], { 'version': '1.22.9-rc.0' }).semver_between, { treatment: 'off', config: null }, 'the rule will return `off` if attribute `version` is not between `1.22.9` and `2.1.0`'); + + // Evaluation of a flag with unsupported matcher + assert.equal(client.getTreatment('any-key', 'flag_with_unsupported_matcher'), 'control', 'evaluation of a flag with an unsupported matcher should return control'); + + let POSTED_IMPRESSIONS_COUNT; + + fetchMock.postOnce(config.urls.events + '/testImpressions/bulk', (_, opts) => { + + const payload = JSON.parse(opts.body); + + function validateImpressionData(featureFlagName, expectedImpressionCount, expectedOnCount, expectedLabel, expectedTreatment = 'on') { + const impressions = payload.find(e => e.f === featureFlagName).i; + + assert.equal(impressions.length, expectedImpressionCount, `We should have ${expectedImpressionCount} impressions for the feature flag ${featureFlagName}`); + assert.equal(impressions.filter((imp) => imp.r === expectedLabel && imp.t === expectedTreatment).length, expectedOnCount, `${expectedOnCount} impression with 'on' treatment and label ${expectedLabel}`); + } + + validateImpressionData('semver_equalto', 3, 1, 'equal to semver'); + validateImpressionData('semver_inlist', 4, 2, 'in list semver'); + validateImpressionData('semver_greater_or_equalto', 5, 3, 'greater than or equal to semver'); + validateImpressionData('semver_less_or_equalto', 5, 4, 'less than or equal to semver'); + validateImpressionData('semver_between', 5, 3, 'between semver'); + validateImpressionData('flag_with_unsupported_matcher', 1, 1, 'unsupported matcher type', 'control'); + + POSTED_IMPRESSIONS_COUNT = payload.reduce((acc, curr) => acc + curr.i.length, 0); + + return 200; + }); + + await client.destroy(); + + setTimeout(() => { + assert.equal(listener.logImpression.callCount, POSTED_IMPRESSIONS_COUNT, 'Impression listener should be called once per each impression generated.'); + + assert.end(); + }, 0); +} diff --git a/src/__tests__/online/browser.spec.js b/src/__tests__/online/browser.spec.js index f93ec18a9..2d9976aee 100644 --- a/src/__tests__/online/browser.spec.js +++ b/src/__tests__/online/browser.spec.js @@ -2,6 +2,7 @@ import tape from 'tape-catch'; import fetchMock from '../testUtils/fetchMock'; import { url } from '../testUtils'; import evaluationsSuite from '../browserSuites/evaluations.spec'; +import evaluationsSemverSuite from '../browserSuites/evaluations-semver.spec'; import impressionsSuite from '../browserSuites/impressions.spec'; import impressionsSuiteDebug from '../browserSuites/impressions.debug.spec'; import impressionsSuiteNone from '../browserSuites/impressions.none.spec'; @@ -100,6 +101,7 @@ tape('## E2E CI Tests ##', function (assert) { assert.test('E2E / In Memory', evaluationsSuite.bind(null, configInMemory, fetchMock)); assert.test('E2E / In Memory with Bucketing Key', evaluationsSuite.bind(null, configInMemoryWithBucketingKey, fetchMock)); assert.test('E2E / In LocalStorage with In Memory Fallback', evaluationsSuite.bind(null, configInLocalStorage, fetchMock)); + assert.test('E2E / In Memory - Semver', evaluationsSemverSuite.bind(null, fetchMock)); /* Check impressions */ assert.test('E2E / Impressions', impressionsSuite.bind(null, fetchMock)); assert.test('E2E / Impressions Debug Mode', impressionsSuiteDebug.bind(null, fetchMock)); diff --git a/src/__tests__/online/node.spec.js b/src/__tests__/online/node.spec.js index dce02929c..5a59d00d3 100644 --- a/src/__tests__/online/node.spec.js +++ b/src/__tests__/online/node.spec.js @@ -4,6 +4,7 @@ import { url } from '../testUtils'; import { settingsFactory } from '../../settings/node'; import evaluationsSuite from '../nodeSuites/evaluations.spec'; +import evaluationsSemverSuite from '../nodeSuites/evaluations-semver.spec'; import eventsSuite from '../nodeSuites/events.spec'; import impressionsSuite from '../nodeSuites/impressions.spec'; import impressionsSuiteDebug from '../nodeSuites/impressions.debug.spec'; @@ -56,6 +57,7 @@ fetchMock.post(url(settings, '/v1/metrics/usage'), 200); tape('## Node JS - E2E CI Tests ##', async function (assert) { /* Check client evaluations. */ assert.test('E2E / In Memory', evaluationsSuite.bind(null, config, key)); + assert.test('E2E / In Memory - Semver', evaluationsSemverSuite.bind(null, fetchMock)); /* Check impressions */ assert.test('E2E / Impressions', impressionsSuite.bind(null, key, fetchMock)); From 09f610289afc7d9bac025b20832955aaca51786f Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Mon, 15 Apr 2024 14:28:46 -0300 Subject: [PATCH 2/8] Handling invalid semver values --- package-lock.json | 14 +++++++------- package.json | 2 +- .../nodeSuites/evaluations-semver.spec.js | 15 ++++++++++----- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/package-lock.json b/package-lock.json index dffb2c127..d736fce7d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "10.25.2", "license": "Apache-2.0", "dependencies": { - "@splitsoftware/splitio-commons": "1.13.2-rc.3", + "@splitsoftware/splitio-commons": "1.13.2-rc.4", "@types/google.analytics": "0.0.40", "@types/ioredis": "^4.28.0", "bloom-filters": "^3.0.0", @@ -875,9 +875,9 @@ "dev": true }, "node_modules/@splitsoftware/splitio-commons": { - "version": "1.13.2-rc.3", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.13.2-rc.3.tgz", - "integrity": "sha512-2AeRqIYqh9N/2prXv4TOJigJ4+1Jk5yi1Dqxo9yLbgF3ZeUpj+fdMfhoCHAL8mcqym9wjjLR5GR+53feYLtqjg==", + "version": "1.13.2-rc.4", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.13.2-rc.4.tgz", + "integrity": "sha512-9HbUKaRygcBsyurDTXOduhBQykyZ2kkc9YLRn7COPDl29mF3W4FJom7MAPI8vd3HunpyjEEoRemylV/W9XV8VQ==", "dependencies": { "tslib": "^2.3.1" }, @@ -8447,9 +8447,9 @@ "dev": true }, "@splitsoftware/splitio-commons": { - "version": "1.13.2-rc.3", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.13.2-rc.3.tgz", - "integrity": "sha512-2AeRqIYqh9N/2prXv4TOJigJ4+1Jk5yi1Dqxo9yLbgF3ZeUpj+fdMfhoCHAL8mcqym9wjjLR5GR+53feYLtqjg==", + "version": "1.13.2-rc.4", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.13.2-rc.4.tgz", + "integrity": "sha512-9HbUKaRygcBsyurDTXOduhBQykyZ2kkc9YLRn7COPDl29mF3W4FJom7MAPI8vd3HunpyjEEoRemylV/W9XV8VQ==", "requires": { "tslib": "^2.3.1" } diff --git a/package.json b/package.json index 8fa0fd959..3e50855a4 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "node": ">=6" }, "dependencies": { - "@splitsoftware/splitio-commons": "1.13.2-rc.3", + "@splitsoftware/splitio-commons": "1.13.2-rc.4", "@types/google.analytics": "0.0.40", "@types/ioredis": "^4.28.0", "bloom-filters": "^3.0.0", diff --git a/src/__tests__/nodeSuites/evaluations-semver.spec.js b/src/__tests__/nodeSuites/evaluations-semver.spec.js index 8a4b6e224..6c3150d80 100644 --- a/src/__tests__/nodeSuites/evaluations-semver.spec.js +++ b/src/__tests__/nodeSuites/evaluations-semver.spec.js @@ -36,12 +36,14 @@ export default async function (fetchMock, assert) { assert.equal(client.getTreatment('emi@split.io', 'semver_equalto', { 'version': '1.22.9' }), 'on', 'the rule will return `on` if attribute `version` is equal to `1.22.9`'); assert.equal(client.getTreatment('emi@split.io', 'semver_equalto', { 'version': '1.22.9+build' }), 'off', 'build metadata is not ignored'); assert.equal(client.getTreatment('emi@split.io', 'semver_equalto', { 'version': '1.22.9-rc.0' }), 'off', 'the rule will return `off` if attribute `version` is not equal to `1.22.9`'); + assert.equal(client.getTreatment('emi@split.io', 'semver_equalto', { 'version': null }), 'off', 'the rule will return `off` if attribute `version` is not the expected type'); // IN_LIST_SEMVER matcher assert.equal(client.getTreatment('emi@split.io', 'semver_inlist', { 'version': '2.1.0' }), 'on', 'the rule will return `on` if attribute `version` is in list (`1.22.9`, `2.1.0`)'); assert.equal(client.getTreatment('emi@split.io', 'semver_inlist', { 'version': '1.22.9' }), 'on', 'the rule will return `on` if attribute `version` is in list (`1.22.9`, `2.1.0`)'); assert.equal(client.getTreatment('emi@split.io', 'semver_inlist', { 'version': '1.22.9+build' }), 'off', 'build metadata is not ignored'); assert.equal(client.getTreatment('emi@split.io', 'semver_inlist', { 'version': '1.22.9-rc.0' }), 'off', 'the rule will return `off` if attribute `version` is not in list (`1.22.9`, `2.1.0`)'); + assert.equal(client.getTreatment('emi@split.io', 'semver_inlist', { 'version': null }), 'off', 'the rule will return `off` if attribute `version` is not the expected type'); // GREATER_THAN_OR_EQUAL_TO_SEMVER matcher assert.equal(client.getTreatments({ matchingKey: 'rulo@split.io', bucketingKey: 'some_bucket' }, ['semver_greater_or_equalto'], { 'version': '1.23.9' }).semver_greater_or_equalto, 'on', 'the rule will return `on` if attribute `version` is greater than or equal to `1.22.9`'); @@ -49,6 +51,7 @@ export default async function (fetchMock, assert) { assert.equal(client.getTreatments({ matchingKey: 'rulo@split.io', bucketingKey: 'some_bucket' }, ['semver_greater_or_equalto'], { 'version': '1.22.9+build' }).semver_greater_or_equalto, 'on', 'build metadata is ignored'); assert.equal(client.getTreatments({ matchingKey: 'rulo@split.io', bucketingKey: 'some_bucket' }, ['semver_greater_or_equalto'], { 'version': '1.22.9-rc.0' }).semver_greater_or_equalto, 'off', 'the rule will return `off` if attribute `version` is not greater than or equal to `1.22.9`'); assert.equal(client.getTreatments({ matchingKey: 'rulo@split.io', bucketingKey: 'some_bucket' }, ['semver_greater_or_equalto'], { 'version': '1.21.9' }).semver_greater_or_equalto, 'off', 'the rule will return `off` if attribute `version` is not greater than or equal to `1.22.9`'); + assert.equal(client.getTreatments({ matchingKey: 'rulo@split.io', bucketingKey: 'some_bucket' }, ['semver_greater_or_equalto'], { 'version': false }).semver_greater_or_equalto, 'off', 'the rule will return `off` if attribute `version` is not the expected type'); // LESS_THAN_OR_EQUAL_TO_SEMVER matcher assert.deepEqual(client.getTreatmentWithConfig('emi@split.io', 'semver_less_or_equalto', { 'version': '1.22.11' }), { treatment: 'off', config: null }, 'the rule will return `off` if attribute `version` is not less than or equal to `1.22.9`'); @@ -56,6 +59,7 @@ export default async function (fetchMock, assert) { assert.deepEqual(client.getTreatmentWithConfig('emi@split.io', 'semver_less_or_equalto', { 'version': '1.22.9+build' }), { treatment: 'on', config: null }, 'build metadata is ignored'); assert.deepEqual(client.getTreatmentWithConfig('emi@split.io', 'semver_less_or_equalto', { 'version': '1.22.9-rc.0' }), { treatment: 'on', config: null }, 'the rule will return `on` if attribute `version` is less than or equal to `1.22.9`'); assert.deepEqual(client.getTreatmentWithConfig('emi@split.io', 'semver_less_or_equalto', { 'version': '1.21.9' }), { treatment: 'on', config: null }, 'the rule will return `on` if attribute `version` is less than or equal to `1.22.9`'); + assert.deepEqual(client.getTreatmentWithConfig('emi@split.io', 'semver_less_or_equalto', { 'version': {} }), { treatment: 'off', config: null }, 'the rule will return `off` if attribute `version` is not the expected type'); // BETWEEN_SEMVER matcher assert.deepEqual(client.getTreatmentsWithConfig('emi@split.io', ['semver_between'], { 'version': '2.1.1' }).semver_between, { treatment: 'off', config: null }, 'the rule will return `off` if attribute `version` is not between `1.22.9` and `2.1.0`'); @@ -63,6 +67,7 @@ export default async function (fetchMock, assert) { assert.deepEqual(client.getTreatmentsWithConfig('emi@split.io', ['semver_between'], { 'version': '1.25.0' }).semver_between, { treatment: 'on', config: null }, 'the rule will return `on` if attribute `version` is between `1.22.9` and `2.1.0`'); assert.deepEqual(client.getTreatmentsWithConfig('emi@split.io', ['semver_between'], { 'version': '1.22.9' }).semver_between, { treatment: 'on', config: null }, 'the rule will return `on` if attribute `version` is between `1.22.9` and `2.1.0`'); assert.deepEqual(client.getTreatmentsWithConfig('emi@split.io', ['semver_between'], { 'version': '1.22.9-rc.0' }).semver_between, { treatment: 'off', config: null }, 'the rule will return `off` if attribute `version` is not between `1.22.9` and `2.1.0`'); + assert.deepEqual(client.getTreatmentsWithConfig('emi@split.io', ['semver_between'], { 'version': [] }).semver_between, { treatment: 'off', config: null }, 'the rule will return `off` if attribute `version` is not the expected type'); // Evaluation of a flag with unsupported matcher assert.equal(client.getTreatment('any-key', 'flag_with_unsupported_matcher'), 'control', 'evaluation of a flag with an unsupported matcher should return control'); @@ -80,11 +85,11 @@ export default async function (fetchMock, assert) { assert.equal(impressions.filter((imp) => imp.r === expectedLabel && imp.t === expectedTreatment).length, expectedOnCount, `${expectedOnCount} impression with 'on' treatment and label ${expectedLabel}`); } - validateImpressionData('semver_equalto', 3, 1, 'equal to semver'); - validateImpressionData('semver_inlist', 4, 2, 'in list semver'); - validateImpressionData('semver_greater_or_equalto', 5, 3, 'greater than or equal to semver'); - validateImpressionData('semver_less_or_equalto', 5, 4, 'less than or equal to semver'); - validateImpressionData('semver_between', 5, 3, 'between semver'); + validateImpressionData('semver_equalto', 4, 1, 'equal to semver'); + validateImpressionData('semver_inlist', 5, 2, 'in list semver'); + validateImpressionData('semver_greater_or_equalto', 6, 3, 'greater than or equal to semver'); + validateImpressionData('semver_less_or_equalto', 6, 4, 'less than or equal to semver'); + validateImpressionData('semver_between', 6, 3, 'between semver'); validateImpressionData('flag_with_unsupported_matcher', 1, 1, 'unsupported matcher type', 'control'); POSTED_IMPRESSIONS_COUNT = payload.reduce((acc, curr) => acc + curr.i.length, 0); From 061344c0e6a14ab41808e07662bdcef6098f1bfc Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Mon, 15 Apr 2024 17:59:09 -0300 Subject: [PATCH 3/8] Prepare rc --- .github/workflows/ci-cd.yml | 4 ++-- package-lock.json | 18 +++++++++--------- package.json | 4 ++-- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index d28295fdb..52f0377ef 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -58,7 +58,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/SDKS_8266_semver_matchers' || github.ref == 'refs/heads/master') }} uses: actions/upload-artifact@v3 with: name: assets @@ -69,7 +69,7 @@ jobs: name: Upload assets runs-on: ubuntu-20.04 needs: build - if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/development' }} + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/SDKS_8266_semver_matchers' }} strategy: matrix: environment: diff --git a/package-lock.json b/package-lock.json index d736fce7d..5027bc9af 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,15 @@ { "name": "@splitsoftware/splitio", - "version": "10.25.2", + "version": "10.25.3-rc.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@splitsoftware/splitio", - "version": "10.25.2", + "version": "10.25.3-rc.0", "license": "Apache-2.0", "dependencies": { - "@splitsoftware/splitio-commons": "1.13.2-rc.4", + "@splitsoftware/splitio-commons": "1.13.2-rc.5", "@types/google.analytics": "0.0.40", "@types/ioredis": "^4.28.0", "bloom-filters": "^3.0.0", @@ -875,9 +875,9 @@ "dev": true }, "node_modules/@splitsoftware/splitio-commons": { - "version": "1.13.2-rc.4", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.13.2-rc.4.tgz", - "integrity": "sha512-9HbUKaRygcBsyurDTXOduhBQykyZ2kkc9YLRn7COPDl29mF3W4FJom7MAPI8vd3HunpyjEEoRemylV/W9XV8VQ==", + "version": "1.13.2-rc.5", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.13.2-rc.5.tgz", + "integrity": "sha512-cOR9DKwzFEPPu5urRTGUuTu4SRrA5GoD4P9f2sZ5mez2yVrkOZy1xJ+5QjcNIiy7jrSZD4PvaxuHE85GtVcvFA==", "dependencies": { "tslib": "^2.3.1" }, @@ -8447,9 +8447,9 @@ "dev": true }, "@splitsoftware/splitio-commons": { - "version": "1.13.2-rc.4", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.13.2-rc.4.tgz", - "integrity": "sha512-9HbUKaRygcBsyurDTXOduhBQykyZ2kkc9YLRn7COPDl29mF3W4FJom7MAPI8vd3HunpyjEEoRemylV/W9XV8VQ==", + "version": "1.13.2-rc.5", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.13.2-rc.5.tgz", + "integrity": "sha512-cOR9DKwzFEPPu5urRTGUuTu4SRrA5GoD4P9f2sZ5mez2yVrkOZy1xJ+5QjcNIiy7jrSZD4PvaxuHE85GtVcvFA==", "requires": { "tslib": "^2.3.1" } diff --git a/package.json b/package.json index 3e50855a4..82b27ca36 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio", - "version": "10.25.2", + "version": "10.25.3-rc.0", "description": "Split SDK", "files": [ "README.md", @@ -40,7 +40,7 @@ "node": ">=6" }, "dependencies": { - "@splitsoftware/splitio-commons": "1.13.2-rc.4", + "@splitsoftware/splitio-commons": "1.13.2-rc.5", "@types/google.analytics": "0.0.40", "@types/ioredis": "^4.28.0", "bloom-filters": "^3.0.0", From f5d11af1c2489ff4c8a1f2182b271b17e1e25263 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Mon, 15 Apr 2024 18:04:18 -0300 Subject: [PATCH 4/8] Update version --- src/settings/defaults/version.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/settings/defaults/version.js b/src/settings/defaults/version.js index f1a241e74..105f27dc1 100644 --- a/src/settings/defaults/version.js +++ b/src/settings/defaults/version.js @@ -1 +1 @@ -export const packageVersion = '10.25.2'; +export const packageVersion = '10.25.3-rc.0'; From e9acdebf82e0b0e26d05bddfcb4ef937d8806c71 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Tue, 16 Apr 2024 11:26:05 -0300 Subject: [PATCH 5/8] Assert some corner cases --- .github/workflows/ci-cd.yml | 4 ++-- .../browserSuites/evaluations-semver.spec.js | 16 +++++++++++----- .../nodeSuites/evaluations-semver.spec.js | 5 +++-- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 52f0377ef..d28295fdb 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -58,7 +58,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/SDKS_8266_semver_matchers' || github.ref == 'refs/heads/master') }} + if: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/development' || github.ref == 'refs/heads/master') }} uses: actions/upload-artifact@v3 with: name: assets @@ -69,7 +69,7 @@ jobs: name: Upload assets runs-on: ubuntu-20.04 needs: build - if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/SDKS_8266_semver_matchers' }} + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/development' }} strategy: matrix: environment: diff --git a/src/__tests__/browserSuites/evaluations-semver.spec.js b/src/__tests__/browserSuites/evaluations-semver.spec.js index 1e2b9794f..cb95b5943 100644 --- a/src/__tests__/browserSuites/evaluations-semver.spec.js +++ b/src/__tests__/browserSuites/evaluations-semver.spec.js @@ -39,12 +39,15 @@ export default async function (fetchMock, assert) { assert.equal(client.getTreatment('semver_equalto', { 'version': '1.22.9' }), 'on', 'the rule will return `on` if attribute `version` is equal to `1.22.9`'); assert.equal(client.getTreatment('semver_equalto', { 'version': '1.22.9+build' }), 'off', 'build metadata is not ignored'); assert.equal(client.getTreatment('semver_equalto', { 'version': '1.22.9-rc.0' }), 'off', 'the rule will return `off` if attribute `version` is not equal to `1.22.9`'); + assert.equal(client.getTreatment('semver_equalto', { 'version': null }), 'off', 'the rule will return `off` if attribute `version` is not the expected type'); + assert.equal(client.getTreatment('semver_equalto'), 'off', 'the rule will return `off` if attribute `version` is not provided'); // IN_LIST_SEMVER matcher assert.equal(client.getTreatment('semver_inlist', { 'version': '2.1.0' }), 'on', 'the rule will return `on` if attribute `version` is in list (`1.22.9`, `2.1.0`)'); assert.equal(client.getTreatment('semver_inlist', { 'version': '1.22.9' }), 'on', 'the rule will return `on` if attribute `version` is in list (`1.22.9`, `2.1.0`)'); assert.equal(client.getTreatment('semver_inlist', { 'version': '1.22.9+build' }), 'off', 'build metadata is not ignored'); assert.equal(client.getTreatment('semver_inlist', { 'version': '1.22.9-rc.0' }), 'off', 'the rule will return `off` if attribute `version` is not in list (`1.22.9`, `2.1.0`)'); + assert.equal(client.getTreatment('semver_inlist', { 'version': null }), 'off', 'the rule will return `off` if attribute `version` is not the expected type'); // GREATER_THAN_OR_EQUAL_TO_SEMVER matcher assert.equal(client.getTreatments(['semver_greater_or_equalto'], { 'version': '1.23.9' }).semver_greater_or_equalto, 'on', 'the rule will return `on` if attribute `version` is greater than or equal to `1.22.9`'); @@ -52,6 +55,7 @@ export default async function (fetchMock, assert) { assert.equal(client.getTreatments(['semver_greater_or_equalto'], { 'version': '1.22.9+build' }).semver_greater_or_equalto, 'on', 'build metadata is ignored'); assert.equal(client.getTreatments(['semver_greater_or_equalto'], { 'version': '1.22.9-rc.0' }).semver_greater_or_equalto, 'off', 'the rule will return `off` if attribute `version` is not greater than or equal to `1.22.9`'); assert.equal(client.getTreatments(['semver_greater_or_equalto'], { 'version': '1.21.9' }).semver_greater_or_equalto, 'off', 'the rule will return `off` if attribute `version` is not greater than or equal to `1.22.9`'); + assert.equal(client.getTreatments(['semver_greater_or_equalto'], { 'version': 'invalid' }).semver_greater_or_equalto, 'off', 'the rule will return `off` if attribute `version` is an invalid semver value'); // LESS_THAN_OR_EQUAL_TO_SEMVER matcher assert.deepEqual(client.getTreatmentWithConfig('semver_less_or_equalto', { 'version': '1.22.11' }), { treatment: 'off', config: null }, 'the rule will return `off` if attribute `version` is not less than or equal to `1.22.9`'); @@ -59,6 +63,7 @@ export default async function (fetchMock, assert) { assert.deepEqual(client.getTreatmentWithConfig('semver_less_or_equalto', { 'version': '1.22.9+build' }), { treatment: 'on', config: null }, 'build metadata is ignored'); assert.deepEqual(client.getTreatmentWithConfig('semver_less_or_equalto', { 'version': '1.22.9-rc.0' }), { treatment: 'on', config: null }, 'the rule will return `on` if attribute `version` is less than or equal to `1.22.9`'); assert.deepEqual(client.getTreatmentWithConfig('semver_less_or_equalto', { 'version': '1.21.9' }), { treatment: 'on', config: null }, 'the rule will return `on` if attribute `version` is less than or equal to `1.22.9`'); + assert.deepEqual(client.getTreatmentWithConfig('semver_less_or_equalto', { 'version': {} }), { treatment: 'off', config: null }, 'the rule will return `off` if attribute `version` is not the expected type'); const client2 = splitio.client('2nd'); await client2.ready(); @@ -69,6 +74,7 @@ export default async function (fetchMock, assert) { assert.deepEqual(client2.getTreatmentsWithConfig(['semver_between'], { 'version': '1.25.0' }).semver_between, { treatment: 'on', config: null }, 'the rule will return `on` if attribute `version` is between `1.22.9` and `2.1.0`'); assert.deepEqual(client2.getTreatmentsWithConfig(['semver_between'], { 'version': '1.22.9' }).semver_between, { treatment: 'on', config: null }, 'the rule will return `on` if attribute `version` is between `1.22.9` and `2.1.0`'); assert.deepEqual(client2.getTreatmentsWithConfig(['semver_between'], { 'version': '1.22.9-rc.0' }).semver_between, { treatment: 'off', config: null }, 'the rule will return `off` if attribute `version` is not between `1.22.9` and `2.1.0`'); + assert.deepEqual(client2.getTreatmentsWithConfig(['semver_between'], { 'version': [] }).semver_between, { treatment: 'off', config: null }, 'the rule will return `off` if attribute `version` is not the expected type'); // Evaluation of a flag with unsupported matcher assert.equal(client2.getTreatment('flag_with_unsupported_matcher'), 'control', 'evaluation of a flag with an unsupported matcher should return control'); @@ -86,11 +92,11 @@ export default async function (fetchMock, assert) { assert.equal(impressions.filter((imp) => imp.r === expectedLabel && imp.t === expectedTreatment).length, expectedOnCount, `${expectedOnCount} impression with 'on' treatment and label ${expectedLabel}`); } - validateImpressionData('semver_equalto', 3, 1, 'equal to semver'); - validateImpressionData('semver_inlist', 4, 2, 'in list semver'); - validateImpressionData('semver_greater_or_equalto', 5, 3, 'greater than or equal to semver'); - validateImpressionData('semver_less_or_equalto', 5, 4, 'less than or equal to semver'); - validateImpressionData('semver_between', 5, 3, 'between semver'); + validateImpressionData('semver_equalto', 5, 1, 'equal to semver'); + validateImpressionData('semver_inlist', 5, 2, 'in list semver'); + validateImpressionData('semver_greater_or_equalto', 6, 3, 'greater than or equal to semver'); + validateImpressionData('semver_less_or_equalto', 6, 4, 'less than or equal to semver'); + validateImpressionData('semver_between', 6, 3, 'between semver'); validateImpressionData('flag_with_unsupported_matcher', 1, 1, 'unsupported matcher type', 'control'); POSTED_IMPRESSIONS_COUNT = payload.reduce((acc, curr) => acc + curr.i.length, 0); diff --git a/src/__tests__/nodeSuites/evaluations-semver.spec.js b/src/__tests__/nodeSuites/evaluations-semver.spec.js index 6c3150d80..6d2a3a475 100644 --- a/src/__tests__/nodeSuites/evaluations-semver.spec.js +++ b/src/__tests__/nodeSuites/evaluations-semver.spec.js @@ -37,6 +37,7 @@ export default async function (fetchMock, assert) { assert.equal(client.getTreatment('emi@split.io', 'semver_equalto', { 'version': '1.22.9+build' }), 'off', 'build metadata is not ignored'); assert.equal(client.getTreatment('emi@split.io', 'semver_equalto', { 'version': '1.22.9-rc.0' }), 'off', 'the rule will return `off` if attribute `version` is not equal to `1.22.9`'); assert.equal(client.getTreatment('emi@split.io', 'semver_equalto', { 'version': null }), 'off', 'the rule will return `off` if attribute `version` is not the expected type'); + assert.equal(client.getTreatment('emi@split.io', 'semver_equalto'), 'off', 'the rule will return `off` if attribute `version` is not provided'); // IN_LIST_SEMVER matcher assert.equal(client.getTreatment('emi@split.io', 'semver_inlist', { 'version': '2.1.0' }), 'on', 'the rule will return `on` if attribute `version` is in list (`1.22.9`, `2.1.0`)'); @@ -51,7 +52,7 @@ export default async function (fetchMock, assert) { assert.equal(client.getTreatments({ matchingKey: 'rulo@split.io', bucketingKey: 'some_bucket' }, ['semver_greater_or_equalto'], { 'version': '1.22.9+build' }).semver_greater_or_equalto, 'on', 'build metadata is ignored'); assert.equal(client.getTreatments({ matchingKey: 'rulo@split.io', bucketingKey: 'some_bucket' }, ['semver_greater_or_equalto'], { 'version': '1.22.9-rc.0' }).semver_greater_or_equalto, 'off', 'the rule will return `off` if attribute `version` is not greater than or equal to `1.22.9`'); assert.equal(client.getTreatments({ matchingKey: 'rulo@split.io', bucketingKey: 'some_bucket' }, ['semver_greater_or_equalto'], { 'version': '1.21.9' }).semver_greater_or_equalto, 'off', 'the rule will return `off` if attribute `version` is not greater than or equal to `1.22.9`'); - assert.equal(client.getTreatments({ matchingKey: 'rulo@split.io', bucketingKey: 'some_bucket' }, ['semver_greater_or_equalto'], { 'version': false }).semver_greater_or_equalto, 'off', 'the rule will return `off` if attribute `version` is not the expected type'); + assert.equal(client.getTreatments({ matchingKey: 'rulo@split.io', bucketingKey: 'some_bucket' }, ['semver_greater_or_equalto'], { 'version': 'invalid' }).semver_greater_or_equalto, 'off', 'the rule will return `off` if attribute `version` is an invalid semver value'); // LESS_THAN_OR_EQUAL_TO_SEMVER matcher assert.deepEqual(client.getTreatmentWithConfig('emi@split.io', 'semver_less_or_equalto', { 'version': '1.22.11' }), { treatment: 'off', config: null }, 'the rule will return `off` if attribute `version` is not less than or equal to `1.22.9`'); @@ -85,7 +86,7 @@ export default async function (fetchMock, assert) { assert.equal(impressions.filter((imp) => imp.r === expectedLabel && imp.t === expectedTreatment).length, expectedOnCount, `${expectedOnCount} impression with 'on' treatment and label ${expectedLabel}`); } - validateImpressionData('semver_equalto', 4, 1, 'equal to semver'); + validateImpressionData('semver_equalto', 5, 1, 'equal to semver'); validateImpressionData('semver_inlist', 5, 2, 'in list semver'); validateImpressionData('semver_greater_or_equalto', 6, 3, 'greater than or equal to semver'); validateImpressionData('semver_less_or_equalto', 6, 4, 'less than or equal to semver'); From 98123956eb7d5fcc2bce4ee1f7bd0148577a66e4 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Tue, 16 Apr 2024 15:17:43 -0300 Subject: [PATCH 6/8] Upgrade JS-commons --- package-lock.json | 18 +++++++++--------- package.json | 4 ++-- src/settings/defaults/version.js | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5027bc9af..ebbdac3d7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,15 @@ { "name": "@splitsoftware/splitio", - "version": "10.25.3-rc.0", + "version": "10.25.3-rc.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@splitsoftware/splitio", - "version": "10.25.3-rc.0", + "version": "10.25.3-rc.1", "license": "Apache-2.0", "dependencies": { - "@splitsoftware/splitio-commons": "1.13.2-rc.5", + "@splitsoftware/splitio-commons": "1.13.2-rc.6", "@types/google.analytics": "0.0.40", "@types/ioredis": "^4.28.0", "bloom-filters": "^3.0.0", @@ -875,9 +875,9 @@ "dev": true }, "node_modules/@splitsoftware/splitio-commons": { - "version": "1.13.2-rc.5", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.13.2-rc.5.tgz", - "integrity": "sha512-cOR9DKwzFEPPu5urRTGUuTu4SRrA5GoD4P9f2sZ5mez2yVrkOZy1xJ+5QjcNIiy7jrSZD4PvaxuHE85GtVcvFA==", + "version": "1.13.2-rc.6", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.13.2-rc.6.tgz", + "integrity": "sha512-316oyRyJP4QRQ8jqmy71Q5EPGE5iC9BXPwSbtgK0+GXk+nLgnyIrCTzIYY6ugLPvLB+kdrMiJ/eBYEm1slMRJQ==", "dependencies": { "tslib": "^2.3.1" }, @@ -8447,9 +8447,9 @@ "dev": true }, "@splitsoftware/splitio-commons": { - "version": "1.13.2-rc.5", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.13.2-rc.5.tgz", - "integrity": "sha512-cOR9DKwzFEPPu5urRTGUuTu4SRrA5GoD4P9f2sZ5mez2yVrkOZy1xJ+5QjcNIiy7jrSZD4PvaxuHE85GtVcvFA==", + "version": "1.13.2-rc.6", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.13.2-rc.6.tgz", + "integrity": "sha512-316oyRyJP4QRQ8jqmy71Q5EPGE5iC9BXPwSbtgK0+GXk+nLgnyIrCTzIYY6ugLPvLB+kdrMiJ/eBYEm1slMRJQ==", "requires": { "tslib": "^2.3.1" } diff --git a/package.json b/package.json index 82b27ca36..28385447a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio", - "version": "10.25.3-rc.0", + "version": "10.25.3-rc.1", "description": "Split SDK", "files": [ "README.md", @@ -40,7 +40,7 @@ "node": ">=6" }, "dependencies": { - "@splitsoftware/splitio-commons": "1.13.2-rc.5", + "@splitsoftware/splitio-commons": "1.13.2-rc.6", "@types/google.analytics": "0.0.40", "@types/ioredis": "^4.28.0", "bloom-filters": "^3.0.0", diff --git a/src/settings/defaults/version.js b/src/settings/defaults/version.js index 105f27dc1..75c8b429b 100644 --- a/src/settings/defaults/version.js +++ b/src/settings/defaults/version.js @@ -1 +1 @@ -export const packageVersion = '10.25.3-rc.0'; +export const packageVersion = '10.25.3-rc.1'; From 8ecfc785fa77c3a2b7fdda9a8f8a8fab5beaa24f Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Tue, 16 Apr 2024 15:21:18 -0300 Subject: [PATCH 7/8] Prepare rc --- .github/workflows/ci-cd.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index d28295fdb..52f0377ef 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -58,7 +58,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/SDKS_8266_semver_matchers' || github.ref == 'refs/heads/master') }} uses: actions/upload-artifact@v3 with: name: assets @@ -69,7 +69,7 @@ jobs: name: Upload assets runs-on: ubuntu-20.04 needs: build - if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/development' }} + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/SDKS_8266_semver_matchers' }} strategy: matrix: environment: From 03324e1603af24c0b044bea2adecec29cddd8d14 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Thu, 18 Apr 2024 15:17:20 -0300 Subject: [PATCH 8/8] Add changelog entry --- .github/workflows/ci-cd.yml | 4 ++-- CHANGES.txt | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 52f0377ef..d28295fdb 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -58,7 +58,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/SDKS_8266_semver_matchers' || github.ref == 'refs/heads/master') }} + if: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/development' || github.ref == 'refs/heads/master') }} uses: actions/upload-artifact@v3 with: name: assets @@ -69,7 +69,7 @@ jobs: name: Upload assets runs-on: ubuntu-20.04 needs: build - if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/SDKS_8266_semver_matchers' }} + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/development' }} strategy: matrix: environment: diff --git a/CHANGES.txt b/CHANGES.txt index 6a09d963d..49cf777b6 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,7 @@ +10.26.0 (April XX, 2024) + - Updated @splitsoftware/splitio-commons package to version 1.14.0 that includes minor updates: + - Added support for Semver matchers. + 10.25.2 (March 26, 2024) - Updated some transitive dependencies for vulnerability fixes. - Bugfixing - Added tslib as an explicit dependency to avoid issues with some package managers that don't resolve it automatically as a transitive dependency from @splitsoftware/splitio-commons (Related to issue https://github.com/splitio/javascript-client/issues/795).