From 410c014b6f6e00481a87ce88295387ea38fb8063 Mon Sep 17 00:00:00 2001 From: AWSHurneyt Date: Wed, 12 Jul 2023 15:42:02 -0700 Subject: [PATCH] Updated tests for alerting dashboards v2.9 release. Added cypress tests for composite monitors, and the monitors list page. Signed-off-by: AWSHurneyt --- ...ample_cluster_metrics_health_monitor.json} | 0 .../sample_cluster_metrics_stats_monitor.json | 73 +++++ .../sample_composite_level_monitor.json | 274 ++++++++++++++++++ .../acknowledge_alerts_modal_spec.js | 3 +- .../bucket_level_monitor_spec.js | 42 ++- .../cluster_metrics_monitor_spec.js | 94 +++--- .../composite_level_monitor_spec.js | 181 ++++++++++++ .../monitors_dashboard_spec.js | 152 ++++++++++ .../query_level_monitor_spec.js | 11 +- .../alerting-dashboards-plugin/commands.js | 67 ++++- .../alerting-dashboards-plugin/constants.js | 1 + 11 files changed, 844 insertions(+), 54 deletions(-) rename cypress/fixtures/plugins/alerting-dashboards-plugin/{sample_cluster_metrics_monitor.json => sample_cluster_metrics_health_monitor.json} (100%) create mode 100644 cypress/fixtures/plugins/alerting-dashboards-plugin/sample_cluster_metrics_stats_monitor.json create mode 100644 cypress/fixtures/plugins/alerting-dashboards-plugin/sample_composite_level_monitor.json create mode 100644 cypress/integration/plugins/alerting-dashboards-plugin/composite_level_monitor_spec.js create mode 100644 cypress/integration/plugins/alerting-dashboards-plugin/monitors_dashboard_spec.js diff --git a/cypress/fixtures/plugins/alerting-dashboards-plugin/sample_cluster_metrics_monitor.json b/cypress/fixtures/plugins/alerting-dashboards-plugin/sample_cluster_metrics_health_monitor.json similarity index 100% rename from cypress/fixtures/plugins/alerting-dashboards-plugin/sample_cluster_metrics_monitor.json rename to cypress/fixtures/plugins/alerting-dashboards-plugin/sample_cluster_metrics_health_monitor.json diff --git a/cypress/fixtures/plugins/alerting-dashboards-plugin/sample_cluster_metrics_stats_monitor.json b/cypress/fixtures/plugins/alerting-dashboards-plugin/sample_cluster_metrics_stats_monitor.json new file mode 100644 index 000000000..a258b0586 --- /dev/null +++ b/cypress/fixtures/plugins/alerting-dashboards-plugin/sample_cluster_metrics_stats_monitor.json @@ -0,0 +1,73 @@ +{ + "name": "sample_cluster_metrics_stats_monitor", + "type": "monitor", + "monitor_type": "cluster_metrics_monitor", + "enabled": true, + "schedule": { + "period": { + "unit": "MINUTES", + "interval": 1 + } + }, + "inputs": [ + { + "uri": { + "api_type": "CLUSTER_STATS", + "path": "_cluster/stats/", + "path_params": "", + "url": "http://localhost:9200/_cluster/stats/" + } + } + ], + "triggers": [ + { + "query_level_trigger": { + "id": "Y5mmA4kBIezNcMbMJnEy", + "name": "sample_cluster_metrics_stats_monitor-trigger1", + "severity": "1", + "condition": { + "script": { + "source": "ctx.results[0].indices.count >= 0", + "lang": "painless" + } + }, + "actions": [] + } + } + ], + "ui_metadata": { + "schedule": { + "timezone": null, + "frequency": "interval", + "period": { + "unit": "MINUTES", + "interval": 1 + }, + "daily": 0, + "weekly": { + "tue": false, + "wed": false, + "thur": false, + "sat": false, + "fri": false, + "mon": false, + "sun": false + }, + "monthly": { + "type": "day", + "day": 1 + }, + "cronExpression": "0 */1 * * *" + }, + "search": { + "searchType": "clusterMetrics", + "timeField": "", + "aggregations": [], + "groupBy": [], + "bucketValue": 1, + "bucketUnitOfTime": "h", + "filters": [] + }, + "monitor_type": "cluster_metrics_monitor" + } +} diff --git a/cypress/fixtures/plugins/alerting-dashboards-plugin/sample_composite_level_monitor.json b/cypress/fixtures/plugins/alerting-dashboards-plugin/sample_composite_level_monitor.json new file mode 100644 index 000000000..9b1f67bf4 --- /dev/null +++ b/cypress/fixtures/plugins/alerting-dashboards-plugin/sample_composite_level_monitor.json @@ -0,0 +1,274 @@ +{ + "sample_composite_monitor": { + "type": "workflow", + "schema_version": 0, + "name": "sampleComponentLevelMonitor", + "workflow_type": "composite", + "enabled": true, + "enabled_time": 1686908176848, + "schedule": { + "period": { + "interval": 1, + "unit": "MINUTES" + } + }, + "inputs": [ + { + "composite_input": { + "sequence": { + "delegates": [ + { + "order": 1, + "monitor_id": "qdYBw4gB2qeAWe54jQyZ" + }, + { + "order": 2, + "monitor_id": "rtYBw4gB2qeAWe54wAx5" + } + ] + } + } + } + ], + "triggers": [ + { + "chained_alert_trigger": { + "id": "pNaQw4gB2qeAWe54Fg2U", + "name": "sample_trigger", + "severity": "1", + "condition": { + "script": { + "source": "(monitor[id=qdYBw4gB2qeAWe54jQyZ]) && (monitor[id=rtYBw4gB2qeAWe54wAx5])", + "lang": "painless" + } + }, + "actions": [ + { + "id": "pdaQw4gB2qeAWe54Fg2U", + "name": "sample_channel", + "destination_id": "6dYFw4gB2qeAWe54NgyL", + "message_template": { + "source": "Monitor {{ctx.monitor.name}} just entered alert status. Please investigate the issue.\n - Trigger: {{ctx.trigger.name}}\n - Severity: {{ctx.trigger.severity}}\n - Period start: {{ctx.periodStart}}\n - Period end: {{ctx.periodEnd}}", + "lang": "mustache" + }, + "throttle_enabled": false, + "subject_template": { + "source": "Monitor {{ctx.monitor.name}} triggered an alert {{ctx.trigger.name}}", + "lang": "mustache" + } + } + ] + } + } + ], + "last_update_time": 1686908180116, + "owner": "alerting", + "monitor_type": "composite" + }, + "sample_composite_index": { + "mappings": { + "properties": { + "audit_category": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword", + "ignore_above": 256 + } + } + }, + "audit_node_host_name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword", + "ignore_above": 256 + } + } + }, + "audit_node_id": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword", + "ignore_above": 256 + } + } + } + } + } + }, + "sample_composite_associated_monitor_1": { + "name": "monitorOne", + "type": "monitor", + "monitor_type": "doc_level_monitor", + "enabled": false, + "schedule": { + "period": { + "unit": "MINUTES", + "interval": 1 + } + }, + "inputs": [ + { + "doc_level_input": { + "description": "", + "indices": ["sample_index_1"], + "queries": [ + { + "id": "monitor_1_query_1", + "name": "monitor_1_query_1", + "query": "NOT (audit_category:\"sample_text\")", + "tags": [] + }, + { + "id": "monitor_1_query_2", + "name": "monitor_1_query_2", + "query": "NOT (audit_node_host_name:\"sample_text\")", + "tags": [] + }, + { + "id": "monitor_1_query_3", + "name": "monitor_1_query_3", + "query": "NOT (audit_node_id:\"sample_text\")", + "tags": [] + } + ] + } + } + ], + "triggers": [ + { + "document_level_trigger": { + "id": "sample_trigger_id_1", + "name": "monitor_1_query_2", + "severity": "1", + "condition": { + "script": { + "source": "query[name=monitor_1_query_1] || query[name=monitor_1_query_2] && query[name=monitor_1_query_3]", + "lang": "painless" + } + }, + "actions": [] + } + } + ] + }, + "sample_composite_associated_monitor_2": { + "name": "monitorTwo", + "type": "monitor", + "monitor_type": "doc_level_monitor", + "enabled": false, + "schedule": { + "period": { + "unit": "MINUTES", + "interval": 1 + } + }, + "inputs": [ + { + "doc_level_input": { + "description": "", + "indices": ["sample_index_2"], + "queries": [ + { + "id": "monitor_2_query_1", + "name": "monitor_2_query_1", + "query": "NOT (audit_category:\"sample_text\")", + "tags": [] + }, + { + "id": "monitor_2_query_2", + "name": "monitor_2_query_2", + "query": "NOT (audit_node_host_name:\"sample_text\")", + "tags": [] + }, + { + "id": "monitor_2_query_3", + "name": "monitor_2_query_3", + "query": "NOT (audit_node_id:\"sample_text\")", + "tags": [] + } + ] + } + } + ], + "triggers": [ + { + "document_level_trigger": { + "id": "sample_trigger_2", + "name": "monitor_2_query_2", + "severity": "1", + "condition": { + "script": { + "source": "query[name=monitor_2_query_1] || query[name=monitor_2_query_2] && query[name=monitor_2_query_3]", + "lang": "painless" + } + }, + "actions": [] + } + } + ] + }, + "sample_composite_associated_monitor_3": { + "name": "monitorThree", + "type": "monitor", + "monitor_type": "doc_level_monitor", + "enabled": false, + "schedule": { + "period": { + "unit": "MINUTES", + "interval": 1 + } + }, + "inputs": [ + { + "doc_level_input": { + "description": "", + "indices": ["sample_index_2"], + "queries": [ + { + "id": "monitor_2_query_1", + "name": "monitor_2_query_1", + "query": "NOT (audit_category:\"sample_text\")", + "tags": [] + }, + { + "id": "monitor_2_query_2", + "name": "monitor_2_query_2", + "query": "NOT (audit_node_host_name:\"sample_text\")", + "tags": [] + }, + { + "id": "monitor_2_query_3", + "name": "monitor_2_query_3", + "query": "NOT (audit_node_id:\"sample_text\")", + "tags": [] + } + ] + } + } + ], + "triggers": [ + { + "document_level_trigger": { + "id": "sample_trigger_2", + "name": "monitor_2_query_2", + "severity": "1", + "condition": { + "script": { + "source": "query[name=monitor_2_query_1] || query[name=monitor_2_query_2] && query[name=monitor_2_query_3]", + "lang": "painless" + } + }, + "actions": [] + } + } + ] + }, + "sample_composite_associated_index_document": { + "audit_category": "FAILED_LOGIN", + "audit_node_host_name": "127.0.0.1", + "audit_node_id": "sample_node_id" + } +} diff --git a/cypress/integration/plugins/alerting-dashboards-plugin/acknowledge_alerts_modal_spec.js b/cypress/integration/plugins/alerting-dashboards-plugin/acknowledge_alerts_modal_spec.js index 043920c19..f06c77dea 100644 --- a/cypress/integration/plugins/alerting-dashboards-plugin/acknowledge_alerts_modal_spec.js +++ b/cypress/integration/plugins/alerting-dashboards-plugin/acknowledge_alerts_modal_spec.js @@ -20,7 +20,8 @@ const TWENTY_SECONDS = 20000; describe('AcknowledgeAlertsModal', () => { before(() => { - // Delete any existing monitors + // Delete all existing monitors and alerts + cy.deleteAllAlerts(); cy.deleteAllMonitors(); // Load sample data diff --git a/cypress/integration/plugins/alerting-dashboards-plugin/bucket_level_monitor_spec.js b/cypress/integration/plugins/alerting-dashboards-plugin/bucket_level_monitor_spec.js index ad9286bfb..46a8a33f7 100644 --- a/cypress/integration/plugins/alerting-dashboards-plugin/bucket_level_monitor_spec.js +++ b/cypress/integration/plugins/alerting-dashboards-plugin/bucket_level_monitor_spec.js @@ -89,14 +89,16 @@ const addTriggerToVisualEditorMonitor = ( ).click({ force: true }); cy.get( - `[name="triggerDefinitions[${triggerIndex}].filters.0.fieldName"]` + `[data-test-subj="triggerDefinitions[${triggerIndex}].filters.0.fieldName"]` ).type(`${GROUP_BY_FIELD}{downarrow}{enter}`); cy.get( - `[name="triggerDefinitions[${triggerIndex}].filters.0.operator"]` + `[data-test-subj="triggerDefinitions[${triggerIndex}].filters.0.operator"]` ).select('includes'); - cy.get(`[name="triggerDefinitions[${triggerIndex}].filters.0.fieldValue"]`) + cy.get( + `[data-test-subj="triggerDefinitions[${triggerIndex}].filters.0.fieldValue"]` + ) .type('a*') .trigger('blur', { force: true }); @@ -259,6 +261,38 @@ describe('Bucket-Level Monitors', () => { cy.get('button').contains('Save').click({ force: true }); + // Add data filters for the query + const filters = [ + // Number type + { field: 'products.quantity', operator: 'is less than', value: 100 }, + // Text type + { field: 'manufacturer', operator: 'starts with', value: 'Amazon' }, + // Keyword type + { field: 'user', operator: 'contains', value: 'Jeff' }, + ]; + + filters.forEach((filter, index) => { + cy.get(`[data-test-subj="addFilterButton"]`).click({ force: true }); + + cy.get(`[data-test-subj="filters.${index}.fieldName"]`).type( + `${filter.field}{downarrow}{enter}` + ); + + cy.get(`[data-test-subj="filters.${index}.operator"]`).select( + filter.operator + ); + + cy.get(`[data-test-subj="filters.${index}.fieldValue"]`).type( + `${filter.value}{enter}` + ); + + // Close data filter popover + cy.contains('Data filter - optional').click({ force: true }); + }); + + // Confirm filter limit text is correct + cy.contains('You can add up to 7 more data filters.', { timeout: 20000 }); + // Add a group by field for the query cy.get('[data-test-subj="addGroupByButton"]').click({ force: true }); @@ -375,7 +409,7 @@ describe('Bucket-Level Monitors', () => { cy.deleteAllMonitors(); // Delete sample data - cy.deleteIndexByName(`${ALERTING_INDEX.SAMPLE_DATA_ECOMMERCE}`); + cy.deleteIndexByName(ALERTING_INDEX.SAMPLE_DATA_ECOMMERCE); cy.deleteIndexByName(TESTING_INDEX_A); cy.deleteIndexByName(TESTING_INDEX_B); }); diff --git a/cypress/integration/plugins/alerting-dashboards-plugin/cluster_metrics_monitor_spec.js b/cypress/integration/plugins/alerting-dashboards-plugin/cluster_metrics_monitor_spec.js index be3075ff4..b9b25c275 100644 --- a/cypress/integration/plugins/alerting-dashboards-plugin/cluster_metrics_monitor_spec.js +++ b/cypress/integration/plugins/alerting-dashboards-plugin/cluster_metrics_monitor_spec.js @@ -3,12 +3,11 @@ * SPDX-License-Identifier: Apache-2.0 */ -import sampleClusterMetricsMonitor from '../../../fixtures/plugins/alerting-dashboards-plugin/sample_cluster_metrics_monitor.json'; - import { ALERTING_INDEX, ALERTING_PLUGIN_NAME, } from '../../../utils/plugins/alerting-dashboards-plugin/constants'; +import sampleClusterMetricsMonitor from '../../../fixtures/plugins/alerting-dashboards-plugin/sample_cluster_metrics_health_monitor.json'; import { BASE_PATH } from '../../../utils/base_constants'; const SAMPLE_CLUSTER_METRICS_HEALTH_MONITOR = @@ -34,7 +33,7 @@ const addClusterMetricsTrigger = ( // TODO: Passing button props in EUI accordion was added in newer versions (31.7.0+). // If this ever becomes available, it can be used to pass data-test-subj for the button. // Since the above is currently not possible, referring to the accordion button using its content - cy.get('button').contains('New trigger').click(); + cy.get('button').contains('New trigger').click({ force: true }); } // Type in the trigger name @@ -102,13 +101,15 @@ describe('ClusterMetricsMonitor', () => { it('for the Cluster Health API', () => { // Confirm empty monitor list is loaded - cy.contains('There are no existing monitors'); + // cy.contains('There are no existing monitors'); // Go to create monitor page - cy.contains('Create monitor').click(); + cy.contains('Create monitor', { timeout: 20000 }).click({ force: true }); // Select ClusterMetrics radio card - cy.get('[data-test-subj="clusterMetricsMonitorRadioCard"]').click(); + cy.get('[data-test-subj="clusterMetricsMonitorRadioCard"]').click({ + force: true, + }); // Wait for input to load and then type in the monitor name cy.get('input[name="name"]').type(SAMPLE_CLUSTER_METRICS_HEALTH_MONITOR); @@ -118,12 +119,14 @@ describe('ClusterMetricsMonitor', () => { 'cluster health{enter}' ); - // Confirm the Query parameters field is present and described as "optional" - cy.contains('Query parameters - optional'); + // Confirm the Path parameters field is present and described as "optional" + cy.contains('Path parameters - optional'); cy.get('[data-test-subj="clusterMetricsParamsFieldText"]'); // Press the 'Run for response' button - cy.get('[data-test-subj="clusterMetricsPreviewButton"]').click(); + cy.get('[data-test-subj="clusterMetricsPreviewButton"]').click({ + force: true, + }); // Add a trigger cy.contains('Add trigger').click({ force: true }); @@ -144,7 +147,7 @@ describe('ClusterMetricsMonitor', () => { // .type('{downarrow}{enter}'); // Click the create button - cy.get('button').contains('Create').click(); + cy.get('button').contains('Create').click({ force: true }); // Confirm we can see only one row in the trigger list by checking element cy.contains('This table contains 1 row'); @@ -153,7 +156,7 @@ describe('ClusterMetricsMonitor', () => { cy.contains(SAMPLE_TRIGGER); // Go back to the Monitors list - cy.get('a').contains('Monitors').click(); + cy.get('a').contains('Monitors').click({ force: true }); // Confirm we can see the created monitor in the list cy.contains(SAMPLE_CLUSTER_METRICS_HEALTH_MONITOR); @@ -161,13 +164,15 @@ describe('ClusterMetricsMonitor', () => { it('for the Nodes Stats API', () => { // Confirm empty monitor list is loaded - cy.contains('There are no existing monitors'); + // cy.contains('There are no existing monitors'); // Go to create monitor page - cy.contains('Create monitor').click(); + cy.contains('Create monitor', { timeout: 20000 }).click({ force: true }); // Select ClusterMetrics radio card - cy.get('[data-test-subj="clusterMetricsMonitorRadioCard"]').click(); + cy.get('[data-test-subj="clusterMetricsMonitorRadioCard"]').click({ + force: true, + }); // Wait for input to load and then type in the monitor name cy.get('input[name="name"]').type( @@ -179,14 +184,16 @@ describe('ClusterMetricsMonitor', () => { 'nodes stats{enter}' ); - // Confirm the Query parameters field is not present - cy.contains('Query parameters').should('not.exist'); + // Confirm the Path parameters field is not present + cy.contains('Path parameters').should('not.exist'); cy.get('[data-test-subj="clusterMetricsParamsFieldText"]').should( 'not.exist' ); // Press the 'Run for response' button - cy.get('[data-test-subj="clusterMetricsPreviewButton"]').click(); + cy.get('[data-test-subj="clusterMetricsPreviewButton"]').click({ + force: true, + }); // Add a trigger cy.contains('Add trigger').click({ force: true }); @@ -207,7 +214,7 @@ describe('ClusterMetricsMonitor', () => { // .type('{downarrow}{enter}'); // Click the create button - cy.get('button').contains('Create').click(); + cy.get('button').contains('Create').click({ force: true }); // Confirm we can see only one row in the trigger list by checking element cy.contains('This table contains 1 row'); @@ -216,14 +223,14 @@ describe('ClusterMetricsMonitor', () => { cy.contains(SAMPLE_TRIGGER); // Go back to the Monitors list - cy.get('a').contains('Monitors').click(); + cy.get('a').contains('Monitors').click({ force: true }); // Confirm we can see the created monitor in the list cy.contains(SAMPLE_CLUSTER_METRICS_NODES_STATS_MONITOR); }); }); - describe('displays Query parameters field appropriately', () => { + describe('displays Path parameters field appropriately', () => { beforeEach(() => { cy.deleteAllMonitors(); cy.reload(); @@ -231,13 +238,15 @@ describe('ClusterMetricsMonitor', () => { it('for the CAT Snapshots API', () => { // Confirm empty monitor list is loaded - cy.contains('There are no existing monitors'); + // cy.contains('There are no existing monitors'); // Go to create monitor page - cy.contains('Create monitor').click(); + cy.contains('Create monitor', { timeout: 20000 }).click({ force: true }); // Select ClusterMetrics radio card - cy.get('[data-test-subj="clusterMetricsMonitorRadioCard"]').click(); + cy.get('[data-test-subj="clusterMetricsMonitorRadioCard"]').click({ + force: true, + }); // Wait for input to load and then type in the monitor name cy.get('input[name="name"]').type( @@ -249,9 +258,9 @@ describe('ClusterMetricsMonitor', () => { 'list snapshots{enter}' ); - // Confirm the Query parameters field is present and is not described as "optional" - cy.contains('Query parameters - optional').should('not.exist'); - cy.contains('Query parameters'); + // Confirm the Path parameters field is present and is not described as "optional" + cy.contains('Path parameters - optional').should('not.exist'); + cy.contains('Path parameters'); cy.get('[data-test-subj="clusterMetricsParamsFieldText"]'); }); }); @@ -263,13 +272,15 @@ describe('ClusterMetricsMonitor', () => { // Begin monitor creation // Confirm empty monitor list is loaded - cy.contains('There are no existing monitors'); + // cy.contains('There are no existing monitors'); // Go to create monitor page - cy.contains('Create monitor').click(); + cy.contains('Create monitor', { timeout: 20000 }).click({ force: true }); // Select ClusterMetrics radio card - cy.get('[data-test-subj="clusterMetricsMonitorRadioCard"]').click(); + cy.get('[data-test-subj="clusterMetricsMonitorRadioCard"]').click({ + force: true, + }); // Wait for input to load and then type in the monitor name cy.get('input[name="name"]').type(SAMPLE_CLUSTER_METRICS_HEALTH_MONITOR); @@ -343,7 +354,9 @@ describe('ClusterMetricsMonitor', () => { describe('the modal CLOSE (i.e., the X button) button is clicked', () => { // Click the CLOSE button - cy.get('[aria-label="Closes this modal window"]').click(); + cy.get('[aria-label="Closes this modal window"]').click({ + force: true, + }); // Confirm clearTriggersModal closed cy.get('[data-test-subj="clusterMetricsClearTriggersModal"]').should( @@ -368,7 +381,9 @@ describe('ClusterMetricsMonitor', () => { // Click the KEEP button cy.get( '[data-test-subj="clusterMetricsClearTriggersModalKeepButton"]' - ).click(); + ).click({ + force: true, + }); // Confirm clearTriggersModal closed cy.get('[data-test-subj="clusterMetricsClearTriggersModal"]').should( @@ -388,7 +403,9 @@ describe('ClusterMetricsMonitor', () => { // Click the CLEAR button cy.get( '[data-test-subj="clusterMetricsClearTriggersModalClearButton"]' - ).click(); + ).click({ + force: true, + }); // Confirm clearTriggersModal closed cy.get('[data-test-subj="clusterMetricsClearTriggersModal"]').should( @@ -413,17 +430,20 @@ describe('ClusterMetricsMonitor', () => { describe('Cluster Health API monitor', () => { it('with a new trigger', () => { + const monitorName = `${sampleClusterMetricsMonitor.name}-new-trigger-test`; + // Create the sample monitor - cy.createMonitor(sampleClusterMetricsMonitor); + cy.createMonitor({ + ...sampleClusterMetricsMonitor, + name: monitorName, + }); cy.reload(); // Confirm the created monitor can be seen - cy.contains(SAMPLE_CLUSTER_METRICS_HEALTH_MONITOR); + cy.contains(monitorName); // Select the monitor - cy.get('a') - .contains(SAMPLE_CLUSTER_METRICS_HEALTH_MONITOR) - .click({ force: true }); + cy.get('a').contains(monitorName).click({ force: true }); // Click Edit button cy.contains('Edit').click({ force: true }); diff --git a/cypress/integration/plugins/alerting-dashboards-plugin/composite_level_monitor_spec.js b/cypress/integration/plugins/alerting-dashboards-plugin/composite_level_monitor_spec.js new file mode 100644 index 000000000..2b5fad88e --- /dev/null +++ b/cypress/integration/plugins/alerting-dashboards-plugin/composite_level_monitor_spec.js @@ -0,0 +1,181 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + ALERTING_API, + ALERTING_PLUGIN_NAME, +} from '../../../utils/plugins/alerting-dashboards-plugin/constants'; +import sampleCompositeJson from '../../../fixtures/plugins/alerting-dashboards-plugin/sample_composite_level_monitor.json'; +import * as _ from 'lodash'; +import { BASE_PATH } from '../../../utils/base_constants'; + +const sample_index_1 = 'sample_index_1'; +const sample_index_2 = 'sample_index_2'; +const SAMPLE_VISUAL_EDITOR_MONITOR = + 'sample_visual_editor_composite_level_monitor'; + +const clearAll = () => { + cy.deleteIndexByName(sample_index_1); + cy.deleteIndexByName(sample_index_2); + + cy.deleteAllAlerts(); + cy.deleteAllMonitors(); +}; + +describe('CompositeLevelMonitor', () => { + before(() => { + clearAll(); + + // Create indices + cy.createIndexByName( + sample_index_1, + sampleCompositeJson.sample_composite_index + ); + cy.createIndexByName( + sample_index_2, + sampleCompositeJson.sample_composite_index + ); + + // Create associated monitors + cy.createMonitor(sampleCompositeJson.sample_composite_associated_monitor_1); + cy.createMonitor(sampleCompositeJson.sample_composite_associated_monitor_2); + cy.createMonitor(sampleCompositeJson.sample_composite_associated_monitor_3); + }); + + beforeEach(() => { + // Set welcome screen tracking to false + localStorage.setItem('home:welcome:show', 'false'); + }); + + describe('can be created', () => { + beforeEach(() => { + // Visit Alerting OpenSearch Dashboards + cy.visit(`${BASE_PATH}/app/${ALERTING_PLUGIN_NAME}#/monitors`); + + // Common text to wait for to confirm page loaded, give up to 20 seconds for initial load + cy.contains('Create monitor', { timeout: 20000 }); + + // Go to create monitor page + cy.contains('Create monitor').click({ force: true }); + + // Select the Composite-Level Monitor type + cy.get('[data-test-subj="compositeLevelMonitorRadioCard"]').click({ + force: true, + }); + }); + + it('by visual editor', () => { + // Select visual editor for method of definition + cy.get('[data-test-subj="visualEditorRadioCard"]').click({ force: true }); + + // Wait for input to load and then type in the monitor name + cy.get('input[name="name"]').type(SAMPLE_VISUAL_EDITOR_MONITOR); + + // Select associated monitors + cy.get('[data-test-subj="monitors_list_0"]') + .type('monitorOne', { delay: 50 }) + .type('{enter}'); + cy.get('[data-test-subj="monitors_list_1"]') + .type('monitorTwo', { delay: 50 }) + .type('{enter}'); + + cy.get('button').contains('Add trigger').click({ force: true }); + + // Type trigger name + cy.get('[data-test-subj="composite-trigger-name"]') + .type('{selectall}') + .type('{backspace}') + .type('Composite trigger'); + + cy.intercept('api/alerting/workflows').as('createMonitorRequest'); + cy.intercept(`api/alerting/monitors?*`).as('getMonitorsRequest'); + cy.get('button').contains('Create').click({ force: true }); + + // Wait for monitor to be created + cy.wait('@createMonitorRequest').then((interceptor) => { + const monitorId = interceptor.response.body.resp._id; + + cy.contains('Loading monitors'); + cy.wait('@getMonitorsRequest').then((interceptor) => { + const monitors = interceptor.response.body.monitors; + const monitor1 = monitors.filter( + (monitor) => monitor.name === 'monitor_1' + ); + const monitor2 = monitors.filter( + (monitor) => monitor.name === 'monitor_2' + ); + + // Let monitor's table render the rows before querying + cy.wait(1000).then(() => { + cy.get('table tbody td').contains(SAMPLE_VISUAL_EDITOR_MONITOR); + + // Load sample data + cy.insertDocumentToIndex( + sample_index_1, + undefined, + sampleCompositeJson.sample_composite_associated_index_document + ); + cy.insertDocumentToIndex( + sample_index_2, + undefined, + sampleCompositeJson.sample_composite_associated_index_document + ); + + cy.wait(1000).then(() => { + cy.executeCompositeMonitor(monitorId); + monitor1[0] && cy.executeMonitor(monitor1[0].id); + monitor2[0] && cy.executeMonitor(monitor2[0].id); + + cy.get('[role="tab"]').contains('Alerts').click({ force: true }); + cy.get('table tbody td').contains('Composite trigger'); + }); + }); + }); + }); + }); + }); + + describe('can be edited', () => { + beforeEach(() => { + const body = { + size: 200, + query: { + match_all: {}, + }, + }; + cy.request({ + method: 'GET', + url: `${Cypress.env('opensearch')}${ALERTING_API.MONITOR_BASE}/_search`, + failOnStatusCode: false, // In case there is no alerting config index in cluster, where the status code is 404 + body, + }).then((response) => { + if (response.status === 200) { + const monitors = response.body.hits.hits; + const createdMonitor = _.find( + monitors, + (monitor) => monitor._source.name === SAMPLE_VISUAL_EDITOR_MONITOR + ); + if (createdMonitor) { + cy.visit( + `${BASE_PATH}/app/${ALERTING_PLUGIN_NAME}#/monitors/${createdMonitor._id}?action=update-monitor&type=workflow` + ); + } else { + cy.log( + 'Failed to get created monitor ', + SAMPLE_VISUAL_EDITOR_MONITOR + ); + throw new Error( + `Failed to get created monitor ${SAMPLE_VISUAL_EDITOR_MONITOR}` + ); + } + } else { + cy.log('Failed to get all monitors.', response); + } + }); + }); + }); + + after(() => clearAll()); +}); diff --git a/cypress/integration/plugins/alerting-dashboards-plugin/monitors_dashboard_spec.js b/cypress/integration/plugins/alerting-dashboards-plugin/monitors_dashboard_spec.js new file mode 100644 index 000000000..1c53b1d37 --- /dev/null +++ b/cypress/integration/plugins/alerting-dashboards-plugin/monitors_dashboard_spec.js @@ -0,0 +1,152 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + ALERTING_INDEX, + ALERTING_PLUGIN_NAME, +} from '../../../utils/plugins/alerting-dashboards-plugin/constants'; +import sampleAlertsFlyoutBucketMonitor from '../../../fixtures/plugins/alerting-dashboards-plugin/sample_alerts_flyout_bucket_level_monitor.json'; +import sampleAlertsFlyoutQueryMonitor from '../../../fixtures/plugins/alerting-dashboards-plugin/sample_alerts_flyout_query_level_monitor.json'; +import sampleClusterMetricsHealthMonitor from '../../../fixtures/plugins/alerting-dashboards-plugin/sample_cluster_metrics_health_monitor.json'; +import sampleClusterMetricsStatsMonitor from '../../../fixtures/plugins/alerting-dashboards-plugin/sample_cluster_metrics_stats_monitor.json'; +import { BASE_PATH } from '../../../../cypress/utils/base_constants'; + +const queryMonitor = { + ...sampleAlertsFlyoutQueryMonitor, + id: 'monitors_dashboard_cypress_query_level', + name: 'monitors_dashboard_cypress_query_level', + enabled: false, +}; +const bucketMonitor = { + ...sampleAlertsFlyoutBucketMonitor, + id: 'monitors_dashboard_cypress_bucket_level', + name: 'monitors_dashboard_cypress_bucket_level', + enabled: false, +}; +const clusterHealthMonitor = { + ...sampleClusterMetricsHealthMonitor, + id: 'monitors_dashboard_cypress_cluster_health', + name: 'monitors_dashboard_cypress_cluster_health', + enabled: false, + triggers: [ + { + query_level_trigger: { + id: 'WJmlA4kBIezNcMbMwnFg', + name: 'sample_cluster_metrics_health_monitor-trigger1', + severity: '1', + condition: { + script: { + source: 'ctx.results[0].status != "green"', + lang: 'painless', + }, + }, + actions: [], + }, + }, + ], +}; +const clusterStatsMonitor = { + ...sampleClusterMetricsStatsMonitor, + enabled: false, + id: 'monitors_dashboard_cypress_cluster_stats', + name: 'monitors_dashboard_cypress_cluster_stats', +}; +const testMonitors = [ + { + monitor: queryMonitor, + expectedAlertsCount: 1, + triggerName: queryMonitor.triggers[0].query_level_trigger.name, + }, + { + monitor: bucketMonitor, + expectedAlertsCount: 46, + triggerName: bucketMonitor.triggers[0].bucket_level_trigger.name, + }, + { + monitor: clusterHealthMonitor, + expectedAlertsCount: 1, + triggerName: clusterHealthMonitor.triggers[0].query_level_trigger.name, + }, + { + monitor: clusterStatsMonitor, + expectedAlertsCount: 1, + triggerName: clusterStatsMonitor.triggers[0].query_level_trigger.name, + }, +]; + +describe('Monitors dashboard page', () => { + before(() => { + // Delete any existing monitors + cy.deleteAllMonitors() + .then(() => { + // Load sample data + cy.loadSampleEcommerceData(); + }) + .then(() => { + // Short wait to reduce flakiness while ecommerce data is loaded + cy.wait(5000); + + // Create the test monitors + testMonitors.forEach((entry) => + cy.createAndExecuteMonitor(entry.monitor) + ); + }); + + // Visit Alerting OpenSearch Dashboards + cy.visit(`${BASE_PATH}/app/${ALERTING_PLUGIN_NAME}#/monitors`); + }); + + beforeEach(() => { + // Refresh Alerting OpenSearch Dashboards + cy.visit(`${BASE_PATH}/app/${ALERTING_PLUGIN_NAME}#/monitors`); + + // Common text to wait for to confirm page loaded, give up to 20 seconds for initial load + cy.contains('Create monitor', { timeout: 20000 }); + }); + + it('Displays expected number of alerts', () => { + // Ensure the 'Monitor name' column is sorted in ascending order by sorting another column first + cy.contains('Last updated by').click({ force: true }); + cy.contains('Monitor name').click({ force: true }); + + testMonitors.forEach((entry) => { + cy.get('tbody > tr') + .filter(`:contains(${entry.monitor.name})`, { timeout: 20000 }) + .within(() => { + cy.get('[class="euiTableRowCell"]') + .filter(':contains(Latest alert)', { timeout: 20000 }) + .should('contain', entry.triggerName); + + cy.get('[class="euiTableRowCell"]') + .filter(':contains(State)', { timeout: 20000 }) + .should('contain', 'Disabled'); + + cy.get('[class="euiTableRowCell"]') + .filter(':contains(Active)', { timeout: 20000 }) + .should('contain', entry.expectedAlertsCount); + + cy.get('[class="euiTableRowCell"]') + .filter(':contains(Acknowledged)', { timeout: 20000 }) + .should('contain', 0); + + cy.get('[class="euiTableRowCell"]') + .filter(':contains(Errors)', { timeout: 20000 }) + .should('contain', 0); + + cy.get('[class="euiTableRowCell"]') + .filter(':contains(Ignored)', { timeout: 20000 }) + .should('contain', 0); + }); + }); + }); + + after(() => { + // Delete all monitors + cy.deleteAllMonitors(); + + // Delete sample data + cy.deleteIndexByName(ALERTING_INDEX.SAMPLE_DATA_ECOMMERCE); + }); +}); diff --git a/cypress/integration/plugins/alerting-dashboards-plugin/query_level_monitor_spec.js b/cypress/integration/plugins/alerting-dashboards-plugin/query_level_monitor_spec.js index 9ff1aac74..5a7a9d858 100644 --- a/cypress/integration/plugins/alerting-dashboards-plugin/query_level_monitor_spec.js +++ b/cypress/integration/plugins/alerting-dashboards-plugin/query_level_monitor_spec.js @@ -34,7 +34,12 @@ const addVisualQueryLevelTrigger = ( thresholdValue ) => { // Click 'Add trigger' button - cy.contains('Add trigger', { timeout: 20000 }).click({ force: true }); + if (triggerIndex === 0) + cy.contains('Add trigger', { timeout: 20000 }).click({ force: true }); + else + cy.contains('Add another trigger', { timeout: 20000 }).click({ + force: true, + }); if (isEdit) { // TODO: Passing button props in EUI accordion was added in newer versions (31.7.0+). @@ -244,6 +249,10 @@ describe('Query-Level Monitors', () => { // Click the Delete button cy.contains('Delete').click({ force: true }); + cy.wait(1000); + cy.get('[data-test-subj="confirmModalConfirmButton"]').click({ + force: true, + }); // Confirm we can see an empty monitor list cy.contains('There are no existing monitors'); diff --git a/cypress/utils/plugins/alerting-dashboards-plugin/commands.js b/cypress/utils/plugins/alerting-dashboards-plugin/commands.js index 962566271..b286ecba6 100644 --- a/cypress/utils/plugins/alerting-dashboards-plugin/commands.js +++ b/cypress/utils/plugins/alerting-dashboards-plugin/commands.js @@ -54,6 +54,39 @@ Cypress.Commands.add('createAndExecuteMonitor', (monitorJSON) => { }); }); +Cypress.Commands.add('executeMonitor', (monitorID) => { + cy.request( + 'POST', + `${Cypress.env('openSearchUrl')}${ + ALERTING_API.MONITOR_BASE + }/${monitorID}/_execute` + ); +}); + +Cypress.Commands.add('executeCompositeMonitor', (monitorID) => { + cy.request( + 'POST', + `${Cypress.env('openSearchUrl')}${ + ALERTING_API.WORKFLOW_BASE + }/${monitorID}/_execute` + ); +}); + +Cypress.Commands.add('deleteAllAlerts', () => { + cy.request({ + method: 'POST', + url: `${Cypress.env( + 'openSearchUrl' + )}/.opendistro-alerting-alert*/_delete_by_query`, + body: { + query: { + match_all: {}, + }, + }, + failOnStatusCode: false, + }); +}); + Cypress.Commands.add('deleteMonitorByName', (monitorName) => { const body = { query: { @@ -84,7 +117,7 @@ Cypress.Commands.add('deleteAllMonitors', () => { size: 200, query: { exists: { - field: 'monitor', + match_all: {}, }, }, }; @@ -95,13 +128,21 @@ Cypress.Commands.add('deleteAllMonitors', () => { body, }).then((response) => { if (response.status === 200) { - for (let i = 0; i < response.body.hits.total.value; i++) { - cy.request( - 'DELETE', - `${Cypress.env('openSearchUrl')}${ALERTING_API.MONITOR_BASE}/${ - response.body.hits.hits[i]._id - }` - ); + const monitors = response.body.hits.hits.sort((monitor) => + monitor._source.type === 'workflow' ? -1 : 1 + ); + for (let i = 0; i < monitors.length; i++) { + if (monitors[i]._id) { + cy.request({ + method: 'DELETE', + url: `${Cypress.env('openSearchUrl')}${ + monitors[i]._source.type === 'workflow' + ? ALERTING_API.WORKFLOW_BASE + : ALERTING_API.MONITOR_BASE + }/${monitors[i]._id}`, + failOnStatusCode: false, + }); + } } } else { cy.log('Failed to get all monitors.', response); @@ -109,12 +150,16 @@ Cypress.Commands.add('deleteAllMonitors', () => { }); }); -Cypress.Commands.add('createIndexByName', (indexName) => { - cy.request('PUT', `${Cypress.env('openSearchUrl')}/${indexName}`); +Cypress.Commands.add('createIndexByName', (indexName, body = {}) => { + cy.request('PUT', `${Cypress.env('openSearchUrl')}/${indexName}`, body); }); Cypress.Commands.add('deleteIndexByName', (indexName) => { - cy.request('DELETE', `${Cypress.env('openSearchUrl')}/${indexName}`); + cy.request({ + method: 'DELETE', + url: `${Cypress.env('openSearchUrl')}/${indexName}`, + failOnStatusCode: false, + }); }); Cypress.Commands.add( diff --git a/cypress/utils/plugins/alerting-dashboards-plugin/constants.js b/cypress/utils/plugins/alerting-dashboards-plugin/constants.js index f4a1aad82..f2df921ac 100644 --- a/cypress/utils/plugins/alerting-dashboards-plugin/constants.js +++ b/cypress/utils/plugins/alerting-dashboards-plugin/constants.js @@ -12,6 +12,7 @@ export const ALERTING_INDEX = { export const ALERTING_API = { MONITOR_BASE: `${API_ROUTE_PREFIX}/monitors`, + WORKFLOW_BASE: `${API_ROUTE_PREFIX}/workflows`, DESTINATION_BASE: `${API_ROUTE_PREFIX}/destinations`, };