chadtmiller.com/pds.js
An atproto PDS in JavaScript, for Cloudflare Workers and Node
0
0
# 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**
```javascript
// 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**
```bash
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:
```javascript
/**
* 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:
```javascript
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):
```javascript
// 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:
```javascript
/** @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**
```bash
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:
```javascript
/**
* 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**
```bash
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:
```json
"test:e2e": "node --test test/e2e.test.js",
```
With:
```json
"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**
```bash
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:
```toml
main = "src/pds.js"
```
With:
```toml
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**
```bash
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**
```bash
git add -A
git commit -m "chore: remove old monolithic pds.js and unused test helpers"
```