testRegistryShowsAnnouncedLibraries

tcmts.cli.test.e2e.registry.testRegistryShowsAnnouncedLibraries · #fl6ghlyh · term

tcm registry: the registry tcm ships, run on its own, finds announced releases (from the relay, then Jetstream), checks each against its author's signed record, mirrors its code, and shows it: the activity feed, the libraries, a library's page and tabs, what changed between versions, search, a person's page, and its code in the forge's pages. Yanked by its author, a version leaves the lists, search and its picker (the newest left is latest), and a library whose every version is yanked isn't listed, its page saying why; the yanks stay in the feed.

import { startFakeAtproto } from "#codebase/tcmts/cli/test/e2e/examples";
import { withChildren } from "#codebase/tcmts/cli/test/e2e/harness";
import { registryOn } from "#codebase/tcmts/cli/test/e2e/registry";
import { codebase, onFakeNetwork } from "#codebase/tcmts/cli/test/e2e/releases";
import { startServerInfo } from "#codebase/tcmts/cli/test/e2e/sync";
import assert from "node:assert/strict";

export const testRegistryShowsAnnouncedLibraries = () =>
  withChildren(async (keep) => {
    const alice = {
      did: "did:plc:alice000000000000000000",
      handle: "alice.test",
      password: "a-pass",
    };
    const maya = {
      did: "did:plc:maya0000000000000000000",
      handle: "maya.test",
      password: "m-pass",
    };
    const net = await startFakeAtproto(keep, [alice, maya]);
    const { ok } = onFakeNetwork(keep, net);
    const geo = codebase("registry geo");
    geo.save(
      `// tcm: namespace geo\n/** Plane geometry. */\nexport const manifest = { name: "geo", description: "Shapes and their areas.", license: "MIT" };\n/** A square's area. */\nexport const area = (side: number) => side * side;\n`,
      "add",
    );
    const source = (await startServerInfo(keep, geo.dir, "tok", "--public")).url;
    await ok(
      geo.dir,
      "release",
      "geo@1.0.0",
      "--announce",
      "--as",
      "alice.test",
      "--source",
      source,
    );
    geo.save(
      `// tcm: namespace geo\n/** A cube's volume. */\nexport const cube = (side: number) => side ** 3;\n`,
      "add",
    );
    await ok(geo.dir, "release", "--announce");

    const { url, until, page, out } = await registryOn(keep, net);
    assert.match(out(), /jobs\s+recurring: ingest \(2s\)/);

    // found, checked and mirrored: both releases in the feed, newest first
    // (counted in the HTML: the page's text is in it twice, its markup and the server components' payload)
    // (and the newer's changes since the older, whichever mirrored first)
    await until("both releases to be mirrored", async () => {
      const p = await page("/");
      return p.match(/class="ev"/g)?.length === 2 && /1 added or extended: cube/.test(p);
    });
    const home = await page("/");
    assert.match(home, /tcm registry/);
    assert.match(home, /@alice\.test<\/b><\/a> released/);
    assert.ok(home.indexOf("1.1.0") < home.indexOf("1.0.0"), "newest first");
    assert.match(home, /minor.*1 added or extended: cube/s);
    assert.match(home, /reaches nothing outside itself/);

    const libs = await page("/libraries");
    assert.match(libs, /@alice\.test\/geo<\/a>/);
    assert.match(libs, /Shapes and their areas\./);
    assert.match(libs, /MIT/);

    // a library's page: signed by its author, how to add it, where it runs
    const lib = await page("/@alice.test/geo");
    assert.match(lib, /✓ signed by @alice\.test/);
    assert.match(lib, /1\.1\.0/);
    assert.match(lib, /tcm lib add @alice\.test\/geo@1\.1\.0/);
    assert.match(lib, /3 anywhere/);
    assert.doesNotMatch(lib, /at:\/\//, "no AT URI on a library's page");
    assert.match(await page("/@alice.test/geo?tab=versions"), /1\.0\.0[\s\S]*first release/);
    assert.match(
      await page("/@alice.test/geo?tab=trust"),
      /it matches a record signed in their repository/,
    );
    const code = await page("/@alice.test/geo?tab=code");
    assert.match(code, /area/);
    assert.match(code, /cube/);

    // what changed, and search, and the code itself in the forge's pages (in the registry's frame)
    assert.match(await page("/@alice.test/geo/changes/1.0.0...1.1.0"), /cube[\s\S]*added/);
    assert.match(await page("/search?q=area"), /geo\.area/);
    const mount = /href="\/code\/(plc_alice0+\/geo\/v1_1_0\/area)"/.exec(code)?.[1];
    assert.ok(mount, code);
    const def = await page(`/code/${mount}`);
    assert.match(def, /tcm registry/);
    assert.match(def, /side \* side/);

    // a person's page
    assert.match(await page("/@alice.test"), /did:plc:alice0+/);

    // what people say: a review, and an advisory from its author, in the feed once they check out, and on its pages
    await ok(
      geo.dir,
      "lib",
      "attest",
      "@alice.test/geo@1.1.0",
      "review",
      "-m",
      "Read all of cube.",
      "--as",
      "maya.test",
    );
    await ok(
      geo.dir,
      "lib",
      "attest",
      "@alice.test/geo@1.0.0",
      "advisory",
      "--severity",
      "high",
      "--fixed-in",
      "1.1.0",
      "-m",
      "area is wrong for negatives",
      "--as",
      "alice.test",
    );
    await until(
      "the review and the advisory",
      async () => (await page("/")).match(/class="ev"/g)?.length === 4,
    );
    const feed = await page("/");
    assert.match(feed, /@maya\.test<\/b><\/a> reviewed <a[^>]*>@alice\.test\/geo 1\.1\.0<\/a>/);
    assert.match(
      feed,
      /@alice\.test<\/b><\/a> <span class="bad">flagged<\/span> <a[^>]*>@alice\.test\/geo 1\.0\.0<\/a>/,
    );
    assert.equal((await page("/?show=reviews")).match(/class="ev"/g)?.length, 1);
    assert.match(
      await page("/@alice.test/geo?tab=trust"),
      /@maya\.test<\/a>.*reviewed it[\s\S]*Read all of cube\./,
    );
    assert.match(
      await page("/@alice.test/geo/1.0.0"),
      /Advisory \(high\) from @alice\.test: area is wrong for negatives Fixed in 1\.1\.0\./,
    );
    assert.match(await page("/libraries"), /advisory on 1\.0\.0 · 1 review/);

    // `tcm lib search` asks the registry (kept for next time): each library, what matched, and the line that adds it
    const me = codebase("registry search");
    const byName = await ok(me.dir, "lib", "search", "area", "--registry", url);
    assert.match(byName, /@alice\.test\/geo 1\.1\.0 {2}0 dependents/);
    assert.match(byName, /Shapes and their areas\./);
    assert.match(byName, /geo\.area {2}\(side: number\) => number {2}A square's area\./);
    assert.match(byName, /tcm lib add @alice\.test\/geo@1\.1\.0/);
    assert.match(
      byName,
      new RegExp(`1 definition in 1 library, from ${url.replace(/\./g, "\\.")}`),
    );
    const byType = await ok(me.dir, "lib", "search", ":(side: number) => number");
    assert.match(byType, /geo\.area/);
    assert.match(byType, /geo\.cube/);
    assert.match(
      await ok(me.dir, "lib", "search", "nothingcalledthis"),
      /nothing in http:\S+ libraries matches nothingcalledthis/,
    );

    // its author yanks 1.1.0: 1.0.0 is latest, and 1.1.0 (and cube, only in it) leaves the lists, search and picker
    await ok(
      geo.dir,
      "lib",
      "attest",
      "@alice.test/geo@1.1.0",
      "yank",
      "-m",
      "use 1.0.0",
      "--as",
      "alice.test",
    );
    await until(
      "the yank of 1.1.0",
      async () => (await page("/")).match(/class="ev"/g)?.length === 5,
    );
    assert.match(
      await page("/"),
      /@alice\.test<\/b><\/a> yanked <a[^>]*>@alice\.test\/geo 1\.1\.0<\/a>/,
    );
    assert.match(
      await page("/libraries"),
      /@alice\.test\/geo<\/a> <span class="mono muted">1\.0\.0</,
    );
    const left = await page("/@alice.test/geo");
    assert.match(left, /tcm lib add @alice\.test\/geo@1\.0\.0/);
    assert.match(left, /1\.0\.0 · latest/);
    assert.doesNotMatch(left, /<option[^>]*>1\.1\.0/, "not in the picker");
    const yanked = await page("/@alice.test/geo/1.1.0");
    assert.match(yanked, /Yanked by @alice\.test: use 1\.0\.0/);
    assert.match(yanked, /The latest is <a[^>]*>1\.0\.0<\/a>/);
    assert.match(
      yanked,
      /<option[^>]*>1\.1\.0 · yanked/,
      "the one asked for, in the picker, as yanked",
    );
    assert.match(await page("/@alice.test/geo?tab=versions"), /yanked: use 1\.0\.0/);
    assert.match(await page("/search?q=cube"), /Nothing matches cube/);
    assert.match(await page("/search?q=area"), /geo\.area/);
    assert.match(await ok(me.dir, "lib", "search", "area"), /@alice\.test\/geo 1\.0\.0/);

    // and 1.0.0: geo isn't listed, searched or counted, but its page opens, saying so
    await ok(
      geo.dir,
      "lib",
      "attest",
      "@alice.test/geo@1.0.0",
      "yank",
      "-m",
      "gone for good",
      "--as",
      "alice.test",
    );
    await until(
      "the yank of 1.0.0",
      async () => (await page("/")).match(/class="ev"/g)?.length === 6,
    );
    const none = await page("/libraries");
    assert.match(none, /No libraries yet\./);
    assert.match(none, /0 from 0 servers/);
    assert.match(none, /Libraries<span class="count">0<\/span>/);
    assert.match(await page("/search?q=area"), /Nothing matches area/);
    assert.match(
      await ok(me.dir, "lib", "search", "area"),
      /nothing in http:\S+ libraries matches area/,
    );
    assert.match(await page("/@alice.test?tab=libraries"), /No libraries yet\./);
    const gone = await page("/@alice.test/geo");
    assert.match(gone, /Yanked by @alice\.test: use 1\.0\.0/, "its newest version");
    assert.doesNotMatch(gone, /The latest is/);
    assert.match(await page("/@alice.test/geo/1.0.0"), /Yanked by @alice\.test: gone for good/);
    assert.match(await page("/"), /yanked <a[^>]*>@alice\.test\/geo 1\.0\.0<\/a>/);
  })

uses