pike/glyph Public Public: anyone can read this; nobody can change it.

forked from apps/glyph

workbenchthis machine
Files acd87b6b6146da11e0c9272c92a02a8e707adf4a
76 lines · 2.3 KB · ~672 tok Raw History
1
/**
2
 * The API's description is the routes' own, committed: emitting it twice
3
 * says the same bytes, the file in git is what the table says now, and the
4
 * app serves exactly that file.
5
 */
6
import { afterAll, expect, test } from "bun:test";
7
import { mkdtempSync, readFileSync } from "node:fs";
8
import { tmpdir } from "node:os";
9
import { join } from "node:path";
10
import { emit, openapi } from "./openapi.ts";
11
import { routes } from "./routes.ts";
12
import { serve } from "./server.ts";
13
14
const committed = readFileSync(
15
  new URL("../contract/openapi.json", import.meta.url),
16
  "utf8",
17
);
18
19
test("emission is stable", () => {
20
  expect(emit()).toBe(emit());
21
});
22
23
test("contract/openapi.json is what the routes say now (else: bun run contract:emit)", () => {
24
  const now = JSON.parse(emit());
25
  const stale = !Bun.deepEquals(JSON.parse(committed), now);
26
  if (stale)
27
    // Generated app's contract CLI: stderr is its output.
28
    console.error(
29
      "contract/openapi.json is stale: run `bun run contract:emit` and commit it",
30
    );
31
  expect(stale).toBe(false);
32
});
33
34
test("every API route is described, and every door names its cell of the table", () => {
35
  const doc = openapi() as {
36
    paths: Record<
37
      string,
38
      Record<
39
        string,
40
        { "x-contract"?: { resource: string; verb: string; least: string } }
41
      >
42
    >;
43
  };
44
  for (const route of routes.filter((r) => r.path.startsWith("/api/"))) {
45
    const op =
46
      doc.paths[route.path.replace(/:(\w+)/g, "{$1}")]?.[
47
        route.method.toLowerCase()
48
      ];
49
    expect([route.method, route.path, op !== undefined]).toEqual([
50
      route.method,
51
      route.path,
52
      true,
53
    ]);
54
    expect(typeof op?.["x-contract"]?.least).toBe("string");
55
  }
56
  expect(doc.paths["/api/audit"]?.get?.["x-contract"]).toEqual({
57
    resource: "audit",
58
    verb: "list",
59
    least: "admin",
60
  });
61
});
62
63
test("GET /openapi.json serves the committed file", async () => {
64
  const server = serve({
65
    port: 0,
66
    dataDir: mkdtempSync(join(tmpdir(), "app-openapi-")),
67
    identityKey: "k",
68
  });
69
  afterAll(() => server.stop(true));
70
  const response = await fetch(
71
    `http://${server.hostname}:${server.port}/openapi.json`,
72
  );
73
  expect(response.status).toBe(200);
74
  expect(response.headers.get("content-type")).toContain("application/json");
75
  expect(await response.text()).toBe(committed);
76
});
↑↓ navigate ↵ open esc close
↑↓ navigate ↵ open esc close

Keyboard shortcuts