unleash-server
Version:
Unleash is an enterprise ready feature flag service. It provides different strategies for handling feature flags.
354 lines • 11 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const supertest_1 = __importDefault(require("supertest"));
const app_1 = __importDefault(require("../../../app"));
const test_config_1 = require("../../../../test/config/test-config");
const schema_1 = require("../shared/schema");
const services_1 = require("../../../services");
const types_1 = require("../../../types");
const database_init_1 = __importDefault(require("../../../../test/e2e/helpers/database-init"));
const date_fns_1 = require("date-fns");
const api_token_1 = require("../../../types/models/api-token");
let db;
let config;
async function getSetup(opts) {
config = (0, test_config_1.createTestConfig)(opts);
db = await (0, database_init_1.default)('metrics', config.getLogger);
const services = (0, services_1.createServices)(db.stores, config, db.rawDatabase);
const app = await (0, app_1.default)(config, db.stores, services);
return {
request: (0, supertest_1.default)(app),
stores: db.stores,
services,
db: db.rawDatabase,
destroy: db.destroy,
};
}
let request;
let stores;
let services;
let destroy;
beforeAll(async () => {
const setup = await getSetup();
request = setup.request;
stores = setup.stores;
destroy = setup.destroy;
services = setup.services;
});
afterAll(async () => {
await destroy();
});
afterEach(async () => {
await stores.featureToggleStore.deleteAll();
});
test('should validate client metrics', () => {
return request
.post('/api/client/metrics')
.send({ random: 'blush' })
.expect(400);
});
test('should accept empty client metrics', () => {
return request
.post('/api/client/metrics')
.send({
appName: 'demo',
instanceId: '1',
bucket: {
start: Date.now(),
stop: Date.now(),
toggles: {},
},
})
.expect(202);
});
test('should accept client metrics with yes/no', () => {
return request
.post('/api/client/metrics')
.send({
appName: 'demo',
instanceId: '1',
bucket: {
start: Date.now(),
stop: Date.now(),
toggles: {
toggleA: {
yes: 200,
no: 0,
},
},
},
})
.expect(202);
});
test('should accept client metrics with yes/no with metricsV2', async () => {
const testRunner = await getSetup();
await testRunner.request
.post('/api/client/metrics')
.send({
appName: 'demo',
instanceId: '1',
bucket: {
start: Date.now(),
stop: Date.now(),
toggles: {
toggleA: {
yes: 200,
no: 0,
},
},
},
})
.expect(202);
testRunner.destroy();
});
test('should accept client metrics with variants', () => {
return request
.post('/api/client/metrics')
.send({
appName: 'demo',
instanceId: '1',
bucket: {
start: Date.now(),
stop: Date.now(),
toggles: {
toggleA: {
yes: 200,
no: 0,
variants: {
variant1: 1,
variant2: 2,
},
},
},
},
})
.expect(202);
});
test('should accept client metrics without yes/no', () => {
return request
.post('/api/client/metrics')
.send({
appName: 'demo',
instanceId: '1',
bucket: {
start: Date.now(),
stop: Date.now(),
toggles: {
toggleA: {
blue: 200,
green: 0,
},
},
},
})
.expect(202);
});
test('schema allow empty strings', () => {
const data = {
appName: 'java-test',
instanceId: 'instance y',
bucket: {
toggles: { Demo2: { yes: '', no: '', variants: {} } },
start: '2019-05-06T08:30:40.514Z',
stop: '2019-05-06T09:30:50.515Z',
},
};
const { error, value } = schema_1.clientMetricsSchema.validate(data);
expect(error).toBeFalsy();
expect(value.bucket.toggles.Demo2.yes).toBe(0);
expect(value.bucket.toggles.Demo2.no).toBe(0);
});
test('schema allow yes=<string nbr>', () => {
const data = {
appName: 'java-test',
instanceId: 'instance y',
bucket: {
toggles: { Demo2: { yes: '12', no: 256, variants: {} } },
start: '2019-05-06T08:30:40.514Z',
stop: '2019-05-06T09:30:50.515Z',
},
};
const { error, value } = schema_1.clientMetricsSchema.validate(data);
expect(error).toBeFalsy();
expect(value.bucket.toggles.Demo2.yes).toBe(12);
expect(value.bucket.toggles.Demo2.no).toBe(256);
});
test('should return a 400 when required fields are missing', async () => {
stores.featureToggleStore.create('default', {
name: 'toggleLastSeen',
createdByUserId: 9999,
});
await request
.post('/api/client/metrics')
.send({
appName: 'demo',
bucket: {
start: Date.now(),
toggles: {
toggleLastSeen: {
yes: 200,
no: 0,
},
},
},
})
.expect(400);
});
test('should return a 200 if required fields are there', async () => {
stores.featureToggleStore.create('default', {
name: 'theOtherToggleLastSeen',
createdByUserId: 9999,
});
await request
.post('/api/client/metrics')
.send({
appName: 'demo',
someParam: 'some-value',
somOtherParam: 'some--other-value',
bucket: {
start: Date.now(),
stop: Date.now(),
toggles: {
theOtherToggleLastSeen: {
yes: 200,
no: 0,
},
},
},
})
.expect(202);
});
test('should return 204 if metrics are disabled by feature flag', async () => {
const { request: localRequest } = await getSetup({
experimental: {
flags: {
disableMetrics: true,
},
},
});
await localRequest
.post('/api/client/metrics')
.send({
appName: 'demo',
someParam: 'some-value',
somOtherParam: 'some--other-value',
bucket: {
start: Date.now(),
stop: Date.now(),
toggles: {
toggleLastSeen: {
yes: 200,
no: 0,
},
},
},
})
.expect(204);
});
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 () => {
const now = new Date();
await request
.post('/api/client/metrics/bulk')
.send({
applications: [],
metrics: [
{
featureName: 'test_feature_one',
appName: 'test_application',
environment: 'default',
timestamp: (0, date_fns_1.startOfHour)(now),
yes: 1000,
no: 800,
variants: {},
},
{
featureName: 'test_feature_two',
appName: 'test_application',
environment: 'development',
timestamp: (0, date_fns_1.startOfHour)(now),
yes: 1000,
no: 800,
variants: {},
},
],
})
.expect(202);
await services.clientMetricsServiceV2.bulkAdd(); // Force bulk collection.
const developmentReport = await services.clientMetricsServiceV2.getClientMetricsForToggle('test_feature_two', 1);
const defaultReport = await services.clientMetricsServiceV2.getClientMetricsForToggle('test_feature_one', 1);
expect(developmentReport).toHaveLength(0);
expect(defaultReport).toHaveLength(1);
expect(defaultReport[0].yes).toBe(1000);
});
test('should accept empty bulk metrics', async () => {
await request
.post('/api/client/metrics/bulk')
.send({
applications: [],
metrics: [],
})
.expect(202);
});
test('should validate bulk metrics data', async () => {
await request
.post('/api/client/metrics/bulk')
.send({ randomData: 'blurb' })
.expect(400);
});
test('bulk metrics should return 204 if metrics are disabled', async () => {
const { request: localRequest } = await getSetup({
experimental: {
flags: {
disableMetrics: true,
},
},
});
await localRequest
.post('/api/client/metrics/bulk')
.send({
applications: [],
metrics: [],
})
.expect(204);
});
test('bulk metrics requires a valid client token to accept metrics', async () => {
const authed = await getSetup({
authentication: {
type: types_1.IAuthType.DEMO,
enableApiToken: true,
},
});
const clientToken = await authed.services.apiTokenService.createApiTokenWithProjects({
tokenName: 'bulk-metrics-test',
type: api_token_1.ApiTokenType.CLIENT,
environment: 'development',
projects: ['*'],
});
const frontendToken = await authed.services.apiTokenService.createApiTokenWithProjects({
tokenName: 'frontend-bulk-metrics-test',
type: api_token_1.ApiTokenType.FRONTEND,
environment: 'development',
projects: ['*'],
});
await authed.request
.post('/api/client/metrics/bulk')
.send({ applications: [], metrics: [] })
.expect(401);
await authed.request
.post('/api/client/metrics/bulk')
.set('Authorization', frontendToken.secret)
.send({ applications: [], metrics: [] })
.expect(403);
await authed.request
.post('/api/client/metrics/bulk')
.set('Authorization', clientToken.secret)
.send({ applications: [], metrics: [] })
.expect(202);
await authed.destroy();
});
});
//# sourceMappingURL=metrics.test.js.map