# 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} 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" ```