registryOn

tcmts.cli.test.e2e.registry.registryOn · #e2q64ans · term · runs on the server

uses node:child_process

tcm registry run on the fake network, reading Jetstream every 2 seconds, until the test ends: its address (at 127.0.0.1, where a loopback OAuth client comes back to), until (waiting for something, saying what the registry said if it doesn't happen), and page (a page's HTML, without the markers server rendering puts between adjacent text). With base, it runs under that path, behind tcm serve --proxy (as tcm's own does, at tcm.codes/registry), listening on IPv6 (as on Railway's private network): url is the registry's under the server, and own the registry's own address.

import { uiPage } from "#codebase/tcmts/cli/test/e2e/contributions";
import type { Keep } from "#codebase/tcmts/cli/test/e2e/contributions";
import type { FakeAtproto } from "#codebase/tcmts/cli/test/e2e/examples";
import { plainEnv, strip, tcmBin, tempDir } from "#codebase/tcmts/cli/test/e2e/harness";
import { codebase, startServerInfo } from "#codebase/tcmts/cli/test/e2e/sync";
import assert from "node:assert/strict";
import { spawn } from "node:child_process";

export async function registryOn(keep: Keep, net: FakeAtproto, { base }: { base?: string } = {}) {
  const dir = tempDir("registry run");
  const child = keep(
    spawn(
      process.execPath,
      [
        tcmBin(),
        "registry",
        "--port",
        "0",
        "--dir",
        "here",
        ...(base ? ["--base", base, "--host", "::1"] : []),
      ],
      {
        cwd: dir,
        env: {
          ...plainEnv(),
          TCM_ATPROTO_DIRECTORY: net.url,
          TCM_ATPROTO_LOCAL: "1",
          TCM_JETSTREAM: net.url,
          TCM_RELAY: net.url,
          TCM_REGISTRY_EVERY: "2s",
        },
      },
    ),
  );
  let out = "";
  child.stdout!.setEncoding("utf8").on("data", (d: string) => (out += d));
  child.stderr!.setEncoding("utf8").on("data", (d: string) => (out += d));
  const until = async (what: string, cond: () => boolean | Promise<boolean>, ms = 180_000) => {
    for (
      const end = Date.now() + ms;
      Date.now() < end;
      await new Promise((r) => setTimeout(r, 500))
    )
      if (await cond()) return;
    throw new Error(`timed out waiting for ${what}:\n${strip(out)}`);
  };
  await until("the registry to start", () => /url\s+http:\/\/\S+/.test(out));
  const own = /url\s+(http:\/\/\S+)/
    .exec(out)![1]!
    .replace(/\/$/, "")
    .replace("//localhost", "//127.0.0.1");
  // (its jobs, which the packed app keeps: said a moment after the url, once the worker is up)
  await until("its jobs", () => /jobs\s+(recurring|no recurring)/.test(out));
  const front = base
    ? (
        await startServerInfo(
          keep,
          codebase(tempDir("registry front"), "front").dir,
          "tok",
          "--proxy",
          `${base}=${own.slice(0, -base.length)}`,
        )
      ).url
    : undefined;
  const url = front ? `${front}${base}` : own;
  const page = async (p: string, cookie?: string) => {
    console.log(`reading ${p}`);
    const got = cookie
      ? await fetch(`${url}${p}`, { headers: { cookie } }).then(async (r) => ({
          status: r.status,
          html: await r.text(),
        }))
      : await uiPage(`${url}${p}`, 30_000);
    assert.equal(got.status, 200, `${p}: ${got.status}\n${strip(out)}`);
    return got.html.replaceAll("<!-- -->", "");
  };
  return { url, own, until, page, out: () => strip(out) };
}

uses

used by