[APM] Migrate api tests to use apm api client (#142229)

* Removed usage of legacySuperTestAsNoAccessUser

* Removed usage of legacySupertestAsApmReadUser and legacySupertestAsApmAnnotationsWriteUser

* Removed legacyprefixed services from config

* Addressing PR comments

* Addressing PR comments

* [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix'

* [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix'

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Yngrid Coello 2022-10-03 17:01:58 +02:00 committed by GitHub
parent 4b37ede0cd
commit 8f8343b7c2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 178 additions and 152 deletions

View file

@ -126,22 +126,10 @@ export function createTestConfig(config: ApmFtrConfig) {
},
ml: MachineLearningAPIProvider,
// legacy clients
legacySupertestAsNoAccessUser: getLegacySupertestClient(
kibanaServer,
ApmUsername.noAccessUser
),
legacySupertestAsApmReadUser: getLegacySupertestClient(
kibanaServer,
ApmUsername.viewerUser
),
legacySupertestAsApmWriteUser: getLegacySupertestClient(
kibanaServer,
ApmUsername.editorUser
),
legacySupertestAsApmAnnotationsWriteUser: getLegacySupertestClient(
kibanaServer,
ApmUsername.apmAnnotationsWriteUser
),
legacySupertestAsApmReadUserWithoutMlAccess: getLegacySupertestClient(
kibanaServer,
ApmUsername.apmReadUserWithoutMlAccess

View file

@ -0,0 +1,15 @@
/*
* 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.
*/
export async function expectToReject<T extends Error>(fn: () => Promise<any>): Promise<T> {
try {
await fn();
} catch (e) {
return e;
}
throw new Error(`Expected fn to throw`);
}

View file

@ -5,20 +5,33 @@
* 2.0.
*/
import {
ENVIRONMENT_ALL,
ENVIRONMENT_NOT_DEFINED,
} from '@kbn/apm-plugin/common/environment_filter_values';
import {
APIClientRequestParamsOf,
APIReturnType,
} from '@kbn/apm-plugin/public/services/rest/create_call_apm_api';
import { RecursivePartial } from '@kbn/apm-plugin/typings/common';
import expect from '@kbn/expect';
import { merge, cloneDeep, isPlainObject } from 'lodash';
import { JsonObject } from '@kbn/utility-types';
import { cloneDeep, isPlainObject, merge } from 'lodash';
import { ApmApiError } from '../../common/apm_api_supertest';
import { FtrProviderContext } from '../../common/ftr_provider_context';
import { expectToReject } from '../../common/utils/expect_to_reject';
const DEFAULT_INDEX_NAME = 'observability-annotations';
export default function annotationApiTests({ getService }: FtrProviderContext) {
const registry = getService('registry');
const supertestRead = getService('legacySupertestAsApmReadUser');
const supertestWrite = getService('legacySupertestAsApmAnnotationsWriteUser');
const apmApiClient = getService('apmApiClient');
const es = getService('es');
function expectContainsObj(source: JsonObject, expected: JsonObject) {
function expectContainsObj(
source: APIReturnType<'POST /api/apm/services/{serviceName}/annotation'>,
expected: JsonObject
) {
expect(source).to.eql(
merge(cloneDeep(source), expected, (a: any, b: any) => {
if (isPlainObject(a) && isPlainObject(b)) {
@ -29,26 +42,46 @@ export default function annotationApiTests({ getService }: FtrProviderContext) {
);
}
function request({ method, url, data }: { method: string; url: string; data?: JsonObject }) {
switch (method.toLowerCase()) {
case 'get':
return supertestRead.get(url).set('kbn-xsrf', 'foo');
function createAnnotation(
body: APIClientRequestParamsOf<'POST /api/apm/services/{serviceName}/annotation'>['params']['body']
) {
return apmApiClient.annotationWriterUser({
endpoint: 'POST /api/apm/services/{serviceName}/annotation',
params: {
path: {
serviceName: 'opbeans-java',
},
body,
},
});
}
case 'post':
return supertestWrite.post(url).send(data).set('kbn-xsrf', 'foo');
default:
throw new Error(`Unsupported method ${method}`);
}
function getAnnotation(
query: RecursivePartial<
APIClientRequestParamsOf<'GET /api/apm/services/{serviceName}/annotation/search'>['params']['query']
>
) {
return apmApiClient.readUser({
endpoint: 'GET /api/apm/services/{serviceName}/annotation/search',
params: {
path: {
serviceName: 'opbeans-java',
},
query: {
environment: ENVIRONMENT_ALL.value,
start: new Date().toISOString(),
end: new Date().toISOString(),
...query,
},
},
});
}
registry.when('Annotations with a basic license', { config: 'basic', archives: [] }, () => {
describe('when creating an annotation', () => {
it('fails with a 403 forbidden', async () => {
const response = await request({
url: '/api/apm/services/opbeans-java/annotation',
method: 'POST',
data: {
const err = await expectToReject<ApmApiError>(() =>
createAnnotation({
'@timestamp': new Date().toISOString(),
message: 'New deployment',
tags: ['foo'],
@ -56,11 +89,11 @@ export default function annotationApiTests({ getService }: FtrProviderContext) {
version: '1.1',
environment: 'production',
},
},
});
})
);
expect(response.status).to.be(403);
expect(response.body.message).to.be(
expect(err.res.status).to.be(403);
expect(err.res.body.message).eql(
'Annotations require at least a gold license or a trial license.'
);
});
@ -79,52 +112,48 @@ export default function annotationApiTests({ getService }: FtrProviderContext) {
});
it('fails with a 400 bad request if data is missing', async () => {
const response = await request({
url: '/api/apm/services/opbeans-java/annotation',
method: 'POST',
});
const err = await expectToReject<ApmApiError>(() =>
// @ts-expect-error
createAnnotation()
);
expect(response.status).to.be(400);
expect(err.res.status).to.be(400);
});
it('fails with a 400 bad request if timestamp is invalid', async () => {
const invalidTimestampErr = await expectToReject<ApmApiError>(() =>
// @ts-expect-error
createAnnotation({
'@timestamp': 'foo',
message: 'foo',
})
);
expect(invalidTimestampErr.res.status).to.be(400);
});
it('fails with a 400 bad request if data is invalid', async () => {
const invalidTimestampResponse = await request({
url: '/api/apm/services/opbeans-java/annotation',
method: 'POST',
data: {
'@timestamp': 'foo',
message: 'foo',
},
});
expect(invalidTimestampResponse.status).to.be(400);
const missingServiceVersionResponse = await request({
url: '/api/apm/services/opbeans-java/annotation',
method: 'POST',
data: {
const err = await expectToReject<ApmApiError>(() =>
// @ts-expect-error
createAnnotation({
'@timestamp': new Date().toISOString(),
message: 'New deployment',
},
});
})
);
expect(missingServiceVersionResponse.status).to.be(400);
expect(err.res.status).to.be(400);
});
it('completes with a 200 and the created annotation if data is complete and valid', async () => {
const timestamp = new Date().toISOString();
const response = await request({
url: '/api/apm/services/opbeans-java/annotation',
method: 'POST',
data: {
'@timestamp': timestamp,
message: 'New deployment',
tags: ['foo'],
service: {
version: '1.1',
environment: 'production',
},
const response = await createAnnotation({
'@timestamp': timestamp,
message: 'New deployment',
tags: ['foo'],
service: {
version: '1.1',
environment: 'production',
},
});
@ -150,14 +179,10 @@ export default function annotationApiTests({ getService }: FtrProviderContext) {
it('prefills `message` and `tags`', async () => {
const timestamp = new Date().toISOString();
const response = await request({
url: '/api/apm/services/opbeans-java/annotation',
method: 'POST',
data: {
'@timestamp': timestamp,
service: {
version: '1.1',
},
const response = await createAnnotation({
'@timestamp': timestamp,
service: {
version: '1.1',
},
});
@ -263,9 +288,10 @@ export default function annotationApiTests({ getService }: FtrProviderContext) {
end: new Date(2020, 4, 2, 20).toISOString(),
};
const response = await request({
url: `/api/apm/services/${serviceName}/annotation/search?start=${range.start}&end=${range.end}&environment=ENVIRONMENT_ALL`,
method: 'GET',
const response = await getAnnotation({
start: range.start,
end: range.end,
environment: ENVIRONMENT_ALL.value,
});
expect(response.status).to.be(200);
@ -283,22 +309,19 @@ export default function annotationApiTests({ getService }: FtrProviderContext) {
expect(
(
await request({
url: `/api/apm/services/${serviceName}/annotation`,
method: 'POST',
data: {
service: {
version: '1.3',
},
'@timestamp': new Date(2020, 4, 2, 21, 30).toISOString(),
await createAnnotation({
service: {
version: '1.3',
},
'@timestamp': new Date(2020, 4, 2, 21, 30).toISOString(),
})
).status
).to.be(200);
const response = await request({
url: `/api/apm/services/${serviceName}/annotation/search?start=${range.start}&end=${range.end}&environment=ENVIRONMENT_ALL`,
method: 'GET',
const response = await getAnnotation({
start: range.start,
end: range.end,
environment: ENVIRONMENT_ALL.value,
});
expect(response.body.annotations.length).to.be(1);
@ -311,22 +334,19 @@ export default function annotationApiTests({ getService }: FtrProviderContext) {
expect(
(
await request({
url: `/api/apm/services/${serviceName}/annotation`,
method: 'POST',
data: {
service: {
version: '1.3',
},
'@timestamp': new Date(2020, 4, 2, 21, 30).toISOString(),
await createAnnotation({
service: {
version: '1.3',
},
'@timestamp': new Date(2020, 4, 2, 21, 30).toISOString(),
})
).status
).to.be(200);
const responseFromEarlierRange = await request({
url: `/api/apm/services/${serviceName}/annotation/search?start=${earlierRange.start}&end=${earlierRange.end}&environment=ENVIRONMENT_ALL`,
method: 'GET',
const responseFromEarlierRange = await getAnnotation({
start: earlierRange.start,
end: earlierRange.end,
environment: ENVIRONMENT_ALL.value,
});
expect(responseFromEarlierRange.body.annotations.length).to.be(2);
@ -337,31 +357,23 @@ export default function annotationApiTests({ getService }: FtrProviderContext) {
it('returns stored annotations for the given environment', async () => {
expect(
(
await request({
url: `/api/apm/services/${serviceName}/annotation`,
method: 'POST',
data: {
service: {
version: '1.3',
},
'@timestamp': new Date(2020, 4, 2, 21, 30).toISOString(),
await createAnnotation({
service: {
version: '1.3',
},
'@timestamp': new Date(2020, 4, 2, 21, 30).toISOString(),
})
).status
).to.be(200);
expect(
(
await request({
url: `/api/apm/services/${serviceName}/annotation`,
method: 'POST',
data: {
service: {
version: '1.4',
environment: 'production',
},
'@timestamp': new Date(2020, 4, 2, 21, 31).toISOString(),
await createAnnotation({
service: {
version: '1.4',
environment: 'production',
},
'@timestamp': new Date(2020, 4, 2, 21, 31).toISOString(),
})
).status
).to.be(200);
@ -371,24 +383,27 @@ export default function annotationApiTests({ getService }: FtrProviderContext) {
end: new Date(2020, 4, 2, 23).toISOString(),
};
const allEnvironmentsResponse = await request({
url: `/api/apm/services/${serviceName}/annotation/search?start=${range.start}&end=${range.end}&environment=ENVIRONMENT_ALL`,
method: 'GET',
const allEnvironmentsResponse = await getAnnotation({
start: range.start,
end: range.end,
environment: ENVIRONMENT_ALL.value,
});
expect(allEnvironmentsResponse.body.annotations.length).to.be(2);
const productionEnvironmentResponse = await request({
url: `/api/apm/services/${serviceName}/annotation/search?start=${range.start}&end=${range.end}&environment=production`,
method: 'GET',
const productionEnvironmentResponse = await getAnnotation({
start: range.start,
end: range.end,
environment: 'production',
});
expect(productionEnvironmentResponse.body.annotations.length).to.be(1);
expect(productionEnvironmentResponse.body.annotations[0].text).to.be('1.4');
const missingEnvironmentsResponse = await request({
url: `/api/apm/services/${serviceName}/annotation/search?start=${range.start}&end=${range.end}&environment=ENVIRONMENT_NOT_DEFINED`,
method: 'GET',
const missingEnvironmentsResponse = await getAnnotation({
start: range.start,
end: range.end,
environment: ENVIRONMENT_NOT_DEFINED.value,
});
expect(missingEnvironmentsResponse.body.annotations.length).to.be(1);

View file

@ -10,6 +10,7 @@ import { PrivilegeType } from '@kbn/apm-plugin/common/privilege_type';
import { ApmUsername } from '@kbn/apm-plugin/server/test_helpers/create_apm_users/authentication';
import { FtrProviderContext } from '../../../common/ftr_provider_context';
import { ApmApiError, ApmApiSupertest } from '../../../common/apm_api_supertest';
import { expectToReject } from '../../../common/utils/expect_to_reject';
export default function ApiTest({ getService }: FtrProviderContext) {
const registry = getService('registry');
@ -50,20 +51,24 @@ export default function ApiTest({ getService }: FtrProviderContext) {
() => {
describe('When the user does not have the required cluster privileges', () => {
it('should return an error when creating an agent key', async () => {
const error = await expectToReject(() => createAgentKey(apmApiClient.writeUser));
const error = await expectToReject<ApmApiError>(() =>
createAgentKey(apmApiClient.writeUser)
);
expect(error.res.status).to.be(500);
expect(error.res.body.message).contain('is missing the following requested privilege');
});
it('should return an error when invalidating an agent key', async () => {
const error = await expectToReject(() =>
const error = await expectToReject<ApmApiError>(() =>
invalidateAgentKey(apmApiClient.writeUser, agentKeyName)
);
expect(error.res.status).to.be(500);
});
it('should return an error when getting a list of agent keys', async () => {
const error = await expectToReject(() => getAgentKeys(apmApiClient.writeUser));
const error = await expectToReject<ApmApiError>(() =>
getAgentKeys(apmApiClient.writeUser)
);
expect(error.res.status).to.be(500);
});
});
@ -71,7 +76,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
describe('When the user does not have the required application privileges', () => {
allApplicationPrivileges.map((privilege) => {
it(`should return an error when creating an agent key with ${privilege} privilege`, async () => {
const error = await expectToReject(() =>
const error = await expectToReject<ApmApiError>(() =>
createAgentKey(apmApiClient.manageOwnAgentKeysUser, [privilege])
);
expect(error.res.status).to.be(500);
@ -159,13 +164,4 @@ export default function ApiTest({ getService }: FtrProviderContext) {
});
}
);
async function expectToReject(fn: () => Promise<any>): Promise<ApmApiError> {
try {
await fn();
} catch (e) {
return e;
}
throw new Error(`Expected fn to throw`);
}
}

View file

@ -6,38 +6,50 @@
*/
import expect from '@kbn/expect';
import { ApmApiError } from '../../../common/apm_api_supertest';
import { FtrProviderContext } from '../../../common/ftr_provider_context';
import { expectToReject } from '../../../common/utils/expect_to_reject';
export default function apiTest({ getService }: FtrProviderContext) {
const registry = getService('registry');
const noAccessUser = getService('legacySupertestAsNoAccessUser');
const apmApiClient = getService('apmApiClient');
function getJobs() {
return noAccessUser.get(`/internal/apm/settings/anomaly-detection/jobs`).set('kbn-xsrf', 'foo');
return apmApiClient.noAccessUser({
endpoint: 'GET /internal/apm/settings/anomaly-detection/jobs',
});
}
function createJobs(environments: string[]) {
return noAccessUser
.post(`/internal/apm/settings/anomaly-detection/jobs`)
.send({ environments })
.set('kbn-xsrf', 'foo');
return apmApiClient.noAccessUser({
endpoint: 'POST /internal/apm/settings/anomaly-detection/jobs',
params: {
body: {
environments,
},
},
});
}
registry.when('ML jobs', { config: 'trial', archives: [] }, () => {
describe('when user does not have read access to ML', () => {
describe('when calling the endpoint for listing jobs', () => {
it('returns an error because the user does not have access', async () => {
const { body } = await getJobs();
expect(body.statusCode).to.be(403);
expect(body.error).to.be('Forbidden');
const err = await expectToReject<ApmApiError>(() => getJobs());
expect(err.res.status).to.be(403);
expect(err.res.body.message).eql('Forbidden');
});
});
describe('when calling create endpoint', () => {
it('returns an error because the user does not have access', async () => {
const { body } = await createJobs(['production', 'staging']);
expect(body.statusCode).to.be(403);
expect(body.error).to.be('Forbidden');
const err = await expectToReject<ApmApiError>(() =>
createJobs(['production', 'staging'])
);
expect(err.res.status).to.be(403);
expect(err.res.body.message).eql('Forbidden');
});
});
});