1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-04-06 01:15:28 +02:00
unleash.unleash/src/lib/features/playground/advanced-playground.test.ts
2024-08-05 10:39:21 +02:00

435 lines
15 KiB
TypeScript

import {
type IUnleashTest,
setupAppWithCustomConfig,
} from '../../../test/e2e/helpers/test-helper';
import dbInit, { type ITestDb } from '../../../test/e2e/helpers/database-init';
import getLogger from '../../../test/fixtures/no-logger';
import type { AdvancedPlaygroundResponseSchema } from '../../openapi';
let app: IUnleashTest;
let db: ITestDb;
beforeAll(async () => {
db = await dbInit('advanced_playground', getLogger);
app = await setupAppWithCustomConfig(
db.stores,
{
experimental: {
flags: {
advancedPlayground: true,
strictSchemaValidation: true,
strategyVariant: true,
},
},
},
db.rawDatabase,
);
});
const createFeatureToggle = async (featureName: string) => {
await app.request
.post('/api/admin/projects/default/features')
.send({
name: featureName,
})
.set('Content-Type', 'application/json')
.expect(201);
};
const createFeatureToggleWithStrategy = async (
featureName: string,
strategy = {
name: 'default',
parameters: {},
} as any,
) => {
await createFeatureToggle(featureName);
return app.request
.post(
`/api/admin/projects/default/features/${featureName}/environments/default/strategies`,
)
.send(strategy)
.expect(200);
};
const enableToggle = (featureName: string) =>
app.request
.post(
`/api/admin/projects/default/features/${featureName}/environments/default/on`,
)
.send({})
.expect(200);
const disableToggle = (featureName: string) =>
app.request
.post(
`/api/admin/projects/default/features/${featureName}/environments/default/off`,
)
.send({})
.expect(200);
afterAll(async () => {
await app.destroy();
await db.destroy();
});
afterEach(async () => {
await db.stores.dependentFeaturesStore.deleteAll();
await db.stores.featureToggleStore.deleteAll();
});
test('advanced playground evaluation with no toggles', async () => {
const { body: result } = await app.request
.post('/api/admin/playground/advanced')
.send({
environments: ['default'],
projects: ['default'],
context: { appName: 'test', userId: '1,2', channel: 'web,mobile' },
})
.set('Content-Type', 'application/json')
.expect(200);
expect(result).toMatchObject({
input: {
environments: ['default'],
projects: ['default'],
context: {
appName: 'test',
userId: '1,2',
channel: 'web,mobile',
},
},
features: [],
});
});
test('advanced playground evaluation with unsatisfied parent dependency', async () => {
await createFeatureToggle('test-parent');
await createFeatureToggle('test-child');
await enableToggle('test-child');
await enableToggle('test-parent');
await disableToggle('test-parent');
await app.addDependency('test-child', 'test-parent');
const { body: result } = await app.request
.post('/api/admin/playground/advanced')
.send({
environments: ['default'],
projects: ['default'],
context: { appName: 'test' },
})
.set('Content-Type', 'application/json')
.expect(200);
const child = result.features[0].environments.default[0];
const parent = result.features[1].environments.default[0];
// child is disabled because of the parent
expect(child.hasUnsatisfiedDependency).toBe(true);
expect(child.isEnabled).toBe(false);
expect(child.strategies.data.length).toBe(1);
expect(child.isEnabledInCurrentEnvironment).toBe(true);
expect(child.variant).toEqual({
name: 'disabled',
enabled: false,
feature_enabled: false,
});
expect(parent.hasUnsatisfiedDependency).toBe(false);
expect(parent.isEnabled).toBe(false);
expect(parent.strategies.data.length).toBe(1);
});
test('advanced playground evaluation with satisfied disabled parent dependency', async () => {
await createFeatureToggle('test-parent');
await createFeatureToggle('test-child');
await enableToggle('test-child');
await app.addDependency('test-child', {
feature: 'test-parent',
enabled: false,
variants: [],
});
const { body: result } = await app.request
.post('/api/admin/playground/advanced')
.send({
environments: ['default'],
projects: ['default'],
context: { appName: 'test' },
})
.set('Content-Type', 'application/json')
.expect(200);
const child = result.features[0].environments.default[0];
const parent = result.features[1].environments.default[0];
expect(child.hasUnsatisfiedDependency).toBe(false);
expect(child.isEnabled).toBe(true);
expect(parent.hasUnsatisfiedDependency).toBe(false);
expect(parent.isEnabled).toBe(false);
});
test('advanced playground evaluation happy path', async () => {
await createFeatureToggleWithStrategy('test-playground-feature');
await enableToggle('test-playground-feature');
const { body: result } = await app.request
.post('/api/admin/playground/advanced')
.send({
environments: ['default'],
projects: ['default'],
context: { appName: 'test', userId: '1,2', channel: 'web,mobile' },
})
.set('Content-Type', 'application/json')
.expect(200);
expect(result).toMatchObject({
input: {
environments: ['default'],
projects: ['default'],
context: {
appName: 'test',
userId: '1,2',
channel: 'web,mobile',
},
},
features: [
{
name: 'test-playground-feature',
projectId: 'default',
environments: {
default: [
{
isEnabled: true,
isEnabledInCurrentEnvironment: true,
hasUnsatisfiedDependency: false,
strategies: {
result: true,
data: [
{
name: 'default',
disabled: false,
parameters: {},
result: {
enabled: true,
evaluationStatus: 'complete',
},
constraints: [],
segments: [],
},
],
},
projectId: 'default',
variant: {
name: 'disabled',
enabled: false,
},
name: 'test-playground-feature',
environment: 'default',
context: {
appName: 'test',
userId: '1',
channel: 'web',
},
variants: [],
},
{
isEnabled: true,
isEnabledInCurrentEnvironment: true,
hasUnsatisfiedDependency: false,
strategies: {
result: true,
data: [
{
name: 'default',
disabled: false,
parameters: {},
result: {
enabled: true,
evaluationStatus: 'complete',
},
constraints: [],
segments: [],
},
],
},
projectId: 'default',
variant: {
name: 'disabled',
enabled: false,
},
name: 'test-playground-feature',
environment: 'default',
context: {
appName: 'test',
userId: '1',
channel: 'mobile',
},
variants: [],
},
{
isEnabled: true,
isEnabledInCurrentEnvironment: true,
hasUnsatisfiedDependency: false,
strategies: {
result: true,
data: [
{
name: 'default',
disabled: false,
parameters: {},
result: {
enabled: true,
evaluationStatus: 'complete',
},
constraints: [],
segments: [],
},
],
},
projectId: 'default',
variant: {
name: 'disabled',
enabled: false,
},
name: 'test-playground-feature',
environment: 'default',
context: {
appName: 'test',
userId: '2',
channel: 'web',
},
variants: [],
},
{
isEnabled: true,
isEnabledInCurrentEnvironment: true,
hasUnsatisfiedDependency: false,
strategies: {
result: true,
data: [
{
name: 'default',
disabled: false,
parameters: {},
result: {
enabled: true,
evaluationStatus: 'complete',
},
constraints: [],
segments: [],
},
],
},
projectId: 'default',
variant: {
name: 'disabled',
enabled: false,
},
name: 'test-playground-feature',
environment: 'default',
context: {
appName: 'test',
userId: '2',
channel: 'mobile',
},
variants: [],
},
],
},
},
],
});
});
test('show matching variant from variants selection only for enabled toggles', async () => {
const variant = {
stickiness: 'random',
name: 'a',
weight: 1000,
payload: {
type: 'string',
value: 'aval',
},
weightType: 'variable',
};
await createFeatureToggleWithStrategy(
'test-playground-feature-with-variants',
{
name: 'flexibleRollout',
constraints: [],
parameters: {
rollout: '50',
stickiness: 'random',
groupId: 'test-playground-feature-with-variants',
},
variants: [variant],
},
);
await enableToggle('test-playground-feature-with-variants');
const { body: result } = await app.request
.post('/api/admin/playground/advanced')
.send({
environments: ['default'],
projects: ['default'],
context: { appName: 'playground', someProperty: '1,2,3,4,5' }, // generate 5 combinations
})
.set('Content-Type', 'application/json')
.expect(200);
const typedResult: AdvancedPlaygroundResponseSchema = result;
const enabledFeatures = typedResult.features[0].environments.default.filter(
(item) => item.isEnabled,
);
const disabledFeatures =
typedResult.features[0].environments.default.filter(
(item) => !item.isEnabled,
);
enabledFeatures.forEach((feature) => {
expect(feature.variant?.name).toBe('a');
expect(feature.variants).toMatchObject([variant]);
});
disabledFeatures.forEach((feature) => {
expect(feature.variant?.name).toBe('disabled');
expect(feature.variants).toMatchObject([]);
});
});
test('should return disabled strategies with unevaluated result', async () => {
await createFeatureToggleWithStrategy(
'test-playground-feature-with-disabled-strategy',
{
name: 'flexibleRollout',
constraints: [],
disabled: true,
parameters: {
rollout: '50',
stickiness: 'random',
groupId: 'test-playground-feature-with-variants',
},
},
);
const { body: result } = await app.request
.post('/api/admin/playground/advanced')
.send({
environments: ['default'],
projects: ['default'],
context: { appName: 'playground' },
})
.set('Content-Type', 'application/json')
.expect(200);
const typedResult: AdvancedPlaygroundResponseSchema = result;
const feature = typedResult.features.find(
(feature) =>
feature.name === 'test-playground-feature-with-disabled-strategy',
);
expect(
feature?.environments.default[0].strategies.data[0].result
.evaluationStatus,
).toBe('unevaluated');
});