UNPKG

unleash-server

Version:

Unleash is an enterprise ready feature toggles service. It provides different strategies for handling feature toggles.

857 lines • 32.3 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.createApiToken = void 0; const test_helper_1 = require("../../helpers/test-helper"); const database_init_1 = __importDefault(require("../../helpers/database-init")); const no_logger_1 = __importDefault(require("../../../fixtures/no-logger")); const util_1 = require("../../../../lib/util"); const api_token_1 = require("../../../../lib/types/models/api-token"); const date_fns_1 = require("date-fns"); const types_1 = require("../../../../lib/types"); const proxy_1 = require("../../../../lib/proxy"); let app; let db; beforeAll(async () => { db = await (0, database_init_1.default)('proxy', no_logger_1.default); app = await (0, test_helper_1.setupAppWithAuth)(db.stores, { frontendApiOrigins: ['https://example.com'], }); }); afterEach(() => { app.services.proxyService.stopAll(); }); afterAll(async () => { await app.destroy(); await db.destroy(); }); beforeEach(async () => { await db.stores.segmentStore.deleteAll(); await db.stores.featureToggleStore.deleteAll(); await db.stores.clientMetricsStoreV2.deleteAll(); await db.stores.apiTokenStore.deleteAll(); }); const createApiToken = (type, overrides = {}) => { return app.services.apiTokenService.createApiTokenWithProjects({ type, projects: ['*'], environment: 'default', username: `${type}-token-${(0, util_1.randomId)()}`, ...overrides, }); }; exports.createApiToken = createApiToken; const createFeatureToggle = async ({ name, project = 'default', environment = 'default', strategies, enabled, }) => { const createdFeature = await app.services.featureToggleService.createFeatureToggle(project, { name }, 'userName', true); const createdStrategies = await Promise.all((strategies ?? []).map(async (s) => app.services.featureToggleService.createStrategy(s, { projectId: project, featureName: name, environment }, 'userName'))); await app.services.featureToggleService.updateEnabled(project, name, environment, enabled, 'userName'); return [createdFeature, createdStrategies]; }; const createProject = async (id, name) => { const user = await db.stores.userStore.insert({ name: (0, util_1.randomId)(), email: `${(0, util_1.randomId)()}@example.com`, }); await app.services.projectService.createProject({ id, name }, user); }; test('should require a frontend token or an admin token', async () => { await app.request .get('/api/frontend') .expect('Content-Type', /json/) .expect(401); }); test('should not allow requests with a client token', async () => { const clientToken = await (0, exports.createApiToken)(api_token_1.ApiTokenType.CLIENT); await app.request .get('/api/frontend') .set('Authorization', clientToken.secret) .expect('Content-Type', /json/) .expect(403); }); test('should allow requests with a token secret alias', async () => { const featureA = (0, util_1.randomId)(); const featureB = (0, util_1.randomId)(); const envA = (0, util_1.randomId)(); const envB = (0, util_1.randomId)(); await db.stores.environmentStore.create({ name: envA, type: 'test' }); await db.stores.environmentStore.create({ name: envB, type: 'test' }); await db.stores.projectStore.addEnvironmentToProject('default', envA); await db.stores.projectStore.addEnvironmentToProject('default', envB); await createFeatureToggle({ name: featureA, enabled: true, environment: envA, strategies: [{ name: 'default', constraints: [], parameters: {} }], }); await createFeatureToggle({ name: featureB, enabled: true, environment: envB, strategies: [{ name: 'default', constraints: [], parameters: {} }], }); const tokenA = await (0, exports.createApiToken)(api_token_1.ApiTokenType.FRONTEND, { alias: (0, util_1.randomId)(), environment: envA, }); const tokenB = await (0, exports.createApiToken)(api_token_1.ApiTokenType.FRONTEND, { alias: (0, util_1.randomId)(), environment: envB, }); await app.request .get('/api/frontend') .expect('Content-Type', /json/) .expect(401); await app.request .get('/api/frontend') .set('Authorization', '') .expect('Content-Type', /json/) .expect(401); await app.request .get('/api/frontend') .set('Authorization', 'null') .expect('Content-Type', /json/) .expect(401); await app.request .get('/api/frontend') .set('Authorization', (0, util_1.randomId)()) .expect('Content-Type', /json/) .expect(401); await app.request .get('/api/frontend') .set('Authorization', tokenA.secret.slice(0, -1)) .expect('Content-Type', /json/) .expect(401); await app.request .get('/api/frontend') .set('Authorization', tokenA.secret) .expect('Content-Type', /json/) .expect(200) .expect((res) => expect(res.body.toggles).toHaveLength(1)) .expect((res) => expect(res.body.toggles[0].name).toEqual(featureA)); await app.request .get('/api/frontend') .set('Authorization', tokenB.secret) .expect('Content-Type', /json/) .expect(200) .expect((res) => expect(res.body.toggles).toHaveLength(1)) .expect((res) => expect(res.body.toggles[0].name).toEqual(featureB)); await app.request .get('/api/frontend') .set('Authorization', tokenA.alias) .expect('Content-Type', /json/) .expect(200) .expect((res) => expect(res.body.toggles).toHaveLength(1)) .expect((res) => expect(res.body.toggles[0].name).toEqual(featureA)); await app.request .get('/api/frontend') .set('Authorization', tokenB.alias) .expect('Content-Type', /json/) .expect(200) .expect((res) => expect(res.body.toggles).toHaveLength(1)) .expect((res) => expect(res.body.toggles[0].name).toEqual(featureB)); }); test('should allow requests with an admin token', async () => { const featureA = (0, util_1.randomId)(); await createFeatureToggle({ name: featureA, enabled: true, strategies: [{ name: 'default', constraints: [], parameters: {} }], }); const adminToken = await (0, exports.createApiToken)(api_token_1.ApiTokenType.ADMIN, { projects: ['*'], environment: '*', }); await app.request .get('/api/frontend') .set('Authorization', adminToken.secret) .expect('Content-Type', /json/) .expect(200) .expect((res) => expect(res.body.toggles).toHaveLength(1)) .expect((res) => expect(res.body.toggles[0].name).toEqual(featureA)); }); test('should not allow admin requests with a frontend token', async () => { const frontendToken = await (0, exports.createApiToken)(api_token_1.ApiTokenType.FRONTEND); await app.request .get('/api/admin/features') .set('Authorization', frontendToken.secret) .expect('Content-Type', /json/) .expect(403); }); test('should not allow client requests with a frontend token', async () => { const frontendToken = await (0, exports.createApiToken)(api_token_1.ApiTokenType.FRONTEND); await app.request .get('/api/client/features') .set('Authorization', frontendToken.secret) .expect('Content-Type', /json/) .expect(403); }); test('should not allow requests with an invalid frontend token', async () => { const frontendToken = await (0, exports.createApiToken)(api_token_1.ApiTokenType.FRONTEND); await app.request .get('/api/frontend') .set('Authorization', frontendToken.secret.slice(0, -1)) .expect('Content-Type', /json/) .expect(401); }); test('should allow requests with a frontend token', async () => { const frontendToken = await (0, exports.createApiToken)(api_token_1.ApiTokenType.FRONTEND); await app.request .get('/api/frontend') .set('Authorization', frontendToken.secret) .expect('Content-Type', /json/) .expect(200) .expect((res) => expect(res.body).toEqual({ toggles: [] })); }); test('should return 405 from unimplemented endpoints', async () => { const frontendToken = await (0, exports.createApiToken)(api_token_1.ApiTokenType.FRONTEND); await app.request .post('/api/frontend') .send({}) .set('Authorization', frontendToken.secret) .expect('Content-Type', /json/) .expect(405); await app.request .get('/api/frontend/client/features') .set('Authorization', frontendToken.secret) .expect('Content-Type', /json/) .expect(405); await app.request .get('/api/frontend/health') .set('Authorization', frontendToken.secret) .expect('Content-Type', /json/) .expect(405); await app.request .get('/api/frontend/internal-backstage/prometheus') .set('Authorization', frontendToken.secret) .expect('Content-Type', /json/) .expect(405); }); test('should enforce frontend API CORS config', async () => { const allowedOrigin = 'https://example.com'; const unknownOrigin = 'https://example.org'; const origin = 'access-control-allow-origin'; const frontendToken = await (0, exports.createApiToken)(api_token_1.ApiTokenType.FRONTEND); await app.request .options('/api/frontend') .set('Origin', unknownOrigin) .set('Authorization', frontendToken.secret) .expect((res) => expect(res.headers[origin]).toBeUndefined()); await app.request .options('/api/frontend') .set('Origin', allowedOrigin) .set('Authorization', frontendToken.secret) .expect((res) => expect(res.headers[origin]).toEqual(allowedOrigin)); await app.request .get('/api/frontend') .set('Origin', unknownOrigin) .set('Authorization', frontendToken.secret) .expect((res) => expect(res.headers[origin]).toBeUndefined()); await app.request .get('/api/frontend') .set('Origin', allowedOrigin) .set('Authorization', frontendToken.secret) .expect((res) => expect(res.headers[origin]).toEqual(allowedOrigin)); }); test('should accept client registration requests', async () => { const frontendToken = await (0, exports.createApiToken)(api_token_1.ApiTokenType.FRONTEND); await app.request .post('/api/frontend/client/register') .set('Authorization', frontendToken.secret) .send({}) .expect('Content-Type', /json/) .expect(400); await app.request .post('/api/frontend/client/register') .set('Authorization', frontendToken.secret) .send({ appName: (0, util_1.randomId)(), instanceId: (0, util_1.randomId)(), sdkVersion: (0, util_1.randomId)(), environment: 'default', interval: 10000, started: new Date(), strategies: ['default'], }) .expect(200) .expect((res) => expect(res.text).toEqual('OK')); }); test('should store proxy client metrics', async () => { const now = new Date(); const appName = (0, util_1.randomId)(); const instanceId = (0, util_1.randomId)(); const featureName = (0, util_1.randomId)(); const frontendToken = await (0, exports.createApiToken)(api_token_1.ApiTokenType.FRONTEND); const adminToken = await (0, exports.createApiToken)(api_token_1.ApiTokenType.ADMIN, { projects: ['*'], environment: '*', }); await app.request .get(`/api/admin/client-metrics/features/${featureName}`) .set('Authorization', adminToken.secret) .expect('Content-Type', /json/) .expect(200) .then((res) => { expect(res.body).toEqual({ featureName, lastHourUsage: [], maturity: 'stable', seenApplications: [], version: 1, }); }); await app.request .post('/api/frontend/client/metrics') .set('Authorization', frontendToken.secret) .send({ appName, instanceId, bucket: { start: now, stop: now, toggles: { [featureName]: { yes: 1, no: 10 } }, }, }) .expect(200) .expect((res) => expect(res.text).toEqual('OK')); await app.request .post('/api/frontend/client/metrics') .set('Authorization', frontendToken.secret) .send({ appName, instanceId, bucket: { start: now, stop: now, toggles: { [featureName]: { yes: 2, no: 20 } }, }, }) .expect(200) .expect((res) => expect(res.text).toEqual('OK')); await app.services.clientMetricsServiceV2.bulkAdd(); await app.request .get(`/api/admin/client-metrics/features/${featureName}`) .set('Authorization', adminToken.secret) .expect('Content-Type', /json/) .expect(200) .then((res) => { expect(res.body).toEqual({ featureName, lastHourUsage: [ { environment: 'default', timestamp: (0, date_fns_1.startOfHour)(now).toISOString(), yes: 3, no: 30, }, ], maturity: 'stable', seenApplications: [appName], version: 1, }); }); }); test('should filter features by enabled/disabled', async () => { const frontendToken = await (0, exports.createApiToken)(api_token_1.ApiTokenType.FRONTEND); await createFeatureToggle({ name: 'enabledFeature1', enabled: true, strategies: [{ name: 'default', constraints: [], parameters: {} }], }); await createFeatureToggle({ name: 'enabledFeature2', enabled: true, strategies: [{ name: 'default', constraints: [], parameters: {} }], }); await createFeatureToggle({ name: 'disabledFeature', enabled: false, strategies: [{ name: 'default', constraints: [], parameters: {} }], }); await app.request .get('/api/frontend') .set('Authorization', frontendToken.secret) .expect('Content-Type', /json/) .expect(200) .expect((res) => { expect(res.body).toEqual({ toggles: [ { name: 'enabledFeature1', enabled: true, impressionData: false, variant: { enabled: false, name: 'disabled' }, }, { name: 'enabledFeature2', enabled: true, impressionData: false, variant: { enabled: false, name: 'disabled' }, }, ], }); }); }); test('should filter features by strategies', async () => { const frontendToken = await (0, exports.createApiToken)(api_token_1.ApiTokenType.FRONTEND); await createFeatureToggle({ name: 'featureWithoutStrategies', enabled: false, strategies: [], }); await createFeatureToggle({ name: 'featureWithUnknownStrategy', enabled: true, strategies: [{ name: 'unknown', constraints: [], parameters: {} }], }); await createFeatureToggle({ name: 'featureWithMultipleStrategies', enabled: true, strategies: [ { name: 'default', constraints: [], parameters: {} }, { name: 'unknown', constraints: [], parameters: {} }, ], }); await app.request .get('/api/frontend') .set('Authorization', frontendToken.secret) .expect('Content-Type', /json/) .expect(200) .expect((res) => { expect(res.body).toEqual({ toggles: [ { name: 'featureWithMultipleStrategies', enabled: true, impressionData: false, variant: { enabled: false, name: 'disabled' }, }, ], }); }); }); test('should filter features by constraints', async () => { const frontendToken = await (0, exports.createApiToken)(api_token_1.ApiTokenType.FRONTEND); await createFeatureToggle({ name: 'featureWithAppNameA', enabled: true, strategies: [ { name: 'default', constraints: [ { contextName: 'appName', operator: 'IN', values: ['a'] }, ], parameters: {}, }, ], }); await createFeatureToggle({ name: 'featureWithAppNameAorB', enabled: true, strategies: [ { name: 'default', constraints: [ { contextName: 'appName', operator: 'IN', values: ['a', 'b'], }, ], parameters: {}, }, ], }); await app.request .get('/api/frontend?appName=a') .set('Authorization', frontendToken.secret) .expect('Content-Type', /json/) .expect(200) .expect((res) => expect(res.body.toggles).toHaveLength(2)); await app.request .get('/api/frontend?appName=b') .set('Authorization', frontendToken.secret) .expect('Content-Type', /json/) .expect(200) .expect((res) => expect(res.body.toggles).toHaveLength(1)); await app.request .get('/api/frontend?appName=c') .set('Authorization', frontendToken.secret) .expect('Content-Type', /json/) .expect(200) .expect((res) => expect(res.body.toggles).toHaveLength(0)); }); test('should be able to set environment as a context variable', async () => { const frontendToken = await (0, exports.createApiToken)(api_token_1.ApiTokenType.FRONTEND); const featureName = 'featureWithEnvironmentConstraint'; await createFeatureToggle({ name: featureName, enabled: true, strategies: [ { name: 'default', constraints: [ { contextName: 'environment', operator: 'IN', values: ['staging'], }, ], parameters: {}, }, ], }); await app.request .get('/api/frontend?environment=staging') .set('Authorization', frontendToken.secret) .expect('Content-Type', /json/) .expect(200) .expect((res) => { expect(res.body.toggles).toHaveLength(1); expect(res.body.toggles[0].name).toBe(featureName); }); await app.request .get('/api/frontend') .set('Authorization', frontendToken.secret) .expect('Content-Type', /json/) .expect(200) .expect((res) => { expect(res.body.toggles).toHaveLength(0); }); }); test('should filter features by project', async () => { const projectA = 'projectA'; const projectB = 'projectB'; await createProject(projectA, (0, util_1.randomId)()); await createProject(projectB, (0, util_1.randomId)()); const frontendTokenDefault = await (0, exports.createApiToken)(api_token_1.ApiTokenType.FRONTEND, { projects: ['default'], }); const frontendTokenProjectA = await (0, exports.createApiToken)(api_token_1.ApiTokenType.FRONTEND, { projects: [projectA], }); const frontendTokenProjectAB = await (0, exports.createApiToken)(api_token_1.ApiTokenType.FRONTEND, { projects: [projectA, projectB], }); await createFeatureToggle({ name: 'featureInProjectDefault', enabled: true, strategies: [{ name: 'default', parameters: {} }], }); await createFeatureToggle({ name: 'featureInProjectA', project: projectA, enabled: true, strategies: [{ name: 'default', parameters: {} }], }); await createFeatureToggle({ name: 'featureInProjectB', project: projectB, enabled: true, strategies: [{ name: 'default', parameters: {} }], }); await app.request .get('/api/frontend') .set('Authorization', frontendTokenDefault.secret) .expect('Content-Type', /json/) .expect(200) .expect((res) => { expect(res.body).toEqual({ toggles: [ { name: 'featureInProjectDefault', enabled: true, impressionData: false, variant: { enabled: false, name: 'disabled' }, }, ], }); }); await app.request .get('/api/frontend') .set('Authorization', frontendTokenProjectA.secret) .expect('Content-Type', /json/) .expect(200) .expect((res) => { expect(res.body).toEqual({ toggles: [ { name: 'featureInProjectA', enabled: true, impressionData: false, variant: { enabled: false, name: 'disabled' }, }, ], }); }); await app.request .get('/api/frontend') .set('Authorization', frontendTokenProjectAB.secret) .expect('Content-Type', /json/) .expect(200) .expect((res) => { expect(res.body).toEqual({ toggles: [ { name: 'featureInProjectA', enabled: true, impressionData: false, variant: { enabled: false, name: 'disabled' }, }, { name: 'featureInProjectB', enabled: true, impressionData: false, variant: { enabled: false, name: 'disabled' }, }, ], }); }); }); test('should filter features by environment', async () => { const environmentA = 'environmentA'; const environmentB = 'environmentB'; await db.stores.environmentStore.create({ name: environmentA, type: 'production', }); await db.stores.environmentStore.create({ name: environmentB, type: 'production', }); await app.services.environmentService.addEnvironmentToProject(environmentA, 'default'); await app.services.environmentService.addEnvironmentToProject(environmentB, 'default'); const frontendTokenEnvironmentDefault = await (0, exports.createApiToken)(api_token_1.ApiTokenType.FRONTEND); const frontendTokenEnvironmentA = await (0, exports.createApiToken)(api_token_1.ApiTokenType.FRONTEND, { environment: environmentA, }); const frontendTokenEnvironmentB = await (0, exports.createApiToken)(api_token_1.ApiTokenType.FRONTEND, { environment: environmentB, }); await createFeatureToggle({ name: 'featureInEnvironmentDefault', enabled: true, strategies: [{ name: 'default', parameters: {} }], }); await createFeatureToggle({ name: 'featureInEnvironmentA', environment: environmentA, enabled: true, strategies: [{ name: 'default', parameters: {} }], }); await createFeatureToggle({ name: 'featureInEnvironmentB', environment: environmentB, enabled: true, strategies: [{ name: 'default', parameters: {} }], }); await app.request .get('/api/frontend') .set('Authorization', frontendTokenEnvironmentDefault.secret) .expect('Content-Type', /json/) .expect(200) .expect((res) => { expect(res.body).toEqual({ toggles: [ { name: 'featureInEnvironmentDefault', enabled: true, impressionData: false, variant: { enabled: false, name: 'disabled' }, }, ], }); }); await app.request .get('/api/frontend') .set('Authorization', frontendTokenEnvironmentA.secret) .expect('Content-Type', /json/) .expect(200) .expect((res) => { expect(res.body).toEqual({ toggles: [ { name: 'featureInEnvironmentA', enabled: true, impressionData: false, variant: { enabled: false, name: 'disabled' }, }, ], }); }); await app.request .get('/api/frontend') .set('Authorization', frontendTokenEnvironmentB.secret) .expect('Content-Type', /json/) .expect(200) .expect((res) => { expect(res.body).toEqual({ toggles: [ { name: 'featureInEnvironmentB', enabled: true, impressionData: false, variant: { enabled: false, name: 'disabled' }, }, ], }); }); }); test('should filter features by segment', async () => { const [featureA, [strategyA]] = await createFeatureToggle({ name: (0, util_1.randomId)(), enabled: true, strategies: [{ name: 'default', parameters: {} }], }); const [featureB, [strategyB]] = await createFeatureToggle({ name: (0, util_1.randomId)(), enabled: true, strategies: [{ name: 'default', parameters: {} }], }); const constraintA = { operator: 'IN', contextName: 'appName', values: ['a'], }; const constraintB = { operator: 'IN', contextName: 'appName', values: ['b'], }; const segmentA = await app.services.segmentService.create({ name: (0, util_1.randomId)(), constraints: [constraintA] }, { email: 'test@example.com' }); const segmentB = await app.services.segmentService.create({ name: (0, util_1.randomId)(), constraints: [constraintB] }, { email: 'test@example.com' }); await app.services.segmentService.addToStrategy(segmentA.id, strategyA.id); await app.services.segmentService.addToStrategy(segmentB.id, strategyB.id); const frontendToken = await (0, exports.createApiToken)(api_token_1.ApiTokenType.FRONTEND); await app.request .get('/api/frontend') .set('Authorization', frontendToken.secret) .expect('Content-Type', /json/) .expect(200) .expect((res) => expect(res.body).toEqual({ toggles: [] })); await app.request .get('/api/frontend?appName=a') .set('Authorization', frontendToken.secret) .expect('Content-Type', /json/) .expect(200) .expect((res) => expect(res.body.toggles).toHaveLength(1)) .expect((res) => expect(res.body.toggles[0].name).toEqual(featureA.name)); await app.request .get('/api/frontend?appName=b') .set('Authorization', frontendToken.secret) .expect('Content-Type', /json/) .expect(200) .expect((res) => expect(res.body.toggles).toHaveLength(1)) .expect((res) => expect(res.body.toggles[0].name).toEqual(featureB.name)); await app.request .get('/api/frontend?appName=c') .set('Authorization', frontendToken.secret) .expect('Content-Type', /json/) .expect(200) .expect((res) => expect(res.body).toEqual({ toggles: [] })); }); test('Should sync proxy for keys on an interval', async () => { jest.useFakeTimers(); const frontendToken = await (0, exports.createApiToken)(api_token_1.ApiTokenType.FRONTEND); const user = await app.services.apiTokenService.getUserForToken(frontendToken.secret); const spy = jest.spyOn(proxy_1.ProxyRepository.prototype, 'featuresForToken'); const proxyRepository = new proxy_1.ProxyRepository({ getLogger: no_logger_1.default, frontendApi: { refreshIntervalInMs: 5000 }, }, db.stores, app.services, user); await proxyRepository.start(); jest.advanceTimersByTime(60000); proxyRepository.stop(); expect(spy.mock.calls.length > 6).toBe(true); jest.useRealTimers(); }); test('Should change fetch interval', async () => { jest.useFakeTimers(); const frontendToken = await (0, exports.createApiToken)(api_token_1.ApiTokenType.FRONTEND); const user = await app.services.apiTokenService.getUserForToken(frontendToken.secret); const spy = jest.spyOn(proxy_1.ProxyRepository.prototype, 'featuresForToken'); const proxyRepository = new proxy_1.ProxyRepository({ getLogger: no_logger_1.default, frontendApi: { refreshIntervalInMs: 1000 }, }, db.stores, app.services, user); await proxyRepository.start(); jest.advanceTimersByTime(60000); proxyRepository.stop(); expect(spy.mock.calls.length > 30).toBe(true); jest.useRealTimers(); }); test('Should not recursively set off timers on events', async () => { jest.useFakeTimers(); const frontendToken = await (0, exports.createApiToken)(api_token_1.ApiTokenType.FRONTEND); const user = await app.services.apiTokenService.getUserForToken(frontendToken.secret); const spy = jest.spyOn(proxy_1.ProxyRepository.prototype, 'dataPolling'); const proxyRepository = new proxy_1.ProxyRepository({ getLogger: no_logger_1.default, frontendApi: { refreshIntervalInMs: 5000 }, }, db.stores, app.services, user); await proxyRepository.start(); db.stores.eventStore.emit(types_1.FEATURE_UPDATED); jest.advanceTimersByTime(10000); proxyRepository.stop(); expect(spy.mock.calls.length < 3).toBe(true); jest.useRealTimers(); }); test('should return all features when specified', async () => { app.config.experimental.flags.proxyReturnAllToggles = true; const frontendToken = await (0, exports.createApiToken)(api_token_1.ApiTokenType.FRONTEND); await createFeatureToggle({ name: 'enabledFeature1', enabled: true, strategies: [{ name: 'default', constraints: [], parameters: {} }], }); await createFeatureToggle({ name: 'enabledFeature2', enabled: true, strategies: [{ name: 'default', constraints: [], parameters: {} }], }); await createFeatureToggle({ name: 'disabledFeature', enabled: false, strategies: [{ name: 'default', constraints: [], parameters: {} }], }); await app.request .get('/api/frontend') .set('Authorization', frontendToken.secret) .expect('Content-Type', /json/) .expect(200) .expect((res) => { expect(res.body).toEqual({ toggles: [ { name: 'enabledFeature1', enabled: true, impressionData: false, variant: { enabled: false, name: 'disabled' }, }, { name: 'enabledFeature2', enabled: true, impressionData: false, variant: { enabled: false, name: 'disabled' }, }, { name: 'disabledFeature', enabled: false, impressionData: false, variant: { enabled: false, name: 'disabled' }, }, ], }); }); }); test('should return maxAge header on options call', async () => { await app.request .options('/api/frontend') .set('Origin', 'https://example.com') .expect(204) .expect((res) => { expect(res.headers['access-control-max-age']).toBe('86400'); }); }); //# sourceMappingURL=proxy.e2e.test.js.map