[Obs AI Assistant] Fix flaky e2e test (contextual insights for APM errors) (#184642)

Closes https://github.com/elastic/kibana/issues/184071
https://github.com/elastic/kibana/issues/184029

Fixes flaky e2e tests 

- set static `error.grouping_key`. This required a small change in
synthtrace.
- retry clicking on contextual insight component to ensure the accordion
is open.
This commit is contained in:
Søren Louv-Jansen 2024-06-26 18:17:58 +02:00 committed by GitHub
parent a46ea29bbd
commit cf47eb5aa8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 59 additions and 31 deletions

View file

@ -25,9 +25,11 @@ export class ApmError extends Serializable<ApmFields> {
this.fields['error.grouping_name'] || this.fields['error.exception']?.[0]?.message;
const [data] = super.serialize();
data['error.grouping_key'] = errorMessage
? generateLongIdWithSeed(errorMessage)
: generateLongId();
data['error.grouping_key'] =
this.fields['error.grouping_key'] ??
(errorMessage ? generateLongIdWithSeed(errorMessage) : generateLongId());
return [data];
}

View file

@ -72,9 +72,20 @@ export class Instance extends Entity<ApmFields> {
'error.grouping_name': getErrorGroupingKey(message),
});
}
error({ message, type, culprit }: { message: string; type?: string; culprit?: string }) {
error({
message,
type,
culprit,
groupingKey,
}: {
message: string;
type?: string;
culprit?: string;
groupingKey?: string;
}) {
return new ApmError({
...this.fields,
...(groupingKey ? { 'error.grouping_key': groupingKey } : {}),
'error.exception': [{ message, ...(type ? { type } : {}) }],
'error.culprit': culprit,
});

View file

@ -60,7 +60,7 @@ export function InsightBase({
return (
<EuiPanel hasBorder hasShadow={false}>
<EuiAccordion
id="obsAiAssistantInsight"
id="obsAiAssistantInsightContainer"
arrowProps={{ css: { alignSelf: 'flex-start' } }}
buttonContent={
<EuiFlexGroup wrap responsive={false} gutterSize="m" data-test-subj={dataTestSubj}>

View file

@ -98,7 +98,7 @@ export class LlmProxy {
waitForIntercept: () => Promise<LlmResponseSimulator>;
}
: {
complete: () => Promise<void>;
waitAndComplete: () => Promise<void>;
} {
const waitForInterceptPromise = Promise.race([
new Promise<LlmResponseSimulator>((outerResolve) => {
@ -149,7 +149,7 @@ export class LlmProxy {
});
}),
new Promise<LlmResponseSimulator>((_, reject) => {
setTimeout(() => reject(new Error(`Interceptor "${name}" timed out after 5000ms`)), 5000);
setTimeout(() => reject(new Error(`Interceptor "${name}" timed out after 20000ms`)), 20000);
}),
]);
@ -162,7 +162,7 @@ export class LlmProxy {
: responseChunks.split(' ').map((token, i) => (i === 0 ? token : ` ${token}`));
return {
complete: async () => {
waitAndComplete: async () => {
const simulator = await waitForInterceptPromise;
for (const chunk of parsedChunks) {
await simulator.next(chunk);

View file

@ -414,11 +414,11 @@ export default function ApiTest({ getService }: FtrProviderContext) {
},
},
])
.complete();
.waitAndComplete();
proxy
.intercept('conversation', (body) => !isFunctionTitleRequest(body), 'Good morning, sir!')
.complete();
.waitAndComplete();
const createResponse = await observabilityAIAssistantAPIClient
.editorUser({
@ -450,7 +450,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
proxy
.intercept('conversation', (body) => !isFunctionTitleRequest(body), 'Good night, sir!')
.complete();
.waitAndComplete();
const updatedResponse = await observabilityAIAssistantAPIClient
.editorUser({

View file

@ -44,6 +44,7 @@ const pages = {
saveButton: 'create-connector-flyout-save-btn',
},
contextualInsights: {
container: 'obsAiAssistantInsightContainer',
button: 'obsAiAssistantInsightButton',
text: 'obsAiAssistantInsightResponse',
},

View file

@ -17,14 +17,13 @@ import { FtrProviderContext } from '../../ftr_provider_context';
export default function ApiTest({ getService, getPageObjects }: FtrProviderContext) {
const ui = getService('observabilityAIAssistantUI');
const find = getService('find');
const testSubjects = getService('testSubjects');
const supertest = getService('supertest');
const retry = getService('retry');
const log = getService('log');
const browser = getService('browser');
const deployment = getService('deployment');
const apmSynthtraceEsClient = getService('apmSynthtraceEsClient');
const { common } = getPageObjects(['header', 'common']);
const { header, common } = getPageObjects(['header', 'common']);
async function createSynthtraceErrors() {
const start = moment().subtract(5, 'minutes').valueOf();
@ -45,7 +44,11 @@ export default function ApiTest({ getService, getPageObjects }: FtrProviderConte
.transaction({ transactionName: 'GET /banana' })
.errors(
serviceInstance
.error({ message: 'Some exception', type: 'exception' })
.error({
message: 'Some exception',
type: 'exception',
groupingKey: 'some-expection-key',
})
.timestamp(timestamp)
)
.duration(10)
@ -87,9 +90,22 @@ export default function ApiTest({ getService, getPageObjects }: FtrProviderConte
}
async function navigateToError() {
await common.navigateToApp('apm');
await browser.get(`${deployment.getHostPort()}/app/apm/services/opbeans-go/errors/`);
await testSubjects.click('errorGroupId');
await common.navigateToUrl('apm', 'services/opbeans-go/errors/some-expection-key', {
shouldUseHashForSubUrl: false,
});
await header.waitUntilLoadingHasFinished();
}
// open contextual insights component and ensure it was opened
async function openContextualInsights() {
await retry.tryForTime(5 * 1000, async () => {
await testSubjects.click(ui.pages.contextualInsights.button);
const isOpen =
(await (
await find.byCssSelector(`[aria-controls="${ui.pages.contextualInsights.container}"]`)
).getAttribute('aria-expanded')) === 'true';
expect(isOpen).to.be(true);
});
}
describe('Contextual insights for APM errors', () => {
@ -113,16 +129,14 @@ export default function ApiTest({ getService, getPageObjects }: FtrProviderConte
]);
});
// FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/184029
describe.skip('when there are no connectors', () => {
describe('when there are no connectors', () => {
it('should not show the contextual insight component', async () => {
await navigateToError();
await testSubjects.missingOrFail(ui.pages.contextualInsights.button);
});
});
// FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/184071
describe.skip('when there are connectors', () => {
describe('when there are connectors', () => {
let proxy: LlmProxy;
before(async () => {
@ -137,17 +151,17 @@ export default function ApiTest({ getService, getPageObjects }: FtrProviderConte
it('should show the contextual insight component on the APM error details page', async () => {
await navigateToError();
proxy
.intercept(
'conversation',
(body) => !isFunctionTitleRequest(body),
'This error is nothing to worry about. Have a nice day!'
)
.complete();
const interceptor = proxy.intercept(
'conversation',
(body) => !isFunctionTitleRequest(body),
'This error is nothing to worry about. Have a nice day!'
);
await testSubjects.click(ui.pages.contextualInsights.button);
await openContextualInsights();
await retry.try(async () => {
await interceptor.waitAndComplete();
await retry.tryForTime(5 * 1000, async () => {
const llmResponse = await testSubjects.getVisibleText(ui.pages.contextualInsights.text);
expect(llmResponse).to.contain('This error is nothing to worry about. Have a nice day!');
});