test(web): 补充 Web MVP 端到端测试与本地测试编排
- 新增 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)均通过
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
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();
|
||||
}
|
||||
Reference in New Issue
Block a user