Skip to content
File

Blob: tests/worker/control/api/openapi.workers.test.ts

typescript77 lines
1import { describe, expect, it } from "vitest";
2import YAML from "yaml";
3 
4import { controlRequest, fetchWorker } from "@tests/worker/helpers/http";
5 
6type OpenApiDocument = {
7 paths: Record<string, unknown>;
8 [key: string]: unknown;
9};
10 
11const expectedApiPaths = [
12 "/api/v1/auth/oidc/login",
13 "/api/v1/auth/oidc/callback",
14 "/api/v1/auth/logout",
15 "/api/v1/me",
16 "/api/v1/pats",
17 "/api/v1/pats/{pat_id}",
18 "/api/v1/calendars",
19 "/api/v1/calendars/{calendar_id}",
20 "/api/v1/addressbooks",
21 "/api/v1/addressbooks/{addressbook_id}",
22 "/api/v1/files/metadata",
23 "/api/v1/files/list",
24 "/api/v1/files/mkdir",
25 "/api/v1/files/rename",
26 "/api/v1/files/delete",
27] as const;
28 
29describe("control-plane OpenAPI routes", () => {
30 it("serves the OpenAPI JSON document from the control-plane host", async () => {
31 const response = await fetchWorker(controlRequest("/api/v1/openapi.json"));
32 expect(response.status).toBe(200);
33 
34 const document = (await response.json()) as { paths?: Record<string, unknown> };
35 for (const path of expectedApiPaths) {
36 expect(document.paths).toHaveProperty(path);
37 }
38 });
39 
40 it("serves equivalent OpenAPI YAML", async () => {
41 const jsonResponse = await fetchWorker(controlRequest("/api/v1/openapi.json"));
42 const yamlResponse = await fetchWorker(controlRequest("/api/v1/openapi.yaml"));
43 
44 expect(yamlResponse.status).toBe(200);
45 const jsonDocument = (await jsonResponse.json()) as OpenApiDocument;
46 const yamlDocument = YAML.parse(await yamlResponse.text()) as OpenApiDocument;
47 expect(yamlDocument).toEqual(jsonDocument);
48 });
49 
50 it("serves Swagger UI for the OpenAPI document", async () => {
51 const response = await fetchWorker(controlRequest("/api/v1/docs"));
52 expect(response.status).toBe(200);
53 expect(response.headers.get("content-type")).toContain("text/html");
54 
55 const body = await response.text();
56 expect(body).toContain("SwaggerUIBundle");
57 expect(body).toContain("/api/v1/openapi.json");
58 });
59 
60 it("returns common JSON errors in the documented shape", async () => {
61 const response = await fetchWorker(controlRequest("/api/v1/me"));
62 expect(response.status).toBe(401);
63 await expect(response.json()).resolves.toEqual({
64 error: {
65 code: "unauthorized",
66 message: "API session required.",
67 },
68 });
69 });
70 
71 it("keeps healthz outside the OpenAPI path tree", async () => {
72 const response = await fetchWorker(controlRequest("/healthz"));
73 expect(response.status).toBe(200);
74 await expect(response.json()).resolves.toEqual({ ok: true, service: "dab" });
75 });
76});