Files
optima/api/tests/unit/middleware/authorization.test.ts
T

166 lines
5.7 KiB
TypeScript

import { describe, test, expect, mock, beforeEach } from "bun:test";
import { Hono } from "hono";
// We test the authMiddleware in isolation by importing and mounting it on a
// minimal Hono app, without touching the real session/user layer.
// Mock the managers and modules that authMiddleware depends on
mock.module("../../../src/managers/sessions", () => ({
sessions: {
fetch: mock(),
},
}));
mock.module("../../../src/modules/globalEvents", () => ({
events: {
emit: mock(),
on: mock(),
any: mock(),
},
setupEventDebugger: mock(),
}));
import { authMiddleware } from "../../../src/api/middleware/authorization";
import { sessions } from "../../../src/managers/sessions";
import { apiResponse } from "../../../src/modules/api-utils/apiResponse";
function createTestApp(permParams?: Parameters<typeof authMiddleware>[0]) {
const app = new Hono();
app.onError((err, c) => {
const response = apiResponse.error(err);
return c.json(response, response.status);
});
app.use("*", authMiddleware(permParams));
app.get("/test", (c) => c.json({ ok: true }));
return app;
}
describe("authMiddleware", () => {
beforeEach(() => {
// Reset mocks
(sessions.fetch as any).mockReset?.();
});
// -------------------------------------------------------------------
// Missing authorization header
// -------------------------------------------------------------------
test("rejects requests without authorization header", async () => {
const app = createTestApp();
const res = await app.request("/test");
expect(res.status).toBe(401);
const body: any = await res.json();
expect(body.error).toBe("AuthorizationError");
expect(body.message).toContain("authorization");
});
// -------------------------------------------------------------------
// Malformed authorization header
// -------------------------------------------------------------------
test("rejects malformed authorization header", async () => {
const app = createTestApp();
const res = await app.request("/test", {
headers: { Authorization: "foobar" },
});
expect(res.status).toBe(401);
const body: any = await res.json();
expect(body.error).toBe("AuthorizationError");
expect(body.message).toContain("malformed");
});
test("rejects authorization missing token value", async () => {
const app = createTestApp();
const res = await app.request("/test", {
headers: { Authorization: "Bearer " },
});
expect(res.status).toBeGreaterThanOrEqual(400);
const body: any = await res.json();
expect(body.successful).toBe(false);
});
// -------------------------------------------------------------------
// Forbidden auth types
// -------------------------------------------------------------------
test("rejects forbidden auth types", async () => {
const app = createTestApp({ forbiddenAuthTypes: ["Key"] });
const res = await app.request("/test", {
headers: { Authorization: "Key aaa.bbb.ccc" },
});
expect(res.status).toBe(403);
const body: any = await res.json();
expect(body.error).toBe("NonpermittedAuthType");
});
// -------------------------------------------------------------------
// Valid token flow
// -------------------------------------------------------------------
test("calls sessions.fetch with access token", async () => {
const mockUser = {
hasPermission: mock(() => Promise.resolve(true)),
};
const mockSession = {
fetchUser: mock(() => Promise.resolve(mockUser)),
};
(sessions.fetch as any).mockResolvedValue?.(mockSession) ??
((sessions as any).fetch = mock(() => Promise.resolve(mockSession)));
const app = createTestApp();
const res = await app.request("/test", {
headers: { Authorization: "Bearer aaa.bbb.ccc" },
});
// If sessions.fetch resolves, the middleware should pass through
expect(res.status).toBe(200);
});
// -------------------------------------------------------------------
// Permission checking
// -------------------------------------------------------------------
test("rejects when user lacks required permission", async () => {
const mockUser = {
hasPermission: mock(() => Promise.resolve(false)),
};
const mockSession = {
fetchUser: mock(() => Promise.resolve(mockUser)),
};
(sessions as any).fetch = mock(() => Promise.resolve(mockSession));
const app = createTestApp({ permissions: ["admin.super"] });
const res = await app.request("/test", {
headers: { Authorization: "Bearer aaa.bbb.ccc" },
});
expect(res.status).toBe(403);
const body: any = await res.json();
expect(body.message).toContain("permission");
});
test("allows when user has all required permissions", async () => {
const mockUser = {
hasPermission: mock(() => Promise.resolve(true)),
};
const mockSession = {
fetchUser: mock(() => Promise.resolve(mockUser)),
};
(sessions as any).fetch = mock(() => Promise.resolve(mockSession));
const app = createTestApp({
permissions: ["company.fetch", "company.list"],
});
const res = await app.request("/test", {
headers: { Authorization: "Bearer aaa.bbb.ccc" },
});
expect(res.status).toBe(200);
});
test("passes through when no permissions required", async () => {
const mockUser = { hasPermission: mock(() => Promise.resolve(true)) };
const mockSession = { fetchUser: mock(() => Promise.resolve(mockUser)) };
(sessions as any).fetch = mock(() => Promise.resolve(mockSession));
const app = createTestApp();
const res = await app.request("/test", {
headers: { Authorization: "Bearer aaa.bbb.ccc" },
});
expect(res.status).toBe(200);
});
});