forked from elastic/kibana
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[ObsUX][APM] Migration of Service Overview tests to deployment agnost…
…ic approach (elastic#200226) ## Summary Part of elastic#193245 Closes elastic#198986 This PR moves all compatible/supported test cases for Service Overview. Unsupported cases are kept in the old test section to run on stateful for now. ## How to Test ### Serverless ``` node scripts/functional_tests_server --config x-pack/test/api_integration/deployment_agnostic/configs/serverless/oblt.serverless.config.ts node scripts/functional_test_runner --config x-pack/test/api_integration/deployment_agnostic/configs/serverless/oblt.serverless.config.ts --grep="APM API tests" ``` It's recommended to be run against [MKI](https://github.com/crespocarlos/kibana/blob/main/x-pack/test_serverless/README.md#run-tests-on-mki) ### Stateful ``` node scripts/functional_tests_server --config x-pack/test/api_integration/deployment_agnostic/configs/stateful/oblt.stateful.config.ts node scripts/functional_test_runner --config x-pack/test/api_integration/deployment_agnostic/configs/stateful/oblt.stateful.config.ts --grep="APM API tests" ``` --------- Co-authored-by: kibanamachine <[email protected]> Co-authored-by: Elastic Machine <[email protected]> (cherry picked from commit 185fa2a)
- Loading branch information
1 parent
4bbe24c
commit 96bba7c
Showing
14 changed files
with
1,306 additions
and
1,200 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
304 changes: 304 additions & 0 deletions
304
...on/deployment_agnostic/apis/observability/apm/service_overview/dependencies/index.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,304 @@ | ||
/* | ||
* 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 { last, pick } from 'lodash'; | ||
import type { ValuesType } from 'utility-types'; | ||
import { type Node, NodeType } from '@kbn/apm-plugin/common/connections'; | ||
import { | ||
ENVIRONMENT_ALL, | ||
ENVIRONMENT_NOT_DEFINED, | ||
} from '@kbn/apm-plugin/common/environment_filter_values'; | ||
import type { APIReturnType } from '@kbn/apm-plugin/public/services/rest/create_call_apm_api'; | ||
import { roundNumber } from '../../utils/common'; | ||
import type { DeploymentAgnosticFtrProviderContext } from '../../../../../ftr_provider_context'; | ||
import { apmDependenciesMapping, createServiceDependencyDocs } from './es_utils'; | ||
|
||
export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderContext) { | ||
const apmApiClient = getService('apmApi'); | ||
const es = getService('es'); | ||
|
||
const { start, end } = { | ||
start: '2021-08-03T06:50:15.910Z', | ||
end: '2021-08-03T07:20:15.910Z', | ||
}; | ||
|
||
function getName(node: Node) { | ||
return node.type === NodeType.service ? node.serviceName : node.dependencyName; | ||
} | ||
|
||
describe('Service Overview', () => { | ||
describe('Dependencies', () => { | ||
describe('when data is not loaded', () => { | ||
it('handles the empty state', async () => { | ||
const response = await apmApiClient.readUser({ | ||
endpoint: `GET /internal/apm/services/{serviceName}/dependencies`, | ||
params: { | ||
path: { serviceName: 'opbeans-java' }, | ||
query: { | ||
start, | ||
end, | ||
numBuckets: 20, | ||
environment: ENVIRONMENT_ALL.value, | ||
}, | ||
}, | ||
}); | ||
|
||
expect(response.status).to.be(200); | ||
expect(response.body.serviceDependencies).to.eql([]); | ||
}); | ||
}); | ||
|
||
describe('when specific data is loaded', () => { | ||
let response: { | ||
status: number; | ||
body: APIReturnType<'GET /internal/apm/services/{serviceName}/dependencies'>; | ||
}; | ||
|
||
const indices = { | ||
metric: 'apm-dependencies-metric', | ||
transaction: 'apm-dependencies-transaction', | ||
span: 'apm-dependencies-span', | ||
}; | ||
|
||
const startTime = new Date(start).getTime(); | ||
const endTime = new Date(end).getTime(); | ||
|
||
after(async () => { | ||
const allIndices = Object.values(indices).join(','); | ||
const indexExists = await es.indices.exists({ index: allIndices }); | ||
if (indexExists) { | ||
await es.indices.delete({ | ||
index: allIndices, | ||
}); | ||
} | ||
}); | ||
|
||
before(async () => { | ||
await es.indices.create({ | ||
index: indices.metric, | ||
body: { | ||
mappings: apmDependenciesMapping, | ||
}, | ||
}); | ||
|
||
await es.indices.create({ | ||
index: indices.transaction, | ||
body: { | ||
mappings: apmDependenciesMapping, | ||
}, | ||
}); | ||
|
||
await es.indices.create({ | ||
index: indices.span, | ||
body: { | ||
mappings: apmDependenciesMapping, | ||
}, | ||
}); | ||
|
||
const docs = [ | ||
...createServiceDependencyDocs({ | ||
service: { | ||
name: 'opbeans-java', | ||
environment: 'production', | ||
}, | ||
agentName: 'java', | ||
span: { | ||
type: 'external', | ||
subtype: 'http', | ||
}, | ||
resource: 'opbeans-node:3000', | ||
outcome: 'success', | ||
responseTime: { | ||
count: 2, | ||
sum: 10, | ||
}, | ||
time: startTime, | ||
to: { | ||
service: { | ||
name: 'opbeans-node', | ||
}, | ||
agentName: 'nodejs', | ||
}, | ||
}), | ||
...createServiceDependencyDocs({ | ||
service: { | ||
name: 'opbeans-java', | ||
environment: 'production', | ||
}, | ||
agentName: 'java', | ||
span: { | ||
type: 'external', | ||
subtype: 'http', | ||
}, | ||
resource: 'opbeans-node:3000', | ||
outcome: 'failure', | ||
responseTime: { | ||
count: 1, | ||
sum: 10, | ||
}, | ||
time: startTime, | ||
}), | ||
...createServiceDependencyDocs({ | ||
service: { | ||
name: 'opbeans-java', | ||
environment: 'production', | ||
}, | ||
agentName: 'java', | ||
span: { | ||
type: 'external', | ||
subtype: 'http', | ||
}, | ||
resource: 'postgres', | ||
outcome: 'success', | ||
responseTime: { | ||
count: 1, | ||
sum: 3, | ||
}, | ||
time: startTime, | ||
}), | ||
...createServiceDependencyDocs({ | ||
service: { | ||
name: 'opbeans-java', | ||
environment: 'production', | ||
}, | ||
agentName: 'java', | ||
span: { | ||
type: 'external', | ||
subtype: 'http', | ||
}, | ||
resource: 'opbeans-node-via-proxy', | ||
outcome: 'success', | ||
responseTime: { | ||
count: 1, | ||
sum: 1, | ||
}, | ||
time: endTime - 1, | ||
to: { | ||
service: { | ||
name: 'opbeans-node', | ||
}, | ||
agentName: 'nodejs', | ||
}, | ||
}), | ||
]; | ||
|
||
const bulkActions = docs.reduce( | ||
(prev, doc) => { | ||
return [...prev, { index: { _index: indices[doc.processor.event] } }, doc]; | ||
}, | ||
[] as Array< | ||
| { | ||
index: { | ||
_index: string; | ||
}; | ||
} | ||
| ValuesType<typeof docs> | ||
> | ||
); | ||
|
||
await es.bulk({ | ||
body: bulkActions, | ||
refresh: 'wait_for', | ||
}); | ||
|
||
response = await apmApiClient.readUser({ | ||
endpoint: `GET /internal/apm/services/{serviceName}/dependencies`, | ||
params: { | ||
path: { serviceName: 'opbeans-java' }, | ||
query: { | ||
start, | ||
end, | ||
numBuckets: 20, | ||
environment: ENVIRONMENT_ALL.value, | ||
}, | ||
}, | ||
}); | ||
}); | ||
|
||
it('returns a 200', () => { | ||
expect(response.status).to.be(200); | ||
}); | ||
|
||
it('returns two dependencies', () => { | ||
expect(response.body.serviceDependencies.length).to.be(2); | ||
}); | ||
|
||
it('returns opbeans-node as a dependency', () => { | ||
const opbeansNode = response.body.serviceDependencies.find( | ||
(item) => getName(item.location) === 'opbeans-node' | ||
); | ||
|
||
expect(opbeansNode !== undefined).to.be(true); | ||
|
||
const values = { | ||
latency: roundNumber(opbeansNode?.currentStats.latency.value), | ||
throughput: roundNumber(opbeansNode?.currentStats.throughput.value), | ||
errorRate: roundNumber(opbeansNode?.currentStats.errorRate.value), | ||
impact: opbeansNode?.currentStats.impact, | ||
...pick(opbeansNode?.location, 'serviceName', 'type', 'agentName', 'environment'), | ||
}; | ||
|
||
const count = 4; | ||
const sum = 21; | ||
const errors = 1; | ||
|
||
expect(values).to.eql({ | ||
agentName: 'nodejs', | ||
environment: ENVIRONMENT_NOT_DEFINED.value, | ||
serviceName: 'opbeans-node', | ||
type: 'service', | ||
errorRate: roundNumber(errors / count), | ||
latency: roundNumber(sum / count), | ||
throughput: roundNumber(count / ((endTime - startTime) / 1000 / 60)), | ||
impact: 100, | ||
}); | ||
|
||
const firstValue = roundNumber(opbeansNode?.currentStats.latency.timeseries[0].y); | ||
const lastValue = roundNumber(last(opbeansNode?.currentStats.latency.timeseries)?.y); | ||
|
||
expect(firstValue).to.be(roundNumber(20 / 3)); | ||
expect(lastValue).to.be(1); | ||
}); | ||
|
||
it('returns postgres as an external dependency', () => { | ||
const postgres = response.body.serviceDependencies.find( | ||
(item) => getName(item.location) === 'postgres' | ||
); | ||
|
||
expect(postgres !== undefined).to.be(true); | ||
|
||
const values = { | ||
latency: roundNumber(postgres?.currentStats.latency.value), | ||
throughput: roundNumber(postgres?.currentStats.throughput.value), | ||
errorRate: roundNumber(postgres?.currentStats.errorRate.value), | ||
impact: postgres?.currentStats.impact, | ||
...pick(postgres?.location, 'spanType', 'spanSubtype', 'dependencyName', 'type'), | ||
}; | ||
|
||
const count = 1; | ||
const sum = 3; | ||
const errors = 0; | ||
|
||
expect(values).to.eql({ | ||
spanType: 'external', | ||
spanSubtype: 'http', | ||
dependencyName: 'postgres', | ||
type: 'dependency', | ||
errorRate: roundNumber(errors / count), | ||
latency: roundNumber(sum / count), | ||
throughput: roundNumber(count / ((endTime - startTime) / 1000 / 60)), | ||
impact: 0, | ||
}); | ||
}); | ||
}); | ||
|
||
// UNSUPPORTED TEST CASES - when data is loaded | ||
// TODO: These tests should be migrated to use synthtrace: https://github.com/elastic/kibana/issues/200743 | ||
}); | ||
}); | ||
} |
42 changes: 42 additions & 0 deletions
42
...ation/deployment_agnostic/apis/observability/apm/service_overview/get_service_node_ids.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
/* | ||
* 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 { take } from 'lodash'; | ||
import { LatencyAggregationType } from '@kbn/apm-plugin/common/latency_aggregation_types'; | ||
import type { ApmApiClient } from '../custom_dashboards/api_helper'; | ||
|
||
export async function getServiceNodeIds({ | ||
apmApiClient, | ||
start, | ||
end, | ||
serviceName = 'opbeans-java', | ||
count = 1, | ||
}: { | ||
apmApiClient: Awaited<ApmApiClient>; | ||
start: string; | ||
end: string; | ||
serviceName?: string; | ||
count?: number; | ||
}) { | ||
const { body } = await apmApiClient.readUser({ | ||
endpoint: `GET /internal/apm/services/{serviceName}/service_overview_instances/main_statistics`, | ||
params: { | ||
path: { serviceName }, | ||
query: { | ||
latencyAggregationType: LatencyAggregationType.avg, | ||
start, | ||
end, | ||
transactionType: 'request', | ||
environment: 'ENVIRONMENT_ALL', | ||
kuery: '', | ||
sortField: 'throughput', | ||
sortDirection: 'desc', | ||
}, | ||
}, | ||
}); | ||
|
||
return take(body.currentPeriod.map((item) => item.serviceNodeName).sort(), count); | ||
} |
17 changes: 17 additions & 0 deletions
17
...test/api_integration/deployment_agnostic/apis/observability/apm/service_overview/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
/* | ||
* 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 type { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; | ||
|
||
export default function ({ loadTestFile }: DeploymentAgnosticFtrProviderContext) { | ||
describe('service_overview', () => { | ||
loadTestFile(require.resolve('./instance_details.spec.ts')); | ||
loadTestFile(require.resolve('./instances_detailed_statistics.spec.ts')); | ||
loadTestFile(require.resolve('./instances_main_statistics.spec.ts')); | ||
loadTestFile(require.resolve('./dependencies/index.spec.ts')); | ||
}); | ||
} |
Oops, something went wrong.