From fbcd1062a5e6af56d60ad0d3c7e909ef6748f061 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Sun, 10 May 2026 00:20:45 +0200 Subject: [PATCH] fix(ci): integration tests + e2e workflows green again MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two unrelated root-causes were keeping the Integration tests and e2e workflows red on every push to main since `d8c549e` (50+ runs). 1. Integration: `.github/workflows/integration.yml` had `ENCRYPTION_KEY: 0000…` as a bare YAML scalar. YAML 1.2 parses a run of zeros as the integer 0 — so the runner exported `ENCRYPTION_KEY=0`, and `decodeKey()` rejected it as "must be hex (>= 32 chars) or base64 (32 bytes)". 6 tests in admin-backups-* and integration-status failed at module load. Quoting the value forces a literal string. 2. e2e: the global-setup seeded user had `onboarding_tour_completed` left at its default `false`, so the spotlight tour mounted a full-viewport `role="dialog"` overlay (z-index 200) on every authenticated spec. Playwright's hit-test then caught the tour's skip-button instead of the intended target — every spec that clicked sidebar / quick-add / logo timed out. Setting the column to `true` in the upsert keeps the tour off the dashboard for the rest of the suite. The two `onboarding-flicker.spec.ts` mocks that fulfil `/api/auth/me` from inside the spec also need the field, since they replace the seeded payload. Workflow change is comment + quoted strings only — no secrets, no runtime cost. --- .github/workflows/integration.yml | 12 +++++++++--- e2e/onboarding-flicker.spec.ts | 6 ++++++ e2e/setup/global-setup.ts | 18 ++++++++++++++---- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 2ec520525..68bbfdef1 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -29,6 +29,12 @@ jobs: # The integration suite boots a Postgres testcontainer and points # Prisma at it via DATABASE_URL set inside `tests/integration/setup.ts`. # These env vars satisfy code paths that read them at module load. - API_TOKEN_HMAC_KEY: integration-tests-do-not-use-in-prod - ENCRYPTION_KEY: 0000000000000000000000000000000000000000000000000000000000000000 - SESSION_SECRET: integration-tests + # + # ENCRYPTION_KEY MUST be quoted: YAML 1.2 parses an unquoted run of + # zeros as the integer 0, which then arrives in `process.env` as the + # string "0" — failing crypto's hex/base64 length check and breaking + # the backups + integration-status suites for 50+ runs (regression + # since v1.4.13). Quoting forces a literal scalar. + API_TOKEN_HMAC_KEY: "integration-tests-do-not-use-in-prod" + ENCRYPTION_KEY: "0000000000000000000000000000000000000000000000000000000000000000" + SESSION_SECRET: "integration-tests" diff --git a/e2e/onboarding-flicker.spec.ts b/e2e/onboarding-flicker.spec.ts index 888aa0ded..b3fef3e94 100644 --- a/e2e/onboarding-flicker.spec.ts +++ b/e2e/onboarding-flicker.spec.ts @@ -46,6 +46,7 @@ test.describe("dashboard onboarding card flicker guard", () => { gender: "MALE", timezone: "Europe/Berlin", onboardingCompletedAt: "2025-01-01T00:00:00.000Z", + onboardingTourCompleted: true, gravatarUrl: null, glucoseUnit: "mg/dL", }, @@ -138,6 +139,11 @@ test.describe("dashboard onboarding card flicker guard", () => { gender: null, timezone: "Europe/Berlin", onboardingCompletedAt: null, + // Suppress the spotlight tour overlay during this spec so + // the onboarding-card visibility probe is not occluded by + // the tour's z-200 dialog. The tour itself is exercised by + // a dedicated spec — not in scope here. + onboardingTourCompleted: true, gravatarUrl: null, glucoseUnit: "mg/dL", }, diff --git a/e2e/setup/global-setup.ts b/e2e/setup/global-setup.ts index 84276af4d..0d6daf02a 100644 --- a/e2e/setup/global-setup.ts +++ b/e2e/setup/global-setup.ts @@ -89,7 +89,14 @@ export default async function globalSetup(config: FullConfig): Promise { // SET clause clears any leftover Codex / insights state from a // previous run so the "Settings → AI" spec sees the disconnected // baseline; we also stamp `onboarding_completed_at` so the auth - // shell does not bounce the user to /onboarding. + // shell does not bounce the user to /onboarding, AND set + // `onboarding_tour_completed = true` so the spotlight tour does + // NOT auto-launch on the dashboard. Without that flag the tour + // mounts a full-viewport `role="dialog"` overlay with `z-index:200` + // that intercepts every pointer event — every authenticated spec + // that tries to click a header / sidebar / quick-add button times + // out (50+ failed CI runs since v1.4.13). Specs that need the + // tour can opt back in by mocking `/api/auth/me`. await pool.query( `INSERT INTO users (id, username, email, password_hash, role, @@ -97,13 +104,15 @@ export default async function globalSetup(config: FullConfig): Promise { height_cm, date_of_birth, gender, codex_connection_status, insights_privacy_mode, - onboarding_completed_at) + onboarding_completed_at, + onboarding_tour_completed) VALUES ($1, $2, $3, $4, $5, $6, $6, 180, $7, 'MALE', 'disconnected', 'aggregated', - $6) + $6, + true) ON CONFLICT (username) DO UPDATE SET email = EXCLUDED.email, password_hash = EXCLUDED.password_hash, @@ -119,7 +128,8 @@ export default async function globalSetup(config: FullConfig): Promise { codex_connection_status = 'disconnected', insights_cached_at = NULL, insights_cached_text = NULL, - onboarding_completed_at = EXCLUDED.onboarding_completed_at`, + onboarding_completed_at = EXCLUDED.onboarding_completed_at, + onboarding_tour_completed = EXCLUDED.onboarding_tour_completed`, [ cuid(), E2E_USER.username,