|
| 1 | +import { Hono } from "hono"; |
| 2 | +import { describe, expect, it } from "vitest"; |
| 3 | +import { respondError } from "./error-response"; |
| 4 | +import { |
| 5 | + requireAdminUser, |
| 6 | + requireSessionUser, |
| 7 | + sessionAuth, |
| 8 | + type SessionVars, |
| 9 | +} from "./session-auth"; |
| 10 | + |
| 11 | +/** Stub matching the Fetcher interface's `.fetch()` shape used by env.AUTH. */ |
| 12 | +function stubAuth(handler: (req: Request) => Response | Promise<Response>): Pick<Fetcher, "fetch"> { |
| 13 | + return { |
| 14 | + fetch: (async (input: RequestInfo | URL, init?: RequestInit) => { |
| 15 | + const req = input instanceof Request ? input : new Request(input, init); |
| 16 | + return handler(req); |
| 17 | + }) as Fetcher["fetch"], |
| 18 | + }; |
| 19 | +} |
| 20 | + |
| 21 | +function appWith(_auth: Pick<Fetcher, "fetch">) { |
| 22 | + return new Hono<SessionVars>() |
| 23 | + .use("/*", sessionAuth) |
| 24 | + .get("/whoami", (c) => c.json({ sessionUser: c.get("sessionUser") })) |
| 25 | + .get("/private", requireSessionUser, (c) => c.json({ ok: true })) |
| 26 | + .get("/admin-only", requireAdminUser, (c) => c.json({ ok: true })) |
| 27 | + .onError((err, c) => respondError(c, err)); |
| 28 | +} |
| 29 | + |
| 30 | +function env(auth: Pick<Fetcher, "fetch">) { |
| 31 | + return { AUTH: auth } as unknown as Env; |
| 32 | +} |
| 33 | + |
| 34 | +describe("sessionAuth", () => { |
| 35 | + it("sets sessionUser to null when there is no cookie/session", async () => { |
| 36 | + const auth = stubAuth(() => new Response(JSON.stringify(null), { status: 200 })); |
| 37 | + const res = await appWith(auth).request("/whoami", {}, env(auth)); |
| 38 | + expect(await res.json()).toEqual({ sessionUser: null }); |
| 39 | + }); |
| 40 | + |
| 41 | + it("sets sessionUser to null when the auth worker returns malformed JSON", async () => { |
| 42 | + const auth = stubAuth(() => new Response("not json", { status: 200 })); |
| 43 | + const res = await appWith(auth).request("/whoami", {}, env(auth)); |
| 44 | + expect(res.status).toBe(200); |
| 45 | + expect(await res.json()).toEqual({ sessionUser: null }); |
| 46 | + }); |
| 47 | + |
| 48 | + it("sets sessionUser to null when the auth worker fetch throws", async () => { |
| 49 | + const auth: Pick<Fetcher, "fetch"> = { |
| 50 | + fetch: (() => { |
| 51 | + throw new Error("network down"); |
| 52 | + }) as Fetcher["fetch"], |
| 53 | + }; |
| 54 | + const res = await appWith(auth).request("/whoami", {}, env(auth)); |
| 55 | + expect(await res.json()).toEqual({ sessionUser: null }); |
| 56 | + }); |
| 57 | + |
| 58 | + it("sets sessionUser for a valid non-admin user, and requireAdminUser 403s", async () => { |
| 59 | + const user = { id: "u1", email: "a@b.com", name: "A", role: "user" }; |
| 60 | + const auth = stubAuth( |
| 61 | + () => new Response(JSON.stringify({ session: {}, user }), { status: 200 }), |
| 62 | + ); |
| 63 | + const whoami = await appWith(auth).request("/whoami", {}, env(auth)); |
| 64 | + expect(await whoami.json()).toEqual({ sessionUser: user }); |
| 65 | + |
| 66 | + const priv = await appWith(auth).request("/private", {}, env(auth)); |
| 67 | + expect(priv.status).toBe(200); |
| 68 | + |
| 69 | + const adminOnly = await appWith(auth).request("/admin-only", {}, env(auth)); |
| 70 | + expect(adminOnly.status).toBe(403); |
| 71 | + }); |
| 72 | + |
| 73 | + it("allows requireAdminUser for a session user with role admin", async () => { |
| 74 | + const user = { id: "u2", email: "admin@b.com", name: "Admin", role: "admin" }; |
| 75 | + const auth = stubAuth( |
| 76 | + () => new Response(JSON.stringify({ session: {}, user }), { status: 200 }), |
| 77 | + ); |
| 78 | + const res = await appWith(auth).request("/admin-only", {}, env(auth)); |
| 79 | + expect(res.status).toBe(200); |
| 80 | + }); |
| 81 | + |
| 82 | + it("requireSessionUser 401s when there is no session", async () => { |
| 83 | + const auth = stubAuth(() => new Response(JSON.stringify(null), { status: 200 })); |
| 84 | + const res = await appWith(auth).request("/private", {}, env(auth)); |
| 85 | + expect(res.status).toBe(401); |
| 86 | + }); |
| 87 | +}); |
0 commit comments