import { test, expect } from "@playwright/test"; /** * 模拟真人:未登录 / 错密 / 鉴权 API(韧性 A-10 / A-2 刷新恢复前置) */ test.describe("resilience auth (human-like)", () => { test("A-10.1 未登录访问 /api/mails → 401,页面进登录", async ({ page, request, }) => { const api = await request.get("/api/mails"); expect(api.status()).toBe(401); const body = await api.json(); expect(body.ok).toBe(false); expect(body.error?.code).toBe("UNAUTHORIZED"); await page.goto("/mails"); await expect(page).toHaveURL(/\/login/, { timeout: 15_000 }); }); test("A-10.1 错误密码不进系统", async ({ page }) => { await page.goto("/login"); const user = page.getByLabel(/用户名/); await user.click(); await user.fill("admin"); const pwd = page.getByLabel(/密码/); await pwd.click(); await pwd.fill("definitely-wrong"); await page.getByRole("button", { name: /登\s*录/ }).click(); await expect(page).toHaveURL(/\/login/, { timeout: 10_000 }); await expect( page.getByText(/Invalid username or password|用户名或密码|无效|失败|错误/i), ).toBeVisible({ timeout: 8_000, }); }); test("A-10.5 登录连点超限可 429(同用户名)", async ({ request }) => { const attempts = []; for (let i = 0; i < 12; i++) { attempts.push( request.post("/api/auth/login", { data: { username: "rate-limit-probe", password: "x" }, }), ); } const results = await Promise.all(attempts); const statuses = results.map((r) => r.status()); expect(statuses.some((s) => s === 429 || s === 401)).toBe(true); }); test("登录成功后 /api/auth/me 有用户;logout 后再 401", async ({ page }) => { await page.goto("/login"); const user = page.getByLabel(/用户名/); await user.click(); await user.fill("admin"); const pwd = page.getByLabel(/密码/); await pwd.click(); await pwd.fill("admin123"); await page.getByRole("button", { name: /登\s*录/ }).click(); await expect(page).toHaveURL(/\/mails/, { timeout: 20_000 }); const me = await page.request.get("/api/auth/me"); expect(me.ok()).toBeTruthy(); const meBody = await me.json(); expect(meBody.ok).toBe(true); expect(meBody.data?.username).toBe("admin"); await page.request.post("/api/auth/logout"); const after = await page.request.get("/api/mails"); expect(after.status()).toBe(401); }); });