test: Add e2e release gate (script + manual checklist)
Releases need more than eyeballs: scripts/e2e-api-check.sh now runs 18 assertions over the live instance (login/refresh, listings, private rows, subscriptions incl. remote channels, watching + stored position, file token, playlist reachability) and TESTS.md defines the manual D-pad checkout that a script cannot do. Also switch the channels screen to the user's own channels (the search-based local listing rejects empty queries) and resolve the real account name at login since logins accept email addresses.
This commit is contained in:
parent
764fa8b34c
commit
83dde8ba74
10 changed files with 284 additions and 8 deletions
112
scripts/e2e-api-check.sh
Executable file
112
scripts/e2e-api-check.sh
Executable file
|
|
@ -0,0 +1,112 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# e2e-api-check.sh — scripted end-to-end smoke test against a live PeerTube instance.
|
||||
#
|
||||
# Usage: ./e2e-api-check.sh (uses ~/.agents/secrets/jtube.env via sops)
|
||||
# sops -d <file>.env | bash <(cat e2e-api-check.sh) — alternative pipe form
|
||||
#
|
||||
# Requires: curl, python3. The account may be read-only-ish; only /watching POSTs and
|
||||
# history reads are needed besides the listings. Prints PASS/FAIL per group; exits
|
||||
# non-zero if any FAIL.
|
||||
set -u
|
||||
|
||||
pass=0; fail=0
|
||||
ok() { pass=$((pass+1)); echo "PASS $1"; }
|
||||
bad() { fail=$((fail+1)); echo "FAIL $1"; }
|
||||
assert() { # assert <name> <true/false>
|
||||
if [ "${2:-x}" = "true" ]; then ok "$1"; else bad "$1"; fi
|
||||
}
|
||||
|
||||
# ---- credentials -----------------------------------------------------------
|
||||
if [ "${JTUBE_URL:-}" = "" ]; then
|
||||
if command -v sops >/dev/null && [ -f "$HOME/.agents/secrets/jtube.env" ]; then
|
||||
eval "$(sops -d --output-type dotenv "$HOME/.agents/secrets/jtube.env" | sed 's/^/export /')"
|
||||
else
|
||||
echo "No credentials: set JTUBE_URL/JTUBE_USER/JTUBE_PASS or provide the store"; exit 2
|
||||
fi
|
||||
fi
|
||||
|
||||
# ---- login -----------------------------------------------------------------
|
||||
CLIENT=$(curl -s "$JTUBE_URL/api/v1/oauth-clients/local")
|
||||
CID=$(echo "$CLIENT" | python3 -c "import json,sys;print(json.load(sys.stdin)['client_id'])")
|
||||
CSEC=$(echo "$CLIENT" | python3 -c "import json,sys;print(json.load(sys.stdin)['client_secret'])")
|
||||
TOKJSON=$(curl -s -X POST "$JTUBE_URL/api/v1/users/token" \
|
||||
-d "grant_type=password" -d "client_id=$CID" -d "client_secret=$CSEC" \
|
||||
-d "username=$JTUBE_USER" -d "password=$JTUBE_PASS")
|
||||
TOK=$(echo "$TOKJSON" | python3 -c "import json,sys;print(json.load(sys.stdin).get('access_token',''))")
|
||||
RTOK=$(echo "$TOKJSON" | python3 -c "import json,sys;print(json.load(sys.stdin).get('refresh_token',''))")
|
||||
assert "F0 login (password grant)" $([ -n "$TOK" ] && [ -n "$RTOK" ] && echo true || echo false)
|
||||
|
||||
AUTH="Authorization: Bearer $TOK"
|
||||
jqp() { python3 -c "import json,sys;d=json.load(sys.stdin);import functools;print(eval('d'+sys.argv[1]))" "$1" 2>/dev/null; }
|
||||
|
||||
# ---- F0b: refresh flow -----------------------------------------------------
|
||||
NEW=$(curl -s -X POST "$JTUBE_URL/api/v1/users/token" \
|
||||
-d "grant_type=refresh_token" -d "client_id=$CID" -d "client_secret=$CSEC" -d "refresh_token=$RTOK" \
|
||||
| jqp "['access_token']")
|
||||
assert "F0 token refresh" $([ -n "$NEW" ] && echo true || echo false)
|
||||
TOK="$NEW"; AUTH="Authorization: Bearer $TOK"
|
||||
|
||||
# ---- F1 public local videos ------------------------------------------------
|
||||
L=$(curl -s "$JTUBE_URL/api/v1/videos?isLocal=true&count=10&sort=-publishedAt")
|
||||
N=$(echo "$L" | jqp "['total']")
|
||||
assert "F1 local videos total>0" $([ "${N:-0}" -gt 0 ] && echo true || echo false)
|
||||
FIRST_UUID=$(echo "$L" | jqp "['data'][0]['shortUUID']")
|
||||
|
||||
# ---- F1 me/videos incl. private -------------------------------------------
|
||||
M=$(curl -s -H "$AUTH" "$JTUBE_URL/api/v1/users/me/videos?count=25&sort=-publishedAt")
|
||||
assert "F1 my-videos wrapped {total,data}" $(echo "$M" | python3 -c "import json,sys;d=json.load(sys.stdin);print('true' if 'total' in d and isinstance(d.get('data'),list) else 'false')")
|
||||
assert "F1 my-videos has entries" $(echo "$M" | jqp "['total'] > 0 and 'true' or 'false'")
|
||||
PRIV=$(echo "$M" | python3 -c "
|
||||
import json,sys
|
||||
d=json.load(sys.stdin)
|
||||
print('true' if any(v['privacy']['id']!=1 for v in d['data']) else 'false')")
|
||||
assert "F1 non-public entries present (seeded test videos)" "$PRIV"
|
||||
|
||||
# ---- F2 own channels -------------------------------------------------------
|
||||
USERNAME=$(curl -s -H "$AUTH" "$JTUBE_URL/api/v1/users/me" | jqp "['username']")
|
||||
AC=$(curl -s -H "$AUTH" "$JTUBE_URL/api/v1/accounts/$USERNAME/video-channels")
|
||||
assert "F2 own channels listed" $(echo "$AC" | python3 -c "import json,sys;print('true' if 'total' in json.load(sys.stdin) else 'false')")
|
||||
CN=$(echo "$AC" | python3 -c "import json,sys; d=json.load(sys.stdin)['data'][0]; print(d['name']+'@'+d['host'])")
|
||||
CV=$(curl -s -H "$AUTH" "$JTUBE_URL/api/v1/video-channels/$CN/videos?count=3")
|
||||
assert "F2 channel videos endpoint" $(echo "$CV" | python3 -c "import json,sys;print('true' if 'total' in json.load(sys.stdin) else 'false')")
|
||||
LC=$(curl -s "$JTUBE_URL/api/v1/video-channels?search=tube&searchTarget=local&count=5")
|
||||
assert "F2 local-channel search (non-empty q)" $(echo "$LC" | python3 -c "import json,sys;print('true' if 'total' in json.load(sys.stdin) else 'false')")
|
||||
|
||||
# ---- F3 search --------------------------------------------------------------
|
||||
S=$(curl -s "$JTUBE_URL/api/v1/videos?search=$FIRST_UUID&isLocal=true&count=5")
|
||||
S2=$(curl -s "$JTUBE_URL/api/v1/videos?search=test&isLocal=true&count=5")
|
||||
assert "F3 search returns list shape" $(echo "$S2" | python3 -c "import json,sys;d=json.load(sys.stdin);print('true' if 'total' in d else 'false')")
|
||||
|
||||
# ---- F4 subscriptions -------------------------------------------------------
|
||||
SUBS=$(curl -s -H "$AUTH" "$JTUBE_URL/api/v1/users/me/subscriptions/videos?count=10&sort=-publishedAt")
|
||||
assert "F4 subscription feed wrapped" $(echo "$SUBS" | python3 -c "import json,sys;d=json.load(sys.stdin);print('true' if 'total' in d else 'false')")
|
||||
SUBS_REMOTE=$(echo "$SUBS" | python3 -c "
|
||||
import json,sys,os
|
||||
d=json.load(sys.stdin)
|
||||
host=os.environ['JTUBE_URL'].replace('https://','').rstrip('/')
|
||||
print('true' if any(v['channel']['host']!=host for v in d['data']) else 'false')")
|
||||
assert "F4 remote-channel entries present" "$SUBS_REMOTE"
|
||||
|
||||
# ---- F5+F6 history & watching ----------------------------------------------
|
||||
H=$(curl -s -H "$AUTH" "$JTUBE_URL/api/v1/users/me/history/videos?count=10")
|
||||
assert "F5 history wrapped" $(echo "$H" | python3 -c "import json,sys;d=json.load(sys.stdin);print('true' if 'total' in d else 'false')")
|
||||
CODE=$(curl -s -o /dev/null -w "%{http_code}" -X POST -H "$AUTH" -H "Content-Type: application/json" \
|
||||
-d '{"currentTime":3}' "$JTUBE_URL/api/v1/videos/$FIRST_UUID/watching")
|
||||
assert "F6 watching -> 204" $([ "$CODE" = "204" ] && echo true || echo false)
|
||||
UH=$(curl -s -H "$AUTH" "$JTUBE_URL/api/v1/videos/$FIRST_UUID?userHistory=true" | jqp "['userHistory']['currentTime']")
|
||||
assert "F6 watching stored (userHistory)" $( [ -n "$UH" ] && echo true || echo false )
|
||||
|
||||
# ---- playback URLs ----------------------------------------------------------
|
||||
D=$(curl -s -H "$AUTH" "$JTUBE_URL/api/v1/videos/$FIRST_UUID")
|
||||
PL=$(echo "$D" | jqp "['streamingPlaylists'][0]['playlistUrl']")
|
||||
assert "F6 detail exposes stream URL" $([ -n "$PL" ] && echo true || echo false)
|
||||
VFTCODE=$(curl -s -o /dev/null -w "%{http_code}" -X POST -H "$AUTH" "$JTUBE_URL/api/v1/videos/$FIRST_UUID/token")
|
||||
assert "F6 file token endpoint exists" $([ "$VFTCODE" = "200" ] && echo true || echo false)
|
||||
VFT=$(curl -s -X POST -H "$AUTH" "$JTUBE_URL/api/v1/videos/$FIRST_UUID/token" | jqp "['files']['token']")
|
||||
HC=$(curl -s -o /dev/null -w "%{http_code}" "$PL?videoFileToken=$VFT")
|
||||
assert "F6 playlist reachable" $([ "$HC" = "200" ] && echo true || echo false)
|
||||
|
||||
echo "-----------------------------"
|
||||
echo "PASS=$pass FAIL=$fail"
|
||||
[ "$fail" = 0 ]
|
||||
Loading…
Add table
Add a link
Reference in a new issue