test: align remaining action tests with Drizzle mocks

EOF

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
SimoandCursor committed 2026-08-01 13:17:35 +02:00
1 parent ed9c23c702
commit 096f55b394
4 files changed
+156 -112

No files matched your search

+49 -30
View File
@@ -2,64 +2,83 @@
import { redirect } from "next/navigation";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { auth } from "@/lib/auth";
import { prisma } from "@/lib/prisma";
import { clientIp, rateLimit } from "@/lib/rate-limit";
import { applyStaff, applyTeam } from "./applications";
const { selectLimit, insertValues } = vi.hoisted(() => {
const selectLimit = vi.fn();
const insertValues = vi.fn().mockResolvedValue([{ insertId: 1 }]);
return { selectLimit, insertValues };
});
vi.mock("@/lib/auth", () => ({ auth: vi.fn() }));
vi.mock("@/lib/prisma", () => ({
prisma: { websiteStaffApplications: { findFirst: vi.fn(), create: vi.fn() } },
vi.mock("@/lib/db", () => ({
db: {
select: vi.fn(() => ({
from: vi.fn(() => ({
where: vi.fn(() => ({
limit: selectLimit,
})),
})),
})),
insert: vi.fn(() => ({ values: insertValues })),
},
WebsiteStaffApplications: {
id: "id",
userId: "userId",
rankId: "rankId",
},
}));
vi.mock("@/lib/rate-limit", () => ({ clientIp: vi.fn(), rateLimit: vi.fn() }));
vi.mock("next/cache", () => ({ revalidatePath: vi.fn() }));
vi.mock("next/navigation", () => ({ redirect: vi.fn() }));
const fakeForm = (data: Record<string, string>) => ({
get: (key: string) => data[key] ?? null,
const fakeForm = (data) => ({
get: (key) => data[key] ?? null,
});
beforeEach(() => {
vi.clearAllMocks();
vi.mocked(auth).mockResolvedValue({ user: { id: "42" } } as never);
vi.mocked(auth).mockResolvedValue({ user: { id: "42" } });
vi.mocked(clientIp).mockResolvedValue("127.0.0.1");
vi.mocked(rateLimit).mockResolvedValue({ ok: true });
selectLimit.mockResolvedValue([]);
insertValues.mockResolvedValue([{ insertId: 1 }]);
});
describe("applyStaff", () => {
it("submits staff application", async () => {
vi.mocked(prisma.websiteStaffApplications.findFirst).mockResolvedValue(
null,
);
vi.mocked(prisma.websiteStaffApplications.create).mockResolvedValue(
{} as never,
);
await applyStaff(
fakeForm({
rankId: "3",
content: "I want to help!",
}) as unknown as FormData,
}),
);
expect(insertValues).toHaveBeenCalledWith(
expect.objectContaining({
userId: 42,
rankId: 3,
content: "I want to help!",
}),
);
expect(prisma.websiteStaffApplications.create).toHaveBeenCalled();
expect(redirect).toHaveBeenCalledWith("/apply/staff?submitted=1");
});
it("redirects to login when not authenticated", async () => {
vi.mocked(auth).mockResolvedValue(null);
await applyStaff(fakeForm({}) as unknown as FormData);
await applyStaff(fakeForm({}));
expect(redirect).toHaveBeenCalledWith("/login");
});
it("returns duplicate status when application exists", async () => {
vi.mocked(prisma.websiteStaffApplications.findFirst).mockResolvedValue({
id: 1,
} as never);
selectLimit.mockResolvedValue([{ id: 1 }]);
await applyStaff(
fakeForm({
rankId: "3",
content: "I want to help!",
}) as unknown as FormData,
}),
);
expect(prisma.websiteStaffApplications.create).not.toHaveBeenCalled();
expect(insertValues).not.toHaveBeenCalled();
expect(redirect).toHaveBeenCalledWith("/apply/staff?error=duplicate");
});
@@ -69,7 +88,7 @@ describe("applyStaff", () => {
fakeForm({
rankId: "3",
content: "I want to help!",
}) as unknown as FormData,
}),
);
expect(redirect).toHaveBeenCalledWith("/apply/staff?error=ratelimit");
});
@@ -77,25 +96,25 @@ describe("applyStaff", () => {
describe("applyTeam", () => {
it("submits team application", async () => {
vi.mocked(prisma.websiteStaffApplications.findFirst).mockResolvedValue(
null,
);
vi.mocked(prisma.websiteStaffApplications.create).mockResolvedValue(
{} as never,
);
await applyTeam(
fakeForm({
teamId: "2",
content: "I want to join team!",
}) as unknown as FormData,
}),
);
expect(insertValues).toHaveBeenCalledWith(
expect.objectContaining({
userId: 42,
rankId: 2,
content: "I want to join team!",
}),
);
expect(prisma.websiteStaffApplications.create).toHaveBeenCalled();
expect(redirect).toHaveBeenCalledWith("/apply/team?submitted=1");
});
it("redirects to login when not authenticated", async () => {
vi.mocked(auth).mockResolvedValue(null);
await applyTeam(fakeForm({}) as unknown as FormData);
await applyTeam(fakeForm({}));
expect(redirect).toHaveBeenCalledWith("/login");
});
});
+57 -27
View File
@@ -1,15 +1,36 @@
// @ts-nocheck
import { beforeEach, describe, expect, it, vi } from "vitest";
import { checkLogin } from "@/lib/auth/password";
import { prisma } from "@/lib/prisma";
import { clientIp, rateLimit } from "@/lib/rate-limit";
import { captchaConfig, verifyCaptcha } from "@/lib/services/captcha";
import { siteSettings } from "@/lib/services/site-settings";
import { precheckLogin } from "./auth-precheck";
const { selectLimit } = vi.hoisted(() => {
const selectLimit = vi.fn().mockResolvedValue([]);
return { selectLimit };
});
vi.mock("@/env", () => ({ env: { CONVERT_PASSWORDS: false } }));
vi.mock("@/lib/auth/password", () => ({ checkLogin: vi.fn() }));
vi.mock("@/lib/prisma", () => ({ prisma: { user: { findUnique: vi.fn() } } }));
vi.mock("@/lib/db", () => ({
db: {
select: vi.fn(() => ({
from: vi.fn(() => ({
where: vi.fn(() => ({
limit: selectLimit,
})),
})),
})),
},
User: {
password: "password",
twoFactorConfirmedAt: "twoFactorConfirmedAt",
mail: "mail",
mailVerified: "mailVerified",
username: "username",
},
}));
vi.mock("@/lib/rate-limit", () => ({ clientIp: vi.fn(), rateLimit: vi.fn() }));
vi.mock("@/lib/services/captcha", () => ({
captchaConfig: vi.fn(),
@@ -25,26 +46,31 @@ beforeEach(() => {
vi.mocked(rateLimit).mockResolvedValue({ ok: true });
vi.mocked(checkLogin).mockResolvedValue({ valid: true } as never);
vi.mocked(captchaConfig).mockResolvedValue({ provider: "none" } as never);
selectLimit.mockResolvedValue([]);
});
describe("precheckLogin", () => {
it("returns ok for valid login without 2FA", async () => {
vi.mocked(prisma.user.findUnique).mockResolvedValue({
password: "hash",
twoFactorConfirmedAt: null,
mail: null,
mailVerified: "0",
} as never);
selectLimit.mockResolvedValue([
{
password: "hash",
twoFactorConfirmedAt: null,
mail: null,
mailVerified: "0",
},
]);
expect(await precheckLogin("user", "pass")).toBe("ok");
});
it("returns twofactor when 2FA is set up", async () => {
vi.mocked(prisma.user.findUnique).mockResolvedValue({
password: "hash",
twoFactorConfirmedAt: new Date(),
mail: null,
mailVerified: "0",
} as never);
selectLimit.mockResolvedValue([
{
password: "hash",
twoFactorConfirmedAt: new Date(),
mail: null,
mailVerified: "0",
},
]);
expect(await precheckLogin("user", "pass")).toBe("twofactor");
});
@@ -57,29 +83,33 @@ describe("precheckLogin", () => {
provider: "hcaptcha",
} as never);
vi.mocked(verifyCaptcha).mockResolvedValue(false);
vi.mocked(prisma.user.findUnique).mockResolvedValue({
password: "hash",
twoFactorConfirmedAt: null,
mail: null,
mailVerified: "0",
} as never);
selectLimit.mockResolvedValue([
{
password: "hash",
twoFactorConfirmedAt: null,
mail: null,
mailVerified: "0",
},
]);
expect(await precheckLogin("user", "pass", "bad-token")).toBe("captcha");
});
it("returns invalid when user not found (dummy hash check)", async () => {
vi.mocked(prisma.user.findUnique).mockResolvedValue(null);
selectLimit.mockResolvedValue([]);
const result = await precheckLogin("nonexistent", "pass");
expect(result).toBe("invalid");
expect(checkLogin).toHaveBeenCalled();
});
it("returns unverified when email verification required", async () => {
vi.mocked(prisma.user.findUnique).mockResolvedValue({
password: "hash",
twoFactorConfirmedAt: null,
mail: "[email protected]",
mailVerified: "0",
} as never);
selectLimit.mockResolvedValue([
{
password: "hash",
twoFactorConfirmedAt: null,
mail: "[email protected]",
mailVerified: "0",
},
]);
vi.mocked(siteSettings.getBool).mockResolvedValue(true);
expect(await precheckLogin("user", "pass")).toBe("unverified");
});
+31 -34
View File
@@ -1,39 +1,38 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
const {
mockFindFirst,
mockUpsert,
mockFindUnique,
mockUpdate,
mockDelete,
mockSendMail,
mockRedirect,
} = vi.hoisted(() => ({
mockFindFirst: vi.fn(),
mockUpsert: vi.fn(),
mockFindUnique: vi.fn(),
mockUpdate: vi.fn(),
mockDelete: vi.fn(),
mockSendMail: vi.fn(),
mockRedirect: vi.fn(),
}));
const { selectLimit, insertOnDup, mockSendMail, mockRedirect } = vi.hoisted(
() => ({
selectLimit: vi.fn(),
insertOnDup: vi.fn().mockResolvedValue({}),
mockSendMail: vi.fn(),
mockRedirect: vi.fn(),
}),
);
vi.mock("next/navigation", () => ({
redirect: (...args: unknown[]) => {
redirect: (...args) => {
mockRedirect(...args);
throw new Error("redirect");
},
}));
vi.mock("@/lib/prisma", () => ({
prisma: {
user: { findFirst: mockFindFirst, update: mockUpdate },
passwordReset: {
upsert: mockUpsert,
findUnique: mockFindUnique,
delete: mockDelete,
},
vi.mock("@/lib/db", () => ({
db: {
select: vi.fn(() => ({
from: vi.fn(() => ({
where: vi.fn(() => ({
limit: selectLimit,
})),
})),
})),
insert: vi.fn(() => ({
values: vi.fn(() => ({
onDuplicateKeyUpdate: insertOnDup,
})),
})),
},
User: { id: "id", mail: "mail" },
PasswordReset: { email: "email", token: "token", createdAt: "createdAt" },
}));
vi.mock("@/lib/services/email", () => ({
@@ -62,22 +61,20 @@ import { requestReset } from "./password-reset";
beforeEach(() => {
vi.clearAllMocks();
insertOnDup.mockResolvedValue({});
});
describe("requestReset", () => {
it("sends a reset email when the user exists", async () => {
mockFindFirst.mockResolvedValue({ id: 1 });
mockUpsert.mockResolvedValue({});
selectLimit.mockResolvedValue([{ id: 1 }]);
const fd = new FormData();
fd.set("email", "[email protected]");
await expect(requestReset(fd)).rejects.toThrow("redirect");
expect(mockFindFirst).toHaveBeenCalledWith(
expect.objectContaining({ where: { mail: "[email protected]" } }),
);
expect(mockUpsert).toHaveBeenCalled();
expect(selectLimit).toHaveBeenCalled();
expect(insertOnDup).toHaveBeenCalled();
expect(mockSendMail).toHaveBeenCalledWith(
"[email protected]",
expect.stringContaining("password reset"),
@@ -86,7 +83,7 @@ describe("requestReset", () => {
});
it("does not send email when user is not found", async () => {
mockFindFirst.mockResolvedValue(null);
selectLimit.mockResolvedValue([]);
const fd = new FormData();
fd.set("email", "[email protected]");
@@ -100,6 +97,6 @@ describe("requestReset", () => {
fd.set("email", "not-an-email");
await expect(requestReset(fd)).rejects.toThrow("redirect");
expect(mockFindFirst).not.toHaveBeenCalled();
expect(selectLimit).not.toHaveBeenCalled();
});
});