mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
* use apmApiClient * refacroting * fixing errors groups main statistics tests * refactoring * fixing error group detailed stats test * fixing ts issue * renaming empty archiver Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
f241f62aa7
commit
cd024b41b7
18 changed files with 859 additions and 761 deletions
|
@ -15,7 +15,7 @@ import { FtrProviderContext } from './ftr_provider_context';
|
|||
|
||||
type ArchiveName =
|
||||
| 'apm_8.0.0'
|
||||
| 'apm_8.0.0_empty'
|
||||
| 'apm_mappings_only_8.0.0'
|
||||
| '8.0.0'
|
||||
| 'metrics_8.0.0'
|
||||
| 'ml_8.0.0'
|
||||
|
|
|
@ -14,6 +14,7 @@ import {
|
|||
import { chunk } from 'lodash';
|
||||
import pLimit from 'p-limit';
|
||||
import { inspect } from 'util';
|
||||
import { PromiseReturnType } from '../../../plugins/observability/typings/common';
|
||||
import { InheritedFtrProviderContext } from './ftr_provider_context';
|
||||
|
||||
export async function synthtraceEsClient(context: InheritedFtrProviderContext) {
|
||||
|
@ -74,3 +75,5 @@ export async function synthtraceEsClient(context: InheritedFtrProviderContext) {
|
|||
},
|
||||
};
|
||||
}
|
||||
|
||||
export type SynthtraceEsClient = PromiseReturnType<typeof synthtraceEsClient>;
|
||||
|
|
|
@ -114,7 +114,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
let errorRateMetricValues: PromiseReturnType<typeof getErrorRateValues>;
|
||||
let errorTransactionValues: PromiseReturnType<typeof getErrorRateValues>;
|
||||
|
||||
registry.when('Services APIs', { config: 'basic', archives: ['apm_8.0.0_empty'] }, () => {
|
||||
registry.when('Services APIs', { config: 'basic', archives: ['apm_mappings_only_8.0.0'] }, () => {
|
||||
describe('when data is loaded ', () => {
|
||||
const GO_PROD_LIST_RATE = 75;
|
||||
const GO_PROD_LIST_ERROR_RATE = 25;
|
||||
|
|
|
@ -113,11 +113,11 @@ export default function apmApiIntegrationTests(providerContext: FtrProviderConte
|
|||
});
|
||||
|
||||
describe('services/error_groups_main_statistics', function () {
|
||||
loadTestFile(require.resolve('./services/error_groups_main_statistics'));
|
||||
loadTestFile(require.resolve('./services/error_groups/error_groups_main_statistics'));
|
||||
});
|
||||
|
||||
describe('services/error_groups_detailed_statistics', function () {
|
||||
loadTestFile(require.resolve('./services/error_groups_detailed_statistics'));
|
||||
loadTestFile(require.resolve('./services/error_groups/error_groups_detailed_statistics'));
|
||||
});
|
||||
|
||||
describe('services/detailed_statistics', function () {
|
||||
|
|
|
@ -116,7 +116,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
let latencyMetricValues: PromiseReturnType<typeof getLatencyValues>;
|
||||
let latencyTransactionValues: PromiseReturnType<typeof getLatencyValues>;
|
||||
|
||||
registry.when('Services APIs', { config: 'basic', archives: ['apm_8.0.0_empty'] }, () => {
|
||||
registry.when('Services APIs', { config: 'basic', archives: ['apm_mappings_only_8.0.0'] }, () => {
|
||||
describe('when data is loaded ', () => {
|
||||
const GO_PROD_RATE = 80;
|
||||
const GO_DEV_RATE = 20;
|
||||
|
|
|
@ -83,89 +83,95 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
}
|
||||
);
|
||||
|
||||
registry.when('data is loaded', { config: 'basic', archives: ['apm_8.0.0_empty'] }, () => {
|
||||
describe('Observability overview api ', () => {
|
||||
const GO_PROD_RATE = 50;
|
||||
const GO_DEV_RATE = 5;
|
||||
const JAVA_PROD_RATE = 45;
|
||||
before(async () => {
|
||||
const serviceGoProdInstance = service('synth-go', 'production', 'go').instance(
|
||||
'instance-a'
|
||||
);
|
||||
const serviceGoDevInstance = service('synth-go', 'development', 'go').instance(
|
||||
'instance-b'
|
||||
);
|
||||
const serviceJavaInstance = service('synth-java', 'production', 'java').instance(
|
||||
'instance-c'
|
||||
);
|
||||
|
||||
await synthtraceEsClient.index([
|
||||
...timerange(start, end)
|
||||
.interval('1m')
|
||||
.rate(GO_PROD_RATE)
|
||||
.flatMap((timestamp) =>
|
||||
serviceGoProdInstance
|
||||
.transaction('GET /api/product/list')
|
||||
.duration(1000)
|
||||
.timestamp(timestamp)
|
||||
.serialize()
|
||||
),
|
||||
...timerange(start, end)
|
||||
.interval('1m')
|
||||
.rate(GO_DEV_RATE)
|
||||
.flatMap((timestamp) =>
|
||||
serviceGoDevInstance
|
||||
.transaction('GET /api/product/:id')
|
||||
.duration(1000)
|
||||
.timestamp(timestamp)
|
||||
.serialize()
|
||||
),
|
||||
...timerange(start, end)
|
||||
.interval('1m')
|
||||
.rate(JAVA_PROD_RATE)
|
||||
.flatMap((timestamp) =>
|
||||
serviceJavaInstance
|
||||
.transaction('POST /api/product/buy')
|
||||
.duration(1000)
|
||||
.timestamp(timestamp)
|
||||
.serialize()
|
||||
),
|
||||
]);
|
||||
});
|
||||
|
||||
after(() => synthtraceEsClient.clean());
|
||||
|
||||
describe('compare throughput values', () => {
|
||||
let throughputValues: PromiseReturnType<typeof getThroughputValues>;
|
||||
registry.when(
|
||||
'data is loaded',
|
||||
{ config: 'basic', archives: ['apm_mappings_only_8.0.0'] },
|
||||
() => {
|
||||
describe('Observability overview api ', () => {
|
||||
const GO_PROD_RATE = 50;
|
||||
const GO_DEV_RATE = 5;
|
||||
const JAVA_PROD_RATE = 45;
|
||||
before(async () => {
|
||||
throughputValues = await getThroughputValues();
|
||||
const serviceGoProdInstance = service('synth-go', 'production', 'go').instance(
|
||||
'instance-a'
|
||||
);
|
||||
const serviceGoDevInstance = service('synth-go', 'development', 'go').instance(
|
||||
'instance-b'
|
||||
);
|
||||
const serviceJavaInstance = service('synth-java', 'production', 'java').instance(
|
||||
'instance-c'
|
||||
);
|
||||
|
||||
await synthtraceEsClient.index([
|
||||
...timerange(start, end)
|
||||
.interval('1m')
|
||||
.rate(GO_PROD_RATE)
|
||||
.flatMap((timestamp) =>
|
||||
serviceGoProdInstance
|
||||
.transaction('GET /api/product/list')
|
||||
.duration(1000)
|
||||
.timestamp(timestamp)
|
||||
.serialize()
|
||||
),
|
||||
...timerange(start, end)
|
||||
.interval('1m')
|
||||
.rate(GO_DEV_RATE)
|
||||
.flatMap((timestamp) =>
|
||||
serviceGoDevInstance
|
||||
.transaction('GET /api/product/:id')
|
||||
.duration(1000)
|
||||
.timestamp(timestamp)
|
||||
.serialize()
|
||||
),
|
||||
...timerange(start, end)
|
||||
.interval('1m')
|
||||
.rate(JAVA_PROD_RATE)
|
||||
.flatMap((timestamp) =>
|
||||
serviceJavaInstance
|
||||
.transaction('POST /api/product/buy')
|
||||
.duration(1000)
|
||||
.timestamp(timestamp)
|
||||
.serialize()
|
||||
),
|
||||
]);
|
||||
});
|
||||
|
||||
it('returns same number of service as shown on service inventory API', () => {
|
||||
const { serviceInventoryCount, observabilityOverview } = throughputValues;
|
||||
[serviceInventoryCount, observabilityOverview.serviceCount].forEach((value) =>
|
||||
expect(value).to.be.equal(2)
|
||||
);
|
||||
});
|
||||
after(() => synthtraceEsClient.clean());
|
||||
|
||||
it('returns same throughput value on service inventory and obs throughput count', () => {
|
||||
const { serviceInventoryThroughputSum, observabilityOverview } = throughputValues;
|
||||
const obsThroughputCount = roundNumber(observabilityOverview.transactionPerMinute.value);
|
||||
[serviceInventoryThroughputSum, obsThroughputCount].forEach((value) =>
|
||||
expect(value).to.be.equal(roundNumber(GO_PROD_RATE + GO_DEV_RATE + JAVA_PROD_RATE))
|
||||
);
|
||||
});
|
||||
describe('compare throughput values', () => {
|
||||
let throughputValues: PromiseReturnType<typeof getThroughputValues>;
|
||||
before(async () => {
|
||||
throughputValues = await getThroughputValues();
|
||||
});
|
||||
|
||||
it('returns same throughput value on service inventory and obs mean throughput timeseries', () => {
|
||||
const { serviceInventoryThroughputSum, observabilityOverview } = throughputValues;
|
||||
const obsThroughputMean = roundNumber(
|
||||
meanBy(observabilityOverview.transactionPerMinute.timeseries, 'y')
|
||||
);
|
||||
[serviceInventoryThroughputSum, obsThroughputMean].forEach((value) =>
|
||||
expect(value).to.be.equal(roundNumber(GO_PROD_RATE + GO_DEV_RATE + JAVA_PROD_RATE))
|
||||
);
|
||||
it('returns same number of service as shown on service inventory API', () => {
|
||||
const { serviceInventoryCount, observabilityOverview } = throughputValues;
|
||||
[serviceInventoryCount, observabilityOverview.serviceCount].forEach((value) =>
|
||||
expect(value).to.be.equal(2)
|
||||
);
|
||||
});
|
||||
|
||||
it('returns same throughput value on service inventory and obs throughput count', () => {
|
||||
const { serviceInventoryThroughputSum, observabilityOverview } = throughputValues;
|
||||
const obsThroughputCount = roundNumber(
|
||||
observabilityOverview.transactionPerMinute.value
|
||||
);
|
||||
[serviceInventoryThroughputSum, obsThroughputCount].forEach((value) =>
|
||||
expect(value).to.be.equal(roundNumber(GO_PROD_RATE + GO_DEV_RATE + JAVA_PROD_RATE))
|
||||
);
|
||||
});
|
||||
|
||||
it('returns same throughput value on service inventory and obs mean throughput timeseries', () => {
|
||||
const { serviceInventoryThroughputSum, observabilityOverview } = throughputValues;
|
||||
const obsThroughputMean = roundNumber(
|
||||
meanBy(observabilityOverview.transactionPerMinute.timeseries, 'y')
|
||||
);
|
||||
[serviceInventoryThroughputSum, obsThroughputMean].forEach((value) =>
|
||||
expect(value).to.be.equal(roundNumber(GO_PROD_RATE + GO_DEV_RATE + JAVA_PROD_RATE))
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -285,7 +285,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
|
||||
registry.when(
|
||||
'Service overview instances main statistics when data is generated',
|
||||
{ config: 'basic', archives: ['apm_8.0.0_empty'] },
|
||||
{ config: 'basic', archives: ['apm_mappings_only_8.0.0'] },
|
||||
() => {
|
||||
describe('for two go instances and one java instance', () => {
|
||||
const GO_A_INSTANCE_RATE_SUCCESS = 10;
|
||||
|
|
|
@ -0,0 +1,199 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import { first, last, sumBy } from 'lodash';
|
||||
import moment from 'moment';
|
||||
import { isFiniteNumber } from '../../../../../plugins/apm/common/utils/is_finite_number';
|
||||
import {
|
||||
APIClientRequestParamsOf,
|
||||
APIReturnType,
|
||||
} from '../../../../../plugins/apm/public/services/rest/createCallApmApi';
|
||||
import { RecursivePartial } from '../../../../../plugins/apm/typings/common';
|
||||
import { FtrProviderContext } from '../../../common/ftr_provider_context';
|
||||
import { registry } from '../../../common/registry';
|
||||
import { config, generateData } from './generate_data';
|
||||
import { getErrorGroupIds } from './get_error_group_ids';
|
||||
|
||||
type ErrorGroupsDetailedStatistics =
|
||||
APIReturnType<'GET /internal/apm/services/{serviceName}/error_groups/detailed_statistics'>;
|
||||
|
||||
export default function ApiTest({ getService }: FtrProviderContext) {
|
||||
const apmApiClient = getService('apmApiClient');
|
||||
const synthtraceEsClient = getService('synthtraceEsClient');
|
||||
|
||||
const serviceName = 'synth-go';
|
||||
const start = new Date('2021-01-01T00:00:00.000Z').getTime();
|
||||
const end = new Date('2021-01-01T00:15:00.000Z').getTime() - 1;
|
||||
|
||||
async function callApi(
|
||||
overrides?: RecursivePartial<
|
||||
APIClientRequestParamsOf<'GET /internal/apm/services/{serviceName}/error_groups/detailed_statistics'>['params']
|
||||
>
|
||||
) {
|
||||
return await apmApiClient.readUser({
|
||||
endpoint: `GET /internal/apm/services/{serviceName}/error_groups/detailed_statistics`,
|
||||
params: {
|
||||
path: { serviceName, ...overrides?.path },
|
||||
query: {
|
||||
start: new Date(start).toISOString(),
|
||||
end: new Date(end).toISOString(),
|
||||
numBuckets: 20,
|
||||
transactionType: 'request',
|
||||
groupIds: JSON.stringify(['foo']),
|
||||
environment: 'ENVIRONMENT_ALL',
|
||||
kuery: '',
|
||||
...overrides?.query,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
registry.when(
|
||||
'Error groups detailed statistics when data is not loaded',
|
||||
{ config: 'basic', archives: [] },
|
||||
() => {
|
||||
it('handles empty state', async () => {
|
||||
const response = await callApi();
|
||||
expect(response.status).to.be(200);
|
||||
expect(response.body).to.be.eql({ currentPeriod: {}, previousPeriod: {} });
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
registry.when(
|
||||
'Error groups detailed statistics',
|
||||
{ config: 'basic', archives: ['apm_mappings_only_8.0.0'] },
|
||||
() => {
|
||||
describe('when data is loaded', () => {
|
||||
const { PROD_LIST_ERROR_RATE, PROD_ID_ERROR_RATE } = config;
|
||||
before(async () => {
|
||||
await generateData({ serviceName, start, end, synthtraceEsClient });
|
||||
});
|
||||
|
||||
after(() => synthtraceEsClient.clean());
|
||||
|
||||
describe('without data comparison', () => {
|
||||
let errorGroupsDetailedStatistics: ErrorGroupsDetailedStatistics;
|
||||
let errorIds: string[] = [];
|
||||
before(async () => {
|
||||
errorIds = await getErrorGroupIds({ serviceName, start, end, apmApiClient });
|
||||
const response = await callApi({
|
||||
query: {
|
||||
groupIds: JSON.stringify(errorIds),
|
||||
},
|
||||
});
|
||||
errorGroupsDetailedStatistics = response.body;
|
||||
});
|
||||
|
||||
it('return detailed statistics for all errors found', () => {
|
||||
expect(Object.keys(errorGroupsDetailedStatistics.currentPeriod).sort()).to.eql(
|
||||
errorIds
|
||||
);
|
||||
});
|
||||
|
||||
it('returns correct number of occurrencies', () => {
|
||||
const numberOfBuckets = 15;
|
||||
const detailedStatisticsOccurrenciesSum = Object.values(
|
||||
errorGroupsDetailedStatistics.currentPeriod
|
||||
)
|
||||
.sort()
|
||||
.map(({ timeseries }) => {
|
||||
return sumBy(timeseries, 'y');
|
||||
});
|
||||
|
||||
expect(detailedStatisticsOccurrenciesSum).to.eql([
|
||||
PROD_ID_ERROR_RATE * numberOfBuckets,
|
||||
PROD_LIST_ERROR_RATE * numberOfBuckets,
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('return empty state when invalid group id', () => {
|
||||
let errorGroupsDetailedStatistics: ErrorGroupsDetailedStatistics;
|
||||
before(async () => {
|
||||
const response = await callApi({
|
||||
query: {
|
||||
groupIds: JSON.stringify(['foo']),
|
||||
},
|
||||
});
|
||||
errorGroupsDetailedStatistics = response.body;
|
||||
});
|
||||
|
||||
it('returns empty state', () => {
|
||||
expect(errorGroupsDetailedStatistics).to.be.eql({
|
||||
currentPeriod: {},
|
||||
previousPeriod: {},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('with comparison', () => {
|
||||
let errorGroupsDetailedStatistics: ErrorGroupsDetailedStatistics;
|
||||
let errorIds: string[] = [];
|
||||
before(async () => {
|
||||
errorIds = await getErrorGroupIds({ serviceName, start, end, apmApiClient });
|
||||
const response = await callApi({
|
||||
query: {
|
||||
groupIds: JSON.stringify(errorIds),
|
||||
start: moment(end).subtract(7, 'minutes').toISOString(),
|
||||
end: new Date(end).toISOString(),
|
||||
comparisonStart: new Date(start).toISOString(),
|
||||
comparisonEnd: moment(start).add(7, 'minutes').toISOString(),
|
||||
},
|
||||
});
|
||||
errorGroupsDetailedStatistics = response.body;
|
||||
});
|
||||
|
||||
it('returns some data', () => {
|
||||
expect(
|
||||
Object.keys(errorGroupsDetailedStatistics.currentPeriod).length
|
||||
).to.be.greaterThan(0);
|
||||
expect(
|
||||
Object.keys(errorGroupsDetailedStatistics.previousPeriod).length
|
||||
).to.be.greaterThan(0);
|
||||
|
||||
const hasCurrentPeriodData = Object.values(
|
||||
errorGroupsDetailedStatistics.currentPeriod
|
||||
)[0].timeseries.some(({ y }) => isFiniteNumber(y));
|
||||
|
||||
const hasPreviousPeriodData = Object.values(
|
||||
errorGroupsDetailedStatistics.previousPeriod
|
||||
)[0].timeseries.some(({ y }) => isFiniteNumber(y));
|
||||
|
||||
expect(hasCurrentPeriodData).to.equal(true);
|
||||
expect(hasPreviousPeriodData).to.equal(true);
|
||||
});
|
||||
|
||||
it('has same start time for both periods', () => {
|
||||
expect(
|
||||
first(Object.values(errorGroupsDetailedStatistics.currentPeriod)[0].timeseries)?.x
|
||||
).to.equal(
|
||||
first(Object.values(errorGroupsDetailedStatistics.previousPeriod)[0].timeseries)?.x
|
||||
);
|
||||
});
|
||||
|
||||
it('has same end time for both periods', () => {
|
||||
expect(
|
||||
last(Object.values(errorGroupsDetailedStatistics.currentPeriod)[0].timeseries)?.x
|
||||
).to.equal(
|
||||
last(Object.values(errorGroupsDetailedStatistics.previousPeriod)[0].timeseries)?.x
|
||||
);
|
||||
});
|
||||
|
||||
it('returns same number of buckets for both periods', () => {
|
||||
expect(
|
||||
Object.values(errorGroupsDetailedStatistics.currentPeriod)[0].timeseries.length
|
||||
).to.equal(
|
||||
Object.values(errorGroupsDetailedStatistics.previousPeriod)[0].timeseries.length
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import expect from '@kbn/expect';
|
||||
import moment from 'moment';
|
||||
import {
|
||||
APIClientRequestParamsOf,
|
||||
APIReturnType,
|
||||
} from '../../../../../plugins/apm/public/services/rest/createCallApmApi';
|
||||
import { RecursivePartial } from '../../../../../plugins/apm/typings/common';
|
||||
import { FtrProviderContext } from '../../../common/ftr_provider_context';
|
||||
import { registry } from '../../../common/registry';
|
||||
import { generateData, config } from './generate_data';
|
||||
|
||||
type ErrorGroupsMainStatistics =
|
||||
APIReturnType<'GET /internal/apm/services/{serviceName}/error_groups/main_statistics'>;
|
||||
|
||||
export default function ApiTest({ getService }: FtrProviderContext) {
|
||||
const apmApiClient = getService('apmApiClient');
|
||||
const synthtraceEsClient = getService('synthtraceEsClient');
|
||||
|
||||
const serviceName = 'synth-go';
|
||||
const start = new Date('2021-01-01T00:00:00.000Z').getTime();
|
||||
const end = new Date('2021-01-01T00:15:00.000Z').getTime() - 1;
|
||||
|
||||
async function callApi(
|
||||
overrides?: RecursivePartial<
|
||||
APIClientRequestParamsOf<'GET /internal/apm/services/{serviceName}/error_groups/main_statistics'>['params']
|
||||
>
|
||||
) {
|
||||
return await apmApiClient.readUser({
|
||||
endpoint: `GET /internal/apm/services/{serviceName}/error_groups/main_statistics`,
|
||||
params: {
|
||||
path: { serviceName, ...overrides?.path },
|
||||
query: {
|
||||
start: new Date(start).toISOString(),
|
||||
end: new Date(end).toISOString(),
|
||||
transactionType: 'request',
|
||||
environment: 'ENVIRONMENT_ALL',
|
||||
kuery: '',
|
||||
...overrides?.query,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
registry.when(
|
||||
'Error groups main statistics when data is not loaded',
|
||||
{ config: 'basic', archives: [] },
|
||||
() => {
|
||||
it('handles empty state', async () => {
|
||||
const response = await callApi();
|
||||
expect(response.status).to.be(200);
|
||||
expect(response.body.error_groups).to.empty();
|
||||
expect(response.body.is_aggregation_accurate).to.eql(true);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
registry.when(
|
||||
'Error groups main statistics',
|
||||
{ config: 'basic', archives: ['apm_mappings_only_8.0.0'] },
|
||||
() => {
|
||||
describe('when data is loaded', () => {
|
||||
const { PROD_LIST_ERROR_RATE, PROD_ID_ERROR_RATE, ERROR_NAME_1, ERROR_NAME_2 } = config;
|
||||
|
||||
before(async () => {
|
||||
await generateData({ serviceName, start, end, synthtraceEsClient });
|
||||
});
|
||||
|
||||
after(() => synthtraceEsClient.clean());
|
||||
|
||||
describe('returns the correct data', () => {
|
||||
let errorGroupMainStatistics: ErrorGroupsMainStatistics;
|
||||
before(async () => {
|
||||
const response = await callApi();
|
||||
errorGroupMainStatistics = response.body;
|
||||
});
|
||||
|
||||
it('returns correct number of occurrencies', () => {
|
||||
expect(errorGroupMainStatistics.error_groups.length).to.equal(2);
|
||||
expect(errorGroupMainStatistics.error_groups.map((error) => error.name).sort()).to.eql([
|
||||
ERROR_NAME_1,
|
||||
ERROR_NAME_2,
|
||||
]);
|
||||
});
|
||||
|
||||
it('returns correct occurences', () => {
|
||||
const numberOfBuckets = 15;
|
||||
expect(
|
||||
errorGroupMainStatistics.error_groups.map((error) => error.occurrences).sort()
|
||||
).to.eql([
|
||||
PROD_LIST_ERROR_RATE * numberOfBuckets,
|
||||
PROD_ID_ERROR_RATE * numberOfBuckets,
|
||||
]);
|
||||
});
|
||||
|
||||
it('has same last seen value as end date', () => {
|
||||
errorGroupMainStatistics.error_groups.map((error) => {
|
||||
expect(error.lastSeen).to.equal(moment(end).startOf('minute').valueOf());
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { service, timerange } from '@elastic/apm-synthtrace';
|
||||
import type { SynthtraceEsClient } from '../../../common/synthtrace_es_client';
|
||||
|
||||
export const config = {
|
||||
PROD_LIST_RATE: 75,
|
||||
PROD_LIST_ERROR_RATE: 25,
|
||||
PROD_ID_RATE: 50,
|
||||
PROD_ID_ERROR_RATE: 50,
|
||||
ERROR_NAME_1: 'Error test 1',
|
||||
ERROR_NAME_2: 'Error test 2',
|
||||
};
|
||||
|
||||
export async function generateData({
|
||||
synthtraceEsClient,
|
||||
serviceName,
|
||||
start,
|
||||
end,
|
||||
}: {
|
||||
synthtraceEsClient: SynthtraceEsClient;
|
||||
serviceName: string;
|
||||
start: number;
|
||||
end: number;
|
||||
}) {
|
||||
const serviceGoProdInstance = service(serviceName, 'production', 'go').instance('instance-a');
|
||||
|
||||
const transactionNameProductList = 'GET /api/product/list';
|
||||
const transactionNameProductId = 'GET /api/product/:id';
|
||||
|
||||
const {
|
||||
PROD_LIST_RATE,
|
||||
PROD_LIST_ERROR_RATE,
|
||||
PROD_ID_RATE,
|
||||
PROD_ID_ERROR_RATE,
|
||||
ERROR_NAME_1,
|
||||
ERROR_NAME_2,
|
||||
} = config;
|
||||
|
||||
await synthtraceEsClient.index([
|
||||
...timerange(start, end)
|
||||
.interval('1m')
|
||||
.rate(PROD_LIST_RATE)
|
||||
.flatMap((timestamp) =>
|
||||
serviceGoProdInstance
|
||||
.transaction(transactionNameProductList)
|
||||
.timestamp(timestamp)
|
||||
.duration(1000)
|
||||
.success()
|
||||
.serialize()
|
||||
),
|
||||
...timerange(start, end)
|
||||
.interval('1m')
|
||||
.rate(PROD_LIST_ERROR_RATE)
|
||||
.flatMap((timestamp) =>
|
||||
serviceGoProdInstance
|
||||
.transaction(transactionNameProductList)
|
||||
.errors(serviceGoProdInstance.error(ERROR_NAME_1, 'foo').timestamp(timestamp))
|
||||
.duration(1000)
|
||||
.timestamp(timestamp)
|
||||
.failure()
|
||||
.serialize()
|
||||
),
|
||||
...timerange(start, end)
|
||||
.interval('1m')
|
||||
.rate(PROD_ID_RATE)
|
||||
.flatMap((timestamp) =>
|
||||
serviceGoProdInstance
|
||||
.transaction(transactionNameProductId)
|
||||
.timestamp(timestamp)
|
||||
.duration(1000)
|
||||
.success()
|
||||
.serialize()
|
||||
),
|
||||
...timerange(start, end)
|
||||
.interval('1m')
|
||||
.rate(PROD_ID_ERROR_RATE)
|
||||
.flatMap((timestamp) =>
|
||||
serviceGoProdInstance
|
||||
.transaction(transactionNameProductId)
|
||||
.errors(serviceGoProdInstance.error(ERROR_NAME_2, 'bar').timestamp(timestamp))
|
||||
.duration(1000)
|
||||
.timestamp(timestamp)
|
||||
.failure()
|
||||
.serialize()
|
||||
),
|
||||
]);
|
||||
}
|
|
@ -5,28 +5,29 @@
|
|||
* 2.0.
|
||||
*/
|
||||
import { take } from 'lodash';
|
||||
import { ApmApiSupertest } from '../../common/apm_api_supertest';
|
||||
import { PromiseReturnType } from '../../../../../plugins/observability/typings/common';
|
||||
import { ApmServices } from '../../../common/config';
|
||||
|
||||
export async function getErrorGroupIds({
|
||||
apmApiSupertest,
|
||||
apmApiClient,
|
||||
start,
|
||||
end,
|
||||
serviceName = 'opbeans-java',
|
||||
count = 5,
|
||||
}: {
|
||||
apmApiSupertest: ApmApiSupertest;
|
||||
start: string;
|
||||
end: string;
|
||||
apmApiClient: PromiseReturnType<ApmServices['apmApiClient']>;
|
||||
start: number;
|
||||
end: number;
|
||||
serviceName?: string;
|
||||
count?: number;
|
||||
}) {
|
||||
const { body } = await apmApiSupertest({
|
||||
const { body } = await apmApiClient.readUser({
|
||||
endpoint: `GET /internal/apm/services/{serviceName}/error_groups/main_statistics`,
|
||||
params: {
|
||||
path: { serviceName },
|
||||
query: {
|
||||
start,
|
||||
end,
|
||||
start: new Date(start).toISOString(),
|
||||
end: new Date(end).toISOString(),
|
||||
transactionType: 'request',
|
||||
environment: 'ENVIRONMENT_ALL',
|
||||
kuery: '',
|
|
@ -1,202 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import url from 'url';
|
||||
import expect from '@kbn/expect';
|
||||
import moment from 'moment';
|
||||
import archives_metadata from '../../common/fixtures/es_archiver/archives_metadata';
|
||||
import { FtrProviderContext } from '../../common/ftr_provider_context';
|
||||
import { registry } from '../../common/registry';
|
||||
import { APIReturnType } from '../../../../plugins/apm/public/services/rest/createCallApmApi';
|
||||
import { createApmApiClient } from '../../common/apm_api_supertest';
|
||||
import { getErrorGroupIds } from './get_error_group_ids';
|
||||
|
||||
type ErrorGroupsDetailedStatistics =
|
||||
APIReturnType<'GET /internal/apm/services/{serviceName}/error_groups/detailed_statistics'>;
|
||||
|
||||
export default function ApiTest({ getService }: FtrProviderContext) {
|
||||
const supertest = getService('legacySupertestAsApmReadUser');
|
||||
const apmApiSupertest = createApmApiClient(supertest);
|
||||
|
||||
const archiveName = 'apm_8.0.0';
|
||||
const metadata = archives_metadata[archiveName];
|
||||
const { start, end } = metadata;
|
||||
|
||||
registry.when(
|
||||
'Error groups detailed statistics when data is not loaded',
|
||||
{ config: 'basic', archives: [] },
|
||||
() => {
|
||||
it('handles empty state', async () => {
|
||||
const groupIds = await getErrorGroupIds({ apmApiSupertest, start, end });
|
||||
|
||||
const response = await supertest.get(
|
||||
url.format({
|
||||
pathname: `/internal/apm/services/opbeans-java/error_groups/detailed_statistics`,
|
||||
query: {
|
||||
start,
|
||||
end,
|
||||
numBuckets: 20,
|
||||
transactionType: 'request',
|
||||
groupIds: JSON.stringify(groupIds),
|
||||
environment: 'ENVIRONMENT_ALL',
|
||||
kuery: '',
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
expect(response.status).to.be(200);
|
||||
expect(response.body).to.be.eql({ currentPeriod: {}, previousPeriod: {} });
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
registry.when(
|
||||
'Error groups detailed statistics when data is loaded',
|
||||
{ config: 'basic', archives: [archiveName] },
|
||||
() => {
|
||||
it('returns the correct data', async () => {
|
||||
const groupIds = await getErrorGroupIds({ apmApiSupertest, start, end });
|
||||
|
||||
const response = await supertest.get(
|
||||
url.format({
|
||||
pathname: `/internal/apm/services/opbeans-java/error_groups/detailed_statistics`,
|
||||
query: {
|
||||
start,
|
||||
end,
|
||||
numBuckets: 20,
|
||||
transactionType: 'request',
|
||||
groupIds: JSON.stringify(groupIds),
|
||||
environment: 'ENVIRONMENT_ALL',
|
||||
kuery: '',
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
expect(response.status).to.be(200);
|
||||
|
||||
const errorGroupsComparisonStatistics = response.body as ErrorGroupsDetailedStatistics;
|
||||
expect(Object.keys(errorGroupsComparisonStatistics.currentPeriod).sort()).to.eql(
|
||||
groupIds.sort()
|
||||
);
|
||||
|
||||
groupIds.forEach((groupId) => {
|
||||
expect(errorGroupsComparisonStatistics.currentPeriod[groupId]).not.to.be.empty();
|
||||
});
|
||||
|
||||
const errorgroupsComparisonStatistics =
|
||||
errorGroupsComparisonStatistics.currentPeriod[groupIds[0]];
|
||||
expect(
|
||||
errorgroupsComparisonStatistics.timeseries.map(({ y }) => y && isFinite(y)).length
|
||||
).to.be.greaterThan(0);
|
||||
expectSnapshot(errorgroupsComparisonStatistics).toMatch();
|
||||
});
|
||||
|
||||
it('returns an empty state when requested groupIds are not available in the given time range', async () => {
|
||||
const response = await supertest.get(
|
||||
url.format({
|
||||
pathname: `/internal/apm/services/opbeans-java/error_groups/detailed_statistics`,
|
||||
query: {
|
||||
start,
|
||||
end,
|
||||
numBuckets: 20,
|
||||
transactionType: 'request',
|
||||
groupIds: JSON.stringify(['foo']),
|
||||
environment: 'ENVIRONMENT_ALL',
|
||||
kuery: '',
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
expect(response.status).to.be(200);
|
||||
expect(response.body).to.be.eql({ currentPeriod: {}, previousPeriod: {} });
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
registry.when(
|
||||
'Error groups detailed statistics when data is loaded with previous data',
|
||||
{ config: 'basic', archives: [archiveName] },
|
||||
() => {
|
||||
describe('returns the correct data', async () => {
|
||||
let response: {
|
||||
status: number;
|
||||
body: ErrorGroupsDetailedStatistics;
|
||||
};
|
||||
let groupIds: string[];
|
||||
|
||||
before(async () => {
|
||||
groupIds = await getErrorGroupIds({ apmApiSupertest, start, end });
|
||||
|
||||
response = await supertest.get(
|
||||
url.format({
|
||||
pathname: `/internal/apm/services/opbeans-java/error_groups/detailed_statistics`,
|
||||
query: {
|
||||
numBuckets: 20,
|
||||
transactionType: 'request',
|
||||
groupIds: JSON.stringify(groupIds),
|
||||
start: moment(end).subtract(15, 'minutes').toISOString(),
|
||||
end,
|
||||
comparisonStart: start,
|
||||
comparisonEnd: moment(start).add(15, 'minutes').toISOString(),
|
||||
environment: 'ENVIRONMENT_ALL',
|
||||
kuery: '',
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
expect(response.status).to.be(200);
|
||||
});
|
||||
|
||||
it('returns correct timeseries', () => {
|
||||
const errorGroupsComparisonStatistics = response.body as ErrorGroupsDetailedStatistics;
|
||||
const errorgroupsComparisonStatistics =
|
||||
errorGroupsComparisonStatistics.currentPeriod[groupIds[0]];
|
||||
expect(
|
||||
errorgroupsComparisonStatistics.timeseries.map(({ y }) => y && isFinite(y)).length
|
||||
).to.be.greaterThan(0);
|
||||
expectSnapshot(errorgroupsComparisonStatistics).toMatch();
|
||||
});
|
||||
|
||||
it('matches x-axis on current period and previous period', () => {
|
||||
const errorGroupsComparisonStatistics = response.body as ErrorGroupsDetailedStatistics;
|
||||
|
||||
const currentPeriodItems = Object.values(errorGroupsComparisonStatistics.currentPeriod);
|
||||
const previousPeriodItems = Object.values(errorGroupsComparisonStatistics.previousPeriod);
|
||||
|
||||
const currentPeriodFirstItem = currentPeriodItems[0];
|
||||
const previousPeriodFirstItem = previousPeriodItems[0];
|
||||
|
||||
expect(currentPeriodFirstItem.timeseries.map(({ x }) => x)).to.be.eql(
|
||||
previousPeriodFirstItem.timeseries.map(({ x }) => x)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an empty state when requested groupIds are not available in the given time range', async () => {
|
||||
const response = await supertest.get(
|
||||
url.format({
|
||||
pathname: `/internal/apm/services/opbeans-java/error_groups/detailed_statistics`,
|
||||
query: {
|
||||
numBuckets: 20,
|
||||
transactionType: 'request',
|
||||
groupIds: JSON.stringify(['foo']),
|
||||
start: moment(end).subtract(15, 'minutes').toISOString(),
|
||||
end,
|
||||
comparisonStart: start,
|
||||
comparisonEnd: moment(start).add(15, 'minutes').toISOString(),
|
||||
environment: 'ENVIRONMENT_ALL',
|
||||
kuery: '',
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
expect(response.status).to.be(200);
|
||||
expect(response.body).to.be.eql({ currentPeriod: {}, previousPeriod: {} });
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
|
@ -1,123 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import url from 'url';
|
||||
import expect from '@kbn/expect';
|
||||
import archives_metadata from '../../common/fixtures/es_archiver/archives_metadata';
|
||||
import { FtrProviderContext } from '../../common/ftr_provider_context';
|
||||
import { registry } from '../../common/registry';
|
||||
import { APIReturnType } from '../../../../plugins/apm/public/services/rest/createCallApmApi';
|
||||
|
||||
type ErrorGroupsMainStatistics =
|
||||
APIReturnType<'GET /internal/apm/services/{serviceName}/error_groups/main_statistics'>;
|
||||
|
||||
export default function ApiTest({ getService }: FtrProviderContext) {
|
||||
const supertest = getService('legacySupertestAsApmReadUser');
|
||||
|
||||
const archiveName = 'apm_8.0.0';
|
||||
const metadata = archives_metadata[archiveName];
|
||||
const { start, end } = metadata;
|
||||
|
||||
registry.when(
|
||||
'Error groups main statistics when data is not loaded',
|
||||
{ config: 'basic', archives: [] },
|
||||
() => {
|
||||
it('handles empty state', async () => {
|
||||
const response = await supertest.get(
|
||||
url.format({
|
||||
pathname: `/internal/apm/services/opbeans-java/error_groups/main_statistics`,
|
||||
query: {
|
||||
start,
|
||||
end,
|
||||
transactionType: 'request',
|
||||
environment: 'ENVIRONMENT_ALL',
|
||||
kuery: '',
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
expect(response.status).to.be(200);
|
||||
|
||||
expect(response.status).to.be(200);
|
||||
expect(response.body.error_groups).to.empty();
|
||||
expect(response.body.is_aggregation_accurate).to.eql(true);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
registry.when(
|
||||
'Error groups main statistics when data is loaded',
|
||||
{ config: 'basic', archives: [archiveName] },
|
||||
() => {
|
||||
it('returns the correct data', async () => {
|
||||
const response = await supertest.get(
|
||||
url.format({
|
||||
pathname: `/internal/apm/services/opbeans-java/error_groups/main_statistics`,
|
||||
query: {
|
||||
start,
|
||||
end,
|
||||
transactionType: 'request',
|
||||
environment: 'production',
|
||||
kuery: '',
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
expect(response.status).to.be(200);
|
||||
|
||||
const errorGroupMainStatistics = response.body as ErrorGroupsMainStatistics;
|
||||
|
||||
expect(errorGroupMainStatistics.is_aggregation_accurate).to.eql(true);
|
||||
expect(errorGroupMainStatistics.error_groups.length).to.be.greaterThan(0);
|
||||
|
||||
expectSnapshot(errorGroupMainStatistics.error_groups.map(({ name }) => name))
|
||||
.toMatchInline(`
|
||||
Array [
|
||||
"Response status 404",
|
||||
"No converter found for return value of type: class com.sun.proxy.$Proxy162",
|
||||
"Response status 404",
|
||||
"Broken pipe",
|
||||
"java.io.IOException: Connection reset by peer",
|
||||
"Request method 'POST' not supported",
|
||||
"java.io.IOException: Connection reset by peer",
|
||||
"null",
|
||||
]
|
||||
`);
|
||||
|
||||
const occurences = errorGroupMainStatistics.error_groups.map(
|
||||
({ occurrences }) => occurrences
|
||||
);
|
||||
|
||||
occurences.forEach((occurence) => expect(occurence).to.be.greaterThan(0));
|
||||
|
||||
expectSnapshot(occurences).toMatchInline(`
|
||||
Array [
|
||||
17,
|
||||
12,
|
||||
4,
|
||||
4,
|
||||
3,
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
]
|
||||
`);
|
||||
|
||||
const firstItem = errorGroupMainStatistics.error_groups[0];
|
||||
|
||||
expectSnapshot(firstItem).toMatchInline(`
|
||||
Object {
|
||||
"group_id": "d16d39e7fa133b8943cea035430a7b4e",
|
||||
"lastSeen": 1627975146078,
|
||||
"name": "Response status 404",
|
||||
"occurrences": 17,
|
||||
}
|
||||
`);
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
|
@ -63,217 +63,223 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
});
|
||||
});
|
||||
|
||||
registry.when('data is loaded', { config: 'basic', archives: ['apm_8.0.0_empty'] }, () => {
|
||||
describe('Throughput chart api', () => {
|
||||
const GO_PROD_RATE = 50;
|
||||
const GO_DEV_RATE = 5;
|
||||
const JAVA_PROD_RATE = 45;
|
||||
|
||||
before(async () => {
|
||||
const serviceGoProdInstance = service(serviceName, 'production', 'go').instance(
|
||||
'instance-a'
|
||||
);
|
||||
const serviceGoDevInstance = service(serviceName, 'development', 'go').instance(
|
||||
'instance-b'
|
||||
);
|
||||
|
||||
const serviceJavaInstance = service('synth-java', 'development', 'java').instance(
|
||||
'instance-c'
|
||||
);
|
||||
|
||||
await synthtraceEsClient.index([
|
||||
...timerange(start, end)
|
||||
.interval('1m')
|
||||
.rate(GO_PROD_RATE)
|
||||
.flatMap((timestamp) =>
|
||||
serviceGoProdInstance
|
||||
.transaction('GET /api/product/list')
|
||||
.duration(1000)
|
||||
.timestamp(timestamp)
|
||||
.serialize()
|
||||
),
|
||||
...timerange(start, end)
|
||||
.interval('1m')
|
||||
.rate(GO_DEV_RATE)
|
||||
.flatMap((timestamp) =>
|
||||
serviceGoDevInstance
|
||||
.transaction('GET /api/product/:id')
|
||||
.duration(1000)
|
||||
.timestamp(timestamp)
|
||||
.serialize()
|
||||
),
|
||||
...timerange(start, end)
|
||||
.interval('1m')
|
||||
.rate(JAVA_PROD_RATE)
|
||||
.flatMap((timestamp) =>
|
||||
serviceJavaInstance
|
||||
.transaction('POST /api/product/buy')
|
||||
.duration(1000)
|
||||
.timestamp(timestamp)
|
||||
.serialize()
|
||||
),
|
||||
]);
|
||||
});
|
||||
|
||||
after(() => synthtraceEsClient.clean());
|
||||
|
||||
describe('compare transactions and metrics based throughput', () => {
|
||||
let throughputMetrics: ThroughputReturn;
|
||||
let throughputTransactions: ThroughputReturn;
|
||||
registry.when(
|
||||
'data is loaded',
|
||||
{ config: 'basic', archives: ['apm_mappings_only_8.0.0'] },
|
||||
() => {
|
||||
describe('Throughput chart api', () => {
|
||||
const GO_PROD_RATE = 50;
|
||||
const GO_DEV_RATE = 5;
|
||||
const JAVA_PROD_RATE = 45;
|
||||
|
||||
before(async () => {
|
||||
const [throughputMetricsResponse, throughputTransactionsResponse] = await Promise.all([
|
||||
callApi({ query: { kuery: 'processor.event : "metric"' } }),
|
||||
callApi({ query: { kuery: 'processor.event : "transaction"' } }),
|
||||
const serviceGoProdInstance = service(serviceName, 'production', 'go').instance(
|
||||
'instance-a'
|
||||
);
|
||||
const serviceGoDevInstance = service(serviceName, 'development', 'go').instance(
|
||||
'instance-b'
|
||||
);
|
||||
|
||||
const serviceJavaInstance = service('synth-java', 'development', 'java').instance(
|
||||
'instance-c'
|
||||
);
|
||||
|
||||
await synthtraceEsClient.index([
|
||||
...timerange(start, end)
|
||||
.interval('1m')
|
||||
.rate(GO_PROD_RATE)
|
||||
.flatMap((timestamp) =>
|
||||
serviceGoProdInstance
|
||||
.transaction('GET /api/product/list')
|
||||
.duration(1000)
|
||||
.timestamp(timestamp)
|
||||
.serialize()
|
||||
),
|
||||
...timerange(start, end)
|
||||
.interval('1m')
|
||||
.rate(GO_DEV_RATE)
|
||||
.flatMap((timestamp) =>
|
||||
serviceGoDevInstance
|
||||
.transaction('GET /api/product/:id')
|
||||
.duration(1000)
|
||||
.timestamp(timestamp)
|
||||
.serialize()
|
||||
),
|
||||
...timerange(start, end)
|
||||
.interval('1m')
|
||||
.rate(JAVA_PROD_RATE)
|
||||
.flatMap((timestamp) =>
|
||||
serviceJavaInstance
|
||||
.transaction('POST /api/product/buy')
|
||||
.duration(1000)
|
||||
.timestamp(timestamp)
|
||||
.serialize()
|
||||
),
|
||||
]);
|
||||
throughputMetrics = throughputMetricsResponse.body;
|
||||
throughputTransactions = throughputTransactionsResponse.body;
|
||||
});
|
||||
|
||||
it('returns some transactions data', () => {
|
||||
expect(throughputTransactions.currentPeriod.length).to.be.greaterThan(0);
|
||||
const hasData = throughputTransactions.currentPeriod.some(({ y }) => isFiniteNumber(y));
|
||||
expect(hasData).to.equal(true);
|
||||
});
|
||||
after(() => synthtraceEsClient.clean());
|
||||
|
||||
it('returns some metrics data', () => {
|
||||
expect(throughputMetrics.currentPeriod.length).to.be.greaterThan(0);
|
||||
const hasData = throughputMetrics.currentPeriod.some(({ y }) => isFiniteNumber(y));
|
||||
expect(hasData).to.equal(true);
|
||||
});
|
||||
describe('compare transactions and metrics based throughput', () => {
|
||||
let throughputMetrics: ThroughputReturn;
|
||||
let throughputTransactions: ThroughputReturn;
|
||||
|
||||
it('has same mean value for metrics and transactions data', () => {
|
||||
const transactionsMean = meanBy(throughputTransactions.currentPeriod, 'y');
|
||||
const metricsMean = meanBy(throughputMetrics.currentPeriod, 'y');
|
||||
[transactionsMean, metricsMean].forEach((value) =>
|
||||
expect(roundNumber(value)).to.be.equal(roundNumber(GO_PROD_RATE + GO_DEV_RATE))
|
||||
);
|
||||
});
|
||||
|
||||
it('has a bucket size of 10 seconds for transactions data', () => {
|
||||
const firstTimerange = throughputTransactions.currentPeriod[0].x;
|
||||
const secondTimerange = throughputTransactions.currentPeriod[1].x;
|
||||
const timeIntervalAsSeconds = (secondTimerange - firstTimerange) / 1000;
|
||||
expect(timeIntervalAsSeconds).to.equal(10);
|
||||
});
|
||||
|
||||
it('has a bucket size of 1 minute for metrics data', () => {
|
||||
const firstTimerange = throughputMetrics.currentPeriod[0].x;
|
||||
const secondTimerange = throughputMetrics.currentPeriod[1].x;
|
||||
const timeIntervalAsMinutes = (secondTimerange - firstTimerange) / 1000 / 60;
|
||||
expect(timeIntervalAsMinutes).to.equal(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('production environment', () => {
|
||||
let throughput: ThroughputReturn;
|
||||
|
||||
before(async () => {
|
||||
const throughputResponse = await callApi({ query: { environment: 'production' } });
|
||||
throughput = throughputResponse.body;
|
||||
});
|
||||
|
||||
it('returns some data', () => {
|
||||
expect(throughput.currentPeriod.length).to.be.greaterThan(0);
|
||||
const hasData = throughput.currentPeriod.some(({ y }) => isFiniteNumber(y));
|
||||
expect(hasData).to.equal(true);
|
||||
});
|
||||
|
||||
it('returns correct average throughput', () => {
|
||||
const throughputMean = meanBy(throughput.currentPeriod, 'y');
|
||||
expect(roundNumber(throughputMean)).to.be.equal(roundNumber(GO_PROD_RATE));
|
||||
});
|
||||
});
|
||||
|
||||
describe('when synth-java is selected', () => {
|
||||
let throughput: ThroughputReturn;
|
||||
|
||||
before(async () => {
|
||||
const throughputResponse = await callApi({ path: { serviceName: 'synth-java' } });
|
||||
throughput = throughputResponse.body;
|
||||
});
|
||||
|
||||
it('returns some data', () => {
|
||||
expect(throughput.currentPeriod.length).to.be.greaterThan(0);
|
||||
const hasData = throughput.currentPeriod.some(({ y }) => isFiniteNumber(y));
|
||||
expect(hasData).to.equal(true);
|
||||
});
|
||||
|
||||
it('returns throughput related to java agent', () => {
|
||||
const throughputMean = meanBy(throughput.currentPeriod, 'y');
|
||||
expect(roundNumber(throughputMean)).to.be.equal(roundNumber(JAVA_PROD_RATE));
|
||||
});
|
||||
});
|
||||
|
||||
describe('time comparisons', () => {
|
||||
let throughputResponse: ThroughputReturn;
|
||||
|
||||
before(async () => {
|
||||
const response = await callApi({
|
||||
query: {
|
||||
start: moment(end).subtract(7, 'minutes').toISOString(),
|
||||
end: new Date(end).toISOString(),
|
||||
comparisonStart: new Date(start).toISOString(),
|
||||
comparisonEnd: moment(start).add(7, 'minutes').toISOString(),
|
||||
},
|
||||
before(async () => {
|
||||
const [throughputMetricsResponse, throughputTransactionsResponse] = await Promise.all([
|
||||
callApi({ query: { kuery: 'processor.event : "metric"' } }),
|
||||
callApi({ query: { kuery: 'processor.event : "transaction"' } }),
|
||||
]);
|
||||
throughputMetrics = throughputMetricsResponse.body;
|
||||
throughputTransactions = throughputTransactionsResponse.body;
|
||||
});
|
||||
|
||||
it('returns some transactions data', () => {
|
||||
expect(throughputTransactions.currentPeriod.length).to.be.greaterThan(0);
|
||||
const hasData = throughputTransactions.currentPeriod.some(({ y }) => isFiniteNumber(y));
|
||||
expect(hasData).to.equal(true);
|
||||
});
|
||||
|
||||
it('returns some metrics data', () => {
|
||||
expect(throughputMetrics.currentPeriod.length).to.be.greaterThan(0);
|
||||
const hasData = throughputMetrics.currentPeriod.some(({ y }) => isFiniteNumber(y));
|
||||
expect(hasData).to.equal(true);
|
||||
});
|
||||
|
||||
it('has same mean value for metrics and transactions data', () => {
|
||||
const transactionsMean = meanBy(throughputTransactions.currentPeriod, 'y');
|
||||
const metricsMean = meanBy(throughputMetrics.currentPeriod, 'y');
|
||||
[transactionsMean, metricsMean].forEach((value) =>
|
||||
expect(roundNumber(value)).to.be.equal(roundNumber(GO_PROD_RATE + GO_DEV_RATE))
|
||||
);
|
||||
});
|
||||
|
||||
it('has a bucket size of 10 seconds for transactions data', () => {
|
||||
const firstTimerange = throughputTransactions.currentPeriod[0].x;
|
||||
const secondTimerange = throughputTransactions.currentPeriod[1].x;
|
||||
const timeIntervalAsSeconds = (secondTimerange - firstTimerange) / 1000;
|
||||
expect(timeIntervalAsSeconds).to.equal(10);
|
||||
});
|
||||
|
||||
it('has a bucket size of 1 minute for metrics data', () => {
|
||||
const firstTimerange = throughputMetrics.currentPeriod[0].x;
|
||||
const secondTimerange = throughputMetrics.currentPeriod[1].x;
|
||||
const timeIntervalAsMinutes = (secondTimerange - firstTimerange) / 1000 / 60;
|
||||
expect(timeIntervalAsMinutes).to.equal(1);
|
||||
});
|
||||
throughputResponse = response.body;
|
||||
});
|
||||
|
||||
it('returns some data', () => {
|
||||
expect(throughputResponse.currentPeriod.length).to.be.greaterThan(0);
|
||||
expect(throughputResponse.previousPeriod.length).to.be.greaterThan(0);
|
||||
describe('production environment', () => {
|
||||
let throughput: ThroughputReturn;
|
||||
|
||||
const hasCurrentPeriodData = throughputResponse.currentPeriod.some(({ y }) =>
|
||||
isFiniteNumber(y)
|
||||
);
|
||||
const hasPreviousPeriodData = throughputResponse.previousPeriod.some(({ y }) =>
|
||||
isFiniteNumber(y)
|
||||
);
|
||||
before(async () => {
|
||||
const throughputResponse = await callApi({ query: { environment: 'production' } });
|
||||
throughput = throughputResponse.body;
|
||||
});
|
||||
|
||||
expect(hasCurrentPeriodData).to.equal(true);
|
||||
expect(hasPreviousPeriodData).to.equal(true);
|
||||
it('returns some data', () => {
|
||||
expect(throughput.currentPeriod.length).to.be.greaterThan(0);
|
||||
const hasData = throughput.currentPeriod.some(({ y }) => isFiniteNumber(y));
|
||||
expect(hasData).to.equal(true);
|
||||
});
|
||||
|
||||
it('returns correct average throughput', () => {
|
||||
const throughputMean = meanBy(throughput.currentPeriod, 'y');
|
||||
expect(roundNumber(throughputMean)).to.be.equal(roundNumber(GO_PROD_RATE));
|
||||
});
|
||||
});
|
||||
|
||||
it('has same start time for both periods', () => {
|
||||
expect(first(throughputResponse.currentPeriod)?.x).to.equal(
|
||||
first(throughputResponse.previousPeriod)?.x
|
||||
);
|
||||
describe('when synth-java is selected', () => {
|
||||
let throughput: ThroughputReturn;
|
||||
|
||||
before(async () => {
|
||||
const throughputResponse = await callApi({ path: { serviceName: 'synth-java' } });
|
||||
throughput = throughputResponse.body;
|
||||
});
|
||||
|
||||
it('returns some data', () => {
|
||||
expect(throughput.currentPeriod.length).to.be.greaterThan(0);
|
||||
const hasData = throughput.currentPeriod.some(({ y }) => isFiniteNumber(y));
|
||||
expect(hasData).to.equal(true);
|
||||
});
|
||||
|
||||
it('returns throughput related to java agent', () => {
|
||||
const throughputMean = meanBy(throughput.currentPeriod, 'y');
|
||||
expect(roundNumber(throughputMean)).to.be.equal(roundNumber(JAVA_PROD_RATE));
|
||||
});
|
||||
});
|
||||
|
||||
it('has same end time for both periods', () => {
|
||||
expect(last(throughputResponse.currentPeriod)?.x).to.equal(
|
||||
last(throughputResponse.previousPeriod)?.x
|
||||
);
|
||||
});
|
||||
describe('time comparisons', () => {
|
||||
let throughputResponse: ThroughputReturn;
|
||||
|
||||
it('returns same number of buckets for both periods', () => {
|
||||
expect(throughputResponse.currentPeriod.length).to.be(
|
||||
throughputResponse.previousPeriod.length
|
||||
);
|
||||
});
|
||||
before(async () => {
|
||||
const response = await callApi({
|
||||
query: {
|
||||
start: moment(end).subtract(7, 'minutes').toISOString(),
|
||||
end: new Date(end).toISOString(),
|
||||
comparisonStart: new Date(start).toISOString(),
|
||||
comparisonEnd: moment(start).add(7, 'minutes').toISOString(),
|
||||
},
|
||||
});
|
||||
throughputResponse = response.body;
|
||||
});
|
||||
|
||||
it('has same mean value for both periods', () => {
|
||||
const currentPeriodMean = meanBy(
|
||||
throughputResponse.currentPeriod.filter((item) => isFiniteNumber(item.y) && item.y > 0),
|
||||
'y'
|
||||
);
|
||||
const previousPeriodMean = meanBy(
|
||||
throughputResponse.previousPeriod.filter(
|
||||
(item) => isFiniteNumber(item.y) && item.y > 0
|
||||
),
|
||||
'y'
|
||||
);
|
||||
const currentPeriod = throughputResponse.currentPeriod;
|
||||
const bucketSize = currentPeriod[1].x - currentPeriod[0].x;
|
||||
const durationAsMinutes = bucketSize / 1000 / 60;
|
||||
[currentPeriodMean, previousPeriodMean].every((value) =>
|
||||
expect(roundNumber(value)).to.be.equal(
|
||||
roundNumber((GO_PROD_RATE + GO_DEV_RATE) / durationAsMinutes)
|
||||
)
|
||||
);
|
||||
it('returns some data', () => {
|
||||
expect(throughputResponse.currentPeriod.length).to.be.greaterThan(0);
|
||||
expect(throughputResponse.previousPeriod.length).to.be.greaterThan(0);
|
||||
|
||||
const hasCurrentPeriodData = throughputResponse.currentPeriod.some(({ y }) =>
|
||||
isFiniteNumber(y)
|
||||
);
|
||||
const hasPreviousPeriodData = throughputResponse.previousPeriod.some(({ y }) =>
|
||||
isFiniteNumber(y)
|
||||
);
|
||||
|
||||
expect(hasCurrentPeriodData).to.equal(true);
|
||||
expect(hasPreviousPeriodData).to.equal(true);
|
||||
});
|
||||
|
||||
it('has same start time for both periods', () => {
|
||||
expect(first(throughputResponse.currentPeriod)?.x).to.equal(
|
||||
first(throughputResponse.previousPeriod)?.x
|
||||
);
|
||||
});
|
||||
|
||||
it('has same end time for both periods', () => {
|
||||
expect(last(throughputResponse.currentPeriod)?.x).to.equal(
|
||||
last(throughputResponse.previousPeriod)?.x
|
||||
);
|
||||
});
|
||||
|
||||
it('returns same number of buckets for both periods', () => {
|
||||
expect(throughputResponse.currentPeriod.length).to.be(
|
||||
throughputResponse.previousPeriod.length
|
||||
);
|
||||
});
|
||||
|
||||
it('has same mean value for both periods', () => {
|
||||
const currentPeriodMean = meanBy(
|
||||
throughputResponse.currentPeriod.filter(
|
||||
(item) => isFiniteNumber(item.y) && item.y > 0
|
||||
),
|
||||
'y'
|
||||
);
|
||||
const previousPeriodMean = meanBy(
|
||||
throughputResponse.previousPeriod.filter(
|
||||
(item) => isFiniteNumber(item.y) && item.y > 0
|
||||
),
|
||||
'y'
|
||||
);
|
||||
const currentPeriod = throughputResponse.currentPeriod;
|
||||
const bucketSize = currentPeriod[1].x - currentPeriod[0].x;
|
||||
const durationAsMinutes = bucketSize / 1000 / 60;
|
||||
[currentPeriodMean, previousPeriodMean].every((value) =>
|
||||
expect(roundNumber(value)).to.be.equal(
|
||||
roundNumber((GO_PROD_RATE + GO_DEV_RATE) / durationAsMinutes)
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -88,7 +88,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
|
||||
registry.when(
|
||||
'Dependencies throughput value',
|
||||
{ config: 'basic', archives: ['apm_8.0.0_empty'] },
|
||||
{ config: 'basic', archives: ['apm_mappings_only_8.0.0'] },
|
||||
() => {
|
||||
describe('when data is loaded', () => {
|
||||
const GO_PROD_RATE = 75;
|
||||
|
|
|
@ -104,7 +104,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
let throughputMetricValues: PromiseReturnType<typeof getThroughputValues>;
|
||||
let throughputTransactionValues: PromiseReturnType<typeof getThroughputValues>;
|
||||
|
||||
registry.when('Services APIs', { config: 'basic', archives: ['apm_8.0.0_empty'] }, () => {
|
||||
registry.when('Services APIs', { config: 'basic', archives: ['apm_mappings_only_8.0.0'] }, () => {
|
||||
describe('when data is loaded ', () => {
|
||||
const GO_PROD_RATE = 80;
|
||||
const GO_DEV_RATE = 20;
|
||||
|
|
|
@ -76,175 +76,181 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
}
|
||||
);
|
||||
|
||||
registry.when('data is loaded', { config: 'basic', archives: ['apm_8.0.0_empty'] }, () => {
|
||||
describe('transactions groups detailed stats', () => {
|
||||
const GO_PROD_RATE = 75;
|
||||
const GO_PROD_ERROR_RATE = 25;
|
||||
before(async () => {
|
||||
const serviceGoProdInstance = service(serviceName, 'production', 'go').instance(
|
||||
'instance-a'
|
||||
);
|
||||
|
||||
const transactionName = 'GET /api/product/list';
|
||||
|
||||
await synthtraceEsClient.index([
|
||||
...timerange(start, end)
|
||||
.interval('1m')
|
||||
.rate(GO_PROD_RATE)
|
||||
.flatMap((timestamp) =>
|
||||
serviceGoProdInstance
|
||||
.transaction(transactionName)
|
||||
.timestamp(timestamp)
|
||||
.duration(1000)
|
||||
.success()
|
||||
.serialize()
|
||||
),
|
||||
...timerange(start, end)
|
||||
.interval('1m')
|
||||
.rate(GO_PROD_ERROR_RATE)
|
||||
.flatMap((timestamp) =>
|
||||
serviceGoProdInstance
|
||||
.transaction(transactionName)
|
||||
.duration(1000)
|
||||
.timestamp(timestamp)
|
||||
.failure()
|
||||
.serialize()
|
||||
),
|
||||
]);
|
||||
});
|
||||
|
||||
after(() => synthtraceEsClient.clean());
|
||||
|
||||
describe('without comparisons', () => {
|
||||
let transactionsStatistics: TransactionsGroupsDetailedStatistics;
|
||||
let metricsStatistics: TransactionsGroupsDetailedStatistics;
|
||||
registry.when(
|
||||
'data is loaded',
|
||||
{ config: 'basic', archives: ['apm_mappings_only_8.0.0'] },
|
||||
() => {
|
||||
describe('transactions groups detailed stats', () => {
|
||||
const GO_PROD_RATE = 75;
|
||||
const GO_PROD_ERROR_RATE = 25;
|
||||
before(async () => {
|
||||
[metricsStatistics, transactionsStatistics] = await Promise.all([
|
||||
callApi({ query: { kuery: 'processor.event : "metric"' } }),
|
||||
callApi({ query: { kuery: 'processor.event : "transaction"' } }),
|
||||
const serviceGoProdInstance = service(serviceName, 'production', 'go').instance(
|
||||
'instance-a'
|
||||
);
|
||||
|
||||
const transactionName = 'GET /api/product/list';
|
||||
|
||||
await synthtraceEsClient.index([
|
||||
...timerange(start, end)
|
||||
.interval('1m')
|
||||
.rate(GO_PROD_RATE)
|
||||
.flatMap((timestamp) =>
|
||||
serviceGoProdInstance
|
||||
.transaction(transactionName)
|
||||
.timestamp(timestamp)
|
||||
.duration(1000)
|
||||
.success()
|
||||
.serialize()
|
||||
),
|
||||
...timerange(start, end)
|
||||
.interval('1m')
|
||||
.rate(GO_PROD_ERROR_RATE)
|
||||
.flatMap((timestamp) =>
|
||||
serviceGoProdInstance
|
||||
.transaction(transactionName)
|
||||
.duration(1000)
|
||||
.timestamp(timestamp)
|
||||
.failure()
|
||||
.serialize()
|
||||
),
|
||||
]);
|
||||
});
|
||||
|
||||
it('returns some transactions data', () => {
|
||||
expect(isEmpty(transactionsStatistics.currentPeriod)).to.be.equal(false);
|
||||
});
|
||||
after(() => synthtraceEsClient.clean());
|
||||
|
||||
it('returns some metrics data', () => {
|
||||
expect(isEmpty(metricsStatistics.currentPeriod)).to.be.equal(false);
|
||||
});
|
||||
describe('without comparisons', () => {
|
||||
let transactionsStatistics: TransactionsGroupsDetailedStatistics;
|
||||
let metricsStatistics: TransactionsGroupsDetailedStatistics;
|
||||
before(async () => {
|
||||
[metricsStatistics, transactionsStatistics] = await Promise.all([
|
||||
callApi({ query: { kuery: 'processor.event : "metric"' } }),
|
||||
callApi({ query: { kuery: 'processor.event : "transaction"' } }),
|
||||
]);
|
||||
});
|
||||
|
||||
it('has same latency mean value for metrics and transactions data', () => {
|
||||
const transactionsCurrentPeriod =
|
||||
transactionsStatistics.currentPeriod[transactionNames[0]];
|
||||
const metricsCurrentPeriod = metricsStatistics.currentPeriod[transactionNames[0]];
|
||||
const transactionsLatencyMean = meanBy(transactionsCurrentPeriod.latency, 'y');
|
||||
const metricsLatencyMean = meanBy(metricsCurrentPeriod.latency, 'y');
|
||||
[transactionsLatencyMean, metricsLatencyMean].forEach((value) =>
|
||||
expect(value).to.be.equal(1000000)
|
||||
);
|
||||
});
|
||||
it('returns some transactions data', () => {
|
||||
expect(isEmpty(transactionsStatistics.currentPeriod)).to.be.equal(false);
|
||||
});
|
||||
|
||||
it('has same error rate mean value for metrics and transactions data', () => {
|
||||
const transactionsCurrentPeriod =
|
||||
transactionsStatistics.currentPeriod[transactionNames[0]];
|
||||
const metricsCurrentPeriod = metricsStatistics.currentPeriod[transactionNames[0]];
|
||||
it('returns some metrics data', () => {
|
||||
expect(isEmpty(metricsStatistics.currentPeriod)).to.be.equal(false);
|
||||
});
|
||||
|
||||
const transactionsErrorRateMean = meanBy(transactionsCurrentPeriod.errorRate, 'y');
|
||||
const metricsErrorRateMean = meanBy(metricsCurrentPeriod.errorRate, 'y');
|
||||
[transactionsErrorRateMean, metricsErrorRateMean].forEach((value) =>
|
||||
expect(asPercent(value, 1)).to.be.equal(`${GO_PROD_ERROR_RATE}%`)
|
||||
);
|
||||
});
|
||||
it('has same latency mean value for metrics and transactions data', () => {
|
||||
const transactionsCurrentPeriod =
|
||||
transactionsStatistics.currentPeriod[transactionNames[0]];
|
||||
const metricsCurrentPeriod = metricsStatistics.currentPeriod[transactionNames[0]];
|
||||
const transactionsLatencyMean = meanBy(transactionsCurrentPeriod.latency, 'y');
|
||||
const metricsLatencyMean = meanBy(metricsCurrentPeriod.latency, 'y');
|
||||
[transactionsLatencyMean, metricsLatencyMean].forEach((value) =>
|
||||
expect(value).to.be.equal(1000000)
|
||||
);
|
||||
});
|
||||
|
||||
it('has same throughput mean value for metrics and transactions data', () => {
|
||||
const transactionsCurrentPeriod =
|
||||
transactionsStatistics.currentPeriod[transactionNames[0]];
|
||||
const metricsCurrentPeriod = metricsStatistics.currentPeriod[transactionNames[0]];
|
||||
const transactionsThroughputMean = roundNumber(
|
||||
meanBy(transactionsCurrentPeriod.throughput, 'y')
|
||||
);
|
||||
const metricsThroughputMean = roundNumber(meanBy(metricsCurrentPeriod.throughput, 'y'));
|
||||
[transactionsThroughputMean, metricsThroughputMean].forEach((value) =>
|
||||
expect(value).to.be.equal(roundNumber(GO_PROD_RATE + GO_PROD_ERROR_RATE))
|
||||
);
|
||||
});
|
||||
it('has same error rate mean value for metrics and transactions data', () => {
|
||||
const transactionsCurrentPeriod =
|
||||
transactionsStatistics.currentPeriod[transactionNames[0]];
|
||||
const metricsCurrentPeriod = metricsStatistics.currentPeriod[transactionNames[0]];
|
||||
|
||||
it('has same impact value for metrics and transactions data', () => {
|
||||
const transactionsCurrentPeriod =
|
||||
transactionsStatistics.currentPeriod[transactionNames[0]];
|
||||
const metricsCurrentPeriod = metricsStatistics.currentPeriod[transactionNames[0]];
|
||||
const transactionsErrorRateMean = meanBy(transactionsCurrentPeriod.errorRate, 'y');
|
||||
const metricsErrorRateMean = meanBy(metricsCurrentPeriod.errorRate, 'y');
|
||||
[transactionsErrorRateMean, metricsErrorRateMean].forEach((value) =>
|
||||
expect(asPercent(value, 1)).to.be.equal(`${GO_PROD_ERROR_RATE}%`)
|
||||
);
|
||||
});
|
||||
|
||||
const transactionsImpact = transactionsCurrentPeriod.impact;
|
||||
const metricsImpact = metricsCurrentPeriod.impact;
|
||||
[transactionsImpact, metricsImpact].forEach((value) => expect(value).to.be.equal(100));
|
||||
});
|
||||
});
|
||||
it('has same throughput mean value for metrics and transactions data', () => {
|
||||
const transactionsCurrentPeriod =
|
||||
transactionsStatistics.currentPeriod[transactionNames[0]];
|
||||
const metricsCurrentPeriod = metricsStatistics.currentPeriod[transactionNames[0]];
|
||||
const transactionsThroughputMean = roundNumber(
|
||||
meanBy(transactionsCurrentPeriod.throughput, 'y')
|
||||
);
|
||||
const metricsThroughputMean = roundNumber(meanBy(metricsCurrentPeriod.throughput, 'y'));
|
||||
[transactionsThroughputMean, metricsThroughputMean].forEach((value) =>
|
||||
expect(value).to.be.equal(roundNumber(GO_PROD_RATE + GO_PROD_ERROR_RATE))
|
||||
);
|
||||
});
|
||||
|
||||
describe('with comparisons', () => {
|
||||
let transactionsStatistics: TransactionsGroupsDetailedStatistics;
|
||||
before(async () => {
|
||||
transactionsStatistics = await callApi({
|
||||
query: {
|
||||
start: moment(end).subtract(7, 'minutes').toISOString(),
|
||||
end: new Date(end).toISOString(),
|
||||
comparisonStart: new Date(start).toISOString(),
|
||||
comparisonEnd: moment(start).add(7, 'minutes').toISOString(),
|
||||
},
|
||||
it('has same impact value for metrics and transactions data', () => {
|
||||
const transactionsCurrentPeriod =
|
||||
transactionsStatistics.currentPeriod[transactionNames[0]];
|
||||
const metricsCurrentPeriod = metricsStatistics.currentPeriod[transactionNames[0]];
|
||||
|
||||
const transactionsImpact = transactionsCurrentPeriod.impact;
|
||||
const metricsImpact = metricsCurrentPeriod.impact;
|
||||
[transactionsImpact, metricsImpact].forEach((value) => expect(value).to.be.equal(100));
|
||||
});
|
||||
});
|
||||
|
||||
it('returns some data for both periods', () => {
|
||||
expect(isEmpty(transactionsStatistics.currentPeriod)).to.be.equal(false);
|
||||
expect(isEmpty(transactionsStatistics.previousPeriod)).to.be.equal(false);
|
||||
});
|
||||
|
||||
it('has same start time for both periods', () => {
|
||||
const currentPeriod = transactionsStatistics.currentPeriod[transactionNames[0]];
|
||||
const previousPeriod = transactionsStatistics.previousPeriod[transactionNames[0]];
|
||||
[
|
||||
[currentPeriod.latency, previousPeriod.latency],
|
||||
[currentPeriod.errorRate, previousPeriod.errorRate],
|
||||
[currentPeriod.throughput, previousPeriod.throughput],
|
||||
].forEach(([currentTimeseries, previousTimeseries]) => {
|
||||
const firstCurrentPeriodDate = new Date(
|
||||
first(currentTimeseries)?.x ?? NaN
|
||||
).toISOString();
|
||||
const firstPreviousPeriodDate = new Date(
|
||||
first(previousPeriod.latency)?.x ?? NaN
|
||||
).toISOString();
|
||||
|
||||
expect(firstCurrentPeriodDate).to.equal(firstPreviousPeriodDate);
|
||||
describe('with comparisons', () => {
|
||||
let transactionsStatistics: TransactionsGroupsDetailedStatistics;
|
||||
before(async () => {
|
||||
transactionsStatistics = await callApi({
|
||||
query: {
|
||||
start: moment(end).subtract(7, 'minutes').toISOString(),
|
||||
end: new Date(end).toISOString(),
|
||||
comparisonStart: new Date(start).toISOString(),
|
||||
comparisonEnd: moment(start).add(7, 'minutes').toISOString(),
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
it('has same end time for both periods', () => {
|
||||
const currentPeriod = transactionsStatistics.currentPeriod[transactionNames[0]];
|
||||
const previousPeriod = transactionsStatistics.previousPeriod[transactionNames[0]];
|
||||
[
|
||||
[currentPeriod.latency, previousPeriod.latency],
|
||||
[currentPeriod.errorRate, previousPeriod.errorRate],
|
||||
[currentPeriod.throughput, previousPeriod.throughput],
|
||||
].forEach(([currentTimeseries, previousTimeseries]) => {
|
||||
const lastCurrentPeriodDate = new Date(last(currentTimeseries)?.x ?? NaN).toISOString();
|
||||
const lastPreviousPeriodDate = new Date(
|
||||
last(previousPeriod.latency)?.x ?? NaN
|
||||
).toISOString();
|
||||
|
||||
expect(lastCurrentPeriodDate).to.equal(lastPreviousPeriodDate);
|
||||
it('returns some data for both periods', () => {
|
||||
expect(isEmpty(transactionsStatistics.currentPeriod)).to.be.equal(false);
|
||||
expect(isEmpty(transactionsStatistics.previousPeriod)).to.be.equal(false);
|
||||
});
|
||||
});
|
||||
|
||||
it('returns same number of buckets for both periods', () => {
|
||||
const currentPeriod = transactionsStatistics.currentPeriod[transactionNames[0]];
|
||||
const previousPeriod = transactionsStatistics.previousPeriod[transactionNames[0]];
|
||||
[
|
||||
[currentPeriod.latency, previousPeriod.latency],
|
||||
[currentPeriod.errorRate, previousPeriod.errorRate],
|
||||
[currentPeriod.throughput, previousPeriod.throughput],
|
||||
].forEach(([currentTimeseries, previousTimeseries]) => {
|
||||
expect(currentTimeseries.length).to.equal(previousTimeseries.length);
|
||||
it('has same start time for both periods', () => {
|
||||
const currentPeriod = transactionsStatistics.currentPeriod[transactionNames[0]];
|
||||
const previousPeriod = transactionsStatistics.previousPeriod[transactionNames[0]];
|
||||
[
|
||||
[currentPeriod.latency, previousPeriod.latency],
|
||||
[currentPeriod.errorRate, previousPeriod.errorRate],
|
||||
[currentPeriod.throughput, previousPeriod.throughput],
|
||||
].forEach(([currentTimeseries, previousTimeseries]) => {
|
||||
const firstCurrentPeriodDate = new Date(
|
||||
first(currentTimeseries)?.x ?? NaN
|
||||
).toISOString();
|
||||
const firstPreviousPeriodDate = new Date(
|
||||
first(previousPeriod.latency)?.x ?? NaN
|
||||
).toISOString();
|
||||
|
||||
expect(firstCurrentPeriodDate).to.equal(firstPreviousPeriodDate);
|
||||
});
|
||||
});
|
||||
it('has same end time for both periods', () => {
|
||||
const currentPeriod = transactionsStatistics.currentPeriod[transactionNames[0]];
|
||||
const previousPeriod = transactionsStatistics.previousPeriod[transactionNames[0]];
|
||||
[
|
||||
[currentPeriod.latency, previousPeriod.latency],
|
||||
[currentPeriod.errorRate, previousPeriod.errorRate],
|
||||
[currentPeriod.throughput, previousPeriod.throughput],
|
||||
].forEach(([currentTimeseries, previousTimeseries]) => {
|
||||
const lastCurrentPeriodDate = new Date(
|
||||
last(currentTimeseries)?.x ?? NaN
|
||||
).toISOString();
|
||||
const lastPreviousPeriodDate = new Date(
|
||||
last(previousPeriod.latency)?.x ?? NaN
|
||||
).toISOString();
|
||||
|
||||
expect(lastCurrentPeriodDate).to.equal(lastPreviousPeriodDate);
|
||||
});
|
||||
});
|
||||
|
||||
it('returns same number of buckets for both periods', () => {
|
||||
const currentPeriod = transactionsStatistics.currentPeriod[transactionNames[0]];
|
||||
const previousPeriod = transactionsStatistics.previousPeriod[transactionNames[0]];
|
||||
[
|
||||
[currentPeriod.latency, previousPeriod.latency],
|
||||
[currentPeriod.errorRate, previousPeriod.errorRate],
|
||||
[currentPeriod.throughput, previousPeriod.throughput],
|
||||
].forEach(([currentTimeseries, previousTimeseries]) => {
|
||||
expect(currentTimeseries.length).to.equal(previousTimeseries.length);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue