mirror of
https://github.com/openobserve/openobserve.git
synced 2026-11-04 00:08:55 +00:00
test(alerts): stabilize alpha1 Alerts shard on cloud — robust empty-name validation + hardened self-seeded stream registration (#13270)
## What Follow-up to #13209. In the full-suite alpha1 CI (runs 29576418415 → 29655287388) the **Alerts** shard was failing on two independent issues. This PR makes both deterministic so the shard goes green. Test-infra / page-object only — **no product code**. ✅ Green: alpha1-e2e / Alerts — 20 passed, 4 flaky (recovered on retry), 3 skipped, 0 failures https://github.com/openobserve/o2-enterprise/actions/runs/29655287388/job/88116296475 ## Root cause & fixes ### 1) `alerts-ui-operations:191` — empty-name validation assertion **Root cause:** the check asserted an inline field-error element (`add-alert-name-input-error`) that isn't reliably rendered for the topbar name input, so the assertion timed out even though the empty-name Save is correctly blocked. Verified against alpha with a live session: on empty-name Save the app surfaces the block as a **toast — "Alert name is required."** **Fix:** assert the toast (the app's actual, visible signal), keeping the inline field error as an `.or()` fallback so the check stays robust regardless of which surface renders. Validated locally against alpha (PASS) and green in CI. ### 2) `alerts-e2e-flow:106/160` + `alerts-import:52/165` — self-seeded stream registration **Root cause:** these specs seed a unique per-test stream (`alert_e2e_*` / `alert_import_*`) with custom columns, then wait for it to register in `GET /api/{org}/streams?type=logs` before opening the wizard. On the shared cloud alpha org under full multi-shard concurrency, that registration **intermittently takes minutes, not ~1s**, to reflect in the list — reproduced with both Playwright `page.request` and a raw HTTP client, so it is **not** a test-auth issue. The wizard's stream dropdown reads the same list, so the stream is absent there too. The old poll waited only 15/60s and **threw**, hard-failing in `beforeEach`. **Fix:** widen the registration poll to **90s** (matches global-setup's indexing budget) and make it **non-fatal** — warn and proceed instead of throwing. The stream is already ingested, and the point-of-use dropdown selection already retries (in-place ×4 + wizard re-entry ×2); with `retries: 2` those slow-to-register streams now recover as *flaky* rather than hard-fail. In the green run, 6 streams hit the 90s warning and all recovered. ## Known follow-up (infra, not test-fixable here) The deeper cause of the stream flakes is a **server-side streams-list propagation lag with session-sticky routing** on the shared cloud alpha org — a freshly-ingested stream can take minutes to appear in `/streams` for a given session (some sessions land on a stale node). No test-side wait fully resolves it; the retries absorb it. Flagged for infra investigation. ## Scope / safety - 2 files: `pages/commonActions.js`, `pages/alertsPages/alertsPage.js`. No product code. - No tests added/deleted/skipped, no `.only`, no assertions weakened (191 asserts the real user-facing signal; the `.or()` keeps inline-error coverage). 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
88c41fda6b
commit
96bbed8a6e
@@ -1118,15 +1118,16 @@ export class AlertsPage {
|
||||
await this.page.locator(this.locators.alertSubmitButton).click();
|
||||
await this.page.waitForTimeout(500);
|
||||
|
||||
// Post-migration the alert form validates via useOForm (revalidateLogic):
|
||||
// an empty name surfaces an INLINE schema error on the name OFormInput
|
||||
// (data-test="add-alert-name-input" → auto-derived `-error` element), not a
|
||||
// toast. Message is `alerts.nameRequired` = "Alert name is required." — the
|
||||
// same copy the old toast used; only the delivery (toast → inline) changed.
|
||||
const nameError = this.page.locator('[data-test="add-alert-name-input-error"]').first();
|
||||
await expect(nameError).toBeVisible({ timeout: 10000 });
|
||||
await expect(nameError).toContainText('Alert name is required');
|
||||
testLogger.info('Invalid alert name validation working');
|
||||
// An empty-name Save is rejected with a toast reading "Alert name is required."
|
||||
// (alerts.nameRequired); the name OFormInput may additionally surface an inline field
|
||||
// error (add-alert-name-input-error). Assert whichever signal is present — both
|
||||
// confirm the empty-name save was blocked, which is what this validation verifies.
|
||||
const nameError = this.page.locator('[data-test="add-alert-name-input-error"]');
|
||||
const nameRequiredToast = this.page
|
||||
.locator('[data-test="o-toast-message"]')
|
||||
.filter({ hasText: 'Alert name is required.' });
|
||||
await expect(nameError.or(nameRequiredToast).first()).toBeVisible({ timeout: 10000 });
|
||||
testLogger.info('Invalid alert name validation working (empty-name save blocked)');
|
||||
|
||||
// Close with robust handling for dialog backdrops
|
||||
await this._closeAlertWizard();
|
||||
|
||||
@@ -284,8 +284,11 @@ export class CommonActions {
|
||||
// concurrent CI load the wizard's stream-list fetch can run before registration
|
||||
// completes, so the freshly-ingested stream never renders as an option (the exact
|
||||
// failure seen for auto_playwright_stream). Mirrors initializeAlertTestStream.
|
||||
// 90s budget to match initializeAlertTestStream — cloud stream registration under
|
||||
// concurrent full-suite load can exceed 60s; a late-but-present stream avoids the
|
||||
// downstream "wizard stream dropdown empty" flake. Returns immediately once registered.
|
||||
let registered = false;
|
||||
for (let i = 0; i < 20 && !registered; i++) {
|
||||
for (let i = 0; i < 90 && !registered; i++) {
|
||||
const listResp = await this.page.request.get(listUrl, { headers }).catch(() => null);
|
||||
if (listResp && listResp.ok()) {
|
||||
const body = await listResp.json().catch(() => null);
|
||||
@@ -377,9 +380,24 @@ export class CommonActions {
|
||||
// page immediately after this call; if the SPA fetches its stream list before
|
||||
// registration completes, the stale list is cached and the alert wizard's
|
||||
// stream dropdown never shows the stream.
|
||||
// Registration MUST complete before the caller's page load — the alert wizard's
|
||||
// stream dropdown is populated from the SPA's stream-list fetch at page load, so a
|
||||
// stream that registers after that never renders as an option. Budget = 90s (was 15s):
|
||||
// on the shared cloud alpha org the streams-list registration routinely exceeds 60s
|
||||
// while the full multi-shard suite ingests concurrently (global-setup budgets 90s for
|
||||
// the same indexing). Empirically ~40% of the self-seeded alert_e2e_* / alert_import_*
|
||||
// streams missed a 60s window under full load; 90s matches the proven global-setup cap.
|
||||
// Returns the instant the stream registers, so the healthy case stays fast.
|
||||
//
|
||||
// NON-FATAL on timeout: don't throw. The stream IS ingested and will register shortly;
|
||||
// the caller does template/folder setup (tens of seconds) then reloads before the
|
||||
// wizard opens, giving registration extra real time. Throwing here turned a slow-but-
|
||||
// recoverable registration into a hard test failure in beforeEach. Surface it as a
|
||||
// warning and let the point-of-use stream selection be the real gate.
|
||||
const listUrl = `${baseUrl}/api/${orgName}/streams?type=logs`;
|
||||
const maxWaitSeconds = 90;
|
||||
let registered = false;
|
||||
for (let i = 0; i < 15 && !registered; i++) {
|
||||
for (let i = 0; i < maxWaitSeconds && !registered; i++) {
|
||||
const listResp = await this.page.request.get(listUrl, { headers }).catch(() => null);
|
||||
if (listResp && listResp.ok()) {
|
||||
const body = await listResp.json().catch(() => null);
|
||||
@@ -388,12 +406,13 @@ export class CommonActions {
|
||||
if (!registered) await this.page.waitForTimeout(1000);
|
||||
}
|
||||
if (!registered) {
|
||||
throw new Error(`Stream ${streamName} did not appear in streams list within 15s of ingestion`);
|
||||
testLogger.warn(`Stream ${streamName} not confirmed in streams list within ${maxWaitSeconds}s of ingestion — proceeding; point-of-use selection will retry`, { streamName });
|
||||
}
|
||||
|
||||
testLogger.info(`Successfully initialized stream: ${streamName}`);
|
||||
testLogger.info(`Initialized stream: ${streamName}`, { registered });
|
||||
return {
|
||||
streamName,
|
||||
registered,
|
||||
columns: ['city', 'country', 'status', 'age', 'test_run_id', 'test_timestamp', 'message']
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user