import { env } from "cloudflare:workers"; import { eq, sql } from "drizzle-orm"; import { beforeEach, describe, expect, it } from "vitest"; import { makeAuth } from "@/worker/auth"; import { makeDb } from "@/worker/db"; import { accounts, sessions, users } from "@/worker/db/schema"; import { ISSUER } from "./helpers"; const fetchRoleByEmail = async (email: string): Promise => { const db = makeDb(env); const row = await db.select({ role: users.role }).from(users).where(eq(users.email, email)).get(); return row?.role ?? null; }; const wipeUsers = async (): Promise => { const db = makeDb(env); // Order matters: accounts/sessions FK -> users. Cascade should handle it, // but be explicit for clarity in this fixture. await db.delete(sessions).where(sql`1 = 1`); await db.delete(accounts).where(sql`1 = 1`); await db.delete(users).where(sql`1 = 1`); }; // The bootstrap-admin promotion hook is gated on userCount === 0 so a // stale BOOTSTRAP_ADMIN_EMAIL is safe to leave configured. Each test // clears the users table first to make assertions on the "first signup" // branch unambiguous. describe("bootstrap admin promotion", () => { beforeEach(wipeUsers); it("promotes the email matching BOOTSTRAP_ADMIN_EMAIL on the very first signup", async () => { const adminEmail = "bootstrap-admin@example.com"; const auth = makeAuth({ ...env, BOOTSTRAP_ADMIN_EMAIL: adminEmail } as Env, { baseURL: ISSUER, issuer: ISSUER }); await auth.api.signUpEmail({ body: { email: adminEmail, password: "correct-horse-battery-staple", name: "Bootstrap" }, asResponse: false, }); expect(await fetchRoleByEmail(adminEmail)).toBe("admin"); }, 15_000); it("does NOT promote a matching email when users already exist (stale env safety)", async () => { const adminEmail = "stale-admin@example.com"; // Seed an unrelated user so userCount > 0 before the BOOTSTRAP signup. const auth = makeAuth({ ...env, BOOTSTRAP_ADMIN_EMAIL: "" } as Env, { baseURL: ISSUER, issuer: ISSUER }); await auth.api.signUpEmail({ body: { email: "first-user@example.com", password: "correct-horse-battery-staple", name: "First" }, asResponse: false, }); expect(await fetchRoleByEmail("first-user@example.com")).toBe("user"); // Now sign up the BOOTSTRAP_ADMIN_EMAIL — must be `user`, not `admin`. const authWithStaleEnv = makeAuth({ ...env, BOOTSTRAP_ADMIN_EMAIL: adminEmail } as Env, { baseURL: ISSUER, issuer: ISSUER, }); await authWithStaleEnv.api.signUpEmail({ body: { email: adminEmail, password: "correct-horse-battery-staple", name: "Stale" }, asResponse: false, }); expect(await fetchRoleByEmail(adminEmail)).toBe("user"); }, 30_000); it("does not promote a non-matching email even when BOOTSTRAP_ADMIN_EMAIL is set", async () => { const adminEmail = "intended-admin@example.com"; const otherEmail = "not-the-admin@example.com"; const auth = makeAuth({ ...env, BOOTSTRAP_ADMIN_EMAIL: adminEmail } as Env, { baseURL: ISSUER, issuer: ISSUER }); await auth.api.signUpEmail({ body: { email: otherEmail, password: "correct-horse-battery-staple", name: "Other" }, asResponse: false, }); expect(await fetchRoleByEmail(otherEmail)).toBe("user"); }, 15_000); it("matches BOOTSTRAP_ADMIN_EMAIL case-insensitively on first signup", async () => { const adminEmail = "Mixed-Case-Admin@Example.com"; const auth = makeAuth({ ...env, BOOTSTRAP_ADMIN_EMAIL: "mixed-case-admin@example.com" } as Env, { baseURL: ISSUER, issuer: ISSUER, }); await auth.api.signUpEmail({ body: { email: adminEmail, password: "correct-horse-battery-staple", name: "Mixed" }, asResponse: false, }); // Better Auth normalises emails to lowercase before insert. expect(await fetchRoleByEmail(adminEmail.toLowerCase())).toBe("admin"); }, 15_000); it("leaves new users as role=user when BOOTSTRAP_ADMIN_EMAIL is empty", async () => { const email = "no-bootstrap@example.com"; const auth = makeAuth({ ...env, BOOTSTRAP_ADMIN_EMAIL: "" } as Env, { baseURL: ISSUER, issuer: ISSUER }); await auth.api.signUpEmail({ body: { email, password: "correct-horse-battery-staple", name: "Plain" }, asResponse: false, }); expect(await fetchRoleByEmail(email)).toBe("user"); }, 15_000); });