+1
-1
package.json
+1
-1
package.json
+39
-35
test/e2e.sh
+39
-35
test/e2e.sh
···
7
8
# Helper for colored output
9
pass() { echo "✓ $1"; }
10
-
fail() { echo "✗ $1" >&2; cleanup; exit 1; }
11
12
# Cleanup function
13
cleanup() {
···
21
22
# Start wrangler dev
23
echo "Starting wrangler dev..."
24
-
npx wrangler dev --port 8787 > /dev/null 2>&1 &
25
WRANGLER_PID=$!
26
27
# Wait for server to be ready
28
for i in {1..30}; do
29
-
if curl -sf "$BASE/" > /dev/null 2>&1; then
30
break
31
fi
32
sleep 0.5
33
done
34
35
# Verify server is up
36
-
curl -sf "$BASE/" > /dev/null || fail "Server failed to start"
37
pass "Server started"
38
39
# Initialize PDS
40
PRIVKEY=$(openssl rand -hex 32)
41
curl -sf -X POST "$BASE/init?did=$DID" \
42
-H "Content-Type: application/json" \
43
-
-d "{\"did\":\"$DID\",\"privateKey\":\"$PRIVKEY\",\"handle\":\"test.local\"}" > /dev/null \
44
-
&& pass "PDS initialized" || fail "PDS init"
45
46
echo
47
echo "Running tests..."
···
51
curl -sf "$BASE/" | grep -q "PDS" && pass "Root returns ASCII art" || fail "Root"
52
53
# 2. describeServer works
54
-
curl -sf "$BASE/xrpc/com.atproto.server.describeServer" | jq -e '.did' > /dev/null \
55
-
&& pass "describeServer" || fail "describeServer"
56
57
# 3. resolveHandle works
58
-
curl -sf "$BASE/xrpc/com.atproto.identity.resolveHandle?handle=test.local" \
59
-
| jq -e '.did' > /dev/null && pass "resolveHandle" || fail "resolveHandle"
60
61
# 4. createSession returns tokens
62
SESSION=$(curl -sf -X POST "$BASE/xrpc/com.atproto.server.createSession" \
···
67
68
# 5. getSession works with token
69
curl -sf "$BASE/xrpc/com.atproto.server.getSession" \
70
-
-H "Authorization: Bearer $TOKEN" | jq -e '.did' > /dev/null \
71
-
&& pass "getSession with valid token" || fail "getSession"
72
73
# 6. Protected endpoint rejects without auth
74
STATUS=$(curl -s -o /dev/null -w "%{http_code}" -X POST "$BASE/xrpc/com.atproto.repo.createRecord" \
···
78
79
# 7. getPreferences works (returns empty array initially)
80
curl -sf "$BASE/xrpc/app.bsky.actor.getPreferences" \
81
-
-H "Authorization: Bearer $TOKEN" | jq -e '.preferences' > /dev/null \
82
-
&& pass "getPreferences" || fail "getPreferences"
83
84
# 8. putPreferences works
85
curl -sf -X POST "$BASE/xrpc/app.bsky.actor.putPreferences" \
86
-H "Authorization: Bearer $TOKEN" \
87
-H "Content-Type: application/json" \
88
-
-d '{"preferences":[{"$type":"app.bsky.actor.defs#savedFeedsPrefV2"}]}' > /dev/null \
89
-
&& pass "putPreferences" || fail "putPreferences"
90
91
# 9. createRecord works with auth
92
RECORD=$(curl -sf -X POST "$BASE/xrpc/com.atproto.repo.createRecord" \
···
98
99
# 10. getRecord retrieves it
100
RKEY=$(echo "$URI" | sed 's|.*/||')
101
-
curl -sf "$BASE/xrpc/com.atproto.repo.getRecord?repo=$DID&collection=app.bsky.feed.post&rkey=$RKEY" \
102
-
| jq -e '.value.text' > /dev/null && pass "getRecord" || fail "getRecord"
103
104
# 11. putRecord updates the record
105
curl -sf -X POST "$BASE/xrpc/com.atproto.repo.putRecord" \
106
-H "Authorization: Bearer $TOKEN" \
107
-H "Content-Type: application/json" \
108
-
-d "{\"repo\":\"$DID\",\"collection\":\"app.bsky.feed.post\",\"rkey\":\"$RKEY\",\"record\":{\"text\":\"updated\",\"createdAt\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}}" \
109
-
| jq -e '.uri' > /dev/null && pass "putRecord" || fail "putRecord"
110
111
# 12. listRecords shows the record
112
-
curl -sf "$BASE/xrpc/com.atproto.repo.listRecords?repo=$DID&collection=app.bsky.feed.post" \
113
-
| jq -e '.records | length > 0' > /dev/null && pass "listRecords" || fail "listRecords"
114
115
# 13. describeRepo returns repo info
116
-
curl -sf "$BASE/xrpc/com.atproto.repo.describeRepo?repo=$DID" \
117
-
| jq -e '.did' > /dev/null && pass "describeRepo" || fail "describeRepo"
118
119
# 14. applyWrites batch operation (create then delete a record)
120
APPLY_RESULT=$(curl -sf -X POST "$BASE/xrpc/com.atproto.repo.applyWrites" \
121
-H "Authorization: Bearer $TOKEN" \
122
-H "Content-Type: application/json" \
123
-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)\"}}]}")
124
-
echo "$APPLY_RESULT" | jq -e '.results' > /dev/null && pass "applyWrites create" || fail "applyWrites create"
125
126
# 15. applyWrites delete
127
curl -sf -X POST "$BASE/xrpc/com.atproto.repo.applyWrites" \
128
-H "Authorization: Bearer $TOKEN" \
129
-H "Content-Type: application/json" \
130
-
-d "{\"repo\":\"$DID\",\"writes\":[{\"\$type\":\"com.atproto.repo.applyWrites#delete\",\"collection\":\"app.bsky.feed.post\",\"rkey\":\"applytest\"}]}" \
131
-
| jq -e '.results' > /dev/null && pass "applyWrites delete" || fail "applyWrites delete"
132
133
# 16. sync.getLatestCommit returns head
134
-
curl -sf "$BASE/xrpc/com.atproto.sync.getLatestCommit?did=$DID" \
135
-
| jq -e '.cid' > /dev/null && pass "sync.getLatestCommit" || fail "sync.getLatestCommit"
136
137
# 17. sync.getRepoStatus returns status
138
-
curl -sf "$BASE/xrpc/com.atproto.sync.getRepoStatus?did=$DID" \
139
-
| jq -e '.did' > /dev/null && pass "sync.getRepoStatus" || fail "sync.getRepoStatus"
140
141
# 18. sync.getRepo returns CAR file
142
REPO_SIZE=$(curl -sf "$BASE/xrpc/com.atproto.sync.getRepo?did=$DID" | wc -c)
···
147
[ "$RECORD_SIZE" -gt 50 ] && pass "sync.getRecord" || fail "sync.getRecord"
148
149
# 20. sync.listRepos lists repos
150
-
curl -sf "$BASE/xrpc/com.atproto.sync.listRepos" \
151
-
| jq -e '.repos | length > 0' > /dev/null && pass "sync.listRepos" || fail "sync.listRepos"
152
153
# Error handling tests
154
echo
···
175
curl -sf -X POST "$BASE/xrpc/com.atproto.repo.deleteRecord" \
176
-H "Authorization: Bearer $TOKEN" \
177
-H "Content-Type: application/json" \
178
-
-d "{\"repo\":\"$DID\",\"collection\":\"app.bsky.feed.post\",\"rkey\":\"$RKEY\"}" > /dev/null \
179
-
&& pass "deleteRecord (cleanup)" || fail "deleteRecord"
180
181
echo
182
echo "All tests passed!"
···
7
8
# Helper for colored output
9
pass() { echo "✓ $1"; }
10
+
fail() {
11
+
echo "✗ $1" >&2
12
+
cleanup
13
+
exit 1
14
+
}
15
16
# Cleanup function
17
cleanup() {
···
25
26
# Start wrangler dev
27
echo "Starting wrangler dev..."
28
+
npx wrangler dev --port 8787 >/dev/null 2>&1 &
29
WRANGLER_PID=$!
30
31
# Wait for server to be ready
32
for i in {1..30}; do
33
+
if curl -sf "$BASE/" >/dev/null 2>&1; then
34
break
35
fi
36
sleep 0.5
37
done
38
39
# Verify server is up
40
+
curl -sf "$BASE/" >/dev/null || fail "Server failed to start"
41
pass "Server started"
42
43
# Initialize PDS
44
PRIVKEY=$(openssl rand -hex 32)
45
curl -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
50
echo
51
echo "Running tests..."
···
55
curl -sf "$BASE/" | grep -q "PDS" && pass "Root returns ASCII art" || fail "Root"
56
57
# 2. describeServer works
58
+
curl -sf "$BASE/xrpc/com.atproto.server.describeServer" | jq -e '.did' >/dev/null &&
59
+
pass "describeServer" || fail "describeServer"
60
61
# 3. resolveHandle works
62
+
curl -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
66
SESSION=$(curl -sf -X POST "$BASE/xrpc/com.atproto.server.createSession" \
···
71
72
# 5. getSession works with token
73
curl -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
78
STATUS=$(curl -s -o /dev/null -w "%{http_code}" -X POST "$BASE/xrpc/com.atproto.repo.createRecord" \
···
82
83
# 7. getPreferences works (returns empty array initially)
84
curl -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
89
curl -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
96
RECORD=$(curl -sf -X POST "$BASE/xrpc/com.atproto.repo.createRecord" \
···
102
103
# 10. getRecord retrieves it
104
RKEY=$(echo "$URI" | sed 's|.*/||')
105
+
curl -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
109
curl -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
116
+
curl -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
120
+
curl -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)
124
APPLY_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)\"}}]}")
128
+
echo "$APPLY_RESULT" | jq -e '.results' >/dev/null && pass "applyWrites create" || fail "applyWrites create"
129
130
# 15. applyWrites delete
131
curl -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
138
+
curl -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
142
+
curl -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
146
REPO_SIZE=$(curl -sf "$BASE/xrpc/com.atproto.sync.getRepo?did=$DID" | wc -c)
···
151
[ "$RECORD_SIZE" -gt 50 ] && pass "sync.getRecord" || fail "sync.getRecord"
152
153
# 20. sync.listRepos lists repos
154
+
curl -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
158
echo
···
179
curl -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
185
echo
186
echo "All tests passed!"