~ knotless Search/
↑↓ move↵ open⇧↵ all results

chadtmiller.com/pds.js

An atproto PDS in JavaScript, for Cloudflare Workers and Node

0
0
main
352 lines · 7.8 KB Raw History
# 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"
```