Drive the first-run experience in a real browser
e2e-setup-browser.sh signs a user with no organisation into the real web console, creates a farm, copies the gateway settings the page shows, connects a stand-in gateway configured from exactly those settings, adds a printer through the form, gets a failed-print alert and dismisses it, then removes the printer. Afterwards it searches every table and the backend log for the access code the browser typed. No printer is touched and dispatch is off. The e2e scripts that start the real proxy also give it its own store file in the scratch directory.
This commit is contained in:
Executable
+137
@@ -0,0 +1,137 @@
|
||||
#!/usr/bin/env bash
|
||||
# The first-run experience in the real web console, in a real Chrome: a user
|
||||
# with no organisation signs in, creates a farm, copies the gateway settings,
|
||||
# connects a gateway configured from exactly those settings, adds a printer
|
||||
# through the form, gets an alert when it fails, and removes it.
|
||||
#
|
||||
# The gateway is a stand-in (scripts/fake-gateway-serve.ts), so NO printer and
|
||||
# no network device is touched; e2e-provision-proxy.sh covers the real proxy.
|
||||
#
|
||||
# SAFETY: nothing here talks to a real printer, and dispatch is off.
|
||||
#
|
||||
# Needs: source ../dev-secrets.sh first (real Supabase, as e2e-camera.sh's
|
||||
# BROWSER mode), a migrated database, Chrome, ../continuum-app installed, and
|
||||
# port 3042 free. Uses the throwaway Supabase user continuum-e2e@example.invalid,
|
||||
# which must have no organisation (it refuses to start otherwise, so it cannot
|
||||
# trample a SERVE=1 stack) and removes what it creates.
|
||||
#
|
||||
# Usage:
|
||||
# set -a; . ../dev-secrets.sh; set +a
|
||||
# scripts/e2e-setup-browser.sh
|
||||
set -euo pipefail
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
cd "$REPO_ROOT"
|
||||
|
||||
PORT="${PORT:-3036}"
|
||||
BASE="http://localhost:$PORT"
|
||||
DATABASE_URL="${DATABASE_URL:-postgres://continuum:continuum@localhost:5432/continuum}"
|
||||
VALKEY_URL="${VALKEY_URL:-redis://localhost:6379}"
|
||||
EDGE_JWT_SECRET="${EDGE_JWT_SECRET:-e2e-edge-secret}"
|
||||
R2_ACCESS_KEY_ID="${R2_ACCESS_KEY_ID:-e2e}"
|
||||
R2_SECRET_ACCESS_KEY="${R2_SECRET_ACCESS_KEY:-e2e}"
|
||||
R2_ENDPOINT="${R2_ENDPOINT:-http://localhost:9000}"
|
||||
export DATABASE_URL VALKEY_URL EDGE_JWT_SECRET PORT R2_ACCESS_KEY_ID R2_SECRET_ACCESS_KEY R2_ENDPOINT
|
||||
|
||||
APP_DIR="${APP_DIR:-$REPO_ROOT/../continuum-app}"
|
||||
APP_PORT="${APP_PORT:-3042}"
|
||||
CHROME_BIN="${CHROME_BIN:-/usr/bin/google-chrome}"
|
||||
|
||||
WORK="$(mktemp -d)"
|
||||
BACKEND_PID=""
|
||||
APP_PID=""
|
||||
USER_ID=""
|
||||
|
||||
fail() { echo "FAIL: $*" >&2; exit 1; }
|
||||
ok() { echo " ok: $*"; }
|
||||
psql_q() { psql "$DATABASE_URL" -tAq -c "$1"; }
|
||||
|
||||
cleanup() {
|
||||
local rc=$?
|
||||
[ -n "$BACKEND_PID" ] && kill "$BACKEND_PID" 2>/dev/null || true
|
||||
[ -n "$APP_PID" ] && kill -- "-$APP_PID" 2>/dev/null || true
|
||||
sleep 0.5
|
||||
[ -n "$BACKEND_PID" ] && kill -9 "$BACKEND_PID" 2>/dev/null || true
|
||||
[ -n "$APP_PID" ] && kill -9 -- "-$APP_PID" 2>/dev/null || true
|
||||
# What this run created, and only that: the user began with no organisation.
|
||||
if [ -n "$USER_ID" ] && [ -z "${KEEP_DATA:-}" ]; then
|
||||
psql_q "DELETE FROM organisations WHERE id IN (SELECT org_id FROM org_members WHERE user_id='$USER_ID')" >/dev/null 2>&1 || true
|
||||
fi
|
||||
if [ $rc -ne 0 ]; then
|
||||
echo; echo "--- backend log (tail) ---"; tail -20 "$WORK/backend.log" 2>/dev/null || true
|
||||
echo "--- web console log (tail) ---"; tail -15 "$WORK/app.log" 2>/dev/null || true
|
||||
fi
|
||||
rm -rf "$WORK"
|
||||
exit $rc
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
for name in SUPABASE_URL SUPABASE_SECRET_KEY SUPABASE_PUBLISHABLE_KEY; do
|
||||
[ -n "${!name:-}" ] || fail "$name is not set - source ../dev-secrets.sh"
|
||||
done
|
||||
psql_q "SELECT 1 FROM printer_alerts LIMIT 0" >/dev/null 2>&1 || fail "no migrated database - run scripts/dev-env.sh up && schema"
|
||||
[ -x "$CHROME_BIN" ] || fail "no Chrome at $CHROME_BIN"
|
||||
[ -d "$APP_DIR/apps/web/node_modules" ] || fail "$APP_DIR has no installed web app"
|
||||
# bash's own /dev/tcp rather than `ss`, which is not installed everywhere.
|
||||
if (exec 3<>"/dev/tcp/127.0.0.1/$APP_PORT") 2>/dev/null; then fail "port $APP_PORT is already in use (a SERVE=1 stack running?)"; fi
|
||||
|
||||
SB_USER="$(bun scripts/e2e-supabase-user.ts)" || fail "could not provision the Supabase test user"
|
||||
sb_field() { printf '%s' "$SB_USER" | python3 -c 'import json,sys; print(json.load(sys.stdin)[sys.argv[1]])' "$1"; }
|
||||
USER_ID="$(sb_field id)"; SB_EMAIL="$(sb_field email)"; SB_PASSWORD="$(sb_field password)"
|
||||
unset SB_USER
|
||||
[ "$(psql_q "SELECT count(*) FROM org_members WHERE user_id='$USER_ID'")" = "0" ] \
|
||||
|| fail "the test user already belongs to an organisation - stop any SERVE=1 stack first"
|
||||
ok "a Supabase user with no organisation"
|
||||
|
||||
echo "==> starting the backend on :$PORT"
|
||||
DISPATCH_ENABLED=false bun run src/index.ts > "$WORK/backend.log" 2>&1 &
|
||||
BACKEND_PID=$!
|
||||
for _ in $(seq 1 60); do curl -sf "$BASE/health" >/dev/null 2>&1 && break; sleep 0.5; done
|
||||
curl -sf "$BASE/health" >/dev/null || fail "backend did not become healthy"
|
||||
ok "backend healthy"
|
||||
|
||||
echo "==> starting the web console on :$APP_PORT"
|
||||
setsid env -C "$APP_DIR/apps/web" \
|
||||
NUXT_PUBLIC_API_BASE="$BASE" \
|
||||
NUXT_PUBLIC_WS_BASE="ws://localhost:$PORT" \
|
||||
SUPABASE_URL="$SUPABASE_URL" \
|
||||
SUPABASE_KEY="$SUPABASE_PUBLISHABLE_KEY" \
|
||||
NUXT_PUBLIC_PASSWORD_LOGIN=true \
|
||||
pnpm exec nuxt dev --host 127.0.0.1 --port "$APP_PORT" > "$WORK/app.log" 2>&1 &
|
||||
APP_PID=$!
|
||||
for _ in $(seq 1 120); do curl -sf -o /dev/null "http://127.0.0.1:$APP_PORT/login" 2>/dev/null && break; sleep 1; done
|
||||
curl -sf -o /dev/null "http://127.0.0.1:$APP_PORT/login" || fail "the web console did not start"
|
||||
ok "the web console is up"
|
||||
|
||||
ACCESS_CODE="BROWSERCODE$(cat /proc/sys/kernel/random/uuid | cut -c1-6)"
|
||||
FRAMES="$WORK/frames.jsonl"; : > "$FRAMES"
|
||||
|
||||
echo "==> first run, in real Chrome"
|
||||
OUT="$(env APP_URL="http://127.0.0.1:$APP_PORT" API_BASE="$BASE" WS_BASE="ws://localhost:$PORT" \
|
||||
SUPABASE_E2E_EMAIL="$SB_EMAIL" SUPABASE_E2E_PASSWORD="$SB_PASSWORD" CHROME_BIN="$CHROME_BIN" \
|
||||
FRAMES_FILE="$FRAMES" ACCESS_CODE="$ACCESS_CODE" \
|
||||
timeout 280 bun scripts/e2e-setup-browser.ts 2>&1 || true)"
|
||||
seen=0; bad=0
|
||||
while IFS=$'\t' read -r verdict label; do
|
||||
case "$verdict" in
|
||||
ok) ok "$label"; seen=$((seen + 1)) ;;
|
||||
FAILED) echo " FAILED: $label" >&2; seen=$((seen + 1)); bad=$((bad + 1)) ;;
|
||||
esac
|
||||
done < <(printf '%s\n' "$OUT" | grep -E "^(ok|FAILED)$(printf '\t')")
|
||||
if [ "$seen" -lt 18 ] && [ "$bad" -eq 0 ]; then
|
||||
bad=1; echo " FAILED: the browser run produced only $seen of 18 results" >&2
|
||||
fi
|
||||
if [ "$bad" -ne 0 ]; then
|
||||
printf '%s\n' "$OUT" | sed 's/^/ | /' >&2
|
||||
fail "the first-run experience did not work"
|
||||
fi
|
||||
|
||||
# The credential the browser typed must rest nowhere in the cloud.
|
||||
grep -q "$ACCESS_CODE" "$WORK/backend.log" && fail "the access code is in the backend log"
|
||||
LEAK="$(psql "$DATABASE_URL" -tAq -c "SELECT string_agg(table_name, ' ') FROM information_schema.tables WHERE table_schema='public'" \
|
||||
| tr ' ' '\n' | while read -r tbl; do [ -n "$tbl" ] && psql_q "SELECT count(*) FROM \"$tbl\" t WHERE row_to_json(t)::text LIKE '%$ACCESS_CODE%'" | sed "s/^/$tbl /"; done | awk '$2>0')"
|
||||
[ -z "$LEAK" ] || fail "the access code is stored in: $LEAK"
|
||||
ok "the access code is in no table and no log"
|
||||
|
||||
echo
|
||||
echo "PASS - a new user went from nothing to a farm, a gateway, a printer, an alert and back."
|
||||
Reference in New Issue
Block a user