Mechanical reformat — no logic changes. 398 files changed, 19125 insertions(+), 16457 deletions(-). Prettier 3.8.4 with .prettierrc (singleQuote:true, semi:true, tabWidth:2, trailingComma:all, printWidth:100). Isolated per D-13-08 for reviewability.
122 lines
7.6 KiB
Markdown
122 lines
7.6 KiB
Markdown
---
|
|
phase: 07-mobile-test-harness
|
|
plan: '02'
|
|
subsystem: test-harness
|
|
tags: [playwright, e2e, global-setup, db-seed, mysql2, readiness-gate]
|
|
dependency_graph:
|
|
requires:
|
|
- 'apps/pwa/playwright.config.ts (07-01) — globalSetup path reference'
|
|
- 'apps/api dev MariaDB :3306 — seed target'
|
|
- 'apps/api DEV_AUTH_BYPASS=true — required in API process before harness runs'
|
|
provides:
|
|
- 'global-setup.ts — /health readiness poll + deterministic reset-and-seed'
|
|
- 'e2e/README.md — run instructions and security guardrails'
|
|
- 'mysql2@3.22.4 devDependency in apps/pwa'
|
|
affects:
|
|
- 'Phase 07 plans 03-04 (specs depend on this seed for populated-state assertions)'
|
|
- 'Phase 08 CI (globalSetup runs unchanged in the CI runner)'
|
|
tech_stack:
|
|
added:
|
|
- 'mysql2@3.22.4 devDependency in apps/pwa — enables mysql2/promise in global-setup.ts'
|
|
patterns:
|
|
- 'TRUNCATE + INSERT (not INSERT IGNORE) for seed rows — D-06 deterministic reset'
|
|
- 'INSERT IGNORE INTO calendars guard — ensures calendar_id=10 FK satisfied on fresh CI DB (Pitfall 4)'
|
|
- 'SET FOREIGN_KEY_CHECKS=0/1 around TRUNCATE — FK-safe truncate ordering'
|
|
- 'fetch() for /health poll — native Node.js 22, no @playwright/test import (Pitfall 2)'
|
|
key_files:
|
|
created:
|
|
- apps/pwa/e2e/README.md
|
|
modified:
|
|
- apps/pwa/e2e/global-setup.ts
|
|
- apps/pwa/package.json
|
|
- pnpm-lock.yaml
|
|
decisions:
|
|
- 'D-07-02-mysql2-in-pwa: Added mysql2@3.22.4 as devDependency to apps/pwa — global-setup.ts needs mysql2/promise for TypeScript types; the package was already in the monorepo (apps/api), so pnpm install just linked it without downloading'
|
|
- "D-07-02-deadline-check: Added explicit deadline check after the health poll loop to distinguish 'loop exited via break (success)' from 'loop exited via deadline expiry' — ensures throw fires correctly on timeout"
|
|
- 'D-07-02-dtend-in-vevent: Added DTEND line to the minimal VCALENDAR seed string for spec compatibility — some CalDAV parsers reject VEVENTs without DTEND'
|
|
metrics:
|
|
duration_seconds: 196
|
|
completed_date: '2026-06-11'
|
|
tasks_completed: 2
|
|
files_changed: 4
|
|
---
|
|
|
|
# Phase 07 Plan 02: globalSetup Readiness Gate + DB Seed Summary
|
|
|
|
**One-liner:** Playwright globalSetup with 60s /health readiness poll and deterministic TRUNCATE+INSERT seed onto calendar_id=10 and user_id=1 lists — idempotent run-over-run.
|
|
|
|
## What Was Built
|
|
|
|
- `apps/pwa/e2e/global-setup.ts` — full implementation replacing the Plan 01 stub:
|
|
- Step 1 (D-08): polls `${PLAYWRIGHT_BASE_URL}/health` with a 60-second deadline; swallows ECONNREFUSED; breaks on first `res.ok`; throws with a clear diagnostic message if the deadline passes
|
|
- Step 2 (D-06/D-07): direct mysql2 connection using exact env-var names from `apps/api/src/db/client.ts`; `SET FOREIGN_KEY_CHECKS=0` → TRUNCATE list_items/list_shares/lists/calendar_events → `SET FOREIGN_KEY_CHECKS=1` → INSERT IGNORE calendars guard (id=10) → one timed calendar_event → E2E Grocery List (owner_id=1, is_shared=true) + list_shares row + Milk/Eggs items
|
|
- No `@playwright/test` imports — plain Node.js (Pitfall 2 compliant)
|
|
|
|
- `apps/pwa/e2e/README.md` — operator reference documenting:
|
|
- Prerequisites: dev stack (API + PWA + MariaDB + Redis) with DEV_AUTH_BYPASS=true
|
|
- Run commands: `pnpm --filter @familysync/pwa test:e2e`, single profile, headed, UI mode
|
|
- Env var contract: PLAYWRIGHT_BASE_URL, DB_HOST, DB_PORT, DB_USER, DB_PASSWORD, DB_NAME — credentials env-only, never hardcoded (T-07-05)
|
|
- Security guardrail: DEV_AUTH_BYPASS is dev-only, NODE_ENV !== 'production' hard guard, production compose MUST NOT set it (T-07-04)
|
|
- No storageState (D-01 — designed out)
|
|
- CI scope note: Phase 8 brings up the stack; harness handles its own readiness gate
|
|
|
|
- `apps/pwa/package.json` — mysql2@3.22.4 added as devDependency (same version as apps/api; pnpm linked without downloading)
|
|
|
|
## Verification Evidence
|
|
|
|
- `grep "^import mysql from 'mysql2/promise'" apps/pwa/e2e/global-setup.ts` — found
|
|
- `grep "from '@playwright/test'" apps/pwa/e2e/global-setup.ts` — absent (Pitfall 2 pass)
|
|
- `grep "TRUNCATE TABLE" apps/pwa/e2e/global-setup.ts` — 4 tables (list_items, list_shares, lists, calendar_events)
|
|
- `grep "INSERT IGNORE INTO calendars" apps/pwa/e2e/global-setup.ts` — found with VALUES (10, 1, ...)
|
|
- `grep "list_shares\|Milk\|Eggs" apps/pwa/e2e/global-setup.ts` — all present
|
|
- `grep -c 'DEV_AUTH_BYPASS|NODE_ENV|storageState|PLAYWRIGHT_BASE_URL|test:e2e' apps/pwa/e2e/README.md` → 15 (acceptance criteria: non-zero count)
|
|
- `tsc --noEmit --project tsconfig.e2e.json` (apps/pwa) → 0 errors
|
|
- `tsc --noEmit` (apps/pwa src) → 0 errors
|
|
|
|
## Deviations from Plan
|
|
|
|
### Auto-fixed Issues
|
|
|
|
**1. [Rule 3 - Blocking] mysql2 not available in apps/pwa**
|
|
|
|
- **Found during:** Task 1 TypeScript check — `tsc --noEmit --project tsconfig.e2e.json` emitted `TS2307: Cannot find module 'mysql2/promise'`
|
|
- **Issue:** mysql2 is in `apps/api/dependencies` but not linked to `apps/pwa`. The global-setup imports `mysql2/promise` which requires the package to be a direct or devDependency of apps/pwa for TypeScript resolution.
|
|
- **Fix:** Added `"mysql2": "3.22.4"` to `apps/pwa/devDependencies` (same version as apps/api to stay in sync). `pnpm install` linked it from the pnpm store in 4s with zero downloads — the binary was already present from apps/api.
|
|
- **Files modified:** `apps/pwa/package.json`, `pnpm-lock.yaml`
|
|
- **Commit:** 53498e3
|
|
|
|
**2. [Rule 2 - Missing Critical] Explicit deadline-exceeded throw after poll loop**
|
|
|
|
- **Found during:** Task 1 implementation review — the research pattern's while loop exits via `break` on success OR when `Date.now() >= deadline`. After the loop, without an explicit check, code would silently proceed to the DB seed on a timed-out poll, causing confusing mysql2 errors rather than a clear "stack is not up" message.
|
|
- **Fix:** Added `if (Date.now() >= deadline) { throw new Error(...) }` immediately after the while loop so timeout is distinguishable from success.
|
|
- **Files modified:** `apps/pwa/e2e/global-setup.ts`
|
|
- **Commit:** 53498e3
|
|
|
|
**3. [Rule 2 - Missing Critical] DTEND in minimal VCALENDAR seed string**
|
|
|
|
- **Found during:** Task 1 implementation — minimal VCALENDAR without DTEND may fail CalDAV/ical.js parsing in some spec paths. Plan said "minimal VCALENDAR/VEVENT" but no explicit DTEND.
|
|
- **Fix:** Added DTEND line (futureStart + 1 hour) to the VCALENDAR seed string for spec compatibility. Does not affect seed idempotency.
|
|
- **Files modified:** `apps/pwa/e2e/global-setup.ts`
|
|
- **Commit:** 53498e3
|
|
|
|
## Known Stubs
|
|
|
|
None — the Plan 01 stub in global-setup.ts is fully replaced with the real implementation.
|
|
|
|
## Threat Surface Scan
|
|
|
|
No new network endpoints, auth paths, or schema changes. All changes are test-infrastructure files only.
|
|
|
|
Threat mitigations from plan threat model:
|
|
|
|
- **T-07-04 (Elevation of Privilege / DEV_AUTH_BYPASS):** README explicitly documents that DEV_AUTH_BYPASS is dev-only, that the API guards on `NODE_ENV !== 'production'`, and that the production compose MUST NOT set it. global-setup does not set the env var (it cannot — it runs after the API is already up).
|
|
- **T-07-05 (Information Disclosure / DB credentials):** global-setup reads DB_HOST/DB_PORT/DB_USER/DB_PASSWORD/DB_NAME from env exclusively, mirroring `apps/api/src/db/client.ts`. No credential is hardcoded. README states this explicitly.
|
|
- **T-07-06 (Information Disclosure / OIDC session state):** No storageState.json is written by the harness. README states this. Designed out per D-01.
|
|
|
|
## Self-Check: PASSED
|
|
|
|
- `apps/pwa/e2e/global-setup.ts` — exists
|
|
- `apps/pwa/e2e/README.md` — exists
|
|
- Task 1 commit `53498e3` — exists
|
|
- Task 2 commit `535ba11` — exists
|