// Tests for types.ts // Pure unit tests import assert from "node:assert/strict"; import { describe, it } from "node:test"; import type { Request as ExpressRequest } from "express"; import { Session } from "./auth/types"; import { contentTypes } from "./content-types"; import { httpCodes } from "./http-codes"; import { AuthenticationRequired, AuthorizationDenied, type Call, isRedirect, massageMethod, methodParser, type Permission, type RedirectResult, type Result, requireAuth, requirePermission, } from "./types"; import { AuthenticatedUser, anonymousUser } from "./user"; // Helper to create a minimal mock Call function createMockCall(overrides: Partial = {}): Call { const defaultSession = new Session(null, anonymousUser); return { pattern: "/test", path: "/test", method: "GET", parameters: {}, request: {} as ExpressRequest, user: anonymousUser, session: defaultSession, ...overrides, }; } describe("types", () => { describe("methodParser", () => { it("accepts valid HTTP methods", () => { assert.equal(methodParser.parse("GET"), "GET"); assert.equal(methodParser.parse("POST"), "POST"); assert.equal(methodParser.parse("PUT"), "PUT"); assert.equal(methodParser.parse("PATCH"), "PATCH"); assert.equal(methodParser.parse("DELETE"), "DELETE"); }); it("rejects invalid methods", () => { assert.throws(() => methodParser.parse("get")); assert.throws(() => methodParser.parse("OPTIONS")); assert.throws(() => methodParser.parse("HEAD")); assert.throws(() => methodParser.parse("")); }); }); describe("massageMethod", () => { it("converts lowercase to uppercase", () => { assert.equal(massageMethod("get"), "GET"); assert.equal(massageMethod("post"), "POST"); assert.equal(massageMethod("put"), "PUT"); assert.equal(massageMethod("patch"), "PATCH"); assert.equal(massageMethod("delete"), "DELETE"); }); it("handles mixed case", () => { assert.equal(massageMethod("Get"), "GET"); assert.equal(massageMethod("pOsT"), "POST"); }); it("throws for invalid methods", () => { assert.throws(() => massageMethod("options")); assert.throws(() => massageMethod("head")); }); }); describe("isRedirect", () => { it("returns true for redirect results", () => { const result: RedirectResult = { code: httpCodes.redirection.Found, contentType: contentTypes.text.html, result: "", redirect: "/other", }; assert.equal(isRedirect(result), true); }); it("returns false for non-redirect results", () => { const result: Result = { code: httpCodes.success.OK, contentType: contentTypes.text.html, result: "hello", }; assert.equal(isRedirect(result), false); }); }); describe("AuthenticationRequired", () => { it("has correct name and message", () => { const err = new AuthenticationRequired(); assert.equal(err.name, "AuthenticationRequired"); assert.equal(err.message, "Authentication required"); }); it("is an instance of Error", () => { const err = new AuthenticationRequired(); assert.ok(err instanceof Error); }); }); describe("AuthorizationDenied", () => { it("has correct name and message", () => { const err = new AuthorizationDenied(); assert.equal(err.name, "AuthorizationDenied"); assert.equal(err.message, "Authorization denied"); }); it("is an instance of Error", () => { const err = new AuthorizationDenied(); assert.ok(err instanceof Error); }); }); describe("requireAuth", () => { it("returns user for authenticated call", () => { const user = AuthenticatedUser.create("test@example.com"); const session = new Session(null, user); const call = createMockCall({ user, session }); const result = requireAuth(call); assert.equal(result, user); }); it("throws AuthenticationRequired for anonymous user", () => { const call = createMockCall({ user: anonymousUser }); assert.throws(() => requireAuth(call), AuthenticationRequired); }); }); describe("requirePermission", () => { it("returns user when they have the permission", () => { const user = AuthenticatedUser.create("test@example.com", { permissions: ["posts:create" as Permission], }); const session = new Session(null, user); const call = createMockCall({ user, session }); const result = requirePermission( call, "posts:create" as Permission, ); assert.equal(result, user); }); it("throws AuthenticationRequired for anonymous user", () => { const call = createMockCall({ user: anonymousUser }); assert.throws( () => requirePermission(call, "posts:create" as Permission), AuthenticationRequired, ); }); it("throws AuthorizationDenied when missing permission", () => { const user = AuthenticatedUser.create("test@example.com", { permissions: ["posts:read" as Permission], }); const session = new Session(null, user); const call = createMockCall({ user, session }); assert.throws( () => requirePermission(call, "posts:create" as Permission), AuthorizationDenied, ); }); }); });