flagsmith-nodejs 6.0.0 → 6.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/publish.yml +17 -17
- package/.github/workflows/pull_request.yaml +33 -33
- package/.husky/pre-commit +0 -0
- package/.prettierignore +2 -1
- package/README.md +2 -1
- package/build/cjs/flagsmith-engine/features/util.js +3 -3
- package/build/cjs/flagsmith-engine/index.d.ts +1 -1
- package/build/cjs/flagsmith-engine/index.js +2 -1
- package/build/cjs/flagsmith-engine/segments/models.js +7 -7
- package/build/cjs/flagsmith-engine/utils/hashing/index.js +1 -1
- package/build/cjs/index.d.ts +4 -4
- package/build/cjs/index.js +3 -1
- package/build/cjs/sdk/analytics.d.ts +1 -1
- package/build/cjs/sdk/index.d.ts +5 -5
- package/build/cjs/sdk/index.js +7 -5
- package/build/cjs/sdk/models.d.ts +32 -5
- package/build/cjs/sdk/models.js +25 -0
- package/build/cjs/sdk/types.d.ts +14 -4
- package/build/cjs/sdk/utils.d.ts +4 -4
- package/build/cjs/sdk/utils.js +2 -2
- package/build/esm/flagsmith-engine/features/models.js +1 -1
- package/build/esm/flagsmith-engine/features/util.js +3 -3
- package/build/esm/flagsmith-engine/index.d.ts +1 -1
- package/build/esm/flagsmith-engine/index.js +1 -1
- package/build/esm/flagsmith-engine/segments/models.js +7 -7
- package/build/esm/flagsmith-engine/utils/hashing/index.js +2 -2
- package/build/esm/flagsmith-engine/utils/index.js +1 -1
- package/build/esm/index.d.ts +4 -4
- package/build/esm/index.js +3 -3
- package/build/esm/sdk/analytics.d.ts +1 -1
- package/build/esm/sdk/index.d.ts +5 -5
- package/build/esm/sdk/index.js +6 -5
- package/build/esm/sdk/models.d.ts +32 -5
- package/build/esm/sdk/models.js +25 -0
- package/build/esm/sdk/types.d.ts +14 -4
- package/build/esm/sdk/utils.d.ts +4 -4
- package/build/esm/sdk/utils.js +2 -2
- package/flagsmith-engine/environments/util.ts +2 -2
- package/flagsmith-engine/features/models.ts +1 -1
- package/flagsmith-engine/features/util.ts +14 -14
- package/flagsmith-engine/identities/models.ts +1 -1
- package/flagsmith-engine/index.ts +1 -1
- package/flagsmith-engine/segments/evaluators.ts +2 -3
- package/flagsmith-engine/segments/models.ts +25 -15
- package/flagsmith-engine/utils/hashing/index.ts +3 -3
- package/flagsmith-engine/utils/index.ts +4 -2
- package/index.ts +19 -22
- package/package.json +2 -2
- package/sdk/analytics.ts +7 -5
- package/sdk/index.ts +38 -21
- package/sdk/models.ts +34 -12
- package/sdk/offline_handlers.ts +1 -1
- package/sdk/types.ts +17 -8
- package/sdk/utils.ts +8 -8
- package/tests/engine/e2e/engine.test.ts +2 -4
- package/tests/engine/unit/engine.test.ts +1 -5
- package/tests/engine/unit/features/models.test.ts +2 -2
- package/tests/engine/unit/identities/identities_builders.test.ts +1 -1
- package/tests/engine/unit/segments/segment_evaluators.test.ts +52 -23
- package/tests/engine/unit/segments/segments_model.test.ts +35 -37
- package/tests/engine/unit/utils/utils.test.ts +28 -30
- package/tests/sdk/analytics.test.ts +25 -26
- package/tests/sdk/flagsmith-cache.test.ts +84 -76
- package/tests/sdk/flagsmith-environment-flags.test.ts +93 -93
- package/tests/sdk/flagsmith-identity-flags.test.ts +146 -149
- package/tests/sdk/flagsmith.test.ts +40 -42
- package/tests/sdk/offline-handlers.test.ts +32 -32
- package/tests/sdk/polling.test.ts +0 -1
- package/tests/sdk/utils.ts +26 -18
- package/vitest.config.ts +10 -15
|
@@ -65,49 +65,47 @@ const conditionMatchCases: [string, string | number | boolean | null, string, bo
|
|
|
65
65
|
[CONDITION_OPERATORS.REGEX, 'foo', '[a-z]+', true],
|
|
66
66
|
[CONDITION_OPERATORS.REGEX, 'FOO', '[a-z]+', false],
|
|
67
67
|
[CONDITION_OPERATORS.REGEX, null, '[a-z]+', false],
|
|
68
|
-
[CONDITION_OPERATORS.EQUAL,
|
|
69
|
-
[CONDITION_OPERATORS.EQUAL,
|
|
70
|
-
[CONDITION_OPERATORS.EQUAL,
|
|
71
|
-
[CONDITION_OPERATORS.NOT_EQUAL,
|
|
72
|
-
[CONDITION_OPERATORS.NOT_EQUAL,
|
|
73
|
-
[CONDITION_OPERATORS.GREATER_THAN,
|
|
74
|
-
[CONDITION_OPERATORS.GREATER_THAN,
|
|
75
|
-
[CONDITION_OPERATORS.GREATER_THAN,
|
|
76
|
-
[CONDITION_OPERATORS.GREATER_THAN,
|
|
77
|
-
[CONDITION_OPERATORS.GREATER_THAN,
|
|
78
|
-
[CONDITION_OPERATORS.LESS_THAN,
|
|
79
|
-
[CONDITION_OPERATORS.LESS_THAN,
|
|
80
|
-
[CONDITION_OPERATORS.LESS_THAN,
|
|
81
|
-
[CONDITION_OPERATORS.LESS_THAN,
|
|
82
|
-
[CONDITION_OPERATORS.GREATER_THAN_INCLUSIVE,
|
|
83
|
-
[CONDITION_OPERATORS.GREATER_THAN_INCLUSIVE,
|
|
84
|
-
[CONDITION_OPERATORS.GREATER_THAN_INCLUSIVE,
|
|
85
|
-
[CONDITION_OPERATORS.LESS_THAN_INCLUSIVE,
|
|
86
|
-
[CONDITION_OPERATORS.LESS_THAN_INCLUSIVE,
|
|
87
|
-
[CONDITION_OPERATORS.LESS_THAN_INCLUSIVE,
|
|
88
|
-
[CONDITION_OPERATORS.MODULO, 1,
|
|
89
|
-
[CONDITION_OPERATORS.MODULO, 2,
|
|
90
|
-
[CONDITION_OPERATORS.MODULO, 3,
|
|
91
|
-
[CONDITION_OPERATORS.MODULO, 34.2,
|
|
92
|
-
[CONDITION_OPERATORS.MODULO, 35.0,
|
|
93
|
-
[CONDITION_OPERATORS.MODULO,
|
|
94
|
-
[CONDITION_OPERATORS.MODULO, 35.0,
|
|
95
|
-
[CONDITION_OPERATORS.IN,
|
|
96
|
-
[CONDITION_OPERATORS.IN,
|
|
97
|
-
[CONDITION_OPERATORS.IN,
|
|
98
|
-
[CONDITION_OPERATORS.IN, 1,
|
|
99
|
-
[CONDITION_OPERATORS.IN, 1,
|
|
100
|
-
[CONDITION_OPERATORS.IN, 1,
|
|
68
|
+
[CONDITION_OPERATORS.EQUAL, '1.0.0', '1.0.0:semver', true],
|
|
69
|
+
[CONDITION_OPERATORS.EQUAL, '1.0.0', '1.0.0:semver', true],
|
|
70
|
+
[CONDITION_OPERATORS.EQUAL, '1.0.0', '1.0.1:semver', false],
|
|
71
|
+
[CONDITION_OPERATORS.NOT_EQUAL, '1.0.0', '1.0.0:semver', false],
|
|
72
|
+
[CONDITION_OPERATORS.NOT_EQUAL, '1.0.0', '1.0.1:semver', true],
|
|
73
|
+
[CONDITION_OPERATORS.GREATER_THAN, '1.0.1', '1.0.0:semver', true],
|
|
74
|
+
[CONDITION_OPERATORS.GREATER_THAN, '1.0.0', '1.0.0-beta:semver', true],
|
|
75
|
+
[CONDITION_OPERATORS.GREATER_THAN, '1.0.1', '1.2.0:semver', false],
|
|
76
|
+
[CONDITION_OPERATORS.GREATER_THAN, '1.0.1', '1.0.1:semver', false],
|
|
77
|
+
[CONDITION_OPERATORS.GREATER_THAN, '1.2.4', '1.2.3-pre.2+build.4:semver', true],
|
|
78
|
+
[CONDITION_OPERATORS.LESS_THAN, '1.0.0', '1.0.1:semver', true],
|
|
79
|
+
[CONDITION_OPERATORS.LESS_THAN, '1.0.0', '1.0.0:semver', false],
|
|
80
|
+
[CONDITION_OPERATORS.LESS_THAN, '1.0.1', '1.0.0:semver', false],
|
|
81
|
+
[CONDITION_OPERATORS.LESS_THAN, '1.0.0-rc.2', '1.0.0-rc.3:semver', true],
|
|
82
|
+
[CONDITION_OPERATORS.GREATER_THAN_INCLUSIVE, '1.0.1', '1.0.0:semver', true],
|
|
83
|
+
[CONDITION_OPERATORS.GREATER_THAN_INCLUSIVE, '1.0.1', '1.2.0:semver', false],
|
|
84
|
+
[CONDITION_OPERATORS.GREATER_THAN_INCLUSIVE, '1.0.1', '1.0.1:semver', true],
|
|
85
|
+
[CONDITION_OPERATORS.LESS_THAN_INCLUSIVE, '1.0.0', '1.0.1:semver', true],
|
|
86
|
+
[CONDITION_OPERATORS.LESS_THAN_INCLUSIVE, '1.0.0', '1.0.0:semver', true],
|
|
87
|
+
[CONDITION_OPERATORS.LESS_THAN_INCLUSIVE, '1.0.1', '1.0.0:semver', false],
|
|
88
|
+
[CONDITION_OPERATORS.MODULO, 1, '2|0', false],
|
|
89
|
+
[CONDITION_OPERATORS.MODULO, 2, '2|0', true],
|
|
90
|
+
[CONDITION_OPERATORS.MODULO, 3, '2|0', false],
|
|
91
|
+
[CONDITION_OPERATORS.MODULO, 34.2, '4|3', false],
|
|
92
|
+
[CONDITION_OPERATORS.MODULO, 35.0, '4|3', true],
|
|
93
|
+
[CONDITION_OPERATORS.MODULO, 'foo', '4|3', false],
|
|
94
|
+
[CONDITION_OPERATORS.MODULO, 35.0, 'foo|bar', false],
|
|
95
|
+
[CONDITION_OPERATORS.IN, 'foo', '', false],
|
|
96
|
+
[CONDITION_OPERATORS.IN, 'foo', 'foo, bar', true],
|
|
97
|
+
[CONDITION_OPERATORS.IN, 'foo', 'foo', true],
|
|
98
|
+
[CONDITION_OPERATORS.IN, 1, '1,2,3,4', true],
|
|
99
|
+
[CONDITION_OPERATORS.IN, 1, '', false],
|
|
100
|
+
[CONDITION_OPERATORS.IN, 1, '1', true],
|
|
101
101
|
['BAD_OP', 'a', 'a', false]
|
|
102
102
|
];
|
|
103
103
|
|
|
104
104
|
test('test_segment_condition_matches_trait_value', () => {
|
|
105
105
|
for (const testCase of conditionMatchCases) {
|
|
106
|
-
const [operator, traitValue, conditionValue, expectedResult] = testCase
|
|
106
|
+
const [operator, traitValue, conditionValue, expectedResult] = testCase;
|
|
107
107
|
expect(
|
|
108
|
-
new SegmentConditionModel(operator, conditionValue, 'foo').matchesTraitValue(
|
|
109
|
-
traitValue
|
|
110
|
-
)
|
|
108
|
+
new SegmentConditionModel(operator, conditionValue, 'foo').matchesTraitValue(traitValue)
|
|
111
109
|
).toBe(expectedResult);
|
|
112
110
|
}
|
|
113
111
|
});
|
|
@@ -2,60 +2,58 @@ import { randomUUID as uuidv4 } from 'node:crypto';
|
|
|
2
2
|
import { getHashedPercentateForObjIds } from '../../../../flagsmith-engine/utils/hashing/index.js';
|
|
3
3
|
|
|
4
4
|
describe('getHashedPercentageForObjIds', () => {
|
|
5
|
-
it.each([
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
let resultOne = getHashedPercentateForObjIds(objIds);
|
|
23
|
-
let resultTwo = getHashedPercentateForObjIds(objIds);
|
|
24
|
-
expect(resultOne).toEqual(resultTwo);
|
|
25
|
-
})
|
|
5
|
+
it.each([[[12, 93]], [[uuidv4(), 99]], [[99, uuidv4()]], [[uuidv4(), uuidv4()]]])(
|
|
6
|
+
'returns x where 0 <= x < 100',
|
|
7
|
+
(objIds: (string | number)[]) => {
|
|
8
|
+
let result = getHashedPercentateForObjIds(objIds);
|
|
9
|
+
expect(result).toBeLessThan(100);
|
|
10
|
+
expect(result).toBeGreaterThanOrEqual(0);
|
|
11
|
+
}
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
it.each([[[12, 93]], [[uuidv4(), 99]], [[99, uuidv4()]], [[uuidv4(), uuidv4()]]])(
|
|
15
|
+
'returns the same value each time',
|
|
16
|
+
(objIds: (string | number)[]) => {
|
|
17
|
+
let resultOne = getHashedPercentateForObjIds(objIds);
|
|
18
|
+
let resultTwo = getHashedPercentateForObjIds(objIds);
|
|
19
|
+
expect(resultOne).toEqual(resultTwo);
|
|
20
|
+
}
|
|
21
|
+
);
|
|
26
22
|
|
|
27
23
|
it('is unique for different object ids', () => {
|
|
28
24
|
let resultOne = getHashedPercentateForObjIds([14, 106]);
|
|
29
25
|
let resultTwo = getHashedPercentateForObjIds([53, 200]);
|
|
30
26
|
expect(resultOne).not.toEqual(resultTwo);
|
|
31
|
-
})
|
|
27
|
+
});
|
|
32
28
|
|
|
33
29
|
it('is evenly distributed', () => {
|
|
34
30
|
// copied from python test here:
|
|
35
31
|
// https://github.com/Flagsmith/flagsmith-engine/blob/main/tests/unit/utils/test_utils_hashing.py#L56
|
|
36
32
|
const testSample = 500;
|
|
37
33
|
const numTestBuckets = 50;
|
|
38
|
-
const testBucketSize = Math.floor(testSample / numTestBuckets)
|
|
39
|
-
const errorFactor = 0.1
|
|
34
|
+
const testBucketSize = Math.floor(testSample / numTestBuckets);
|
|
35
|
+
const errorFactor = 0.1;
|
|
40
36
|
|
|
41
37
|
// Given
|
|
42
|
-
let objectIdPairs = Array.from(Array(testSample).keys()).flatMap(d =>
|
|
38
|
+
let objectIdPairs = Array.from(Array(testSample).keys()).flatMap(d =>
|
|
39
|
+
Array.from(Array(testSample).keys()).map(e => [d, e].flat())
|
|
40
|
+
);
|
|
43
41
|
|
|
44
42
|
// When
|
|
45
|
-
let values = objectIdPairs.map(
|
|
43
|
+
let values = objectIdPairs.map(objIds => getHashedPercentateForObjIds(objIds));
|
|
46
44
|
|
|
47
45
|
// Then
|
|
48
46
|
for (let i = 0; i++; i < numTestBuckets) {
|
|
49
47
|
let bucketStart = i * testBucketSize;
|
|
50
48
|
let bucketEnd = (i + 1) * testBucketSize;
|
|
51
49
|
let bucketValueLimit = Math.min(
|
|
52
|
-
(i + 1) / numTestBuckets + errorFactor + (
|
|
50
|
+
(i + 1) / numTestBuckets + errorFactor + (i + 1) / numTestBuckets,
|
|
53
51
|
1
|
|
54
|
-
)
|
|
52
|
+
);
|
|
55
53
|
|
|
56
54
|
for (let i = bucketStart; i++; i < bucketEnd) {
|
|
57
55
|
expect(values[i]).toBeLessThanOrEqual(bucketValueLimit);
|
|
58
56
|
}
|
|
59
57
|
}
|
|
60
|
-
})
|
|
61
|
-
})
|
|
58
|
+
});
|
|
59
|
+
});
|
|
@@ -1,40 +1,43 @@
|
|
|
1
|
-
import {analyticsProcessor, fetch} from './utils.js';
|
|
1
|
+
import { analyticsProcessor, fetch } from './utils.js';
|
|
2
2
|
|
|
3
3
|
test('test_analytics_processor_track_feature_updates_analytics_data', () => {
|
|
4
4
|
const aP = analyticsProcessor();
|
|
5
|
-
aP.trackFeature(
|
|
6
|
-
expect(aP.analyticsData[
|
|
5
|
+
aP.trackFeature('myFeature');
|
|
6
|
+
expect(aP.analyticsData['myFeature']).toBe(1);
|
|
7
7
|
|
|
8
|
-
aP.trackFeature(
|
|
9
|
-
expect(aP.analyticsData[
|
|
8
|
+
aP.trackFeature('myFeature');
|
|
9
|
+
expect(aP.analyticsData['myFeature']).toBe(2);
|
|
10
10
|
});
|
|
11
11
|
|
|
12
12
|
test('test_analytics_processor_flush_clears_analytics_data', async () => {
|
|
13
13
|
const aP = analyticsProcessor();
|
|
14
|
-
aP.trackFeature(
|
|
14
|
+
aP.trackFeature('myFeature');
|
|
15
15
|
await aP.flush();
|
|
16
16
|
expect(aP.analyticsData).toStrictEqual({});
|
|
17
17
|
});
|
|
18
18
|
|
|
19
19
|
test('test_analytics_processor_flush_post_request_data_match_ananlytics_data', async () => {
|
|
20
20
|
const aP = analyticsProcessor();
|
|
21
|
-
aP.trackFeature(
|
|
22
|
-
aP.trackFeature(
|
|
21
|
+
aP.trackFeature('myFeature1');
|
|
22
|
+
aP.trackFeature('myFeature2');
|
|
23
23
|
await aP.flush();
|
|
24
24
|
expect(fetch).toHaveBeenCalledTimes(1);
|
|
25
|
-
expect(fetch).toHaveBeenCalledWith(
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
expect(fetch).toHaveBeenCalledWith(
|
|
26
|
+
'http://testUrl/analytics/flags/',
|
|
27
|
+
expect.objectContaining({
|
|
28
|
+
body: '{"myFeature1":1,"myFeature2":1}',
|
|
29
|
+
headers: { 'Content-Type': 'application/json', 'X-Environment-Key': 'test-key' },
|
|
30
|
+
method: 'POST'
|
|
31
|
+
})
|
|
32
|
+
);
|
|
30
33
|
});
|
|
31
34
|
|
|
32
|
-
vi.useFakeTimers()
|
|
35
|
+
vi.useFakeTimers();
|
|
33
36
|
test('test_analytics_processor_flush_post_request_data_match_ananlytics_data_test', async () => {
|
|
34
37
|
const aP = analyticsProcessor();
|
|
35
|
-
aP.trackFeature(
|
|
38
|
+
aP.trackFeature('myFeature1');
|
|
36
39
|
setTimeout(() => {
|
|
37
|
-
aP.trackFeature(
|
|
40
|
+
aP.trackFeature('myFeature2');
|
|
38
41
|
expect(fetch).toHaveBeenCalledTimes(1);
|
|
39
42
|
}, 15000);
|
|
40
43
|
vi.runOnlyPendingTimers();
|
|
@@ -46,7 +49,6 @@ test('test_analytics_processor_flush_early_exit_if_analytics_data_is_empty', asy
|
|
|
46
49
|
expect(fetch).not.toHaveBeenCalled();
|
|
47
50
|
});
|
|
48
51
|
|
|
49
|
-
|
|
50
52
|
test('errors in fetch sending analytics data are swallowed', async () => {
|
|
51
53
|
// Given
|
|
52
54
|
// we mock the fetch function to throw and error to mimick a network failure
|
|
@@ -63,21 +65,18 @@ test('errors in fetch sending analytics data are swallowed', async () => {
|
|
|
63
65
|
// Then
|
|
64
66
|
// we expect that fetch was called but the exception was handled
|
|
65
67
|
expect(fetch).toHaveBeenCalled();
|
|
66
|
-
})
|
|
68
|
+
});
|
|
67
69
|
|
|
68
70
|
test('analytics is only flushed once even if requested concurrently', async () => {
|
|
69
71
|
const processor = analyticsProcessor();
|
|
70
72
|
processor.trackFeature('myFeature');
|
|
71
73
|
fetch.mockImplementation(() => {
|
|
72
74
|
return new Promise((resolve, _) => {
|
|
73
|
-
setTimeout(resolve, 1000)
|
|
74
|
-
})
|
|
75
|
+
setTimeout(resolve, 1000);
|
|
76
|
+
});
|
|
75
77
|
});
|
|
76
|
-
const flushes = Promise.all([
|
|
77
|
-
processor.flush(),
|
|
78
|
-
processor.flush(),
|
|
79
|
-
])
|
|
78
|
+
const flushes = Promise.all([processor.flush(), processor.flush()]);
|
|
80
79
|
vi.runOnlyPendingTimers();
|
|
81
80
|
await flushes;
|
|
82
|
-
expect(fetch).toHaveBeenCalledTimes(1)
|
|
83
|
-
})
|
|
81
|
+
expect(fetch).toHaveBeenCalledTimes(1);
|
|
82
|
+
});
|
|
@@ -1,113 +1,121 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
fetch,
|
|
3
|
+
environmentJSON,
|
|
4
|
+
environmentModel,
|
|
5
|
+
flagsJSON,
|
|
6
|
+
flagsmith,
|
|
7
|
+
identitiesJSON,
|
|
8
|
+
TestCache
|
|
9
|
+
} from './utils.js';
|
|
2
10
|
|
|
3
11
|
test('test_empty_cache_not_read_but_populated', async () => {
|
|
4
|
-
|
|
5
|
-
|
|
12
|
+
const cache = new TestCache();
|
|
13
|
+
const set = vi.spyOn(cache, 'set');
|
|
6
14
|
|
|
7
|
-
|
|
8
|
-
|
|
15
|
+
const flg = flagsmith({ cache });
|
|
16
|
+
const allFlags = (await flg.getEnvironmentFlags()).allFlags();
|
|
9
17
|
|
|
10
|
-
|
|
11
|
-
|
|
18
|
+
expect(set).toBeCalled();
|
|
19
|
+
expect(await cache.get('flags')).toBeTruthy();
|
|
12
20
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
21
|
+
expect(fetch).toBeCalledTimes(1);
|
|
22
|
+
expect(allFlags[0].enabled).toBe(true);
|
|
23
|
+
expect(allFlags[0].value).toBe('some-value');
|
|
24
|
+
expect(allFlags[0].featureName).toBe('some_feature');
|
|
17
25
|
});
|
|
18
26
|
|
|
19
27
|
test('test_api_not_called_when_cache_present', async () => {
|
|
20
|
-
|
|
21
|
-
|
|
28
|
+
const cache = new TestCache();
|
|
29
|
+
const set = vi.spyOn(cache, 'set');
|
|
22
30
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
31
|
+
const flg = flagsmith({ cache });
|
|
32
|
+
await (await flg.getEnvironmentFlags()).allFlags();
|
|
33
|
+
const allFlags = await (await flg.getEnvironmentFlags()).allFlags();
|
|
26
34
|
|
|
27
|
-
|
|
28
|
-
|
|
35
|
+
expect(set).toBeCalled();
|
|
36
|
+
expect(await cache.get('flags')).toBeTruthy();
|
|
29
37
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
38
|
+
expect(fetch).toBeCalledTimes(1);
|
|
39
|
+
expect(allFlags[0].enabled).toBe(true);
|
|
40
|
+
expect(allFlags[0].value).toBe('some-value');
|
|
41
|
+
expect(allFlags[0].featureName).toBe('some_feature');
|
|
34
42
|
});
|
|
35
43
|
|
|
36
44
|
test('test_api_called_twice_when_no_cache', async () => {
|
|
37
|
-
|
|
45
|
+
fetch.mockImplementation(() => Promise.resolve(new Response(flagsJSON)));
|
|
38
46
|
|
|
39
|
-
|
|
40
|
-
|
|
47
|
+
const flg = flagsmith();
|
|
48
|
+
await (await flg.getEnvironmentFlags()).allFlags();
|
|
41
49
|
|
|
42
|
-
|
|
50
|
+
const allFlags = await (await flg.getEnvironmentFlags()).allFlags();
|
|
43
51
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
52
|
+
expect(fetch).toBeCalledTimes(2);
|
|
53
|
+
expect(allFlags[0].enabled).toBe(true);
|
|
54
|
+
expect(allFlags[0].value).toBe('some-value');
|
|
55
|
+
expect(allFlags[0].featureName).toBe('some_feature');
|
|
48
56
|
});
|
|
49
57
|
|
|
50
58
|
test('test_get_environment_flags_uses_local_environment_when_available', async () => {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
59
|
+
const cache = new TestCache();
|
|
60
|
+
const set = vi.spyOn(cache, 'set');
|
|
61
|
+
|
|
62
|
+
const flg = flagsmith({ cache, environmentKey: 'ser.key', enableLocalEvaluation: true });
|
|
63
|
+
const model = environmentModel(JSON.parse(environmentJSON));
|
|
64
|
+
const getEnvironment = vi.spyOn(flg, 'getEnvironment');
|
|
65
|
+
getEnvironment.mockResolvedValue(model);
|
|
66
|
+
|
|
67
|
+
const allFlags = (await flg.getEnvironmentFlags()).allFlags();
|
|
68
|
+
|
|
69
|
+
expect(set).toBeCalled();
|
|
70
|
+
expect(fetch).toBeCalledTimes(0);
|
|
71
|
+
expect(getEnvironment).toBeCalledTimes(1);
|
|
72
|
+
expect(allFlags[0].enabled).toBe(model.featureStates[0].enabled);
|
|
73
|
+
expect(allFlags[0].value).toBe(model.featureStates[0].getValue());
|
|
74
|
+
expect(allFlags[0].featureName).toBe(model.featureStates[0].feature.name);
|
|
67
75
|
});
|
|
68
76
|
|
|
69
77
|
test('test_cache_used_for_identity_flags', async () => {
|
|
70
|
-
|
|
71
|
-
|
|
78
|
+
const cache = new TestCache();
|
|
79
|
+
const set = vi.spyOn(cache, 'set');
|
|
72
80
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
81
|
+
const identifier = 'identifier';
|
|
82
|
+
const traits = { some_trait: 'some_value' };
|
|
83
|
+
const flg = flagsmith({ cache });
|
|
76
84
|
|
|
77
|
-
|
|
78
|
-
|
|
85
|
+
(await flg.getIdentityFlags(identifier, traits)).allFlags();
|
|
86
|
+
const identityFlags = (await flg.getIdentityFlags(identifier, traits)).allFlags();
|
|
79
87
|
|
|
80
|
-
|
|
81
|
-
|
|
88
|
+
expect(set).toBeCalled();
|
|
89
|
+
expect(await cache.get('flags-identifier')).toBeTruthy();
|
|
82
90
|
|
|
83
|
-
|
|
91
|
+
expect(fetch).toBeCalledTimes(1);
|
|
84
92
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
93
|
+
expect(identityFlags[0].enabled).toBe(true);
|
|
94
|
+
expect(identityFlags[0].value).toBe('some-value');
|
|
95
|
+
expect(identityFlags[0].featureName).toBe('some_feature');
|
|
88
96
|
});
|
|
89
97
|
|
|
90
98
|
test('test_cache_used_for_identity_flags_local_evaluation', async () => {
|
|
91
|
-
|
|
92
|
-
|
|
99
|
+
const cache = new TestCache();
|
|
100
|
+
const set = vi.spyOn(cache, 'set');
|
|
93
101
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
102
|
+
const identifier = 'identifier';
|
|
103
|
+
const traits = { some_trait: 'some_value' };
|
|
104
|
+
const flg = flagsmith({
|
|
105
|
+
cache,
|
|
106
|
+
environmentKey: 'ser.key',
|
|
107
|
+
enableLocalEvaluation: true
|
|
108
|
+
});
|
|
101
109
|
|
|
102
|
-
|
|
103
|
-
|
|
110
|
+
(await flg.getIdentityFlags(identifier, traits)).allFlags();
|
|
111
|
+
const identityFlags = (await flg.getIdentityFlags(identifier, traits)).allFlags();
|
|
104
112
|
|
|
105
|
-
|
|
106
|
-
|
|
113
|
+
expect(set).toBeCalled();
|
|
114
|
+
expect(await cache.get('flags-identifier')).toBeTruthy();
|
|
107
115
|
|
|
108
|
-
|
|
116
|
+
expect(fetch).toBeCalledTimes(1);
|
|
109
117
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
118
|
+
expect(identityFlags[0].enabled).toBe(true);
|
|
119
|
+
expect(identityFlags[0].value).toBe('some-value');
|
|
120
|
+
expect(identityFlags[0].featureName).toBe('some_feature');
|
|
113
121
|
});
|