Skip to content

Commit

Permalink
Chore/increase client api test coverage (#8950)
Browse files Browse the repository at this point in the history
Added more tests around specific plans. Also added snapshot as per our
conversation @gastonfournier, but I'm unsure how much value it will give
because it seems that the tests should already catch this using
respondWithValidation and the OpenAPI schema. The problem here is that
empty array is a valid state, so there were no reason for the schema to
break the tests.
  • Loading branch information
FredrikOseberg authored Dec 12, 2024
1 parent 8ad63bc commit 7c646bc
Show file tree
Hide file tree
Showing 3 changed files with 230 additions and 43 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/core-feature-alert.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ on:
- opened
- synchronize
paths:
- src/lib/features/client-feature-toggles/*
- src/lib/features/frontend-api/*
- src/lib/features/client-feature-toggles/**
- src/lib/features/frontend-api/**

jobs:
notify-core-changes:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should match snapshot from /api/client/features 1`] = `
{
"features": [
{
"description": null,
"enabled": false,
"impressionData": false,
"name": "test1",
"project": "default",
"stale": false,
"strategies": [
{
"constraints": [],
"name": "flexibleRollout",
"parameters": {
"groupId": "test1",
"rollout": "100",
"stickiness": "default",
},
"variants": [],
},
],
"type": "release",
"variants": [],
},
{
"description": null,
"enabled": false,
"impressionData": false,
"name": "test2",
"project": "default",
"stale": false,
"strategies": [
{
"constraints": [
{
"contextName": "userId",
"operator": "IN",
"values": [
"123",
],
},
],
"name": "default",
"parameters": {},
"variants": [],
},
],
"type": "release",
"variants": [],
},
],
"meta": {
"etag": ""61824cd0:11"",
"queryHash": "61824cd0",
"revisionId": 11,
},
"query": {
"environment": "default",
"inlineSegmentConstraints": true,
},
"version": 2,
}
`;
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,19 @@ import { type IUserWithRootRole, TEST_AUDIT_USER } from '../../../types';
let app: IUnleashTest;
let db: ITestDb;
let dummyAdmin: IUserWithRootRole;
let proApp: IUnleashTest;
let proDb: ITestDb;
let enterpriseApp: IUnleashTest;
let enterpriseDb: ITestDb;
let enterpriseDummyAdmin: IUserWithRootRole;
let proDummyAdmin: IUserWithRootRole;

const apiClientResponse = [
const getApiClientResponse = (project = 'default') => [
{
name: 'test1',
type: 'release',
enabled: false,
project: 'default',
project: project,
stale: false,
strategies: [
{
Expand All @@ -41,7 +47,7 @@ const apiClientResponse = [
name: 'test2',
type: 'release',
enabled: false,
project: 'default',
project: project,
stale: false,
strategies: [
{
Expand All @@ -63,6 +69,59 @@ const apiClientResponse = [
},
];

const cleanup = async (db: ITestDb, app: IUnleashTest) => {
const all =
await db.stores.projectStore.getEnvironmentsForProject('default');
await Promise.all(
all
.filter((env) => env.environment !== DEFAULT_ENV)
.map(async (env) =>
db.stores.projectStore.deleteEnvironmentForProject(
'default',
env.environment,
),
),
);
};

const setupFeatures = async (
db: ITestDb,
app: IUnleashTest,
project = 'default',
) => {
await db.rawDatabase.raw('DELETE FROM features');

await app.createFeature('test1', project);
await app.createFeature('test2', project);

await app.addStrategyToFeatureEnv(
{
name: 'flexibleRollout',
constraints: [],
parameters: {
rollout: '100',
stickiness: 'default',
groupId: 'test1',
},
},
DEFAULT_ENV,
'test1',
project,
);
await app.addStrategyToFeatureEnv(
{
name: 'default',
constraints: [
{ contextName: 'userId', operator: 'IN', values: ['123'] },
],
parameters: {},
},
DEFAULT_ENV,
'test2',
project,
);
};

beforeAll(async () => {
db = await dbInit('client_feature_toggles', getLogger);
app = await setupAppWithCustomConfig(
Expand All @@ -77,6 +136,38 @@ beforeAll(async () => {
db.rawDatabase,
);

enterpriseDb = await dbInit('client_feature_toggles_enterprise', getLogger);
enterpriseApp = await setupAppWithCustomConfig(
enterpriseDb.stores,
{
experimental: {
flags: {
strictSchemaValidation: true,
},
},
ui: {
environment: 'Enterprise',
},
},
enterpriseDb.rawDatabase,
);

proDb = await dbInit('client_feature_toggles_pro', getLogger);
proApp = await setupAppWithCustomConfig(
proDb.stores,
{
experimental: {
flags: {
strictSchemaValidation: true,
},
},
ui: {
environment: 'Pro',
},
},
proDb.rawDatabase,
);

dummyAdmin = await app.services.userService.createUser(
{
name: 'Some Name',
Expand All @@ -85,26 +176,39 @@ beforeAll(async () => {
},
TEST_AUDIT_USER,
);

enterpriseDummyAdmin = await enterpriseApp.services.userService.createUser(
{
name: 'Some Name',
email: '[email protected]',
rootRole: RoleName.ADMIN,
},
TEST_AUDIT_USER,
);

proDummyAdmin = await proApp.services.userService.createUser(
{
name: 'Some Name',
email: '[email protected]',
rootRole: RoleName.ADMIN,
},
TEST_AUDIT_USER,
);
});

afterEach(async () => {
const all =
await db.stores.projectStore.getEnvironmentsForProject('default');
await Promise.all(
all
.filter((env) => env.environment !== DEFAULT_ENV)
.map(async (env) =>
db.stores.projectStore.deleteEnvironmentForProject(
'default',
env.environment,
),
),
);
await cleanup(db, app);
await cleanup(proDb, proApp);
await cleanup(enterpriseDb, enterpriseApp);
});

afterAll(async () => {
await app.destroy();
await db.destroy();
await proApp.destroy();
await proDb.destroy();
await enterpriseApp.destroy();
await enterpriseDb.destroy();
});

test('should fetch single feature', async () => {
Expand Down Expand Up @@ -164,40 +268,57 @@ test('should support filtering on project', async () => {
});

test('should return correct data structure from /api/client/features', async () => {
await db.rawDatabase.raw('DELETE FROM features');
await setupFeatures(db, app);

await app.createFeature('test1', 'default');
await app.createFeature('test2', 'default');
const result = await app.request
.get('/api/client/features')
.expect('Content-Type', /json/)
.expect(200);

await app.addStrategyToFeatureEnv(
{
name: 'flexibleRollout',
constraints: [],
parameters: {
rollout: '100',
stickiness: 'default',
groupId: 'test1',
},
},
DEFAULT_ENV,
'test1',
expect(result.body.features).toEqual(getApiClientResponse());
});

test('should return correct data structure from /api/client/features for pro', async () => {
await proApp.services.projectService.createProject(
{ name: 'Pro', id: 'pro' },
proDummyAdmin,
TEST_AUDIT_USER,
);
await app.addStrategyToFeatureEnv(
{
name: 'default',
constraints: [
{ contextName: 'userId', operator: 'IN', values: ['123'] },
],
parameters: {},
},
DEFAULT_ENV,
'test2',

await setupFeatures(proDb, proApp, 'pro');

const result = await proApp.request
.get('/api/client/features')
.expect('Content-Type', /json/)
.expect(200);

expect(result.body.features).toEqual(getApiClientResponse('pro'));
});

test('should return correct data structure from /api/client/features for Enterprise', async () => {
await enterpriseApp.services.projectService.createProject(
{ name: 'Enterprise', id: 'enterprise' },
enterpriseDummyAdmin,
TEST_AUDIT_USER,
);

await setupFeatures(enterpriseDb, enterpriseApp, 'enterprise');

const result = await enterpriseApp.request
.get('/api/client/features')
.expect('Content-Type', /json/)
.expect(200);

expect(result.body.features).toEqual(getApiClientResponse('enterprise'));
});

test('should match snapshot from /api/client/features', async () => {
await setupFeatures(db, app);

const result = await app.request
.get('/api/client/features')
.expect('Content-Type', /json/)
.expect(200);

expect(result.body.features).toEqual(apiClientResponse);
expect(result.body).toMatchSnapshot();
});

0 comments on commit 7c646bc

Please sign in to comment.