mirror of
https://github.com/Unleash/unleash.git
synced 2025-04-10 01:16:39 +02:00
251 lines
7.4 KiB
TypeScript
251 lines
7.4 KiB
TypeScript
import dbInit, { ITestDb } from '../../../test/e2e/helpers/database-init';
|
|
import {
|
|
IUnleashTest,
|
|
setupAppWithCustomConfig,
|
|
} from '../../../test/e2e/helpers/test-helper';
|
|
import getLogger from '../../../test/fixtures/no-logger';
|
|
import { FeatureSearchQueryParameters } from '../../openapi/spec/feature-search-query-parameters';
|
|
|
|
let app: IUnleashTest;
|
|
let db: ITestDb;
|
|
|
|
beforeAll(async () => {
|
|
db = await dbInit('feature_search', getLogger);
|
|
app = await setupAppWithCustomConfig(
|
|
db.stores,
|
|
{
|
|
experimental: {
|
|
flags: {
|
|
strictSchemaValidation: true,
|
|
featureSearchAPI: true,
|
|
},
|
|
},
|
|
},
|
|
db.rawDatabase,
|
|
);
|
|
});
|
|
|
|
afterAll(async () => {
|
|
await app.destroy();
|
|
await db.destroy();
|
|
});
|
|
|
|
beforeEach(async () => {
|
|
await db.stores.featureToggleStore.deleteAll();
|
|
});
|
|
|
|
const searchFeatures = async (
|
|
{ query = '', projectId = 'default' }: FeatureSearchQueryParameters,
|
|
expectedCode = 200,
|
|
) => {
|
|
return app.request
|
|
.get(`/api/admin/search/features?query=${query}&projectId=${projectId}`)
|
|
.expect(expectedCode);
|
|
};
|
|
|
|
const searchFeaturesWithCursor = async (
|
|
{
|
|
query = '',
|
|
projectId = 'default',
|
|
cursor = '',
|
|
limit = 10,
|
|
}: FeatureSearchQueryParameters,
|
|
expectedCode = 200,
|
|
) => {
|
|
return app.request
|
|
.get(
|
|
`/api/admin/search/features?query=${query}&projectId=${projectId}&cursor=${cursor}&limit=${limit}`,
|
|
)
|
|
.expect(expectedCode);
|
|
};
|
|
|
|
const filterFeaturesByType = async (types: string[], expectedCode = 200) => {
|
|
const typeParams = types.map((type) => `type[]=${type}`).join('&');
|
|
return app.request
|
|
.get(`/api/admin/search/features?${typeParams}`)
|
|
.expect(expectedCode);
|
|
};
|
|
|
|
const filterFeaturesByTag = async (tags: string[], expectedCode = 200) => {
|
|
const tagParams = tags.map((tag) => `tag[]=${tag}`).join('&');
|
|
return app.request
|
|
.get(`/api/admin/search/features?${tagParams}`)
|
|
.expect(expectedCode);
|
|
};
|
|
|
|
const filterFeaturesByEnvironmentStatus = async (
|
|
environmentStatuses: string[],
|
|
expectedCode = 200,
|
|
) => {
|
|
const statuses = environmentStatuses
|
|
.map((status) => `status[]=${status}`)
|
|
.join('&');
|
|
return app.request
|
|
.get(`/api/admin/search/features?${statuses}`)
|
|
.expect(expectedCode);
|
|
};
|
|
|
|
const searchFeaturesWithoutQueryParams = async (expectedCode = 200) => {
|
|
return app.request.get(`/api/admin/search/features`).expect(expectedCode);
|
|
};
|
|
|
|
test('should search matching features by name', async () => {
|
|
await app.createFeature('my_feature_a');
|
|
await app.createFeature('my_feature_b');
|
|
await app.createFeature('my_feat_c');
|
|
|
|
const { body } = await searchFeatures({ query: 'feature' });
|
|
|
|
expect(body).toMatchObject({
|
|
features: [{ name: 'my_feature_a' }, { name: 'my_feature_b' }],
|
|
});
|
|
});
|
|
|
|
test('should paginate with cursor', async () => {
|
|
await app.createFeature('my_feature_a');
|
|
await app.createFeature('my_feature_b');
|
|
await app.createFeature('my_feature_c');
|
|
await app.createFeature('my_feature_d');
|
|
|
|
const { body: firstPage } = await searchFeaturesWithCursor({
|
|
query: 'feature',
|
|
cursor: '',
|
|
limit: 2,
|
|
});
|
|
const nextCursor =
|
|
firstPage.features[firstPage.features.length - 1].createdAt;
|
|
|
|
expect(firstPage).toMatchObject({
|
|
features: [{ name: 'my_feature_a' }, { name: 'my_feature_b' }],
|
|
});
|
|
|
|
const { body: secondPage } = await searchFeaturesWithCursor({
|
|
query: 'feature',
|
|
cursor: nextCursor,
|
|
limit: 2,
|
|
});
|
|
|
|
expect(secondPage).toMatchObject({
|
|
features: [{ name: 'my_feature_c' }, { name: 'my_feature_d' }],
|
|
});
|
|
const lastCursor =
|
|
secondPage.features[secondPage.features.length - 1].createdAt;
|
|
|
|
const { body: lastPage } = await searchFeaturesWithCursor({
|
|
query: 'feature',
|
|
cursor: lastCursor,
|
|
limit: 2,
|
|
});
|
|
expect(lastPage).toMatchObject({
|
|
features: [],
|
|
});
|
|
});
|
|
|
|
test('should filter features by type', async () => {
|
|
await app.createFeature({ name: 'my_feature_a', type: 'release' });
|
|
await app.createFeature({ name: 'my_feature_b', type: 'experimental' });
|
|
|
|
const { body } = await filterFeaturesByType([
|
|
'experimental',
|
|
'kill-switch',
|
|
]);
|
|
|
|
expect(body).toMatchObject({
|
|
features: [{ name: 'my_feature_b' }],
|
|
});
|
|
});
|
|
|
|
test('should filter features by tag', async () => {
|
|
await app.createFeature('my_feature_a');
|
|
await app.createFeature('my_feature_b');
|
|
await app.addTag('my_feature_a', { type: 'simple', value: 'my_tag' });
|
|
|
|
const { body } = await filterFeaturesByTag(['simple:my_tag']);
|
|
|
|
expect(body).toMatchObject({
|
|
features: [{ name: 'my_feature_a' }],
|
|
});
|
|
});
|
|
|
|
test('should filter features by environment status', async () => {
|
|
await app.createFeature('my_feature_a');
|
|
await app.createFeature('my_feature_b');
|
|
await app.enableFeature('my_feature_a', 'default');
|
|
|
|
const { body } = await filterFeaturesByEnvironmentStatus([
|
|
'default:enabled',
|
|
'nonexistentEnv:disabled',
|
|
'default:wrongStatus',
|
|
]);
|
|
|
|
expect(body).toMatchObject({
|
|
features: [{ name: 'my_feature_a' }],
|
|
});
|
|
});
|
|
|
|
test('filter with invalid tag should ignore filter', async () => {
|
|
await app.createFeature('my_feature_a');
|
|
await app.createFeature('my_feature_b');
|
|
await app.addTag('my_feature_a', { type: 'simple', value: 'my_tag' });
|
|
|
|
const { body } = await filterFeaturesByTag(['simple']);
|
|
|
|
expect(body).toMatchObject({
|
|
features: [{ name: 'my_feature_a' }, { name: 'my_feature_b' }],
|
|
});
|
|
});
|
|
|
|
test('should search matching features by tag', async () => {
|
|
await app.createFeature('my_feature_a');
|
|
await app.createFeature('my_feature_b');
|
|
await app.addTag('my_feature_a', { type: 'simple', value: 'my_tag' });
|
|
|
|
const { body: fullMatch } = await searchFeatures({
|
|
query: 'simple:my_tag',
|
|
});
|
|
const { body: tagTypeMatch } = await searchFeatures({ query: 'simple' });
|
|
const { body: tagValueMatch } = await searchFeatures({ query: 'my_tag' });
|
|
const { body: partialTagMatch } = await searchFeatures({ query: 'e:m' });
|
|
|
|
expect(fullMatch).toMatchObject({
|
|
features: [{ name: 'my_feature_a' }],
|
|
});
|
|
expect(tagTypeMatch).toMatchObject({
|
|
features: [{ name: 'my_feature_a' }],
|
|
});
|
|
expect(tagValueMatch).toMatchObject({
|
|
features: [{ name: 'my_feature_a' }],
|
|
});
|
|
expect(partialTagMatch).toMatchObject({
|
|
features: [{ name: 'my_feature_a' }],
|
|
});
|
|
});
|
|
|
|
test('should return empty features', async () => {
|
|
const { body } = await searchFeatures({ query: '' });
|
|
expect(body).toMatchObject({ features: [] });
|
|
});
|
|
|
|
test('should not search features from another project', async () => {
|
|
await app.createFeature('my_feature_a');
|
|
await app.createFeature('my_feature_b');
|
|
|
|
const { body } = await searchFeatures({
|
|
query: '',
|
|
projectId: 'another_project',
|
|
});
|
|
|
|
expect(body).toMatchObject({ features: [] });
|
|
});
|
|
|
|
test('should return features without query', async () => {
|
|
await app.createFeature('my_feature_a');
|
|
await app.createFeature('my_feature_b');
|
|
|
|
const { body } = await searchFeaturesWithoutQueryParams();
|
|
|
|
expect(body).toMatchObject({
|
|
features: [{ name: 'my_feature_a' }, { name: 'my_feature_b' }],
|
|
});
|
|
});
|