fix(edge-fn): replace getClaims with adminClient.auth.getUser(token)

fix(edge-fn): use user.id instead of claims.sub; fixes 500s and false cert_required
fix(migrations): drop broad reservations SELECT policy; add reservation_slots view with security_invoker=false
fix(tests): correct weekSlot() keys from start/end to start_time/end_time
fix(tests): spread overlap test slots across separate ISO weeks
fix(tests): update e2e assertion to match actual authenticated home text
fix(app): hide IonMenu before user is authenticated
feat(dx): add test:all script running unit, integration, and e2e in sequence
docs(claude-md): document SELinux fix, Edge Function auth pattern, security_invoker behaviour
This commit is contained in:
2026-04-20 14:32:37 -04:00
parent d07a02c9dc
commit 108c042921
33 changed files with 2745 additions and 12 deletions

View File

@@ -1,70 +0,0 @@
# Session Handoff: Auth Tests + Backend Ansible Plan
**Date:** 2026-04-12
**Session Focus:** Fix broken auth unit tests; plan bab-backend-ansible rewrite; update EE dependencies
## What Was Accomplished
1. **Deleted `tests/unit/auth-callback.test.ts`** — low-value component test per agreed test strategy; E2E covers this
2. **Extracted pure auth logic**`app/utils/auth.ts` (`checkAuthRedirect(userValue, path): string | null`)
3. **Simplified `app/middleware/auth.ts`** — delegates to `checkAuthRedirect`; Nuxt-specific code is now minimal
4. **Rewrote `tests/unit/auth-middleware.test.ts`** — tests `checkAuthRedirect` directly, no mocking, node env; **7/7 passing**
5. **Documented Nuxt testing lessons** → memory `feedback_nuxt_testing.md`
6. **Wrote backend rewrite plan**`docs/summaries/plan-bab-backend-ansible-rewrite.md`
7. **Updated `ee-demo` EE** with: `amazon.aws` collection, `boto3`/`botocore`, `postgresql` RPM, `supabase` CLI install via `SUPABASE_VERSION` build arg; updated `build.sh` (user also added `--redhat` flag)
8. **Resolved all plan open questions** except one (see below)
## Exact State of Work in Progress
- `tests/integration/auth-session.test.ts` — written last session, not yet run (requires local Supabase + `SUPABASE_SERVICE_ROLE_KEY`)
- Playwright E2E — not yet set up; `tests/e2e/` directory does not exist
- `.gitea/workflows/build.yaml` — not yet created
## Key Technical Decisions This Session
- **Extract-don't-mock pattern**: Nuxt auto-imports compile to concrete dist paths; `vi.mock('#imports')` doesn't intercept them. Pattern: extract logic to `app/utils/` with no Nuxt deps, test directly. CONFIRMED.
- **Skip unit tests for simple page components**: Ionic component registration + Supabase init failures make `mountSuspended` too brittle. Cover with Playwright E2E instead. CONFIRMED.
## Files Created or Modified
| File Path | Action | Description |
|-----------|--------|-------------|
| `app/utils/auth.ts` | Created | `checkAuthRedirect` pure function; `PUBLIC_ROUTES` constant |
| `app/middleware/auth.ts` | Modified | Now delegates to `checkAuthRedirect` |
| `tests/unit/auth-middleware.test.ts` | Rewritten | Tests pure function; 7/7 passing |
| `tests/unit/auth-callback.test.ts` | Deleted | Low-value component test |
| `docs/summaries/plan-bab-backend-ansible-rewrite.md` | Created | Full rewrite plan with implementation sequence |
| `docs/context/sdlc-architecture.md` | Updated | Dev URL, nginx webroot, Gitea URL, artifact token path |
| `/home/ptoal/Dev/ExecutionEnvironments/ee-demo/execution-environment.yml` | Modified | Added `postgresql` RPM, `supabase` CLI build step |
| `/home/ptoal/Dev/ExecutionEnvironments/ee-demo/requirements.yml` | Modified | Added `amazon.aws >= 9.0.0` |
| `/home/ptoal/Dev/ExecutionEnvironments/ee-demo/requirements.txt` | Modified | Added `boto3`, `botocore` |
| `/home/ptoal/Dev/ExecutionEnvironments/ee-demo/build.sh` | Modified | Added `SUPABASE_VERSION` guard; user added `--redhat` flag |
## What the NEXT Session Should Do
**If continuing oysqn.app frontend:**
1. Set up Playwright — `yarn add -D @playwright/test`, create `tests/e2e/`, write login flow E2E test
2. Create `.gitea/workflows/build.yaml` following bab-app pattern (semantic-release + artifact)
**If starting bab-backend-ansible rewrite:**
1. Read `docs/summaries/plan-bab-backend-ansible-rewrite.md` — full scope and implementation sequence
2. Start with step 3: `sync_gitea_secrets.yml` (lowest risk, standalone)
3. Note: work in `/home/ptoal/Dev/Projects/bab-backend-ansible`, not oysqn.app
## Open Questions Requiring User Input
- [ ] **`kv/oys/dev/supabase/postgres_url`** — not in Vault; needed before `migrate_supabase.yml` can run rollback SQL via `psql` against dev. Add to Vault before first dev migration run.
## Confirmed Infrastructure Values
| Item | Value |
|------|-------|
| Dev URL | `https://bab.toal.ca` |
| nginx webroot (bab1) | `/usr/share/nginx/html/` |
| Gitea URL | `https://gitea.toal.ca/` |
| Gitea artifact token | `kv/oys/bab_gitea` |
| Backup path (bab1) | `/var/backups/oysqn/` (assumed — confirm before first prod backup) |
## Files to Load Next Session
- **Frontend session:** `docs/summaries/handoff-2026-04-12-splash-and-login.md` (prior UI work context)
- **Backend session:** `docs/summaries/plan-bab-backend-ansible-rewrite.md`; `docs/context/sdlc-architecture.md`

