mirror of
https://github.com/better-auth/better-auth.git
synced 2026-07-23 18:33:16 -05:00
243 lines
5.9 KiB
TypeScript
243 lines
5.9 KiB
TypeScript
import { betterAuth } from "better-auth";
|
|
import { admin } from "better-auth/plugins";
|
|
import Database from "better-sqlite3";
|
|
import prompts from "prompts";
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { createAdminAction } from "../src/commands/create-admin";
|
|
import { migrateAction } from "../src/commands/migrate";
|
|
import * as config from "../src/utils/get-config";
|
|
|
|
vi.mock("prompts", () => ({
|
|
default: vi.fn(),
|
|
}));
|
|
|
|
describe("create-admin", () => {
|
|
let db: Database.Database;
|
|
let lastConsoleError = "";
|
|
|
|
beforeEach(() => {
|
|
lastConsoleError = "";
|
|
vi.mocked(prompts).mockReset();
|
|
vi.spyOn(console, "error").mockImplementation((message) => {
|
|
lastConsoleError = String(message);
|
|
});
|
|
vi.spyOn(process, "exit").mockImplementation((code) => {
|
|
if (code && Number(code) !== 0) {
|
|
throw new Error(lastConsoleError || "process.exit(" + code + ")");
|
|
}
|
|
return code as never;
|
|
});
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.restoreAllMocks();
|
|
db?.close();
|
|
});
|
|
|
|
async function setupAuth(withAdmin = true, requireEmailVerification = false) {
|
|
db = new Database(":memory:");
|
|
const auth = betterAuth({
|
|
baseURL: "http://localhost:3000",
|
|
database: db,
|
|
emailAndPassword: {
|
|
enabled: true,
|
|
requireEmailVerification,
|
|
},
|
|
plugins: withAdmin ? [admin()] : [],
|
|
});
|
|
|
|
vi.spyOn(config, "getConfig").mockImplementation(async () => auth.options);
|
|
await migrateAction({
|
|
cwd: process.cwd(),
|
|
config: "test/auth.ts",
|
|
yes: true,
|
|
});
|
|
|
|
return auth;
|
|
}
|
|
|
|
it("creates an initial admin user that can sign in with password", async () => {
|
|
const auth = await setupAuth(true, true);
|
|
|
|
await createAdminAction({
|
|
cwd: process.cwd(),
|
|
config: "test/auth.ts",
|
|
email: "Admin@Example.com",
|
|
password: "secure-password",
|
|
name: "Root Admin",
|
|
});
|
|
|
|
const user = db
|
|
.prepare(
|
|
"SELECT email, name, role, emailVerified FROM user WHERE email = ?",
|
|
)
|
|
.get("admin@example.com") as {
|
|
email: string;
|
|
name: string;
|
|
role: string;
|
|
emailVerified: number | boolean;
|
|
};
|
|
expect(user).toMatchObject({
|
|
email: "admin@example.com",
|
|
name: "Root Admin",
|
|
role: "admin",
|
|
});
|
|
expect(Boolean(user.emailVerified)).toBe(true);
|
|
|
|
const signIn = await auth.api.signInEmail({
|
|
body: {
|
|
email: "admin@example.com",
|
|
password: "secure-password",
|
|
},
|
|
});
|
|
expect(signIn.user.email).toBe("admin@example.com");
|
|
});
|
|
|
|
it("uses Admin as the default name without prompting", async () => {
|
|
await setupAuth();
|
|
|
|
await createAdminAction({
|
|
cwd: process.cwd(),
|
|
config: "test/auth.ts",
|
|
email: "admin@example.com",
|
|
password: "secure-password",
|
|
});
|
|
|
|
const user = db
|
|
.prepare("SELECT name FROM user WHERE email = ?")
|
|
.get("admin@example.com") as { name: string };
|
|
expect(user.name).toBe("Admin");
|
|
expect(prompts).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("passes additional user data to the admin create-user API", async () => {
|
|
await setupAuth();
|
|
|
|
await createAdminAction({
|
|
cwd: process.cwd(),
|
|
config: "test/auth.ts",
|
|
email: "admin@example.com",
|
|
password: "secure-password",
|
|
name: "Root Admin",
|
|
data: JSON.stringify({ image: "https://example.com/avatar.png" }),
|
|
});
|
|
|
|
const user = db
|
|
.prepare("SELECT image FROM user WHERE email = ?")
|
|
.get("admin@example.com") as { image: string };
|
|
expect(user.image).toBe("https://example.com/avatar.png");
|
|
});
|
|
|
|
it("rejects duplicate admin email addresses", async () => {
|
|
await setupAuth();
|
|
|
|
await createAdminAction({
|
|
cwd: process.cwd(),
|
|
config: "test/auth.ts",
|
|
email: "admin@example.com",
|
|
password: "secure-password",
|
|
name: "Root Admin",
|
|
});
|
|
|
|
await expect(
|
|
createAdminAction({
|
|
cwd: process.cwd(),
|
|
config: "test/auth.ts",
|
|
email: "admin@example.com",
|
|
password: "secure-password",
|
|
name: "Root Admin",
|
|
force: true,
|
|
}),
|
|
).rejects.toThrow(/already exists/i);
|
|
});
|
|
|
|
it("requires the admin plugin", async () => {
|
|
await setupAuth(false);
|
|
|
|
await expect(
|
|
createAdminAction({
|
|
cwd: process.cwd(),
|
|
config: "test/auth.ts",
|
|
email: "admin@example.com",
|
|
password: "secure-password",
|
|
name: "Root Admin",
|
|
}),
|
|
).rejects.toThrow(/admin plugin is required/i);
|
|
});
|
|
|
|
it("returns a friendly error when existing-user inspection fails", async () => {
|
|
const adapter = {
|
|
id: "test",
|
|
create: vi.fn(),
|
|
findOne: vi.fn(),
|
|
findMany: vi.fn(),
|
|
count: vi.fn().mockRejectedValue(new Error("missing user table")),
|
|
update: vi.fn(),
|
|
updateMany: vi.fn(),
|
|
delete: vi.fn(),
|
|
deleteMany: vi.fn(),
|
|
transaction: vi.fn(async (callback: (adapter: unknown) => unknown) =>
|
|
callback(adapter),
|
|
),
|
|
} as any;
|
|
const auth = betterAuth({
|
|
baseURL: "http://localhost:3000",
|
|
database: () => adapter,
|
|
emailAndPassword: {
|
|
enabled: true,
|
|
},
|
|
plugins: [admin()],
|
|
});
|
|
vi.spyOn(config, "getConfig").mockImplementation(async () => auth.options);
|
|
|
|
await expect(
|
|
createAdminAction({
|
|
cwd: process.cwd(),
|
|
config: "test/auth.ts",
|
|
email: "admin@example.com",
|
|
password: "secure-password",
|
|
}),
|
|
).rejects.toThrow(/Failed to inspect existing users/);
|
|
await expect(
|
|
createAdminAction({
|
|
cwd: process.cwd(),
|
|
config: "test/auth.ts",
|
|
email: "admin@example.com",
|
|
password: "secure-password",
|
|
}),
|
|
).rejects.toThrow(/missing user table/);
|
|
});
|
|
|
|
it("asks for confirmation when users already exist", async () => {
|
|
const auth = await setupAuth();
|
|
vi.mocked(prompts).mockResolvedValueOnce({ confirmed: false });
|
|
|
|
await auth.api.signUpEmail({
|
|
body: {
|
|
email: "user@example.com",
|
|
password: "password",
|
|
name: "Existing User",
|
|
},
|
|
});
|
|
|
|
await createAdminAction({
|
|
cwd: process.cwd(),
|
|
config: "test/auth.ts",
|
|
email: "admin@example.com",
|
|
password: "secure-password",
|
|
name: "Root Admin",
|
|
});
|
|
|
|
expect(prompts).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
type: "confirm",
|
|
name: "confirmed",
|
|
}),
|
|
);
|
|
const count = db.prepare("SELECT COUNT(*) as count FROM user").get() as {
|
|
count: number;
|
|
};
|
|
expect(count.count).toBe(1);
|
|
});
|
|
});
|