this repo has no description
1#!/bin/bash 2# E2E tests for PDS - runs against local wrangler dev 3set -e 4 5BASE="http://localhost:8787" 6DID="did:plc:c6vxslynzebnlk5kw2orx37o" 7 8# Helper for colored output 9pass() { echo "$1"; } 10fail() { 11 echo "$1" >&2 12 cleanup 13 exit 1 14} 15 16# Cleanup function 17cleanup() { 18 if [ -n "$WRANGLER_PID" ]; then 19 echo "Shutting down wrangler..." 20 kill $WRANGLER_PID 2>/dev/null || true 21 wait $WRANGLER_PID 2>/dev/null || true 22 fi 23} 24trap cleanup EXIT 25 26# Start wrangler dev 27echo "Starting wrangler dev..." 28npx wrangler dev --port 8787 >/dev/null 2>&1 & 29WRANGLER_PID=$! 30 31# Wait for server to be ready 32for i in {1..30}; do 33 if curl -sf "$BASE/" >/dev/null 2>&1; then 34 break 35 fi 36 sleep 0.5 37done 38 39# Verify server is up 40curl -sf "$BASE/" >/dev/null || fail "Server failed to start" 41pass "Server started" 42 43# Initialize PDS 44PRIVKEY=$(openssl rand -hex 32) 45curl -sf -X POST "$BASE/init?did=$DID" \ 46 -H "Content-Type: application/json" \ 47 -d "{\"did\":\"$DID\",\"privateKey\":\"$PRIVKEY\",\"handle\":\"test.local\"}" >/dev/null && 48 pass "PDS initialized" || fail "PDS init" 49 50echo 51echo "Running tests..." 52echo 53 54# 1. Root returns ASCII art 55curl -sf "$BASE/" | grep -q "PDS" && pass "Root returns ASCII art" || fail "Root" 56 57# 2. describeServer works 58curl -sf "$BASE/xrpc/com.atproto.server.describeServer" | jq -e '.did' >/dev/null && 59 pass "describeServer" || fail "describeServer" 60 61# 3. resolveHandle works 62curl -sf "$BASE/xrpc/com.atproto.identity.resolveHandle?handle=test.local" | 63 jq -e '.did' >/dev/null && pass "resolveHandle" || fail "resolveHandle" 64 65# 4. createSession returns tokens 66SESSION=$(curl -sf -X POST "$BASE/xrpc/com.atproto.server.createSession" \ 67 -H "Content-Type: application/json" \ 68 -d "{\"identifier\":\"$DID\",\"password\":\"test-password\"}") 69TOKEN=$(echo "$SESSION" | jq -r '.accessJwt') 70[ "$TOKEN" != "null" ] && [ -n "$TOKEN" ] && pass "createSession returns token" || fail "createSession" 71 72# 5. getSession works with token 73curl -sf "$BASE/xrpc/com.atproto.server.getSession" \ 74 -H "Authorization: Bearer $TOKEN" | jq -e '.did' >/dev/null && 75 pass "getSession with valid token" || fail "getSession" 76 77# 6. Protected endpoint rejects without auth 78STATUS=$(curl -s -o /dev/null -w "%{http_code}" -X POST "$BASE/xrpc/com.atproto.repo.createRecord" \ 79 -H "Content-Type: application/json" \ 80 -d '{"repo":"x","collection":"x","record":{}}') 81[ "$STATUS" = "401" ] && pass "createRecord rejects without auth" || fail "createRecord should reject" 82 83# 7. getPreferences works (returns empty array initially) 84curl -sf "$BASE/xrpc/app.bsky.actor.getPreferences" \ 85 -H "Authorization: Bearer $TOKEN" | jq -e '.preferences' >/dev/null && 86 pass "getPreferences" || fail "getPreferences" 87 88# 8. putPreferences works 89curl -sf -X POST "$BASE/xrpc/app.bsky.actor.putPreferences" \ 90 -H "Authorization: Bearer $TOKEN" \ 91 -H "Content-Type: application/json" \ 92 -d '{"preferences":[{"$type":"app.bsky.actor.defs#savedFeedsPrefV2"}]}' >/dev/null && 93 pass "putPreferences" || fail "putPreferences" 94 95# 9. createRecord works with auth 96RECORD=$(curl -sf -X POST "$BASE/xrpc/com.atproto.repo.createRecord" \ 97 -H "Authorization: Bearer $TOKEN" \ 98 -H "Content-Type: application/json" \ 99 -d "{\"repo\":\"$DID\",\"collection\":\"app.bsky.feed.post\",\"record\":{\"text\":\"test\",\"createdAt\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}}") 100URI=$(echo "$RECORD" | jq -r '.uri') 101[ "$URI" != "null" ] && [ -n "$URI" ] && pass "createRecord with auth" || fail "createRecord" 102 103# 10. getRecord retrieves it 104RKEY=$(echo "$URI" | sed 's|.*/||') 105curl -sf "$BASE/xrpc/com.atproto.repo.getRecord?repo=$DID&collection=app.bsky.feed.post&rkey=$RKEY" | 106 jq -e '.value.text' >/dev/null && pass "getRecord" || fail "getRecord" 107 108# 11. putRecord updates the record 109curl -sf -X POST "$BASE/xrpc/com.atproto.repo.putRecord" \ 110 -H "Authorization: Bearer $TOKEN" \ 111 -H "Content-Type: application/json" \ 112 -d "{\"repo\":\"$DID\",\"collection\":\"app.bsky.feed.post\",\"rkey\":\"$RKEY\",\"record\":{\"text\":\"updated\",\"createdAt\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}}" | 113 jq -e '.uri' >/dev/null && pass "putRecord" || fail "putRecord" 114 115# 12. listRecords shows the record 116curl -sf "$BASE/xrpc/com.atproto.repo.listRecords?repo=$DID&collection=app.bsky.feed.post" | 117 jq -e '.records | length > 0' >/dev/null && pass "listRecords" || fail "listRecords" 118 119# 13. describeRepo returns repo info 120curl -sf "$BASE/xrpc/com.atproto.repo.describeRepo?repo=$DID" | 121 jq -e '.did' >/dev/null && pass "describeRepo" || fail "describeRepo" 122 123# 14. applyWrites batch operation (create then delete a record) 124APPLY_RESULT=$(curl -sf -X POST "$BASE/xrpc/com.atproto.repo.applyWrites" \ 125 -H "Authorization: Bearer $TOKEN" \ 126 -H "Content-Type: application/json" \ 127 -d "{\"repo\":\"$DID\",\"writes\":[{\"\$type\":\"com.atproto.repo.applyWrites#create\",\"collection\":\"app.bsky.feed.post\",\"rkey\":\"applytest\",\"value\":{\"text\":\"batch\",\"createdAt\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}}]}") 128echo "$APPLY_RESULT" | jq -e '.results' >/dev/null && pass "applyWrites create" || fail "applyWrites create" 129 130# 15. applyWrites delete 131curl -sf -X POST "$BASE/xrpc/com.atproto.repo.applyWrites" \ 132 -H "Authorization: Bearer $TOKEN" \ 133 -H "Content-Type: application/json" \ 134 -d "{\"repo\":\"$DID\",\"writes\":[{\"\$type\":\"com.atproto.repo.applyWrites#delete\",\"collection\":\"app.bsky.feed.post\",\"rkey\":\"applytest\"}]}" | 135 jq -e '.results' >/dev/null && pass "applyWrites delete" || fail "applyWrites delete" 136 137# 16. sync.getLatestCommit returns head 138curl -sf "$BASE/xrpc/com.atproto.sync.getLatestCommit?did=$DID" | 139 jq -e '.cid' >/dev/null && pass "sync.getLatestCommit" || fail "sync.getLatestCommit" 140 141# 17. sync.getRepoStatus returns status 142curl -sf "$BASE/xrpc/com.atproto.sync.getRepoStatus?did=$DID" | 143 jq -e '.did' >/dev/null && pass "sync.getRepoStatus" || fail "sync.getRepoStatus" 144 145# 18. sync.getRepo returns CAR file 146REPO_SIZE=$(curl -sf "$BASE/xrpc/com.atproto.sync.getRepo?did=$DID" | wc -c) 147[ "$REPO_SIZE" -gt 100 ] && pass "sync.getRepo returns CAR" || fail "sync.getRepo" 148 149# 19. sync.getRecord returns record with proof (binary CAR data) 150RECORD_SIZE=$(curl -sf "$BASE/xrpc/com.atproto.sync.getRecord?did=$DID&collection=app.bsky.feed.post&rkey=$RKEY" | wc -c) 151[ "$RECORD_SIZE" -gt 50 ] && pass "sync.getRecord" || fail "sync.getRecord" 152 153# 20. sync.listRepos lists repos 154curl -sf "$BASE/xrpc/com.atproto.sync.listRepos" | 155 jq -e '.repos | length > 0' >/dev/null && pass "sync.listRepos" || fail "sync.listRepos" 156 157# Error handling tests 158echo 159echo "Testing error handling..." 160 161# 21. Invalid password rejected 162STATUS=$(curl -s -o /dev/null -w "%{http_code}" -X POST "$BASE/xrpc/com.atproto.server.createSession" \ 163 -H "Content-Type: application/json" \ 164 -d "{\"identifier\":\"$DID\",\"password\":\"wrong-password\"}") 165[ "$STATUS" = "401" ] && pass "Invalid password rejected (401)" || fail "Invalid password should return 401" 166 167# 22. Wrong repo rejected (can't modify another user's repo) 168STATUS=$(curl -s -o /dev/null -w "%{http_code}" -X POST "$BASE/xrpc/com.atproto.repo.createRecord" \ 169 -H "Authorization: Bearer $TOKEN" \ 170 -H "Content-Type: application/json" \ 171 -d '{"repo":"did:plc:z72i7hdynmk6r22z27h6tvur","collection":"app.bsky.feed.post","record":{"text":"x","createdAt":"2024-01-01T00:00:00Z"}}') 172[ "$STATUS" = "403" ] && pass "Wrong repo rejected (403)" || fail "Wrong repo should return 403" 173 174# 23. Non-existent record returns 404 175STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$BASE/xrpc/com.atproto.repo.getRecord?repo=$DID&collection=app.bsky.feed.post&rkey=nonexistent") 176[ "$STATUS" = "400" ] || [ "$STATUS" = "404" ] && pass "Non-existent record error" || fail "Non-existent record should error" 177 178# Cleanup: delete the test record 179curl -sf -X POST "$BASE/xrpc/com.atproto.repo.deleteRecord" \ 180 -H "Authorization: Bearer $TOKEN" \ 181 -H "Content-Type: application/json" \ 182 -d "{\"repo\":\"$DID\",\"collection\":\"app.bsky.feed.post\",\"rkey\":\"$RKEY\"}" >/dev/null && 183 pass "deleteRecord (cleanup)" || fail "deleteRecord" 184 185echo 186echo "All tests passed!"