View File

@@ -1,66 +0,0 @@
# Session Handoff: Playwright E2E Setup
**Date:** 2026-04-19
**Session Focus:** Set up Playwright E2E testing; write and pass auth flow tests
## What Was Accomplished
1. **Installed `@playwright/test` 1.59.1** + Chromium headless shell binary
2. **Created `playwright.config.ts`** — Pixel 5 viewport, `reuseExistingServer` for local dev, `github` reporter in CI, 30s timeout, 1 worker (serial)
3. **Created `tests/e2e/helpers/mailpit.ts`**`deleteAllMail()` and `getMagicLink(email)` against Mailpit at `http://127.0.0.1:54324`
4. **Created `tests/e2e/auth.spec.ts`** — 2 tests: full magic link flow + protected route redirect
5. **Fixed `supabase/config.toml`**`site_url` changed from `http://127.0.0.1:3000` to `http://localhost:3000`; `additional_redirect_urls` updated to include `http://localhost:3000/auth/callback` and `http://127.0.0.1:3000/auth/callback`
6. **Added scripts to `package.json`**: `test:e2e`, `test:e2e:ui`, `test:e2e:headed`
7. **Both tests pass: 2/2**
## Key Debugging Discoveries
| Problem | Root Cause | Fix |
|---------|-----------|-----|
| `getByRole('button', { name: 'Log In' })` not found | `IonButton` with `router-link` renders as `<a>` (role=`link`) | Changed to `getByRole('link', { name: 'Log In' })` |
| `getByLabel('Email address')` not found | `IonLabel` is not associated via `for`/`id` with `IonInput` | Changed to `getByPlaceholder('you@example.com')` |
| `page.goto(magicLink)``ERR_CONNECTION_REFUSED` to `127.0.0.1:54321` | Playwright's Chromium headless shell cannot reach Supabase local auth server directly | Use Node.js `fetch(magicLink, { redirect: 'manual' })` to follow redirect server-side; navigate browser to the resulting app callback URL |
| Callback URL was `/?code=` instead of `/auth/callback?code=` | `emailRedirectTo` not whitelisted in `supabase/config.toml`; Supabase fell back to `site_url` | Fixed `config.toml` redirect URLs; required `supabase stop && supabase start` |
## Ionic-Specific Playwright Patterns (confirmed working)
- `IonButton` with `router-link` → use `getByRole('link', { name: '...' })`
- `IonButton` without `router-link` → use `getByRole('button', { name: '...' })`
- `IonInput` → use `getByPlaceholder(...)` (label association not standard HTML)
- Never navigate browser directly to Supabase local auth URL — follow redirect server-side first
## Files Created or Modified
| File | Action |
|------|--------|
| `playwright.config.ts` | Created |
| `tests/e2e/helpers/mailpit.ts` | Created |
| `tests/e2e/auth.spec.ts` | Created |
| `supabase/config.toml` | Modified — site_url and redirect URLs |
| `package.json` | Modified — added test:e2e scripts |
## What the NEXT Session Should Do
**Option A — CI pipeline:**
1. Create `.gitea/workflows/build.yaml` — unit tests + build + E2E (read bab-app pipeline as reference)
2. Note: E2E in CI requires local Supabase running in the pipeline — confirm if feasible or skip E2E in CI for now
**Option B — Home page content:**
1. Implement authenticated home content in `app/pages/index.vue` (currently "Welcome to OYS Borrow a Boat" placeholder)
2. Likely a boat list or dashboard — check `docs/planning/` for persona requirements
## Open Questions
- [ ] Should E2E tests run in CI (requires Supabase in pipeline) or local-only? — OPEN
- [ ] What is the authenticated home content? Boat list, dashboard, or something else? — check planning docs
## Dev Environment Reference
```bash
# Required before running E2E:
DOCKER_HOST=unix:///run/user/1000/podman/podman.sock npx supabase start
# Run E2E:
yarn test:e2e # headless
yarn test:e2e:headed # visible browser
yarn test:e2e:ui # Playwright UI mode
```

View File

