Skip to content

Commit

Permalink
Add API tests for top dependencies (#116788) (#116988)
Browse files Browse the repository at this point in the history
* Add top dependencies API tests

Co-authored-by: Søren Louv-Jansen <sorenlouv@gmail.com>

Co-authored-by: Giorgos Bamparopoulos <georgios.bamparopoulos@elastic.co>
Co-authored-by: Søren Louv-Jansen <sorenlouv@gmail.com>
  • Loading branch information
3 people authored Nov 1, 2021
1 parent 06411b8 commit cf7634d
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 17 deletions.
29 changes: 18 additions & 11 deletions x-pack/test/apm_api_integration/tests/dependencies/generate_data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,40 +8,47 @@ import { service, timerange } from '@elastic/apm-synthtrace';
import type { SynthtraceEsClient } from '../../common/synthtrace_es_client';

export const dataConfig = {
spanType: 'db',
rate: 20,
transaction: {
name: 'GET /api/product/list',
duration: 1000,
},
span: {
name: 'GET apm-*/_search',
type: 'db',
subType: 'elasticsearch',
destination: 'elasticsearch',
},
};

export async function generateData({
synthtraceEsClient,
backendName,
start,
end,
}: {
synthtraceEsClient: SynthtraceEsClient;
backendName: string;
start: number;
end: number;
}) {
const instance = service('synth-go', 'production', 'go').instance('instance-a');
const transactionName = 'GET /api/product/list';
const spanName = 'GET apm-*/_search';
const { rate, transaction, span } = dataConfig;

await synthtraceEsClient.index(
timerange(start, end)
.interval('1m')
.rate(10)
.rate(rate)
.flatMap((timestamp) =>
instance
.transaction(transactionName)
.transaction(transaction.name)
.timestamp(timestamp)
.duration(1000)
.duration(transaction.duration)
.success()
.children(
instance
.span(spanName, dataConfig.spanType, backendName)
.duration(1000)
.span(span.name, span.type, span.subType)
.duration(transaction.duration)
.success()
.destination(backendName)
.destination(span.destination)
.timestamp(timestamp)
)
.serialize()
Expand Down
12 changes: 6 additions & 6 deletions x-pack/test/apm_api_integration/tests/dependencies/metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,13 @@ export default function ApiTest({ getService }: FtrProviderContext) {

const start = new Date('2021-01-01T00:00:00.000Z').getTime();
const end = new Date('2021-01-01T00:15:00.000Z').getTime() - 1;
const backendName = 'elasticsearch';

async function callApi() {
return await apmApiClient.readUser({
endpoint: `GET /internal/apm/backends/metadata`,
params: {
query: {
backendName,
backendName: dataConfig.span.destination,
start: new Date(start).toISOString(),
end: new Date(end).toISOString(),
},
Expand All @@ -44,16 +43,17 @@ export default function ApiTest({ getService }: FtrProviderContext) {
);

registry.when(
'Dependency metadata when data is loaded',
'Dependency metadata when data is generated',
{ config: 'basic', archives: ['apm_mappings_only_8.0.0'] },
() => {
it('returns correct metadata for the dependency', async () => {
await generateData({ synthtraceEsClient, backendName, start, end });
await generateData({ synthtraceEsClient, start, end });
const { status, body } = await callApi();
const { span } = dataConfig;

expect(status).to.be(200);
expect(body.metadata.spanType).to.equal(dataConfig.spanType);
expect(body.metadata.spanSubtype).to.equal(backendName);
expect(body.metadata.spanType).to.equal(span.type);
expect(body.metadata.spanSubtype).to.equal(span.subType);
});
}
);
Expand Down
142 changes: 142 additions & 0 deletions x-pack/test/apm_api_integration/tests/dependencies/top_dependencies.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/*
* 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 { APIReturnType } from '../../../../plugins/apm/public/services/rest/createCallApmApi';
import { FtrProviderContext } from '../../common/ftr_provider_context';
import { registry } from '../../common/registry';
import { dataConfig, generateData } from './generate_data';
import { NodeType, BackendNode } from '../../../../plugins/apm/common/connections';
import { roundNumber } from '../../utils';

type TopDependencies = APIReturnType<'GET /internal/apm/backends/top_backends'>;

export default function ApiTest({ getService }: FtrProviderContext) {
const apmApiClient = getService('apmApiClient');
const synthtraceEsClient = getService('synthtraceEsClient');

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() {
return await apmApiClient.readUser({
endpoint: 'GET /internal/apm/backends/top_backends',
params: {
query: {
start: new Date(start).toISOString(),
end: new Date(end).toISOString(),
environment: 'ENVIRONMENT_ALL',
kuery: '',
numBuckets: 20,
offset: '',
},
},
});
}

registry.when(
'Top dependencies when data is not loaded',
{ config: 'basic', archives: [] },
() => {
it('handles empty state', async () => {
const { status, body } = await callApi();
expect(status).to.be(200);
expect(body.backends).to.empty();
});
}
);

registry.when(
'Top dependencies',
{ config: 'basic', archives: ['apm_mappings_only_8.0.0'] },
() => {
describe('when data is generated', () => {
let topDependencies: TopDependencies;

before(async () => {
await generateData({ synthtraceEsClient, start, end });
const response = await callApi();
topDependencies = response.body;
});

after(() => synthtraceEsClient.clean());

it('returns an array of dependencies', () => {
expect(topDependencies).to.have.property('backends');
expect(topDependencies.backends).to.have.length(1);
});

it('returns correct dependency information', () => {
const location = topDependencies.backends[0].location as BackendNode;
const { span } = dataConfig;

expect(location.type).to.be(NodeType.backend);
expect(location.backendName).to.be(span.destination);
expect(location.spanType).to.be(span.type);
expect(location.spanSubtype).to.be(span.subType);
expect(location).to.have.property('id');
});

describe('returns the correct stats', () => {
let backends: TopDependencies['backends'][number];

before(() => {
backends = topDependencies.backends[0];
});

it("doesn't have previous stats", () => {
expect(backends.previousStats).to.be(null);
});

it('has an "impact" property', () => {
expect(backends.currentStats).to.have.property('impact');
});

it('returns the correct latency', () => {
const {
currentStats: { latency },
} = backends;

const { transaction } = dataConfig;

expect(latency.value).to.be(transaction.duration * 1000);
expect(latency.timeseries.every(({ y }) => y === transaction.duration * 1000)).to.be(
true
);
});

it('returns the correct throughput', () => {
const {
currentStats: { throughput },
} = backends;
const { rate } = dataConfig;

expect(roundNumber(throughput.value)).to.be(roundNumber(rate));
});

it('returns the correct total time', () => {
const {
currentStats: { totalTime },
} = backends;
const { rate, transaction } = dataConfig;

expect(
totalTime.timeseries.every(({ y }) => y === rate * transaction.duration * 1000)
).to.be(true);
});

it('returns the correct error rate', () => {
const {
currentStats: { errorRate },
} = backends;
expect(errorRate.value).to.be(0);
expect(errorRate.timeseries.every(({ y }) => y === 0)).to.be(true);
});
});
});
}
);
}
4 changes: 4 additions & 0 deletions x-pack/test/apm_api_integration/tests/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,10 @@ export default function apmApiIntegrationTests(providerContext: FtrProviderConte
loadTestFile(require.resolve('./dependencies/metadata'));
});

describe('dependencies/top_dependencies', function () {
loadTestFile(require.resolve('./dependencies/top_dependencies'));
});

registry.run(providerContext);
});
}

0 comments on commit cf7634d

Please sign in to comment.