- Vitest multi-project setup (unit / integration / component) - Real-Postgres integration harness via in-process PGlite (TEST_DATABASE_URL override), migrations applied per worker; production code untouched - Unit: routing, locale, validation/Zod schemas, metadata, mail, site-theme, marquee, media, portfolio helpers, plus architecture-rule tests - Integration: Prisma data layer, API routes, and all server actions - Component: UI primitives and form components (jsdom + Testing Library) - 371 tests passing
82 lines
2.6 KiB
TypeScript
82 lines
2.6 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
|
|
vi.mock("next/headers", () => ({
|
|
headers: async () => new Headers({ "x-forwarded-for": "203.0.113.7" }),
|
|
cookies: async () => ({ get: () => undefined, set: () => {}, delete: () => {} }),
|
|
}));
|
|
|
|
import {
|
|
getAdminLockState,
|
|
isAdminAuthConfigured,
|
|
isPasswordValid,
|
|
registerFailedAdminAttempt,
|
|
resetAdminFailedAttempts,
|
|
} from "@/lib/admin-auth";
|
|
|
|
afterEach(() => {
|
|
vi.unstubAllEnvs();
|
|
});
|
|
|
|
describe("admin auth configuration", () => {
|
|
it("is configured only when both password and secret are set", () => {
|
|
vi.stubEnv("ADMIN_PASSWORD", "");
|
|
vi.stubEnv("ADMIN_AUTH_SECRET", "");
|
|
expect(isAdminAuthConfigured()).toBe(false);
|
|
|
|
vi.stubEnv("ADMIN_PASSWORD", "pw");
|
|
vi.stubEnv("ADMIN_AUTH_SECRET", "");
|
|
expect(isAdminAuthConfigured()).toBe(false);
|
|
|
|
vi.stubEnv("ADMIN_PASSWORD", "pw");
|
|
vi.stubEnv("ADMIN_AUTH_SECRET", "secret");
|
|
expect(isAdminAuthConfigured()).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe("isPasswordValid", () => {
|
|
it("returns false when auth is not configured", () => {
|
|
vi.stubEnv("ADMIN_PASSWORD", "");
|
|
vi.stubEnv("ADMIN_AUTH_SECRET", "");
|
|
expect(isPasswordValid("anything")).toBe(false);
|
|
});
|
|
|
|
it("accepts the correct password and rejects wrong ones", () => {
|
|
vi.stubEnv("ADMIN_PASSWORD", "s3cret-password");
|
|
vi.stubEnv("ADMIN_AUTH_SECRET", "hmac-secret");
|
|
expect(isPasswordValid("s3cret-password")).toBe(true);
|
|
expect(isPasswordValid("wrong")).toBe(false);
|
|
expect(isPasswordValid("s3cret-passwordX")).toBe(false); // length mismatch
|
|
});
|
|
});
|
|
|
|
describe("login lockout", () => {
|
|
it("locks the IP after the failed-attempt threshold", async () => {
|
|
expect((await getAdminLockState()).locked).toBe(false);
|
|
|
|
for (let i = 0; i < 4; i += 1) {
|
|
const state = await registerFailedAdminAttempt();
|
|
expect(state.locked).toBe(false);
|
|
}
|
|
expect((await getAdminLockState()).locked).toBe(false);
|
|
|
|
const fifth = await registerFailedAdminAttempt();
|
|
expect(fifth.locked).toBe(true);
|
|
expect(fifth.remainingSeconds).toBeGreaterThan(0);
|
|
|
|
const lockState = await getAdminLockState();
|
|
expect(lockState.locked).toBe(true);
|
|
expect(lockState.remainingSeconds).toBeGreaterThan(0);
|
|
expect(lockState.remainingSeconds).toBeLessThanOrEqual(15 * 60);
|
|
});
|
|
|
|
it("clears the lock on reset", async () => {
|
|
for (let i = 0; i < 5; i += 1) {
|
|
await registerFailedAdminAttempt();
|
|
}
|
|
expect((await getAdminLockState()).locked).toBe(true);
|
|
|
|
await resetAdminFailedAttempts();
|
|
expect((await getAdminLockState()).locked).toBe(false);
|
|
});
|
|
});
|