From 44e4af57017d36ee52595c80454c589a01810127 Mon Sep 17 00:00:00 2001 From: Emmanuel Zamora Date: Mon, 8 Aug 2022 19:27:23 -0300 Subject: [PATCH 1/2] [SDKS-5964, SDKS-5965] Add E2E tests. fix bloom filter behaviour --- package-lock.json | 8 +- package.json | 4 +- .../browserSuites/impressions.none.spec.js | 92 ++++++++++++++++++ .../nodeSuites/impressions.none.spec.js | 97 +++++++++++++++++++ src/__tests__/online/browser.spec.js | 2 + src/__tests__/online/node.spec.js | 2 + src/__tests__/testUtils/index.js | 2 +- .../filter/__tests__/bloomFilter.spec.js | 14 +-- src/platform/filter/bloomFilter.js | 10 +- src/settings/defaults/version.js | 2 +- types/splitio.d.ts | 2 +- 11 files changed, 216 insertions(+), 19 deletions(-) create mode 100644 src/__tests__/browserSuites/impressions.none.spec.js create mode 100644 src/__tests__/nodeSuites/impressions.none.spec.js diff --git a/package-lock.json b/package-lock.json index 71138b261..a56481d77 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio", - "version": "10.21.1", + "version": "10.21.1-rc.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -494,9 +494,9 @@ "dev": true }, "@splitsoftware/splitio-commons": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.6.1.tgz", - "integrity": "sha512-lijSMpX5a7HiphPnzr7fKlicGSd5TeBWX5URLPWfL87SPlO+8iF7S7PNFPg8GMu+44CdRrp4REHPJo97ht6ePQ==", + "version": "1.6.2-rc.2", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.6.2-rc.2.tgz", + "integrity": "sha512-YPOLbXfuV45t8EKLXHFcWVvnI5Zre6xsnfkVPw3DbEUzKro/n2HrYYyG/G2SZCBZP4TSpk0vemGBH7HOP/m9MQ==", "requires": { "tslib": "^2.3.1" } diff --git a/package.json b/package.json index 1faf90cb6..7450ba7f8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio", - "version": "10.21.1", + "version": "10.21.1-rc.0", "description": "Split SDK", "files": [ "README.md", @@ -34,7 +34,7 @@ }, "dependencies": { "@ably/bloomit": "^1.4.2", - "@splitsoftware/splitio-commons": "1.6.1", + "@splitsoftware/splitio-commons": "1.6.2-rc.2", "@types/google.analytics": "0.0.40", "@types/ioredis": "^4.28.0", "ioredis": "^4.28.0", diff --git a/src/__tests__/browserSuites/impressions.none.spec.js b/src/__tests__/browserSuites/impressions.none.spec.js new file mode 100644 index 000000000..dccc8c1ad --- /dev/null +++ b/src/__tests__/browserSuites/impressions.none.spec.js @@ -0,0 +1,92 @@ +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 mySegmentsFacundo from '../mocks/mysegments.facundo@split.io.json'; +import { NONE } from '@splitsoftware/splitio-commons/src/utils/constants'; +import { url } from '../testUtils'; + +const baseUrls = { + sdk: 'https://sdk.baseurl/impressionsNoneSuite', + events: 'https://events.baseurl/impressionsNoneSuite' +}; + +const settings = settingsFactory({ + core: { + key: '' + }, + urls: baseUrls, + streamingEnabled: false +}); + +const config = { + core: { + authorizationKey: '', + key: 'facundo@split.io' + }, + scheduler: { + featuresRefreshRate: 1, + segmentsRefreshRate: 1, + impressionsRefreshRate: 3000, + impressionsQueueSize: 3, // flush impressions when 3 are queued + uniqueKeysCacheSize: 3 // flush impressions when 3 are queued + }, + urls: baseUrls, + startup: { + eventsFirstPushWindow: 3000 + }, + sync: { + impressionsMode: NONE + }, + streamingEnabled: false +}; + +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?since=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.get(url(settings, '/splitChanges?since=1457552620999'), { status: 200, body: splitChangesMock2 }); + fetchMock.get(url(settings, '/mySegments/facundo%40split.io'), { status: 200, body: mySegmentsFacundo }); + fetchMock.get(url(settings, '/mySegments/emma%40split.io'), { status: 200, body: mySegmentsFacundo }); + fetchMock.postOnce(baseUrls.events + '/testImpressions/count', 200); + const splitio = SplitFactory(config); + const client = splitio.client(); + const sharedClient = splitio.client('emma@split.io'); + + fetchMock.postOnce(url(settings, '/v1/keys/cs'), (url, opts) => { + const data = JSON.parse(opts.body); + + assert.equal(data.keys.length, 2, 'We performed evaluations for three split, so we should have 3 item total.'); + + function validateImpressionData(output, expected) { + assert.equal(output.k, expected.key, 'Present impressions should have the correct featureName.'); + assert.deepEqual(output.fs, expected.features, 'Present impressions should have the correct key list.'); + } + + client.destroy().then(() => { + validateImpressionData(data.keys[0], { + key: 'facundo@split.io', + features:['split_with_config','always_off', 'always_on'] + }); + validateImpressionData(data.keys[1], { + key: 'emma@split.io', + features:['always_off', 'always_on'] + }); + + assert.end(); + + }); + + return 200; + }); + + splitio.Logger.enable(); + + await client.ready(); + + client.getTreatment('split_with_config'); + sharedClient.getTreatment('always_off'); + client.getTreatment('always_off'); + sharedClient.getTreatment('always_on'); + client.getTreatment('always_on'); + +} diff --git a/src/__tests__/nodeSuites/impressions.none.spec.js b/src/__tests__/nodeSuites/impressions.none.spec.js new file mode 100644 index 000000000..9fb37bb17 --- /dev/null +++ b/src/__tests__/nodeSuites/impressions.none.spec.js @@ -0,0 +1,97 @@ +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 { url } from '../testUtils'; + +const baseUrls = { + sdk: 'https://sdk.baseurl/impressionsDebugSuite', + events: 'https://events.baseurl/impressionsDebugSuite' +}; + +const settings = settingsFactory({ + core: { + key: '' + }, + urls: baseUrls, + streamingEnabled: false +}); + +const config = { + core: { + authorizationKey: '' + }, + scheduler: { + featuresRefreshRate: 1, + segmentsRefreshRate: 1, + impressionsRefreshRate: 3000, + impressionsQueueSize: 3, // flush impressions when 3 are queued + uniqueKeysCacheSize: 7 // flush impressions when 3 are queued + }, + urls: baseUrls, + startup: { + eventsFirstPushWindow: 3000 + }, + sync: { + impressionsMode: NONE + }, + streamingEnabled: false +}; + +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?since=-1'), { status: 200, body: splitChangesMock1 }); + fetchMock.get(url(settings, '/splitChanges?since=1457552620999'), { status: 200, body: splitChangesMock2 }); + fetchMock.get(url(settings, '/splitChanges?since=1457552620999'), { status: 200, body: splitChangesMock2 }); + fetchMock.get(new RegExp(`${url(settings, '/segmentChanges/')}.*`), { status: 200, body: { since: 10, till: 10, name: 'segmentName', added: [], removed: [] } }); + fetchMock.postOnce(baseUrls.events + '/testImpressions/count', 200); + const splitio = SplitFactory(config); + const client = splitio.client(); + + fetchMock.postOnce(url(settings, '/v1/keys/ss'), (url, opts) => { + const data = JSON.parse(opts.body); + + assert.equal(data.keys.length, 3, 'We performed evaluations for three split, so we should have 3 item total.'); + + function validateImpressionData(output, expected) { + assert.equal(output.f, expected.featureName, 'Present impressions should have the correct featureName.'); + assert.deepEqual(output.ks, expected.keys, 'Present impressions should have the correct key list.'); + } + + client.destroy().then(() => { + validateImpressionData(data.keys[0], { + featureName: 'split_with_config', + keys:['emma@split.io','emi@split.io'] + }); + validateImpressionData(data.keys[1], { + featureName: 'always_off', + keys:['emma@split.io','emi@split.io'] + }); + validateImpressionData(data.keys[2], { + featureName: 'always_on', + keys:['emma@split.io','emi@split.io','nico@split.io'] + }); + + assert.end(); + + }); + + return 200; + }); + + splitio.Logger.enable(); + + await client.ready(); + + client.getTreatment('emma@split.io', 'split_with_config'); + client.getTreatment('emma@split.io', 'always_off'); + client.getTreatment('emma@split.io', 'always_on'); + client.getTreatment('emi@split.io', 'always_on'); + client.getTreatment('nico@split.io', 'always_on'); + client.getTreatment('emma@split.io', 'always_on'); + client.getTreatment('emi@split.io', 'always_off'); + client.getTreatment('emi@split.io', 'split_with_config'); + client.getTreatment('emma@split.io', 'split_with_config'); + +} diff --git a/src/__tests__/online/browser.spec.js b/src/__tests__/online/browser.spec.js index e5bb5bc47..a96dfe9b7 100644 --- a/src/__tests__/online/browser.spec.js +++ b/src/__tests__/online/browser.spec.js @@ -4,6 +4,7 @@ import { url } from '../testUtils'; import evaluationsSuite from '../browserSuites/evaluations.spec'; import impressionsSuite from '../browserSuites/impressions.spec'; import impressionsSuiteDebug from '../browserSuites/impressions.debug.spec'; +import impressionsSuiteNone from '../browserSuites/impressions.none.spec'; import telemetrySuite from '../browserSuites/telemetry.spec'; import impressionsListenerSuite from '../browserSuites/impressions-listener.spec'; import readinessSuite from '../browserSuites/readiness.spec'; @@ -101,6 +102,7 @@ tape('## E2E CI Tests ##', function (assert) { /* Check impressions */ assert.test('E2E / Impressions', impressionsSuite.bind(null, fetchMock)); assert.test('E2E / Impressions Debug Mode', impressionsSuiteDebug.bind(null, fetchMock)); + assert.test('E2E / Impressions None Mode', impressionsSuiteNone.bind(null, fetchMock)); /* Check impression listener */ assert.test('E2E / Impression listener', impressionsListenerSuite); /* Check telemetry */ diff --git a/src/__tests__/online/node.spec.js b/src/__tests__/online/node.spec.js index 3fbc56bff..81304cce2 100644 --- a/src/__tests__/online/node.spec.js +++ b/src/__tests__/online/node.spec.js @@ -7,6 +7,7 @@ import evaluationsSuite from '../nodeSuites/evaluations.spec'; import eventsSuite from '../nodeSuites/events.spec'; import impressionsSuite from '../nodeSuites/impressions.spec'; import impressionsSuiteDebug from '../nodeSuites/impressions.debug.spec'; +import impressionsSuiteNone from '../nodeSuites/impressions.none.spec'; import telemetrySuite from '../nodeSuites/telemetry.spec'; import impressionsListenerSuite from '../nodeSuites/impressions-listener.spec'; import expectedTreatmentsSuite from '../nodeSuites/expected-treatments.spec'; @@ -57,6 +58,7 @@ tape('## Node JS - E2E CI Tests ##', async function (assert) { /* Check impressions */ assert.test('E2E / Impressions', impressionsSuite.bind(null, key, fetchMock)); assert.test('E2E / Impressions Debug Mode', impressionsSuiteDebug.bind(null, key, fetchMock)); + assert.test('E2E / Impressions None Mode', impressionsSuiteNone.bind(null, key, fetchMock)); assert.test('E2E / Impressions listener', impressionsListenerSuite); /* Check telemetry */ diff --git a/src/__tests__/testUtils/index.js b/src/__tests__/testUtils/index.js index 302e9449d..271798202 100644 --- a/src/__tests__/testUtils/index.js +++ b/src/__tests__/testUtils/index.js @@ -43,7 +43,7 @@ export function hasNoCacheHeader(fetchMockOpts) { return fetchMockOpts.headers['Cache-Control'] === 'no-cache'; } -const telemetryEndpointMatcher = /^\/v1\/metrics\/(config|usage)/; +const telemetryEndpointMatcher = /^\/v1\/(metrics|keys)\/(config|usage|ss|cs)/; const eventsEndpointMatcher = /^\/(testImpressions|metrics|events)/; const authEndpointMatcher = /^\/v2\/auth/; const streamingEndpointMatcher = /^\/(sse|event-stream)/; diff --git a/src/platform/filter/__tests__/bloomFilter.spec.js b/src/platform/filter/__tests__/bloomFilter.spec.js index ee0ac743c..c6b6e0dbd 100644 --- a/src/platform/filter/__tests__/bloomFilter.spec.js +++ b/src/platform/filter/__tests__/bloomFilter.spec.js @@ -5,17 +5,17 @@ tape('Bloom filter', (assert) => { const bloomFilter = bloomFilterFactory(); - assert.true(bloomFilter.add('test1')); - assert.false(bloomFilter.contains('test2')); - assert.true(bloomFilter.contains('test1')); + assert.true(bloomFilter.add('feature','key')); + assert.false(bloomFilter.contains('feature1','key')); + assert.true(bloomFilter.contains('feature','key')); bloomFilter.clear(); - assert.false(bloomFilter.contains('test1')); + assert.false(bloomFilter.contains('feature','key')); - assert.true(bloomFilter.add('test3')); - assert.false(bloomFilter.contains('test4')); - assert.true(bloomFilter.contains('test3')); + assert.true(bloomFilter.add('feature2','key')); + assert.false(bloomFilter.contains('feature3','key')); + assert.true(bloomFilter.contains('feature2','key')); assert.end(); diff --git a/src/platform/filter/bloomFilter.js b/src/platform/filter/bloomFilter.js index 32c563b2e..bff7657b1 100644 --- a/src/platform/filter/bloomFilter.js +++ b/src/platform/filter/bloomFilter.js @@ -8,13 +8,17 @@ export function bloomFilterFactory(expectedInsertions = EXPECTED_INSERTIONS, err return { - add(data) { - if (filter.has(data)) return false; + add(key, value) { + const data = `${key}:${value}`; + if (filter.has(data)) { + return false; + } filter.add(data); return true; }, - contains(data) { + contains(key, value) { + const data = `${key}:${value}`; return filter.has(data); }, diff --git a/src/settings/defaults/version.js b/src/settings/defaults/version.js index f64e87413..de8d2483a 100644 --- a/src/settings/defaults/version.js +++ b/src/settings/defaults/version.js @@ -1 +1 @@ -export const packageVersion = '10.21.1'; +export const packageVersion = '10.21.1-rc.0'; diff --git a/types/splitio.d.ts b/types/splitio.d.ts index bba9086de..917dfbf6f 100644 --- a/types/splitio.d.ts +++ b/types/splitio.d.ts @@ -914,7 +914,7 @@ declare namespace SplitIO { * ImpressionsMode type * @typedef {string} ImpressionsMode */ - type ImpressionsMode = 'OPTIMIZED' | 'DEBUG'; + type ImpressionsMode = 'OPTIMIZED' | 'DEBUG' | 'NONE'; /** * User consent status. * @typedef {string} ConsentStatus From 9d9e6329bde5503db66afa1d04782913c6aa7d2c Mon Sep 17 00:00:00 2001 From: Emmanuel Zamora Date: Tue, 9 Aug 2022 16:12:34 -0300 Subject: [PATCH 2/2] Tests reading improvement, ci-cd update to push to development s3 --- .github/workflows/ci-cd.yml | 2 +- .../browserSuites/impressions.none.spec.js | 34 ++++++++-------- .../nodeSuites/impressions.none.spec.js | 40 +++++++++---------- 3 files changed, 37 insertions(+), 39 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index eb98398c7..0b315b22c 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -62,7 +62,7 @@ jobs: aws-region: us-east-1 - name: Upload to S3 (development) - if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/development' }} + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/SDKS-5521-MTK' }} run: aws s3 sync $SOURCE_DIR s3://$BUCKET/$DEST_DIR $ARGS env: BUCKET: split-public-stage diff --git a/src/__tests__/browserSuites/impressions.none.spec.js b/src/__tests__/browserSuites/impressions.none.spec.js index dccc8c1ad..ba938beb8 100644 --- a/src/__tests__/browserSuites/impressions.none.spec.js +++ b/src/__tests__/browserSuites/impressions.none.spec.js @@ -55,23 +55,20 @@ export default async function (fetchMock, assert) { fetchMock.postOnce(url(settings, '/v1/keys/cs'), (url, opts) => { const data = JSON.parse(opts.body); - assert.equal(data.keys.length, 2, 'We performed evaluations for three split, so we should have 3 item total.'); - - function validateImpressionData(output, expected) { - assert.equal(output.k, expected.key, 'Present impressions should have the correct featureName.'); - assert.deepEqual(output.fs, expected.features, 'Present impressions should have the correct key list.'); - } - - client.destroy().then(() => { - validateImpressionData(data.keys[0], { - key: 'facundo@split.io', - features:['split_with_config','always_off', 'always_on'] - }); - validateImpressionData(data.keys[1], { - key: 'emma@split.io', - features:['always_off', 'always_on'] - }); - + assert.deepEqual(data, { + keys: [ + { + k: 'facundo@split.io', + fs: ['split_with_config','always_off', 'always_on'] + }, + { + k: 'emma@split.io', + fs: ['always_off', 'always_on'] + } + ] + }, 'We performed evaluations for two keys, so we should have 2 item total.'); + + client.destroy().then(() => { assert.end(); }); @@ -87,6 +84,9 @@ export default async function (fetchMock, assert) { sharedClient.getTreatment('always_off'); client.getTreatment('always_off'); sharedClient.getTreatment('always_on'); + sharedClient.getTreatment('always_off'); client.getTreatment('always_on'); + client.getTreatment('always_off'); + client.getTreatment('split_with_config'); } diff --git a/src/__tests__/nodeSuites/impressions.none.spec.js b/src/__tests__/nodeSuites/impressions.none.spec.js index 9fb37bb17..d69c08c0c 100644 --- a/src/__tests__/nodeSuites/impressions.none.spec.js +++ b/src/__tests__/nodeSuites/impressions.none.spec.js @@ -27,7 +27,7 @@ const config = { segmentsRefreshRate: 1, impressionsRefreshRate: 3000, impressionsQueueSize: 3, // flush impressions when 3 are queued - uniqueKeysCacheSize: 7 // flush impressions when 3 are queued + uniqueKeysCacheSize: 7 // flush impressions when 7 are queued }, urls: baseUrls, startup: { @@ -52,29 +52,25 @@ export default async function (key, fetchMock, assert) { fetchMock.postOnce(url(settings, '/v1/keys/ss'), (url, opts) => { const data = JSON.parse(opts.body); - assert.equal(data.keys.length, 3, 'We performed evaluations for three split, so we should have 3 item total.'); - - function validateImpressionData(output, expected) { - assert.equal(output.f, expected.featureName, 'Present impressions should have the correct featureName.'); - assert.deepEqual(output.ks, expected.keys, 'Present impressions should have the correct key list.'); - } + assert.deepEqual(data, { + keys: [ + { + f: 'split_with_config', + ks:['emma@split.io','emi@split.io'] + }, + { + f: 'always_off', + ks:['emma@split.io','emi@split.io'] + }, + { + f: 'always_on', + ks:['emma@split.io','emi@split.io','nico@split.io'] + } + ] + }, 'We performed evaluations for three split, so we should have 3 item total.'); client.destroy().then(() => { - validateImpressionData(data.keys[0], { - featureName: 'split_with_config', - keys:['emma@split.io','emi@split.io'] - }); - validateImpressionData(data.keys[1], { - featureName: 'always_off', - keys:['emma@split.io','emi@split.io'] - }); - validateImpressionData(data.keys[2], { - featureName: 'always_on', - keys:['emma@split.io','emi@split.io','nico@split.io'] - }); - assert.end(); - }); return 200; @@ -89,8 +85,10 @@ export default async function (key, fetchMock, assert) { client.getTreatment('emma@split.io', 'always_on'); client.getTreatment('emi@split.io', 'always_on'); client.getTreatment('nico@split.io', 'always_on'); + client.getTreatment('emma@split.io', 'always_off'); client.getTreatment('emma@split.io', 'always_on'); client.getTreatment('emi@split.io', 'always_off'); + client.getTreatment('nico@split.io', 'always_on'); client.getTreatment('emi@split.io', 'split_with_config'); client.getTreatment('emma@split.io', 'split_with_config');