mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-20 00:08:02 +01:00
444 lines
14 KiB
TypeScript
444 lines
14 KiB
TypeScript
import {
|
|
ClientInitOptions,
|
|
mapFeaturesForClient,
|
|
mapSegmentsForClient,
|
|
offlineUnleashClient,
|
|
} from './offline-unleash-client';
|
|
import {
|
|
Unleash as UnleashClientNode,
|
|
InMemStorageProvider as InMemStorageProviderNode,
|
|
} from 'unleash-client';
|
|
import { once } from 'events';
|
|
import { playgroundStrategyEvaluation } from '../../openapi/spec/playground-strategy-schema';
|
|
|
|
export const offlineUnleashClientNode = async ({
|
|
features,
|
|
context,
|
|
logError,
|
|
segments,
|
|
}: ClientInitOptions): Promise<UnleashClientNode> => {
|
|
const client = new UnleashClientNode({
|
|
...context,
|
|
appName: context.appName,
|
|
disableMetrics: true,
|
|
refreshInterval: 0,
|
|
skipInstanceCountWarning: true,
|
|
url: 'not-needed',
|
|
storageProvider: new InMemStorageProviderNode(),
|
|
bootstrap: {
|
|
data: mapFeaturesForClient(features),
|
|
segments: mapSegmentsForClient(segments),
|
|
},
|
|
});
|
|
|
|
client.on('error', logError);
|
|
client.start();
|
|
|
|
await once(client, 'ready');
|
|
|
|
return client;
|
|
};
|
|
|
|
describe('offline client', () => {
|
|
it('considers enabled variants with a default strategy to be on', async () => {
|
|
const name = 'toggle-name';
|
|
const client = await offlineUnleashClient({
|
|
features: [
|
|
{
|
|
name,
|
|
project: 'default',
|
|
enabled: true,
|
|
strategies: [{ name: 'default' }],
|
|
variants: [],
|
|
type: '',
|
|
stale: false,
|
|
},
|
|
],
|
|
context: { appName: 'other-app', environment: 'default' },
|
|
logError: console.log,
|
|
});
|
|
|
|
expect(client.isEnabled(name).result).toBeTruthy();
|
|
});
|
|
|
|
it('constrains on appName', async () => {
|
|
const enabledFeature = 'toggle-name';
|
|
const disabledFeature = 'other-toggle';
|
|
const appName = 'app-name';
|
|
const client = await offlineUnleashClient({
|
|
features: [
|
|
{
|
|
name: enabledFeature,
|
|
enabled: true,
|
|
project: 'default',
|
|
strategies: [
|
|
{
|
|
name: 'default',
|
|
constraints: [
|
|
{
|
|
contextName: 'appName',
|
|
operator: 'IN',
|
|
values: [appName],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
variants: [],
|
|
type: '',
|
|
stale: false,
|
|
},
|
|
{
|
|
name: disabledFeature,
|
|
enabled: true,
|
|
project: 'default',
|
|
strategies: [
|
|
{
|
|
name: 'default',
|
|
constraints: [
|
|
{
|
|
contextName: 'appName',
|
|
operator: 'IN',
|
|
values: ['otherApp'],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
variants: [],
|
|
type: '',
|
|
stale: false,
|
|
},
|
|
],
|
|
context: { appName, environment: 'default' },
|
|
logError: console.log,
|
|
});
|
|
|
|
expect(client.isEnabled(enabledFeature).result).toBeTruthy();
|
|
expect(client.isEnabled(disabledFeature).result).toBeFalsy();
|
|
});
|
|
|
|
it('considers disabled features with a default strategy to be enabled', async () => {
|
|
const name = 'toggle-name';
|
|
const context = { appName: 'client-test' };
|
|
const client = await offlineUnleashClient({
|
|
features: [
|
|
{
|
|
strategies: [
|
|
{
|
|
name: 'default',
|
|
},
|
|
],
|
|
project: 'default',
|
|
stale: false,
|
|
enabled: false,
|
|
name,
|
|
type: 'experiment',
|
|
variants: [],
|
|
},
|
|
],
|
|
context,
|
|
logError: console.log,
|
|
});
|
|
|
|
const result = client.isEnabled(name, context);
|
|
|
|
expect(result.result).toBe(true);
|
|
});
|
|
|
|
it('considers disabled variants with a default strategy and variants to be on', async () => {
|
|
const name = 'toggle-name';
|
|
const client = await offlineUnleashClient({
|
|
features: [
|
|
{
|
|
strategies: [
|
|
{
|
|
name: 'default',
|
|
},
|
|
],
|
|
project: 'default',
|
|
stale: false,
|
|
enabled: false,
|
|
name,
|
|
type: 'experiment',
|
|
variants: [
|
|
{
|
|
name: 'a',
|
|
weight: 500,
|
|
weightType: 'variable',
|
|
stickiness: 'default',
|
|
overrides: [],
|
|
},
|
|
{
|
|
name: 'b',
|
|
weight: 500,
|
|
weightType: 'variable',
|
|
stickiness: 'default',
|
|
overrides: [],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
context: { appName: 'client-test' },
|
|
logError: console.log,
|
|
});
|
|
|
|
expect(client.isEnabled(name).result).toBe(true);
|
|
});
|
|
|
|
it("returns variant {name: 'disabled', enabled: false } if the toggle isn't enabled", async () => {
|
|
const name = 'toggle-name';
|
|
const client = await offlineUnleashClient({
|
|
features: [
|
|
{
|
|
strategies: [],
|
|
stale: false,
|
|
enabled: false,
|
|
name,
|
|
project: 'default',
|
|
type: 'experiment',
|
|
variants: [
|
|
{
|
|
name: 'a',
|
|
weight: 500,
|
|
weightType: 'variable',
|
|
stickiness: 'default',
|
|
overrides: [],
|
|
},
|
|
{
|
|
name: 'b',
|
|
weight: 500,
|
|
weightType: 'variable',
|
|
stickiness: 'default',
|
|
overrides: [],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
context: { appName: 'client-test' },
|
|
logError: console.log,
|
|
});
|
|
|
|
expect(client.isEnabled(name).result).toBeFalsy();
|
|
expect(client.getVariant(name).name).toEqual('disabled');
|
|
expect(client.getVariant(name).enabled).toBeFalsy();
|
|
});
|
|
|
|
it('returns the disabled variant if there are no variants', async () => {
|
|
const name = 'toggle-name';
|
|
const client = await offlineUnleashClient({
|
|
features: [
|
|
{
|
|
strategies: [
|
|
{
|
|
name: 'default',
|
|
constraints: [],
|
|
},
|
|
],
|
|
project: 'default',
|
|
stale: false,
|
|
enabled: true,
|
|
name,
|
|
type: 'experiment',
|
|
variants: [],
|
|
},
|
|
],
|
|
context: { appName: 'client-test' },
|
|
logError: console.log,
|
|
});
|
|
|
|
expect(client.getVariant(name, {}).name).toEqual('disabled');
|
|
expect(client.getVariant(name, {}).enabled).toBeFalsy();
|
|
expect(client.isEnabled(name, {}).result).toBeTruthy();
|
|
});
|
|
|
|
it(`returns '${playgroundStrategyEvaluation.unknownResult}' if it can't evaluate a feature`, async () => {
|
|
const name = 'toggle-name';
|
|
const context = { appName: 'client-test' };
|
|
|
|
const client = await offlineUnleashClient({
|
|
features: [
|
|
{
|
|
strategies: [
|
|
{
|
|
name: 'unimplemented-custom-strategy',
|
|
constraints: [],
|
|
},
|
|
],
|
|
project: 'default',
|
|
stale: false,
|
|
enabled: true,
|
|
name,
|
|
type: 'experiment',
|
|
variants: [],
|
|
},
|
|
],
|
|
context,
|
|
logError: console.log,
|
|
});
|
|
|
|
const result = client.isEnabled(name, context);
|
|
|
|
result.strategies.forEach((strategy) =>
|
|
expect(strategy.result.enabled).toEqual(
|
|
playgroundStrategyEvaluation.unknownResult,
|
|
),
|
|
);
|
|
expect(result.result).toEqual(
|
|
playgroundStrategyEvaluation.unknownResult,
|
|
);
|
|
});
|
|
|
|
it(`returns '${playgroundStrategyEvaluation.unknownResult}' for the application hostname strategy`, async () => {
|
|
const name = 'toggle-name';
|
|
const context = { appName: 'client-test' };
|
|
|
|
const client = await offlineUnleashClient({
|
|
features: [
|
|
{
|
|
strategies: [
|
|
{
|
|
name: 'applicationHostname',
|
|
constraints: [],
|
|
},
|
|
],
|
|
project: 'default',
|
|
stale: false,
|
|
enabled: true,
|
|
name,
|
|
type: 'experiment',
|
|
variants: [],
|
|
},
|
|
],
|
|
context,
|
|
logError: console.log,
|
|
});
|
|
|
|
const result = client.isEnabled(name, context);
|
|
|
|
result.strategies.forEach((strategy) =>
|
|
expect(strategy.result.enabled).toEqual(
|
|
playgroundStrategyEvaluation.unknownResult,
|
|
),
|
|
);
|
|
expect(result.result).toEqual(
|
|
playgroundStrategyEvaluation.unknownResult,
|
|
);
|
|
});
|
|
|
|
it('returns strategies in the order they are provided', async () => {
|
|
const featureName = 'featureName';
|
|
const strategies = [
|
|
{
|
|
name: 'default',
|
|
constraints: [],
|
|
parameters: {},
|
|
},
|
|
{
|
|
name: 'default',
|
|
constraints: [
|
|
{
|
|
values: ['my-app-name'],
|
|
inverted: false,
|
|
operator: 'IN' as 'IN',
|
|
contextName: 'appName',
|
|
caseInsensitive: false,
|
|
},
|
|
],
|
|
parameters: {},
|
|
},
|
|
{
|
|
name: 'applicationHostname',
|
|
constraints: [],
|
|
parameters: {
|
|
hostNames: 'myhostname.com',
|
|
},
|
|
},
|
|
{
|
|
name: 'flexibleRollout',
|
|
constraints: [],
|
|
parameters: {
|
|
groupId: 'killer',
|
|
rollout: '34',
|
|
stickiness: 'userId',
|
|
},
|
|
},
|
|
{
|
|
name: 'userWithId',
|
|
constraints: [],
|
|
parameters: {
|
|
userIds: 'uoea,ueoa',
|
|
},
|
|
},
|
|
{
|
|
name: 'remoteAddress',
|
|
constraints: [],
|
|
parameters: {
|
|
IPs: '196.6.6.05',
|
|
},
|
|
},
|
|
];
|
|
|
|
const context = { appName: 'client-test' };
|
|
|
|
const client = await offlineUnleashClient({
|
|
features: [
|
|
{
|
|
strategies,
|
|
// impressionData: false,
|
|
enabled: true,
|
|
name: featureName,
|
|
project: 'default',
|
|
// description: '',
|
|
// project: 'heartman-for-test',
|
|
stale: false,
|
|
type: 'kill-switch',
|
|
variants: [
|
|
{
|
|
name: 'a',
|
|
weight: 334,
|
|
weightType: 'variable',
|
|
stickiness: 'default',
|
|
overrides: [],
|
|
payload: {
|
|
type: 'json',
|
|
value: '{"hello": "world"}',
|
|
},
|
|
},
|
|
{
|
|
name: 'b',
|
|
weight: 333,
|
|
weightType: 'variable',
|
|
stickiness: 'default',
|
|
overrides: [],
|
|
payload: {
|
|
type: 'string',
|
|
value: 'ueoau',
|
|
},
|
|
},
|
|
{
|
|
name: 'c',
|
|
weight: 333,
|
|
weightType: 'variable',
|
|
stickiness: 'default',
|
|
payload: {
|
|
type: 'csv',
|
|
value: '1,2,3',
|
|
},
|
|
overrides: [],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
context,
|
|
logError: console.log,
|
|
});
|
|
|
|
const evaluatedStrategies = client
|
|
.isEnabled(featureName, context)
|
|
.strategies.map((strategy) => strategy.name);
|
|
|
|
expect(evaluatedStrategies).toEqual(
|
|
strategies.map((strategy) => strategy.name),
|
|
);
|
|
});
|
|
});
|