@@ -0,0 +1,56 @@
# Session Handoff: Edge Functions, Auth Pattern, and Test Fixes
**Date:** 2026-04-20
**Session Duration:** ~2 hours
**Session Focus:** Fix create-reservation Edge Function auth, resolve 12 failing integration tests, fix RBAC RLS, add SELinux dev docs
**Context Usage at Handoff:** ~60%
## What Was Accomplished
1. Diagnosed and fixed SELinux blocking Edge Functions locally → documented fix in `CLAUDE.md`
2. Updated Edge Function auth from `userClient.auth.getUser()` (anon key + auth header) to `adminClient.auth.getUser(token)` (service role + JWT arg) → `supabase/functions/create-reservation/index.ts`
3. Fixed `weekSlot()` test helper returning `{start, end}` instead of `{start_time, end_time}``tests/integration/booking-constraints.test.ts`
4. Fixed overlap tests using days 30/31/32 (same ISO week, hitting weekly pre-booking limit before DB overlap constraint fires) → spread across days 14/21/28 (different weeks)
5. Fixed RBAC: `"Authenticated users can read non-private reservation slots"` policy on `reservations` was never dropped when `reservation_slots` view was created → new migration drops it
6. Fixed `reservation_slots` view from `security_invoker = true` to `security_invoker = false` so it reads as owner, not caller → new migration recreates view + grants
7. Fixed E2E test asserting `"Welcome to OYS Borrow a Boat"` (doesn't exist) → changed to `"Upcoming Reservations"` which is always present when authenticated
8. Added `v-if="authStore.user"` to `IonMenu` in `app.vue` — menu not rendered before login
9. Added `yarn test:all` script (unit → integration → e2e in sequence)
## Decisions Made This Session
- **Use `adminClient.auth.getUser(token)`** (not `getClaims`) BECAUSE `getClaims` is not reliably available in `npm:@supabase/supabase-js@2` Deno import and its return shape is undocumented for that context — STATUS: confirmed
- **`reservation_slots` view uses `security_invoker = false`** BECAUSE `security_invoker = true` caused it to apply the calling user's RLS (returning 0 rows for non-owners after broad policy was dropped) — STATUS: confirmed
- **Overlap tests use weeks 14/21/28 days ahead** BECAUSE original days 30/31/32 fell in the same ISO week; direct insert on day+31 consumed the 2nd weekly pre-booking slot, blocking the day+32 "different time" test — STATUS: confirmed
## Key Numbers Generated or Discovered This Session
- Integration tests before: 12 failed / 8 passed (20 total)
- Integration tests after: 0 failed / 20 passed (ASSUMED — verify with `yarn test:integration`)
- E2E tests: 1 failed / 1 passed → 2 passed after auth text fix (ASSUMED — verify with `yarn test:e2e`)
## Files Created or Modified
| File Path | Action | Description |
|-----------|--------|-------------|
| `supabase/functions/create-reservation/index.ts` | Modified | Auth: `getClaims``adminClient.auth.getUser(token)`; `claims.sub``user.id` |
| `tests/integration/booking-constraints.test.ts` | Modified | `weekSlot` key names fixed; overlap test days spread across weeks |
| `tests/e2e/auth.spec.ts` | Modified | Assertion changed from missing text to `"Upcoming Reservations"` |
| `app/app.vue` | Modified | `v-if="authStore.user"` on `IonMenu` |
| `package.json` | Modified | Added `test:all` script |
| `CLAUDE.md` | Modified | Added Edge Functions section: SELinux fix, auth pattern, `security_invoker` note |
| `supabase/migrations/20260420180000_drop_open_reservations_read_policy.sql` | Created | Drops `"Authenticated users can read non-private reservation slots"` policy |
| `supabase/migrations/20260420190000_fix_reservation_slots_view.sql` | Created | Recreates `reservation_slots` with `security_invoker = false`; grants SELECT to authenticated |
| `supabase/migrations/20260420132336_booking_rules_and_rbac.sql` | Modified | Fixed original view creation to `security_invoker = false` + added GRANT for `db reset` consistency |
## What the NEXT Session Should Do
1. **First**: Verify all tests pass — `yarn test:all` (requires local Supabase running with functions served)
2. **Then**: Work on reservations UI — `app/pages/reservations/` exists but contents unknown; likely needs create/list/detail pages wired to the Edge Function
## Open Questions Requiring User Input
- [ ] What pages exist under `app/pages/reservations/`? Are they scaffolded or complete? — impacts next UI session scope
- [ ] Are there additional Edge Functions planned (e.g., cancel-reservation, admin endpoints)? — impacts function auth pattern reuse
## Assumptions That Need Validation
- ASSUMED: `yarn test:all` passes cleanly after migrations applied — validate by running `npx supabase migration up && yarn test:all`
- ASSUMED: `reservation_slots` view grant is sufficient for anon client queries in tests — validate by observing RBAC test pass
## Files to Load Next Session
- `docs/summaries/handoff-2026-04-20-edge-functions-auth-and-test-fixes.md` — this file
- `supabase/functions/create-reservation/index.ts` — if continuing Edge Function work
- `app/pages/reservations/` — if working on reservations UI