From 73b83666e21f768b96d5cbad51607323b0090ca8 Mon Sep 17 00:00:00 2001 From: David Rajnoha Date: Wed, 24 Sep 2025 14:03:23 +0200 Subject: [PATCH] fix(cypress): metric change workaround cluster:health:components:map was changed to cluster_health_components_map. This commit updates the mocking to return the new metric and supports running tests in environments with old backend and new frontend. This workaround is by default disabled and will be fully removed after the COO 1.3.0 release --- web/cypress.config.ts | 1 + web/cypress/README.md | 5 ++ web/cypress/configure-env.sh | 9 +++ .../e2e/incidents/00.coo_incidents_e2e.cy.ts | 1 + web/cypress/e2e/incidents/01.incidents.cy.ts | 2 + web/cypress/fixtures/export.sh | 3 + .../README.md | 36 ++++++++++++ .../mock-generators.ts | 5 +- .../prometheus-mocks.ts | 56 ++++++++++++++++++- 9 files changed, 114 insertions(+), 4 deletions(-) diff --git a/web/cypress.config.ts b/web/cypress.config.ts index 47aad4b4..172007a4 100644 --- a/web/cypress.config.ts +++ b/web/cypress.config.ts @@ -22,6 +22,7 @@ export default defineConfig({ LOGIN_USERNAME: process.env.CYPRESS_LOGIN_USERS.split(',')[0].split(':')[0], LOGIN_PASSWORD: process.env.CYPRESS_LOGIN_USERS.split(',')[0].split(':')[1], TIMEZONE: process.env.CYPRESS_TIMEZONE || 'UTC', + MOCK_NEW_METRICS: process.env.CYPRESS_MOCK_NEW_METRICS || 'false', typeDelay: 200, }, fixturesFolder: 'cypress/fixtures', diff --git a/web/cypress/README.md b/web/cypress/README.md index 2b06d5d2..37366440 100644 --- a/web/cypress/README.md +++ b/web/cypress/README.md @@ -67,6 +67,11 @@ Set the following var to specify the cluster timezone for incident timeline calc export CYPRESS_TIMEZONE= ``` +Set the following var to transform old metric names to new format in mocks (temporary workaround for testing against locally built instances). +```bash +export CYPRESS_MOCK_NEW_METRICS=false +``` + Set the following var to enable Cypress session management for faster test execution. ```bash export CYPRESS_SESSION=true diff --git a/web/cypress/configure-env.sh b/web/cypress/configure-env.sh index 5db5df4a..96cf978f 100755 --- a/web/cypress/configure-env.sh +++ b/web/cypress/configure-env.sh @@ -173,6 +173,7 @@ print_current_config() { print_var "CYPRESS_CUSTOM_COO_BUNDLE_IMAGE" "${CYPRESS_CUSTOM_COO_BUNDLE_IMAGE-}" print_var "CYPRESS_MCP_CONSOLE_IMAGE" "${CYPRESS_MCP_CONSOLE_IMAGE-}" print_var "CYPRESS_TIMEZONE" "${CYPRESS_TIMEZONE-}" + print_var "CYPRESS_MOCK_NEW_METRICS" "${CYPRESS_MOCK_NEW_METRICS-}" print_var "CYPRESS_SESSION" "${CYPRESS_SESSION-}" print_var "CYPRESS_SKIP_KBV_INSTALL" "${CYPRESS_SKIP_KBV_INSTALL-}" print_var "CYPRESS_KBV_UI_INSTALL" "${CYPRESS_KBV_UI_INSTALL-}" @@ -220,6 +221,7 @@ main() { local def_custom_coo_bundle=${CYPRESS_CUSTOM_COO_BUNDLE_IMAGE-} local def_mcp_console_image=${CYPRESS_MCP_CONSOLE_IMAGE-} local def_timezone=${CYPRESS_TIMEZONE-} + local def_mock_new_metrics=${CYPRESS_MOCK_NEW_METRICS-} local def_session=${CYPRESS_SESSION-} local def_skip_kbv=${CYPRESS_SKIP_KBV_INSTALL-} local def_kbv_ui_install=${CYPRESS_KBV_UI_INSTALL-} @@ -416,6 +418,11 @@ main() { local timezone timezone=$(ask "Cluster timezone (CYPRESS_TIMEZONE)" "${def_timezone:-UTC}") + local mock_new_metrics_ans + mock_new_metrics_ans=$(ask_yes_no "Transform old metric names to new format in mocks? (sets CYPRESS_MOCK_NEW_METRICS)" "$(bool_to_default_yn "$def_mock_new_metrics")") + local mock_new_metrics="false" + [[ "$mock_new_metrics_ans" == "y" ]] && mock_new_metrics="true" + local session_ans session_ans=$(ask_yes_no "Enable Cypress session management for faster test execution? (sets CYPRESS_SESSION)" "$(bool_to_default_yn "$def_session")") local session="false" @@ -464,6 +471,7 @@ main() { if [[ -n "$timezone" ]]; then export_lines+=("export CYPRESS_TIMEZONE='$(printf %s "$timezone" | escape_for_single_quotes)'" ) fi + export_lines+=("export CYPRESS_MOCK_NEW_METRICS='$(printf %s "$mock_new_metrics" | escape_for_single_quotes)'" ) export_lines+=("export CYPRESS_SESSION='$(printf %s "$session" | escape_for_single_quotes)'" ) if [[ -n "$skip_kbv_install" ]]; then @@ -511,6 +519,7 @@ main() { [[ -n "${CYPRESS_CUSTOM_COO_BUNDLE_IMAGE-}$custom_coo_bundle" ]] && echo " CYPRESS_CUSTOM_COO_BUNDLE_IMAGE=${CYPRESS_CUSTOM_COO_BUNDLE_IMAGE:-$custom_coo_bundle}" [[ -n "${CYPRESS_MCP_CONSOLE_IMAGE-}$mcp_console_image" ]] && echo " CYPRESS_MCP_CONSOLE_IMAGE=${CYPRESS_MCP_CONSOLE_IMAGE:-$mcp_console_image}" [[ -n "${CYPRESS_TIMEZONE-}$timezone" ]] && echo " CYPRESS_TIMEZONE=${CYPRESS_TIMEZONE:-$timezone}" + echo " CYPRESS_MOCK_NEW_METRICS=${CYPRESS_MOCK_NEW_METRICS:-$mock_new_metrics}" echo " CYPRESS_SESSION=${CYPRESS_SESSION:-$session}" echo " CYPRESS_SKIP_KBV_INSTALL=${CYPRESS_SKIP_KBV_INSTALL:-$skip_kbv_install}" echo " CYPRESS_KBV_UI_INSTALL=${CYPRESS_KBV_UI_INSTALL:-$kbv_ui_install}" diff --git a/web/cypress/e2e/incidents/00.coo_incidents_e2e.cy.ts b/web/cypress/e2e/incidents/00.coo_incidents_e2e.cy.ts index 7cbdc3c1..7ae7ae5b 100644 --- a/web/cypress/e2e/incidents/00.coo_incidents_e2e.cy.ts +++ b/web/cypress/e2e/incidents/00.coo_incidents_e2e.cy.ts @@ -37,6 +37,7 @@ describe('BVT: Incidents - e2e', () => { }); it('1. Admin perspective - Incidents page - Incident with custom alert lifecycle', () => { + cy.transformMetrics(); cy.log('1.1 Navigate to Incidents page and clear filters'); incidentsPage.goTo(); incidentsPage.clearAllFilters(); diff --git a/web/cypress/e2e/incidents/01.incidents.cy.ts b/web/cypress/e2e/incidents/01.incidents.cy.ts index 4ba586c4..36718ccb 100644 --- a/web/cypress/e2e/incidents/01.incidents.cy.ts +++ b/web/cypress/e2e/incidents/01.incidents.cy.ts @@ -40,6 +40,8 @@ describe('BVT: Incidents - UI', () => { beforeEach(() => { cy.log('Navigate to Observe → Incidents'); incidentsPage.goTo(); + // Temporary workaround for testing against locally built instances. + cy.transformMetrics(); }); it('1. Admin perspective - Incidents page - Toolbar and charts toggle functionality', () => { diff --git a/web/cypress/fixtures/export.sh b/web/cypress/fixtures/export.sh index bc8dd511..13671dcc 100755 --- a/web/cypress/fixtures/export.sh +++ b/web/cypress/fixtures/export.sh @@ -36,5 +36,8 @@ export CYPRESS_MCP_CONSOLE_IMAGE= # Set the following var to specify the cluster timezone for incident timeline calculations. Defaults to UTC if not specified. export CYPRESS_TIMEZONE= +# Set the following var to transform old metric names to new format in mocks (for testing against locally built instances) +export CYPRESS_MOCK_NEW_METRICS=false + # Set the following var to enable Cypress session management for faster test execution. export CYPRESS_SESSION=true diff --git a/web/cypress/support/incidents_prometheus_query_mocks/README.md b/web/cypress/support/incidents_prometheus_query_mocks/README.md index c6bcc5cc..2020f2f6 100644 --- a/web/cypress/support/incidents_prometheus_query_mocks/README.md +++ b/web/cypress/support/incidents_prometheus_query_mocks/README.md @@ -41,6 +41,16 @@ const incidents: IncidentDefinition[] = [ cy.mockIncidents(incidents); ``` +### Metric Transformation for Locally Built Instances + +```typescript +// Transform old metric names to new format (for testing against locally built instances) +cy.transformMetrics(); + +// Then visit the page or perform actions that trigger Prometheus queries +cy.visit('/monitoring/incidents'); +``` + ## Key Features - **Schema Validation**: All YAML fixtures are validated against JSON Schema @@ -48,6 +58,7 @@ cy.mockIncidents(incidents); - **Timeline Support**: Define incident start/end times and severity changes - **Timezone Configuration**: Set timezone via `CYPRESS_TIMEZONE` environment variable - **Multiple Query Types**: Supports both `cluster:health:components:map` and `ALERTS` queries +- **Metric Transformation**: Transform old metric names to new format via `cy.transformMetrics()` ## File Structure @@ -104,6 +115,31 @@ export CYPRESS_TIMEZONE="Asia/Tokyo" Default: UTC (if `CYPRESS_TIMEZONE` is not set) +### Metric Transformation + +Enable transformation of old metric names to new format for testing against locally built instances: + +```bash +export CYPRESS_MOCK_NEW_METRICS=true +``` + +When enabled, `cy.transformMetrics()` will intercept Prometheus queries and transform both request and response: +- **Request**: `cluster:health:components:map` → `cluster_health_components_map` +- **Response**: `cluster:health:components:map` → `cluster_health_components_map` + +**Usage:** +```typescript +// Call before visiting pages that make Prometheus queries +cy.transformMetrics(); +cy.visit('/monitoring/incidents'); +``` + +**Use Cases:** +- **`CYPRESS_MOCK_NEW_METRICS=false`** (default): Test against current release/backend +- **`CYPRESS_MOCK_NEW_METRICS=true`**: Test against locally built instances with new metric format + +Default: `false` (if `CYPRESS_MOCK_NEW_METRICS` is not set) + ## YAML Fixture Format ```yaml diff --git a/web/cypress/support/incidents_prometheus_query_mocks/mock-generators.ts b/web/cypress/support/incidents_prometheus_query_mocks/mock-generators.ts index 635cf6f6..76a3ce46 100644 --- a/web/cypress/support/incidents_prometheus_query_mocks/mock-generators.ts +++ b/web/cypress/support/incidents_prometheus_query_mocks/mock-generators.ts @@ -2,6 +2,7 @@ import { IncidentDefinition, PrometheusResult } from './types'; import { severityToValue, parseQueryLabels } from './utils'; import { nowInClusterTimezone } from './utils'; +import { NEW_METRIC_NAME, OLD_METRIC_NAME } from './prometheus-mocks'; /** * Generates 5-minute interval timestamps between start and end time @@ -98,6 +99,8 @@ export function createIncidentMock(incidents: IncidentDefinition[], query?: stri // Parse query to extract label selectors if provided const queryLabels = query ? parseQueryLabels(query) : {}; + const versioned_metric = query?.includes(NEW_METRIC_NAME) ? NEW_METRIC_NAME : OLD_METRIC_NAME; + incidents.forEach(incident => { // Filter incidents based on query parameters if (queryLabels.group_id) { @@ -113,7 +116,7 @@ export function createIncidentMock(incidents: IncidentDefinition[], query?: stri incident.alerts.forEach(alert => { const metric: Record = { - __name__: 'cluster:health:components:map', + __name__: versioned_metric, component: incident.component, layer: incident.layer, group_id: incident.id, diff --git a/web/cypress/support/incidents_prometheus_query_mocks/prometheus-mocks.ts b/web/cypress/support/incidents_prometheus_query_mocks/prometheus-mocks.ts index a6f79ed2..4df5c8a8 100644 --- a/web/cypress/support/incidents_prometheus_query_mocks/prometheus-mocks.ts +++ b/web/cypress/support/incidents_prometheus_query_mocks/prometheus-mocks.ts @@ -7,17 +7,22 @@ declare global { interface Chainable { mockIncidents(incidents: IncidentDefinition[]): Chainable; mockIncidentFixture(fixturePath: string): Chainable; + transformMetrics(): Chainable; } } } +export const NEW_METRIC_NAME = 'cluster_health_components_map'; +export const OLD_METRIC_NAME = 'cluster:health:components:map'; +const MOCK_QUERY = '/api/prometheus/api/v1/query_range*'; + /** * Main mocking function - sets up cy.intercept for Prometheus query_range API * Intercepts the query_range API and returns the mock data for the incidents * @param incidents */ export function mockPrometheusQueryRange(incidents: IncidentDefinition[]): void { - cy.intercept('GET', '/api/prometheus/api/v1/query_range*', (req) => { + cy.intercept('GET', MOCK_QUERY, (req) => { const url = new URL(req.url, window.location.origin); const query = url.searchParams.get('query') || ''; @@ -25,14 +30,17 @@ export function mockPrometheusQueryRange(incidents: IncidentDefinition[]): void console.log(`Query: ${query}`); let results: any[]; + + const versioned_metric = query.includes(NEW_METRIC_NAME) + ? NEW_METRIC_NAME: OLD_METRIC_NAME; - if (!(query.includes('cluster:health:components:map') || query.includes('ALERTS{'))) { + if (!(query.includes(versioned_metric) || query.includes('ALERTS{'))) { console.log(`Passing through non-mocked query`); req.continue(); return; } - results = query.includes('cluster:health:components:map') ? createIncidentMock(incidents, query) : createAlertDetailsMock(incidents, query); + results = query.includes(versioned_metric) ? createIncidentMock(incidents, query) : createAlertDetailsMock(incidents, query); const response: PrometheusResponse = { status: 'success', data: { @@ -85,4 +93,46 @@ Cypress.Commands.add('mockIncidentFixture', (fixturePath: string) => { // The mocking is not applied until the page is reloaded and the components fetch the new data cy.reload(); +}); + +Cypress.Commands.add('transformMetrics', () => { + cy.log('=== SETTING UP METRIC TRANSFORMATION ==='); + const mockNewMetrics = Cypress.env('MOCK_NEW_METRICS') === true; + + if (!mockNewMetrics) { + cy.log('CYPRESS_MOCK_NEW_METRICS is disabled, skipping transformation'); + return; + } + + cy.log('Transforming old metric queries to new format'); + + cy.intercept('GET', MOCK_QUERY, (req) => { + const url = new URL(req.url, window.location.origin); + const query = url.searchParams.get('query') || ''; + const hasNewMetric = query.includes(NEW_METRIC_NAME); + + if (hasNewMetric) { + const transformedQuery = query.replace(new RegExp(NEW_METRIC_NAME, 'g'), OLD_METRIC_NAME); + console.log(`Transforming metric query: ${query} -> ${transformedQuery}`); + + // Update the URL with the transformed query + url.searchParams.set('query', transformedQuery); + req.url = url.toString(); + + // Also transform the response to use new metric names + req.continue((res) => { + if (res.body?.data?.result) { + res.body.data.result.forEach((result: any) => { + if (result?.metric?.__name__ === OLD_METRIC_NAME) { + console.log(`Transforming response metric name: ${OLD_METRIC_NAME} -> ${NEW_METRIC_NAME}`); + result.metric.__name__ = NEW_METRIC_NAME; + } + }); + } + res.send(); + }); + } else { + req.continue(); + } + }); }); \ No newline at end of file