flagsmith-nodejs
Version:
Flagsmith lets you manage features flags and remote config across web, mobile and server side applications. Deliver true Continuous Integration. Get builds out faster. Control who has access to new features.
223 lines (207 loc) • 9.7 kB
text/typescript
import { SegmentSource } from '../../../../flagsmith-engine/evaluation/models';
import { EvaluationContext } from '../../../../flagsmith-engine/evaluationContext/evaluationContext.types';
import { CONSTANTS } from '../../../../flagsmith-engine/features/constants';
import {
ALL_RULE,
ANY_RULE,
CONDITION_OPERATORS,
NONE_RULE
} from '../../../../flagsmith-engine/segments/constants';
import {
all,
any,
SegmentConditionModel,
SegmentModel,
SegmentRuleModel
} from '../../../../flagsmith-engine/segments/models';
const conditionMatchCases: [string, string | number | boolean | null, string, boolean][] = [
[CONDITION_OPERATORS.EQUAL, 'bar', 'bar', true],
[CONDITION_OPERATORS.EQUAL, 'bar', 'baz', false],
[CONDITION_OPERATORS.EQUAL, 1, '1', true],
[CONDITION_OPERATORS.EQUAL, 1, '2', false],
[CONDITION_OPERATORS.EQUAL, true, 'true', true],
[CONDITION_OPERATORS.EQUAL, false, 'false', true],
[CONDITION_OPERATORS.EQUAL, false, 'true', false],
[CONDITION_OPERATORS.EQUAL, true, 'false', false],
[CONDITION_OPERATORS.EQUAL, 1.23, '1.23', true],
[CONDITION_OPERATORS.EQUAL, 1.23, '4.56', false],
[CONDITION_OPERATORS.GREATER_THAN, 2, '1', true],
[CONDITION_OPERATORS.GREATER_THAN, 1, '1', false],
[CONDITION_OPERATORS.GREATER_THAN, 0, '1', false],
[CONDITION_OPERATORS.GREATER_THAN, 2.1, '2.0', true],
[CONDITION_OPERATORS.GREATER_THAN, 2.1, '2.1', false],
[CONDITION_OPERATORS.GREATER_THAN, 2.0, '2.1', false],
[CONDITION_OPERATORS.GREATER_THAN_INCLUSIVE, 2, '1', true],
[CONDITION_OPERATORS.GREATER_THAN_INCLUSIVE, 1, '1', true],
[CONDITION_OPERATORS.GREATER_THAN_INCLUSIVE, 0, '1', false],
[CONDITION_OPERATORS.GREATER_THAN_INCLUSIVE, 2.1, '2.0', true],
[CONDITION_OPERATORS.GREATER_THAN_INCLUSIVE, 2.1, '2.1', true],
[CONDITION_OPERATORS.GREATER_THAN_INCLUSIVE, 2.0, '2.1', false],
[CONDITION_OPERATORS.LESS_THAN, 1, '2', true],
[CONDITION_OPERATORS.LESS_THAN, 1, '1', false],
[CONDITION_OPERATORS.LESS_THAN, 1, '0', false],
[CONDITION_OPERATORS.LESS_THAN, 2.0, '2.1', true],
[CONDITION_OPERATORS.LESS_THAN, 2.1, '2.1', false],
[CONDITION_OPERATORS.LESS_THAN, 2.1, '2.0', false],
[CONDITION_OPERATORS.LESS_THAN_INCLUSIVE, 1, '2', true],
[CONDITION_OPERATORS.LESS_THAN_INCLUSIVE, 1, '1', true],
[CONDITION_OPERATORS.LESS_THAN_INCLUSIVE, 1, '0', false],
[CONDITION_OPERATORS.LESS_THAN_INCLUSIVE, 2.0, '2.1', true],
[CONDITION_OPERATORS.LESS_THAN_INCLUSIVE, 2.1, '2.1', true],
[CONDITION_OPERATORS.LESS_THAN_INCLUSIVE, 2.1, '2.0', false],
[CONDITION_OPERATORS.NOT_EQUAL, 'bar', 'baz', true],
[CONDITION_OPERATORS.NOT_EQUAL, 'bar', 'bar', false],
[CONDITION_OPERATORS.NOT_EQUAL, 1, '2', true],
[CONDITION_OPERATORS.NOT_EQUAL, 1, '1', false],
[CONDITION_OPERATORS.NOT_EQUAL, true, 'false', true],
[CONDITION_OPERATORS.NOT_EQUAL, false, 'true', true],
[CONDITION_OPERATORS.NOT_EQUAL, false, 'false', false],
[CONDITION_OPERATORS.NOT_EQUAL, true, 'true', false],
[CONDITION_OPERATORS.CONTAINS, 'bar', 'b', true],
[CONDITION_OPERATORS.CONTAINS, 'bar', 'bar', true],
[CONDITION_OPERATORS.CONTAINS, 'bar', 'baz', false],
[CONDITION_OPERATORS.CONTAINS, null, 'foo', false],
[CONDITION_OPERATORS.NOT_CONTAINS, 'bar', 'b', false],
[CONDITION_OPERATORS.NOT_CONTAINS, 'bar', 'bar', false],
[CONDITION_OPERATORS.NOT_CONTAINS, 'bar', 'baz', true],
[CONDITION_OPERATORS.NOT_CONTAINS, null, 'foo', false],
[CONDITION_OPERATORS.REGEX, 'foo', '[a-z]+', true],
[CONDITION_OPERATORS.REGEX, 'FOO', '[a-z]+', false],
[CONDITION_OPERATORS.REGEX, null, '[a-z]+', false],
[CONDITION_OPERATORS.EQUAL, '1.0.0', '1.0.0:semver', true],
[CONDITION_OPERATORS.EQUAL, '1.0.0', '1.0.0:semver', true],
[CONDITION_OPERATORS.EQUAL, '1.0.0', '1.0.1:semver', false],
[CONDITION_OPERATORS.NOT_EQUAL, '1.0.0', '1.0.0:semver', false],
[CONDITION_OPERATORS.NOT_EQUAL, '1.0.0', '1.0.1:semver', true],
[CONDITION_OPERATORS.GREATER_THAN, '1.0.1', '1.0.0:semver', true],
[CONDITION_OPERATORS.GREATER_THAN, '1.0.0', '1.0.0-beta:semver', true],
[CONDITION_OPERATORS.GREATER_THAN, '1.0.1', '1.2.0:semver', false],
[CONDITION_OPERATORS.GREATER_THAN, '1.0.1', '1.0.1:semver', false],
[CONDITION_OPERATORS.GREATER_THAN, '1.2.4', '1.2.3-pre.2+build.4:semver', true],
[CONDITION_OPERATORS.LESS_THAN, '1.0.0', '1.0.1:semver', true],
[CONDITION_OPERATORS.LESS_THAN, '1.0.0', '1.0.0:semver', false],
[CONDITION_OPERATORS.LESS_THAN, '1.0.1', '1.0.0:semver', false],
[CONDITION_OPERATORS.LESS_THAN, '1.0.0-rc.2', '1.0.0-rc.3:semver', true],
[CONDITION_OPERATORS.GREATER_THAN_INCLUSIVE, '1.0.1', '1.0.0:semver', true],
[CONDITION_OPERATORS.GREATER_THAN_INCLUSIVE, '1.0.1', '1.2.0:semver', false],
[CONDITION_OPERATORS.GREATER_THAN_INCLUSIVE, '1.0.1', '1.0.1:semver', true],
[CONDITION_OPERATORS.LESS_THAN_INCLUSIVE, '1.0.0', '1.0.1:semver', true],
[CONDITION_OPERATORS.LESS_THAN_INCLUSIVE, '1.0.0', '1.0.0:semver', true],
[CONDITION_OPERATORS.LESS_THAN_INCLUSIVE, '1.0.1', '1.0.0:semver', false],
[CONDITION_OPERATORS.MODULO, 1, '2|0', false],
[CONDITION_OPERATORS.MODULO, 2, '2|0', true],
[CONDITION_OPERATORS.MODULO, 3, '2|0', false],
[CONDITION_OPERATORS.MODULO, 34.2, '4|3', false],
[CONDITION_OPERATORS.MODULO, 35.0, '4|3', true],
[CONDITION_OPERATORS.MODULO, 'foo', '4|3', false],
[CONDITION_OPERATORS.MODULO, 35.0, 'foo|bar', false],
[CONDITION_OPERATORS.IN, 'foo', '', false],
[CONDITION_OPERATORS.IN, 'foo', 'foo, bar', true],
[CONDITION_OPERATORS.IN, 'foo', 'foo', true],
[CONDITION_OPERATORS.IN, 1, '1,2,3,4', true],
[CONDITION_OPERATORS.IN, 1, '', false],
[CONDITION_OPERATORS.IN, 1, '1', true],
['BAD_OP', 'a', 'a', false]
];
test('test_segment_condition_matches_trait_value', () => {
for (const testCase of conditionMatchCases) {
const [operator, traitValue, conditionValue, expectedResult] = testCase;
expect(
new SegmentConditionModel(operator, conditionValue, 'foo').matchesTraitValue(traitValue)
).toBe(expectedResult);
}
});
test('test_segment_rule_none', () => {
const testCases: [boolean[], boolean][] = [
[[], true],
[[false], true],
[[false, false], true],
[[false, true], false],
[[true, true], false]
];
for (const testCase of testCases) {
expect(SegmentRuleModel.none(testCase[0])).toBe(testCase[1]);
}
});
test('test_segment_rule_matching_function', () => {
const testCases: [string, CallableFunction][] = [
[ALL_RULE, all],
[ANY_RULE, any],
[NONE_RULE, SegmentRuleModel.none]
];
for (const testCase of testCases) {
expect(new SegmentRuleModel(testCase[0]).matchingFunction()).toBe(testCase[1]);
}
});
test('test_fromSegmentResult_with_multiple_variants', () => {
const segmentResults = [{ name: 'test_segment', metadata: { id: '1' } }];
const evaluationContext: EvaluationContext = {
identity: {
key: 'not_exist',
identifier: 'not_exist'
},
environment: {
key: 'test',
name: 'test'
},
features: {},
segments: {
'1': {
key: '1',
name: 'test_segment',
metadata: {
source: SegmentSource.API,
id: '1'
},
rules: [
{
type: 'ALL',
conditions: [
{
property: '$.identity.identifier',
operator: 'EQUAL',
value: 'test-user'
}
]
}
],
overrides: [
{
key: 'override',
name: 'multivariate_feature',
enabled: true,
value: 'default_value',
priority: 1,
metadata: {
id: 1
},
variants: [
{ id: 1, value: 'variant_a', weight: 30 },
{ id: 2, value: 'variant_b', weight: 70 }
]
}
]
}
}
};
const result = SegmentModel.fromSegmentResult(segmentResults, evaluationContext);
expect(result).toHaveLength(1);
const segment = result[0];
expect(segment.name).toBe('test_segment');
expect(segment.featureStates).toHaveLength(1);
const featureState = segment.featureStates[0];
expect(featureState.feature.name).toBe('multivariate_feature');
expect(featureState.feature.type).toBe(CONSTANTS.MULTIVARIATE);
expect(featureState.enabled).toBe(true);
expect(featureState.getValue()).toBe('default_value');
// Test multivariate variants
expect(featureState.multivariateFeatureStateValues).toHaveLength(2);
const variant1 = featureState.multivariateFeatureStateValues[0];
expect(variant1.multivariateFeatureOption.value).toBe('variant_a');
expect(variant1.percentageAllocation).toBe(30);
expect(variant1.id).toBe(1);
const variant2 = featureState.multivariateFeatureStateValues[1];
expect(variant2.multivariateFeatureOption.value).toBe('variant_b');
expect(variant2.percentageAllocation).toBe(70);
expect(variant2.id).toBe(2);
});