One deck form per session; rename leagues; e2e test script
- A player's deck for a night is edited in a single form and written to their match rows and their standings row, so the two can't disagree. Matches, standings and hand imports all fill blank decks from the decks already entered, whatever order the files are imported in. The imported standings table shows decks read-only. - Admins can rename a league. - test/e2e.sh runs end-to-end checks against the local podman stack (fixtures by default, or real exports: test/e2e.sh sampledata). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a99997b96d
commit
022d161934
6 files changed
+147
-36
No files matched your search
Executable
+99
@@ -0,0 +1,99 @@
|
||||
#!/usr/bin/env bash
|
||||
# End-to-end checks against the local podman stack: imports, the single deck form, hand import, league rename/delete.
|
||||
# usage: scripts/local.sh up && test/e2e.sh [dir]
|
||||
# dir holds Melee exports named *matches*.json / *tandings*.json (default test/fixtures; real ones: test/e2e.sh sampledata).
|
||||
# Creates a throwaway league and deletes it again at the end.
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
DIR=${1:-test/fixtures}
|
||||
set -a; source .env.local; set +a
|
||||
B=http://127.0.0.1:$HOST_PORT
|
||||
T=$(mktemp -d); trap 'rm -rf "$T"' EXIT
|
||||
fails=0
|
||||
|
||||
check() { if [[ "$2" == "$3" ]]; then echo "ok $1"; else echo "FAIL $1: got '$2', want '$3'"; fails=$((fails + 1)); fi; }
|
||||
req() { curl -s -b "$T/jar" -c "$T/jar" -o "$T/out" -w '%{http_code}' "$@"; }
|
||||
anon() { curl -s -o /dev/null -w '%{http_code}' "$@"; }
|
||||
db() { podman exec mtg-local-db-1 psql -U league -tAc "$1"; }
|
||||
files() { for f in "$DIR"/$1; do printf -- '-F\nfiles=@%s\n' "$f"; done; }
|
||||
mapfile -t MATCHES < <(files '*atches*.json')
|
||||
mapfile -t STANDINGS < <(files '*tandings*.json')
|
||||
decks_of() { # distinct decks of player $2 in session $1, across match rows and the standings row
|
||||
db "SELECT string_agg(DISTINCT coalesce(d, '-'), ',') FROM (SELECT p1_deck d FROM matches WHERE session_id=$1 AND p1_id=$2
|
||||
UNION ALL SELECT p2_deck FROM matches WHERE session_id=$1 AND p2_id=$2
|
||||
UNION ALL SELECT deck FROM session_results WHERE session_id=$1 AND player_id=$2) x"
|
||||
}
|
||||
|
||||
echo "== auth and leagues"
|
||||
check "admin route needs login" "$(anon -d name=x "$B/admin/league")" 403
|
||||
check "login" "$(req -d "password=$ADMIN_PASSWORD" "$B/login")" 303
|
||||
req -d name=e2e-league -d best_n=6 "$B/admin/league" >/dev/null
|
||||
LG=$(db "SELECT max(id) FROM leagues")
|
||||
check "rename needs login" "$(anon -d name=x "$B/admin/league/$LG/rename")" 403
|
||||
check "rename rejects empty name" "$(req -d 'name= ' "$B/admin/league/$LG/rename")" 400
|
||||
check "rename" "$(req -d 'name=e2e renamed' "$B/admin/league/$LG/rename")" 303
|
||||
check "renamed in db" "$(db "SELECT name FROM leagues WHERE id=$LG")" "e2e renamed"
|
||||
req "$B/league/$LG" >/dev/null; check "league page shows new name" "$(grep -c '<h1>e2e renamed</h1>' "$T/out")" 1
|
||||
check "rename unknown league" "$(req -d name=x "$B/admin/league/2147483647/rename")" 404
|
||||
|
||||
echo "== session A: standings, then a deck, then matches"
|
||||
req -d date=2026-10-14 "$B/admin/league/$LG/session" >/dev/null; SA=$(db "SELECT max(id) FROM sessions")
|
||||
check "import standings" "$(req "${STANDINGS[@]}" "$B/admin/session/$SA/import-melee")" 303
|
||||
P1=$(db "SELECT player_id FROM session_results r JOIN players p ON p.id=r.player_id WHERE session_id=$SA ORDER BY p.name LIMIT 1")
|
||||
check "deck form saves to standings" "$(req -d "deck_$P1=Burn" "$B/admin/session/$SA/decks")" 303
|
||||
check "standings deck" "$(decks_of "$SA" "$P1")" Burn
|
||||
check "import all matches at once" "$(req "${MATCHES[@]}" "$B/admin/session/$SA/import-melee")" 303
|
||||
check "matches picked up the standings deck" "$(decks_of "$SA" "$P1")" Burn
|
||||
req "$B/session/$SA" >/dev/null
|
||||
check "one deck form on the page" "$(grep -c '/decks"' "$T/out")" 1
|
||||
check "one deck input per session player" "$(grep -o 'name="deck_' "$T/out" | wc -l)" "$(db "SELECT count(*) FROM ($(sed 's/\$1/'"$SA"'/g' <<<"SELECT pid FROM (SELECT p1_id pid FROM matches WHERE session_id=\$1 UNION SELECT p2_id FROM matches WHERE session_id=\$1 AND p2_id IS NOT NULL UNION SELECT player_id FROM session_results WHERE session_id=\$1) x")) y")"
|
||||
check "deck form saves to matches and standings" "$(req -d "deck_$P1=Control" "$B/admin/session/$SA/decks")" 303
|
||||
check "both updated" "$(decks_of "$SA" "$P1")" Control
|
||||
check "clearing a deck" "$(req -d "deck_$P1=" "$B/admin/session/$SA/decks")" 303
|
||||
check "cleared everywhere" "$(decks_of "$SA" "$P1")" -
|
||||
req -d "deck_$P1=Control" "$B/admin/session/$SA/decks" >/dev/null
|
||||
|
||||
echo "== session A: hand import"
|
||||
csv() { db "COPY (SELECT m.round, a.name, CASE WHEN m.p1_id=$P1 THEN '$1' ELSE '' END, coalesce(b.name, ''),
|
||||
CASE WHEN m.p2_id=$P1 THEN '$1' ELSE '' END, m.p1_wins, m.p2_wins, m.draws FROM matches m JOIN players a ON a.id=m.p1_id
|
||||
LEFT JOIN players b ON b.id=m.p2_id WHERE m.session_id=$SA ORDER BY m.round, m.id) TO STDOUT WITH CSV"; }
|
||||
before=$(db "SELECT count(*) FROM matches WHERE session_id=$SA")
|
||||
check "hand import with blank decks" "$(req --data-urlencode "data=$(csv '')" "$B/admin/session/$SA/import")" 303
|
||||
check "match count unchanged" "$(db "SELECT count(*) FROM matches WHERE session_id=$SA")" "$before"
|
||||
check "blank keeps the entered deck" "$(decks_of "$SA" "$P1")" Control
|
||||
check "hand import with a new deck" "$(req --data-urlencode "data=$(csv Elves)" "$B/admin/session/$SA/import")" 303
|
||||
check "new deck reaches standings too" "$(decks_of "$SA" "$P1")" Elves
|
||||
|
||||
echo "== session B: matches, then a deck, then standings"
|
||||
req -d date=2026-10-15 "$B/admin/league/$LG/session" >/dev/null; SB=$(db "SELECT max(id) FROM sessions")
|
||||
check "import matches" "$(req "${MATCHES[@]}" "$B/admin/session/$SB/import-melee")" 303
|
||||
P2=$(db "SELECT p1_id FROM matches WHERE session_id=$SB AND p2_id IS NOT NULL ORDER BY id LIMIT 1")
|
||||
check "deck on matches" "$(req -d "deck_$P2=Elves" "$B/admin/session/$SB/decks")" 303
|
||||
check "import standings" "$(req "${STANDINGS[@]}" "$B/admin/session/$SB/import-melee")" 303
|
||||
if [[ -n "$(db "SELECT 1 FROM session_results WHERE session_id=$SB AND player_id=$P2")" ]]; then
|
||||
check "standings picked up the match deck" "$(decks_of "$SB" "$P2")" Elves
|
||||
else echo "skip standings deck check: player not in the standings export"; fi
|
||||
|
||||
echo "== scores"
|
||||
# only meaningful when the matches exports cover every round of the standings export
|
||||
python3 -I - "$DIR" "$(curl -s "$B/api/league/$LG/leaderboard")" "$(db "SELECT json_agg(json_build_object('name', p.name, 'points', r.points))
|
||||
FROM session_results r JOIN players p ON p.id=r.player_id WHERE r.session_id=$SA")" <<'EOF' || fails=$((fails + 1))
|
||||
import glob, json, sys
|
||||
d, board, final = sys.argv[1], json.loads(sys.argv[2]), json.loads(sys.argv[3])
|
||||
rounds = {m["RoundNumber"] for f in glob.glob(d + "/*atches*.json") for m in json.load(open(f))}
|
||||
last = max(r["RoundNumber"] for f in glob.glob(d + "/*tandings*.json") for r in json.load(open(f)))
|
||||
if rounds != set(range(1, last + 1)):
|
||||
print("skip leaderboard check: matches exports don't cover rounds 1 to", last); sys.exit(0)
|
||||
got = {r["name"]: r["total"] / 2 for r in board} # sessions A and B hold the same night
|
||||
bad = [f"{r['name']} {got.get(r['name'])} != {r['points']}" for r in final if got.get(r["name"]) != r["points"]]
|
||||
if bad: print("FAIL leaderboard vs Melee standings:", bad); sys.exit(1)
|
||||
print("ok leaderboard matches Melee's points for", len(final), "players")
|
||||
EOF
|
||||
|
||||
echo "== delete league"
|
||||
check "delete league" "$(req -X POST "$B/admin/league/$LG/delete")" 303
|
||||
check "league gone" "$(db "SELECT count(*) FROM leagues WHERE id=$LG")" 0
|
||||
check "its sessions gone" "$(db "SELECT count(*) FROM sessions WHERE id IN ($SA, $SB)")" 0
|
||||
check "its matches gone" "$(db "SELECT count(*) FROM matches WHERE session_id IN ($SA, $SB)")" 0
|
||||
|
||||
echo; [[ $fails == 0 ]] && echo "all e2e checks passed" || { echo "$fails e2e check(s) failed"; exit 1; }
|
||||
Reference in new issue
Block a user