import { describe, expect, it } from "vitest"; import { openApiSpec } from "@/lib/openapi"; // spec ถูก inline เป็น JSON ลงหน้า Swagger UI ตรง ๆ (app/api/swagger/route.ts) — // $ref ที่ชี้หาย (แก้ชื่อ schema ใน lib/openapi.ts แล้วลืมแก้อีกฝั่ง) จะไม่มี error // ตอน build เพราะเป็นแค่ string กลาง object literal ที่ serialize ได้ปกติ — จับได้ที่นี่ก่อน // เห็นเป็นช่องว่าง ๆ บนหน้า /api/swagger function collectRefs(node: unknown, out: string[] = []): string[] { if (Array.isArray(node)) { for (const item of node) collectRefs(item, out); } else if (node !== null && typeof node === "object") { for (const [key, value] of Object.entries(node)) { if (key === "$ref" && typeof value === "string") out.push(value); else collectRefs(value, out); } } return out; } /** แกะ JSON pointer (#/a/b/c) — null = ชี้ไปที่ไม่มีจริง */ function resolveRef(spec: object, ref: string): unknown { if (!ref.startsWith("#/")) return null; let node: unknown = spec; for (const part of ref.slice(2).split("/")) { if (node === null || typeof node !== "object") return null; const key = part.replace(/~1/g, "/").replace(/~0/g, "~"); node = (node as Record)[key]; if (node === undefined) return null; } return node; } describe("openApiSpec", () => { it("ทุก $ref ใน spec ชี้ถึงจุดที่มีจริง", () => { const refs = collectRefs(openApiSpec); expect(refs.length).toBeGreaterThan(0); const broken = refs.filter((ref) => resolveRef(openApiSpec, ref) === null); expect(broken).toEqual([]); }); it("กลุ่ม auth: path สั้น /api/auth/* ครบทั้งคู่ และ path เดิมที่เป็นต้นทางยังอยู่", () => { const paths = openApiSpec.paths as Record; expect(paths["/api/auth/token"]).toBeDefined(); expect(paths["/api/auth/session"]).toBeDefined(); // path เดิม (ที่ /api/auth/* alias มา) ต้องยัง document อยู่ expect(paths["/api/documents/token"]).toBeDefined(); expect(paths["/api/note/auth/token"]).toBeDefined(); }); });