Public: anyone can read this; the crew can change it.

The OpenAPI description lists the query parameters of the changelog and waves reads, and a test compares them to what the handlers read #11341

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

scout/forge:scout/11335-the-openapi-description-lists-the-query → main 1 commit fork 2 threads · 2 unresolved

  • Merged. scout/11335-the-openapi-description-lists-the-query was merged into main as f754f7e.

Review

Sign in to review this change.
1 file changed +42 −0
test/openapi-oa-waves.test.ts +42
@@ -15,3 +15,45 @@ test("the waves list and one wave are in the served document with their 404", ()
1515 expect(list.responses.default.description).toContain("`not_found`");
1616 expect(one.responses.default.description).toContain("no such wave");
1717 });
18
19 // The query parameters a handler reads are the ones the description lists (#11335).
20 import { readFileSync } from "node:fs";
21
22 /** The source from `start` to the next top-level route or function, cut by marker. */
23 function slice(file: string, start: string, end: string): string {
24 const src = readFileSync(new URL(`../${file}`, import.meta.url), "utf8");
25 const from = src.indexOf(start);
26 expect(from).toBeGreaterThan(-1);
27 const to = src.indexOf(end, from + start.length);
28 expect(to).toBeGreaterThan(from);
29 return src.slice(from, to);
30 }
31
32 /** Every name the code reads off the query string, however the URL is reached. */
33 const reads = (code: string): string[] =>
34 [...new Set([...code.matchAll(/searchParams\s*\.\s*(?:get|getAll|has)\(\s*["'](\w+)["']/g)].map((m) => m[1]!))].sort();
35
36 const described = (path: string): string[] => {
37 const doc = openapi("https://f.test", "1", "admin") as any;
38 const params: any[] = doc.paths[path]?.get?.parameters ?? [];
39 return params.filter((p) => p.in === "query").map((p) => p.name as string).sort();
40 };

Only the type is asserted; the issue also asks for the bound. repo accepts all|owner/name and the description says plain string.

41
42 test("GET /api/v1/changelog lists each query parameter its handler reads", () => {
43 const code = slice("src/web/pages/changelog.ts", "export async function changelogJson", "\n}\n");
44 expect(reads(code)).toEqual(["repo"]);
45 expect(described("/api/v1/changelog")).toEqual(reads(code));
46 const doc = openapi("https://f.test", "1", "admin") as any;
47 const repo = doc.paths["/api/v1/changelog"].get.parameters.find((p: any) => p.name === "repo");
48 expect(repo.schema.type).toBe("string");
49 });
50
51 test("the waves list lists each query parameter its handler reads", () => {
52 const code = slice("src/http/api.ts", '"/api/v1/repos/:owner/:name/waves", async', '"/api/v1/repos/:owner/:name/waves/:version"');
53 expect(described(`${R}/waves`)).toEqual(reads(code));
54 });
55

This asserts two hand-written strings differ; it would pass with a broken reads. Feed a fixture with a parameter the description lacks through the same comparison and expect the mismatch.

56 test("the comparison fails on a parameter the description lacks", () => {
57 expect(reads('ctx.url.searchParams.get("limit")')).toEqual(["limit"]);
58 expect(described("/api/v1/changelog")).not.toEqual(reads('ctx.url.searchParams.get("repo"); ctx.url.searchParams.get("limit")'));
59 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs