Implements the full E2E flow against the deployed docker stack and
documents it as a repeatable test playbook.
Surfaced and fixed three real issues while building the E2E:
1. JWT secret mismatch — docker-compose.override.yml backend was using
a NoteLett-only JWT_SECRET that platform-service did not share, so
every Authorization: Bearer call returned 'Invalid or expired token'.
Aligned the override to use platform-service's actual secret
(dev-ecosystem-secret-do-not-use-in-production).
2. CORS preflight missing PATCH/DELETE — @bytelyst/fastify-core registers
@fastify/cors with only { origin }, which leaves Access-Control-Allow-
Methods at the @fastify/cors default of 'GET,HEAD,POST'. Real browser
PATCH/DELETE preflights would fail. Added an onSend hook in
backend/src/server.ts that rewrites the header to
'GET,HEAD,POST,PATCH,PUT,DELETE,OPTIONS' on CORS preflight responses.
3. Product 'notelett' wasn't registered with platform-service — auth
register/login both error with 'Unknown or disabled product: notelett'.
The seed script now POSTs to /api/products idempotently.
Deliverables:
- scripts/e2e-docker-seed.sh — idempotent: registers the notelett product
and creates two test users (admin@notelett.app with role=admin who can
write, user@notelett.app with role=user who is read-only). Re-runs are
no-ops once seeded.
- scripts/e2e-docker-test.sh — 9-step E2E that drives the deployed stack
via HTTP only (no browser): login → CORS preflight for PATCH →
workspace create → note create → note read → note PATCH (status:
draft→active) → note list → note delete → workspace delete.
- docs/testing/E2E_DOCKER_TESTING.md — full playbook covering prereqs,
seed, automated E2E, manual UI smoke, stack architecture diagram,
troubleshooting (JWT mismatch, unknown product, role rejection,
CORS, port conflict, data loss), tear-down, CI wiring guidance.
- package.json — pnpm e2e:docker:seed and pnpm e2e:docker:test
shortcuts.
Verified live on this host's deployed stack:
$ bash scripts/e2e-docker-seed.sh
↷ product 'notelett' already exists
↷ admin user already registered + login works
✓ user created
🟢 Seed complete.
$ bash scripts/e2e-docker-test.sh
✓ user=usr_e094e0c2-... role=admin
✓ CORS allows PATCH
✓ workspace created
✓ note created
✓ note read matches
✓ note patched (status: draft → active)
✓ note list returned (1 item)
✓ note deleted (HTTP 204)
✓ workspace deleted (HTTP 204)
🟢 All 9 E2E steps passed.
Backend regression suite still green: 380/380.
100 lines
3.6 KiB
Bash
Executable File
100 lines
3.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# NoteLett Docker compose E2E seed.
|
|
#
|
|
# Idempotent setup for scripts/e2e-docker-test.sh and for manual UI testing:
|
|
# 1. Register the `notelett` product with the running platform-service.
|
|
# 2. Register one admin test user (admin@notelett.app).
|
|
# 3. Register one read-only test user (user@notelett.app).
|
|
#
|
|
# Re-running is safe: each step skips if the resource already exists.
|
|
#
|
|
# Prerequisites:
|
|
# - platform-service is reachable at http://localhost:4003
|
|
# - NoteLett backend is reachable at http://localhost:4016 (or run
|
|
# `docker compose up -d` first)
|
|
#
|
|
# Usage:
|
|
# bash scripts/e2e-docker-seed.sh
|
|
|
|
set -e
|
|
PLATFORM="${PLATFORM:-http://localhost:4003}"
|
|
|
|
ADMIN_EMAIL="admin@notelett.app"
|
|
ADMIN_PASS="Notelett!Test#2026"
|
|
USER_EMAIL="user@notelett.app"
|
|
USER_PASS="Notelett!Test#2026"
|
|
|
|
step() { echo ""; echo "── $* ──"; }
|
|
ok() { echo "✓ $*"; }
|
|
skip() { echo "↷ $*"; }
|
|
fail() { echo "✗ $*" >&2; exit 1; }
|
|
|
|
step "1. Register product 'notelett' with platform-service"
|
|
EXISTING=$(curl -sS "$PLATFORM/api/products/notelett" -o /dev/null -w "%{http_code}")
|
|
if [[ "$EXISTING" == "200" ]]; then
|
|
skip "product 'notelett' already exists"
|
|
else
|
|
RESP=$(curl -sS -X POST "$PLATFORM/api/products" -H "Content-Type: application/json" \
|
|
-d '{
|
|
"productId": "notelett",
|
|
"displayName": "NoteLett",
|
|
"licensePrefix": "NL",
|
|
"packageName": "@notelett/web",
|
|
"defaultPlan": "free",
|
|
"trialDays": 14,
|
|
"websiteUrl": "https://notelett.app",
|
|
"status": "active"
|
|
}')
|
|
echo "$RESP" | python3 -c "import json,sys; d=json.load(sys.stdin); print(f' id={d.get(\"id\")} status={d.get(\"status\")}')" \
|
|
|| fail "product create failed: $RESP"
|
|
ok "product created"
|
|
fi
|
|
|
|
step "2. Register admin test user ($ADMIN_EMAIL)"
|
|
LOGIN_PROBE=$(curl -sS -X POST "$PLATFORM/api/auth/login" -H "Content-Type: application/json" \
|
|
-d "{\"email\":\"$ADMIN_EMAIL\",\"password\":\"$ADMIN_PASS\",\"productId\":\"notelett\"}")
|
|
if echo "$LOGIN_PROBE" | grep -q "accessToken"; then
|
|
skip "admin user already registered + login works"
|
|
else
|
|
REG=$(curl -sS -X POST "$PLATFORM/api/auth/register" -H "Content-Type: application/json" \
|
|
-d "{
|
|
\"email\":\"$ADMIN_EMAIL\",
|
|
\"password\":\"$ADMIN_PASS\",
|
|
\"displayName\":\"Admin User\",
|
|
\"role\":\"admin\",
|
|
\"productId\":\"notelett\"
|
|
}")
|
|
echo "$REG" | python3 -c "import json,sys; d=json.load(sys.stdin); u=d.get('user',{}); print(f' email={u.get(\"email\")} role={u.get(\"role\")}')" \
|
|
|| fail "admin register failed: $REG"
|
|
ok "admin user created"
|
|
fi
|
|
|
|
step "3. Register read-only test user ($USER_EMAIL)"
|
|
LOGIN_PROBE=$(curl -sS -X POST "$PLATFORM/api/auth/login" -H "Content-Type: application/json" \
|
|
-d "{\"email\":\"$USER_EMAIL\",\"password\":\"$USER_PASS\",\"productId\":\"notelett\"}")
|
|
if echo "$LOGIN_PROBE" | grep -q "accessToken"; then
|
|
skip "user already registered + login works"
|
|
else
|
|
REG=$(curl -sS -X POST "$PLATFORM/api/auth/register" -H "Content-Type: application/json" \
|
|
-d "{
|
|
\"email\":\"$USER_EMAIL\",
|
|
\"password\":\"$USER_PASS\",
|
|
\"displayName\":\"Read-only User\",
|
|
\"role\":\"user\",
|
|
\"productId\":\"notelett\"
|
|
}")
|
|
echo "$REG" | python3 -c "import json,sys; d=json.load(sys.stdin); u=d.get('user',{}); print(f' email={u.get(\"email\")} role={u.get(\"role\")}')" \
|
|
|| fail "user register failed: $REG"
|
|
ok "user created"
|
|
fi
|
|
|
|
echo ""
|
|
echo "🟢 Seed complete."
|
|
echo ""
|
|
echo "Test credentials:"
|
|
echo " Admin (read+write): $ADMIN_EMAIL / $ADMIN_PASS"
|
|
echo " Read-only: $USER_EMAIL / $USER_PASS"
|
|
echo ""
|
|
echo "Next: bash scripts/e2e-docker-test.sh"
|
|
echo "Or open the web app: http://localhost:3050/login"
|