From 2b34a6e14afe1202abf4d6680d23c4ab4a214f33 Mon Sep 17 00:00:00 2001 From: Juan Pablo Djeredjian Date: Tue, 17 Oct 2023 13:05:49 +0200 Subject: [PATCH] [Security Solution] Install/Update Prebuilt Rules Test Implementation refactor (#165488) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: https://github.com/elastic/kibana/issues/148192 (Tick the two open checkboxes in that issue when merging this PR) ## Summary This PR rewrites/refactors Cypress tests for the Installation and Upgrade of Prebuilt Rules implemented in https://github.com/elastic/kibana/pull/161687. Most of the changes here address feedback received in that PR - answered those comments there. - RBAC/Authorization: adds tests scenarios for users with full privileges (happy path) - Gets rid of huge util helpers such as `assertRuleAvailableForInstallAndInstallOne` and rewrites test cases in a more descriptive way, with step by step actions. - Gets rid of complex logic in tests and their helpers - removing if/else logic within them and removing optional flags passed to helpers. - Fixes `bulkCreateRuleAssets` util and uses it in other helpers to install multiple `security-rule` assets with a single bulk request to ES. Additionally: checked `installation_and_upgrade.md` test plan to make sure it matches with the test in place. Added [link](https://github.com/elastic/kibana/issues/166215) to a ticket for a to-do task for the sections: - Rule installation workflow: filtering, sorting, pagination - Rule upgrade workflow: filtering, sorting, pagination ## Flaky test runner ~~https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/3420~~ ~~🟢~~ https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/3513 --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> (cherry picked from commit 24c008b4c5026dd543f1b4aded94f3787bce5fb0) # Conflicts: # x-pack/test/security_solution_cypress/package.json --- .../installation_and_upgrade.md | 4 +- .../use_add_prebuilt_rules_table_columns.tsx | 10 +- ...e_upgrade_prebuilt_rules_table_columns.tsx | 10 +- ...t_rules_install_update_authorization.cy.ts | 132 +++++++--- ..._rules_install_update_error_handling.cy.ts | 138 ++++++---- ...built_rules_install_update_workflows.cy.ts | 126 +++++---- .../prebuilt_rules_management.cy.ts | 2 +- .../prebuilt_rules_notifications.cy.ts | 18 +- .../bulk_actions/bulk_edit_rules.cy.ts | 6 +- .../bulk_edit_rules_actions.cy.ts | 2 +- .../import_export/export_rule.cy.ts | 6 +- .../rules_table/rules_table_selection.cy.ts | 2 +- .../cypress/screens/alerts_detection_rules.ts | 12 +- .../cypress/tasks/api_calls/prebuilt_rules.ts | 99 +++---- .../cypress/tasks/common.ts | 1 + .../cypress/tasks/prebuilt_rules.ts | 242 ++++++------------ 16 files changed, 438 insertions(+), 372 deletions(-) diff --git a/x-pack/plugins/security_solution/docs/testing/test_plans/detection_response/prebuilt_rules/installation_and_upgrade.md b/x-pack/plugins/security_solution/docs/testing/test_plans/detection_response/prebuilt_rules/installation_and_upgrade.md index 09198dd2d9ef8..b8ae0c85c82e6 100644 --- a/x-pack/plugins/security_solution/docs/testing/test_plans/detection_response/prebuilt_rules/installation_and_upgrade.md +++ b/x-pack/plugins/security_solution/docs/testing/test_plans/detection_response/prebuilt_rules/installation_and_upgrade.md @@ -431,7 +431,7 @@ And user should see a CTA that leads to the Rule Management page ### Rule installation workflow: filtering, sorting, pagination -TODO: add scenarios +TODO: add scenarios https://github.com/elastic/kibana/issues/166215 ### Rule installation workflow: misc cases @@ -515,7 +515,7 @@ And user should NOT see the Rule Updates table ### Rule upgrade workflow: filtering, sorting, pagination -TODO: add scenarios +TODO: add scenarios https://github.com/elastic/kibana/issues/166215 ### Rule upgrade workflow: misc cases diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/add_prebuilt_rules_table/use_add_prebuilt_rules_table_columns.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/add_prebuilt_rules_table/use_add_prebuilt_rules_table_columns.tsx index 275dc1276cb5a..4376014ab110e 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/add_prebuilt_rules_table/use_add_prebuilt_rules_table_columns.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/add_prebuilt_rules_table/use_add_prebuilt_rules_table_columns.tsx @@ -42,6 +42,7 @@ const RuleName = ({ name, ruleId }: RuleNameProps) => { onClick={() => { openRulePreview(ruleId); }} + data-test-subj="ruleName" > {name} @@ -121,7 +122,14 @@ const createInstallButtonColumn = ( onClick={() => installOneRule(ruleId)} data-test-subj={`installSinglePrebuiltRuleButton-${ruleId}`} > - {isRuleInstalling ? : i18n.INSTALL_RULE_BUTTON} + {isRuleInstalling ? ( + + ) : ( + i18n.INSTALL_RULE_BUTTON + )} ); }, diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/use_upgrade_prebuilt_rules_table_columns.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/use_upgrade_prebuilt_rules_table_columns.tsx index 054d77cb5e441..3f72208dc51bf 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/use_upgrade_prebuilt_rules_table_columns.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/use_upgrade_prebuilt_rules_table_columns.tsx @@ -40,6 +40,7 @@ const RuleName = ({ name, ruleId }: RuleNameProps) => { onClick={() => { openRulePreview(ruleId); }} + data-test-subj="ruleName" > {name} @@ -120,7 +121,14 @@ const createUpgradeButtonColumn = ( onClick={() => upgradeOneRule(ruleId)} data-test-subj={`upgradeSinglePrebuiltRuleButton-${ruleId}`} > - {isRuleUpgrading ? : i18n.UPDATE_RULE_BUTTON} + {isRuleUpgrading ? ( + + ) : ( + i18n.UPDATE_RULE_BUTTON + )} ); }, diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_install_update_authorization.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_install_update_authorization.cy.ts index 63e1aa776cd07..f95a4274a5181 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_install_update_authorization.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_install_update_authorization.cy.ts @@ -13,23 +13,30 @@ import { import { ROLES } from '@kbn/security-solution-plugin/common/test'; import { createRuleAssetSavedObject } from '../../../helpers/rules'; -import { createAndInstallMockedPrebuiltRules } from '../../../tasks/api_calls/prebuilt_rules'; -import { resetRulesTableState, deleteAlertsAndRules } from '../../../tasks/common'; -import { login } from '../../../tasks/login'; +import { + createAndInstallMockedPrebuiltRules, + installPrebuiltRuleAssets, + preventPrebuiltRulesPackageInstallation, +} from '../../../tasks/api_calls/prebuilt_rules'; import { visit } from '../../../tasks/navigation'; import { RULES_MANAGEMENT_URL } from '../../../urls/rules_management'; import { ADD_ELASTIC_RULES_BTN, getInstallSingleRuleButtonByRuleId, getUpgradeSingleRuleButtonByRuleId, + GO_BACK_TO_RULES_TABLE_BUTTON, INSTALL_ALL_RULES_BUTTON, + RULES_MANAGEMENT_TAB, + RULES_MANAGEMENT_TABLE, RULES_UPDATES_TAB, RULE_CHECKBOX, UPGRADE_ALL_RULES_BUTTON, } from '../../../screens/alerts_detection_rules'; +import { cleanKibana } from '../../../tasks/common'; +import { login } from '../../../tasks/login'; +// Rule to test update const RULE_1_ID = 'rule_1'; -const RULE_2_ID = 'rule_2'; const OUTDATED_RULE_1 = createRuleAssetSavedObject({ name: 'Outdated rule 1', rule_id: RULE_1_ID, @@ -40,22 +47,25 @@ const UPDATED_RULE_1 = createRuleAssetSavedObject({ rule_id: RULE_1_ID, version: 2, }); -const OUTDATED_RULE_2 = createRuleAssetSavedObject({ - name: 'Outdated rule 2', + +// Rule to test installation +const RULE_2_ID = 'rule_2'; +const RULE_2 = createRuleAssetSavedObject({ + name: 'Rule 2', rule_id: RULE_2_ID, version: 1, }); -const UPDATED_RULE_2 = createRuleAssetSavedObject({ - name: 'Updated rule 2', - rule_id: RULE_2_ID, - version: 2, -}); const loadPageAsReadOnlyUser = (url: string) => { login(ROLES.reader); visit(url, { role: ROLES.reader }); }; +const loginPageAsWriteAuthorizedUser = (url: string) => { + login(ROLES.hunter); + visit(url); +}; + // TODO: https://github.com/elastic/kibana/issues/164451 We should find a way to make this spec work in Serverless // TODO: https://github.com/elastic/kibana/issues/161540 describe( @@ -63,29 +73,18 @@ describe( { tags: ['@ess', '@serverless', '@skipInServerless'] }, () => { beforeEach(() => { - login(); - resetRulesTableState(); - deleteAlertsAndRules(); - cy.task('esArchiverResetKibana'); - createAndInstallMockedPrebuiltRules({ rules: [OUTDATED_RULE_1, OUTDATED_RULE_2] }); + preventPrebuiltRulesPackageInstallation(); + cleanKibana(); }); describe('User with read privileges on Security Solution', () => { - const RULE_1 = createRuleAssetSavedObject({ - name: 'Test rule 1', - rule_id: 'rule_1', - }); - const RULE_2 = createRuleAssetSavedObject({ - name: 'Test rule 2', - rule_id: 'rule_2', - }); - beforeEach(() => { + it('should not be able to install prebuilt rules', () => { + // Install one prebuilt rule asset to assert that user can't install it + installPrebuiltRuleAssets([RULE_2]); + // Now login with read-only user in preparation for test - createAndInstallMockedPrebuiltRules({ rules: [RULE_1, RULE_2], installToKibana: false }); loadPageAsReadOnlyUser(RULES_MANAGEMENT_URL); - }); - it('should not be able to install prebuilt rules', () => { // Check that Add Elastic Rules button is disabled cy.get(ADD_ELASTIC_RULES_BTN).should('be.disabled'); @@ -94,25 +93,21 @@ describe( // installation buttons are disabled cy.visit(`${APP_PATH}${RULES_ADD_PATH}`); cy.get(INSTALL_ALL_RULES_BUTTON).should('be.disabled'); - cy.get(getInstallSingleRuleButtonByRuleId(RULE_1['security-rule'].rule_id)).should( + cy.get(getInstallSingleRuleButtonByRuleId(UPDATED_RULE_1['security-rule'].rule_id)).should( 'not.exist' ); cy.get(RULE_CHECKBOX).should('not.exist'); }); - }); - describe('User with read privileges on Security Solution', () => { - beforeEach(() => { - /* Create a second version of the rule, making it available for update */ - createAndInstallMockedPrebuiltRules({ - rules: [UPDATED_RULE_1, UPDATED_RULE_2], - installToKibana: false, - }); + it('should not be able to upgrade prebuilt rules', () => { + // Install one prebuilt rule asset to assert that user can't upgrade it + createAndInstallMockedPrebuiltRules([OUTDATED_RULE_1]); + // Create a new version of the rule to make it available for upgrade + installPrebuiltRuleAssets([UPDATED_RULE_1]); + // Now login with read-only user in preparation for test loadPageAsReadOnlyUser(RULES_MANAGEMENT_URL); - }); - it('should not be able to upgrade prebuilt rules', () => { // Check that Rule Update tab is not shown cy.get(RULES_UPDATES_TAB).should('not.exist'); @@ -121,11 +116,70 @@ describe( // upgrade buttons are disabled cy.visit(`${APP_PATH}${RULES_UPDATES}`); cy.get(UPGRADE_ALL_RULES_BUTTON).should('be.disabled'); + + // Upgrade button and selection checkbox should not be visible cy.get(getUpgradeSingleRuleButtonByRuleId(OUTDATED_RULE_1['security-rule'].rule_id)).should( 'not.exist' ); cy.get(RULE_CHECKBOX).should('not.exist'); }); }); + + describe('User with write privileges on Security Solution', () => { + it('should be able to install prebuilt rules', () => { + // Install one prebuilt rule asset to assert that user can install it + installPrebuiltRuleAssets([RULE_2]); + loginPageAsWriteAuthorizedUser(RULES_MANAGEMENT_URL); + + // Check that Add Elastic Rules button is enabled + cy.get(ADD_ELASTIC_RULES_BTN).should('not.be.disabled'); + + // Navigate to Add Elastic Rules page and assert that rules can be selected + // and all installation buttons are enabled + cy.get(ADD_ELASTIC_RULES_BTN).click(); + cy.get(INSTALL_ALL_RULES_BUTTON).should('not.be.disabled'); + cy.get(getInstallSingleRuleButtonByRuleId(RULE_2['security-rule'].rule_id)).should('exist'); + cy.get(RULE_CHECKBOX).should('exist'); + + // Install all available prebuilt rules + cy.get(INSTALL_ALL_RULES_BUTTON).click(); + + // Rule shouldn't be available for installation anymore + cy.get(getInstallSingleRuleButtonByRuleId(RULE_2['security-rule'].rule_id)).should( + 'not.exist' + ); + + // Navigate back to rules table and assert rule is installed + cy.get(GO_BACK_TO_RULES_TABLE_BUTTON).click(); + cy.get(RULES_MANAGEMENT_TABLE).contains(RULE_2['security-rule'].name); + }); + + it('should be able to upgrade prebuilt rules', () => { + // Install one prebuilt rule asset to assert that user can upgrade it + createAndInstallMockedPrebuiltRules([OUTDATED_RULE_1]); + // Create a new version of the rule to make it available for upgrade + installPrebuiltRuleAssets([UPDATED_RULE_1]); + loginPageAsWriteAuthorizedUser(RULES_MANAGEMENT_URL); + + // Check that Rule Update tab is shown + cy.get(RULES_UPDATES_TAB).should('exist'); + + // Navigate to Rule Update tab and assert that rules can be selected + // and all upgrade buttons are enabled + cy.get(RULES_UPDATES_TAB).click(); + cy.get(UPGRADE_ALL_RULES_BUTTON).should('not.be.disabled'); + cy.get(RULE_CHECKBOX).should('exist'); + cy.get(getUpgradeSingleRuleButtonByRuleId(OUTDATED_RULE_1['security-rule'].rule_id)).should( + 'exist' + ); + + // Upgrade the rule and assert that it's upgraded + cy.get( + getUpgradeSingleRuleButtonByRuleId(OUTDATED_RULE_1['security-rule'].rule_id) + ).click(); + cy.get(RULES_MANAGEMENT_TAB).click(); + cy.get(RULES_MANAGEMENT_TABLE).contains(UPDATED_RULE_1['security-rule'].name); + }); + }); } ); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_install_update_error_handling.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_install_update_error_handling.cy.ts index a0004572f8608..effb732489849 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_install_update_error_handling.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_install_update_error_handling.cy.ts @@ -6,20 +6,32 @@ */ import { createRuleAssetSavedObject } from '../../../helpers/rules'; -import { createAndInstallMockedPrebuiltRules } from '../../../tasks/api_calls/prebuilt_rules'; -import { resetRulesTableState, deleteAlertsAndRules } from '../../../tasks/common'; +import { + getInstallSingleRuleButtonByRuleId, + getUpgradeSingleRuleButtonByRuleId, + INSTALL_ALL_RULES_BUTTON, + INSTALL_SELECTED_RULES_BUTTON, + SELECT_ALL_RULES_ON_PAGE_CHECKBOX, + UPGRADE_ALL_RULES_BUTTON, + UPGRADE_SELECTED_RULES_BUTTON, +} from '../../../screens/alerts_detection_rules'; +import { selectRulesByName } from '../../../tasks/alerts_detection_rules'; +import { + installPrebuiltRuleAssets, + createAndInstallMockedPrebuiltRules, + preventPrebuiltRulesPackageInstallation, +} from '../../../tasks/api_calls/prebuilt_rules'; +import { cleanKibana } from '../../../tasks/common'; import { login } from '../../../tasks/login'; import { addElasticRulesButtonClick, - assertRuleAvailableForInstallAndInstallOne, - assertRuleAvailableForInstallAndInstallSelected, - assertRuleAvailableForInstallAndInstallAllInPage, - assertRuleAvailableForInstallAndInstallAll, - assertRuleUpgradeAvailableAndUpgradeOne, - assertRuleUpgradeAvailableAndUpgradeSelected, - assertRuleUpgradeAvailableAndUpgradeAllInPage, - assertRuleUpgradeAvailableAndUpgradeAll, + assertInstallationRequestIsComplete, + interceptInstallationRequestToFail, + interceptUpgradeRequestToFail, + assertUpgradeFailure, ruleUpdatesTabClick, + assertInstallationFailure, + assertUpgradeRequestIsComplete, } from '../../../tasks/prebuilt_rules'; import { visitRulesManagementTable } from '../../../tasks/rules_management'; @@ -28,10 +40,9 @@ describe( { tags: ['@ess', '@serverless'] }, () => { beforeEach(() => { + preventPrebuiltRulesPackageInstallation(); + cleanKibana(); login(); - resetRulesTableState(); - deleteAlertsAndRules(); - cy.task('esArchiverResetKibana'); visitRulesManagementTable(); }); @@ -45,37 +56,52 @@ describe( name: 'Test rule 2', rule_id: 'rule_2', }); + beforeEach(() => { - createAndInstallMockedPrebuiltRules({ rules: [RULE_1, RULE_2], installToKibana: false }); + // Make two mock rules available for installation + installPrebuiltRuleAssets([RULE_1, RULE_2]); }); it('installing prebuilt rules one by one', () => { + // Navigate to install Elastic rules page addElasticRulesButtonClick(); - assertRuleAvailableForInstallAndInstallOne({ rules: [RULE_1], didRequestFail: true }); + + // Intercept and force the installation request to fail + interceptInstallationRequestToFail([RULE_1]); + + // Attempt to install rule + cy.get(getInstallSingleRuleButtonByRuleId(RULE_1['security-rule'].rule_id)).click(); + // Wait for request to complete + assertInstallationRequestIsComplete([RULE_1]); + + assertInstallationFailure([RULE_1]); }); it('installing multiple selected prebuilt rules by selecting them individually', () => { addElasticRulesButtonClick(); - assertRuleAvailableForInstallAndInstallSelected({ - rules: [RULE_1, RULE_2], - didRequestFail: true, - }); + + interceptInstallationRequestToFail([RULE_1, RULE_2]); + selectRulesByName([RULE_1['security-rule'].name, RULE_2['security-rule'].name]); + cy.get(INSTALL_SELECTED_RULES_BUTTON).click(); + assertInstallationRequestIsComplete([RULE_1, RULE_2]); + assertInstallationFailure([RULE_1, RULE_2]); }); it('installing multiple selected prebuilt rules by selecting all in page', () => { addElasticRulesButtonClick(); - assertRuleAvailableForInstallAndInstallAllInPage({ - rules: [RULE_1, RULE_2], - didRequestFail: true, - }); + interceptInstallationRequestToFail([RULE_1, RULE_2]); + cy.get(SELECT_ALL_RULES_ON_PAGE_CHECKBOX).click(); + cy.get(INSTALL_SELECTED_RULES_BUTTON).click(); + assertInstallationRequestIsComplete([RULE_1, RULE_2]); + assertInstallationFailure([RULE_1, RULE_2]); }); it('installing all available rules at once', () => { addElasticRulesButtonClick(); - assertRuleAvailableForInstallAndInstallAll({ - rules: [RULE_1, RULE_2], - didRequestFail: true, - }); + interceptInstallationRequestToFail([RULE_1, RULE_2]); + cy.get(INSTALL_ALL_RULES_BUTTON).click(); + assertInstallationRequestIsComplete([RULE_1, RULE_2]); + assertInstallationFailure([RULE_1, RULE_2]); }); }); @@ -102,44 +128,66 @@ describe( rule_id: RULE_2_ID, version: 2, }); + beforeEach(() => { /* Create a new rule and install it */ - createAndInstallMockedPrebuiltRules({ rules: [OUTDATED_RULE_1, OUTDATED_RULE_2] }); + createAndInstallMockedPrebuiltRules([OUTDATED_RULE_1, OUTDATED_RULE_2]); /* Create a second version of the rule, making it available for update */ - createAndInstallMockedPrebuiltRules({ - rules: [UPDATED_RULE_1, UPDATED_RULE_2], - installToKibana: false, - }); + installPrebuiltRuleAssets([UPDATED_RULE_1, UPDATED_RULE_2]); cy.reload(); }); it('upgrading prebuilt rules one by one', () => { + interceptUpgradeRequestToFail([OUTDATED_RULE_1]); + + // Navigate to Rule Upgrade table ruleUpdatesTabClick(); - assertRuleUpgradeAvailableAndUpgradeOne({ rules: [OUTDATED_RULE_1], didRequestFail: true }); + + // Attempt to upgrade rule + cy.get( + getUpgradeSingleRuleButtonByRuleId(OUTDATED_RULE_1['security-rule'].rule_id) + ).click(); + // Wait for request to complete + assertUpgradeRequestIsComplete([OUTDATED_RULE_1]); + + assertUpgradeFailure([OUTDATED_RULE_1]); }); it('upgrading multiple selected prebuilt rules by selecting them individually', () => { + interceptUpgradeRequestToFail([OUTDATED_RULE_1, OUTDATED_RULE_2]); + + // Navigate to Rule Upgrade table ruleUpdatesTabClick(); - assertRuleUpgradeAvailableAndUpgradeSelected({ - rules: [OUTDATED_RULE_1, OUTDATED_RULE_2], - didRequestFail: true, - }); + + selectRulesByName([ + OUTDATED_RULE_1['security-rule'].name, + OUTDATED_RULE_2['security-rule'].name, + ]); + cy.get(UPGRADE_SELECTED_RULES_BUTTON).click(); + assertUpgradeRequestIsComplete([OUTDATED_RULE_1, OUTDATED_RULE_2]); + assertUpgradeFailure([OUTDATED_RULE_1, OUTDATED_RULE_2]); }); it('upgrading multiple selected prebuilt rules by selecting all in page', () => { + interceptUpgradeRequestToFail([OUTDATED_RULE_1, OUTDATED_RULE_2]); + + // Navigate to Rule Upgrade table ruleUpdatesTabClick(); - assertRuleUpgradeAvailableAndUpgradeAllInPage({ - rules: [OUTDATED_RULE_1, OUTDATED_RULE_2], - didRequestFail: true, - }); + cy.get(SELECT_ALL_RULES_ON_PAGE_CHECKBOX).click(); + cy.get(UPGRADE_SELECTED_RULES_BUTTON).click(); + assertUpgradeRequestIsComplete([OUTDATED_RULE_1, OUTDATED_RULE_2]); + assertUpgradeFailure([OUTDATED_RULE_1, OUTDATED_RULE_2]); }); it('upgrading all rules with available upgrades at once', () => { + interceptUpgradeRequestToFail([OUTDATED_RULE_1, OUTDATED_RULE_2]); + + // Navigate to Rule Upgrade table ruleUpdatesTabClick(); - assertRuleUpgradeAvailableAndUpgradeAll({ - rules: [OUTDATED_RULE_1, OUTDATED_RULE_2], - didRequestFail: true, - }); + + cy.get(UPGRADE_ALL_RULES_BUTTON).click(); + assertUpgradeRequestIsComplete([OUTDATED_RULE_1, OUTDATED_RULE_2]); + assertUpgradeFailure([OUTDATED_RULE_1, OUTDATED_RULE_2]); }); }); } diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_install_update_workflows.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_install_update_workflows.cy.ts index 5c8ff2a4d4963..68830ff0611a8 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_install_update_workflows.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_install_update_workflows.cy.ts @@ -10,33 +10,35 @@ import type { Rule } from '@kbn/security-solution-plugin/public/detection_engine import { createRuleAssetSavedObject } from '../../../helpers/rules'; import { + getInstallSingleRuleButtonByRuleId, + getUpgradeSingleRuleButtonByRuleId, GO_BACK_TO_RULES_TABLE_BUTTON, INSTALL_ALL_RULES_BUTTON, INSTALL_SELECTED_RULES_BUTTON, - NO_RULES_AVAILABLE_FOR_INSTALL_MESSSAGE, - NO_RULES_AVAILABLE_FOR_UPGRADE_MESSSAGE, + NO_RULES_AVAILABLE_FOR_INSTALL_MESSAGE, + NO_RULES_AVAILABLE_FOR_UPGRADE_MESSAGE, RULES_UPDATES_TAB, RULE_CHECKBOX, SELECT_ALL_RULES_ON_PAGE_CHECKBOX, TOASTER, + UPGRADE_ALL_RULES_BUTTON, + UPGRADE_SELECTED_RULES_BUTTON, } from '../../../screens/alerts_detection_rules'; +import { selectRulesByName } from '../../../tasks/alerts_detection_rules'; import { getRuleAssets, + installPrebuiltRuleAssets, createAndInstallMockedPrebuiltRules, } from '../../../tasks/api_calls/prebuilt_rules'; import { resetRulesTableState, deleteAlertsAndRules } from '../../../tasks/common'; import { login } from '../../../tasks/login'; import { addElasticRulesButtonClick, - assertRuleAvailableForInstallAndInstallOne, - assertRuleAvailableForInstallAndInstallSelected, - assertRuleAvailableForInstallAndInstallAllInPage, - assertRuleAvailableForInstallAndInstallAll, - assertRuleUpgradeAvailableAndUpgradeOne, - assertRuleUpgradeAvailableAndUpgradeSelected, - assertRuleUpgradeAvailableAndUpgradeAllInPage, - assertRuleUpgradeAvailableAndUpgradeAll, ruleUpdatesTabClick, + assertInstallationSuccess, + assertInstallationRequestIsComplete, + assertUpgradeRequestIsComplete, + assertUpgradeSuccess, } from '../../../tasks/prebuilt_rules'; import { visitRulesManagementTable } from '../../../tasks/rules_management'; @@ -99,11 +101,17 @@ describe( }); it('should install rules from the Fleet package when user clicks on CTA', () => { + interface Response { + body: { + hits: { + hits: Array<{ _source: { ['security-rule']: Rule } }>; + }; + }; + } const getRulesAndAssertNumberInstalled = () => { getRuleAssets().then((response) => { - const ruleIds = response.body.hits.hits.map( - (hit: { _source: { ['security-rule']: Rule } }) => - hit._source['security-rule'].rule_id + const ruleIds = (response as Response).body.hits.hits.map( + (hit) => hit._source['security-rule'].rule_id ); const numberOfRulesToInstall = new Set(ruleIds).size; @@ -147,52 +155,49 @@ describe( rule_id: 'rule_2', }); beforeEach(() => { - createAndInstallMockedPrebuiltRules({ rules: [RULE_1, RULE_2], installToKibana: false }); + installPrebuiltRuleAssets([RULE_1, RULE_2]); cy.intercept('POST', '/internal/detection_engine/prebuilt_rules/installation/_perform').as( 'installPrebuiltRules' ); + addElasticRulesButtonClick(); }); it('should install prebuilt rules one by one', () => { - addElasticRulesButtonClick(); - assertRuleAvailableForInstallAndInstallOne({ rules: [RULE_1] }); + // Attempt to install rules + cy.get(getInstallSingleRuleButtonByRuleId(RULE_1['security-rule'].rule_id)).click(); + // Wait for request to complete + assertInstallationRequestIsComplete([RULE_1]); + // Assert installation succeeded + assertInstallationSuccess([RULE_1]); }); it('should install multiple selected prebuilt rules by selecting them individually', () => { - addElasticRulesButtonClick(); - assertRuleAvailableForInstallAndInstallSelected({ rules: [RULE_1, RULE_2] }); + selectRulesByName([RULE_1['security-rule'].name, RULE_2['security-rule'].name]); + cy.get(INSTALL_SELECTED_RULES_BUTTON).click(); + assertInstallationRequestIsComplete([RULE_1, RULE_2]); + assertInstallationSuccess([RULE_1, RULE_2]); }); it('should install multiple selected prebuilt rules by selecting all in page', () => { - addElasticRulesButtonClick(); - assertRuleAvailableForInstallAndInstallAllInPage({ rules: [RULE_1, RULE_2] }); + cy.get(SELECT_ALL_RULES_ON_PAGE_CHECKBOX).click(); + cy.get(INSTALL_SELECTED_RULES_BUTTON).click(); + assertInstallationRequestIsComplete([RULE_1, RULE_2]); + assertInstallationSuccess([RULE_1, RULE_2]); }); it('should install all available rules at once', () => { - addElasticRulesButtonClick(); - assertRuleAvailableForInstallAndInstallAll({ rules: [RULE_1, RULE_2] }); + cy.get(INSTALL_ALL_RULES_BUTTON).click(); + assertInstallationRequestIsComplete([RULE_1, RULE_2]); + assertInstallationSuccess([RULE_1, RULE_2]); }); it('should display an empty screen when all available prebuilt rules have been installed', () => { - addElasticRulesButtonClick(); cy.get(INSTALL_ALL_RULES_BUTTON).click(); cy.get(TOASTER).should('be.visible').should('have.text', `2 rules installed successfully.`); cy.get(RULE_CHECKBOX).should('not.exist'); - cy.get(NO_RULES_AVAILABLE_FOR_INSTALL_MESSSAGE).should('exist'); + cy.get(NO_RULES_AVAILABLE_FOR_INSTALL_MESSAGE).should('exist'); cy.get(GO_BACK_TO_RULES_TABLE_BUTTON).should('exist'); }); - - it('should fail gracefully with toast error message when request to install rules fails', () => { - /* Stub request to force rules installation to fail */ - cy.intercept('POST', '/internal/detection_engine/prebuilt_rules/installation/_perform', { - statusCode: 500, - }).as('installPrebuiltRules'); - addElasticRulesButtonClick(); - cy.get(SELECT_ALL_RULES_ON_PAGE_CHECKBOX).click(); - cy.get(INSTALL_SELECTED_RULES_BUTTON).click(); - cy.wait('@installPrebuiltRules'); - cy.get(TOASTER).should('be.visible').should('have.text', 'Rule installation failed'); - }); }); describe('Upgrade of prebuilt rules', () => { @@ -223,43 +228,52 @@ describe( 'updatePrebuiltRules' ); /* Create a new rule and install it */ - createAndInstallMockedPrebuiltRules({ rules: [OUTDATED_RULE_1, OUTDATED_RULE_2] }); + createAndInstallMockedPrebuiltRules([OUTDATED_RULE_1, OUTDATED_RULE_2]); /* Create a second version of the rule, making it available for update */ - createAndInstallMockedPrebuiltRules({ - rules: [UPDATED_RULE_1, UPDATED_RULE_2], - installToKibana: false, - }); - cy.reload(); + installPrebuiltRuleAssets([UPDATED_RULE_1, UPDATED_RULE_2]); + + visitRulesManagementTable(); + ruleUpdatesTabClick(); }); it('should upgrade prebuilt rules one by one', () => { - ruleUpdatesTabClick(); - assertRuleUpgradeAvailableAndUpgradeOne({ rules: [OUTDATED_RULE_1] }); + // Attempt to upgrade rule + cy.get( + getUpgradeSingleRuleButtonByRuleId(OUTDATED_RULE_1['security-rule'].rule_id) + ).click(); + // Wait for request to complete + assertUpgradeRequestIsComplete([OUTDATED_RULE_1]); + + assertUpgradeSuccess([OUTDATED_RULE_1]); }); it('should upgrade multiple selected prebuilt rules by selecting them individually', () => { - ruleUpdatesTabClick(); - assertRuleUpgradeAvailableAndUpgradeSelected({ rules: [OUTDATED_RULE_1, OUTDATED_RULE_2] }); + selectRulesByName([ + OUTDATED_RULE_1['security-rule'].name, + OUTDATED_RULE_2['security-rule'].name, + ]); + cy.get(UPGRADE_SELECTED_RULES_BUTTON).click(); + assertUpgradeRequestIsComplete([OUTDATED_RULE_1, OUTDATED_RULE_2]); + assertUpgradeSuccess([OUTDATED_RULE_1, OUTDATED_RULE_2]); }); it('should upgrade multiple selected prebuilt rules by selecting all in page', () => { - ruleUpdatesTabClick(); - assertRuleUpgradeAvailableAndUpgradeAllInPage({ - rules: [OUTDATED_RULE_1, OUTDATED_RULE_2], - }); + cy.get(SELECT_ALL_RULES_ON_PAGE_CHECKBOX).click(); + cy.get(UPGRADE_SELECTED_RULES_BUTTON).click(); + assertUpgradeRequestIsComplete([OUTDATED_RULE_1, OUTDATED_RULE_2]); + assertUpgradeSuccess([OUTDATED_RULE_1, OUTDATED_RULE_2]); }); it('should upgrade all rules with available upgrades at once', () => { - ruleUpdatesTabClick(); - assertRuleUpgradeAvailableAndUpgradeAll({ rules: [OUTDATED_RULE_1, OUTDATED_RULE_2] }); - cy.get(RULES_UPDATES_TAB).should('not.exist'); + cy.get(UPGRADE_ALL_RULES_BUTTON).click(); + assertUpgradeRequestIsComplete([OUTDATED_RULE_1, OUTDATED_RULE_2]); + assertUpgradeSuccess([OUTDATED_RULE_1, OUTDATED_RULE_2]); }); it('should display an empty screen when all rules with available updates have been upgraded', () => { - ruleUpdatesTabClick(); - assertRuleUpgradeAvailableAndUpgradeAll({ rules: [OUTDATED_RULE_1, OUTDATED_RULE_2] }); + cy.get(UPGRADE_ALL_RULES_BUTTON).click(); cy.get(RULES_UPDATES_TAB).should('not.exist'); - cy.get(NO_RULES_AVAILABLE_FOR_UPGRADE_MESSSAGE).should('exist'); + cy.get(NO_RULES_AVAILABLE_FOR_UPGRADE_MESSAGE).should('exist'); }); }); } diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_management.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_management.cy.ts index a7ef216bf9571..7fef8e4abbea2 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_management.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_management.cy.ts @@ -62,7 +62,7 @@ describe('Prebuilt rules', { tags: ['@ess', '@serverless'] }, () => { deletePrebuiltRulesAssets(); preventPrebuiltRulesPackageInstallation(); visit(RULES_MANAGEMENT_URL); - createAndInstallMockedPrebuiltRules({ rules }); + createAndInstallMockedPrebuiltRules(rules); cy.reload(); waitForPrebuiltDetectionRulesToBeLoaded(); disableAutoRefresh(); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_notifications.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_notifications.cy.ts index e98d6a9487c24..eef35c79d3624 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_notifications.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_notifications.cy.ts @@ -14,6 +14,7 @@ import { import { deleteFirstRule } from '../../../tasks/alerts_detection_rules'; import { installAllPrebuiltRulesRequest, + installPrebuiltRuleAssets, createAndInstallMockedPrebuiltRules, } from '../../../tasks/api_calls/prebuilt_rules'; import { @@ -54,8 +55,8 @@ describe( }); it('should NOT display install or update notifications when latest rules are installed', () => { - createAndInstallMockedPrebuiltRules({ rules: [RULE_1], installToKibana: true }); visitRulesManagementTable(); + createAndInstallMockedPrebuiltRules([RULE_1]); /* Assert that there are no installation or update notifications */ /* Add Elastic Rules button should not contain a number badge */ @@ -67,7 +68,7 @@ describe( describe('Notifications', () => { beforeEach(() => { - createAndInstallMockedPrebuiltRules({ rules: [RULE_1], installToKibana: false }); + installPrebuiltRuleAssets([RULE_1]); }); describe('Rules installation notification when no rules have been installed', () => { @@ -96,11 +97,8 @@ describe( rule_id: 'rule_3', }); - createAndInstallMockedPrebuiltRules({ - rules: [RULE_2, RULE_3], - installToKibana: false, - }); visitRulesManagementTable(); + installPrebuiltRuleAssets([RULE_2, RULE_3]); }); }); @@ -136,9 +134,8 @@ describe( rule_id: 'rule_1', version: 2, }); - createAndInstallMockedPrebuiltRules({ rules: [UPDATED_RULE], installToKibana: false }); + installPrebuiltRuleAssets([UPDATED_RULE]); visitRulesManagementTable(); - cy.reload(); }); }); @@ -167,10 +164,7 @@ describe( rule_id: 'rule_1', version: 2, }); - createAndInstallMockedPrebuiltRules({ - rules: [RULE_2, UPDATED_RULE], - installToKibana: false, - }); + installPrebuiltRuleAssets([RULE_2, UPDATED_RULE]); visitRulesManagementTable(); }); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules.cy.ts index c877221cb02bc..8d551b655fca4 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules.cy.ts @@ -194,7 +194,7 @@ describe( }); it('Only prebuilt rules selected', () => { - createAndInstallMockedPrebuiltRules({ rules: PREBUILT_RULES }); + createAndInstallMockedPrebuiltRules(PREBUILT_RULES); // select Elastic(prebuilt) rules, check if we can't proceed further, as Elastic rules are not editable filterByElasticRules(); @@ -213,7 +213,7 @@ describe( it('Prebuilt and custom rules selected: user proceeds with custom rules editing', () => { getRulesManagementTableRows().then((existedRulesRows) => { - createAndInstallMockedPrebuiltRules({ rules: PREBUILT_RULES }); + createAndInstallMockedPrebuiltRules(PREBUILT_RULES); // modal window should show how many rules can be edit, how many not selectAllRules(); @@ -238,7 +238,7 @@ describe( }); it('Prebuilt and custom rules selected: user cancels action', () => { - createAndInstallMockedPrebuiltRules({ rules: PREBUILT_RULES }); + createAndInstallMockedPrebuiltRules(PREBUILT_RULES); getRulesManagementTableRows().then((rows) => { // modal window should show how many rules can be edit, how many not diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules_actions.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules_actions.cy.ts index fb54b04005139..1d59135012a9d 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules_actions.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules_actions.cy.ts @@ -145,7 +145,7 @@ describe( rule_id: 'rule_2', }); - createAndInstallMockedPrebuiltRules({ rules: [RULE_1, RULE_2] }); + createAndInstallMockedPrebuiltRules([RULE_1, RULE_2]); }); context('Restricted action privileges', () => { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/import_export/export_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/import_export/export_rule.cy.ts index 2e1f4ec1524cf..262c6904db4bd 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/import_export/export_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/import_export/export_rule.cy.ts @@ -103,7 +103,7 @@ describe('Export rules', { tags: ['@ess', '@serverless', '@brokenInServerless'] }); it('shows a modal saying that no rules can be exported if all the selected rules are prebuilt', function () { - createAndInstallMockedPrebuiltRules({ rules: prebuiltRules }); + createAndInstallMockedPrebuiltRules(prebuiltRules); filterByElasticRules(); selectAllRules(); @@ -117,7 +117,7 @@ describe('Export rules', { tags: ['@ess', '@serverless', '@brokenInServerless'] it('exports only custom rules', function () { const expectedNumberCustomRulesToBeExported = 1; - createAndInstallMockedPrebuiltRules({ rules: prebuiltRules }); + createAndInstallMockedPrebuiltRules(prebuiltRules); selectAllRules(); bulkExportRules(); @@ -170,7 +170,7 @@ describe('Export rules', { tags: ['@ess', '@serverless', '@brokenInServerless'] // one rule with exception, one without it const expectedNumberCustomRulesToBeExported = 2; - createAndInstallMockedPrebuiltRules({ rules: prebuiltRules }); + createAndInstallMockedPrebuiltRules(prebuiltRules); reload(); selectAllRules(); bulkExportRules(); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rules_table/rules_table_selection.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rules_table/rules_table_selection.cy.ts index 6ae36029a8c0a..d07186c8252f9 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rules_table/rules_table_selection.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rules_table/rules_table_selection.cy.ts @@ -42,7 +42,7 @@ describe('Rules table: selection', { tags: ['@ess', '@serverless'] }, () => { beforeEach(() => { login(); /* Create and install two mock rules */ - createAndInstallMockedPrebuiltRules({ rules: [RULE_1, RULE_2] }); + createAndInstallMockedPrebuiltRules([RULE_1, RULE_2]); visit(RULES_MANAGEMENT_URL); waitForPrebuiltDetectionRulesToBeLoaded(); }); diff --git a/x-pack/test/security_solution_cypress/cypress/screens/alerts_detection_rules.ts b/x-pack/test/security_solution_cypress/cypress/screens/alerts_detection_rules.ts index 1ae1ed9d95dbb..14c885fc076e2 100644 --- a/x-pack/test/security_solution_cypress/cypress/screens/alerts_detection_rules.ts +++ b/x-pack/test/security_solution_cypress/cypress/screens/alerts_detection_rules.ts @@ -61,6 +61,14 @@ export const UPGRADE_ALL_RULES_BUTTON = '[data-test-subj="upgradeAllRulesButton" export const UPGRADE_SELECTED_RULES_BUTTON = '[data-test-subj="upgradeSelectedRulesButton"]'; +export const getInstallSingleRuleLoadingSpinnerByRuleId = (ruleId: string) => { + return `[data-test-subj="installSinglePrebuiltRuleButton-loadingSpinner-${ruleId}"]`; +}; + +export const getUpgradeSingleRuleLoadingSpinnerByRuleId = (ruleId: string) => { + return `[data-test-subj="upgradeSinglePrebuiltRuleButton-loadingSpinner-${ruleId}"]`; +}; + export const GO_BACK_TO_RULES_TABLE_BUTTON = '[data-test-subj="addRulesGoBackToRulesTableBtn"]'; export const RULES_TABLE_REFRESH_INDICATOR = '[data-test-subj="loading-spinner"]'; @@ -179,7 +187,7 @@ export const getUpgradeSingleRuleButtonByRuleId = (ruleId: string) => { return `[data-test-subj="upgradeSinglePrebuiltRuleButton-${ruleId}"]`; }; -export const NO_RULES_AVAILABLE_FOR_INSTALL_MESSSAGE = +export const NO_RULES_AVAILABLE_FOR_INSTALL_MESSAGE = '[data-test-subj="noPrebuiltRulesAvailableForInstall"]'; -export const NO_RULES_AVAILABLE_FOR_UPGRADE_MESSSAGE = +export const NO_RULES_AVAILABLE_FOR_UPGRADE_MESSAGE = '[data-test-subj="noPrebuiltRulesAvailableForUpgrade"]'; diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/prebuilt_rules.ts b/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/prebuilt_rules.ts index 48b21115b9895..98b9a884e683a 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/prebuilt_rules.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/prebuilt_rules.ts @@ -13,9 +13,10 @@ import { ELASTIC_SECURITY_RULE_ID } from '@kbn/security-solution-plugin/common/d import type { PrePackagedRulesStatusResponse } from '@kbn/security-solution-plugin/public/detection_engine/rule_management/logic/types'; import { getPrebuiltRuleWithExceptionsMock } from '@kbn/security-solution-plugin/server/lib/detection_engine/prebuilt_rules/mocks'; import { createRuleAssetSavedObject } from '../../helpers/rules'; +import { rootRequest } from '../common'; export const getPrebuiltRulesStatus = () => { - return cy.request({ + return rootRequest({ method: 'GET', url: 'api/detection_engine/rules/prepackaged/_status', headers: { @@ -39,7 +40,7 @@ export const SAMPLE_PREBUILT_RULE = createRuleAssetSavedObject({ * instead of all rules available in the `security_detection_engine` package */ export const installAllPrebuiltRulesRequest = () => - cy.request({ + rootRequest({ method: 'POST', url: PERFORM_RULE_INSTALLATION_URL, headers: { @@ -52,6 +53,30 @@ export const installAllPrebuiltRulesRequest = () => }, }); +/* Install specific prebuilt rules. Should be available as security-rule saved objects +/* as a prerequisite for this request to succeed. + * Use in combination with `preventPrebuiltRulesPackageInstallation` and + * `createNewRuleAsset` to create mocked prebuilt rules and install only those + * instead of all rules available in the `security_detection_engine` package + */ +export const installSpecificPrebuiltRulesRequest = (rules: Array) => + rootRequest({ + method: 'POST', + url: PERFORM_RULE_INSTALLATION_URL, + headers: { + 'kbn-xsrf': 'cypress-creds', + 'x-elastic-internal-origin': 'security-solution', + 'elastic-api-version': '1', + }, + body: { + mode: 'SPECIFIC_RULES', + rules: rules.map((rule) => ({ + rule_id: rule['security-rule'].rule_id, + version: rule['security-rule'].version, + })), + }, + }); + export const getAvailablePrebuiltRulesCount = () => { cy.log('Get prebuilt rules count'); return getPrebuiltRulesStatus().then(({ body }) => { @@ -123,48 +148,44 @@ export const bulkCreateRuleAssets = ({ index?: string; rules?: Array; }) => { - const url = `${Cypress.env('ELASTICSEARCH_URL')}/${index}/_bulk`; + cy.log( + 'Bulk Install prebuilt rules', + rules?.map((rule) => rule['security-rule'].rule_id).join(', ') + ); + const url = `${Cypress.env('ELASTICSEARCH_URL')}/${index}/_bulk?refresh`; const bulkIndexRequestBody = rules.reduce((body, rule) => { const indexOperation = { index: { _index: index, - _id: rule['security-rule'].rule_id, + _id: `security-rule:${rule['security-rule'].rule_id}`, }, }; const documentData = JSON.stringify(rule); - return body.concat(JSON.stringify(indexOperation), '\n', documentData, '\n'); }, ''); - cy.request({ + rootRequest({ method: 'PUT', - url: `${Cypress.env('ELASTICSEARCH_URL')}/${index}/_mapping?refresh`, + url: `${Cypress.env('ELASTICSEARCH_URL')}/${index}/_mapping`, body: { dynamic: true, }, headers: { 'Content-Type': 'application/json', - 'x-elastic-internal-origin': 'security-solution', }, }); cy.waitUntil( () => { - return cy - .request({ - method: 'POST', - url, - headers: { - 'kbn-xsrf': 'cypress-creds', - 'x-elastic-internal-origin': 'security-solution', - 'Content-Type': 'application/json', - }, - failOnStatusCode: false, - body: bulkIndexRequestBody, - }) - .then((response) => response.status === 200); + return rootRequest({ + method: 'POST', + url, + headers: { 'kbn-xsrf': 'cypress-creds', 'Content-Type': 'application/json' }, + failOnStatusCode: false, + body: bulkIndexRequestBody, + }).then((response) => response.status === 200); }, { interval: 500, timeout: 12000 } ); @@ -172,7 +193,7 @@ export const bulkCreateRuleAssets = ({ export const getRuleAssets = (index: string | undefined = '.kibana_security_solution') => { const url = `${Cypress.env('ELASTICSEARCH_URL')}/${index}/_search?size=10000`; - return cy.request({ + return rootRequest({ method: 'GET', url, headers: { @@ -194,6 +215,7 @@ export const getRuleAssets = (index: string | undefined = '.kibana_security_solu /* Used primarily to prevent the unwanted installation of "real" prebuilt rules /* during e2e tests, and allow for manual installation of mock rules instead. */ export const preventPrebuiltRulesPackageInstallation = () => { + cy.log('Prevent prebuilt rules package installation'); cy.intercept('POST', '/api/fleet/epm/packages/_bulk*', {}); cy.intercept('POST', '/api/fleet/epm/packages/security_detection_engine/*', {}); }; @@ -202,42 +224,31 @@ export const preventPrebuiltRulesPackageInstallation = () => { * Install prebuilt rule assets. After installing these assets become available to be installed * as prebuilt rules. Prebuilt rule assets can be generated via `createRuleAssetSavedObject()` helper function. * - * It's also important to take into account that business logic tries to fetch prebuilt rules Fleet package + * It's also important to take into account that the business logic tries to fetch prebuilt rules Fleet package * and you need to add `preventPrebuiltRulesPackageInstallation()` to `beforeEach` section (before visit commands) - * to avoid actually pulling a real Fleet package and have only provided prebuilt rule assets for testing. + * to avoid actually pulling a real Fleet package and have only the mocked prebuilt rule assets for testing. */ export const installPrebuiltRuleAssets = (ruleAssets: Array): void => { cy.log('Create mocked available to install prebuilt rules', ruleAssets.length); preventPrebuiltRulesPackageInstallation(); - // TODO: use this bulk method once the issue with Cypress is fixed - // bulkCreateRuleAssets({ rules }); - ruleAssets.forEach((rule) => { - createNewRuleAsset({ rule }); - }); + + bulkCreateRuleAssets({ rules: ruleAssets }); }; /** * Prevent the installation of the `security_detection_engine` package from Fleet. * The create a `security-rule` asset for each rule provided in the `rules` array. - * Optionally install the rules to Kibana, with a flag defaulting to true - * Explicitly set the `installToKibana` flag to false in cases when needing to - * make mock rules available for installation or update, but do those operations manually * * * @param {Array} rules - Rule assets to be created and optionally installed * - * * @param {string} installToKibana - Flag to decide whether to install the rules as 'alerts' SO. Defaults to true. */ -export const createAndInstallMockedPrebuiltRules = ({ - rules: ruleAssets, - installToKibana = true, -}: { - rules: Array; - installToKibana?: boolean; -}) => { +export const createAndInstallMockedPrebuiltRules = ( + ruleAssets: Array +) => { + preventPrebuiltRulesPackageInstallation(); + // Install assets into ES as `security-rule` SOs installPrebuiltRuleAssets(ruleAssets); - if (installToKibana) { - cy.log('Install prebuilt rules', ruleAssets.length); - return installAllPrebuiltRulesRequest(); - } + // Install rules into Kibana as `alerts` SOs + return installSpecificPrebuiltRulesRequest(ruleAssets); }; diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/common.ts b/x-pack/test/security_solution_cypress/cypress/tasks/common.ts index 3c2467c25f6ad..67bcf971c3b15 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/common.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/common.ts @@ -93,6 +93,7 @@ export const resetRulesTableState = () => { export const cleanKibana = () => { resetRulesTableState(); + deletePrebuiltRulesAssets(); deleteAlertsAndRules(); deleteAllCasesItems(); deleteTimelines(); diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/prebuilt_rules.ts b/x-pack/test/security_solution_cypress/cypress/tasks/prebuilt_rules.ts index 7b58dda17015f..fc78399fff8e4 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/prebuilt_rules.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/prebuilt_rules.ts @@ -9,18 +9,12 @@ import { RULES_ADD_PATH, RULES_UPDATES } from '@kbn/security-solution-plugin/com import { ADD_ELASTIC_RULES_BTN, ADD_ELASTIC_RULES_TABLE, - getInstallSingleRuleButtonByRuleId, - getUpgradeSingleRuleButtonByRuleId, - INSTALL_ALL_RULES_BUTTON, - INSTALL_SELECTED_RULES_BUTTON, + getInstallSingleRuleLoadingSpinnerByRuleId, + getUpgradeSingleRuleLoadingSpinnerByRuleId, RULES_MANAGEMENT_TABLE, RULES_UPDATES_TAB, RULES_UPDATES_TABLE, - RULE_CHECKBOX, - SELECT_ALL_RULES_ON_PAGE_CHECKBOX, TOASTER, - UPGRADE_ALL_RULES_BUTTON, - UPGRADE_SELECTED_RULES_BUTTON, } from '../screens/alerts_detection_rules'; import { RULE_MANAGEMENT_PAGE_BREADCRUMB } from '../screens/breadcrumbs'; import type { SAMPLE_PREBUILT_RULE } from './api_calls/prebuilt_rules'; @@ -35,190 +29,116 @@ export const ruleUpdatesTabClick = () => { cy.location('pathname').should('include', RULES_UPDATES); }; -interface RuleInstallUpgradeAssertionPayload { - rules: Array; - didRequestFail?: boolean; -} - -export const assertRuleAvailableForInstallAndInstallOne = ({ - rules, - didRequestFail = false, -}: RuleInstallUpgradeAssertionPayload) => { - interceptInstallationRequestToFail(rules, didRequestFail); - const rule = rules[0]; - cy.get(getInstallSingleRuleButtonByRuleId(rule['security-rule'].rule_id)).click(); - cy.wait('@installPrebuiltRules'); - assertInstallationSuccessOrFailure([rule], didRequestFail); -}; - -export const assertRuleAvailableForInstallAndInstallSelected = ({ - rules, - didRequestFail = false, -}: RuleInstallUpgradeAssertionPayload) => { - interceptInstallationRequestToFail(rules, didRequestFail); - let i = 0; +export const assertInstallationRequestIsComplete = (rules: Array) => { for (const rule of rules) { - cy.get(RULE_CHECKBOX).eq(i).click(); - cy.get(ADD_ELASTIC_RULES_TABLE).contains(rule['security-rule'].name); - i++; + cy.get(getInstallSingleRuleLoadingSpinnerByRuleId(rule['security-rule'].rule_id)).should( + 'exist' + ); } - cy.get(INSTALL_SELECTED_RULES_BUTTON).click(); - cy.wait('@installPrebuiltRules'); - assertInstallationSuccessOrFailure(rules, didRequestFail); -}; - -export const assertRuleAvailableForInstallAndInstallAllInPage = ({ - rules, - didRequestFail = false, -}: RuleInstallUpgradeAssertionPayload) => { - interceptInstallationRequestToFail(rules, didRequestFail); for (const rule of rules) { - cy.get(ADD_ELASTIC_RULES_TABLE).contains(rule['security-rule'].name); + cy.get(getInstallSingleRuleLoadingSpinnerByRuleId(rule['security-rule'].rule_id)).should( + 'not.exist' + ); } - cy.get(SELECT_ALL_RULES_ON_PAGE_CHECKBOX).click(); - cy.get(INSTALL_SELECTED_RULES_BUTTON).click(); - cy.wait('@installPrebuiltRules'); - assertInstallationSuccessOrFailure(rules, didRequestFail); }; -export const assertRuleAvailableForInstallAndInstallAll = ({ - rules, - didRequestFail = false, -}: RuleInstallUpgradeAssertionPayload) => { - interceptInstallationRequestToFail(rules, didRequestFail); +export const assertUpgradeRequestIsComplete = (rules: Array) => { for (const rule of rules) { - cy.get(ADD_ELASTIC_RULES_TABLE).contains(rule['security-rule'].name); + cy.get(getUpgradeSingleRuleLoadingSpinnerByRuleId(rule['security-rule'].rule_id)).should( + 'exist' + ); + } + for (const rule of rules) { + cy.get(getUpgradeSingleRuleLoadingSpinnerByRuleId(rule['security-rule'].rule_id)).should( + 'not.exist' + ); } - cy.get(INSTALL_ALL_RULES_BUTTON).click(); - cy.wait('@installPrebuiltRules'); - assertInstallationSuccessOrFailure(rules, didRequestFail); }; -const assertInstallationSuccessOrFailure = ( - rules: Array, - didRequestFail: boolean -) => { +/** + * Assert that when the rule installation succeeds, the toast is shown with the right message + * -confirming the succesful install- and subsequently check that the rules available for installation + * are not present in the Add Elastic Rules table anymore + */ +export const assertInstallationSuccess = (rules: Array) => { const rulesString = rules.length > 1 ? 'rules' : 'rule'; - const toastMessage = didRequestFail - ? `${rules.length} ${rulesString} failed to install.` - : `${rules.length} ${rulesString} installed successfully.`; + const toastMessage = `${rules.length} ${rulesString} installed successfully.`; cy.get(TOASTER).should('be.visible').should('have.text', toastMessage); - if (didRequestFail) { - for (const rule of rules) { - cy.get(ADD_ELASTIC_RULES_TABLE).contains(rule['security-rule'].name); - } - } else { - cy.get(RULE_MANAGEMENT_PAGE_BREADCRUMB).click(); - for (const rule of rules) { - cy.get(RULES_MANAGEMENT_TABLE).contains(rule['security-rule'].name); - } - } -}; -const interceptInstallationRequestToFail = ( - rules: Array, - didRequestFail: boolean -) => { - if (didRequestFail) { - cy.intercept('POST', '/internal/detection_engine/prebuilt_rules/installation/_perform', { - body: { - summary: { - succeeded: [], - skipped: [], - failed: rules.length, - }, - }, - }).as('installPrebuiltRules'); + // Go back to rules table and assert that the rules are installed + cy.get(RULE_MANAGEMENT_PAGE_BREADCRUMB).click(); + for (const rule of rules) { + cy.get(RULES_MANAGEMENT_TABLE).contains(rule['security-rule'].name); } }; -export const assertRuleUpgradeAvailableAndUpgradeOne = ({ - rules, - didRequestFail = false, -}: RuleInstallUpgradeAssertionPayload) => { - interceptUpgradeRequestToFail(rules, didRequestFail); - const rule = rules[0]; - cy.get(getUpgradeSingleRuleButtonByRuleId(rule['security-rule'].rule_id)).click(); - cy.wait('@updatePrebuiltRules'); - assertUpgradeSuccessOrFailure([rule], didRequestFail); -}; +/** + * Assert that when the rule installation fails, the toast is shown with the right message + * -notifying that the installation failed- and subsequently check that the rules available for installation + * are still present in the Rule Update table + */ +export const assertInstallationFailure = (rules: Array) => { + const rulesString = rules.length > 1 ? 'rules' : 'rule'; + const toastMessage = `${rules.length} ${rulesString} failed to install.`; + cy.get(TOASTER).should('be.visible').should('have.text', toastMessage); -export const assertRuleUpgradeAvailableAndUpgradeSelected = ({ - rules, - didRequestFail = false, -}: RuleInstallUpgradeAssertionPayload) => { - interceptUpgradeRequestToFail(rules, didRequestFail); - let i = 0; + // Check rules are still available for install for (const rule of rules) { - cy.get(RULE_CHECKBOX).eq(i).click(); - cy.get(RULES_UPDATES_TABLE).contains(rule['security-rule'].name); - i++; + cy.get(ADD_ELASTIC_RULES_TABLE).contains(rule['security-rule'].name); } - cy.get(UPGRADE_SELECTED_RULES_BUTTON).click(); - cy.wait('@updatePrebuiltRules'); - assertUpgradeSuccessOrFailure(rules, didRequestFail); }; -export const assertRuleUpgradeAvailableAndUpgradeAllInPage = ({ - rules, - didRequestFail = false, -}: RuleInstallUpgradeAssertionPayload) => { - interceptUpgradeRequestToFail(rules, didRequestFail); - for (const rule of rules) { - cy.get(RULES_UPDATES_TABLE).contains(rule['security-rule'].name); - } - cy.get(SELECT_ALL_RULES_ON_PAGE_CHECKBOX).click(); - cy.get(UPGRADE_SELECTED_RULES_BUTTON).click(); - cy.wait('@updatePrebuiltRules'); - assertUpgradeSuccessOrFailure(rules, didRequestFail); +export const interceptInstallationRequestToFail = (rules: Array) => { + cy.intercept('POST', '/internal/detection_engine/prebuilt_rules/installation/_perform', { + body: { + summary: { + succeeded: [], + skipped: [], + failed: rules.length, + }, + }, + delay: 500, // Add delay to give Cypress time to find the loading spinner + }).as('installPrebuiltRules'); }; -export const assertRuleUpgradeAvailableAndUpgradeAll = ({ - rules, - didRequestFail = false, -}: RuleInstallUpgradeAssertionPayload) => { - interceptUpgradeRequestToFail(rules, didRequestFail); +/** + * Assert that when the rule version upgrade succeeds, the toast is shown with the right message + * -confirming the succesful upgrade- and subsequently check that the rules available for upgrade + * are not present in the Rule Update table anymore + */ +export const assertUpgradeSuccess = (rules: Array) => { + const rulesString = rules.length > 1 ? 'rules' : 'rule'; + const toastMessage = `${rules.length} ${rulesString} updated successfully.`; + cy.get(TOASTER).should('be.visible').should('have.text', toastMessage); for (const rule of rules) { - cy.get(RULES_UPDATES_TABLE).contains(rule['security-rule'].name); + cy.get(rule['security-rule'].name).should('not.exist'); } - cy.get(UPGRADE_ALL_RULES_BUTTON).click(); - cy.wait('@updatePrebuiltRules'); - assertUpgradeSuccessOrFailure(rules, didRequestFail); }; -const assertUpgradeSuccessOrFailure = ( - rules: Array, - didRequestFail: boolean -) => { +/** + * Assert that when the rule version upgrade fails, the toast is shown with the right message + * -notifying that the upgrade failed- and subsequently check that the rules available for upgrade + * are still present in the Rule Update table + */ +export const assertUpgradeFailure = (rules: Array) => { const rulesString = rules.length > 1 ? 'rules' : 'rule'; - const toastMessage = didRequestFail - ? `${rules.length} ${rulesString} failed to update.` - : `${rules.length} ${rulesString} updated successfully.`; + const toastMessage = `${rules.length} ${rulesString} failed to update.`; cy.get(TOASTER).should('be.visible').should('have.text', toastMessage); - if (didRequestFail) { - for (const rule of rules) { - cy.get(RULES_UPDATES_TABLE).contains(rule['security-rule'].name); - } - } else { - for (const rule of rules) { - cy.get(rule['security-rule'].name).should('not.exist'); - } + + for (const rule of rules) { + cy.get(RULES_UPDATES_TABLE).contains(rule['security-rule'].name); } }; -const interceptUpgradeRequestToFail = ( - rules: Array, - didRequestFail: boolean -) => { - if (didRequestFail) { - cy.intercept('POST', '/internal/detection_engine/prebuilt_rules/upgrade/_perform', { - body: { - summary: { - succeeded: [], - skipped: [], - failed: rules.length, - }, +export const interceptUpgradeRequestToFail = (rules: Array) => { + cy.intercept('POST', '/internal/detection_engine/prebuilt_rules/upgrade/_perform', { + body: { + summary: { + succeeded: [], + skipped: [], + failed: rules.length, }, - }).as('updatePrebuiltRules'); - } + }, + delay: 500, // Add delay to give Cypress time to find the loading spinner + }).as('updatePrebuiltRules'); };