import { createHmac } from "node:crypto"; import { PrismaClient } from "@prisma/client"; const DATABASE_URL = process.env.DATABASE_URL ?? "postgresql://drift:drift_test_only@127.0.0.1:55432/drift_bottle_test?schema=public"; const ADMIN_PHONE = "13900000001"; const ADMIN_DEVICE = "e2e-admin-device"; // 与测试/开发环境一致的演示密钥(auth.e2e-spec.ts 同款) const PHONE_HMAC_KEY = "test-phone-hmac-key-with-at-least-32-bytes"; function phoneHmac(phone: string): string { return createHmac("sha256", PHONE_HMAC_KEY).update(phone).digest("hex"); } function normalizePhone(raw: string): string { const compact = raw.replace(/[\s()-]/g, ""); const local = compact.startsWith("+86") ? compact.slice(3) : compact.startsWith("86") && compact.length === 13 ? compact.slice(2) : compact; if (!/^1[3-9]\d{9}$/.test(local)) throw new Error(`invalid admin phone: ${raw}`); return `+86${local}`; } export const E2E_ADMIN = { phone: ADMIN_PHONE, deviceId: ADMIN_DEVICE }; const TABLES = [ "outbox_events", "moderation_tasks", "bottle_pick_leases", "bottle_pick_requests", "bottle_pick_history", "messages", "conversation_members", "conversations", "notifications", "reports", "sanctions", "audit_logs", "refresh_tokens", "sessions", "daily_usage", "bottles", "anonymous_profiles", "accounts", ]; /** * 清空所有业务表(保留 schema)并重建管理员账号。 * 既用于 globalSetup,也被各 spec 的 beforeEach 调用,避免用例间数据污染 *(残留瓶子会让「捞取」捞到别的用例的瓶子,导致会话归属错乱)。 */ export async function resetDatabase(): Promise { const prisma = new PrismaClient({ datasources: { db: { url: DATABASE_URL } }, }); try { for (const table of TABLES) { await prisma.$executeRawUnsafe(`TRUNCATE TABLE "${table}" CASCADE`); } const digest = phoneHmac(normalizePhone(ADMIN_PHONE)); await prisma.account.upsert({ where: { phoneHmac: digest }, update: { role: "ADMIN", status: "ACTIVE" }, create: { role: "ADMIN", status: "ACTIVE", phoneHmac: digest, // 测试库仅用于校验 phoneHmac 查找,明文备用 phoneCiphertext: Buffer.from("admin-e2e-placeholder", "utf8"), anonymousProfile: { create: { nickname: "管理员", avatarColor: "#66CCFF", reviewStatus: "APPROVED", }, }, }, }); } finally { await prisma.$disconnect(); } } export default async function globalSetup(): Promise { await resetDatabase(); }