From 137741fa5525311c736faa2cacbda67ab267ad79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Louv-Jansen?= Date: Wed, 19 Oct 2022 11:31:10 +0200 Subject: [PATCH] Add api test for suggestions --- .../get_suggestions_with_terms_aggregation.ts | 2 +- .../tests/suggestions/generate_data.ts | 77 ++++ .../tests/suggestions/suggestions.spec.ts | 361 ++++++++++++------ 3 files changed, 332 insertions(+), 108 deletions(-) create mode 100644 x-pack/test/apm_api_integration/tests/suggestions/generate_data.ts diff --git a/x-pack/plugins/apm/server/routes/suggestions/get_suggestions_with_terms_aggregation.ts b/x-pack/plugins/apm/server/routes/suggestions/get_suggestions_with_terms_aggregation.ts index 77a7528fbb1a3..56ed34805c2fb 100644 --- a/x-pack/plugins/apm/server/routes/suggestions/get_suggestions_with_terms_aggregation.ts +++ b/x-pack/plugins/apm/server/routes/suggestions/get_suggestions_with_terms_aggregation.ts @@ -23,7 +23,7 @@ export async function getSuggestionsWithTermsAggregation({ fieldName: string; fieldValue: string; searchAggregatedTransactions: boolean; - serviceName: string; + serviceName?: string; setup: Setup; size: number; start: number; diff --git a/x-pack/test/apm_api_integration/tests/suggestions/generate_data.ts b/x-pack/test/apm_api_integration/tests/suggestions/generate_data.ts new file mode 100644 index 0000000000000..13d6359e0a733 --- /dev/null +++ b/x-pack/test/apm_api_integration/tests/suggestions/generate_data.ts @@ -0,0 +1,77 @@ +/* + * 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 { apm, timerange } from '@kbn/apm-synthtrace'; +import type { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; +import { times } from 'lodash'; + +export async function generateData({ + synthtraceEsClient, + start, + end, +}: { + synthtraceEsClient: ApmSynthtraceEsClient; + start: number; + end: number; +}) { + const services = times(5).flatMap((serviceId) => { + return ['go', 'java'].flatMap((agentName) => { + return ['production', 'development', 'staging'].flatMap((environment) => { + return times(5).flatMap((envId) => { + const service = apm + .service({ + name: `${agentName}-${serviceId}`, + environment: `${environment}-${envId}`, + agentName, + }) + .instance('instance-a'); + + return service; + }); + }); + }); + }); + + const transactionNames = [ + 'GET /api/product/:id', + 'PUT /api/product/:id', + 'GET /api/user/:id', + 'PUT /api/user/:id', + ]; + + const phpService = apm + .service({ + name: `custom-php-service`, + environment: `custom-php-environment`, + agentName: 'php', + }) + .instance('instance-a'); + + const docs = timerange(start, end) + .ratePerMinute(1) + .generator((timestamp) => { + const autoGeneratedDocs = services.flatMap((service) => { + return transactionNames.flatMap((transactionName) => { + return service + .transaction({ transactionName, transactionType: 'my-custom-type' }) + .timestamp(timestamp) + .duration(1000); + }); + }); + + const customDoc = phpService + .transaction({ + transactionName: 'GET /api/php/memory', + transactionType: 'custom-php-type', + }) + .timestamp(timestamp) + .duration(1000); + + return [...autoGeneratedDocs, customDoc]; + }); + + return await synthtraceEsClient.index(docs); +} diff --git a/x-pack/test/apm_api_integration/tests/suggestions/suggestions.spec.ts b/x-pack/test/apm_api_integration/tests/suggestions/suggestions.spec.ts index 692cd1c0cf7f1..db15db23776c7 100644 --- a/x-pack/test/apm_api_integration/tests/suggestions/suggestions.spec.ts +++ b/x-pack/test/apm_api_integration/tests/suggestions/suggestions.spec.ts @@ -7,139 +7,286 @@ import { SERVICE_ENVIRONMENT, SERVICE_NAME, + TRANSACTION_NAME, TRANSACTION_TYPE, } from '@kbn/apm-plugin/common/elasticsearch_fieldnames'; -import archives_metadata from '../../common/fixtures/es_archiver/archives_metadata'; +import expect from '@kbn/expect'; import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { generateData } from './generate_data'; + +const startNumber = new Date('2021-01-01T00:00:00.000Z').getTime(); +const endNumber = new Date('2021-01-01T00:05:00.000Z').getTime() - 1; + +const start = new Date(startNumber).toISOString(); +const end = new Date(endNumber).toISOString(); export default function suggestionsTests({ getService }: FtrProviderContext) { const registry = getService('registry'); const apmApiClient = getService('apmApiClient'); - const archiveName = 'apm_8.0.0'; - const { start, end } = archives_metadata[archiveName]; - - registry.when( - 'suggestions when data is loaded', - { config: 'basic', archives: [archiveName] }, - () => { - describe('with environment', () => { - describe('with an empty string parameter', () => { - it('returns all environments', async () => { - const { body } = await apmApiClient.readUser({ - endpoint: 'GET /internal/apm/suggestions', - params: { query: { fieldName: SERVICE_ENVIRONMENT, fieldValue: '', start, end } }, - }); - - expectSnapshot(body).toMatchInline(` - Object { - "terms": Array [ - "production", - "testing", - ], - } - `); + const synthtraceEsClient = getService('synthtraceEsClient'); + + registry.when('suggestions when data is loaded', { config: 'basic', archives: [] }, async () => { + before(async () => { + await generateData({ + synthtraceEsClient, + start: startNumber, + end: endNumber, + }); + }); + + after(() => synthtraceEsClient.clean()); + + describe(`field: ${SERVICE_ENVIRONMENT}`, () => { + describe('when fieldValue is empty', () => { + it('returns all environments', async () => { + const { body } = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/suggestions', + params: { + query: { fieldName: SERVICE_ENVIRONMENT, fieldValue: '', start, end }, + }, + }); + + expectSnapshot(body.terms).toMatchInline(` + Array [ + "custom-php-environment", + "development-0", + "development-1", + "development-2", + "development-3", + "development-4", + "production-0", + "production-1", + "production-2", + "production-3", + "production-4", + "staging-0", + "staging-1", + "staging-2", + "staging-3", + "staging-4", + ] + `); + }); + }); + + describe('when fieldValue is not empty', () => { + it('returns environments that start with the fieldValue', async () => { + const { body } = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/suggestions', + params: { query: { fieldName: SERVICE_ENVIRONMENT, fieldValue: 'prod', start, end } }, + }); + + expectSnapshot(body.terms).toMatchInline(` + Array [ + "production-0", + "production-1", + "production-2", + "production-3", + "production-4", + ] + `); + }); + + it('returns environments that contain the fieldValue', async () => { + const { body } = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/suggestions', + params: { + query: { fieldName: SERVICE_ENVIRONMENT, fieldValue: 'evelopment', start, end }, + }, + }); + + expectSnapshot(body.terms).toMatchInline(` + Array [ + "development-0", + "development-1", + "development-2", + "development-3", + "development-4", + ] + `); + }); + + it('returns no results if nothing matches', async () => { + const { body } = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/suggestions', + params: { + query: { fieldName: SERVICE_ENVIRONMENT, fieldValue: 'foobar', start, end }, + }, + }); + + expect(body.terms).to.eql([]); + }); + }); + }); + + describe(`field: ${SERVICE_NAME}`, () => { + describe('when fieldValue is empty', () => { + it('returns all service names', async () => { + const { body } = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/suggestions', + params: { query: { fieldName: SERVICE_NAME, fieldValue: '', start, end } }, + }); + + expectSnapshot(body.terms).toMatchInline(` + Array [ + "custom-php-service", + "go-0", + "go-1", + "go-2", + "go-3", + "go-4", + "java-0", + "java-1", + "java-2", + "java-3", + "java-4", + ] + `); + }); + }); + + describe('when fieldValue is not empty', () => { + it('returns services that start with the fieldValue', async () => { + const { body } = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/suggestions', + params: { query: { fieldName: SERVICE_NAME, fieldValue: 'java', start, end } }, + }); + + expectSnapshot(body.terms).toMatchInline(` + Array [ + "java-0", + "java-1", + "java-2", + "java-3", + "java-4", + ] + `); + }); + + it('returns services that contains the fieldValue', async () => { + const { body } = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/suggestions', + params: { query: { fieldName: SERVICE_NAME, fieldValue: '1', start, end } }, }); + + expectSnapshot(body.terms).toMatchInline(` + Array [ + "go-1", + "java-1", + ] + `); }); + }); + }); + + describe(`field: ${TRANSACTION_TYPE}`, () => { + describe('when fieldValue is empty', () => { + it('returns all transaction types', async () => { + const { body } = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/suggestions', + params: { query: { fieldName: TRANSACTION_TYPE, fieldValue: '', start, end } }, + }); - describe('with a string parameter', () => { - it('returns items matching the string parameter', async () => { - const { body } = await apmApiClient.readUser({ - endpoint: 'GET /internal/apm/suggestions', - params: { query: { fieldName: SERVICE_ENVIRONMENT, fieldValue: 'pr', start, end } }, - }); - - expectSnapshot(body).toMatchInline(` - Object { - "terms": Array [ - "production", - ], - } + expectSnapshot(body.terms).toMatchInline(` + Array [ + "custom-php-type", + "my-custom-type", + ] `); + }); + }); + + describe('with a string parameter', () => { + it('returns items matching the string parameter', async () => { + const { body } = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/suggestions', + params: { query: { fieldName: TRANSACTION_TYPE, fieldValue: 'custom', start, end } }, }); + + expectSnapshot(body.terms).toMatchInline(` + Array [ + "custom-php-type", + ] + `); }); }); + }); - describe('with service name', () => { - describe('with an empty string parameter', () => { - it('returns all services', async () => { - const { body } = await apmApiClient.readUser({ - endpoint: 'GET /internal/apm/suggestions', - params: { query: { fieldName: SERVICE_NAME, fieldValue: '', start, end } }, - }); - - expectSnapshot(body).toMatchInline(` - Object { - "terms": Array [ - "auditbeat", - "opbeans-dotnet", - "opbeans-go", - "opbeans-java", - "opbeans-node", - "opbeans-python", - "opbeans-ruby", - "opbeans-rum", - ], - } - `); + describe(`field: ${TRANSACTION_NAME}`, () => { + describe('when fieldValue is empty', () => { + it('returns all transaction names', async () => { + const { body } = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/suggestions', + params: { query: { fieldName: TRANSACTION_NAME, fieldValue: '', start, end } }, }); + + expectSnapshot(body.terms).toMatchInline(` + Array [ + "GET /api/php/memory", + "GET /api/product/:id", + "GET /api/user/:id", + "PUT /api/product/:id", + "PUT /api/user/:id", + ] + `); }); + }); - describe('with a string parameter', () => { - it('returns items matching the string parameter', async () => { - const { body } = await apmApiClient.readUser({ - endpoint: 'GET /internal/apm/suggestions', - params: { query: { fieldName: SERVICE_NAME, fieldValue: 'aud', start, end } }, - }); - - expectSnapshot(body).toMatchInline(` - Object { - "terms": Array [ - "auditbeat", - ], - } - `); + describe('with a string parameter', () => { + it('returns items matching the string parameter', async () => { + const { body } = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/suggestions', + params: { query: { fieldName: TRANSACTION_NAME, fieldValue: 'product', start, end } }, }); + + expectSnapshot(body.terms).toMatchInline(` + Array [ + "GET /api/product/:id", + "PUT /api/product/:id", + ] + `); }); }); - describe('with transaction type', () => { - describe('with an empty string parameter', () => { - it('returns all transaction types', async () => { - const { body } = await apmApiClient.readUser({ - endpoint: 'GET /internal/apm/suggestions', - params: { query: { fieldName: TRANSACTION_TYPE, fieldValue: '', start, end } }, - }); - - expectSnapshot(body).toMatchInline(` - Object { - "terms": Array [ - "Worker", - "celery", - "page-load", - "request", - ], - } - `); + describe('when limiting the suggestions to a specific service', () => { + it('returns items matching the string parameter', async () => { + const { body } = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/suggestions', + params: { + query: { + serviceName: 'custom-php-service', + fieldName: TRANSACTION_NAME, + fieldValue: '', + start, + end, + }, + }, }); + + expectSnapshot(body.terms).toMatchInline(` + Array [ + "GET /api/php/memory", + ] + `); }); - describe('with a string parameter', () => { - it('returns items matching the string parameter', async () => { - const { body } = await apmApiClient.readUser({ - endpoint: 'GET /internal/apm/suggestions', - params: { query: { fieldName: TRANSACTION_TYPE, fieldValue: 'w', start, end } }, - }); - - expectSnapshot(body).toMatchInline(` - Object { - "terms": Array [ - "Worker", - ], - } - `); + it('does not return transactions from other services', async () => { + const { body } = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/suggestions', + params: { + query: { + serviceName: 'custom-php-service', + fieldName: TRANSACTION_NAME, + fieldValue: 'product', + start, + end, + }, + }, }); + + expect(body.terms).to.eql([]); }); }); - } - ); + }); + }); }