yiekheng d236196476 feat(web): getCurrentUser / requireUser / requireAdmin helpers
Reads the session cookie from next/headers, verifies via auth-cookie,
loads the operators row, returns the shape every existing call site
expects (.id, .defaultTimezone, etc) plus the new .role and
.username. getSeededOperator stays as a thin compat shim that
delegates to getCurrentUser, so the ~12 tests that mock
@/lib/operator keep working without churn.
2026-05-10 17:46:16 +08:00

90 lines
2.5 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from "vitest";
const cookiesGetMock = vi.fn();
const findUserMock = vi.fn();
vi.mock("next/headers", () => ({
cookies: async () => ({ get: cookiesGetMock }),
}));
vi.mock("./db", () => ({
db: {
query: {
operators: {
findFirst: (...a: unknown[]) => findUserMock(...a),
},
},
},
}));
const SECRET = "test-secret";
beforeEach(() => {
process.env.AUTH_SECRET = SECRET;
process.env.OPERATOR_TOKEN_VERSION = "1";
cookiesGetMock.mockReset();
findUserMock.mockReset();
});
import { signSession } from "./auth-cookie";
import { getCurrentUser, requireUser, requireAdmin } from "./auth";
const NOW_S = Math.floor(Date.now() / 1000);
const ADMIN = {
id: "11111111-1111-1111-1111-111111111111",
username: "admin",
role: "admin" as const,
displayName: "Admin",
defaultTimezone: "UTC",
passwordHash: null,
};
const USER = { ...ADMIN, id: "22222222-2222-2222-2222-222222222222", username: "alice", role: "user" as const };
async function makeCookie(role: "admin" | "user"): Promise<string> {
return signSession(
{
userId: role === "admin" ? ADMIN.id : USER.id,
role,
iat: NOW_S,
exp: NOW_S + 3600,
v: 1,
},
SECRET,
);
}
describe("auth helpers", () => {
it("getCurrentUser returns null when no cookie is set", async () => {
cookiesGetMock.mockReturnValue(undefined);
const u = await getCurrentUser();
expect(u).toBeNull();
});
it("getCurrentUser returns the user row for a valid admin cookie", async () => {
const cookie = await makeCookie("admin");
cookiesGetMock.mockReturnValue({ value: cookie });
findUserMock.mockResolvedValue(ADMIN);
const u = await getCurrentUser();
expect(u?.id).toBe(ADMIN.id);
expect(u?.role).toBe("admin");
});
it("requireUser throws when there is no session", async () => {
cookiesGetMock.mockReturnValue(undefined);
await expect(requireUser()).rejects.toThrow();
});
it("requireAdmin throws when role is 'user'", async () => {
const cookie = await makeCookie("user");
cookiesGetMock.mockReturnValue({ value: cookie });
findUserMock.mockResolvedValue(USER);
await expect(requireAdmin()).rejects.toThrow();
});
it("requireAdmin returns the user when role is 'admin'", async () => {
const cookie = await makeCookie("admin");
cookiesGetMock.mockReturnValue({ value: cookie });
findUserMock.mockResolvedValue(ADMIN);
const u = await requireAdmin();
expect(u.role).toBe("admin");
});
});