testRegistryShowsAnnouncedLibraries
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>/); })