import { describe, test, expect } from "bun:test"; import app from "../../src/api/server"; describe("API Server — Integration", () => { // ------------------------------------------------------------------- // Teapot route (no auth required) // ------------------------------------------------------------------- describe("GET /v1/teapot", () => { test("returns 418 I'm not a teapot", async () => { const res = await app.request("/v1/teapot"); expect(res.status).toBe(418); const body: any = await res.json(); expect(body.status).toBe(418); expect(body.message).toBe("I'm not a teapot"); expect(body.successful).toBe(true); }); test("returns JSON content type", async () => { const res = await app.request("/v1/teapot"); expect(res.headers.get("content-type")).toContain("application/json"); }); }); // ------------------------------------------------------------------- // Not Found // ------------------------------------------------------------------- describe("Not Found handling", () => { test("returns 404 for unknown routes", async () => { const res = await app.request("/v1/nonexistent"); expect(res.status).toBe(404); const body: any = await res.json(); expect(body.successful).toBe(false); expect(body.error).toBe("NotFound"); }); test("includes method and path in message", async () => { const res = await app.request("/v1/some/random/path", { method: "POST", }); const body: any = await res.json(); expect(body.message).toContain("POST"); expect(body.message).toContain("/v1/some/random/path"); }); test("returns 404 for root path", async () => { const res = await app.request("/"); expect(res.status).toBe(404); }); }); // ------------------------------------------------------------------- // CORS // ------------------------------------------------------------------- describe("CORS", () => { test("includes CORS headers", async () => { const res = await app.request("/v1/teapot", { headers: { Origin: "http://localhost:3000" }, }); // Hono's cors middleware should add access-control headers const acaoHeader = res.headers.get("access-control-allow-origin"); expect(acaoHeader).toBeDefined(); }); test("handles OPTIONS preflight", async () => { const res = await app.request("/v1/teapot", { method: "OPTIONS", headers: { Origin: "http://localhost:3000", "Access-Control-Request-Method": "GET", }, }); // Should not be 404 expect(res.status).toBeLessThan(400); }); }); // ------------------------------------------------------------------- // Auth-protected routes (should reject without auth) // ------------------------------------------------------------------- describe("Protected routes require authorization", () => { const protectedRoutes = [ { method: "GET", path: "/v1/company/companies" }, { method: "GET", path: "/v1/company/count" }, { method: "GET", path: "/v1/credential/credentials/some-id" }, { method: "POST", path: "/v1/credential/credentials" }, { method: "GET", path: "/v1/role" }, { method: "POST", path: "/v1/role" }, { method: "GET", path: "/v1/user/users" }, ]; test.each(protectedRoutes)( "$method $path returns 401 without auth header", async ({ method, path }) => { const res = await app.request(path, { method }); expect(res.status).toBe(401); const body: any = await res.json(); expect(body.successful).toBe(false); }, ); test.each(protectedRoutes)( "$method $path returns error with invalid auth header", async ({ method, path }) => { const res = await app.request(path, { method, headers: { Authorization: "invalid-format" }, }); const body: any = await res.json(); expect(body.successful).toBe(false); }, ); }); // ------------------------------------------------------------------- // Error handling // ------------------------------------------------------------------- describe("Error handling", () => { test("ZodError returns 400 with error details", async () => { // POST to credentials without proper body should trigger a Zod error const res = await app.request("/v1/credential/credentials", { method: "POST", body: JSON.stringify({}), headers: { "Content-Type": "application/json", Authorization: "Bearer invalid.token.here", }, }); // Will get auth error first, which is expected const body: any = await res.json(); expect(body.successful).toBe(false); }); }); });