134 lines
4.7 KiB
TypeScript
134 lines
4.7 KiB
TypeScript
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);
|
|
});
|
|
});
|
|
});
|