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 all 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 @@ -70,7 +70,6 @@ export class Instance extends Entity<ApmFields> {
...this.fields,
'error.type': 'crash',
'error.exception': [{ message, ...(type ? { type } : {}) }],
'error.grouping_name': getErrorGroupingKey(message),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found out that this is causing the following issue

error": {
    "type": "document_parsing_exception",
    "reason": "[1:446] failed to parse field [error.grouping_name] of type [keyword] in document with id 'Bd2CBpMB-u5CWjDqauRY'. Preview of field's value: 'test'",
    "caused_by": {
      "type": "illegal_argument_exception",
      "reason": "Cannot index data directly into a field with a [script] parameter"
    }
  },
  
  

and as a result the documents are dropped.

The grouping_name in the mapping

"grouping_name": {
              "type": "keyword",
              "script": {
                "source": """def logMessage = params['_source'].error?.log?.message;
if (logMessage != null && logMessage != "") {
  emit(logMessage);
  return;
}
def exception = params['_source'].error?.exception;
if (exception != null && exception.isEmpty() == false) {
  def exceptionMessage = exception instanceof Map ? exception?.message : exception[0]?.message;
  if (exceptionMessage instanceof List) {
    exceptionMessage = exceptionMessage[0]
  }
  if (exceptionMessage != null && exceptionMessage != "") {
    emit(exceptionMessage);
  }
}
""",
                "lang": "painless"
              }
            },

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I removed it

});
}
error({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,6 @@ export class MobileDevice extends Entity<ApmFields> {
'error.type': 'crash',
'error.id': generateLongIdWithSeed(message),
'error.exception': [{ message, ...{ type: 'crash' } }],
'error.grouping_name': groupingName || message,
});
}
}
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,6 +12,7 @@ export default function apmApiIntegrationTests({
}: DeploymentAgnosticFtrProviderContext) {
describe('APM', function () {
loadTestFile(require.resolve('./agent_explorer'));
loadTestFile(require.resolve('./mobile'));
loadTestFile(require.resolve('./custom_dashboards'));
loadTestFile(require.resolve('./dependencies'));
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/*
* 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();
});

describe('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,
]);
});
});
});
});
});
}
Loading