Skip to content

Commit 0e6b1ee

Browse files
authored
Merge pull request #161 from splitio/sdks-7444
Flag sets
2 parents a88e058 + 8978288 commit 0e6b1ee

24 files changed

Lines changed: 1739 additions & 113 deletions

.github/workflows/unstable.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,12 @@ jobs:
2222
- name: Create build version
2323
run: echo "BUILD_VERSION=$(cat package.json | grep version | head -1 | awk '{ print $2 }' | sed 's/[\",]//g' | tr -d '[[:space:]]')" >> $GITHUB_ENV
2424

25+
- name: Get short hash
26+
run: echo "SHORT_SHA=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
27+
2528
- name: Docker Build
2629
uses: docker/build-push-action@v5
2730
with:
2831
context: .
2932
push: true
30-
tags: splitio-docker-dev.jfrog.io/${{ github.event.repository.name }}:${{ env.BUILD_VERSION}}
33+
tags: splitio-docker-dev.jfrog.io/${{ github.event.repository.name }}:${{ env.SHORT_SHA}}

.jest/setEnvVars.js

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
// Environments for testing
2-
process.env.SPLIT_EVALUATOR_ENVIRONMENTS='[{"API_KEY":"localhost","AUTH_TOKEN":"test"},{"API_KEY":"apikey1","AUTH_TOKEN":"key_blue"},{"API_KEY":"apikey2","AUTH_TOKEN":"key_red"}]'
2+
process.env.SPLIT_EVALUATOR_ENVIRONMENTS = `[
3+
{"API_KEY":"localhost","AUTH_TOKEN":"test"},
4+
{"API_KEY":"apikey1","AUTH_TOKEN":"key_blue"},
5+
{"API_KEY":"apikey2","AUTH_TOKEN":"key_red"},
6+
{"API_KEY":"apikey3","AUTH_TOKEN":"key_green","FLAG_SET_FILTER":"set_green"},
7+
{"API_KEY":"apikey4","AUTH_TOKEN":"key_purple","FLAG_SET_FILTER":"set_purple"},
8+
{"API_KEY":"apikey5","AUTH_TOKEN":"key_pink","FLAG_SET_FILTER":"set_green,set_purple"}
9+
]`;
310

