mirror of
https://github.com/Unleash/unleash.git
synced 2025-04-01 01:18:10 +02:00
776 lines
21 KiB
TypeScript
776 lines
21 KiB
TypeScript
import type FeatureToggleService from '../feature-toggle-service';
|
|
import { createTestConfig } from '../../../../test/config/test-config';
|
|
import dbInit, {
|
|
type ITestDb,
|
|
} from '../../../../test/e2e/helpers/database-init';
|
|
import { DEFAULT_ENV, extractAuditInfoFromUser } from '../../../util';
|
|
import type { FeatureStrategySchema } from '../../../openapi';
|
|
import type User from '../../../types/user';
|
|
import {
|
|
type IConstraint,
|
|
type IUnleashConfig,
|
|
type IUnleashStores,
|
|
type IVariant,
|
|
SKIP_CHANGE_REQUEST,
|
|
SYSTEM_USER_AUDIT,
|
|
TEST_AUDIT_USER,
|
|
} from '../../../types';
|
|
import EnvironmentService from '../../project-environments/environment-service';
|
|
import {
|
|
ForbiddenError,
|
|
NotFoundError,
|
|
PatternError,
|
|
PermissionError,
|
|
} from '../../../error';
|
|
import type { ISegmentService } from '../../segment/segment-service-interface';
|
|
import { createFeatureToggleService, createSegmentService } from '../..';
|
|
import { insertLastSeenAt } from '../../../../test/e2e/helpers/test-helper';
|
|
import { EventService } from '../../../services';
|
|
|
|
let stores: IUnleashStores;
|
|
let db: ITestDb;
|
|
let service: FeatureToggleService;
|
|
let segmentService: ISegmentService;
|
|
let eventService: EventService;
|
|
let environmentService: EnvironmentService;
|
|
let unleashConfig: IUnleashConfig;
|
|
const TEST_USER_ID = -9999;
|
|
const mockConstraints = (): IConstraint[] => {
|
|
return Array.from({ length: 5 }).map(() => ({
|
|
values: ['x', 'y', 'z'],
|
|
operator: 'IN',
|
|
contextName: 'a',
|
|
}));
|
|
};
|
|
|
|
const irrelevantDate = new Date();
|
|
|
|
beforeAll(async () => {
|
|
const config = createTestConfig({
|
|
experimental: { flags: { archiveProjects: true } },
|
|
});
|
|
db = await dbInit(
|
|
'feature_toggle_service_v2_service_serial',
|
|
config.getLogger,
|
|
);
|
|
unleashConfig = config;
|
|
stores = db.stores;
|
|
|
|
segmentService = createSegmentService(db.rawDatabase, config);
|
|
|
|
service = createFeatureToggleService(db.rawDatabase, config);
|
|
|
|
eventService = new EventService(stores, config);
|
|
});
|
|
|
|
afterAll(async () => {
|
|
await db.rawDatabase('change_request_settings').del();
|
|
await db.destroy();
|
|
});
|
|
|
|
beforeEach(async () => {
|
|
await db.rawDatabase('change_request_settings').del();
|
|
});
|
|
test('Should create feature flag strategy configuration', async () => {
|
|
const projectId = 'default';
|
|
const username = 'feature-flag';
|
|
const config: Omit<FeatureStrategySchema, 'id'> = {
|
|
name: 'default',
|
|
constraints: [],
|
|
parameters: {},
|
|
};
|
|
|
|
await service.createFeatureToggle(
|
|
'default',
|
|
{
|
|
name: 'Demo',
|
|
},
|
|
TEST_AUDIT_USER,
|
|
);
|
|
|
|
const createdConfig = await service.createStrategy(
|
|
config,
|
|
{ projectId, featureName: 'Demo', environment: DEFAULT_ENV },
|
|
TEST_AUDIT_USER,
|
|
);
|
|
|
|
expect(createdConfig.name).toEqual('default');
|
|
expect(createdConfig.id).toBeDefined();
|
|
});
|
|
|
|
test('Should be able to update existing strategy configuration', async () => {
|
|
const projectId = 'default';
|
|
const username = 'existing-strategy';
|
|
const featureName = 'update-existing-strategy';
|
|
const config: Omit<FeatureStrategySchema, 'id'> = {
|
|
name: 'default',
|
|
constraints: [],
|
|
parameters: {},
|
|
};
|
|
|
|
await service.createFeatureToggle(
|
|
projectId,
|
|
{
|
|
name: featureName,
|
|
},
|
|
TEST_AUDIT_USER,
|
|
);
|
|
|
|
const createdConfig = await service.createStrategy(
|
|
config,
|
|
{ projectId, featureName, environment: DEFAULT_ENV },
|
|
TEST_AUDIT_USER,
|
|
);
|
|
expect(createdConfig.name).toEqual('default');
|
|
const updatedConfig = await service.updateStrategy(
|
|
createdConfig.id,
|
|
{ parameters: { b2b: 'true' } },
|
|
{ projectId, featureName, environment: DEFAULT_ENV },
|
|
TEST_AUDIT_USER,
|
|
);
|
|
expect(createdConfig.id).toEqual(updatedConfig.id);
|
|
expect(updatedConfig.parameters).toEqual({ b2b: 'true' });
|
|
});
|
|
|
|
test('Should be able to get strategy by id', async () => {
|
|
const featureName = 'get-strategy-by-id';
|
|
const projectId = 'default';
|
|
|
|
const userName = 'strategy';
|
|
const config: Omit<FeatureStrategySchema, 'id'> = {
|
|
name: 'default',
|
|
constraints: [],
|
|
variants: [],
|
|
parameters: {},
|
|
title: 'some-title',
|
|
};
|
|
await service.createFeatureToggle(
|
|
projectId,
|
|
{
|
|
name: featureName,
|
|
},
|
|
TEST_AUDIT_USER,
|
|
);
|
|
|
|
const createdConfig = await service.createStrategy(
|
|
config,
|
|
{ projectId, featureName, environment: DEFAULT_ENV },
|
|
TEST_AUDIT_USER,
|
|
);
|
|
const fetchedConfig = await service.getStrategy(createdConfig.id);
|
|
expect(fetchedConfig).toEqual(createdConfig);
|
|
});
|
|
|
|
test('should ignore name in the body when updating feature flag', async () => {
|
|
const featureName = 'body-name-update';
|
|
const projectId = 'default';
|
|
|
|
const userName = 'strategy';
|
|
const secondFeatureName = 'body-name-update2';
|
|
|
|
await service.createFeatureToggle(
|
|
projectId,
|
|
{
|
|
name: featureName,
|
|
description: 'First flag',
|
|
},
|
|
TEST_AUDIT_USER,
|
|
);
|
|
|
|
await service.createFeatureToggle(
|
|
projectId,
|
|
{
|
|
name: secondFeatureName,
|
|
description: 'Second flag',
|
|
},
|
|
TEST_AUDIT_USER,
|
|
);
|
|
|
|
const update = {
|
|
name: secondFeatureName,
|
|
description: "I'm changed",
|
|
};
|
|
|
|
await service.updateFeatureToggle(
|
|
projectId,
|
|
update,
|
|
featureName,
|
|
TEST_AUDIT_USER,
|
|
);
|
|
const featureOne = await service.getFeature({ featureName });
|
|
const featureTwo = await service.getFeature({
|
|
featureName: secondFeatureName,
|
|
});
|
|
|
|
expect(featureOne.description).toBe(`I'm changed`);
|
|
expect(featureTwo.description).toBe('Second flag');
|
|
});
|
|
|
|
test('should not get empty rows as features', async () => {
|
|
const projectId = 'default';
|
|
|
|
const userName = 'strategy';
|
|
|
|
await service.createFeatureToggle(
|
|
projectId,
|
|
{
|
|
name: 'linked-with-segment',
|
|
description: 'First flag',
|
|
},
|
|
TEST_AUDIT_USER,
|
|
);
|
|
|
|
await service.createFeatureToggle(
|
|
projectId,
|
|
{
|
|
name: 'not-linked-with-segment',
|
|
description: 'Second flag',
|
|
},
|
|
TEST_AUDIT_USER,
|
|
);
|
|
|
|
const user = { email: 'test@example.com' } as User;
|
|
const postData = {
|
|
name: 'Unlinked segment',
|
|
constraints: mockConstraints(),
|
|
};
|
|
await segmentService.create(postData, extractAuditInfoFromUser(user));
|
|
|
|
const features = await service.getClientFeatures();
|
|
const namelessFeature = features.find((p) => !p.name);
|
|
|
|
expect(features.length).toBe(7);
|
|
expect(namelessFeature).toBeUndefined();
|
|
});
|
|
|
|
test('adding and removing an environment preserves variants when variants per env is off', async () => {
|
|
const featureName = 'something-that-has-variants';
|
|
const prodEnv = 'mock-prod-env';
|
|
|
|
await stores.environmentStore.create({
|
|
name: prodEnv,
|
|
type: 'production',
|
|
});
|
|
|
|
await service.createFeatureToggle(
|
|
'default',
|
|
{
|
|
name: featureName,
|
|
description: 'Second flag',
|
|
variants: [
|
|
{
|
|
name: 'variant1',
|
|
weight: 100,
|
|
weightType: 'fix',
|
|
stickiness: 'default',
|
|
},
|
|
],
|
|
},
|
|
TEST_AUDIT_USER,
|
|
);
|
|
|
|
//force the variantEnvironments flag off so that we can test legacy behavior
|
|
environmentService = new EnvironmentService(
|
|
stores,
|
|
{
|
|
...unleashConfig,
|
|
// @ts-expect-error - incomplete flag resolver definition
|
|
flagResolver: {
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
isEnabled: (flagName: string) => false,
|
|
},
|
|
},
|
|
eventService,
|
|
);
|
|
|
|
await environmentService.addEnvironmentToProject(
|
|
prodEnv,
|
|
'default',
|
|
SYSTEM_USER_AUDIT,
|
|
);
|
|
await environmentService.removeEnvironmentFromProject(
|
|
prodEnv,
|
|
'default',
|
|
SYSTEM_USER_AUDIT,
|
|
);
|
|
await environmentService.addEnvironmentToProject(
|
|
prodEnv,
|
|
'default',
|
|
SYSTEM_USER_AUDIT,
|
|
);
|
|
|
|
const flag = await service.getFeature({
|
|
featureName,
|
|
projectId: undefined,
|
|
environmentVariants: false,
|
|
});
|
|
expect(flag.variants).toHaveLength(1);
|
|
});
|
|
|
|
test('cloning a feature flag copies variant environments correctly', async () => {
|
|
const newFlagName = 'Molly';
|
|
const clonedFlagName = 'Dolly';
|
|
const targetEnv = 'gene-lab';
|
|
|
|
await service.createFeatureToggle(
|
|
'default',
|
|
{
|
|
name: newFlagName,
|
|
},
|
|
TEST_AUDIT_USER,
|
|
);
|
|
|
|
await stores.environmentStore.create({
|
|
name: 'gene-lab',
|
|
type: 'production',
|
|
});
|
|
|
|
await stores.featureEnvironmentStore.connectFeatureToEnvironmentsForProject(
|
|
newFlagName,
|
|
'default',
|
|
);
|
|
|
|
await stores.featureEnvironmentStore.addVariantsToFeatureEnvironment(
|
|
newFlagName,
|
|
targetEnv,
|
|
[
|
|
{
|
|
name: 'variant1',
|
|
weight: 100,
|
|
weightType: 'fix',
|
|
stickiness: 'default',
|
|
},
|
|
],
|
|
);
|
|
|
|
await service.cloneFeatureToggle(
|
|
newFlagName,
|
|
'default',
|
|
clonedFlagName,
|
|
SYSTEM_USER_AUDIT,
|
|
true,
|
|
);
|
|
|
|
const clonedFlag =
|
|
await stores.featureStrategiesStore.getFeatureToggleWithVariantEnvs(
|
|
clonedFlagName,
|
|
);
|
|
|
|
const defaultEnv = clonedFlag.environments.find(
|
|
(x) => x.name === 'default',
|
|
);
|
|
const newEnv = clonedFlag.environments.find((x) => x.name === targetEnv);
|
|
|
|
expect(defaultEnv!!.variants).toHaveLength(0);
|
|
expect(newEnv!!.variants).toHaveLength(1);
|
|
});
|
|
|
|
test('cloning a feature flag not allowed for change requests enabled', async () => {
|
|
await db.rawDatabase('change_request_settings').insert({
|
|
project: 'default',
|
|
environment: 'default',
|
|
});
|
|
await expect(
|
|
service.cloneFeatureToggle(
|
|
'newFlagName',
|
|
'default',
|
|
'clonedFlagName',
|
|
SYSTEM_USER_AUDIT,
|
|
true,
|
|
),
|
|
).rejects.toEqual(
|
|
new ForbiddenError(
|
|
`Cloning not allowed. Project default has change requests enabled.`,
|
|
),
|
|
);
|
|
});
|
|
|
|
test('changing to a project with change requests enabled should not be allowed', async () => {
|
|
await db.rawDatabase('change_request_settings').insert({
|
|
project: 'default',
|
|
environment: 'default',
|
|
});
|
|
await expect(
|
|
service.changeProject('newFlagName', 'default', TEST_AUDIT_USER),
|
|
).rejects.toEqual(
|
|
new ForbiddenError(
|
|
`Changing project not allowed. Project default has change requests enabled.`,
|
|
),
|
|
);
|
|
});
|
|
|
|
test('Cloning a feature flag also clones segments correctly', async () => {
|
|
const featureName = 'FlagWithSegments';
|
|
const clonedFeatureName = 'AWholeNewFeatureFlag';
|
|
|
|
const segment = await segmentService.create(
|
|
{
|
|
name: 'SomeSegment',
|
|
constraints: mockConstraints(),
|
|
},
|
|
TEST_AUDIT_USER,
|
|
);
|
|
|
|
await service.createFeatureToggle(
|
|
'default',
|
|
{
|
|
name: featureName,
|
|
},
|
|
TEST_AUDIT_USER,
|
|
);
|
|
|
|
const config: Omit<FeatureStrategySchema, 'id'> = {
|
|
name: 'default',
|
|
constraints: [],
|
|
parameters: {},
|
|
segments: [segment.id],
|
|
};
|
|
|
|
await service.createStrategy(
|
|
config,
|
|
{ projectId: 'default', featureName, environment: DEFAULT_ENV },
|
|
TEST_AUDIT_USER,
|
|
);
|
|
|
|
await service.cloneFeatureToggle(
|
|
featureName,
|
|
'default',
|
|
clonedFeatureName,
|
|
TEST_AUDIT_USER,
|
|
true,
|
|
);
|
|
|
|
const feature = await service.getFeature({
|
|
featureName: clonedFeatureName,
|
|
});
|
|
expect(
|
|
feature.environments.find((x) => x.name === 'default')?.strategies[0]
|
|
.segments,
|
|
).toHaveLength(1);
|
|
});
|
|
|
|
test('If change requests are enabled, cannot change variants without going via CR', async () => {
|
|
const featureName = 'feature-with-variants-per-env-and-cr';
|
|
await service.createFeatureToggle(
|
|
'default',
|
|
{ name: featureName },
|
|
TEST_AUDIT_USER,
|
|
);
|
|
|
|
// Force all feature flags on to make sure we have Change requests on
|
|
const customFeatureService = createFeatureToggleService(db.rawDatabase, {
|
|
...unleashConfig,
|
|
// @ts-expect-error - incomplete flag resolver definition
|
|
flagResolver: {
|
|
isEnabled: () => true,
|
|
},
|
|
});
|
|
|
|
const newVariant: IVariant = {
|
|
name: 'cr-enabled',
|
|
weight: 100,
|
|
weightType: 'variable',
|
|
stickiness: 'default',
|
|
};
|
|
await db.rawDatabase('change_request_settings').insert({
|
|
project: 'default',
|
|
environment: 'default',
|
|
});
|
|
return expect(async () =>
|
|
customFeatureService.crProtectedSaveVariantsOnEnv(
|
|
'default',
|
|
featureName,
|
|
'default',
|
|
[newVariant],
|
|
{
|
|
createdAt: irrelevantDate,
|
|
email: '',
|
|
id: 0,
|
|
imageUrl: '',
|
|
loginAttempts: 0,
|
|
name: '',
|
|
permissions: [],
|
|
seenAt: irrelevantDate,
|
|
username: '',
|
|
isAPI: true,
|
|
},
|
|
TEST_AUDIT_USER,
|
|
[],
|
|
),
|
|
).rejects.toThrowError(new PermissionError(SKIP_CHANGE_REQUEST));
|
|
});
|
|
|
|
test('If CRs are protected for any environment in the project stops bulk update of variants', async () => {
|
|
const user = { email: 'test@example.com', username: 'test-user' } as User;
|
|
const project = await stores.projectStore.create({
|
|
id: 'crOnVariantsProject',
|
|
name: 'crOnVariantsProject',
|
|
});
|
|
const enabledEnv = await stores.environmentStore.create({
|
|
name: 'crenabledenv',
|
|
type: 'production',
|
|
});
|
|
const disabledEnv = await stores.environmentStore.create({
|
|
name: 'crdisabledenv',
|
|
type: 'production',
|
|
});
|
|
|
|
await stores.projectStore.addEnvironmentToProject(
|
|
project.id,
|
|
enabledEnv.name,
|
|
);
|
|
await stores.projectStore.addEnvironmentToProject(
|
|
project.id,
|
|
disabledEnv.name,
|
|
);
|
|
|
|
// Force all feature flags on to make sure we have Change requests on
|
|
const customFeatureService = createFeatureToggleService(db.rawDatabase, {
|
|
...unleashConfig,
|
|
// @ts-expect-error - incomplete flag resolver definition
|
|
flagResolver: {
|
|
isEnabled: () => true,
|
|
},
|
|
});
|
|
|
|
const flag = await service.createFeatureToggle(
|
|
project.id,
|
|
{ name: 'crOnVariantFlag' },
|
|
TEST_AUDIT_USER,
|
|
);
|
|
|
|
const variant: IVariant = {
|
|
name: 'cr-enabled',
|
|
weight: 100,
|
|
weightType: 'variable',
|
|
stickiness: 'default',
|
|
};
|
|
await db.rawDatabase('change_request_settings').insert({
|
|
project: project.id,
|
|
environment: enabledEnv.name,
|
|
});
|
|
|
|
await customFeatureService.setVariantsOnEnvs(
|
|
project.id,
|
|
flag.name,
|
|
[enabledEnv.name, disabledEnv.name],
|
|
[variant],
|
|
TEST_AUDIT_USER,
|
|
);
|
|
|
|
const newVariants = [
|
|
{ ...variant, weight: 500 },
|
|
{
|
|
name: 'cr-enabled-2',
|
|
weight: 500,
|
|
weightType: 'fix' as const,
|
|
stickiness: 'default',
|
|
},
|
|
];
|
|
return expect(async () =>
|
|
customFeatureService.crProtectedSetVariantsOnEnvs(
|
|
project.id,
|
|
flag.name,
|
|
[enabledEnv.name, disabledEnv.name],
|
|
newVariants,
|
|
{
|
|
createdAt: irrelevantDate,
|
|
email: '',
|
|
id: 0,
|
|
imageUrl: '',
|
|
loginAttempts: 0,
|
|
name: '',
|
|
permissions: [],
|
|
seenAt: irrelevantDate,
|
|
username: '',
|
|
isAPI: true,
|
|
},
|
|
TEST_AUDIT_USER,
|
|
),
|
|
).rejects.toThrowError(new PermissionError(SKIP_CHANGE_REQUEST));
|
|
});
|
|
|
|
test('getPlaygroundFeatures should return ids and titles (if they exist) on client strategies', async () => {
|
|
const featureName = 'check-returned-strategy-configuration';
|
|
const projectId = 'default';
|
|
|
|
const title = 'custom strategy title';
|
|
const userName = 'strategy';
|
|
const config: Omit<FeatureStrategySchema, 'id'> = {
|
|
name: 'default',
|
|
constraints: [],
|
|
parameters: {},
|
|
title,
|
|
};
|
|
await service.createFeatureToggle(
|
|
projectId,
|
|
{
|
|
name: featureName,
|
|
},
|
|
TEST_AUDIT_USER,
|
|
);
|
|
|
|
await service.createStrategy(
|
|
config,
|
|
{ projectId, featureName, environment: DEFAULT_ENV },
|
|
TEST_AUDIT_USER,
|
|
);
|
|
|
|
const playgroundFeatures = await service.getPlaygroundFeatures();
|
|
|
|
const strategyWithTitle = playgroundFeatures.find(
|
|
(feature) => feature.name === featureName,
|
|
)!.strategies[0];
|
|
|
|
expect(strategyWithTitle.title).toStrictEqual(title);
|
|
|
|
for (const strategy of playgroundFeatures.flatMap(
|
|
(feature) => feature.strategies,
|
|
)) {
|
|
expect(strategy.id).not.toBeUndefined();
|
|
}
|
|
});
|
|
|
|
describe('flag name validation', () => {
|
|
test('should validate feature names if the project has flag name pattern', async () => {
|
|
const projectId = 'pattern-validation';
|
|
const featureNaming = {
|
|
pattern: 'testpattern.+',
|
|
example: 'testpattern-one!',
|
|
description: 'naming description',
|
|
};
|
|
const project = {
|
|
id: projectId,
|
|
name: projectId,
|
|
mode: 'open' as const,
|
|
defaultStickiness: 'default',
|
|
};
|
|
|
|
await stores.projectStore.create(project);
|
|
await stores.projectStore.updateProjectEnterpriseSettings({
|
|
id: projectId,
|
|
featureNaming,
|
|
});
|
|
|
|
const validFeatures = ['testpattern-feature', 'testpattern-feature2'];
|
|
const invalidFeatures = ['a', 'b', 'c'];
|
|
|
|
for (const feature of invalidFeatures) {
|
|
await expect(
|
|
service.validateFeatureFlagNameAgainstPattern(
|
|
feature,
|
|
projectId,
|
|
),
|
|
).rejects.toBeInstanceOf(PatternError);
|
|
}
|
|
|
|
for (const feature of validFeatures) {
|
|
await expect(
|
|
service.validateFeatureFlagNameAgainstPattern(
|
|
feature,
|
|
projectId,
|
|
),
|
|
).resolves.toBeFalsy();
|
|
}
|
|
});
|
|
|
|
test("should allow anything if the project doesn't exist", async () => {
|
|
const projectId = 'project-that-doesnt-exist';
|
|
const validFeatures = ['testpattern-feature', 'testpattern-feature2'];
|
|
|
|
for (const feature of validFeatures) {
|
|
await expect(
|
|
service.validateFeatureFlagNameAgainstPattern(
|
|
feature,
|
|
projectId,
|
|
),
|
|
).resolves.toBeFalsy();
|
|
}
|
|
});
|
|
});
|
|
|
|
test('Should return last seen at per environment', async () => {
|
|
const featureName = 'last-seen-at-per-env';
|
|
const projectId = 'default';
|
|
|
|
await service.createFeatureToggle(
|
|
projectId,
|
|
{
|
|
name: featureName,
|
|
},
|
|
TEST_AUDIT_USER,
|
|
);
|
|
|
|
// Test with feature flag on
|
|
const config = createTestConfig();
|
|
|
|
const featureService = createFeatureToggleService(db.rawDatabase, config);
|
|
|
|
const lastSeenAtStoreDate = await insertLastSeenAt(
|
|
featureName,
|
|
db.rawDatabase,
|
|
);
|
|
|
|
const featureToggle = await featureService.getFeature({
|
|
featureName,
|
|
projectId: 'default',
|
|
environmentVariants: false,
|
|
});
|
|
|
|
expect(featureToggle.environments[0].lastSeenAt).toEqual(
|
|
new Date(lastSeenAtStoreDate),
|
|
);
|
|
expect(featureToggle.lastSeenAt).toEqual(new Date(lastSeenAtStoreDate));
|
|
});
|
|
|
|
test('Should return "default" for stickiness when creating a flexibleRollout strategy with empty stickiness', async () => {
|
|
const strategy = {
|
|
name: 'flexibleRollout',
|
|
parameters: {
|
|
rollout: '100',
|
|
stickiness: '',
|
|
},
|
|
constraints: [],
|
|
};
|
|
const feature = {
|
|
name: 'test-feature-stickiness-1',
|
|
description: 'the #1 feature',
|
|
};
|
|
const projectId = 'default';
|
|
|
|
await service.createFeatureToggle(projectId, feature, TEST_AUDIT_USER);
|
|
await service.createStrategy(
|
|
strategy,
|
|
{ projectId, featureName: feature.name, environment: DEFAULT_ENV },
|
|
TEST_AUDIT_USER,
|
|
);
|
|
|
|
const featureDB = await service.getFeature({ featureName: feature.name });
|
|
|
|
expect(featureDB.environments[0]).toMatchObject({
|
|
strategies: [{ parameters: { stickiness: 'default' } }],
|
|
});
|
|
});
|
|
|
|
test('Should not allow to add flags to archived projects', async () => {
|
|
const project = await stores.projectStore.create({
|
|
id: 'archivedProject',
|
|
name: 'archivedProject',
|
|
});
|
|
await stores.projectStore.archive(project.id);
|
|
|
|
await expect(
|
|
service.createFeatureToggle(
|
|
project.id,
|
|
{
|
|
name: 'irrelevant',
|
|
},
|
|
TEST_AUDIT_USER,
|
|
),
|
|
).rejects.toEqual(
|
|
new NotFoundError(
|
|
`Active project with id archivedProject does not exist`,
|
|
),
|
|
);
|
|
});
|