[8.10] Simplify test code, reenable skipped suite (#163158) (#165333)

# Backport

This will backport the following commits from `main` to `8.10`:
- [Simplify test code, reenable skipped suite
(#163158)](https://github.com/elastic/kibana/pull/163158)

<!--- Backport version: 8.9.7 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Gerard
Soldevila","email":"gerard.soldevila@elastic.co"},"sourceCommit":{"committedDate":"2023-08-31T13:09:19Z","message":"Simplify
test code, reenable skipped suite (#163158)\n\nAttempt at fixing
https://github.com/elastic/kibana/issues/149611\r\n\r\nI updated the
test code as follows:\r\n* Removed the RxJS logic and simply factorised
the reads to read only\r\nonce.\r\n* Got rid of the \"retry\" service.
There's already a mechanism in place\r\nto make sure the logs are
up-to-date.\r\n* Updated the `setCommonlyUsedTime` method to make sure
it awaits for\r\nthe popup to be ready before clicking.\r\n* Skipped 4
tests that seem outdated, the logs don't have the related\r\nentries
even after waiting for more than one minute and flushing (in\r\nfact,
they all seem to systematically fail on `main` too):\r\n *
lnsLegacyMetric\r\n * [Flights] Delays & Cancellations\r\n * [Flights]
Destination Weather\r\n * [Flights] Delay Buckets\r\n\r\nAttached is the
generated\r\n[kibana.log](12260144/kibana.log)\r\n(focussing
only the `browser.ts` tests).\r\n\r\nSo for the skipped tests, this does
not look like flakiness anymore, but\r\nrather outdated / incorrect
checks. I propose we review and update them\r\non a separate issue /
PR.\r\n\r\n50 runs
results\r\n[here](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/3026).","sha":"a62d9a90f53c6ebec48d4982393c637cc73c26ac","branchLabelMapping":{"^v8.11.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["Team:Core","release_note:skip","test-failure-flaky","backport:prev-minor","v8.11.0"],"number":163158,"url":"https://github.com/elastic/kibana/pull/163158","mergeCommit":{"message":"Simplify
test code, reenable skipped suite (#163158)\n\nAttempt at fixing
https://github.com/elastic/kibana/issues/149611\r\n\r\nI updated the
test code as follows:\r\n* Removed the RxJS logic and simply factorised
the reads to read only\r\nonce.\r\n* Got rid of the \"retry\" service.
There's already a mechanism in place\r\nto make sure the logs are
up-to-date.\r\n* Updated the `setCommonlyUsedTime` method to make sure
it awaits for\r\nthe popup to be ready before clicking.\r\n* Skipped 4
tests that seem outdated, the logs don't have the related\r\nentries
even after waiting for more than one minute and flushing (in\r\nfact,
they all seem to systematically fail on `main` too):\r\n *
lnsLegacyMetric\r\n * [Flights] Delays & Cancellations\r\n * [Flights]
Destination Weather\r\n * [Flights] Delay Buckets\r\n\r\nAttached is the
generated\r\n[kibana.log](12260144/kibana.log)\r\n(focussing
only the `browser.ts` tests).\r\n\r\nSo for the skipped tests, this does
not look like flakiness anymore, but\r\nrather outdated / incorrect
checks. I propose we review and update them\r\non a separate issue /
PR.\r\n\r\n50 runs
results\r\n[here](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/3026).","sha":"a62d9a90f53c6ebec48d4982393c637cc73c26ac"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v8.11.0","labelRegex":"^v8.11.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/163158","number":163158,"mergeCommit":{"message":"Simplify
test code, reenable skipped suite (#163158)\n\nAttempt at fixing
https://github.com/elastic/kibana/issues/149611\r\n\r\nI updated the
test code as follows:\r\n* Removed the RxJS logic and simply factorised
the reads to read only\r\nonce.\r\n* Got rid of the \"retry\" service.
There's already a mechanism in place\r\nto make sure the logs are
up-to-date.\r\n* Updated the `setCommonlyUsedTime` method to make sure
it awaits for\r\nthe popup to be ready before clicking.\r\n* Skipped 4
tests that seem outdated, the logs don't have the related\r\nentries
even after waiting for more than one minute and flushing (in\r\nfact,
they all seem to systematically fail on `main` too):\r\n *
lnsLegacyMetric\r\n * [Flights] Delays & Cancellations\r\n * [Flights]
Destination Weather\r\n * [Flights] Delay Buckets\r\n\r\nAttached is the
generated\r\n[kibana.log](12260144/kibana.log)\r\n(focussing
only the `browser.ts` tests).\r\n\r\nSo for the skipped tests, this does
not look like flakiness anymore, but\r\nrather outdated / incorrect
checks. I propose we review and update them\r\non a separate issue /
PR.\r\n\r\n50 runs
results\r\n[here](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/3026).","sha":"a62d9a90f53c6ebec48d4982393c637cc73c26ac"}}]}]
BACKPORT-->

Co-authored-by: Gerard Soldevila <gerard.soldevila@elastic.co>
This commit is contained in:
Kibana Machine 2023-08-31 10:26:11 -04:00 committed by GitHub
parent 032383020c
commit fe93fa7b6e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 71 additions and 126 deletions

View file

@ -92,7 +92,9 @@ export class TimePickerPageObject extends FtrService {
* @param option 'Today' | 'This_week' | 'Last_15 minutes' | 'Last_24 hours' ...
*/
async setCommonlyUsedTime(option: CommonlyUsed | string) {
await this.testSubjects.exists('superDatePickerToggleQuickMenuButton', { timeout: 5000 });
await this.testSubjects.click('superDatePickerToggleQuickMenuButton');
await this.testSubjects.exists(`superDatePickerCommonlyUsed_${option}`, { timeout: 5000 });
await this.testSubjects.click(`superDatePickerCommonlyUsed_${option}`);
}

View file

@ -4,18 +4,15 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import Fs from 'fs/promises';
import Path from 'path';
import JSON5 from 'json5';
import Fs from 'fs/promises';
import { isEqualWith } from 'lodash';
import type { Ecs, KibanaExecutionContext } from '@kbn/core/server';
import type { RetryService } from '@kbn/ftr-common-functional-services';
import { concatMap, defer, filter, firstValueFrom, ReplaySubject, scan, timeout } from 'rxjs';
export const logFilePath = Path.resolve(__dirname, './kibana.log');
export const ANY = Symbol('any');
let logstream$: ReplaySubject<Ecs> | undefined;
export function getExecutionContextFromLogRecord(record: Ecs | undefined): KibanaExecutionContext {
if (record?.log?.logger !== 'execution_context' || !record?.message) {
throw new Error(`The record is not an entry of execution context`);
@ -37,95 +34,41 @@ export function isExecutionContextLog(
}
}
// to avoid splitting log record containing \n symbol
const endOfLine = /(?<=})\s*\n/;
export async function assertLogContains({
description,
/**
* Checks the provided log records against the provided predicate
*/
export function assertLogContains({
logs,
predicate,
retry,
description,
}: {
description: string;
logs: Ecs[];
predicate: (record: Ecs) => boolean;
retry: RetryService;
}): Promise<void> {
// logs are written to disk asynchronously. I sacrificed performance to reduce flakiness.
await retry.waitFor(description, async () => {
if (!logstream$) {
logstream$ = getLogstream$();
}
try {
await firstValueFrom(logstream$.pipe(filter(predicate), timeout(5_000)));
return true;
} catch (err) {
return false;
}
});
description: string;
}) {
if (!logs.some(predicate)) {
throw new Error(`Unable to find log entries: ${description}`);
}
}
/**
* Creates an observable that continuously tails the log file.
* Reads the log file and parses the JSON objects that it contains.
*/
function getLogstream$(): ReplaySubject<Ecs> {
const stream$ = new ReplaySubject<Ecs>();
defer(async function* () {
const fd = await Fs.open(logFilePath, 'rs');
while (!stream$.isStopped) {
const { bytesRead, buffer } = await fd.read();
if (bytesRead) {
yield buffer.toString('utf8', 0, bytesRead);
}
}
await fd.close();
})
.pipe(
scan<string, { buffer: string; records: Ecs[] }>(
({ buffer }, chunk) => {
const logString = buffer.concat(chunk);
const lines = logString.split(endOfLine);
const lastLine = lines.pop();
const records = lines.map((s) => JSON.parse(s));
let leftover = '';
if (lastLine) {
try {
const validRecord = JSON.parse(lastLine);
records.push(validRecord);
} catch (err) {
leftover = lastLine;
}
}
return { buffer: leftover, records };
},
{
records: [], // The ECS entries in the logs
buffer: '', // Accumulated leftovers from the previous operation
}
),
concatMap(({ records }) => records)
)
.subscribe(stream$);
// let the content start flowing
stream$.subscribe();
return stream$;
}
export function closeLogstream() {
logstream$?.complete();
logstream$ = undefined;
export async function readLogFile(): Promise<Ecs[]> {
await forceSyncLogFile();
const logFileContent = await Fs.readFile(logFilePath, 'utf-8');
return logFileContent
.split('\n')
.filter(Boolean)
.map<Ecs>((str) => JSON5.parse(str));
}
/**
* Truncates the log file to avoid tests looking at the logs from previous executions.
*/
export async function clearLogFile() {
closeLogstream();
await Fs.writeFile(logFilePath, '', 'utf8');
await forceSyncLogFile();
logstream$ = getLogstream$();
}
/**

View file

@ -7,14 +7,12 @@
import type { Ecs, KibanaExecutionContext } from '@kbn/core/server';
import type { FtrProviderContext } from '../ftr_provider_context';
import { assertLogContains, forceSyncLogFile, isExecutionContextLog } from '../test_utils';
import { assertLogContains, isExecutionContextLog, readLogFile } from '../test_utils';
export default function ({ getService, getPageObjects }: FtrProviderContext) {
const PageObjects = getPageObjects(['common', 'dashboard', 'header', 'home', 'timePicker']);
const retry = getService('retry');
// Failing: See https://github.com/elastic/kibana/issues/149611
describe.skip('Browser apps', () => {
describe('Browser apps', () => {
before(async () => {
await PageObjects.common.navigateToUrl('home', '/tutorial_directory/sampleData', {
useActualUrl: true,
@ -32,11 +30,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
describe('discover app', () => {
let logs: Ecs[];
before(async () => {
await PageObjects.common.navigateToApp('discover');
await PageObjects.timePicker.setCommonlyUsedTime('Last_7 days');
await PageObjects.header.waitUntilLoadingHasFinished();
await forceSyncLogFile();
logs = await readLogFile();
});
function checkExecutionContextEntry(expectedExecutionContext: KibanaExecutionContext) {
@ -56,7 +56,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
description: 'execution context propagates to Elasticsearch via "x-opaque-id" header',
predicate: (record) =>
Boolean(record.http?.request?.id?.includes('kibana:application:discover')),
retry,
logs,
});
});
@ -76,7 +76,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
description: 'fetch documents',
},
}),
retry,
logs,
});
});
@ -104,20 +104,22 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
},
},
}),
retry,
logs,
});
});
});
});
describe('dashboard app', () => {
let logs: Ecs[];
before(async () => {
await PageObjects.common.navigateToApp('dashboard');
await PageObjects.dashboard.loadSavedDashboard('[Flights] Global Flight Dashboard');
await PageObjects.timePicker.setCommonlyUsedTime('Last_7 days');
await PageObjects.dashboard.waitForRenderComplete();
await PageObjects.header.waitUntilLoadingHasFinished();
await forceSyncLogFile();
logs = await readLogFile();
});
function checkHttpRequestId(suffix: string) {
@ -172,7 +174,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
predicate: checkHttpRequestId(
'dashboard:dashboards:7adfa750-4c81-11e8-b3d7-01146121b73d;lens:lnsXY:086ac2e9-dd16-4b45-92b8-1e43ff7e3f65'
),
retry,
logs,
});
});
@ -199,19 +201,19 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
},
},
}),
retry,
logs,
});
});
});
describe('lnsMetric', () => {
describe.skip('lnsMetric', () => {
it('propagates to Elasticsearch via "x-opaque-id" header', async () => {
await assertLogContains({
description: 'execution context propagates to Elasticsearch via "x-opaque-id" header',
predicate: checkHttpRequestId(
'dashboard:dashboards:7adfa750-4c81-11e8-b3d7-01146121b73d;lens:lnsLegacyMetric:b766e3b8-4544-46ed-99e6-9ecc4847e2a2'
),
retry,
logs,
});
});
@ -238,7 +240,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
},
},
}),
retry,
logs,
});
});
});
@ -250,7 +252,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
predicate: checkHttpRequestId(
'dashboard:dashboards:7adfa750-4c81-11e8-b3d7-01146121b73d;lens:lnsDatatable:fb86b32f-fb7a-45cf-9511-f366fef51bbd'
),
retry,
logs,
});
});
@ -277,7 +279,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
},
},
}),
retry,
logs,
});
});
});
@ -289,7 +291,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
predicate: checkHttpRequestId(
'dashboard:dashboards:7adfa750-4c81-11e8-b3d7-01146121b73d;lens:lnsPie:5d53db36-2d5a-4adc-af7b-cec4c1a294e0'
),
retry,
logs,
});
});
@ -316,7 +318,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
},
},
}),
retry,
logs,
});
});
});
@ -329,7 +331,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
predicate: checkHttpRequestId(
'dashboard:dashboards:7adfa750-4c81-11e8-b3d7-01146121b73d;search:discover:571aaf70-4c88-11e8-b3d7-01146121b73d'
),
retry,
logs,
});
});
@ -356,17 +358,17 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
},
},
}),
retry,
logs,
});
});
});
describe('propagates context for TSVB visualizations', () => {
describe.skip('propagates context for TSVB visualizations', () => {
it('propagates to Elasticsearch via "x-opaque-id" header', async () => {
await assertLogContains({
description: 'execution context propagates to Elasticsearch via "x-opaque-id" header',
predicate: checkHttpRequestId('agg_based:metrics:bcb63b50-4c89-11e8-b3d7-01146121b73d'),
retry,
logs,
});
});
@ -387,7 +389,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
url: '/app/visualize#/edit/bcb63b50-4c89-11e8-b3d7-01146121b73d',
},
}),
retry,
logs,
});
});
});
@ -399,7 +401,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
predicate: checkHttpRequestId(
'dashboard:dashboards:7adfa750-4c81-11e8-b3d7-01146121b73d;agg_based:vega:ed78a660-53a0-11e8-acbd-0be0ad9d822b'
),
retry,
logs,
});
});
@ -426,19 +428,19 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
},
},
}),
retry,
logs,
});
});
});
describe('propagates context for Tag Cloud visualization', () => {
describe.skip('propagates context for Tag Cloud visualization', () => {
it('propagates to Elasticsearch via "x-opaque-id" header', async () => {
await assertLogContains({
description: 'execution context propagates to Elasticsearch via "x-opaque-id" header',
predicate: checkHttpRequestId(
'dashboard:dashboards:7adfa750-4c81-11e8-b3d7-01146121b73d;agg_based:tagcloud:293b5a30-4c8f-11e8-b3d7-01146121b73d'
),
retry,
logs,
});
});
@ -465,19 +467,19 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
},
},
}),
retry,
logs,
});
});
});
describe('propagates context for Vertical bar visualization', () => {
describe.skip('propagates context for Vertical bar visualization', () => {
it('propagates to Elasticsearch via "x-opaque-id" header', async () => {
await assertLogContains({
description: 'execution context propagates to Elasticsearch via "x-opaque-id" header',
predicate: checkHttpRequestId(
'dashboard:dashboards:7adfa750-4c81-11e8-b3d7-01146121b73d;agg_based:histogram:9886b410-4c8b-11e8-b3d7-01146121b73d'
),
retry,
logs,
});
});
@ -504,7 +506,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
},
},
}),
retry,
logs,
});
});
});

View file

@ -6,7 +6,7 @@
*/
import { FtrProviderContext } from '../ftr_provider_context';
import { clearLogFile, closeLogstream } from '../test_utils';
import { clearLogFile } from '../test_utils';
export default function ({ loadTestFile }: FtrProviderContext) {
describe('Execution context', function () {
@ -15,9 +15,6 @@ export default function ({ loadTestFile }: FtrProviderContext) {
// If any of the tests rely on logs generating during bootstrap, we might need to change this.
await clearLogFile();
});
after(() => {
closeLogstream();
});
loadTestFile(require.resolve('./browser'));
loadTestFile(require.resolve('./server'));

View file

@ -6,10 +6,9 @@
*/
import expect from '@kbn/expect';
import type { FtrProviderContext } from '../ftr_provider_context';
import { assertLogContains, forceSyncLogFile } from '../test_utils';
import { readLogFile, assertLogContains } from '../test_utils';
export default function ({ getService }: FtrProviderContext) {
const retry = getService('retry');
const supertest = getService('supertest');
describe('Log Correlation', () => {
@ -24,7 +23,7 @@ export default function ({ getService }: FtrProviderContext) {
expect(response2.body.traceId).not.to.be(response1.body.traceId);
await forceSyncLogFile();
const logs = await readLogFile();
let responseTraceId: string | undefined;
await assertLogContains({
@ -41,7 +40,7 @@ export default function ({ getService }: FtrProviderContext) {
}
return false;
},
retry,
logs,
});
expect(responseTraceId).to.be.a('string');
@ -56,7 +55,7 @@ export default function ({ getService }: FtrProviderContext) {
record.message?.includes('HEAD /')
),
retry,
logs,
});
});
});

View file

@ -11,14 +11,13 @@ import {
} from '@kbn/core-http-common';
import expect from '@kbn/expect';
import type { FtrProviderContext } from '../ftr_provider_context';
import { assertLogContains, isExecutionContextLog, ANY } from '../test_utils';
import { readLogFile, assertLogContains, isExecutionContextLog, ANY } from '../test_utils';
function delay(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
export default function ({ getService }: FtrProviderContext) {
const retry = getService('retry');
const supertest = getService('supertest');
const log = getService('log');
@ -69,6 +68,7 @@ export default function ({ getService }: FtrProviderContext) {
const alertId = createdAlert.id;
await waitForStatus(alertId, new Set(['ok']), 90_000);
const logs = await readLogFile();
await assertLogContains({
description:
@ -80,7 +80,7 @@ export default function ({ getService }: FtrProviderContext) {
`kibana:task%20manager:run%20alerting%3Atest.executionContext:`
)
),
retry,
logs,
});
await assertLogContains({
@ -90,7 +90,7 @@ export default function ({ getService }: FtrProviderContext) {
Boolean(
record.http?.request?.id?.includes(`alert:execute%20test.executionContext:${alertId}`)
),
retry,
logs,
});
await assertLogContains({
@ -109,7 +109,7 @@ export default function ({ getService }: FtrProviderContext) {
description: 'execute [test.executionContext] with name [abc] in [default] namespace',
},
}),
retry,
logs,
});
});
@ -122,6 +122,8 @@ export default function ({ getService }: FtrProviderContext) {
.send({ unencrypted: false })
.expect(200);
const logs = await readLogFile();
await assertLogContains({
description:
'usage_collection execution context propagates to Elasticsearch via "x-opaque-id" header',
@ -132,7 +134,7 @@ export default function ({ getService }: FtrProviderContext) {
`kibana:usage_collection:collector.fetch:application_usage`
)
),
retry,
logs,
});
await assertLogContains({
@ -144,7 +146,7 @@ export default function ({ getService }: FtrProviderContext) {
id: 'application_usage',
description: 'Fetch method in the Collector "application_usage"',
}),
retry,
logs,
});
});
});