411
// Before all tests, sdk module is mocked to create a wrapper where a different yaml file is assigned to each environment
512
// sdk factory mock to set a different yaml for each apikey and localhost mode
@@ -33,6 +40,13 @@ jest.mock('../sdk', () => ({
3340
...settings.core,
3441
authorizationKey: authorizationKey,
3542
},
43+
urls: {
44+
sdk: 'https://sdk.test.io/api',
45+
events: 'https://events.test.io/api',
46+
auth: 'https://auth.test.io/api',
47+
streaming: 'https://streaming.test.io',
48+
telemetry: 'https://telemetry.test.io/api',
49+
},
3650
startup: {
3751
readyTimeout: 1,
3852
},
@@ -47,7 +61,7 @@ jest.mock('../sdk', () => ({
4761
};
4862

4963
let sdk = jest.requireActual('../sdk');
50-
const { factory, telemetry, impressionsMode } = sdk.getSplitFactory(configForMock);
64+
const { factory, impressionsMode } = sdk.getSplitFactory(configForMock);
5165

5266
const mockedTelemetry = {
5367
splits: {

admin/__tests__/stats.test.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ describe('stats', () => {
7878
const authToken = environment.AUTH_TOKEN;
7979
const apiKey = environment.API_KEY;
8080
const mock = apiKeyMocksMap[apiKey];
81+
if (!mock) return;
8182
expect(stats.environments[utils.obfuscate(authToken)]).toEqual({
8283
splitCount: mock.splitNames.length,
8384
segmentCount: mock.segments.length,
Lines changed: 301 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
1+
const request = require('supertest');
2+
const app = require('../../app');
3+
const { expectError, expectErrorContaining, expectOkMultipleResults, getLongKey } = require('../../utils/testWrapper');
4+
const { NULL_FLAG_SETS, EMPTY_FLAG_SETS } = require('../../utils/constants');
5+
const { expectedGreenResults, expectedPurpleResults, expectedPinkResults } = require('../../utils/mocks');
6+
7+
jest.mock('node-fetch', () => {
8+
return jest.fn().mockImplementation((url) => {
9+
10+
const sdkUrl = 'https://sdk.test.io/api/splitChanges?since=-1';
11+
const splitChange2 = require('../../utils/mocks/splitchanges.since.-1.till.1602796638344.json');
12+
if (url.startsWith(sdkUrl)) return Promise.resolve({ status: 200, json: () => (splitChange2), ok: true });
13+
14+
return Promise.resolve({ status: 200, json: () => ({}), ok: true });
15+
});
16+
});
17+
18+
describe('get-treatments-by-sets', () => {
19+
20+
beforeEach(() => {
21+
jest.resetModules();
22+
jest.clearAllMocks();
23+
});
24+
25+
afterAll(() => {
26+
// Unmock fetch
27+
jest.unmock('node-fetch');
28+
});
29+
30+
// Testing authorization
31+
test('should be 401 if auth is not passed', async (done) => {
32+
const response = await request(app)
33+
.get('/client/get-treatments-by-sets?key=test&flag-sets=my-experiment');
34+
expectError(response, 401, 'Unauthorized');
35+
done();
36+
});
37+
38+
test('should be 401 if auth does not match', async (done) => {
39+
const response = await request(app)
40+
.get('/client/get-treatments-by-sets?key=test&flag-sets=my-experiment')
41+
.set('Authorization', 'invalid');
42+
expectError(response, 401, 'Unauthorized');
43+
done();
44+
});
45+
46+
// Testing Input Validation.
47+
// The following tests are going to check null parameters, wrong types or lengths.
48+
test('should be 400 if key is not passed', async (done) => {
49+
const expected = [
50+
'you passed a null or undefined key, key must be a non-empty string.'
51+
];
52+
const response = await request(app)
53+
.get('/client/get-treatments-by-sets?flag-sets=test')
54+
.set('Authorization', 'key_green');
55+
expectErrorContaining(response, 400, expected);
56+
done();
57+
});
58+
59+
test('should be 400 if key is empty', async (done) => {
60+
const expected = [
61+
'you passed an empty string, key must be a non-empty string.'
62+
];
63+
const response = await request(app)
64+
.get('/client/get-treatments-by-sets?key=&flag-sets=test')
65+
.set('Authorization', 'key_green');
66+
expectErrorContaining(response, 400, expected);
67+
done();
68+
});
69+
70+
test('should be 400 if key is empty trimmed', async (done) => {
71+
const expected = [
72+
'you passed an empty string, key must be a non-empty string.'
73+
];
74+
const response = await request(app)
75+
.get('/client/get-treatments-by-sets?key= &flag-sets=test')
76+
.set('Authorization', 'key_green');
77+
expectErrorContaining(response, 400, expected);
78+
done();
79+
});
80+
81+
test('should be 400 if key is too long', async (done) => {
82+
const expected = [
83+
'key too long, key must be 250 characters or less.'
84+
];
85+
let key = '';
86+
for (let i = 0; i <=250; i++) {
87+
key += 'a';
88+
}
89+
const response = await request(app)
90+
.get(`/client/get-treatments-by-sets?key=${key}&flag-sets=test`)
91+
.set('Authorization', 'key_green');
92+
expectErrorContaining(response, 400, expected);
93+
done();
94+
});
95+
96+
test('should be 400 if bucketing-key is empty', async (done) => {
97+
const expected = [
98+
'you passed an empty string, bucketing-key must be a non-empty string.'
99+
];
100+
const response = await request(app)
101+
.get('/client/get-treatments-by-sets?key=key&bucketing-key=&flag-sets=test')
102+
.set('Authorization', 'key_green');
103+
expectErrorContaining(response, 400, expected);
104+
done();
105+
});
106+
107+
test('should be 400 if bucketing-key is empty trimmed', async (done) => {
108+
const expected = [
109+
'you passed an empty string, bucketing-key must be a non-empty string.'
110+
];
111+
const response = await request(app)
112+
.get('/client/get-treatments-by-sets?key=key&bucketing-key= &flag-sets=test')
113+
.set('Authorization', 'key_green');
114+
expectErrorContaining(response, 400, expected);
115+
done();
116+
});
117+
118+
test('should be 400 if bucketing-key is too long', async (done) => {
119+
const expected = [
120+
'bucketing-key too long, bucketing-key must be 250 characters or less.'
121+
];
122+
let key = '';
123+
for (let i = 0; i <=250; i++) {
124+
key += 'a';
125+
}
126+
const response = await request(app)
127+
.get(`/client/get-treatments-by-sets?key=key&bucketing-key=${key}&flag-sets=test`)
128+
.set('Authorization', 'key_green');
129+
expectErrorContaining(response, 400, expected);
130+
done();
131+
});
132+
133+
test('should be 400 if flag-sets is not passed', async (done) => {
134+
const expected = [
135+
NULL_FLAG_SETS
136+
];
137+
const response = await request(app)
138+
.get('/client/get-treatments-by-sets?key=test')
139+
.set('Authorization', 'key_green');
140+
expectErrorContaining(response, 400, expected);
141+
done();
142+
});
143+
144+
test('should be 400 if flag-sets is empty', async (done) => {
145+
const expected = [
146+
EMPTY_FLAG_SETS
147+
];
148+
const response = await request(app)
149+
.get('/client/get-treatments-by-sets?key=test&flag-sets=')
150+
.set('Authorization', 'key_green');
151+
expectErrorContaining(response, 400, expected);
152+
done();
153+
});
154+
155+
test('should be 400 if flag-sets is empty trimmed', async (done) => {
156+
const expected = [
157+
EMPTY_FLAG_SETS
158+
];
159+
const response = await request(app)
160+
.get('/client/get-treatments-by-sets?key=test&flag-sets= ')
161+
.set('Authorization', 'key_green');
162+
expectErrorContaining(response, 400, expected);
163+
done();
164+
});
165+
166+
test('should be 400 if there are errors in key and flag-sets', async (done) => {
167+
const expected = [
168+
'you passed an empty string, key must be a non-empty string.',
169+
EMPTY_FLAG_SETS
170+
];
171+
const response = await request(app)
172+
.get('/client/get-treatments-by-sets?key=&flag-sets= ')
173+
.set('Authorization', 'key_green');
174+
expectErrorContaining(response, 400, expected);
175+
done();
176+
});
177+
178+
test('should be 400 if attributes is invalid', async (done) => {
179+
const expected = [
180+
'attributes must be a plain object.'
181+
];
182+
const response = await request(app)
183+
.get('/client/get-treatments-by-sets?key=test&flag-sets=my-experiment&attributes=lalala')
184+
.set('Authorization', 'key_green');
185+
expectErrorContaining(response, 400, expected);
186+
done();
187+
});
188+
189+
test('should be 400 if there are multiple errors', async (done) => {
190+
const expected = [
191+
'you passed an empty string, key must be a non-empty string.',
192+
EMPTY_FLAG_SETS,
193+
'attributes must be a plain object.'
194+
];
195+
const response = await request(app)
196+
.get('/client/get-treatments-by-sets?key= &flag-sets=&attributes="lalala"')
197+
.set('Authorization', 'key_green');
198+
expectErrorContaining(response, 400, expected);
199+
done();
200+
});
201+
202+
test('should be 400 if there are multiple errors in every input', async (done) => {
203+
const expected = [
204+
'you passed an empty string, key must be a non-empty string.',
205+
EMPTY_FLAG_SETS,
206+
'attributes must be a plain object.',
207+
'bucketing-key too long, bucketing-key must be 250 characters or less.'
208+
];
209+
const key = getLongKey();
210+
const response = await request(app)
211+
.get(`/client/get-treatments-by-sets?bucketing-key=${key}&key= &flag-sets=&attributes="lalala"`)
212+
.set('Authorization', 'key_green');
213+
expectErrorContaining(response, 400, expected);
214+
done();
215+
});
216+
217+
test('should be 400 if attributes is an invalid json (POST)', async (done) => {
218+
const response = await request(app)
219+
.post('/client/get-treatments-by-sets?key=test&flag-sets=my-experiment')
220+
.set('Content-Type', 'application/json')
221+
// eslint-disable-next-line no-useless-escape
222+
.send('\|\\\"/regex/i') // Syntax error parsing the JSON.
223+
.set('Authorization', 'key_green');
224+
expectError(response, 400);
225+
expect(response.body.error.type).toBe('entity.parse.failed'); // validate the error
226+
done();
227+
});
228+
229+
test('should be 200 if is valid attributes (GET)', async (done) => {
230+
const response = await request(app)
231+
.get('/client/get-treatments-by-sets?key=key_green&flag-sets=set_green&attributes={"test":"test"}')
232+
.set('Authorization', 'key_green');
233+
expectOkMultipleResults(response, 200, expectedGreenResults, 3);
234+
done();
235+
});
236+
237+
test('should be 200 when attributes is null (GET)', async (done) => {
238+
const response = await request(app)
239+
.get('/client/get-treatments-by-sets?key=key_green&flag-sets=set_green')
240+
.set('Authorization', 'key_green');
241+
expectOkMultipleResults(response, 200, expectedGreenResults, 3);
242+
done();
243+
});
244+
245+
test('should be 200 if is valid attributes (POST)', async (done) => {
246+
const response = await request(app)
247+
.post('/client/get-treatments-by-sets?key=key_green&flag-sets=set_green')
248+
.set('Authorization', 'key_green')
249+
.send({
250+
attributes: {test:'test'},
251+
});
252+
expectOkMultipleResults(response, 200, expectedGreenResults, 3);
253+
done();
254+
});
255+
256+
test('should be 200 if is valid attributes as string (POST)', async (done) => {
257+
const response = await request(app)
258+
.post('/client/get-treatments-by-sets?key=key_green&flag-sets=set_green')
259+
.send(JSON.stringify({
260+
attributes: {test:'test'},
261+
}))
262+
.set('Authorization', 'key_green');
263+
expectOkMultipleResults(response, 200, expectedGreenResults, 3);
264+
done();
265+
});
266+
267+
test('should be 200 if attributes is null (POST)', async (done) => {
268+
const response = await request(app)
269+
.post('/client/get-treatments-by-sets?key=key_green&flag-sets=set_green')
270+
.send({
271+
attributes: null,
272+
})
273+
.set('Authorization', 'key_green');
274+
expectOkMultipleResults(response, 200, expectedGreenResults, 3);
275+
done();
276+
});
277+
278+
test('should be 200 with multiple evaluation but evualuate configured flag sets', async (done) => {
279+
const response = await request(app)
280+
.get('/client/get-treatments-by-sets?key=key_green&flag-sets=set_green,set_purple,nonexistant-experiment')
281+
.set('Authorization', 'key_green');
282+
expectOkMultipleResults(response, 200, expectedGreenResults, 3);
283+
done();
284+
});
285+
286+
test('should be 200 with multiple evaluation but evualuate configured flag sets', async (done) => {
287+
const response = await request(app)
288+
.get('/client/get-treatments-by-sets?key=key_purple&flag-sets=set_green,set_purple,nonexistant-experiment')
289+
.set('Authorization', 'key_purple');
290+
expectOkMultipleResults(response, 200, expectedPurpleResults, 3);
291+
done();
292+
});
293+
294+
test('should be 200 with multiple evaluation but evualuate configured flag sets', async (done) => {
295+
const response = await request(app)
296+
.get('/client/get-treatments-by-sets?key=key_purple&flag-sets=set_green,set_purple,nonexistant-experiment')
297+
.set('Authorization', 'key_pink');
298+
expectOkMultipleResults(response, 200, expectedPinkResults, 5);
299+
done();
300+
});
301+
});

0 commit comments

Comments
 (0)