unleash-server
Version:
Unleash is an enterprise ready feature flag service. It provides different strategies for handling feature flags.
153 lines • 6.22 kB
JavaScript
import fc from 'fast-check';
import { ALL_OPERATORS } from '../lib/util/constants.js';
import { WeightType } from '../lib/types/model.js';
export const urlFriendlyString = () => fc
.array(fc.oneof(fc
.integer({ min: 0x30, max: 0x39 })
.map(String.fromCharCode), // numbers
fc
.integer({ min: 0x41, max: 0x5a })
.map(String.fromCharCode), // UPPERCASE LETTERS
fc
.integer({ min: 0x61, max: 0x7a })
.map(String.fromCharCode), // lowercase letters
fc.constantFrom('-', '_', '~', '.'), // rest
fc.lorem({ maxCount: 1 })), { minLength: 1 })
.map((arr) => arr.join(''))
// filter out strings that are only dots because they mess with url parsing
.filter((string) => ![...string].every((char) => char === '.'));
export const commonISOTimestamp = () => fc
.date({
min: new Date('1900-01-01T00:00:00.000Z'),
max: new Date('9999-12-31T23:59:59.999Z'),
noInvalidDate: true,
})
.map((timestamp) => timestamp.toISOString());
export const strategyConstraint = () => fc.record({
contextName: urlFriendlyString(),
operator: fc.constantFrom(...ALL_OPERATORS),
caseInsensitive: fc.boolean(),
inverted: fc.boolean(),
values: fc.array(fc.string(), { minLength: 1 }),
value: fc.string(),
});
const strategyConstraints = () => fc.array(strategyConstraint());
export const strategy = (name, parameters) => parameters
? fc.record({
name: fc.constant(name),
id: fc.uuid(),
parameters,
segments: fc.uniqueArray(fc.integer({ min: 1 })),
constraints: strategyConstraints(),
}, { requiredKeys: ['name', 'parameters', 'id'] })
: fc.record({
id: fc.uuid(),
name: fc.constant(name),
segments: fc.uniqueArray(fc.integer({ min: 1 })),
constraints: strategyConstraints(),
}, { requiredKeys: ['name', 'id'] });
export const segment = () => fc.record({
id: fc.integer({ min: 1 }),
name: urlFriendlyString(),
constraints: strategyConstraints(),
});
export const strategies = () => fc.uniqueArray(fc.oneof(strategy('default'), strategy('flexibleRollout', fc.record({
groupId: fc.lorem({ maxCount: 1 }),
rollout: fc.nat({ max: 100 }).map(String),
stickiness: fc.constantFrom('default', 'userId', 'sessionId'),
})), strategy('applicationHostname', fc.record({
hostNames: fc
.uniqueArray(fc.domain())
.map((domains) => domains.join(',')),
})), strategy('remoteAddress', fc.record({
IPs: fc.uniqueArray(fc.ipV4()).map((ips) => ips.join(',')),
})), strategy('custom-strategy', fc.record({
customParam: fc
.uniqueArray(fc.lorem())
.map((words) => words.join(',')),
}))), { selector: (generatedStrategy) => generatedStrategy.id });
export const variant = () => fc.record({
name: urlFriendlyString(),
weight: fc.nat({ max: 1000 }),
weightType: fc.constant(WeightType.VARIABLE),
stickiness: fc.constant('default'),
payload: fc.option(fc.oneof(fc.record({
type: fc.constant('json'),
value: fc.json(),
}), fc.record({
type: fc.constant('csv'),
value: fc
.array(fc.lorem())
.map((words) => words.join(',')),
}), fc.record({
type: fc.constant('string'),
value: fc.string(),
})), { nil: undefined }),
}, { requiredKeys: ['name', 'weight', 'weightType', 'stickiness'] });
export const variants = () => fc
.uniqueArray(variant(), {
maxLength: 1000,
selector: (variantInstance) => variantInstance.name,
})
.map((allVariants) => allVariants.map((variantInstance) => ({
...variantInstance,
weight: Math.round(1000 / allVariants.length),
})));
export const clientFeature = (name) => fc.record({
name: name ? fc.constant(name) : urlFriendlyString(),
type: fc.constantFrom('release', 'kill-switch', 'experiment', 'operational', 'permission'),
description: fc.lorem(),
project: urlFriendlyString(),
enabled: fc.boolean(),
createdAt: commonISOTimestamp(),
lastSeenAt: commonISOTimestamp(),
stale: fc.boolean(),
impressionData: fc.option(fc.boolean(), { nil: undefined }),
strategies: strategies(),
variants: variants(),
}, { requiredKeys: ['name', 'enabled', 'project', 'strategies'] });
export const clientFeatures = (constraints) => fc.uniqueArray(clientFeature(), {
...constraints,
selector: (v) => v.name,
});
export const clientFeaturesAndSegments = (featureConstraints) => {
const segments = () => fc.uniqueArray(segment(), {
selector: (generatedSegment) => generatedSegment.id,
});
// create segments and make sure that all strategies reference segments that
// exist
return fc
.tuple(segments(), clientFeatures(featureConstraints))
.map(([generatedSegments, generatedFeatures]) => {
const renumberedSegments = generatedSegments.map((generatedSegment, index) => ({
...generatedSegment,
id: index + 1,
}));
const features = generatedFeatures.map((feature) => ({
...feature,
...(feature.strategies && {
strategies: feature.strategies.map((generatedStrategy) => ({
...generatedStrategy,
...(generatedStrategy.segments && {
segments: renumberedSegments.length > 0
? [
...new Set(generatedStrategy.segments.map((generatedSegment) => (generatedSegment %
renumberedSegments.length) +
1)),
]
: [],
}),
})),
}),
}));
return {
features,
segments: renumberedSegments,
};
});
};
// TEST ARBITRARIES
test('url-friendly strings are URL-friendly', () => fc.assert(fc.property(urlFriendlyString(), (input) => /^[\w~.-]+$/.test(input))));
test('variant payloads are either present or undefined; never null', () => fc.assert(fc.property(variant(), (generatedVariant) => !!generatedVariant.payload ||
generatedVariant.payload === undefined)));
//# sourceMappingURL=arbitraries.test.js.map