pelagia-portal/automation/refresh-test-db.sh
Hardik a72e980558
All checks were successful
PR checks / checks (pull_request) Successful in 46s
PR checks / integration (pull_request) Successful in 32s
test(staging): feature-level verification of closed issues + seeded test users
Adds a Playwright suite (App/tests/staging/) that logs into the running staging
instance (ppms-staging, :3200) and verifies each closed portal issue is actually
fixed — feature level, driving the real UI, one spec per issue.

To make credential login possible against the prod-mirror pelagia_test (which only
holds real, mostly SSO-only users), prisma/seed-test-users.ts idempotently seeds one
known-password @pelagia.local user per role, and automation/refresh-test-db.sh runs
it after every daily refresh so the logins persist on staging.

Result against staging: 41 passed, 1 skipped (#10 — no attachment data on staging).
Two closed issues were found NOT fixed and are recorded as documented test.fail():
  - #13 Accounts "payments completed this month" card is absent.
  - #24/#40 logout tooltip still reads "Sign out" (pipeline test issues).

Docs/TESTING.md documents the suite, the seeded users, how to run it against
staging, and the full issue -> script mapping.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 11:49:48 +05:30

94 lines
4.6 KiB
Bash

#!/usr/bin/env bash
# Refresh the test database from production. Runs daily via cron on pms1.
#
# pelagia_test is a throwaway mirror of prod (pelagia) so the autofix Claude can
# run integration tests / a dev server against realistic data WITHOUT touching
# production. The test DB is owned by pelagia_user (created once as superuser);
# this refresh runs purely as pelagia_user using the prod connection string.
set -uo pipefail
ENV_FILE="${1:-/home/shad0w/pms/App/.env}"
PROD_DB="pelagia"
TEST_DB="pelagia_test"
log() { echo "$(date '+%F %T') $*"; }
PROD_URL=$(grep -E '^DATABASE_URL' "$ENV_FILE" | sed -E 's/^DATABASE_URL=//; s/^"//; s/"$//')
[ -n "$PROD_URL" ] || { log "ERROR: no DATABASE_URL in $ENV_FILE"; exit 1; }
# Derive the test URL by swapping ONLY the database-name path segment (anchored on
# @host/ so the 'pelagia' inside the username is never touched).
TEST_URL=$(printf '%s' "$PROD_URL" | sed -E "s#(@[^/]+/)$PROD_DB([?]|\$)#\1$TEST_DB\2#")
if [ "$TEST_URL" = "$PROD_URL" ]; then
log "ERROR: failed to derive test URL (db name not found in connection string)"; exit 1
fi
log "Refreshing $TEST_DB from $PROD_DB ..."
# Safety: only ever drop/restore the derived TEST database, never prod.
TEST_DBNAME=$(printf '%s' "$TEST_URL" | sed -E 's#.*/([^/?]+)([?].*)?$#\1#')
[ "$TEST_DBNAME" = "$TEST_DB" ] || { log "ERROR: refusing to refresh '$TEST_DBNAME' (expected '$TEST_DB')"; exit 1; }
# Fully reset the test schema BEFORE restoring. `pg_dump --clean` only drops the
# objects that exist in PROD, so anything created on the test DB by a previously
# applied UNRELEASED migration (e.g. crewing tables/types/enum values not yet in
# prod) would survive as a leftover — and then collide with the `migrate deploy`
# replay below ("type already exists", P3009), blocking every later migration.
# Dropping the schema first guarantees the restore brings exactly prod's objects
# and the unreleased migrations apply cleanly. (No-op on an already-empty DB.)
errfile=$(mktemp)
psql "$TEST_URL" -v ON_ERROR_STOP=1 -c "DROP SCHEMA public CASCADE; CREATE SCHEMA public;" >/dev/null 2>"$errfile"
pg_dump --no-owner --no-privileges "$PROD_URL" \
| psql "$TEST_URL" >/dev/null 2>>"$errfile"
prod_tables=$(psql -tAc "SELECT count(*) FROM information_schema.tables WHERE table_schema='public';" "$PROD_URL")
test_tables=$(psql -tAc "SELECT count(*) FROM information_schema.tables WHERE table_schema='public';" "$TEST_URL")
if [ "$test_tables" = "$prod_tables" ] && [ "$test_tables" -gt 0 ]; then
log "Data copied. $TEST_DB has $test_tables public tables (prod has $prod_tables)."
rm -f "$errfile"
else
log "WARNING: table counts differ (test=$test_tables prod=$prod_tables). Recent errors:"
tail -8 "$errfile"
rm -f "$errfile"
exit 1
fi
# The test DB now has PROD's schema, which may be behind master. Apply master's
# unreleased migrations so the code under test (staging + autofix) doesn't 500 on
# columns prod doesn't have yet (e.g. poDate). Uses a stable master checkout.
MIG_DIR=""
for d in "$HOME/pelagia-staging/App" "$HOME/pelagia-autofix/App"; do
[ -d "$d/prisma/migrations" ] && { MIG_DIR="$d"; break; }
done
if [ -n "$MIG_DIR" ]; then
export NVM_DIR="$HOME/.nvm"; . "$NVM_DIR/nvm.sh" 2>/dev/null || true
log "Applying master migrations from $MIG_DIR ..."
if ( cd "$MIG_DIR" && DATABASE_URL="$TEST_URL" pnpm db:migrate:deploy ) >/tmp/migrate-test-db.log 2>&1; then
log "Migrations applied."
else
log "WARNING: migrate deploy failed; see /tmp/migrate-test-db.log"; tail -5 /tmp/migrate-test-db.log
fi
else
log "No master checkout with migrations found; skipping migrate (test DB has prod schema only)."
fi
# Seed deterministic, credential-capable TEST USERS (one per role) so the staging
# instance can be logged into for end-to-end feature verification. The prod mirror
# only carries real @pelagiamarine.com users (mostly SSO-only, no known password),
# which makes credential login — and therefore the Playwright closed-issue suite
# (App/tests/staging/) — impossible. These @pelagia.local accounts never exist in
# prod, so there is no collision; the seed is idempotent (upsert by email).
# See App/tests/staging/ and Docs/TESTING.md.
if [ -n "$MIG_DIR" ] && [ -f "$MIG_DIR/prisma/seed-test-users.ts" ]; then
log "Seeding test users into $TEST_DB ..."
if ( cd "$MIG_DIR" && DATABASE_URL="$TEST_URL" pnpm tsx prisma/seed-test-users.ts ) >/tmp/seed-test-users.log 2>&1; then
log "Test users seeded."
else
log "WARNING: test-user seed failed; see /tmp/seed-test-users.log"; tail -5 /tmp/seed-test-users.log
fi
else
log "Skipping test-user seed (no checkout with prisma/seed-test-users.ts)."
fi