Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[APM] Migrate /mobile API tests to deployment agnostic folder #199021

Merged
merged 23 commits into from
Nov 11, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
eee7cd3
Add foundation to run apm tests
crespocarlos Nov 4, 2024
5857da2
Force Git to recognize latest_agent_versions.spec.ts as moved
crespocarlos Nov 4, 2024
c45c662
Updated latest_agent_versions.spec.ts after moving
crespocarlos Nov 4, 2024
66f5e4c
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine Nov 4, 2024
17fb5e2
Simplify apmApi
crespocarlos Nov 4, 2024
4d684c6
Symplify createApmSynthtraceEsClient
crespocarlos Nov 4, 2024
06e7f74
Merge branch 'main' into 193245-apm-deployment-agnostic-tests-part-1
elasticmachine Nov 5, 2024
4870a81
[APM] Migrate `/mobile` API tests to deployment agnostic folder
kpatticha Nov 5, 2024
4671875
Merge branch 'main' of github.com:elastic/kibana into migrate-mobile-…
kpatticha Nov 6, 2024
9a19a9b
Remove registry service
kpatticha Nov 6, 2024
a1db7f1
fix
kpatticha Nov 6, 2024
1d64be8
Remove registry service
kpatticha Nov 6, 2024
0906fa1
remove service once and for all
kpatticha Nov 6, 2024
14105cb
Unskip mobile tests
kpatticha Nov 8, 2024
c9bccbf
Fix synthrace dropped document due to wrong type
kpatticha Nov 8, 2024
0f511a1
Unskip tests
kpatticha Nov 8, 2024
6b4ddc0
Merge branch 'main' of github.com:elastic/kibana into migrate-mobile-…
kpatticha Nov 8, 2024
995aaa9
Can't pass grouping_name
kpatticha Nov 8, 2024
2633eab
Merge branch 'main' into migrate-mobile-api-tests
kpatticha Nov 8, 2024
06403c2
Clean up code
kpatticha Nov 8, 2024
3878972
Merge branch 'migrate-mobile-api-tests' of github.com:kpatticha/kiban…
kpatticha Nov 8, 2024
76d5c9f
Merge branch 'main' into migrate-mobile-api-tests
kpatticha Nov 11, 2024
59fd80c
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Nov 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,7 @@ export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderCon
it('returns a version when agent is listed in the file', async () => {
const { status, body } = await callApi();
expect(status).to.be(200);

const agents = body.data;

const nodeAgent = agents[nodeAgentName] as ElasticApmAgentLatestVersion;
expect(nodeAgent?.latest_version).not.to.be(undefined);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ export default function apmApiIntegrationTests({
}: DeploymentAgnosticFtrProviderContext) {
describe('APM', function () {
loadTestFile(require.resolve('./agent_explorer'));
loadTestFile(require.resolve('./mobile'));
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/*
* 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 { apm, timerange } from '@kbn/apm-synthtrace-client';
import {
APIClientRequestParamsOf,
APIReturnType,
} from '@kbn/apm-plugin/public/services/rest/create_call_apm_api';
import { RecursivePartial } from '@kbn/apm-plugin/typings/common';
import type { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace';
import type { DeploymentAgnosticFtrProviderContext } from '../../../../../ftr_provider_context';

type ErrorGroups =
APIReturnType<'GET /internal/apm/mobile-services/{serviceName}/crashes/groups/main_statistics'>['errorGroups'];

export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderContext) {
const apmApiClient = getService('apmApi');
const synthtrace = getService('synthtrace');

const serviceName = 'synth-swift';
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/mobile-services/{serviceName}/crashes/groups/main_statistics'>['params']
>
) {
return await apmApiClient.readUser({
endpoint: 'GET /internal/apm/mobile-services/{serviceName}/crashes/groups/main_statistics',
params: {
path: { serviceName, ...overrides?.path },
query: {
start: new Date(start).toISOString(),
end: new Date(end).toISOString(),
environment: 'ENVIRONMENT_ALL',
kuery: '',
...overrides?.query,
},
},
});
}

describe('Crash group list', () => {
it('handles empty state', async () => {
const response = await callApi();
expect(response.status).to.be(200);
expect(response.body.errorGroups).to.empty();
});

// FLAKY: https://github.com/elastic/kibana/issues/177651
describe.skip('when data is loaded', () => {
describe('errors group', () => {
let apmSynthtraceEsClient: ApmSynthtraceEsClient;

const appleTransaction = {
name: 'GET /apple 🍎 ',
successRate: 75,
failureRate: 25,
};

const bananaTransaction = {
name: 'GET /banana 🍌',
successRate: 50,
failureRate: 50,
};

before(async () => {
const serviceInstance = apm
.service({ name: serviceName, environment: 'production', agentName: 'swift' })
.instance('instance-a');

apmSynthtraceEsClient = await synthtrace.createApmSynthtraceEsClient();

await apmSynthtraceEsClient.index([
timerange(start, end)
.interval('1m')
.rate(appleTransaction.successRate)
.generator((timestamp) =>
serviceInstance
.transaction({ transactionName: appleTransaction.name })
.timestamp(timestamp)
.duration(1000)
.success()
),
timerange(start, end)
.interval('1m')
.rate(appleTransaction.failureRate)
.generator((timestamp) =>
serviceInstance
.transaction({ transactionName: appleTransaction.name })
.errors(
serviceInstance
.crash({
message: 'crash 1',
})
.timestamp(timestamp)
)
.duration(1000)
.timestamp(timestamp)
.failure()
),
timerange(start, end)
.interval('1m')
.rate(bananaTransaction.successRate)
.generator((timestamp) =>
serviceInstance
.transaction({ transactionName: bananaTransaction.name })
.timestamp(timestamp)
.duration(1000)
.success()
),
timerange(start, end)
.interval('1m')
.rate(bananaTransaction.failureRate)
.generator((timestamp) =>
serviceInstance
.transaction({ transactionName: bananaTransaction.name })
.errors(
serviceInstance
.crash({
message: 'crash 2',
})
.timestamp(timestamp)
)
.duration(1000)
.timestamp(timestamp)
.failure()
),
]);
});

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

describe('returns the correct data', () => {
let errorGroups: ErrorGroups;
before(async () => {
const response = await callApi();
errorGroups = response.body.errorGroups;
});
it('returns correct number of crashes', () => {
expect(errorGroups.length).to.equal(2);
expect(errorGroups.map((error) => error.name).sort()).to.eql(['crash 1', 'crash 2']);
});

it('returns correct occurrences', () => {
const numberOfBuckets = 15;
expect(errorGroups.map((error) => error.occurrences).sort()).to.eql([
appleTransaction.failureRate * numberOfBuckets,
bananaTransaction.failureRate * numberOfBuckets,
]);
});
});
});
});
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
/*
* 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 { isFiniteNumber } from '@kbn/apm-plugin/common/utils/is_finite_number';
import {
APIClientRequestParamsOf,
APIReturnType,
} from '@kbn/apm-plugin/public/services/rest/create_call_apm_api';
import type { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace';

import { RecursivePartial } from '@kbn/apm-plugin/typings/common';
import type { DeploymentAgnosticFtrProviderContext } from '../../../../../ftr_provider_context';
import { config, generateData } from './generate_data';

type ErrorsDistribution =
APIReturnType<'GET /internal/apm/mobile-services/{serviceName}/crashes/distribution'>;

export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderContext) {
const apmApiClient = getService('apmApi');
const synthtrace = getService('synthtrace');

const serviceName = 'synth-swift';
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/mobile-services/{serviceName}/crashes/distribution'>['params']
>
) {
const response = await apmApiClient.readUser({
endpoint: 'GET /internal/apm/mobile-services/{serviceName}/crashes/distribution',
params: {
path: {
serviceName,
...overrides?.path,
},
query: {
start: new Date(start).toISOString(),
end: new Date(end).toISOString(),
environment: 'ENVIRONMENT_ALL',
kuery: '',
...overrides?.query,
},
},
});
return response;
}

describe('Distribution', () => {
it('handles the empty state', async () => {
const response = await callApi();
expect(response.status).to.be(200);
expect(response.body.currentPeriod.length).to.be(0);
expect(response.body.previousPeriod.length).to.be(0);
});

// FLAKY: https://github.com/elastic/kibana/issues/177652
describe.skip('when data is loaded', () => {
describe('errors distribution', () => {
let apmSynthtraceEsClient: ApmSynthtraceEsClient;

const { appleTransaction, bananaTransaction } = config;
before(async () => {
apmSynthtraceEsClient = await synthtrace.createApmSynthtraceEsClient();
await generateData({ serviceName, start, end, apmSynthtraceEsClient });
});

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

describe('without comparison', () => {
let errorsDistribution: ErrorsDistribution;
before(async () => {
const response = await callApi();
errorsDistribution = response.body;
});

it('displays combined number of occurrences', () => {
const countSum = sumBy(errorsDistribution.currentPeriod, 'y');
const numberOfBuckets = 15;
expect(countSum).to.equal(
(appleTransaction.failureRate + bananaTransaction.failureRate) * numberOfBuckets
);
});

describe('displays correct start in errors distribution chart', () => {
let errorsDistributionWithComparison: ErrorsDistribution;
before(async () => {
const responseWithComparison = await callApi({
query: {
start: new Date(start).toISOString(),
end: new Date(end).toISOString(),
offset: '15m',
},
});
errorsDistributionWithComparison = responseWithComparison.body;
});
it('has same start time when comparison is enabled', () => {
expect(first(errorsDistribution.currentPeriod)?.x).to.equal(
first(errorsDistributionWithComparison.currentPeriod)?.x
);
});
});
});

describe('displays occurrences for type "apple transaction" only', () => {
let errorsDistribution: ErrorsDistribution;
before(async () => {
const response = await callApi({
query: { kuery: `error.exception.type:"${appleTransaction.name}"` },
});
errorsDistribution = response.body;
});
it('displays combined number of occurrences', () => {
const countSum = sumBy(errorsDistribution.currentPeriod, 'y');
const numberOfBuckets = 15;
expect(countSum).to.equal(appleTransaction.failureRate * numberOfBuckets);
});
});

describe('with comparison', () => {
describe('when data is returned', () => {
let errorsDistribution: ErrorsDistribution;
before(async () => {
const fiveMinutes = 5 * 60 * 1000;
const response = await callApi({
query: {
start: new Date(end - fiveMinutes).toISOString(),
end: new Date(end).toISOString(),
offset: '5m',
},
});
errorsDistribution = response.body;
});
it('returns some data', () => {
const hasCurrentPeriodData = errorsDistribution.currentPeriod.some(({ y }) =>
isFiniteNumber(y)
);

const hasPreviousPeriodData = errorsDistribution.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(errorsDistribution.currentPeriod)?.x).to.equal(
first(errorsDistribution.previousPeriod)?.x
);
});

it('has same end time for both periods', () => {
expect(last(errorsDistribution.currentPeriod)?.x).to.equal(
last(errorsDistribution.previousPeriod)?.x
);
});

it('returns same number of buckets for both periods', () => {
expect(errorsDistribution.currentPeriod.length).to.equal(
errorsDistribution.previousPeriod.length
);
});
});

describe('when no data is returned', () => {
let errorsDistribution: ErrorsDistribution;
before(async () => {
const response = await callApi({
query: {
start: '2021-01-03T00:00:00.000Z',
end: '2021-01-03T00:15:00.000Z',
offset: '1d',
},
});
errorsDistribution = response.body;
});

it('has same start time for both periods', () => {
expect(first(errorsDistribution.currentPeriod)?.x).to.equal(
first(errorsDistribution.previousPeriod)?.x
);
});

it('has same end time for both periods', () => {
expect(last(errorsDistribution.currentPeriod)?.x).to.equal(
last(errorsDistribution.previousPeriod)?.x
);
});

it('returns same number of buckets for both periods', () => {
expect(errorsDistribution.currentPeriod.length).to.equal(
errorsDistribution.previousPeriod.length
);
});
});
});
});
});
});
}
Loading