diff --git a/web/scripts/standard-site.test.mjs b/web/scripts/standard-site.test.mjs --- a/web/scripts/standard-site.test.mjs +++ b/web/scripts/standard-site.test.mjs @@ -13,24 +13,30 @@ import { test, after } from "node:test"; import assert from "node:assert/strict"; import { execFileSync } from "node:child_process"; -import { existsSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import { + existsSync, + readFileSync, + readdirSync, + rmSync, + writeFileSync, +} from "node:fs"; import { join } from "node:path"; import { fileURLToPath } from "node:url"; const web = fileURLToPath(new URL("../", import.meta.url)); const WELL_KNOWN = join(web, "public/.well-known/site.standard.publication"); +const POSTS = join(web, "src/content/blog"); // Nothing resolvable. A DID that looked real would be followed by the first // person who copied it out of here. const DOCUMENT = "at://did:plc:example/site.standard.document/fakerkey"; -// No post in src/content/blog has a record, so the published case needs one -// written for the build and taken away again. Under web/.astro, which is -// gitignored and already the build's scratch - not os.tmpdir(), because the -// last thing a build does is rename its output into place and a rename across -// filesystems is EXDEV. -const SLUG = "fixture-borborygmus"; -const fixture = join(web, "src/content/blog", `${SLUG}.md`); +// A post that has been published, which the tree may or may not have one of on +// any given day. Written for the build and taken away again. Under web/.astro, +// which is gitignored and already the build's scratch - not os.tmpdir(), +// because the last thing a build does is rename its output into place and a +// rename across filesystems is EXDEV. +const fixture = join(POSTS, "fixture-borborygmus.md"); const outDir = join(web, ".astro/standard-site"); writeFileSync( @@ -45,6 +51,21 @@ Borborygmus quincunx widdershins. `, ); + +// Which posts claim a record, read while the fixture is still on disk. Slugs +// are filenames, which is what Astro's glob loader makes its ids from. Sequoia +// writes the value quoted, so the quotes come off. +const published = new Map( + readdirSync(POSTS) + .filter((file) => file.endsWith(".md")) + .map((file) => [ + file.replace(/\.md$/, ""), + /^atUri:\s*["']?(at:\/\/\S+?)["']?\s*$/m.exec( + readFileSync(join(POSTS, file), "utf8"), + )?.[1], + ]), +); + try { execFileSync( join(web, "node_modules/.bin/astro"), @@ -60,20 +81,28 @@ after(() => rmSync(outDir, { recursive: true, force: true })); -const page = (path) => readFileSync(join(outDir, path, "index.html"), "utf8"); +test("a post links to its record, and only if it has one", () => { + const built = readdirSync(join(outDir, "blog"), { withFileTypes: true }) + .filter((entry) => entry.isDirectory()) + .map((entry) => entry.name); + assert.ok(built.length > 1, "the fixture and at least one post should build"); -test("a post with atUri links to its record", () => { - assert.match( - page(`blog/${SLUG}`), - new RegExp(` { - assert.ok( - !page("blog/placeholder-borborygmus").includes("site.standard.document"), - "an unpublished post rendered a document link", - ); + for (const slug of built) { + const html = readFileSync(join(outDir, "blog", slug, "index.html"), "utf8"); + const atUri = published.get(slug); + if (atUri) { + assert.match( + html, + new RegExp(` { @@ -82,11 +111,13 @@ // edited by hand, and the symptom is a publication nobody can verify. if (!existsSync(WELL_KNOWN)) return; - const body = readFileSync(WELL_KNOWN, "utf8"); + // Trimmed: the end-of-file-fixer hook adds a newline to every file in the + // repo, this one included, so the bytes on disk are never exactly the URI. + const body = readFileSync(WELL_KNOWN, "utf8").trim(); assert.match( body, - /^at:\/\/did:[^\s]+\/site\.standard\.publication\/[^\s]+$/, - "the key must be one AT-URI, with no trailing newline", + /^at:\/\/did:\S+\/site\.standard\.publication\/\S+$/, + "the key must be one AT-URI and nothing else", ); assert.equal( body,