afab4e7cba
- 新增 Playwright E2E(治理、登录、投瓶捞瓶回复与实时聊天、举报拉黑与断线恢复)共 9 条用例 - 每个用例前重置数据库、用例后关闭全部 browser context,消除跨用例污染与资源泄漏 - 前端改由 Playwright 托管构建与 preview(关闭 SW);Vite 代理注入 Origin 头以通过接口 Origin 校验 - 根级 vitest 排除 Playwright 用例与需专用配置的 web 用例,避免误收集导致的假失败 - 测试手机号按实测校验结果收敛白名单,避免用例传入会被后端拒绝的号段 验证结果: - Web E2E 9/9 连续两轮通过(含运行前后服务健康检查) - API E2E 146/146 连续两轮通过 - 单测:根级 14 文件/119 用例、web 包 8 文件/31 用例通过 - typecheck、lint、build、依赖审计(audit)均通过
92 lines
2.6 KiB
TypeScript
92 lines
2.6 KiB
TypeScript
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<void> {
|
|
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<void> {
|
|
await resetDatabase();
|
|
}
|