#!/usr/bin/env bash # NoteLett Docker compose E2E test. # # Drives the full critical-path auth + notes flow against the deployed # Docker stack via HTTP: # 1. Login to platform-service (port 4003) # 2. CORS preflight for PATCH (port 4016) # 3. Create workspace (port 4016, requires admin role) # 4. Create note (port 4016) # 5. Read note by id (point read, /workspaceId partition) # 6. PATCH note (title + status) # 7. List notes in workspace # 8. DELETE note # 9. DELETE workspace # # Prerequisites: # - `docker compose up -d` has been run from the repo root. # - The sibling platform-service is running on http://localhost:4003 and # has been seeded with the `notelett` product + a test admin user. # Run scripts/e2e-docker-seed.sh once to do both. # - JWT_SECRET in docker-compose.override.yml MUST match platform-service. # # Usage: # bash scripts/e2e-docker-test.sh # # Environment overrides: # PLATFORM default http://localhost:4003 # BACKEND default http://localhost:4016 # EMAIL default admin@notelett.app # PASS default Notelett!Test#2026 (matches scripts/e2e-docker-seed.sh) set -e PLATFORM="${PLATFORM:-http://localhost:4003}" BACKEND="${BACKEND:-http://localhost:4016}" EMAIL="${EMAIL:-admin@notelett.app}" PASS="${PASS:-Notelett!Test#2026}" step() { echo ""; echo "── $* ──"; } ok() { echo "✓ $*"; } fail() { echo "✗ $*" >&2; exit 1; } step "1. Login as $EMAIL" LOGIN=$(curl -sS -X POST "$PLATFORM/api/auth/login" -H "Content-Type: application/json" \ -d "{\"email\":\"$EMAIL\",\"password\":\"$PASS\",\"productId\":\"notelett\"}") TOKEN=$(echo "$LOGIN" | python3 -c "import json,sys; print(json.load(sys.stdin).get('accessToken',''))") USER_ID=$(echo "$LOGIN" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('user',{}).get('id',''))") ROLE=$(echo "$LOGIN" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('user',{}).get('role',''))") if [[ -z "$TOKEN" ]]; then echo "$LOGIN" >&2 fail "login failed — run scripts/e2e-docker-seed.sh first" fi ok "user=$USER_ID role=$ROLE" step "2. CORS preflight for PATCH" curl -sS -X OPTIONS "$BACKEND/api/notes/x" \ -H "Origin: http://localhost:3050" \ -H "Access-Control-Request-Method: PATCH" -D - -o /dev/null \ | grep -i "access-control-allow-methods" \ | grep -qE "PATCH" \ && ok "CORS allows PATCH" \ || fail "CORS does not advertise PATCH" step "3. Create workspace" WS_ID="ws-e2e-$(date +%s)" WS=$(curl -sS -X POST "$BACKEND/api/workspaces" \ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ -d "{\"id\":\"$WS_ID\",\"name\":\"E2E Workspace\",\"description\":\"Created by e2e-docker-test.sh\"}") echo "$WS" | python3 -c "import json,sys; d=json.load(sys.stdin); print(f' id={d[\"id\"]} name={d[\"name\"]}')" ok "workspace created" step "4. Create note" NOTE_ID="note-e2e-$(date +%s)" NOTE=$(curl -sS -X POST "$BACKEND/api/notes" \ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ -d "{\"id\":\"$NOTE_ID\",\"workspaceId\":\"$WS_ID\",\"title\":\"E2E Note\",\"body\":\"
created by script
\",\"tags\":[\"e2e\"],\"sourceType\":\"manual\"}") echo "$NOTE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(f' id={d[\"id\"]} status={d[\"status\"]} title={d[\"title\"]}')" ok "note created" step "5. Read note" GOT=$(curl -sS "$BACKEND/api/notes/$NOTE_ID?workspaceId=$WS_ID" -H "Authorization: Bearer $TOKEN") echo "$GOT" | python3 -c "import json,sys; d=json.load(sys.stdin); print(f' title={d[\"title\"]} tags={d[\"tags\"]}')" [[ "$(echo "$GOT" | python3 -c "import json,sys; print(json.load(sys.stdin)[\"id\"])")" == "$NOTE_ID" ]] \ && ok "note read matches" \ || fail "note read mismatch" step "6. PATCH note (title + status → active)" PATCHED=$(curl -sS -X PATCH "$BACKEND/api/notes/$NOTE_ID?workspaceId=$WS_ID" \ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ -d '{"title":"E2E Note (edited)","status":"active"}') echo "$PATCHED" | python3 -c "import json,sys; d=json.load(sys.stdin); print(f' title={d[\"title\"]} status={d[\"status\"]}')" ok "note patched" step "7. List notes in workspace" LIST=$(curl -sS "$BACKEND/api/notes?workspaceId=$WS_ID" -H "Authorization: Bearer $TOKEN") echo "$LIST" | python3 -c "import json,sys; d=json.load(sys.stdin); print(f' total={d[\"total\"]} items={[n[\"title\"] for n in d[\"items\"]]}')" ok "note list returned" step "8. DELETE note" curl -sS -X DELETE "$BACKEND/api/notes/$NOTE_ID?workspaceId=$WS_ID" \ -H "Authorization: Bearer $TOKEN" -o /dev/null -w " delete HTTP %{http_code}\n" ok "note deleted" step "9. DELETE workspace" curl -sS -X DELETE "$BACKEND/api/workspaces/$WS_ID" \ -H "Authorization: Bearer $TOKEN" -o /dev/null -w " delete HTTP %{http_code}\n" ok "workspace deleted" echo "" echo "🟢 All 9 E2E steps passed."