Add error handling/retry logic for search source alert tests (#196443)

## Summary

Resolves https://github.com/elastic/kibana/issues/193842.

Adds error handling & retry logic for search source alerts that are
causing failures on MKI.

### Checklist

- [x] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed
This commit is contained in:
Lukas Olson 2024-12-10 13:27:36 -07:00 committed by GitHub
parent 75760bbb13
commit 5acba9678a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 125 additions and 89 deletions

View file

@ -45,53 +45,59 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
let connectorId: string;
const createSourceIndex = () =>
es.index({
index: SOURCE_DATA_VIEW,
body: {
settings: { number_of_shards: 1 },
mappings: {
properties: {
'@timestamp': { type: 'date' },
message: { type: 'keyword' },
},
},
},
});
retry.try(() =>
createIndex(SOURCE_DATA_VIEW, {
'@timestamp': { type: 'date' },
message: { type: 'keyword' },
})
);
const generateNewDocs = async (docsNumber: number) => {
const createOutputDataIndex = () =>
retry.try(() =>
createIndex(OUTPUT_DATA_VIEW, {
rule_id: { type: 'text' },
rule_name: { type: 'text' },
alert_id: { type: 'text' },
context_link: { type: 'text' },
})
);
async function createIndex(index: string, properties: unknown) {
try {
await es.index({
index,
body: {
settings: { number_of_shards: 1 },
mappings: { properties },
},
});
} catch (e) {
log.error(`Failed to create index "${index}" with error "${e.message}"`);
}
}
async function generateNewDocs(docsNumber: number, index = SOURCE_DATA_VIEW) {
const mockMessages = Array.from({ length: docsNumber }, (_, i) => `msg-${i}`);
const dateNow = new Date();
const dateToSet = new Date(dateNow);
dateToSet.setMinutes(dateNow.getMinutes() - 10);
for (const message of mockMessages) {
await es.transport.request({
path: `/${SOURCE_DATA_VIEW}/_doc`,
method: 'POST',
body: {
'@timestamp': dateToSet.toISOString(),
message,
},
});
try {
await Promise.all(
mockMessages.map((message) =>
es.transport.request({
path: `/${index}/_doc`,
method: 'POST',
body: {
'@timestamp': dateToSet.toISOString(),
message,
},
})
)
);
} catch (e) {
log.error(`Failed to generate new docs in "${index}" with error "${e.message}"`);
}
};
const createOutputDataIndex = () =>
es.index({
index: OUTPUT_DATA_VIEW,
body: {
settings: {
number_of_shards: 1,
},
mappings: {
properties: {
rule_id: { type: 'text' },
rule_name: { type: 'text' },
alert_id: { type: 'text' },
context_link: { type: 'text' },
},
},
},
});
}
const deleteAlerts = (alertIds: string[]) =>
asyncForEach(alertIds, async (alertId: string) => {

View file

@ -48,53 +48,59 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
let connectorId: string;
const createSourceIndex = () =>
es.index({
index: SOURCE_DATA_VIEW,
body: {
settings: { number_of_shards: 1 },
mappings: {
properties: {
'@timestamp': { type: 'date' },
message: { type: 'keyword' },
},
},
},
});
retry.try(() =>
createIndex(SOURCE_DATA_VIEW, {
'@timestamp': { type: 'date' },
message: { type: 'keyword' },
})
);
const generateNewDocs = async (docsNumber: number) => {
const createOutputDataIndex = () =>
retry.try(() =>
createIndex(OUTPUT_DATA_VIEW, {
rule_id: { type: 'text' },
rule_name: { type: 'text' },
alert_id: { type: 'text' },
context_link: { type: 'text' },
})
);
async function createIndex(index: string, properties: unknown) {
try {
await es.index({
index,
body: {
settings: { number_of_shards: 1 },
mappings: { properties },
},
});
} catch (e) {
log.error(`Failed to create index "${index}" with error "${e.message}"`);
}
}
async function generateNewDocs(docsNumber: number, index = SOURCE_DATA_VIEW) {
const mockMessages = Array.from({ length: docsNumber }, (_, i) => `msg-${i}`);
const dateNow = new Date();
const dateToSet = new Date(dateNow);
dateToSet.setMinutes(dateNow.getMinutes() - 10);
for (const message of mockMessages) {
await es.transport.request({
path: `/${SOURCE_DATA_VIEW}/_doc`,
method: 'POST',
body: {
'@timestamp': dateToSet.toISOString(),
message,
},
});
try {
await Promise.all(
mockMessages.map((message) =>
es.transport.request({
path: `/${index}/_doc`,
method: 'POST',
body: {
'@timestamp': dateToSet.toISOString(),
message,
},
})
)
);
} catch (e) {
log.error(`Failed to generate new docs in "${index}" with error "${e.message}"`);
}
};
const createOutputDataIndex = () =>
es.index({
index: OUTPUT_DATA_VIEW,
body: {
settings: {
number_of_shards: 1,
},
mappings: {
properties: {
rule_id: { type: 'text' },
rule_name: { type: 'text' },
alert_id: { type: 'text' },
context_link: { type: 'text' },
},
},
},
});
}
const deleteAlerts = (alertIds: string[]) =>
asyncForEach(alertIds, async (alertId: string) => {
@ -216,7 +222,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const openDiscoverAlertFlyout = async () => {
await testSubjects.click('discoverAlertsButton');
await testSubjects.click('discoverCreateAlertButton');
// Different create rule buttons in serverless
if (await testSubjects.exists('discoverCreateAlertButton')) {
await testSubjects.click('discoverCreateAlertButton');
} else {
await testSubjects.click('discoverAppMenuCustomThresholdRule');
}
};
const openManagementAlertFlyout = async () => {
@ -366,8 +377,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
};
describe('Search source Alert', function () {
// see details: https://github.com/elastic/kibana/issues/193842
this.tags(['failsOnMKI', 'skipSvlOblt']);
// Failing: https://github.com/elastic/kibana/issues/203045
this.tags(['skipSvlOblt']);
before(async () => {
await security.testUser.setRoles(['discover_alert']);
await PageObjects.svlCommonPage.loginAsAdmin();
@ -502,7 +514,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await testSubjects.click('thresholdPopover');
await testSubjects.setValue('alertThresholdInput0', '1');
await testSubjects.click('saveEditedRuleButton');
// Different save buttons in serverless
if (await testSubjects.exists('saveEditedRuleButton')) {
await testSubjects.click('saveEditedRuleButton');
} else {
await testSubjects.click('rulePageFooterSaveButton');
}
await PageObjects.header.waitUntilLoadingHasFinished();
await openAlertResults(RULE_NAME);
@ -652,8 +670,15 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.header.waitUntilLoadingHasFinished();
await retry.waitFor('rule name value is correct', async () => {
await testSubjects.setValue('ruleNameInput', newAlert);
const ruleName = await testSubjects.getAttribute('ruleNameInput', 'value');
let ruleName;
// Rule name input is different in serverless
if (await testSubjects.exists('ruleNameInput')) {
await testSubjects.setValue('ruleNameInput', newAlert);
ruleName = await testSubjects.getAttribute('ruleNameInput', 'value');
} else {
await testSubjects.setValue('ruleDetailsNameInput', newAlert);
ruleName = await testSubjects.getAttribute('ruleDetailsNameInput', 'value');
}
return ruleName === newAlert;
});
@ -677,7 +702,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await comboBox.set('ruleFormConsumerSelect', 'Stack Rules');
}
await testSubjects.click('saveRuleButton');
// Save rule button is different in serverless
if (await testSubjects.exists('saveRuleButton')) {
await testSubjects.click('saveRuleButton');
} else {
await testSubjects.click('rulePageFooterSaveButton');
}
await retry.waitFor('confirmation modal', async () => {
return await testSubjects.exists('confirmModalConfirmButton');