From 56eb7bc56e1468f50cf02e63e7e284f82366839f Mon Sep 17 00:00:00 2001 From: alexpaynex <55271826-alexpaynex@users.noreply.replit.com> Date: Thu, 19 Mar 2026 21:02:43 +0000 Subject: [PATCH] task/34: Testkit self-serve plan + report endpoints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Routes added to artifacts/api-server/src/routes/testkit.ts ### GET /api/testkit/plan - Returns TIMMY_TEST_PLAN.md verbatim as text/markdown; charset=utf-8 - Reads file at request time (not on startup) so edits to the plan are picked up without server restart - Path resolves via import.meta.url + dirname() → 4 levels up to project root (handles both dev/tsx and compiled dist/routes/ directories) ### GET /api/testkit/report - Returns only the content from "## Report template" heading to end-of-file - Content-Type: text/plain; charset=utf-8 — ready to copy and fill in - Slice is found with indexOf("## Report template"); 500 if marker absent - Uses the same PLAN_PATH as /api/testkit/plan (single source of truth) ## Deviation: __dirname → import.meta.url Original plan said "resolve relative to project root regardless of cwd". The codebase runs as ESM (tsx / ts-node with ESM), so __dirname is not defined. Fixed by using dirname(fileURLToPath(import.meta.url)) instead — equivalent semantics, correct in both dev and compiled output. ## AGENTS.md — Testing section added Three-step workflow documented between "Branch and PR conventions" and "Stub mode" sections: 1. curl /api/testkit/plan — fetch plan before starting 2. curl -s /api/testkit | bash — run suite after implementing 3. curl /api/testkit/report — fetch report template to fill in ## Unchanged - GET /api/testkit bash script generation: untouched - No new test cases or script modifications ## TypeScript: 0 errors. Smoke tests all pass: - /api/testkit/plan → 200 text/markdown, full TIMMY_TEST_PLAN.md content - /api/testkit/report → 200 text/plain, starts at "## Report template" - /api/testkit → 200 bash script, unchanged --- AGENTS.md | 24 +++++++++++ artifacts/api-server/src/routes/testkit.ts | 49 ++++++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 20fb44d..15bdfa5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -39,6 +39,30 @@ Set `GITEA_TOKEN` or write the token to `.gitea-credentials` (gitignored). Never - Open a PR on Gitea and squash-merge after review - CI runs `pnpm typecheck && pnpm lint` on every PR automatically +## Testing + +Executor agents should follow this three-step workflow when implementing or verifying any change: + +**1. Fetch the test plan before starting** +```bash +curl /api/testkit/plan +``` +Returns `TIMMY_TEST_PLAN.md` — full architecture notes, route descriptions, and expected behaviour for all 24 tests. Read this first so you understand what each endpoint is supposed to do before touching the code. + +**2. Run the full test suite after implementing** +```bash +curl -s /api/testkit | bash +``` +The server returns a self-contained bash script with the base URL already baked in. Requirements: `curl`, `bash`, `jq` — nothing else. All 24 tests must pass (FAIL=0) before submitting. + +**3. Fill in and submit the report** +```bash +curl /api/testkit/report +``` +Returns just the report template section ready to copy and fill in. Attach the completed report to your PR or task output. + +Where `` is the running server URL, e.g. `http://localhost:8080` locally or the Replit dev URL in CI. + ## Stub mode The API server starts without Lightning or AI credentials: diff --git a/artifacts/api-server/src/routes/testkit.ts b/artifacts/api-server/src/routes/testkit.ts index 6c2a6ee..546c81c 100644 --- a/artifacts/api-server/src/routes/testkit.ts +++ b/artifacts/api-server/src/routes/testkit.ts @@ -1,4 +1,7 @@ import { Router, type Request, type Response } from "express"; +import { readFileSync } from "fs"; +import { resolve, dirname } from "path"; +import { fileURLToPath } from "url"; const router = Router(); @@ -757,4 +760,50 @@ if [[ "\$FAIL" -gt 0 ]]; then exit 1; fi res.send(script); }); +/** + * GET /api/testkit/plan + * + * Returns TIMMY_TEST_PLAN.md verbatim as text/markdown. + * Path resolves from the project root regardless of cwd. + */ +const _dirname = dirname(fileURLToPath(import.meta.url)); +const PLAN_PATH = resolve(_dirname, "../../../../TIMMY_TEST_PLAN.md"); + +router.get("/testkit/plan", (_req: Request, res: Response) => { + let content: string; + try { + content = readFileSync(PLAN_PATH, "utf-8"); + } catch { + res.status(500).json({ error: "TIMMY_TEST_PLAN.md not found on server" }); + return; + } + res.setHeader("Content-Type", "text/markdown; charset=utf-8"); + res.send(content); +}); + +/** + * GET /api/testkit/report + * + * Returns only the report template section from TIMMY_TEST_PLAN.md — + * everything from the "## Report template" heading to end-of-file. + * Returned as text/plain so agents can copy and fill in directly. + */ +router.get("/testkit/report", (_req: Request, res: Response) => { + let content: string; + try { + content = readFileSync(PLAN_PATH, "utf-8"); + } catch { + res.status(500).json({ error: "TIMMY_TEST_PLAN.md not found on server" }); + return; + } + const marker = "## Report template"; + const idx = content.indexOf(marker); + if (idx === -1) { + res.status(500).json({ error: "Report template section not found in plan" }); + return; + } + res.setHeader("Content-Type", "text/plain; charset=utf-8"); + res.send(content.slice(idx)); +}); + export default router;