Files
openobserve/tests/ui-testing/playwright-tests/Correlation/correlation-cache.spec.js
T
Shrinath Rao a28dcda276 test(correlation): standardize e2e suite onto framework harness (phase 0 — reference pattern) (#13775)
## What & why

Begins consolidating the **correlation (service-streams) e2e suite**
onto the standard `tests/ui-testing` framework. Today those specs live
in a self-contained parallel harness (own configs, own auth, own `O2_*`
env vars, per-test orgs with no teardown, non-standard names) and are
**not registered in any CI matrix** — so they never run automatically.

This is **Phase 0**: land the shared standard-shaped API helper and
convert the first spec as the **reference pattern** for review, before
batch-converting the rest.

## Changes
- **`utils/correlation-api-helpers.js`** (new) — single source of truth
for correlation API plumbing. Auth via `authedRequest`/`getAuthHeaders`
(framework session + 401/403 self-heal), per-test **fresh-org create +
teardown**. No `test`/`expect`.
- **`Correlation/correlation-discovery.spec.js`** (new) — converted from
`journeyA`: `enhanced-baseFixtures`, `@correlation`/`@P0` tags,
Copyright + JSDoc header, `testLogger.testStart`, standard `ZO_*` env
and naming. Assertions ported **1:1** (DISC-01/02/03).
- Remove `Correlation/journeyA-discovery.api.spec.js` (superseded).

## Deliberate deviation (documented)
Correlation discovery / identity config / `_reset` are **org-global and
destructive**, so each test provisions a **fresh org** and tears it down
in `afterEach`. This keeps the isolation the feature genuinely needs
while fixing the real defect in the old suite: **orgs were created but
never deleted** (leak).

## Validation
- `node --check` clean; requires resolve.
- Collects cleanly under `playwright.config.js` (`--list` → 3 tests,
tagged).
- ⚠️ **Green run pending an enterprise env** (service_streams 403s on
OSS). Not wired into CI yet, so `main` stays green. **Draft** until
validated.

## Follow-ups (later phases)
Batch-convert remaining 4 API + 3 UI specs · revive
`correlationSettings.spec.js` against the new UI · register a dedicated
**Correlation** shard in the ENT `ci_matrix.ent.json` · delete
`corr-*.config.js`.
2026-08-12 07:43:41 +00:00

209 lines
7.7 KiB
JavaScript

// Copyright 2026 OpenObserve Inc.
/**
* Correlation — FE cache invalidation [P1] (enterprise-only)
*
* CACHE-01: semantic-group edits vs the FE 5-min module cache (KNOWN GAP —
* nothing calls clearCache() cross-page; marked test.fail so the suite stays
* green while the bug stands — an unexpected pass means it was fixed).
* CACHE-02: identity-config edits saved through the settings UI must reach the
* next correlate from the logs page in the same SPA session (fixed behavior).
*
* Shared plumbing: ../utils/correlation-api-helpers.js + correlation-ui-helpers.js.
*/
const { test, expect } = require("../utils/enhanced-baseFixtures.js");
const testLogger = require("../utils/test-logger.js");
const {
createCorrelationOrg,
deleteOrg,
ingestLogs,
saveIdentity,
getIdentity,
addSemanticGroup,
waitForServices,
} = require("../utils/correlation-api-helpers.js");
const {
BASE,
withSetupPage,
openLogsAndQuery,
openFirstRowDialog,
sniff,
waitFor,
} = require("../utils/correlation-ui-helpers.js");
const PageManager = require("../../pages/page-manager.js");
test.describe.configure({ mode: "serial" });
test.describe("Correlation — FE cache invalidation", { tag: ["@correlation", "@P1"] }, () => {
let org;
test.beforeEach(async ({}, testInfo) => {
testLogger.testStart(testInfo.title, testInfo.file);
test.setTimeout(600_000);
});
test.beforeAll(async ({ browser }) => {
test.setTimeout(600_000);
await withSetupPage(browser, async (page) => {
org = await createCorrelationOrg(page, "corr_ui_cache");
const save = await saveIdentity(page, org, {
sets: [{ id: "vm", label: "VM", distinguish_by: ["host"] }],
tracked_alias_ids: ["host"],
service_optional: false,
});
if (save.status !== 200) throw new Error(`cfg save failed: ${JSON.stringify(save.body)}`);
await ingestLogs(page, org, "d_ui_logs", [
{ service: "duisvc", host: "duih1", dc: "eu-9", environment: "prod", message: "m1" },
{ service: "duisvc", host: "duih1", dc: "eu-9", environment: "prod", message: "m2" },
]);
await waitForServices(
page,
org,
(r) => r.some((row) => row.service_name === "duisvc"),
"duisvc discovered",
);
});
});
test.afterAll(async ({ browser }) => {
await withSetupPage(browser, (page) => deleteOrg(page, org));
});
async function correlateViaDialog(page) {
const pm = new PageManager(page);
await openFirstRowDialog(page);
const metricsTab = pm.correlationDrawerPage.getCorrelatedMetricsTab();
await metricsTab.waitFor({ state: "visible", timeout: 15_000 });
await metricsTab.click();
await page.waitForTimeout(5000);
}
async function closeDialog(page) {
await page.keyboard.press("Escape");
await page.waitForTimeout(1000);
}
test("CACHE-01: semantic-group change within the 5-min cache window reaches the next correlate (KNOWN GAP) (TC-D3)", async ({
page,
}) => {
// Expected to FAIL today: no cross-page clearCache() caller exists, so the
// stale module cache wins. An unexpected pass = the invalidation got fixed.
test.fail(
true,
"FE semantic-groups module cache is never invalidated cross-page (plan CACHE-01)",
);
const traffic = sniff(page);
await openLogsAndQuery(page, org, "d_ui_logs");
// Correlate #1 primes the module caches; 'dc' maps to no group yet.
await correlateViaDialog(page);
await waitFor(() => traffic.correlateRequests.length >= 1, { label: "first correlate request" });
const req1 = traffic.correlateRequests[0];
expect(req1.available_dimensions.datacenter).toBeUndefined();
await closeDialog(page);
// Server-side truth changes (API): new group maps 'dc' → datacenter, and the
// identity config tracks it. The FE page is untouched.
await addSemanticGroup(page, org, {
id: "datacenter",
display: "Datacenter",
group: "Custom",
fields: ["dc"],
});
const upd = await saveIdentity(page, org, {
sets: [{ id: "vm", label: "VM", distinguish_by: ["host"] }],
tracked_alias_ids: ["host", "datacenter"],
service_optional: false,
});
expect(upd.status).toBe(200);
// Correlate #2, same page, well inside the 5-min TTL.
const before = traffic.correlateRequests.length;
await correlateViaDialog(page);
await waitFor(() => traffic.correlateRequests.length > before, {
label: "second correlate request",
});
const req2 = traffic.correlateRequests[traffic.correlateRequests.length - 1];
// Fresh-config behavior: the new group extracts dc=eu-9 into the request.
expect(
req2.available_dimensions.datacenter,
"correlate must use fresh semantic groups (stale 5-min cache if undefined)",
).toBe("eu-9");
});
test("CACHE-02: identity-config saved via settings UI reaches the next correlate in the same SPA session (TC-D4)", async ({
page,
}) => {
const pm = new PageManager(page);
const traffic = sniff(page);
await openLogsAndQuery(page, org, "d_ui_logs");
// Baseline correlate: 'environment' is extracted (default group) but the
// identity config doesn't track it → filtered out of the request.
await correlateViaDialog(page);
await waitFor(() => traffic.correlateRequests.length >= 1, {
label: "baseline correlate request",
});
const req1 = traffic.correlateRequests[0];
expect(
req1.available_dimensions.environment,
"baseline: environment must be filtered out (not tracked)",
).toBeUndefined();
await closeDialog(page);
// SPA-navigate to settings (same JS runtime) and add 'environment' to
// tracked aliases through the UI, then save.
await page.goto(`${BASE}/web/settings/correlation?org_identifier=${org}`, {
waitUntil: "domcontentloaded",
});
await pm.correlationSettingsPage.getDetectionRulesTab().click();
await page.waitForTimeout(2500);
const addFieldBtn = pm.correlationSettingsPage.getAddFieldButton();
await addFieldBtn.waitFor({ state: "visible", timeout: 15_000 });
await addFieldBtn.click();
const fieldTrigger = pm.correlationSettingsPage.getSelectAFieldTrigger().first();
await fieldTrigger.waitFor({ state: "visible", timeout: 10_000 });
await fieldTrigger.click();
await page.waitForTimeout(600);
await page.keyboard.type("Environment");
await page.waitForTimeout(800);
await pm.correlationSettingsPage
.getOptionByName(/Environment/i)
.first()
.click()
.catch(async () => {
await pm.correlationSettingsPage.getTextByPattern(/^Environment$/).first().click();
});
await page.waitForTimeout(500);
const saveBtn = pm.correlationSettingsPage.getSaveConfigurationButton().first();
await saveBtn.waitFor({ state: "visible", timeout: 10_000 });
await saveBtn.click();
await page.waitForTimeout(2500);
// The save landed server-side (environment now part of the identity).
const cfg = await getIdentity(page, org);
const identityIds = new Set([
...(cfg.tracked_alias_ids || []),
...(cfg.sets || []).flatMap((s) => s.distinguish_by || []),
]);
expect(identityIds.has("environment"), JSON.stringify(cfg)).toBe(true);
// Back to logs (SPA route change, no reload) → correlate again.
await openLogsAndQuery(page, org, "d_ui_logs");
const before = traffic.correlateRequests.length;
await correlateViaDialog(page);
await waitFor(() => traffic.correlateRequests.length > before, {
label: "post-save correlate request",
});
const req2 = traffic.correlateRequests[traffic.correlateRequests.length - 1];
expect(
req2.available_dimensions.environment,
"identity-config save must invalidate the FE cache (clearIdentityConfigCache)",
).toBe("prod");
});
});