mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-17 13:46:47 +02:00
chore!: remove deprecated default env from new installs (#10080)
**BREAKING CHANGE**: DEFAULT_ENV changed from `default` (should not be
used anymore) to `development`
## About the changes
- Only delete default env if the install is fresh new.
- Consider development the new default. The main consequence of this
change is that the default is no longer considered `type=production`
environment but also for frontend tokens due to this assumption:
724c4b78a2/src/lib/schema/api-token-schema.test.ts (L54-L59)
(I believe this is mostly due to the [support for admin
tokens](https://github.com/Unleash/unleash/pull/10080#discussion_r2126871567))
- `feature_toggle_update_total` metric reports `n/a` in environment and
environment type as it's not environment specific
This commit is contained in:
parent
d6f76a098e
commit
bdb763c9d5
@ -63,12 +63,12 @@ exports[`should match snapshot from /api/client/features 1`] = `
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
"meta": {
|
"meta": {
|
||||||
"etag": ""61824cd0:19"",
|
"etag": ""76d8bb0e:19"",
|
||||||
"queryHash": "61824cd0",
|
"queryHash": "76d8bb0e",
|
||||||
"revisionId": 19,
|
"revisionId": 19,
|
||||||
},
|
},
|
||||||
"query": {
|
"query": {
|
||||||
"environment": "default",
|
"environment": "development",
|
||||||
"inlineSegmentConstraints": true,
|
"inlineSegmentConstraints": true,
|
||||||
},
|
},
|
||||||
"version": 2,
|
"version": 2,
|
||||||
|
@ -21,9 +21,7 @@ let db: ITestDb;
|
|||||||
let eventStore: IEventStore;
|
let eventStore: IEventStore;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
db = await dbInit('dependent_features', getLogger, {
|
db = await dbInit('dependent_features', getLogger);
|
||||||
dbInitMethod: 'legacy' as const,
|
|
||||||
});
|
|
||||||
app = await setupAppWithCustomConfig(
|
app = await setupAppWithCustomConfig(
|
||||||
db.stores,
|
db.stores,
|
||||||
{
|
{
|
||||||
|
@ -53,9 +53,9 @@ test('querying environments in OSS only returns environments that are included i
|
|||||||
.get('/api/admin/environments')
|
.get('/api/admin/environments')
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect((res) => {
|
.expect((res) => {
|
||||||
expect(res.body.environments).toHaveLength(3);
|
expect(res.body.environments).toHaveLength(2);
|
||||||
const names = res.body.environments.map((env) => env.name);
|
const names = res.body.environments.map((env) => env.name);
|
||||||
expect(names).toEqual(['default', 'development', 'production']);
|
expect(names).toEqual(['development', 'production']);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -64,8 +64,8 @@ test('querying project environments in OSS only returns environments that are in
|
|||||||
.get('/api/admin/environments/project/default')
|
.get('/api/admin/environments/project/default')
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect((res) => {
|
.expect((res) => {
|
||||||
expect(res.body.environments).toHaveLength(3);
|
expect(res.body.environments).toHaveLength(2);
|
||||||
const names = res.body.environments.map((env) => env.name);
|
const names = res.body.environments.map((env) => env.name);
|
||||||
expect(names).toEqual(['default', 'development', 'production']);
|
expect(names).toEqual(['development', 'production']);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -12,9 +12,7 @@ let app: IUnleashTest;
|
|||||||
let db: ITestDb;
|
let db: ITestDb;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
db = await dbInit('environment_api_serial', getLogger, {
|
db = await dbInit('environment_api_serial', getLogger);
|
||||||
dbInitMethod: 'legacy' as const,
|
|
||||||
});
|
|
||||||
app = await setupAppWithCustomConfig(
|
app = await setupAppWithCustomConfig(
|
||||||
db.stores,
|
db.stores,
|
||||||
{
|
{
|
||||||
@ -34,23 +32,32 @@ afterAll(async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Can list all existing environments', async () => {
|
test('Can list all existing environments', async () => {
|
||||||
await app.request
|
const { body } = await app.request
|
||||||
.get('/api/admin/environments')
|
.get('/api/admin/environments')
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/);
|
||||||
.expect((res) => {
|
expect(body.version).toBe(1);
|
||||||
expect(res.body.version).toBe(1);
|
expect(body.environments[0]).toStrictEqual({
|
||||||
expect(res.body.environments[0]).toStrictEqual({
|
|
||||||
name: DEFAULT_ENV,
|
name: DEFAULT_ENV,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
sortOrder: 1,
|
sortOrder: 2,
|
||||||
type: 'production',
|
type: 'development',
|
||||||
protected: true,
|
protected: false,
|
||||||
requiredApprovals: null,
|
requiredApprovals: null,
|
||||||
projectCount: 1,
|
projectCount: 1,
|
||||||
apiTokenCount: 0,
|
apiTokenCount: 0,
|
||||||
enabledToggleCount: 0,
|
enabledToggleCount: 0,
|
||||||
});
|
});
|
||||||
|
expect(body.environments[1]).toStrictEqual({
|
||||||
|
name: 'production',
|
||||||
|
enabled: true,
|
||||||
|
sortOrder: 3,
|
||||||
|
type: 'production',
|
||||||
|
protected: false,
|
||||||
|
requiredApprovals: null,
|
||||||
|
projectCount: 1,
|
||||||
|
apiTokenCount: 0,
|
||||||
|
enabledToggleCount: 0,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -23,7 +23,6 @@ import { FeatureLifecycleReadModel } from './feature-lifecycle-read-model.js';
|
|||||||
import type { IFeatureLifecycleReadModel } from './feature-lifecycle-read-model-type.js';
|
import type { IFeatureLifecycleReadModel } from './feature-lifecycle-read-model-type.js';
|
||||||
import { STAGE_ENTERED } from '../../metric-events.js';
|
import { STAGE_ENTERED } from '../../metric-events.js';
|
||||||
import type ClientInstanceService from '../metrics/instance/instance-service.js';
|
import type ClientInstanceService from '../metrics/instance/instance-service.js';
|
||||||
import { DEFAULT_ENV } from '../../server-impl.js';
|
|
||||||
|
|
||||||
let app: IUnleashTest;
|
let app: IUnleashTest;
|
||||||
let db: ITestDb;
|
let db: ITestDb;
|
||||||
@ -34,9 +33,7 @@ let featureLifecycleReadModel: IFeatureLifecycleReadModel;
|
|||||||
let clientInstanceService: ClientInstanceService;
|
let clientInstanceService: ClientInstanceService;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
db = await dbInit('feature_lifecycle', getLogger, {
|
db = await dbInit('feature_lifecycle', getLogger);
|
||||||
dbInitMethod: 'legacy' as const,
|
|
||||||
});
|
|
||||||
app = await setupAppWithAuth(
|
app = await setupAppWithAuth(
|
||||||
db.stores,
|
db.stores,
|
||||||
{
|
{
|
||||||
@ -127,26 +124,27 @@ const getFeaturesLifecycleCount = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
test('should return lifecycle stages', async () => {
|
test('should return lifecycle stages', async () => {
|
||||||
|
const environment = 'production'; // prod environment moves lifecycle to live stage
|
||||||
await app.createFeature('my_feature_a');
|
await app.createFeature('my_feature_a');
|
||||||
await app.enableFeature('my_feature_a', DEFAULT_ENV);
|
await app.enableFeature('my_feature_a', environment);
|
||||||
eventStore.emit(FEATURE_CREATED, { featureName: 'my_feature_a' });
|
eventStore.emit(FEATURE_CREATED, { featureName: 'my_feature_a' });
|
||||||
await reachedStage('my_feature_a', 'initial');
|
await reachedStage('my_feature_a', 'initial');
|
||||||
await expectFeatureStage('my_feature_a', 'initial');
|
await expectFeatureStage('my_feature_a', 'initial');
|
||||||
eventBus.emit(CLIENT_METRICS_ADDED, [
|
eventBus.emit(CLIENT_METRICS_ADDED, [
|
||||||
{
|
{
|
||||||
featureName: 'my_feature_a',
|
featureName: 'my_feature_a',
|
||||||
environment: DEFAULT_ENV,
|
environment: environment,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
featureName: 'non_existent_feature',
|
featureName: 'non_existent_feature',
|
||||||
environment: DEFAULT_ENV,
|
environment: environment,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// missing feature
|
// missing feature
|
||||||
eventBus.emit(CLIENT_METRICS_ADDED, [
|
eventBus.emit(CLIENT_METRICS_ADDED, [
|
||||||
{
|
{
|
||||||
environment: DEFAULT_ENV,
|
environment: environment,
|
||||||
yes: 0,
|
yes: 0,
|
||||||
no: 0,
|
no: 0,
|
||||||
},
|
},
|
||||||
@ -241,13 +239,14 @@ test('should backfill archived feature', async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should not backfill for existing lifecycle', async () => {
|
test('should not backfill for existing lifecycle', async () => {
|
||||||
|
const environment = 'production'; // prod environment moves lifecycle to live stage
|
||||||
await app.createFeature('my_feature_e');
|
await app.createFeature('my_feature_e');
|
||||||
await app.enableFeature('my_feature_e', DEFAULT_ENV);
|
await app.enableFeature('my_feature_e', environment);
|
||||||
eventStore.emit(FEATURE_CREATED, { featureName: 'my_feature_e' });
|
eventStore.emit(FEATURE_CREATED, { featureName: 'my_feature_e' });
|
||||||
eventBus.emit(CLIENT_METRICS_ADDED, [
|
eventBus.emit(CLIENT_METRICS_ADDED, [
|
||||||
{
|
{
|
||||||
featureName: 'my_feature_e',
|
featureName: 'my_feature_e',
|
||||||
environment: DEFAULT_ENV,
|
environment: environment,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
await reachedStage('my_feature_e', 'live');
|
await reachedStage('my_feature_e', 'live');
|
||||||
|
@ -18,9 +18,7 @@ let eventStore: IEventStore;
|
|||||||
let featureLinkReadModel: IFeatureLinksReadModel;
|
let featureLinkReadModel: IFeatureLinksReadModel;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
db = await dbInit('feature_link', getLogger, {
|
db = await dbInit('feature_link', getLogger);
|
||||||
dbInitMethod: 'legacy' as const,
|
|
||||||
});
|
|
||||||
app = await setupAppWithAuth(
|
app = await setupAppWithAuth(
|
||||||
db.stores,
|
db.stores,
|
||||||
{
|
{
|
||||||
|
@ -9,11 +9,9 @@ import {
|
|||||||
import getLogger from '../../../test/fixtures/no-logger.js';
|
import getLogger from '../../../test/fixtures/no-logger.js';
|
||||||
import type { FeatureSearchQueryParameters } from '../../openapi/spec/feature-search-query-parameters.js';
|
import type { FeatureSearchQueryParameters } from '../../openapi/spec/feature-search-query-parameters.js';
|
||||||
import {
|
import {
|
||||||
CREATE_FEATURE_STRATEGY,
|
|
||||||
DEFAULT_PROJECT,
|
DEFAULT_PROJECT,
|
||||||
type IUnleashStores,
|
type IUnleashStores,
|
||||||
TEST_AUDIT_USER,
|
TEST_AUDIT_USER,
|
||||||
UPDATE_FEATURE_ENVIRONMENT,
|
|
||||||
} from '../../types/index.js';
|
} from '../../types/index.js';
|
||||||
import { DEFAULT_ENV } from '../../util/index.js';
|
import { DEFAULT_ENV } from '../../util/index.js';
|
||||||
|
|
||||||
@ -22,9 +20,7 @@ let db: ITestDb;
|
|||||||
let stores: IUnleashStores;
|
let stores: IUnleashStores;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
db = await dbInit('feature_search', getLogger, {
|
db = await dbInit('feature_search', getLogger);
|
||||||
dbInitMethod: 'legacy' as const,
|
|
||||||
});
|
|
||||||
app = await setupAppWithAuth(
|
app = await setupAppWithAuth(
|
||||||
db.stores,
|
db.stores,
|
||||||
{
|
{
|
||||||
@ -46,38 +42,6 @@ beforeAll(async () => {
|
|||||||
})
|
})
|
||||||
.expect(200);
|
.expect(200);
|
||||||
|
|
||||||
await stores.environmentStore.create({
|
|
||||||
name: 'development',
|
|
||||||
type: 'development',
|
|
||||||
});
|
|
||||||
|
|
||||||
await app.linkProjectToEnvironment('default', 'development');
|
|
||||||
|
|
||||||
await stores.accessStore.addPermissionsToRole(
|
|
||||||
body.rootRole,
|
|
||||||
[
|
|
||||||
{ name: UPDATE_FEATURE_ENVIRONMENT },
|
|
||||||
{ name: CREATE_FEATURE_STRATEGY },
|
|
||||||
],
|
|
||||||
'development',
|
|
||||||
);
|
|
||||||
|
|
||||||
await stores.environmentStore.create({
|
|
||||||
name: 'production',
|
|
||||||
type: 'production',
|
|
||||||
});
|
|
||||||
|
|
||||||
await app.linkProjectToEnvironment('default', 'production');
|
|
||||||
|
|
||||||
await stores.accessStore.addPermissionsToRole(
|
|
||||||
body.rootRole,
|
|
||||||
[
|
|
||||||
{ name: UPDATE_FEATURE_ENVIRONMENT },
|
|
||||||
{ name: CREATE_FEATURE_STRATEGY },
|
|
||||||
],
|
|
||||||
'production',
|
|
||||||
);
|
|
||||||
|
|
||||||
await app.services.userService.createUser(
|
await app.services.userService.createUser(
|
||||||
{
|
{
|
||||||
username: 'admin@test.com',
|
username: 'admin@test.com',
|
||||||
@ -865,11 +829,6 @@ test('should return segments in payload with no duplicates/nulls', async () => {
|
|||||||
name: 'my_feature_a',
|
name: 'my_feature_a',
|
||||||
segments: [mySegment.name],
|
segments: [mySegment.name],
|
||||||
environments: [
|
environments: [
|
||||||
{
|
|
||||||
name: 'default',
|
|
||||||
hasStrategies: true,
|
|
||||||
hasEnabledStrategies: true,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'development',
|
name: 'development',
|
||||||
hasStrategies: true,
|
hasStrategies: true,
|
||||||
@ -1183,11 +1142,6 @@ test('should return environment usage metrics and lifecycle', async () => {
|
|||||||
name: 'my_feature_b',
|
name: 'my_feature_b',
|
||||||
lifecycle: { stage: 'completed', status: 'discarded' },
|
lifecycle: { stage: 'completed', status: 'discarded' },
|
||||||
environments: [
|
environments: [
|
||||||
{
|
|
||||||
name: 'default',
|
|
||||||
yes: 0,
|
|
||||||
no: 0,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'development',
|
name: 'development',
|
||||||
yes: 10,
|
yes: 10,
|
||||||
@ -1448,7 +1402,6 @@ test('should return change request ids per environment', async () => {
|
|||||||
{
|
{
|
||||||
name: 'my_feature_a',
|
name: 'my_feature_a',
|
||||||
environments: [
|
environments: [
|
||||||
{ name: 'default', changeRequestIds: [] },
|
|
||||||
{ name: 'development', changeRequestIds: [5, 6, 7] },
|
{ name: 'development', changeRequestIds: [5, 6, 7] },
|
||||||
{ name: 'production', changeRequestIds: [1] },
|
{ name: 'production', changeRequestIds: [1] },
|
||||||
],
|
],
|
||||||
@ -1456,7 +1409,6 @@ test('should return change request ids per environment', async () => {
|
|||||||
{
|
{
|
||||||
name: 'my_feature_b',
|
name: 'my_feature_b',
|
||||||
environments: [
|
environments: [
|
||||||
{ name: 'default', changeRequestIds: [] },
|
|
||||||
{ name: 'development', changeRequestIds: [8] },
|
{ name: 'development', changeRequestIds: [8] },
|
||||||
{ name: 'production', changeRequestIds: [] },
|
{ name: 'production', changeRequestIds: [] },
|
||||||
],
|
],
|
||||||
@ -1557,7 +1509,6 @@ test('should return release plan milestones', async () => {
|
|||||||
{
|
{
|
||||||
name: 'my_feature_a',
|
name: 'my_feature_a',
|
||||||
environments: [
|
environments: [
|
||||||
{ name: 'default' },
|
|
||||||
{
|
{
|
||||||
name: 'development',
|
name: 'development',
|
||||||
totalMilestones: 3,
|
totalMilestones: 3,
|
||||||
@ -1569,6 +1520,5 @@ test('should return release plan milestones', async () => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
expect(body.features[0].environments[0].milestoneName).toBeUndefined();
|
expect(body.features[0].environments[1].milestoneName).toBeUndefined();
|
||||||
expect(body.features[0].environments[2].milestoneName).toBeUndefined();
|
|
||||||
});
|
});
|
||||||
|
@ -11,9 +11,7 @@ let app: IUnleashTest;
|
|||||||
let db: ITestDb;
|
let db: ITestDb;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
db = await dbInit('archive_test_serial', getLogger, {
|
db = await dbInit('archive_test_serial', getLogger);
|
||||||
dbInitMethod: 'legacy' as const,
|
|
||||||
});
|
|
||||||
app = await setupAppWithCustomConfig(
|
app = await setupAppWithCustomConfig(
|
||||||
db.stores,
|
db.stores,
|
||||||
{
|
{
|
||||||
@ -125,26 +123,6 @@ test('Should disable all environments when reviving a toggle', async () => {
|
|||||||
archived: true,
|
archived: true,
|
||||||
createdByUserId: 9999,
|
createdByUserId: 9999,
|
||||||
});
|
});
|
||||||
|
|
||||||
await db.stores.environmentStore.create({
|
|
||||||
name: 'development',
|
|
||||||
enabled: true,
|
|
||||||
type: 'development',
|
|
||||||
sortOrder: 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
await db.stores.environmentStore.create({
|
|
||||||
name: 'production',
|
|
||||||
enabled: true,
|
|
||||||
type: 'production',
|
|
||||||
sortOrder: 2,
|
|
||||||
});
|
|
||||||
|
|
||||||
await db.stores.featureEnvironmentStore.addEnvironmentToFeature(
|
|
||||||
'feat-proj-1',
|
|
||||||
'default',
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
await db.stores.featureEnvironmentStore.addEnvironmentToFeature(
|
await db.stores.featureEnvironmentStore.addEnvironmentToFeature(
|
||||||
'feat-proj-1',
|
'feat-proj-1',
|
||||||
'production',
|
'production',
|
||||||
|
@ -64,9 +64,6 @@ beforeAll(async () => {
|
|||||||
db = await dbInit(
|
db = await dbInit(
|
||||||
'feature_toggle_service_v2_service_serial',
|
'feature_toggle_service_v2_service_serial',
|
||||||
config.getLogger,
|
config.getLogger,
|
||||||
{
|
|
||||||
dbInitMethod: 'legacy' as const,
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
unleashConfig = config;
|
unleashConfig = config;
|
||||||
stores = db.stores;
|
stores = db.stores;
|
||||||
|
@ -17,9 +17,7 @@ let app: IUnleashTest;
|
|||||||
let db: ITestDb;
|
let db: ITestDb;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
db = await dbInit('feature_strategy_auth_api_serial', getLogger, {
|
db = await dbInit('feature_strategy_auth_api_serial', getLogger);
|
||||||
dbInitMethod: 'legacy' as const,
|
|
||||||
});
|
|
||||||
app = await setupAppWithAuth(
|
app = await setupAppWithAuth(
|
||||||
db.stores,
|
db.stores,
|
||||||
{
|
{
|
||||||
|
@ -87,9 +87,7 @@ const updateStrategy = async (
|
|||||||
};
|
};
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
db = await dbInit('feature_strategy_api_serial', getLogger, {
|
db = await dbInit('feature_strategy_api_serial', getLogger);
|
||||||
dbInitMethod: 'legacy' as const,
|
|
||||||
});
|
|
||||||
app = await setupAppWithCustomConfig(
|
app = await setupAppWithCustomConfig(
|
||||||
db.stores,
|
db.stores,
|
||||||
{
|
{
|
||||||
|
@ -27,9 +27,7 @@ let app: IUnleashTest;
|
|||||||
let db: ITestDb;
|
let db: ITestDb;
|
||||||
let frontendApiService: FrontendApiService;
|
let frontendApiService: FrontendApiService;
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
db = await dbInit('frontend_api', getLogger, {
|
db = await dbInit('frontend_api', getLogger);
|
||||||
dbInitMethod: 'legacy' as const,
|
|
||||||
});
|
|
||||||
app = await setupAppWithAuth(
|
app = await setupAppWithAuth(
|
||||||
db.stores,
|
db.stores,
|
||||||
{
|
{
|
||||||
|
@ -417,7 +417,7 @@ describe('bulk metrics', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('filters out metrics for environments we do not have access for. No auth setup so we can only access default env', async () => {
|
test('without access to production environment due to no auth setup, we can only access the default env', async () => {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
|
|
||||||
await request
|
await request
|
||||||
@ -437,7 +437,7 @@ describe('bulk metrics', () => {
|
|||||||
{
|
{
|
||||||
featureName: 'test_feature_two',
|
featureName: 'test_feature_two',
|
||||||
appName: 'test_application',
|
appName: 'test_application',
|
||||||
environment: 'development',
|
environment: 'production',
|
||||||
timestamp: startOfHour(now),
|
timestamp: startOfHour(now),
|
||||||
yes: 1000,
|
yes: 1000,
|
||||||
no: 800,
|
no: 800,
|
||||||
|
@ -13,9 +13,7 @@ let app: IUnleashTest;
|
|||||||
let db: ITestDb;
|
let db: ITestDb;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
db = await dbInit('advanced_playground', getLogger, {
|
db = await dbInit('advanced_playground', getLogger);
|
||||||
dbInitMethod: 'legacy' as const,
|
|
||||||
});
|
|
||||||
app = await setupAppWithCustomConfig(
|
app = await setupAppWithCustomConfig(
|
||||||
db.stores,
|
db.stores,
|
||||||
{
|
{
|
||||||
|
@ -5,6 +5,7 @@ import dbInit, {
|
|||||||
} from '../../../test/e2e/helpers/database-init.js';
|
} from '../../../test/e2e/helpers/database-init.js';
|
||||||
import NotFoundError from '../../error/notfound-error.js';
|
import NotFoundError from '../../error/notfound-error.js';
|
||||||
import {
|
import {
|
||||||
|
type IEnvironment,
|
||||||
type IUnleashStores,
|
type IUnleashStores,
|
||||||
SYSTEM_USER,
|
SYSTEM_USER,
|
||||||
SYSTEM_USER_AUDIT,
|
SYSTEM_USER_AUDIT,
|
||||||
@ -13,54 +14,48 @@ import NameExistsError from '../../error/name-exists-error.js';
|
|||||||
import type { EventService } from '../../services/index.js';
|
import type { EventService } from '../../services/index.js';
|
||||||
import { createEventsService } from '../events/createEventsService.js';
|
import { createEventsService } from '../events/createEventsService.js';
|
||||||
import { test, beforeAll, afterAll, expect } from 'vitest';
|
import { test, beforeAll, afterAll, expect } from 'vitest';
|
||||||
import { DEFAULT_ENV } from '../../server-impl.js';
|
|
||||||
let stores: IUnleashStores;
|
let stores: IUnleashStores;
|
||||||
let db: ITestDb;
|
let db: ITestDb;
|
||||||
let service: EnvironmentService;
|
let service: EnvironmentService;
|
||||||
let eventService: EventService;
|
let eventService: EventService;
|
||||||
|
let createdEnvironment: IEnvironment;
|
||||||
|
let withApprovals: IEnvironment;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
const config = createTestConfig();
|
const config = createTestConfig();
|
||||||
db = await dbInit('environment_service_serial', config.getLogger, {
|
db = await dbInit('environment_service_serial', config.getLogger);
|
||||||
dbInitMethod: 'legacy' as const,
|
|
||||||
});
|
|
||||||
stores = db.stores;
|
stores = db.stores;
|
||||||
eventService = createEventsService(db.rawDatabase, config);
|
eventService = createEventsService(db.rawDatabase, config);
|
||||||
service = new EnvironmentService(stores, config, eventService);
|
service = new EnvironmentService(stores, config, eventService);
|
||||||
|
|
||||||
|
createdEnvironment = await db.stores.environmentStore.create({
|
||||||
|
name: 'testenv',
|
||||||
|
type: 'production',
|
||||||
|
});
|
||||||
|
|
||||||
|
withApprovals = await db.stores.environmentStore.create({
|
||||||
|
name: 'approval_env',
|
||||||
|
type: 'production',
|
||||||
|
requiredApprovals: 1,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(async () => {
|
afterAll(async () => {
|
||||||
await db.destroy();
|
await db.destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Can get environment', async () => {
|
test('Can get environment', async () => {
|
||||||
const created = await db.stores.environmentStore.create({
|
const retrieved = await service.get(createdEnvironment.name);
|
||||||
name: 'testenv',
|
expect(retrieved).toEqual(createdEnvironment);
|
||||||
type: 'production',
|
|
||||||
});
|
|
||||||
|
|
||||||
const retrieved = await service.get('testenv');
|
|
||||||
expect(retrieved).toEqual(created);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Can get all', async () => {
|
test('Can get all', async () => {
|
||||||
await db.stores.environmentStore.create({
|
|
||||||
name: 'testenv2',
|
|
||||||
type: 'production',
|
|
||||||
});
|
|
||||||
|
|
||||||
const environments = await service.getAll();
|
const environments = await service.getAll();
|
||||||
expect(environments).toHaveLength(3); // the one we created plus 'default'
|
expect(environments).toHaveLength(4); // the 2 created plus 'development' and 'production''
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Can manage required approvals', async () => {
|
test('Can manage required approvals', async () => {
|
||||||
const created = await db.stores.environmentStore.create({
|
const retrieved = await service.get(withApprovals.name);
|
||||||
name: 'approval_env',
|
|
||||||
type: 'production',
|
|
||||||
requiredApprovals: 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
const retrieved = await service.get('approval_env');
|
|
||||||
|
|
||||||
await db.stores.environmentStore.update(
|
await db.stores.environmentStore.update(
|
||||||
{
|
{
|
||||||
type: 'production',
|
type: 'production',
|
||||||
@ -77,13 +72,16 @@ test('Can manage required approvals', async () => {
|
|||||||
const changeRequestEnvs =
|
const changeRequestEnvs =
|
||||||
await db.stores.environmentStore.getChangeRequestEnvironments([
|
await db.stores.environmentStore.getChangeRequestEnvironments([
|
||||||
'approval_env',
|
'approval_env',
|
||||||
DEFAULT_ENV,
|
'development',
|
||||||
'other',
|
'other',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
expect(retrieved).toEqual(created);
|
expect(retrieved).toEqual(withApprovals);
|
||||||
expect(updated).toEqual({ ...created, requiredApprovals: 2 });
|
expect(updated).toEqual({ ...withApprovals, requiredApprovals: 2 });
|
||||||
expect(groupRetrieved).toMatchObject({ ...created, requiredApprovals: 2 });
|
expect(groupRetrieved).toMatchObject({
|
||||||
|
...withApprovals,
|
||||||
|
requiredApprovals: 2,
|
||||||
|
});
|
||||||
expect(changeRequestEnvs).toEqual([
|
expect(changeRequestEnvs).toEqual([
|
||||||
{ name: 'approval_env', requiredApprovals: 2 },
|
{ name: 'approval_env', requiredApprovals: 2 },
|
||||||
]);
|
]);
|
||||||
@ -345,32 +343,22 @@ test('When given overrides should remap projects to override environments', asyn
|
|||||||
expect(projects).not.toContain('default');
|
expect(projects).not.toContain('default');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Override works correctly when enabling default and disabling prod and dev', async () => {
|
test('Override works correctly when enabling a custom environment and disabling prod and dev', async () => {
|
||||||
const defaultEnvironment = DEFAULT_ENV;
|
const newEnvironment = 'custom';
|
||||||
const prodEnvironment = 'production';
|
|
||||||
const devEnvironment = 'development';
|
|
||||||
|
|
||||||
await db.stores.environmentStore.create({
|
await db.stores.environmentStore.create({
|
||||||
name: prodEnvironment,
|
name: newEnvironment,
|
||||||
type: 'production',
|
type: 'production',
|
||||||
});
|
});
|
||||||
|
await service.toggleEnvironment(newEnvironment, true);
|
||||||
await db.stores.environmentStore.create({
|
await service.overrideEnabledProjects([newEnvironment]);
|
||||||
name: devEnvironment,
|
|
||||||
type: 'development',
|
|
||||||
});
|
|
||||||
await service.toggleEnvironment(prodEnvironment, true);
|
|
||||||
await service.toggleEnvironment(devEnvironment, true);
|
|
||||||
|
|
||||||
await service.overrideEnabledProjects([defaultEnvironment]);
|
|
||||||
|
|
||||||
const environments = await service.getAll();
|
const environments = await service.getAll();
|
||||||
const targetedEnvironment = environments.find(
|
const targetedEnvironment = environments.find(
|
||||||
(env) => env.name === defaultEnvironment,
|
(env) => env.name === newEnvironment,
|
||||||
);
|
);
|
||||||
|
|
||||||
const allOtherEnvironments = environments
|
const allOtherEnvironments = environments
|
||||||
.filter((x) => x.name !== defaultEnvironment)
|
.filter((x) => x.name !== newEnvironment)
|
||||||
.map((env) => env.enabled);
|
.map((env) => env.enabled);
|
||||||
const envNames = environments.map((x) => x.name);
|
const envNames = environments.map((x) => x.name);
|
||||||
|
|
||||||
|
@ -12,9 +12,7 @@ let app: IUnleashTest;
|
|||||||
let db: ITestDb;
|
let db: ITestDb;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
db = await dbInit('project_environments_api_serial', getLogger, {
|
db = await dbInit('project_environments_api_serial', getLogger);
|
||||||
dbInitMethod: 'legacy' as const,
|
|
||||||
});
|
|
||||||
app = await setupAppWithCustomConfig(
|
app = await setupAppWithCustomConfig(
|
||||||
db.stores,
|
db.stores,
|
||||||
{
|
{
|
||||||
@ -65,7 +63,7 @@ test('Should add environment to project', async () => {
|
|||||||
const environment = envs.find((env) => env.environment === 'test');
|
const environment = envs.find((env) => env.environment === 'test');
|
||||||
|
|
||||||
expect(environment).toBeDefined();
|
expect(environment).toBeDefined();
|
||||||
expect(envs).toHaveLength(2); // test + default
|
expect(envs).toHaveLength(3); // test + development + production
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Should validate environment', async () => {
|
test('Should validate environment', async () => {
|
||||||
|
@ -16,9 +16,7 @@ let projectStore: IProjectStore;
|
|||||||
const testDate = '2023-10-01T12:34:56.000Z';
|
const testDate = '2023-10-01T12:34:56.000Z';
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
db = await dbInit('projects_api_serial', getLogger, {
|
db = await dbInit('projects_api_serial', getLogger);
|
||||||
dbInitMethod: 'legacy' as const,
|
|
||||||
});
|
|
||||||
app = await setupAppWithCustomConfig(
|
app = await setupAppWithCustomConfig(
|
||||||
db.stores,
|
db.stores,
|
||||||
{
|
{
|
||||||
|
@ -121,7 +121,6 @@ beforeAll(async () => {
|
|||||||
anonymiseEventLog: true,
|
anonymiseEventLog: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
dbInitMethod: 'legacy' as const,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
db = await dbInit('segments_api_serial', getLogger, customOptions);
|
db = await dbInit('segments_api_serial', getLogger, customOptions);
|
||||||
|
@ -197,9 +197,7 @@ const createTestSegments = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
db = await dbInit('segments', getLogger, {
|
db = await dbInit('segments', getLogger);
|
||||||
dbInitMethod: 'legacy' as const,
|
|
||||||
});
|
|
||||||
app = await setupAppWithCustomConfig(
|
app = await setupAppWithCustomConfig(
|
||||||
db.stores,
|
db.stores,
|
||||||
{
|
{
|
||||||
|
@ -128,7 +128,7 @@ test('should collect metrics for updated toggles', async () => {
|
|||||||
|
|
||||||
const metrics = await prometheusRegister.metrics();
|
const metrics = await prometheusRegister.metrics();
|
||||||
expect(metrics).toMatch(
|
expect(metrics).toMatch(
|
||||||
/feature_toggle_update_total\{toggle="TestToggle",project="default",environment="default",environmentType="production",action="updated"\} 1/,
|
/feature_toggle_update_total\{toggle="TestToggle",project="default",environment="n\/a",environmentType="n\/a",action="updated"\} 1/,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -902,8 +902,8 @@ export function registerPrometheusMetrics(
|
|||||||
featureFlagUpdateTotal.increment({
|
featureFlagUpdateTotal.increment({
|
||||||
toggle: featureName,
|
toggle: featureName,
|
||||||
project,
|
project,
|
||||||
environment: 'default',
|
environment: 'n/a',
|
||||||
environmentType: 'production',
|
environmentType: 'n/a',
|
||||||
action: 'updated',
|
action: 'updated',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -27,9 +27,9 @@ export const apiTokenSchema = {
|
|||||||
},
|
},
|
||||||
environment: {
|
environment: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description:
|
description: 'The environment the token has access to.',
|
||||||
'The environment the token has access to. `*` if it has access to all environments.',
|
|
||||||
example: 'development',
|
example: 'development',
|
||||||
|
default: 'development',
|
||||||
},
|
},
|
||||||
project: {
|
project: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import joi from 'joi';
|
import joi from 'joi';
|
||||||
import { ALL } from '../types/models/api-token.js';
|
import { ALL } from '../types/models/api-token.js';
|
||||||
import { ApiTokenType } from '../types/model.js';
|
import { ApiTokenType } from '../types/model.js';
|
||||||
import { DEFAULT_ENV } from '../util/constants.js';
|
|
||||||
|
|
||||||
export const createApiToken = joi
|
export const createApiToken = joi
|
||||||
.object()
|
.object()
|
||||||
@ -14,10 +13,6 @@ export const createApiToken = joi
|
|||||||
.valid(ApiTokenType.CLIENT, ApiTokenType.FRONTEND),
|
.valid(ApiTokenType.CLIENT, ApiTokenType.FRONTEND),
|
||||||
expiresAt: joi.date().optional(),
|
expiresAt: joi.date().optional(),
|
||||||
projects: joi.array().min(1).optional().default([ALL]),
|
projects: joi.array().min(1).optional().default([ALL]),
|
||||||
environment: joi.when('type', {
|
environment: joi.string().optional().default('development'),
|
||||||
is: joi.string().valid(ApiTokenType.CLIENT, ApiTokenType.FRONTEND),
|
|
||||||
then: joi.string().optional().default(DEFAULT_ENV),
|
|
||||||
otherwise: joi.string().optional().default(ALL),
|
|
||||||
}),
|
|
||||||
})
|
})
|
||||||
.options({ stripUnknown: true, allowUnknown: false, abortEarly: false });
|
.options({ stripUnknown: true, allowUnknown: false, abortEarly: false });
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
export const DEFAULT_ENV = 'default';
|
export const DEFAULT_ENV = 'development';
|
||||||
|
|
||||||
export const ALL_PROJECTS = '*';
|
export const ALL_PROJECTS = '*';
|
||||||
export const ALL_ENVS = '*';
|
export const ALL_ENVS = '*';
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
exports.up = function(db, cb) {
|
||||||
|
db.runSql(
|
||||||
|
`SELECT count(*) as count FROM events;`,
|
||||||
|
(err, results) => {
|
||||||
|
if (Number(results.rows[0].count) === 4) {
|
||||||
|
// If there are exactly 4 events, it means this is a new install
|
||||||
|
db.runSql(
|
||||||
|
`DELETE FROM environments WHERE name = 'default';`,
|
||||||
|
|
||||||
|
cb);
|
||||||
|
} else {
|
||||||
|
// If there are not exactly 4 events, do nothing
|
||||||
|
cb();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.down = function(db, cb) {
|
||||||
|
cb();
|
||||||
|
};
|
@ -11,9 +11,7 @@ let db: ITestDb;
|
|||||||
let app: IUnleashTest;
|
let app: IUnleashTest;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
db = await dbInit('context_api_serial', getLogger, {
|
db = await dbInit('context_api_serial', getLogger);
|
||||||
dbInitMethod: 'legacy' as const,
|
|
||||||
});
|
|
||||||
app = await setupAppWithCustomConfig(
|
app = await setupAppWithCustomConfig(
|
||||||
db.stores,
|
db.stores,
|
||||||
{
|
{
|
||||||
@ -34,13 +32,15 @@ afterAll(async () => {
|
|||||||
|
|
||||||
test('gets all context fields', async () => {
|
test('gets all context fields', async () => {
|
||||||
expect.assertions(1);
|
expect.assertions(1);
|
||||||
return app.request
|
const { body } = await app.request
|
||||||
.get('/api/admin/context')
|
.get('/api/admin/context')
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200);
|
||||||
.expect((res) => {
|
|
||||||
expect(res.body.length).toBe(3);
|
// because tests share the database, we might have more fields than expected
|
||||||
});
|
expect(body.map((field) => field.name)).toEqual(
|
||||||
|
expect.arrayContaining(['environment', 'userId', 'sessionId']),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('get the context field', async () => {
|
test('get the context field', async () => {
|
||||||
|
@ -15,9 +15,7 @@ let stores: IUnleashStores;
|
|||||||
let refreshDbMetrics: () => Promise<void>;
|
let refreshDbMetrics: () => Promise<void>;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
db = await dbInit('instance_admin_api_serial', getLogger, {
|
db = await dbInit('instance_admin_api_serial', getLogger);
|
||||||
dbInitMethod: 'legacy' as const,
|
|
||||||
});
|
|
||||||
stores = db.stores;
|
stores = db.stores;
|
||||||
await stores.settingStore.insert('instanceInfo', { id: 'test-static' });
|
await stores.settingStore.insert('instanceInfo', { id: 'test-static' });
|
||||||
app = await setupAppWithCustomConfig(
|
app = await setupAppWithCustomConfig(
|
||||||
@ -126,7 +124,7 @@ test('should return signed instance statistics', async () => {
|
|||||||
.expect((res) => {
|
.expect((res) => {
|
||||||
expect(res.body.instanceId).toBe('test-static');
|
expect(res.body.instanceId).toBe('test-static');
|
||||||
expect(res.body.sum).toBe(
|
expect(res.body.sum).toBe(
|
||||||
'5ba2cb7c3e29f4e5b3382c560b92b837f3603dc7db73a501ec331c7f0ed17bd0',
|
'd9bac94bba7afa20d98f0a9d54a84b79a6668f8103b8f89db85d05d38e84f519',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -11,9 +11,7 @@ let app: IUnleashTest;
|
|||||||
let db: ITestDb;
|
let db: ITestDb;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
db = await dbInit('metrics_serial', getLogger, {
|
db = await dbInit('metrics_serial', getLogger);
|
||||||
dbInitMethod: 'legacy' as const,
|
|
||||||
});
|
|
||||||
app = await setupAppWithCustomConfig(
|
app = await setupAppWithCustomConfig(
|
||||||
db.stores,
|
db.stores,
|
||||||
{
|
{
|
||||||
@ -179,9 +177,9 @@ test('should save multiple projects from token', async () => {
|
|||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200);
|
.expect(200);
|
||||||
|
|
||||||
expect(body).toMatchObject({
|
expect(body.applications).toEqual(
|
||||||
applications: [
|
expect.arrayContaining([
|
||||||
{
|
expect.objectContaining({
|
||||||
appName: 'multi-project-app',
|
appName: 'multi-project-app',
|
||||||
usage: [
|
usage: [
|
||||||
{
|
{
|
||||||
@ -193,8 +191,7 @@ test('should save multiple projects from token', async () => {
|
|||||||
project: 'mainProject',
|
project: 'mainProject',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
}),
|
||||||
],
|
]),
|
||||||
total: 1,
|
);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@ -12,9 +12,7 @@ let db: ITestDb;
|
|||||||
let user: IUser;
|
let user: IUser;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
db = await dbInit('project_health_api_serial', getLogger, {
|
db = await dbInit('project_health_api_serial', getLogger);
|
||||||
dbInitMethod: 'legacy' as const,
|
|
||||||
});
|
|
||||||
app = await setupAppWithCustomConfig(
|
app = await setupAppWithCustomConfig(
|
||||||
db.stores,
|
db.stores,
|
||||||
{
|
{
|
||||||
|
@ -9,9 +9,7 @@ let app: IUnleashTest;
|
|||||||
let db: ITestDb;
|
let db: ITestDb;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
db = await dbInit('strategy_api_serial', getLogger, {
|
db = await dbInit('strategy_api_serial', getLogger);
|
||||||
dbInitMethod: 'legacy' as const,
|
|
||||||
});
|
|
||||||
app = await setupAppWithCustomConfig(db.stores, {
|
app = await setupAppWithCustomConfig(db.stores, {
|
||||||
experimental: {
|
experimental: {
|
||||||
flags: {
|
flags: {
|
||||||
@ -19,6 +17,16 @@ beforeAll(async () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
await db.stores.strategyStore.createStrategy({
|
||||||
|
name: 'toBeDeleted',
|
||||||
|
description: 'toBeDeleted strategy',
|
||||||
|
parameters: [],
|
||||||
|
});
|
||||||
|
await db.stores.strategyStore.createStrategy({
|
||||||
|
name: 'toBeUpdated',
|
||||||
|
description: 'toBeUpdated strategy',
|
||||||
|
parameters: [],
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(async () => {
|
afterAll(async () => {
|
||||||
@ -29,13 +37,11 @@ afterAll(async () => {
|
|||||||
test('gets all strategies', async () => {
|
test('gets all strategies', async () => {
|
||||||
expect.assertions(1);
|
expect.assertions(1);
|
||||||
|
|
||||||
return app.request
|
const { body } = await app.request
|
||||||
.get('/api/admin/strategies')
|
.get('/api/admin/strategies')
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200);
|
||||||
.expect((res) => {
|
expect(body.strategies).toHaveLength(6);
|
||||||
expect(res.body.strategies).toHaveLength(3);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('gets a strategy by name', async () => {
|
test('gets a strategy by name', async () => {
|
||||||
@ -93,9 +99,7 @@ test('refuses to create a strategy with an existing name', async () => {
|
|||||||
test('deletes a new strategy', async () => {
|
test('deletes a new strategy', async () => {
|
||||||
expect.assertions(0);
|
expect.assertions(0);
|
||||||
|
|
||||||
return app.request
|
return app.request.delete('/api/admin/strategies/toBeDeleted').expect(200);
|
||||||
.delete('/api/admin/strategies/usersWithEmail')
|
|
||||||
.expect(200);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("can't delete a strategy that dose not exist", async () => {
|
test("can't delete a strategy that dose not exist", async () => {
|
||||||
@ -108,10 +112,10 @@ test('updates a exiting strategy', async () => {
|
|||||||
expect.assertions(0);
|
expect.assertions(0);
|
||||||
|
|
||||||
return app.request
|
return app.request
|
||||||
.put('/api/admin/strategies/default')
|
.put('/api/admin/strategies/toBeUpdated')
|
||||||
.send({
|
.send({
|
||||||
name: 'default',
|
name: 'toBeUpdated',
|
||||||
description: 'Default is the best!',
|
description: 'toBeUpdated is the best!',
|
||||||
parameters: [],
|
parameters: [],
|
||||||
})
|
})
|
||||||
.set('Content-Type', 'application/json')
|
.set('Content-Type', 'application/json')
|
||||||
|
@ -15,9 +15,7 @@ const userId = -9999;
|
|||||||
const projectId = 'default';
|
const projectId = 'default';
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
db = await dbInit('feature_env_api_client', getLogger, {
|
db = await dbInit('feature_env_api_client', getLogger);
|
||||||
dbInitMethod: 'legacy' as const,
|
|
||||||
});
|
|
||||||
app = await setupAppWithCustomConfig(db.stores, {}, db.rawDatabase);
|
app = await setupAppWithCustomConfig(db.stores, {}, db.rawDatabase);
|
||||||
|
|
||||||
await app.services.featureToggleService.createFeatureToggle(
|
await app.services.featureToggleService.createFeatureToggle(
|
||||||
|
@ -152,26 +152,26 @@ describe.each([
|
|||||||
.expect(200);
|
.expect(200);
|
||||||
|
|
||||||
if (etagVariant.feature_enabled) {
|
if (etagVariant.feature_enabled) {
|
||||||
expect(res.headers.etag).toBe(`"61824cd0:16:${etagVariant.name}"`);
|
expect(res.headers.etag).toBe(`"76d8bb0e:16:${etagVariant.name}"`);
|
||||||
expect(res.body.meta.etag).toBe(
|
expect(res.body.meta.etag).toBe(
|
||||||
`"61824cd0:16:${etagVariant.name}"`,
|
`"76d8bb0e:16:${etagVariant.name}"`,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
expect(res.headers.etag).toBe('"61824cd0:16"');
|
expect(res.headers.etag).toBe('"76d8bb0e:16"');
|
||||||
expect(res.body.meta.etag).toBe('"61824cd0:16"');
|
expect(res.body.meta.etag).toBe('"76d8bb0e:16"');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test(`returns ${etagVariant.feature_enabled ? 200 : 304} for pre-calculated hash${etagVariant.feature_enabled ? ' because hash changed' : ''}`, async () => {
|
test(`returns ${etagVariant.feature_enabled ? 200 : 304} for pre-calculated hash${etagVariant.feature_enabled ? ' because hash changed' : ''}`, async () => {
|
||||||
const res = await app.request
|
const res = await app.request
|
||||||
.get('/api/client/features')
|
.get('/api/client/features')
|
||||||
.set('if-none-match', '"61824cd0:16"')
|
.set('if-none-match', '"76d8bb0e:16"')
|
||||||
.expect(etagVariant.feature_enabled ? 200 : 304);
|
.expect(etagVariant.feature_enabled ? 200 : 304);
|
||||||
|
|
||||||
if (etagVariant.feature_enabled) {
|
if (etagVariant.feature_enabled) {
|
||||||
expect(res.headers.etag).toBe(`"61824cd0:16:${etagVariant.name}"`);
|
expect(res.headers.etag).toBe(`"76d8bb0e:16:${etagVariant.name}"`);
|
||||||
expect(res.body.meta.etag).toBe(
|
expect(res.body.meta.etag).toBe(
|
||||||
`"61824cd0:16:${etagVariant.name}"`,
|
`"76d8bb0e:16:${etagVariant.name}"`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -193,13 +193,13 @@ describe.each([
|
|||||||
.expect(200);
|
.expect(200);
|
||||||
|
|
||||||
if (etagVariant.feature_enabled) {
|
if (etagVariant.feature_enabled) {
|
||||||
expect(res.headers.etag).toBe(`"61824cd0:16:${etagVariant.name}"`);
|
expect(res.headers.etag).toBe(`"76d8bb0e:16:${etagVariant.name}"`);
|
||||||
expect(res.body.meta.etag).toBe(
|
expect(res.body.meta.etag).toBe(
|
||||||
`"61824cd0:16:${etagVariant.name}"`,
|
`"76d8bb0e:16:${etagVariant.name}"`,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
expect(res.headers.etag).toBe('"61824cd0:16"');
|
expect(res.headers.etag).toBe('"76d8bb0e:16"');
|
||||||
expect(res.body.meta.etag).toBe('"61824cd0:16"');
|
expect(res.body.meta.etag).toBe('"76d8bb0e:16"');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -16,9 +16,7 @@ let db: ITestDb;
|
|||||||
|
|
||||||
let defaultToken: IApiToken;
|
let defaultToken: IApiToken;
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
db = await dbInit('metrics_two_api_client', getLogger, {
|
db = await dbInit('metrics_two_api_client', getLogger);
|
||||||
dbInitMethod: 'legacy' as const,
|
|
||||||
});
|
|
||||||
app = await setupAppWithAuth(db.stores, {}, db.rawDatabase);
|
app = await setupAppWithAuth(db.stores, {}, db.rawDatabase);
|
||||||
defaultToken =
|
defaultToken =
|
||||||
await app.services.apiTokenService.createApiTokenWithProjects({
|
await app.services.apiTokenService.createApiTokenWithProjects({
|
||||||
|
@ -15,9 +15,7 @@ let app: IUnleashTest;
|
|||||||
let db: ITestDb;
|
let db: ITestDb;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
db = await dbInit('register_client', getLogger, {
|
db = await dbInit('register_client', getLogger);
|
||||||
dbInitMethod: 'legacy' as const,
|
|
||||||
});
|
|
||||||
app = await setupApp(db.stores);
|
app = await setupApp(db.stores);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,16 +1,11 @@
|
|||||||
import { log } from 'db-migrate-shared';
|
import { log } from 'db-migrate-shared';
|
||||||
import { migrateDb } from '../../../migrator.js';
|
|
||||||
import { createStores } from '../../../lib/db/index.js';
|
import { createStores } from '../../../lib/db/index.js';
|
||||||
import { createDb } from '../../../lib/db/db-pool.js';
|
import { createDb } from '../../../lib/db/db-pool.js';
|
||||||
import { getDbConfig } from './database-config.js';
|
import { getDbConfig } from './database-config.js';
|
||||||
import { createTestConfig } from '../../config/test-config.js';
|
import { createTestConfig } from '../../config/test-config.js';
|
||||||
import dbState from './database.json' with { type: 'json' };
|
|
||||||
import type { LogProvider } from '../../../lib/logger.js';
|
import type { LogProvider } from '../../../lib/logger.js';
|
||||||
import noLoggerProvider from '../../fixtures/no-logger.js';
|
import noLoggerProvider from '../../fixtures/no-logger.js';
|
||||||
import type EnvironmentStore from '../../../lib/features/project-environments/environment-store.js';
|
|
||||||
import type { IUnleashStores } from '../../../lib/types/index.js';
|
import type { IUnleashStores } from '../../../lib/types/index.js';
|
||||||
import type { IFeatureEnvironmentStore } from '../../../lib/types/stores/feature-environment-store.js';
|
|
||||||
import { DEFAULT_ENV } from '../../../lib/util/constants.js';
|
|
||||||
import type {
|
import type {
|
||||||
IUnleashConfig,
|
IUnleashConfig,
|
||||||
IUnleashOptions,
|
IUnleashOptions,
|
||||||
@ -27,72 +22,6 @@ process.setMaxListeners(0);
|
|||||||
|
|
||||||
export const testDbPrefix = 'unleashtestdb_';
|
export const testDbPrefix = 'unleashtestdb_';
|
||||||
|
|
||||||
async function getDefaultEnvRolePermissions(knex) {
|
|
||||||
return knex.table('role_permission').whereIn('environment', ['default']);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function restoreRolePermissions(knex, rolePermissions) {
|
|
||||||
await knex.table('role_permission').insert(rolePermissions);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function resetDatabase(knex) {
|
|
||||||
return Promise.all([
|
|
||||||
knex
|
|
||||||
.table('environments')
|
|
||||||
.del(), // deletes role permissions transitively
|
|
||||||
knex.table('strategies').del(),
|
|
||||||
knex.table('features').del(),
|
|
||||||
knex.table('client_applications').del(),
|
|
||||||
knex.table('client_instances').del(),
|
|
||||||
knex.table('context_fields').del(),
|
|
||||||
knex.table('users').del(),
|
|
||||||
knex.table('projects').del(),
|
|
||||||
knex.table('tags').del(),
|
|
||||||
knex.table('tag_types').del(),
|
|
||||||
knex.table('addons').del(),
|
|
||||||
knex.table('users').del(),
|
|
||||||
knex.table('api_tokens').del(),
|
|
||||||
knex.table('api_token_project').del(),
|
|
||||||
knex
|
|
||||||
.table('reset_tokens')
|
|
||||||
.del(),
|
|
||||||
// knex.table('settings').del(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createStrategies(store) {
|
|
||||||
return dbState.strategies.map((s) => store.createStrategy(s));
|
|
||||||
}
|
|
||||||
|
|
||||||
function createContextFields(store) {
|
|
||||||
return dbState.contextFields.map((c) => store.create(c));
|
|
||||||
}
|
|
||||||
|
|
||||||
function createProjects(store) {
|
|
||||||
return dbState.projects.map((i) => store.create(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
function createTagTypes(store) {
|
|
||||||
return dbState.tag_types.map((t) => store.createTagType(t));
|
|
||||||
}
|
|
||||||
|
|
||||||
async function connectProject(store: IFeatureEnvironmentStore): Promise<void> {
|
|
||||||
await store.connectProject(DEFAULT_ENV, 'default');
|
|
||||||
}
|
|
||||||
|
|
||||||
async function createEnvironments(store: EnvironmentStore): Promise<void> {
|
|
||||||
await Promise.all(dbState.environments.map(async (e) => store.create(e)));
|
|
||||||
}
|
|
||||||
|
|
||||||
async function setupDatabase(stores) {
|
|
||||||
await createEnvironments(stores.environmentStore);
|
|
||||||
await Promise.all(createStrategies(stores.strategyStore));
|
|
||||||
await Promise.all(createContextFields(stores.contextFieldStore));
|
|
||||||
await Promise.all(createProjects(stores.projectStore));
|
|
||||||
await Promise.all(createTagTypes(stores.tagTypeStore));
|
|
||||||
await connectProject(stores.featureEnvironmentStore);
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ITestDb {
|
export interface ITestDb {
|
||||||
config: IUnleashConfig;
|
config: IUnleashConfig;
|
||||||
stores: IUnleashStores;
|
stores: IUnleashStores;
|
||||||
@ -102,7 +31,6 @@ export interface ITestDb {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type DBTestOptions = {
|
type DBTestOptions = {
|
||||||
dbInitMethod?: 'legacy' | 'template';
|
|
||||||
stopMigrationAt?: string; // filename where migration should stop
|
stopMigrationAt?: string; // filename where migration should stop
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -112,16 +40,12 @@ export default async function init(
|
|||||||
configOverride: Partial<IUnleashOptions & DBTestOptions> = {},
|
configOverride: Partial<IUnleashOptions & DBTestOptions> = {},
|
||||||
): Promise<ITestDb> {
|
): Promise<ITestDb> {
|
||||||
const testDbName = `${testDbPrefix}${uuidv4().replace(/-/g, '')}`;
|
const testDbName = `${testDbPrefix}${uuidv4().replace(/-/g, '')}`;
|
||||||
const useDbTemplate =
|
|
||||||
(configOverride.dbInitMethod ?? 'template') === 'template';
|
|
||||||
const testDBTemplateName = process.env.TEST_DB_TEMPLATE_NAME;
|
const testDBTemplateName = process.env.TEST_DB_TEMPLATE_NAME;
|
||||||
const config = createTestConfig({
|
const config = createTestConfig({
|
||||||
db: {
|
db: {
|
||||||
...getDbConfig(),
|
...getDbConfig(),
|
||||||
pool: { min: 1, max: 4 },
|
pool: { min: 1, max: 4 },
|
||||||
...(useDbTemplate
|
database: testDbName,
|
||||||
? { database: testDbName }
|
|
||||||
: { schema: databaseSchema }),
|
|
||||||
ssl: false,
|
ssl: false,
|
||||||
},
|
},
|
||||||
...configOverride,
|
...configOverride,
|
||||||
@ -130,7 +54,6 @@ export default async function init(
|
|||||||
|
|
||||||
log.setLogLevel('error');
|
log.setLogLevel('error');
|
||||||
|
|
||||||
if (useDbTemplate) {
|
|
||||||
if (!testDBTemplateName) {
|
if (!testDBTemplateName) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'TEST_DB_TEMPLATE_NAME environment variable is not set',
|
'TEST_DB_TEMPLATE_NAME environment variable is not set',
|
||||||
@ -145,40 +68,16 @@ export default async function init(
|
|||||||
await client.query(`ALTER DATABASE ${testDbName} SET TIMEZONE TO UTC`);
|
await client.query(`ALTER DATABASE ${testDbName} SET TIMEZONE TO UTC`);
|
||||||
|
|
||||||
await client.end();
|
await client.end();
|
||||||
} else {
|
|
||||||
const db = createDb(config);
|
|
||||||
|
|
||||||
await db.raw(`DROP SCHEMA IF EXISTS ${config.db.schema} CASCADE`);
|
|
||||||
await db.raw(`CREATE SCHEMA IF NOT EXISTS ${config.db.schema}`);
|
|
||||||
await migrateDb(config, configOverride.stopMigrationAt);
|
|
||||||
await db.destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
const testDb = createDb(config);
|
const testDb = createDb(config);
|
||||||
const stores = createStores(config, testDb);
|
const stores = createStores(config, testDb);
|
||||||
stores.eventStore.setMaxListeners(0);
|
stores.eventStore.setMaxListeners(0);
|
||||||
|
|
||||||
if (!useDbTemplate) {
|
|
||||||
const defaultRolePermissions =
|
|
||||||
await getDefaultEnvRolePermissions(testDb);
|
|
||||||
await resetDatabase(testDb);
|
|
||||||
await setupDatabase(stores);
|
|
||||||
await restoreRolePermissions(testDb, defaultRolePermissions);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
config,
|
config,
|
||||||
rawDatabase: testDb,
|
rawDatabase: testDb,
|
||||||
stores,
|
stores,
|
||||||
reset: async () => {
|
reset: async () => {},
|
||||||
if (!useDbTemplate) {
|
|
||||||
const defaultRolePermissions =
|
|
||||||
await getDefaultEnvRolePermissions(testDb);
|
|
||||||
await resetDatabase(testDb);
|
|
||||||
await setupDatabase(stores);
|
|
||||||
await restoreRolePermissions(testDb, defaultRolePermissions);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
destroy: async () => {
|
destroy: async () => {
|
||||||
return new Promise<void>((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
testDb.destroy((error) => (error ? reject(error) : resolve()));
|
testDb.destroy((error) => (error ? reject(error) : resolve()));
|
||||||
|
@ -8,9 +8,7 @@ let stores: IUnleashStores;
|
|||||||
let db: ITestDb;
|
let db: ITestDb;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
db = await dbInit('api_token_store_serial', getLogger, {
|
db = await dbInit('api_token_store_serial', getLogger);
|
||||||
dbInitMethod: 'legacy' as const,
|
|
||||||
});
|
|
||||||
stores = db.stores;
|
stores = db.stores;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user