From 79bd15c90e48a1651871fd841f1bc33d73c631a2 Mon Sep 17 00:00:00 2001 From: dktsudgg Date: Wed, 15 Jul 2026 23:10:52 +0900 Subject: [PATCH 1/3] Restore cmd field in MultiHandleExample interface The commit that refined em dash spacing (74d18f0e) accidentally renamed the `cmd` field to `cmdPrefix`, so this commit restores it. Assisted-by: Claude Code:claude-fable-5 --- examples/test-examples/mod.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/test-examples/mod.ts b/examples/test-examples/mod.ts index 7edb03497..b5c7c6844 100644 --- a/examples/test-examples/mod.ts +++ b/examples/test-examples/mod.ts @@ -108,7 +108,7 @@ interface MultiHandleExample { name: string; dir: string; /** Command prefix—the handle is appended as the final argument. */ - cmdPrefix?: string[]; + cmd: string[]; /** Handles to try, in order. Pass if any succeeds. */ handles: string[]; description: string; From ddf7ae499261320aeaac4a18d8200edc184e29eb Mon Sep 17 00:00:00 2001 From: dktsudgg Date: Thu, 16 Jul 2026 01:15:38 +0900 Subject: [PATCH 2/3] Enforce example registration in `test-examples` Background: `test-examples` promises that every directory under examples/ is registered in one of its registries, but it only printed a warning when one was not, so a new example could silently miss automated checks. In fact, examples/solidstart had never been registered. Changes: - Add registry-consistency tests that fail when an example in the examples/ directory is unregistered or a registered example has no matching directory. - Make examples/test-examples a root Deno workspace member so that CI picks up the added tests. - Register the solidstart example, which the new test caught as unregistered. Closes https://github.com/fedify-dev/fedify/issues/886 Assisted-by: Claude Code:claude-fable-5 --- deno.json | 1 + examples/test-examples/deno.json | 6 +++ examples/test-examples/mod.test.ts | 46 ++++++++++++++++++++++ examples/test-examples/mod.ts | 63 +++++++++++++++++++++--------- 4 files changed, 98 insertions(+), 18 deletions(-) create mode 100644 examples/test-examples/deno.json create mode 100644 examples/test-examples/mod.test.ts diff --git a/deno.json b/deno.json index a5b026089..cdef4dd87 100644 --- a/deno.json +++ b/deno.json @@ -39,6 +39,7 @@ "./examples/lint/oxlint", "./examples/monitoring", "./examples/rfc-9421-test", + "./examples/test-examples", "./test/smoke/harness" ], "imports": { diff --git a/examples/test-examples/deno.json b/examples/test-examples/deno.json new file mode 100644 index 000000000..1f84a0d13 --- /dev/null +++ b/examples/test-examples/deno.json @@ -0,0 +1,6 @@ +{ + "tasks": { + "test": "deno test --allow-all .", + "start": "deno run --allow-all mod.ts" + } +} diff --git a/examples/test-examples/mod.test.ts b/examples/test-examples/mod.test.ts new file mode 100644 index 000000000..78b6e947a --- /dev/null +++ b/examples/test-examples/mod.test.ts @@ -0,0 +1,46 @@ +/** + * Registry-consistency tests for the example test runner. + * + * Every example directory under examples/ must be registered in exactly one + * of the registries in mod.ts (SERVER_EXAMPLES, SCRIPT_EXAMPLES, + * MULTI_HANDLE_EXAMPLES, or SKIPPED_EXAMPLES). These tests fail when a new + * example is added without being registered, so it cannot silently miss + * automated checks. + * + * Usage (from repository root): + * deno test --allow-all examples/test-examples + */ +import { fromFileUrl } from "@std/path"; +import assert from "node:assert/strict"; +import test from "node:test"; + +import { getRegisteredExampleNames, scanUnregisteredExamples } from "./mod.ts"; + +const EXAMPLES_DIR = fromFileUrl(new URL("../", import.meta.url)); + +test("Every example in the examples/ directory is registered in test-examples", async () => { + const unregistered = await scanUnregisteredExamples(); + assert.deepEqual( + unregistered, + [], + `Unregistered example directories found: ${unregistered.join(", ")}`, + ); +}); + +test("Every registered example has a matching examples/ directory", async () => { + const directories = new Set(); + for await (const entry of Deno.readDir(EXAMPLES_DIR)) { + if (entry.isDirectory) directories.add(entry.name); + } + const unmatchedExamples = [...getRegisteredExampleNames()] + .filter((name) => !directories.has(name)) + .sort(); + + assert.deepEqual( + unmatchedExamples, + [], + `Registered examples without a matching directory found: ${ + unmatchedExamples.join(", ") + }`, + ); +}); diff --git a/examples/test-examples/mod.ts b/examples/test-examples/mod.ts index b5c7c6844..62359f53f 100644 --- a/examples/test-examples/mod.ts +++ b/examples/test-examples/mod.ts @@ -294,6 +294,19 @@ const SERVER_EXAMPLES: ServerExample[] = [ readyUrl: "http://localhost:3000/", readyTimeout: 30_000, }, + { + // SolidStart sample using @fedify/solidstart; actor path is + // /users/{identifier}. Built with vinxi build; served with vinxi start + // on port 3000. + name: "solidstart", + dir: "solidstart", + buildCmd: ["pnpm", "build"], + startCmd: ["pnpm", "start"], + port: 3000, + actor: "demo", + readyUrl: "http://localhost:3000/", + readyTimeout: 30_000, + }, ]; const SCRIPT_EXAMPLES: ScriptExample[] = [ @@ -350,6 +363,32 @@ const SKIPPED_EXAMPLES: SkippedExample[] = [ }, ]; +export function getRegisteredExampleNames(): Set { + return new Set([ + ...SERVER_EXAMPLES.map((e) => e.name), + ...SCRIPT_EXAMPLES.map((e) => e.name), + ...MULTI_HANDLE_EXAMPLES.map((e) => e.name), + ...SKIPPED_EXAMPLES.map((e) => e.name), + ]); +} + +/** + * Scans the examples/ directory and returns the names of sub-directories + * that are not registered in any of the registries above. + */ +export async function scanUnregisteredExamples(): Promise { + const registeredNames = getRegisteredExampleNames(); + const unregistered: string[] = []; + for await (const entry of Deno.readDir(EXAMPLES_DIR)) { + if (!entry.isDirectory) continue; + if (entry.name === "test-examples") continue; + if (!registeredNames.has(entry.name)) { + unregistered.push(entry.name); + } + } + return unregistered; +} + // ─── ANSI Colors ────────────────────────────────────────────────────────────── const c = { @@ -805,24 +844,8 @@ async function main(): Promise { } // ── Unregistered examples ──────────────────────────────────────────────── - // Collect every example name that appears in any registry. - const registeredNames = new Set([ - ...SERVER_EXAMPLES.map((e) => e.name), - ...SCRIPT_EXAMPLES.map((e) => e.name), - ...MULTI_HANDLE_EXAMPLES.map((e) => e.name), - ...SKIPPED_EXAMPLES.map((e) => e.name), - ]); - // Scan the examples/ directory for sub-directories that are not registered. - const unregistered: string[] = []; - for await (const entry of Deno.readDir(EXAMPLES_DIR)) { - if (!entry.isDirectory) continue; - // The test-examples directory is the test runner itself—skip it. - if (entry.name === "test-examples") continue; - if (!registeredNames.has(entry.name)) { - unregistered.push(entry.name); - } - } + const unregistered = await scanUnregisteredExamples(); // ── Summary ────────────────────────────────────────────────────────────── const passed = results.filter((r) => r.status === "pass"); @@ -871,4 +894,8 @@ async function main(): Promise { Deno.exit(0); } -await main(); +// Run the test runner only when this module is executed directly, so that +// mod.test.ts can import the registry helpers above without side effects. +if (import.meta.main) { + await main(); +} From 2d186c2e477c13f23f809bef9c8228407a69239b Mon Sep 17 00:00:00 2001 From: dktsudgg Date: Fri, 17 Jul 2026 11:42:21 +0900 Subject: [PATCH 3/3] Refactor `getRegisteredExampleNames` function in `test-examples` Combine the registered example arrays before mapping their names to avoid repeated map operations. --- examples/test-examples/mod.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/test-examples/mod.ts b/examples/test-examples/mod.ts index 62359f53f..f4a420135 100644 --- a/examples/test-examples/mod.ts +++ b/examples/test-examples/mod.ts @@ -365,11 +365,11 @@ const SKIPPED_EXAMPLES: SkippedExample[] = [ export function getRegisteredExampleNames(): Set { return new Set([ - ...SERVER_EXAMPLES.map((e) => e.name), - ...SCRIPT_EXAMPLES.map((e) => e.name), - ...MULTI_HANDLE_EXAMPLES.map((e) => e.name), - ...SKIPPED_EXAMPLES.map((e) => e.name), - ]); + ...SERVER_EXAMPLES, + ...SCRIPT_EXAMPLES, + ...MULTI_HANDLE_EXAMPLES, + ...SKIPPED_EXAMPLES, + ].map((e) => e.name)); } /**