mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
* error rate with synthtrace
* latency with synthtrace
* adding production env test
* addressing pr comments
(cherry picked from commit d675d91c94
)
Co-authored-by: Cauê Marcondes <55978943+cauemarcondes@users.noreply.github.com>
This commit is contained in:
parent
e91a5c3684
commit
92dd90d688
4 changed files with 279 additions and 720 deletions
|
@ -1,268 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`APM API tests basic apm_8.0.0 Error rate when data is loaded returns the transaction error rate has the correct error rate 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"x": 1627973400000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1627973460000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1627973520000,
|
||||
"y": 0.333333333333333,
|
||||
},
|
||||
Object {
|
||||
"x": 1627973580000,
|
||||
"y": 0.181818181818182,
|
||||
},
|
||||
Object {
|
||||
"x": 1627973640000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1627973700000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1627973760000,
|
||||
"y": 0.166666666666667,
|
||||
},
|
||||
Object {
|
||||
"x": 1627973820000,
|
||||
"y": 0.181818181818182,
|
||||
},
|
||||
Object {
|
||||
"x": 1627973880000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1627973940000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974000000,
|
||||
"y": 0.0833333333333333,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974060000,
|
||||
"y": 0.0769230769230769,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974120000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974180000,
|
||||
"y": 0.1,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974240000,
|
||||
"y": 0.153846153846154,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974300000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974360000,
|
||||
"y": null,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974420000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974480000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974540000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974600000,
|
||||
"y": 0.125,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974660000,
|
||||
"y": 0.6,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974720000,
|
||||
"y": 0.2,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974780000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974840000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974900000,
|
||||
"y": 0.0666666666666667,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974960000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1627975020000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1627975080000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1627975140000,
|
||||
"y": 0.181818181818182,
|
||||
},
|
||||
Object {
|
||||
"x": 1627975200000,
|
||||
"y": null,
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`APM API tests basic apm_8.0.0 Error rate when data is loaded returns the transaction error rate with comparison data has the correct error rate 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"x": 1627974300000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974360000,
|
||||
"y": null,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974420000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974480000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974540000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974600000,
|
||||
"y": 0.125,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974660000,
|
||||
"y": 0.6,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974720000,
|
||||
"y": 0.2,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974780000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974840000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974900000,
|
||||
"y": 0.0666666666666667,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974960000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1627975020000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1627975080000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1627975140000,
|
||||
"y": 0.181818181818182,
|
||||
},
|
||||
Object {
|
||||
"x": 1627975200000,
|
||||
"y": null,
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`APM API tests basic apm_8.0.0 Error rate when data is loaded returns the transaction error rate with comparison data has the correct error rate 2`] = `
|
||||
Array [
|
||||
Object {
|
||||
"x": 1627974300000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974360000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974420000,
|
||||
"y": 0.333333333333333,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974480000,
|
||||
"y": 0.181818181818182,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974540000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974600000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974660000,
|
||||
"y": 0.166666666666667,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974720000,
|
||||
"y": 0.181818181818182,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974780000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974840000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974900000,
|
||||
"y": 0.0833333333333333,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974960000,
|
||||
"y": 0.0769230769230769,
|
||||
},
|
||||
Object {
|
||||
"x": 1627975020000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1627975080000,
|
||||
"y": 0.1,
|
||||
},
|
||||
Object {
|
||||
"x": 1627975140000,
|
||||
"y": 0.153846153846154,
|
||||
},
|
||||
Object {
|
||||
"x": 1627975200000,
|
||||
"y": null,
|
||||
},
|
||||
]
|
||||
`;
|
|
@ -1,131 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`APM API tests basic apm_8.0.0 Latency with a basic license when data is loaded time comparison returns some data 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"x": 1627974300000,
|
||||
"y": 22799,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974360000,
|
||||
"y": 3227391,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974420000,
|
||||
"y": 15565.2222222222,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974480000,
|
||||
"y": 54307.5714285714,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974540000,
|
||||
"y": 16655,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974600000,
|
||||
"y": 9453,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974660000,
|
||||
"y": 31119,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974720000,
|
||||
"y": 15282.2,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974780000,
|
||||
"y": 18709,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974840000,
|
||||
"y": 12095,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974900000,
|
||||
"y": 16291,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974960000,
|
||||
"y": 13444.3333333333,
|
||||
},
|
||||
Object {
|
||||
"x": 1627975020000,
|
||||
"y": 13241.6666666667,
|
||||
},
|
||||
Object {
|
||||
"x": 1627975080000,
|
||||
"y": 25535,
|
||||
},
|
||||
Object {
|
||||
"x": 1627975140000,
|
||||
"y": 11024.6,
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`APM API tests basic apm_8.0.0 Latency with a basic license when data is loaded time comparison returns some data 2`] = `
|
||||
Array [
|
||||
Object {
|
||||
"x": 1627974300000,
|
||||
"y": 34866.2,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974360000,
|
||||
"y": 104799,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974420000,
|
||||
"y": 36247,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974480000,
|
||||
"y": 22207,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974540000,
|
||||
"y": 80191,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974600000,
|
||||
"y": 11520.4545454545,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974660000,
|
||||
"y": 47031.8888888889,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974720000,
|
||||
"y": 30249.6666666667,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974780000,
|
||||
"y": 14868.3333333333,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974840000,
|
||||
"y": 17199,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974900000,
|
||||
"y": 19837.2222222222,
|
||||
},
|
||||
Object {
|
||||
"x": 1627974960000,
|
||||
"y": 19397.6666666667,
|
||||
},
|
||||
Object {
|
||||
"x": 1627975020000,
|
||||
"y": 22473.6666666667,
|
||||
},
|
||||
Object {
|
||||
"x": 1627975080000,
|
||||
"y": 11362.2,
|
||||
},
|
||||
Object {
|
||||
"x": 1627975140000,
|
||||
"y": 26319,
|
||||
},
|
||||
]
|
||||
`;
|
|
@ -4,12 +4,15 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { apm, timerange } from '@kbn/apm-synthtrace';
|
||||
import expect from '@kbn/expect';
|
||||
import { first, last } from 'lodash';
|
||||
import moment from 'moment';
|
||||
import { APIReturnType } from '@kbn/apm-plugin/public/services/rest/create_call_apm_api';
|
||||
import archives_metadata from '../../common/fixtures/es_archiver/archives_metadata';
|
||||
import {
|
||||
APIClientRequestParamsOf,
|
||||
APIReturnType,
|
||||
} from '@kbn/apm-plugin/public/services/rest/create_call_apm_api';
|
||||
import { RecursivePartial } from '@kbn/apm-plugin/typings/common';
|
||||
import { FtrProviderContext } from '../../common/ftr_provider_context';
|
||||
|
||||
type ErrorRate =
|
||||
|
@ -18,48 +21,36 @@ type ErrorRate =
|
|||
export default function ApiTest({ getService }: FtrProviderContext) {
|
||||
const registry = getService('registry');
|
||||
const apmApiClient = getService('apmApiClient');
|
||||
|
||||
const archiveName = 'apm_8.0.0';
|
||||
const synthtraceEsClient = getService('synthtraceEsClient');
|
||||
|
||||
// url parameters
|
||||
const { start, end } = archives_metadata[archiveName];
|
||||
const transactionType = 'request';
|
||||
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 fetchErrorCharts({
|
||||
serviceName,
|
||||
query,
|
||||
}: {
|
||||
serviceName: string;
|
||||
query: {
|
||||
start: string;
|
||||
end: string;
|
||||
transactionType: string;
|
||||
environment: string;
|
||||
kuery: string;
|
||||
offset?: string;
|
||||
};
|
||||
}) {
|
||||
async function fetchErrorCharts(
|
||||
overrides?: RecursivePartial<
|
||||
APIClientRequestParamsOf<'GET /internal/apm/services/{serviceName}/transactions/charts/error_rate'>['params']
|
||||
>
|
||||
) {
|
||||
return await apmApiClient.readUser({
|
||||
endpoint: `GET /internal/apm/services/{serviceName}/transactions/charts/error_rate`,
|
||||
params: {
|
||||
path: { serviceName },
|
||||
query,
|
||||
path: { serviceName: overrides?.path?.serviceName || 'opbeans-go' },
|
||||
query: {
|
||||
start: new Date(start).toISOString(),
|
||||
end: new Date(end).toISOString(),
|
||||
transactionType: 'request',
|
||||
environment: 'ENVIRONMENT_ALL',
|
||||
kuery: '',
|
||||
...overrides?.query,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
registry.when('Error rate when data is not loaded', { config: 'basic', archives: [] }, () => {
|
||||
it('handles the empty state', async () => {
|
||||
const response = await fetchErrorCharts({
|
||||
serviceName: 'opbeans-java',
|
||||
query: {
|
||||
start,
|
||||
end,
|
||||
transactionType,
|
||||
environment: 'ENVIRONMENT_ALL',
|
||||
kuery: '',
|
||||
},
|
||||
});
|
||||
const response = await fetchErrorCharts();
|
||||
expect(response.status).to.be(200);
|
||||
|
||||
const body = response.body as ErrorRate;
|
||||
|
@ -71,14 +62,9 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
|
||||
it('handles the empty state with comparison data', async () => {
|
||||
const response = await fetchErrorCharts({
|
||||
serviceName: 'opbeans-java',
|
||||
query: {
|
||||
transactionType,
|
||||
start: moment(end).subtract(15, 'minutes').toISOString(),
|
||||
end,
|
||||
offset: '15m',
|
||||
environment: 'ENVIRONMENT_ALL',
|
||||
kuery: '',
|
||||
start: moment(end).subtract(7, 'minutes').toISOString(),
|
||||
offset: '7m',
|
||||
},
|
||||
});
|
||||
expect(response.status).to.be(200);
|
||||
|
@ -91,149 +77,174 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
});
|
||||
});
|
||||
|
||||
registry.when(
|
||||
'Error rate when data is loaded',
|
||||
{ config: 'basic', archives: [archiveName] },
|
||||
() => {
|
||||
describe('returns the transaction error rate', () => {
|
||||
let errorRateResponse: ErrorRate;
|
||||
registry.when('Error rate when data is loaded', { config: 'basic', archives: [] }, () => {
|
||||
const config = {
|
||||
firstTransaction: {
|
||||
name: 'GET /apple 🍎 ',
|
||||
successRate: 50,
|
||||
failureRate: 50,
|
||||
},
|
||||
};
|
||||
before(async () => {
|
||||
const serviceGoProdInstance = apm
|
||||
.service({ name: 'opbeans-go', environment: 'production', agentName: 'go' })
|
||||
.instance('instance-a');
|
||||
|
||||
before(async () => {
|
||||
const response = await fetchErrorCharts({
|
||||
serviceName: 'opbeans-java',
|
||||
query: {
|
||||
start,
|
||||
end,
|
||||
transactionType,
|
||||
environment: 'ENVIRONMENT_ALL',
|
||||
kuery: '',
|
||||
},
|
||||
});
|
||||
const { firstTransaction } = config;
|
||||
|
||||
errorRateResponse = response.body;
|
||||
});
|
||||
|
||||
it('returns some data', () => {
|
||||
expect(errorRateResponse.currentPeriod.average).to.be.greaterThan(0);
|
||||
expect(errorRateResponse.previousPeriod.average).to.be(null);
|
||||
|
||||
expect(errorRateResponse.currentPeriod.timeseries.length).to.be.greaterThan(0);
|
||||
expect(errorRateResponse.previousPeriod.timeseries).to.empty();
|
||||
|
||||
const nonNullDataPoints = errorRateResponse.currentPeriod.timeseries.filter(
|
||||
({ y }) => y !== null
|
||||
);
|
||||
|
||||
expect(nonNullDataPoints.length).to.be.greaterThan(0);
|
||||
});
|
||||
|
||||
it('has the correct start date', () => {
|
||||
expectSnapshot(
|
||||
new Date(first(errorRateResponse.currentPeriod.timeseries)?.x ?? NaN).toISOString()
|
||||
).toMatchInline(`"2021-08-03T06:50:00.000Z"`);
|
||||
});
|
||||
|
||||
it('has the correct end date', () => {
|
||||
expectSnapshot(
|
||||
new Date(last(errorRateResponse.currentPeriod.timeseries)?.x ?? NaN).toISOString()
|
||||
).toMatchInline(`"2021-08-03T07:20:00.000Z"`);
|
||||
});
|
||||
|
||||
it('has the correct number of buckets', () => {
|
||||
expectSnapshot(errorRateResponse.currentPeriod.timeseries.length).toMatchInline(`31`);
|
||||
});
|
||||
|
||||
it('has the correct calculation for average', () => {
|
||||
expectSnapshot(errorRateResponse.currentPeriod.average).toMatchInline(
|
||||
`0.0848214285714286`
|
||||
);
|
||||
});
|
||||
|
||||
it('has the correct error rate', () => {
|
||||
expectSnapshot(errorRateResponse.currentPeriod.timeseries).toMatch();
|
||||
const documents = timerange(start, end)
|
||||
.ratePerMinute(firstTransaction.successRate)
|
||||
.generator((timestamp) =>
|
||||
serviceGoProdInstance
|
||||
.transaction({ transactionName: firstTransaction.name })
|
||||
.timestamp(timestamp)
|
||||
.duration(1000)
|
||||
.success()
|
||||
)
|
||||
.merge(
|
||||
timerange(start, end)
|
||||
.ratePerMinute(firstTransaction.failureRate)
|
||||
.generator((timestamp) =>
|
||||
serviceGoProdInstance
|
||||
.transaction({ transactionName: firstTransaction.name })
|
||||
.errors(
|
||||
serviceGoProdInstance
|
||||
.error({
|
||||
message: 'Error 1',
|
||||
type: firstTransaction.name,
|
||||
groupingName: 'Error test',
|
||||
})
|
||||
.timestamp(timestamp)
|
||||
)
|
||||
.duration(1000)
|
||||
.timestamp(timestamp)
|
||||
.failure()
|
||||
)
|
||||
);
|
||||
|
||||
await synthtraceEsClient.index(documents);
|
||||
});
|
||||
|
||||
after(() => synthtraceEsClient.clean());
|
||||
|
||||
describe('returns the transaction error rate', () => {
|
||||
let errorRateResponse: ErrorRate;
|
||||
|
||||
before(async () => {
|
||||
const response = await fetchErrorCharts({
|
||||
query: { transactionName: config.firstTransaction.name },
|
||||
});
|
||||
errorRateResponse = response.body;
|
||||
});
|
||||
|
||||
describe('returns the transaction error rate with comparison data', () => {
|
||||
let errorRateResponse: ErrorRate;
|
||||
it('returns some data', () => {
|
||||
expect(errorRateResponse.currentPeriod.average).to.be.greaterThan(0);
|
||||
expect(errorRateResponse.previousPeriod.average).to.be(null);
|
||||
|
||||
before(async () => {
|
||||
const response = await fetchErrorCharts({
|
||||
serviceName: 'opbeans-java',
|
||||
query: {
|
||||
transactionType,
|
||||
start: moment(end).subtract(15, 'minutes').toISOString(),
|
||||
end,
|
||||
offset: '15m',
|
||||
environment: 'ENVIRONMENT_ALL',
|
||||
kuery: '',
|
||||
},
|
||||
});
|
||||
expect(errorRateResponse.currentPeriod.timeseries).not.to.be.empty();
|
||||
expect(errorRateResponse.previousPeriod.timeseries).to.empty();
|
||||
|
||||
errorRateResponse = response.body;
|
||||
});
|
||||
const nonNullDataPoints = errorRateResponse.currentPeriod.timeseries.filter(
|
||||
({ y }) => y !== null
|
||||
);
|
||||
|
||||
it('returns some data', () => {
|
||||
expect(errorRateResponse.currentPeriod.average).to.be.greaterThan(0);
|
||||
expect(errorRateResponse.previousPeriod.average).to.be.greaterThan(0);
|
||||
|
||||
expect(errorRateResponse.currentPeriod.timeseries.length).to.be.greaterThan(0);
|
||||
expect(errorRateResponse.previousPeriod.timeseries.length).to.be.greaterThan(0);
|
||||
|
||||
const currentPeriodNonNullDataPoints = errorRateResponse.currentPeriod.timeseries.filter(
|
||||
({ y }) => y !== null
|
||||
);
|
||||
|
||||
const previousPeriodNonNullDataPoints =
|
||||
errorRateResponse.previousPeriod.timeseries.filter(({ y }) => y !== null);
|
||||
|
||||
expect(currentPeriodNonNullDataPoints.length).to.be.greaterThan(0);
|
||||
expect(previousPeriodNonNullDataPoints.length).to.be.greaterThan(0);
|
||||
});
|
||||
|
||||
it('has the correct start date', () => {
|
||||
expectSnapshot(
|
||||
new Date(first(errorRateResponse.currentPeriod.timeseries)?.x ?? NaN).toISOString()
|
||||
).toMatchInline(`"2021-08-03T07:05:00.000Z"`);
|
||||
expectSnapshot(
|
||||
new Date(first(errorRateResponse.previousPeriod.timeseries)?.x ?? NaN).toISOString()
|
||||
).toMatchInline(`"2021-08-03T07:05:00.000Z"`);
|
||||
});
|
||||
|
||||
it('has the correct end date', () => {
|
||||
expectSnapshot(
|
||||
new Date(last(errorRateResponse.currentPeriod.timeseries)?.x ?? NaN).toISOString()
|
||||
).toMatchInline(`"2021-08-03T07:20:00.000Z"`);
|
||||
expectSnapshot(
|
||||
new Date(last(errorRateResponse.previousPeriod.timeseries)?.x ?? NaN).toISOString()
|
||||
).toMatchInline(`"2021-08-03T07:20:00.000Z"`);
|
||||
});
|
||||
|
||||
it('has the correct number of buckets', () => {
|
||||
expectSnapshot(errorRateResponse.currentPeriod.timeseries.length).toMatchInline(`16`);
|
||||
expectSnapshot(errorRateResponse.previousPeriod.timeseries.length).toMatchInline(`16`);
|
||||
});
|
||||
|
||||
it('has the correct calculation for average', () => {
|
||||
expectSnapshot(errorRateResponse.currentPeriod.average).toMatchInline(
|
||||
`0.0792079207920792`
|
||||
);
|
||||
expectSnapshot(errorRateResponse.previousPeriod.average).toMatchInline(
|
||||
`0.0894308943089431`
|
||||
);
|
||||
});
|
||||
|
||||
it('has the correct error rate', () => {
|
||||
expectSnapshot(errorRateResponse.currentPeriod.timeseries).toMatch();
|
||||
expectSnapshot(errorRateResponse.previousPeriod.timeseries).toMatch();
|
||||
});
|
||||
|
||||
it('matches x-axis on current period and previous period', () => {
|
||||
expect(errorRateResponse.currentPeriod.timeseries.map(({ x }) => x)).to.be.eql(
|
||||
errorRateResponse.previousPeriod.timeseries.map(({ x }) => x)
|
||||
);
|
||||
});
|
||||
expect(nonNullDataPoints).not.to.be.empty();
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
it('has the correct start date', () => {
|
||||
expect(
|
||||
new Date(first(errorRateResponse.currentPeriod.timeseries)?.x ?? NaN).toISOString()
|
||||
).to.eql('2021-01-01T00:00:00.000Z');
|
||||
});
|
||||
|
||||
it('has the correct end date', () => {
|
||||
expect(
|
||||
new Date(last(errorRateResponse.currentPeriod.timeseries)?.x ?? NaN).toISOString()
|
||||
).to.eql('2021-01-01T00:14:00.000Z');
|
||||
});
|
||||
|
||||
it('has the correct number of buckets', () => {
|
||||
expect(errorRateResponse.currentPeriod.timeseries.length).to.be.eql(15);
|
||||
});
|
||||
|
||||
it('has the correct calculation for average', () => {
|
||||
expect(errorRateResponse.currentPeriod.average).to.eql(
|
||||
config.firstTransaction.failureRate / 100
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('returns the transaction error rate with comparison data', () => {
|
||||
let errorRateResponse: ErrorRate;
|
||||
|
||||
before(async () => {
|
||||
const response = await fetchErrorCharts({
|
||||
query: {
|
||||
transactionName: config.firstTransaction.name,
|
||||
start: moment(end).subtract(7, 'minutes').toISOString(),
|
||||
offset: '7m',
|
||||
},
|
||||
});
|
||||
|
||||
errorRateResponse = response.body;
|
||||
});
|
||||
|
||||
it('returns some data', () => {
|
||||
expect(errorRateResponse.currentPeriod.average).to.be.greaterThan(0);
|
||||
expect(errorRateResponse.previousPeriod.average).to.be.greaterThan(0);
|
||||
|
||||
expect(errorRateResponse.currentPeriod.timeseries).not.to.be.empty();
|
||||
expect(errorRateResponse.previousPeriod.timeseries).not.to.be.empty();
|
||||
|
||||
const currentPeriodNonNullDataPoints = errorRateResponse.currentPeriod.timeseries.filter(
|
||||
({ y }) => y !== null
|
||||
);
|
||||
|
||||
const previousPeriodNonNullDataPoints = errorRateResponse.previousPeriod.timeseries.filter(
|
||||
({ y }) => y !== null
|
||||
);
|
||||
|
||||
expect(currentPeriodNonNullDataPoints).not.to.be.empty();
|
||||
expect(previousPeriodNonNullDataPoints).not.to.be.empty();
|
||||
});
|
||||
|
||||
it('has the correct start date', () => {
|
||||
expect(
|
||||
new Date(first(errorRateResponse.currentPeriod.timeseries)?.x ?? NaN).toISOString()
|
||||
).to.eql('2021-01-01T00:07:00.000Z');
|
||||
expect(
|
||||
new Date(first(errorRateResponse.previousPeriod.timeseries)?.x ?? NaN).toISOString()
|
||||
).to.eql('2021-01-01T00:07:00.000Z');
|
||||
});
|
||||
|
||||
it('has the correct end date', () => {
|
||||
expect(
|
||||
new Date(last(errorRateResponse.currentPeriod.timeseries)?.x ?? NaN).toISOString()
|
||||
).to.eql('2021-01-01T00:14:00.000Z');
|
||||
expect(
|
||||
new Date(last(errorRateResponse.previousPeriod.timeseries)?.x ?? NaN).toISOString()
|
||||
).to.eql('2021-01-01T00:14:00.000Z');
|
||||
});
|
||||
|
||||
it('has the correct number of buckets', () => {
|
||||
expect(errorRateResponse.currentPeriod.timeseries.length).to.eql(8);
|
||||
expect(errorRateResponse.previousPeriod.timeseries.length).to.eql(8);
|
||||
});
|
||||
|
||||
it('has the correct calculation for average', () => {
|
||||
expect(errorRateResponse.currentPeriod.average).to.eql(
|
||||
config.firstTransaction.failureRate / 100
|
||||
);
|
||||
expect(errorRateResponse.previousPeriod.average).to.eql(
|
||||
config.firstTransaction.failureRate / 100
|
||||
);
|
||||
});
|
||||
|
||||
it('matches x-axis on current period and previous period', () => {
|
||||
expect(errorRateResponse.currentPeriod.timeseries.map(({ x }) => x)).to.be.eql(
|
||||
errorRateResponse.previousPeriod.timeseries.map(({ x }) => x)
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -4,13 +4,17 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { apm, timerange } from '@kbn/apm-synthtrace';
|
||||
import expect from '@kbn/expect';
|
||||
import moment from 'moment';
|
||||
import { APIReturnType } from '@kbn/apm-plugin/public/services/rest/create_call_apm_api';
|
||||
import {
|
||||
APIClientRequestParamsOf,
|
||||
APIReturnType,
|
||||
} from '@kbn/apm-plugin/public/services/rest/create_call_apm_api';
|
||||
import { LatencyAggregationType } from '@kbn/apm-plugin/common/latency_aggregation_types';
|
||||
import { RecursivePartial } from '@kbn/apm-plugin/typings/common';
|
||||
import { meanBy } from 'lodash';
|
||||
import { FtrProviderContext } from '../../common/ftr_provider_context';
|
||||
import archives_metadata from '../../common/fixtures/es_archiver/archives_metadata';
|
||||
|
||||
type LatencyChartReturnType =
|
||||
APIReturnType<'GET /internal/apm/services/{serviceName}/transactions/charts/latency'>;
|
||||
|
@ -18,31 +22,30 @@ type LatencyChartReturnType =
|
|||
export default function ApiTest({ getService }: FtrProviderContext) {
|
||||
const registry = getService('registry');
|
||||
const apmApiClient = getService('apmApiClient');
|
||||
const synthtraceEsClient = getService('synthtraceEsClient');
|
||||
|
||||
const archiveName = 'apm_8.0.0';
|
||||
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;
|
||||
|
||||
const { start, end } = archives_metadata[archiveName];
|
||||
|
||||
async function fetchLatencyCharts({
|
||||
serviceName,
|
||||
query,
|
||||
}: {
|
||||
serviceName: string;
|
||||
query: {
|
||||
start: string;
|
||||
end: string;
|
||||
latencyAggregationType: LatencyAggregationType;
|
||||
transactionType: string;
|
||||
environment: string;
|
||||
kuery: string;
|
||||
offset?: string;
|
||||
};
|
||||
}) {
|
||||
async function fetchLatencyCharts(
|
||||
overrides?: RecursivePartial<
|
||||
APIClientRequestParamsOf<'GET /internal/apm/services/{serviceName}/transactions/charts/latency'>['params']
|
||||
>
|
||||
) {
|
||||
return await apmApiClient.readUser({
|
||||
endpoint: `GET /internal/apm/services/{serviceName}/transactions/charts/latency`,
|
||||
params: {
|
||||
path: { serviceName },
|
||||
query,
|
||||
path: { serviceName: overrides?.path?.serviceName || serviceName },
|
||||
query: {
|
||||
start: new Date(start).toISOString(),
|
||||
end: new Date(end).toISOString(),
|
||||
latencyAggregationType: LatencyAggregationType.avg,
|
||||
transactionType: 'request',
|
||||
environment: 'ENVIRONMENT_ALL',
|
||||
kuery: '',
|
||||
...overrides?.query,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
@ -52,20 +55,8 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
{ config: 'basic', archives: [] },
|
||||
() => {
|
||||
it('handles the empty state', async () => {
|
||||
const response = await fetchLatencyCharts({
|
||||
serviceName: 'opbeans-node',
|
||||
query: {
|
||||
start,
|
||||
end,
|
||||
latencyAggregationType: LatencyAggregationType.avg,
|
||||
transactionType: 'request',
|
||||
environment: 'testing',
|
||||
kuery: '',
|
||||
},
|
||||
});
|
||||
|
||||
const response = await fetchLatencyCharts();
|
||||
expect(response.status).to.be(200);
|
||||
|
||||
const latencyChartReturn = response.body as LatencyChartReturnType;
|
||||
|
||||
expect(latencyChartReturn.currentPeriod.overallAvgDuration).to.be(null);
|
||||
|
@ -77,73 +68,90 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
|
||||
registry.when(
|
||||
'Latency with a basic license when data is loaded',
|
||||
{ config: 'basic', archives: [archiveName] },
|
||||
{ config: 'basic', archives: [] },
|
||||
() => {
|
||||
const GO_PROD_RATE = 80;
|
||||
const GO_DEV_RATE = 20;
|
||||
const GO_PROD_DURATION = 1000;
|
||||
const GO_DEV_DURATION = 500;
|
||||
before(async () => {
|
||||
const serviceGoProdInstance = apm
|
||||
.service({ name: serviceName, environment: 'production', agentName: 'go' })
|
||||
.instance('instance-a');
|
||||
const serviceGoDevInstance = apm
|
||||
.service({ name: serviceName, environment: 'development', agentName: 'go' })
|
||||
.instance('instance-b');
|
||||
|
||||
await synthtraceEsClient.index([
|
||||
timerange(start, end)
|
||||
.ratePerMinute(GO_PROD_RATE)
|
||||
.generator((timestamp) =>
|
||||
serviceGoProdInstance
|
||||
.transaction({ transactionName: 'GET /api/product/list' })
|
||||
.duration(GO_PROD_DURATION)
|
||||
.timestamp(timestamp)
|
||||
),
|
||||
timerange(start, end)
|
||||
.ratePerMinute(GO_DEV_RATE)
|
||||
.generator((timestamp) =>
|
||||
serviceGoDevInstance
|
||||
.transaction({ transactionName: 'GET /api/product/:id' })
|
||||
.duration(GO_DEV_DURATION)
|
||||
.timestamp(timestamp)
|
||||
),
|
||||
]);
|
||||
});
|
||||
|
||||
after(() => synthtraceEsClient.clean());
|
||||
|
||||
const expectedLatencyAvgValueMs =
|
||||
((GO_PROD_RATE * GO_PROD_DURATION + GO_DEV_RATE * GO_DEV_DURATION) /
|
||||
(GO_PROD_RATE + GO_DEV_RATE)) *
|
||||
1000;
|
||||
|
||||
describe('average latency type', () => {
|
||||
it('returns average duration and timeseries', async () => {
|
||||
const response = await fetchLatencyCharts({
|
||||
serviceName: 'opbeans-node',
|
||||
query: {
|
||||
start,
|
||||
end,
|
||||
latencyAggregationType: LatencyAggregationType.avg,
|
||||
transactionType: 'request',
|
||||
environment: 'testing',
|
||||
kuery: '',
|
||||
},
|
||||
});
|
||||
const response = await fetchLatencyCharts();
|
||||
|
||||
expect(response.status).to.be(200);
|
||||
const latencyChartReturn = response.body as LatencyChartReturnType;
|
||||
expect(latencyChartReturn.currentPeriod.overallAvgDuration).not.to.be(null);
|
||||
expect(latencyChartReturn.currentPeriod.latencyTimeseries.length).to.be.eql(31);
|
||||
expect(latencyChartReturn.currentPeriod.overallAvgDuration).to.be(
|
||||
expectedLatencyAvgValueMs
|
||||
);
|
||||
expect(latencyChartReturn.currentPeriod.latencyTimeseries.length).to.be.eql(15);
|
||||
});
|
||||
});
|
||||
|
||||
describe('95th percentile latency type', () => {
|
||||
it('returns average duration and timeseries', async () => {
|
||||
const response = await fetchLatencyCharts({
|
||||
serviceName: 'opbeans-node',
|
||||
query: {
|
||||
start,
|
||||
end,
|
||||
latencyAggregationType: LatencyAggregationType.p95,
|
||||
transactionType: 'request',
|
||||
environment: 'testing',
|
||||
kuery: '',
|
||||
},
|
||||
query: { latencyAggregationType: LatencyAggregationType.p95 },
|
||||
});
|
||||
|
||||
expect(response.status).to.be(200);
|
||||
const latencyChartReturn = response.body as LatencyChartReturnType;
|
||||
expect(latencyChartReturn.currentPeriod.overallAvgDuration).not.to.be(null);
|
||||
expect(latencyChartReturn.currentPeriod.latencyTimeseries.length).to.be.eql(31);
|
||||
expect(latencyChartReturn.currentPeriod.overallAvgDuration).to.be(
|
||||
expectedLatencyAvgValueMs
|
||||
);
|
||||
expect(latencyChartReturn.currentPeriod.latencyTimeseries.length).to.be.eql(15);
|
||||
});
|
||||
});
|
||||
|
||||
describe('99th percentile latency type', () => {
|
||||
it('returns average duration and timeseries', async () => {
|
||||
const response = await fetchLatencyCharts({
|
||||
serviceName: 'opbeans-node',
|
||||
query: {
|
||||
start,
|
||||
end,
|
||||
latencyAggregationType: LatencyAggregationType.p99,
|
||||
transactionType: 'request',
|
||||
environment: 'testing',
|
||||
kuery: '',
|
||||
},
|
||||
});
|
||||
|
||||
expect(response.status).to.be(200);
|
||||
const latencyChartReturn = response.body as LatencyChartReturnType;
|
||||
|
||||
expect(latencyChartReturn.currentPeriod.overallAvgDuration).not.to.be(null);
|
||||
expectSnapshot(latencyChartReturn.currentPeriod.overallAvgDuration).toMatchInline(
|
||||
`53906.6603773585`
|
||||
expect(latencyChartReturn.currentPeriod.overallAvgDuration).to.be(
|
||||
expectedLatencyAvgValueMs
|
||||
);
|
||||
|
||||
expect(latencyChartReturn.currentPeriod.latencyTimeseries.length).to.be.eql(31);
|
||||
expect(latencyChartReturn.currentPeriod.latencyTimeseries.length).to.be.eql(15);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -152,14 +160,9 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
|
||||
before(async () => {
|
||||
response = await fetchLatencyCharts({
|
||||
serviceName: 'opbeans-node',
|
||||
query: {
|
||||
latencyAggregationType: LatencyAggregationType.avg,
|
||||
transactionType: 'request',
|
||||
start: moment(end).subtract(15, 'minutes').toISOString(),
|
||||
end,
|
||||
offset: '15m',
|
||||
environment: 'ENVIRONMENT_ALL',
|
||||
start: moment(end).subtract(7, 'minutes').toISOString(),
|
||||
offset: '7m',
|
||||
kuery: '',
|
||||
},
|
||||
});
|
||||
|
@ -175,8 +178,8 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
latencyChartReturn.previousPeriod.latencyTimeseries.filter(({ y }) => y !== null);
|
||||
expect(previousPeriodNonNullDataPoints.length).to.be.greaterThan(0);
|
||||
|
||||
expectSnapshot(currentPeriodNonNullDataPoints).toMatch();
|
||||
expectSnapshot(previousPeriodNonNullDataPoints).toMatch();
|
||||
expect(meanBy(currentPeriodNonNullDataPoints, 'y')).to.eql(expectedLatencyAvgValueMs);
|
||||
expect(meanBy(previousPeriodNonNullDataPoints, 'y')).to.eql(expectedLatencyAvgValueMs);
|
||||
});
|
||||
|
||||
it('matches x-axis on current period and previous period', () => {
|
||||
|
@ -192,14 +195,8 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
|
||||
before(async () => {
|
||||
response = await fetchLatencyCharts({
|
||||
serviceName: 'opbeans-node',
|
||||
query: {
|
||||
start,
|
||||
end,
|
||||
latencyAggregationType: LatencyAggregationType.avg,
|
||||
transactionType: 'request',
|
||||
environment: 'does-not-exist',
|
||||
kuery: '',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
@ -216,74 +213,24 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
expect(currentPeriodNonNullDataPoints).to.be.empty();
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
registry.when(
|
||||
'Transaction latency with a trial license when data is loaded',
|
||||
{ config: 'trial', archives: [archiveName] },
|
||||
() => {
|
||||
let response: Awaited<ReturnType<typeof fetchLatencyCharts>>;
|
||||
describe('with production environment', () => {
|
||||
let response: Awaited<ReturnType<typeof fetchLatencyCharts>>;
|
||||
|
||||
const transactionType = 'request';
|
||||
|
||||
describe('without an environment', () => {
|
||||
before(async () => {
|
||||
response = await fetchLatencyCharts({
|
||||
serviceName: 'opbeans-node',
|
||||
query: {
|
||||
start,
|
||||
end,
|
||||
latencyAggregationType: LatencyAggregationType.avg,
|
||||
transactionType,
|
||||
environment: 'ENVIRONMENT_ALL',
|
||||
kuery: '',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an ok response', () => {
|
||||
expect(response.status).to.eql(200);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with environment selected', () => {
|
||||
before(async () => {
|
||||
response = await fetchLatencyCharts({
|
||||
serviceName: 'opbeans-node',
|
||||
query: {
|
||||
start,
|
||||
end,
|
||||
latencyAggregationType: LatencyAggregationType.avg,
|
||||
transactionType,
|
||||
environment: 'production',
|
||||
kuery: '',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should have a successful response', () => {
|
||||
expect(response.status).to.eql(200);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with all environments selected', () => {
|
||||
before(async () => {
|
||||
response = await fetchLatencyCharts({
|
||||
serviceName: 'opbeans-node',
|
||||
query: {
|
||||
start,
|
||||
end,
|
||||
latencyAggregationType: LatencyAggregationType.avg,
|
||||
transactionType,
|
||||
environment: 'ENVIRONMENT_ALL',
|
||||
kuery: '',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should have a successful response', () => {
|
||||
expect(response.status).to.eql(200);
|
||||
it('returns average duration and timeseries', async () => {
|
||||
const latencyChartReturn = response.body as LatencyChartReturnType;
|
||||
expect(latencyChartReturn.currentPeriod.overallAvgDuration).to.be(
|
||||
GO_PROD_DURATION * 1000
|
||||
);
|
||||
expect(latencyChartReturn.currentPeriod.latencyTimeseries.length).to.be.eql(15);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue