mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[Security Solution] Tests: Filter by rule execution status (#160502)
**Resolves: https://github.com/elastic/kibana/issues/138903**
## Summary
Adds an E2E Cypress test to check filtering by execution status in the
rules table.
<img width="953" alt="Screenshot 2023-06-26 at 14 10 10"
src="e1eb67ed
-779c-42ad-8194-04a26598cfbc">
This commit is contained in:
parent
8e80f192e3
commit
c30a7d47eb
6 changed files with 189 additions and 2 deletions
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* 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 { cleanKibana, resetRulesTableState, deleteAlertsAndRules } from '../../tasks/common';
|
||||
import { login, visitWithoutDateRange } from '../../tasks/login';
|
||||
import { esArchiverResetKibana } from '../../tasks/es_archiver';
|
||||
import {
|
||||
expectRulesWithExecutionStatus,
|
||||
filterByExecutionStatus,
|
||||
expectNumberOfRulesShownOnPage,
|
||||
} from '../../tasks/rule_filters';
|
||||
|
||||
import { SECURITY_DETECTIONS_RULES_URL } from '../../urls/navigation';
|
||||
|
||||
import { waitForRulesTableToBeLoaded } from '../../tasks/alerts_detection_rules';
|
||||
|
||||
import { createRule, waitForRulesToFinishExecution } from '../../tasks/api_calls/rules';
|
||||
import { deleteIndex, createIndex, createDocument } from '../../tasks/api_calls/elasticsearch';
|
||||
|
||||
import { getNewRule } from '../../objects/rule';
|
||||
|
||||
describe('Rule management filters', () => {
|
||||
before(() => {
|
||||
cleanKibana();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
login();
|
||||
// Make sure persisted rules table state is cleared
|
||||
resetRulesTableState();
|
||||
deleteAlertsAndRules();
|
||||
esArchiverResetKibana();
|
||||
});
|
||||
|
||||
describe('Last response filter', () => {
|
||||
it('Filters rules by last response', function () {
|
||||
deleteIndex('test_index');
|
||||
|
||||
createIndex('test_index', {
|
||||
'@timestamp': {
|
||||
type: 'date',
|
||||
},
|
||||
});
|
||||
|
||||
createDocument('test_index', {});
|
||||
|
||||
createRule(
|
||||
getNewRule({
|
||||
name: 'Successful rule',
|
||||
rule_id: 'successful_rule',
|
||||
index: ['test_index'],
|
||||
})
|
||||
);
|
||||
|
||||
createRule(
|
||||
getNewRule({
|
||||
name: 'Warning rule',
|
||||
rule_id: 'warning_rule',
|
||||
index: ['non_existent_index'],
|
||||
})
|
||||
);
|
||||
|
||||
createRule(
|
||||
getNewRule({
|
||||
name: 'Failed rule',
|
||||
rule_id: 'failed_rule',
|
||||
index: ['test_index'],
|
||||
// Setting a crazy large "Additional look-back time" to force a failure
|
||||
from: 'now-9007199254746990s',
|
||||
})
|
||||
);
|
||||
|
||||
waitForRulesToFinishExecution(['successful_rule', 'warning_rule', 'failed_rule'], new Date());
|
||||
|
||||
visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL);
|
||||
|
||||
waitForRulesTableToBeLoaded();
|
||||
|
||||
// Initial table state - before filtering by status
|
||||
expectNumberOfRulesShownOnPage(3);
|
||||
expectRulesWithExecutionStatus(1, 'Succeeded');
|
||||
expectRulesWithExecutionStatus(1, 'Warning');
|
||||
expectRulesWithExecutionStatus(1, 'Failed');
|
||||
|
||||
// Table state after filtering by Succeeded status
|
||||
filterByExecutionStatus('Succeeded');
|
||||
expectNumberOfRulesShownOnPage(1);
|
||||
expectRulesWithExecutionStatus(1, 'Succeeded');
|
||||
|
||||
// Table state after filtering by Warning status
|
||||
filterByExecutionStatus('Warning');
|
||||
expectNumberOfRulesShownOnPage(1);
|
||||
expectRulesWithExecutionStatus(1, 'Warning');
|
||||
|
||||
// Table state after filtering by Failed status
|
||||
filterByExecutionStatus('Failed');
|
||||
expectNumberOfRulesShownOnPage(1);
|
||||
expectRulesWithExecutionStatus(1, 'Failed');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -176,3 +176,9 @@ export const REFRESH_SETTINGS_SWITCH = '[data-test-subj="refreshSettingsSwitch"]
|
|||
export const REFRESH_SETTINGS_SELECTION_NOTE = '[data-test-subj="refreshSettingsSelectionNote"]';
|
||||
|
||||
export const REFRESH_RULES_STATUS = '[data-test-subj="refreshRulesStatus"]';
|
||||
|
||||
export const RULE_EXECUTION_STATUS_BADGE = '[data-test-subj="ruleExecutionStatus"]';
|
||||
|
||||
export const EXECUTION_STATUS_FILTER_BUTTON = '[data-test-subj="executionStatusFilterButton"]';
|
||||
|
||||
export const EXECUTION_STATUS_FILTER_OPTION = '[data-test-subj="executionStatusFilterOption"]';
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { rootRequest } from '../common';
|
||||
|
||||
export const deleteIndex = (index: string) => {
|
||||
|
@ -16,6 +15,24 @@ export const deleteIndex = (index: string) => {
|
|||
});
|
||||
};
|
||||
|
||||
export const createIndex = (indexName: string, properties: Record<string, unknown>) =>
|
||||
rootRequest({
|
||||
method: 'PUT',
|
||||
url: `${Cypress.env('ELASTICSEARCH_URL')}/${indexName}`,
|
||||
body: {
|
||||
mappings: {
|
||||
properties,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const createDocument = (indexName: string, document: Record<string, unknown>) =>
|
||||
rootRequest({
|
||||
method: 'POST',
|
||||
url: `${Cypress.env('ELASTICSEARCH_URL')}/${indexName}/_doc`,
|
||||
body: document,
|
||||
});
|
||||
|
||||
export const waitForNewDocumentToBeIndexed = (index: string, initialNumberOfDocuments: number) => {
|
||||
cy.waitUntil(
|
||||
() =>
|
||||
|
|
|
@ -7,9 +7,13 @@
|
|||
|
||||
import moment from 'moment';
|
||||
import { rootRequest } from '../common';
|
||||
import { DETECTION_ENGINE_RULES_URL } from '../../../common/constants';
|
||||
import {
|
||||
DETECTION_ENGINE_RULES_URL,
|
||||
DETECTION_ENGINE_RULES_URL_FIND,
|
||||
} from '../../../common/constants';
|
||||
import type { RuleCreateProps, RuleResponse } from '../../../common/detection_engine/rule_schema';
|
||||
import { internalAlertingSnoozeRule } from '../../urls/routes';
|
||||
import type { FetchRulesResponse } from '../../../public/detection_engine/rule_management/logic/types';
|
||||
|
||||
export const createRule = (
|
||||
rule: RuleCreateProps
|
||||
|
@ -72,3 +76,28 @@ export const importRule = (ndjsonPath: string) => {
|
|||
.should('be.equal', 200);
|
||||
});
|
||||
};
|
||||
|
||||
export const waitForRulesToFinishExecution = (ruleIds: string[], afterDate?: Date) =>
|
||||
cy.waitUntil(
|
||||
() =>
|
||||
rootRequest<FetchRulesResponse>({
|
||||
method: 'GET',
|
||||
url: DETECTION_ENGINE_RULES_URL_FIND,
|
||||
}).then((response) => {
|
||||
const areAllRulesFinished = ruleIds.every((ruleId) =>
|
||||
response.body.data.some((rule) => {
|
||||
const ruleExecutionDate = rule.execution_summary?.last_execution?.date;
|
||||
|
||||
const isDateOk = afterDate
|
||||
? !!(ruleExecutionDate && new Date(ruleExecutionDate) > afterDate)
|
||||
: true;
|
||||
|
||||
return (
|
||||
rule.rule_id === ruleId && typeof rule.execution_summary !== 'undefined' && isDateOk
|
||||
);
|
||||
})
|
||||
);
|
||||
return areAllRulesFinished;
|
||||
}),
|
||||
{ interval: 500, timeout: 12000 }
|
||||
);
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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 {
|
||||
RULE_EXECUTION_STATUS_BADGE,
|
||||
EXECUTION_STATUS_FILTER_BUTTON,
|
||||
EXECUTION_STATUS_FILTER_OPTION,
|
||||
} from '../screens/alerts_detection_rules';
|
||||
|
||||
export const expectRulesWithExecutionStatus = (expectedCount: number, status: string) => {
|
||||
cy.get(`${RULE_EXECUTION_STATUS_BADGE}:contains("${status}")`).should(
|
||||
'have.length',
|
||||
expectedCount
|
||||
);
|
||||
};
|
||||
|
||||
export const expectNumberOfRulesShownOnPage = (expectedCount: number) =>
|
||||
cy.get(RULE_EXECUTION_STATUS_BADGE).should('have.length', expectedCount);
|
||||
|
||||
export const filterByExecutionStatus = (status: string) => {
|
||||
cy.get(EXECUTION_STATUS_FILTER_BUTTON).click();
|
||||
cy.get(`${EXECUTION_STATUS_FILTER_OPTION}:contains("${status}")`).click();
|
||||
};
|
|
@ -77,6 +77,7 @@ const RuleExecutionStatusSelectorComponent = ({
|
|||
isSelected={isExecutionStatusPopoverOpen}
|
||||
hasActiveFilters={selectedStatus !== undefined}
|
||||
numActiveFilters={selectedStatus !== undefined ? 1 : 0}
|
||||
data-test-subj="executionStatusFilterButton"
|
||||
>
|
||||
{i18n.COLUMN_LAST_RESPONSE}
|
||||
</EuiFilterButton>
|
||||
|
@ -108,11 +109,13 @@ const RuleExecutionStatusSelectorComponent = ({
|
|||
css={`
|
||||
margin-top: 4px; // aligns the badge within the option
|
||||
`}
|
||||
data-test-subj="executionStatusFilterOption"
|
||||
>
|
||||
<RuleStatusBadge status={status} showTooltip={false} />
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
data-test-subj="executionStatusFilterSelectableList"
|
||||
>
|
||||
{(list) => (
|
||||
<div
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue