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

chadtmiller.com/pds.js

An atproto PDS in JavaScript, for Cloudflare Workers and Node

0
0
main
111 lines · 2.8 KB Raw History
// test/helpers/deno-server.js
import { spawn } from 'node:child_process';
import { mkdirSync, rmSync } from 'node:fs';
import { TEST_PORT } from './test-port.js';

const TEST_DATA_DIR = './test-data';
const USE_LOCAL_INFRA = process.env.USE_LOCAL_INFRA !== 'false';

/** @type {import('node:child_process').ChildProcess | null} */
let denoProcess = null;

/**
 * Start Deno PDS server for e2e tests
 * Spawns Deno process running examples/deno/main.ts
 * @returns {Promise<{close: () => Promise<void>}>} Server instance with close() method
 */
export async function startDenoServer() {
  // Fresh data each run
  rmSync(TEST_DATA_DIR, { recursive: true, force: true });
  mkdirSync(TEST_DATA_DIR, { recursive: true });

  const hostname = USE_LOCAL_INFRA
    ? 'host.docker.internal:3443'
    : `localhost:${TEST_PORT}`;

  const env = {
    ...process.env,
    PDS_DB_PATH: `${TEST_DATA_DIR}/pds.db`,
    PDS_BLOBS_DIR: `${TEST_DATA_DIR}/blobs`,
    JWT_SECRET: 'test-secret-for-e2e',
    HOSTNAME: hostname,
    PORT: String(TEST_PORT),
    PDS_PASSWORD: 'test-password',
    RELAY_URL: USE_LOCAL_INFRA ? 'http://localhost:2470' : '',
    APPVIEW_URL: 'https://api.bsky.app',
    APPVIEW_DID: 'did:web:api.bsky.app',
  };

  denoProcess = spawn(
    'deno',
    [
      'run',
      '--allow-net',
      '--allow-read',
      '--allow-write',
      '--allow-env',
      '--unstable-ffi',
      'examples/deno/main.ts',
    ],
    {
      env,
      stdio: ['ignore', 'pipe', 'pipe'],
      cwd: process.cwd(),
    },
  );

  // Log output for debugging
  denoProcess.stdout?.on('data', (data) => {
    const msg = data.toString().trim();
    if (msg) console.log(`[deno] ${msg}`);
  });

  denoProcess.stderr?.on('data', (data) => {
    const msg = data.toString().trim();
    if (msg) console.error(`[deno] ${msg}`);
  });

  // Wait for server to be ready
  await waitForReady();

  return {
    async close() {
      if (denoProcess) {
        denoProcess.kill('SIGTERM');
        // Wait for process to exit
        await new Promise((resolve) => {
          denoProcess?.on('exit', resolve);
          // Timeout after 5s
          setTimeout(resolve, 5000);
        });
        denoProcess = null;
      }
    },
  };
}

/**
 * Wait for Deno server to be ready
 */
async function waitForReady(maxAttempts = 30) {
  for (let i = 0; i < maxAttempts; i++) {
    try {
      const res = await fetch(`http://localhost:${TEST_PORT}/`);
      if (res.ok) return;
    } catch {
      // Server not ready yet
    }
    await new Promise((r) => setTimeout(r, 500));
  }
  throw new Error('Deno server failed to start');
}

/**
 * Stop Deno PDS server
 * @param {{close: () => Promise<void>}} server - Server instance from startDenoServer
 */
export async function stopDenoServer(server) {
  await server.close();
}

export { TEST_PORT, USE_LOCAL_INFRA };