UNPKG

unleash-server

Version:

Unleash is an enterprise ready feature flag service. It provides different strategies for handling feature flags.

153 lines • 6.22 kB
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