chadtmiller.com/pds.js
An atproto PDS in JavaScript, for Cloudflare Workers and Node
Fix Remaining E2E Tests Implementation Plan
For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
Goal: Fix remaining 27 failing e2e tests on Node (and corresponding Cloudflare failures)
Architecture: The tests exercise core PDS functionality. Most failures are in packages/core/pds.js handlers that have missing validations, incorrect responses, or missing headers. OAuth scope tests depend on getOAuthTokenWithScope helper which needs the full PAR→authorize→token flow to work.
Tech Stack: JavaScript, Vitest, Node.js test runner
Current Status
- Node: 44 passed, 27 failed
- Cloudflare: 27 passed, 44 failed
Failure Categories
- Root path (1 test) - Text missing "PDS"
- Blob operations (4 tests) - Missing headers and validation
- OAuth token flow (5 tests) - PAR→authorize→token broken
- OAuth scope tests (7 tests) - Depend on token flow
- Foreign DID/proxy (5 tests) - Not implemented
- Misc (5 tests) - Various issues
Task 1: Fix Root Path Response
Files:
- Modify:
packages/core/pds.js:1447
Step 1: Update handleRoot text
Change line 1447 from:
ATProto Personal Data Server
to:
ATProto PDS - Personal Data Server
Step 2: Run test to verify
Run: PLATFORM=node pnpm vitest run test/e2e.test.js -t "root returns ASCII"
Expected: PASS
Step 3: Commit
git add packages/core/pds.js
git commit -m "fix: add PDS text to root path response"
Task 2: Fix getBlob Missing Headers
Files:
- Modify:
packages/core/pds.js:1780-1785
Step 1: Add security headers to handleGetBlob
Replace response creation:
return new Response(result.data, {
headers: {
'Content-Type': result.mimeType,
'Cache-Control': 'public, max-age=31536000, immutable',
'X-Content-Type-Options': 'nosniff',
},
})
Step 2: Run test to verify
Run: PLATFORM=node pnpm vitest run test/e2e.test.js -t "getBlob retrieves data"
Expected: PASS
Step 3: Commit
git add packages/core/pds.js
git commit -m "fix: add X-Content-Type-Options header to getBlob"
Task 3: Fix getBlob DID Validation
Files:
- Modify:
packages/core/pds.js:1761-1770
Step 1: Add DID validation to handleGetBlob
After the missing parameters check, add:
// Validate DID matches this PDS
const myDid = await this.getDid()
if (did !== myDid) {
return Response.json(
{ error: 'InvalidRequest', message: 'DID does not match this PDS' },
{ status: 400 }
)
}
// Validate CID format (basic check)
if (!cid.startsWith('b') || cid.length < 10) {
return Response.json(
{ error: 'InvalidRequest', message: 'Invalid CID format' },
{ status: 400 }
)
}
Step 2: Run tests to verify
Run: PLATFORM=node pnpm vitest run test/e2e.test.js -t "getBlob"
Expected: All 4 getBlob tests PASS
Step 3: Commit
git add packages/core/pds.js
git commit -m "fix: add DID and CID validation to getBlob"
Task 4: Fix refreshSession Access Token Rejection
Files:
- Modify:
packages/core/pds.js- handleRefreshSession
Step 1: Find and read handleRefreshSession
Run: grep -n "handleRefreshSession" packages/core/pds.js
Step 2: Add access token detection
The handler should check if the token is an access token (not refresh token) and reject with 400.
Look for the token validation logic and ensure it distinguishes between access and refresh tokens.
Step 3: Run test to verify
Run: PLATFORM=node pnpm vitest run test/e2e.test.js -t "refreshSession rejects access token"
Expected: PASS
Step 4: Commit
git add packages/core/pds.js
git commit -m "fix: reject access tokens in refreshSession endpoint"
Task 5: Fix putPreferences
Files:
- Modify:
packages/core/pds.js- handlePutPreferences
Step 1: Find and read handlePutPreferences
Run: grep -n "handlePutPreferences\|putPreferences" packages/core/pds.js
Step 2: Investigate the failure
Run: PLATFORM=node pnpm vitest run test/e2e.test.js -t "putPreferences works"
Read the error output to understand what's failing.
Step 3: Fix the issue
Based on error output, fix the handler.
Step 4: Run test to verify
Expected: PASS
Step 5: Commit
git add packages/core/pds.js
git commit -m "fix: putPreferences handler"
Task 6: Fix Wrong Repo Rejection (403)
Files:
- Modify:
packages/core/pds.js- repo operation handlers
Step 1: Investigate the failure
Run: PLATFORM=node pnpm vitest run test/e2e.test.js -t "wrong repo rejected"
Read the error output.
Step 2: Find the handler
The test likely calls createRecord with a DID that doesn't match auth. The handler should return 403, not another status.
Step 3: Fix the issue
Ensure repo operations check that auth.did matches the requested repo DID and return 403 Forbidden if not.
Step 4: Run test to verify
Expected: PASS
Step 5: Commit
git add packages/core/pds.js
git commit -m "fix: return 403 for wrong repo access"
Task 7: Fix deleteRecord Blob Cleanup
Files:
- Modify:
packages/core/pds.js- handleDeleteRecord
Step 1: Investigate the failure
Run: PLATFORM=node pnpm vitest run test/e2e.test.js -t "deleteRecord with blob cleans up"
Read error output.
Step 2: Check blob cleanup logic
The handler should:
- Find blobs referenced by the record
- Unlink them from the record
- Run cleanup to delete orphaned blobs
Step 3: Fix the issue
Ensure the delete flow properly cleans up blob references.
Step 4: Commit
git add packages/core/pds.js
git commit -m "fix: blob cleanup on record deletion"
Task 8: Fix OAuth PAR → Authorize → Token Flow
Files:
- Modify:
packages/core/pds.jsorpackages/core/oauth.js
Step 1: Run the failing test with verbose output
Run: PLATFORM=node pnpm vitest run test/e2e.test.js -t "full PAR" --reporter=verbose
Step 2: Trace the OAuth flow
Check each step:
- PAR request creates request_uri
- Authorize POST with password validates and creates auth code
- Token exchange validates code and returns tokens
Step 3: Identify and fix the broken step
Common issues:
- DPoP proof validation
- Auth code storage/retrieval
- Token generation
Step 4: Run test to verify
Expected: PASS
Step 5: Commit
git add packages/core/*.js
git commit -m "fix: OAuth PAR authorize token flow"
Task 9: Fix DPoP Validation Issues
Files:
- Modify:
packages/core/oauth.jsorpackages/core/pds.js
Step 1: Run DPoP-related tests
Run: PLATFORM=node pnpm vitest run test/e2e.test.js -t "DPoP"
Step 2: Fix DPoP key mismatch detection
Ensure the token endpoint validates that the DPoP key matches the one used in PAR.
Step 3: Fix DPoP jti replay detection
Ensure jti values are stored and checked for replay.
Step 4: Run tests to verify
Expected: Both DPoP tests PASS
Step 5: Commit
git add packages/core/*.js
git commit -m "fix: DPoP validation for key mismatch and jti replay"
Task 10: Fix PKCE and Redirect URI Validation
Files:
- Modify:
packages/core/oauth.jsorpackages/core/pds.js
Step 1: Run PKCE test
Run: PLATFORM=node pnpm vitest run test/e2e.test.js -t "PKCE failure"
Step 2: Fix code_verifier validation
Ensure token endpoint validates code_verifier against stored code_challenge.
Step 3: Run redirect_uri test
Run: PLATFORM=node pnpm vitest run test/e2e.test.js -t "redirect_uri mismatch"
Step 4: Fix redirect_uri validation
Ensure redirect_uri in token request matches the one in PAR.
Step 5: Run tests to verify
Expected: Both tests PASS
Step 6: Commit
git add packages/core/*.js
git commit -m "fix: PKCE and redirect_uri validation"
Task 11: Fix OAuth Scope Tests (Batch)
Files:
- Depends on Task 8 (OAuth flow must work first)
Step 1: Verify OAuth flow is working
Run: PLATFORM=node pnpm vitest run test/e2e.test.js -t "full PAR"
Must PASS before proceeding.
Step 2: Run all scope tests
Run: PLATFORM=node pnpm vitest run test/e2e.test.js -t "scope"
Step 3: Investigate timeouts
The 315ms timeouts suggest the getOAuthTokenWithScope helper is failing. If the OAuth flow works (Task 8), these should start passing.
Step 4: Fix any remaining scope enforcement issues
Check that scope is properly stored and enforced in token validation.
Step 5: Commit
git add packages/core/*.js
git commit -m "fix: OAuth scope enforcement"
Task 12: Add Foreign DID / Proxy Stub Responses
Files:
- Modify:
packages/core/pds.js
Step 1: Check test expectations
The tests expect:
proxies to AppView- returns proxied response or errorunknown proxy service- returns 400malformed atproto-proxy header- returns 400returns local record for local DID- returns the recorddescribeRepo handles foreign DID- returns 404 for unknown DID
Step 2: Add basic proxy header handling
In the relevant handlers, add:
const proxyHeader = request.headers.get('atproto-proxy')
if (proxyHeader) {
// Parse and validate proxy header
const match = proxyHeader.match(/^did:[\w:]+#[\w]+$/)
if (!match) {
return Response.json(
{ error: 'InvalidRequest', message: 'Malformed atproto-proxy header' },
{ status: 400 }
)
}
// For now, return error for proxy requests (not implemented)
return Response.json(
{ error: 'InvalidRequest', message: 'Unknown proxy service' },
{ status: 400 }
)
}
Step 3: Run tests to verify
Run: PLATFORM=node pnpm vitest run test/e2e.test.js -t "proxy\|foreign"
Expected: Most tests PASS
Step 4: Commit
git add packages/core/pds.js
git commit -m "feat: add basic atproto-proxy header handling"
Task 13: Final Test Run and Cleanup
Step 1: Run all Node tests
Run: pnpm test:e2e:node
Target: 60+ tests passing
Step 2: Run all Cloudflare tests
Run: pnpm test:e2e:cloudflare
Target: 50+ tests passing
Step 3: Commit any final fixes
Step 4: Summary commit
git add .
git commit -m "test: fix e2e tests for dual platform support"
Execution Notes
- Tasks 1-3 are straightforward fixes
- Tasks 4-7 require investigation
- Tasks 8-11 are interdependent (OAuth flow must work first)
- Task 12 is additive (new functionality)
- Run
pnpm test:e2e:nodefrequently to track progress