UNPKG

unleash-server

Version:

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

264 lines • 12.7 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const prom_client_1 = require("prom-client"); const events_1 = __importDefault(require("events")); const test_config_1 = require("../test/config/test-config"); const metric_events_1 = require("./metric-events"); const events_2 = require("./types/events"); const metrics_1 = require("./metrics"); const store_1 = __importDefault(require("../test/fixtures/store")); const instance_stats_service_1 = require("./features/instance-stats/instance-stats-service"); const version_service_1 = __importDefault(require("./services/version-service")); const getActiveUsers_1 = require("./features/instance-stats/getActiveUsers"); const getProductionChanges_1 = require("./features/instance-stats/getProductionChanges"); const fake_environment_store_1 = __importDefault(require("./features/project-environments/fake-environment-store")); const services_1 = require("./services"); const no_logger_1 = __importDefault(require("../test/fixtures/no-logger")); const no_logger_2 = __importDefault(require("../test/fixtures/no-logger")); const database_init_1 = __importDefault(require("../test/e2e/helpers/database-init")); const feature_lifecycle_store_1 = require("./features/feature-lifecycle/feature-lifecycle-store"); const feature_lifecycle_read_model_1 = require("./features/feature-lifecycle/feature-lifecycle-read-model"); const getLicensedUsers_1 = require("./features/instance-stats/getLicensedUsers"); const monitor = (0, metrics_1.createMetricsMonitor)(); const eventBus = new events_1.default(); const prometheusRegister = prom_client_1.register; let eventStore; let environmentStore; let statsService; let stores; let schedulerService; let featureLifeCycleStore; let featureLifeCycleReadModel; let db; let refreshDbMetrics; beforeAll(async () => { const config = (0, test_config_1.createTestConfig)({ server: { serverMetrics: true, }, }); stores = (0, store_1.default)(); eventStore = stores.eventStore; environmentStore = new fake_environment_store_1.default(); stores.environmentStore = environmentStore; const versionService = new version_service_1.default(stores, config); db = await (0, database_init_1.default)('metrics_test', no_logger_2.default); featureLifeCycleReadModel = new feature_lifecycle_read_model_1.FeatureLifecycleReadModel(db.rawDatabase, config.flagResolver); stores.featureLifecycleReadModel = featureLifeCycleReadModel; featureLifeCycleStore = new feature_lifecycle_store_1.FeatureLifecycleStore(db.rawDatabase); stores.featureLifecycleStore = featureLifeCycleStore; statsService = new instance_stats_service_1.InstanceStatsService(stores, config, versionService, (0, getActiveUsers_1.createFakeGetActiveUsers)(), (0, getProductionChanges_1.createFakeGetProductionChanges)(), (0, getLicensedUsers_1.createFakeGetLicensedUsers)()); schedulerService = new services_1.SchedulerService(no_logger_1.default, { isMaintenanceMode: () => Promise.resolve(false), }, eventBus); const metricsDbConf = { client: { pool: { min: 0, max: 4, numUsed: () => 2, numFree: () => 2, numPendingAcquires: () => 0, numPendingCreates: () => 1, }, }, }; const { collectAggDbMetrics, collectStaticCounters } = (0, metrics_1.registerPrometheusMetrics)(config, stores, '4.0.0', eventBus, statsService); refreshDbMetrics = collectAggDbMetrics; await collectStaticCounters(); }); afterAll(async () => { schedulerService.stop(); }); test('should collect metrics for requests', async () => { eventBus.emit(metric_events_1.REQUEST_TIME, { path: 'somePath', method: 'GET', statusCode: 200, time: 1337, }); const metrics = await prometheusRegister.metrics(); expect(metrics).toMatch(/http_request_duration_milliseconds\{quantile="0\.99",path="somePath",method="GET",status="200",appName="undefined"\}.*1337/); }); test('should collect metrics for updated toggles', async () => { stores.eventStore.emit(events_2.FEATURE_UPDATED, { featureName: 'TestToggle', project: 'default', data: { name: 'TestToggle' }, }); const metrics = await prometheusRegister.metrics(); expect(metrics).toMatch(/feature_toggle_update_total\{toggle="TestToggle",project="default",environment="default",environmentType="production",action="updated"\} 1/); }); test('should set environmentType when toggle is flipped', async () => { await environmentStore.create({ name: 'testEnvironment', enabled: true, type: 'testType', sortOrder: 1, }); stores.eventStore.emit(events_2.FEATURE_ENVIRONMENT_ENABLED, { featureName: 'TestToggle', project: 'default', environment: 'testEnvironment', data: { name: 'TestToggle' }, }); // Wait for event to be processed, not nice, but it works. await new Promise((done) => { setTimeout(done, 1); }); const metrics = await prometheusRegister.metrics(); expect(metrics).toMatch(/feature_toggle_update_total\{toggle="TestToggle",project="default",environment="testEnvironment",environmentType="testType",action="updated"\} 1/); }); test('should collect metrics for client metric reports', async () => { eventBus.emit(events_2.CLIENT_METRICS, [ { featureName: 'TestToggle', yes: 10, no: 5, }, ]); const metrics = await prometheusRegister.metrics(); expect(metrics).toMatch(/feature_toggle_usage_total\{toggle="TestToggle",active="true",appName="undefined"\} 10\nfeature_toggle_usage_total\{toggle="TestToggle",active="false",appName="undefined"\} 5/); }); test('should collect metrics for db query timings', async () => { eventBus.emit(metric_events_1.DB_TIME, { store: 'foo', action: 'bar', time: 0.1337, }); const metrics = await prometheusRegister.metrics(); expect(metrics).toMatch(/db_query_duration_seconds\{quantile="0\.99",store="foo",action="bar"\} 0.1337/); }); test('should collect metrics for function timings', async () => { eventBus.emit(metric_events_1.FUNCTION_TIME, { functionName: 'getToggles', className: 'ToggleService', time: 0.1337, }); const metrics = await prometheusRegister.metrics(); expect(metrics).toMatch(/function_duration_seconds\{quantile="0\.99",functionName="getToggles",className="ToggleService"\} 0.1337/); }); test('should collect metrics for feature flag size', async () => { await refreshDbMetrics(); const metrics = await prometheusRegister.metrics(); expect(metrics).toMatch(/feature_toggles_total\{version="(.*)"\} 0/); }); test('should collect metrics for archived feature flag size', async () => { const metrics = await prometheusRegister.metrics(); expect(metrics).toMatch(/feature_toggles_archived_total 0/); }); test('should collect metrics for total client apps', async () => { await refreshDbMetrics(); const metrics = await prometheusRegister.metrics(); expect(metrics).toMatch(/client_apps_total\{range="(.*)"\} 0/); }); test('Should collect metrics for database', async () => { (0, metrics_1.registerPrometheusPostgresMetrics)(db.rawDatabase, eventBus, '15.0.0'); const metrics = await prometheusRegister.metrics(); expect(metrics).toMatch(/db_pool_max/); expect(metrics).toMatch(/db_pool_min/); expect(metrics).toMatch(/db_pool_used/); expect(metrics).toMatch(/db_pool_free/); expect(metrics).toMatch(/db_pool_pending_creates/); expect(metrics).toMatch(/db_pool_pending_acquires/); }); test('Should collect metrics for client sdk versions', async () => { eventStore.emit(events_2.CLIENT_REGISTER, { sdkName: 'unleash-client-node', sdkVersion: '3.2.5', }); eventStore.emit(events_2.CLIENT_REGISTER, { sdkName: 'unleash-client-node', sdkVersion: '3.2.5', }); eventStore.emit(events_2.CLIENT_REGISTER, { sdkName: 'unleash-client-node', sdkVersion: '3.2.5', }); eventStore.emit(events_2.CLIENT_REGISTER, { sdkName: 'unleash-client-java', sdkVersion: '5.0.0', }); eventStore.emit(events_2.CLIENT_REGISTER, { sdkName: 'unleash-client-java', sdkVersion: '5.0.0', }); eventStore.emit(events_2.CLIENT_REGISTER, { sdkName: 'unleash-client-java', sdkVersion: '5.0.0', }); const metrics = await prometheusRegister.getSingleMetricAsString('client_sdk_versions'); expect(metrics).toMatch(/client_sdk_versions\{sdk_name="unleash-client-node",sdk_version="3\.2\.5"\,platform_name=\"not-set\",platform_version=\"not-set\",yggdrasil_version=\"not-set\",spec_version=\"not-set\"} 3/); expect(metrics).toMatch(/client_sdk_versions\{sdk_name="unleash-client-java",sdk_version="5\.0\.0"\,platform_name=\"not-set\",platform_version=\"not-set\",yggdrasil_version=\"not-set\",spec_version=\"not-set\"} 3/); eventStore.emit(events_2.CLIENT_REGISTER, { sdkName: 'unleash-client-node', sdkVersion: '3.2.5', }); const newmetrics = await prometheusRegister.getSingleMetricAsString('client_sdk_versions'); expect(newmetrics).toMatch(/client_sdk_versions\{sdk_name="unleash-client-node",sdk_version="3\.2\.5"\,platform_name=\"not-set\",platform_version=\"not-set\",yggdrasil_version=\"not-set\",spec_version=\"not-set\"} 4/); }); test('Should register intervals when client registered', async () => { eventBus.emit(metric_events_1.CLIENT_REGISTERED, { appName: 'unleash-client-node', environment: 'development', interval: '15', }); const metrics = await prometheusRegister.getSingleMetricAsString('client_registration_total'); expect(metrics).toMatch(/client_registration_total{appName=\"unleash-client-node\",environment=\"development\",interval=\"15\"} 1/); }); test('Should not collect client sdk version if sdkVersion is of wrong format or non-existent', async () => { eventStore.emit(events_2.CLIENT_REGISTER, { sdkVersion: 'unleash-client-rust' }); eventStore.emit(events_2.CLIENT_REGISTER, {}); const metrics = await prometheusRegister.getSingleMetricAsString('client_sdk_versions'); expect(metrics).not.toMatch(/unleash-client-rust/); }); test('should collect metrics for project disabled numbers', async () => { eventStore.emit(events_2.PROJECT_ENVIRONMENT_REMOVED, { project: 'default', environment: 'staging', createdBy: 'Jay', createdByUserId: 26, }); const recordedMetric = await prometheusRegister.getSingleMetricAsString('project_environments_disabled'); expect(recordedMetric).toMatch(/project_environments_disabled{project_id=\"default\"} 1/); }); test('should collect metrics for lifecycle', async () => { await db.stores.featureToggleStore.create('default', { name: 'my_feature_b', createdByUserId: 9999, }); await featureLifeCycleStore.insert([ { feature: 'my_feature_b', stage: 'initial', }, ]); const stageCount = await featureLifeCycleReadModel.getStageCountByProject(); const stageDurations = await featureLifeCycleReadModel.getAllWithStageDuration(); expect(stageCount).toHaveLength(1); expect(stageDurations).toHaveLength(1); const metrics = await prometheusRegister.metrics(); expect(metrics).toMatch(/feature_lifecycle_stage_duration/); expect(metrics).toMatch(/feature_lifecycle_stage_count_by_project/); expect(metrics).toMatch(/feature_lifecycle_stage_entered/); }); test('should collect limit exceeded metrics', async () => { eventBus.emit(metric_events_1.EXCEEDS_LIMIT, { resource: 'feature flags', limit: '5000', }); const recordedMetric = await prometheusRegister.getSingleMetricAsString('exceeds_limit_error'); expect(recordedMetric).toMatch(/exceeds_limit_error{resource=\"feature flags\",limit=\"5000\"} 1/); }); test('should collect traffic_total metrics', async () => { const recordedMetric = await prometheusRegister.getSingleMetricAsString('traffic_total'); expect(recordedMetric).toMatch(/traffic_total 0/); }); test('should collect licensed_users metrics', async () => { const recordedMetric = await prometheusRegister.getSingleMetricAsString('licensed_users'); expect(recordedMetric).toMatch(/licensed_users 0/); }); //# sourceMappingURL=metrics.test.js.map