chadtmiller.com/pds.js
An atproto PDS in JavaScript, for Cloudflare Workers and Node
Dual Platform E2E Tests Implementation Plan
For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
Goal: Run existing e2e tests against both Cloudflare (wrangler dev) and Node.js (SQLite) platforms using separate npm scripts.
Architecture: Create a Node server test helper, make BASE URL configurable via environment variable, update wrangler.toml to use the new packages architecture.
Tech Stack: Vitest, Node.js, better-sqlite3, pnpm workspaces
Task 1: Create Node Server Test Helper
Files:
- Create:
test/helpers/node-server.js
Step 1: Write the node-server.js helper
// test/helpers/node-server.js
import { rmSync, mkdirSync } from 'node:fs'
import { createServer } from '@pdsjs/node'
const TEST_DATA_DIR = './test-data'
const TEST_PORT = 3000
/**
* Start Node.js PDS server for e2e tests
* Cleans test data directory for fresh state each run
* @returns {Promise<Object>} Server instance with close() method
*/
export async function startNodeServer() {
// Fresh data each run
rmSync(TEST_DATA_DIR, { recursive: true, force: true })
mkdirSync(TEST_DATA_DIR, { recursive: true })
const server = await createServer({
port: TEST_PORT,
dbPath: `${TEST_DATA_DIR}/pds.db`,
blobsDir: `${TEST_DATA_DIR}/blobs`,
jwtSecret: 'test-secret-for-e2e',
hostname: `localhost:${TEST_PORT}`,
password: 'test-password',
appviewUrl: 'https://api.bsky.app',
appviewDid: 'did:web:api.bsky.app',
})
await server.listen()
return server
}
/**
* Stop Node.js PDS server
* @param {Object} server - Server instance from startNodeServer
*/
export async function stopNodeServer(server) {
await server.close()
}
Step 2: Verify the helper loads without errors
Run: node -e "import('./test/helpers/node-server.js')"
Expected: No errors (module loads successfully)
Step 3: Commit
git add test/helpers/node-server.js
git commit -m "feat: add node server test helper for e2e tests"
Task 2: Make BASE URL Configurable in E2E Tests
Files:
- Modify:
test/e2e.test.js:1-25
Step 1: Update imports and BASE URL logic
Replace lines 1-13 with:
/**
* E2E tests for PDS - runs against local wrangler dev or Node.js server
* Uses Vitest and fetch
*/
import { spawn } from 'node:child_process';
import { randomBytes } from 'node:crypto';
import { afterAll, beforeAll, describe, it, expect } from 'vitest';
import { DpopClient } from './helpers/dpop.js';
import { getOAuthTokenWithScope, setBaseUrl } from './helpers/oauth.js';
import { startNodeServer, stopNodeServer } from './helpers/node-server.js';
const PLATFORM = process.env.PLATFORM || 'cloudflare';
const BASE = PLATFORM === 'node' ? 'http://localhost:3000' : 'http://localhost:8787';
// Configure oauth helper to use the same BASE
setBaseUrl(BASE);
Step 2: Update beforeAll to handle both platforms
Replace the beforeAll block (lines 90-115) with:
describe('E2E Tests', () => {
/** @type {import('node:child_process').ChildProcess|null} */
let wrangler = null;
/** @type {Object|null} */
let nodeServer = null;
beforeAll(async () => {
if (PLATFORM === 'node') {
// Start Node.js server
nodeServer = await startNodeServer();
} else {
// Start wrangler
wrangler = spawn(
'npx',
['wrangler', 'dev', '--port', '8787', '--persist-to', '.wrangler/state'],
{
stdio: 'pipe',
cwd: process.cwd(),
},
);
}
await waitForServer();
// Initialize PDS
const privKey = randomBytes(32).toString('hex');
const res = await fetch(`${BASE}/init?did=${DID}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
did: DID,
privateKey: privKey,
handle: 'test.local',
}),
});
expect(res.ok).toBeTruthy();
});
afterAll(async () => {
if (PLATFORM === 'node') {
if (nodeServer) await stopNodeServer(nodeServer);
} else {
if (wrangler) wrangler.kill();
}
});
Step 3: Remove old wrangler and token variable declarations
Remove lines 16-23 (the old variable declarations outside describe block):
// DELETE THESE LINES:
/** @type {import('node:child_process').ChildProcess|null} */
let wrangler = null;
/** @type {string} */
let token = '';
/** @type {string} */
let refreshToken = '';
/** @type {string} */
let testRkey = '';
Step 4: Add variable declarations inside describe block
After the beforeAll block, add these declarations inside the describe block:
/** @type {string} */
let token = '';
/** @type {string} */
let refreshToken = '';
/** @type {string} */
let testRkey = '';
Step 5: Run the test to verify syntax
Run: node --check test/e2e.test.js
Expected: No syntax errors
Step 6: Commit
git add test/e2e.test.js
git commit -m "feat: make e2e tests platform-configurable via PLATFORM env var"
Task 3: Make BASE URL Configurable in OAuth Helper
Files:
- Modify:
test/helpers/oauth.js:1-10
Step 1: Add BASE URL configuration
Replace lines 1-8 with:
/**
* OAuth flow helpers for e2e tests
*/
import { randomBytes } from 'node:crypto';
import { DpopClient } from './dpop.js';
let BASE = 'http://localhost:8787';
/**
* Set the base URL for OAuth requests
* @param {string} url
*/
export function setBaseUrl(url) {
BASE = url;
}
Step 2: Run syntax check
Run: node --check test/helpers/oauth.js
Expected: No syntax errors
Step 3: Commit
git add test/helpers/oauth.js
git commit -m "feat: make oauth helper BASE URL configurable"
Task 4: Update package.json with Platform-Specific Scripts
Files:
- Modify:
package.json:14
Step 1: Replace the test:e2e script with platform-specific scripts
Replace line 14:
"test:e2e": "node --test test/e2e.test.js",
With:
"test:e2e:node": "PLATFORM=node vitest run test/e2e.test.js",
"test:e2e:cloudflare": "PLATFORM=cloudflare vitest run test/e2e.test.js",
Step 2: Verify package.json is valid
Run: node -e "require('./package.json')"
Expected: No errors
Step 3: Commit
git add package.json
git commit -m "feat: add platform-specific e2e test scripts"
Task 5: Update wrangler.toml Entry Point
Files:
- Modify:
wrangler.toml:2
Step 1: Update main entry point
Replace line 2:
main = "src/pds.js"
With:
main = "packages/cloudflare/index.js"
Step 2: Verify wrangler config
Run: npx wrangler deploy --dry-run
Expected: Dry run succeeds (may show warnings but no errors about missing entry point)
Step 3: Commit
git add wrangler.toml
git commit -m "feat: update wrangler entry point to new packages architecture"
Task 6: Run E2E Tests on Both Platforms
Step 1: Run Node.js e2e tests
Run: pnpm test:e2e:node
Expected: All tests pass
Step 2: Run Cloudflare e2e tests
Run: pnpm test:e2e:cloudflare
Expected: All tests pass
Step 3: Final commit if any fixes needed
If tests fail, fix issues and commit fixes before proceeding.
Task 7: Clean Up (Optional)
Files:
- Delete:
src/pds.js(old monolithic file) - Delete:
test/helpers/scenarios.js(if unused, per git status)
Step 1: Verify src/pds.js is no longer referenced
Run: grep -r "src/pds.js" --include="*.js" --include="*.json" --include="*.toml" .
Expected: No references (after wrangler.toml update)
Step 2: Delete old file
Run: rm src/pds.js
Step 3: Delete scenarios.js if unused
Run: grep -r "scenarios" test/ --include="*.js"
If no references found: rm test/helpers/scenarios.js
Step 4: Commit cleanup
git add -A
git commit -m "chore: remove old monolithic pds.js and unused test helpers"