#!/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 .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 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 ]