test(ai-character): 리소스 운영 E2E 보강
This commit is contained in:
75
tests/e2e/error-mapping.spec.ts
Normal file
75
tests/e2e/error-mapping.spec.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
import type { Page } from "@playwright/test";
|
||||
|
||||
import { apiBaseUrl } from "./api-base-url";
|
||||
|
||||
type ApiProbe = {
|
||||
readonly message: string | null;
|
||||
readonly status: number;
|
||||
};
|
||||
|
||||
async function fetchMockApi(page: Page, path: string, init?: RequestInit): Promise<ApiProbe> {
|
||||
return page.evaluate(
|
||||
async ({ apiBaseUrl, init, path }) => {
|
||||
const response = await fetch(`${apiBaseUrl}${path}`, init);
|
||||
const body = (await response.json()) as { readonly message?: string | null };
|
||||
|
||||
return { message: body.message ?? null, status: response.status };
|
||||
},
|
||||
{ apiBaseUrl, init, path },
|
||||
);
|
||||
}
|
||||
|
||||
async function waitForMockWorker(page: Page): Promise<void> {
|
||||
await page.goto("/");
|
||||
await expect
|
||||
.poll(() => page.evaluate(async () => {
|
||||
const registration = await navigator.serviceWorker.ready;
|
||||
|
||||
return registration.active?.scriptURL.endsWith("/mockServiceWorker.js") ?? false;
|
||||
}))
|
||||
.toBe(true);
|
||||
if (!(await page.evaluate(() => navigator.serviceWorker.controller !== null))) {
|
||||
await page.reload();
|
||||
}
|
||||
await expect.poll(() => page.evaluate(() => navigator.serviceWorker.controller !== null)).toBe(true);
|
||||
}
|
||||
|
||||
test("mock API exposes Korean auth and media error messages without backend fallback", async ({ page }) => {
|
||||
// Given
|
||||
await waitForMockWorker(page);
|
||||
|
||||
// When
|
||||
const missingBearer = await fetchMockApi(page, "/api/v2/admin/ai-characters?page=0&size=20");
|
||||
const forbiddenLogout = await fetchMockApi(page, "/member/logout", {
|
||||
headers: { Authorization: "Bearer mock-member-jwt" },
|
||||
method: "POST",
|
||||
});
|
||||
const unsupportedLoginMedia = await fetchMockApi(page, "/admin/member/login", {
|
||||
body: "not-json",
|
||||
headers: { "Content-Type": "text/plain" },
|
||||
method: "POST",
|
||||
});
|
||||
|
||||
// Then
|
||||
expect(missingBearer).toEqual({ message: "인증 정보가 없습니다.", status: 401 });
|
||||
expect(forbiddenLogout).toEqual({ message: "접근 권한이 없습니다.", status: 403 });
|
||||
expect(unsupportedLoginMedia).toEqual({ message: "지원하지 않는 미디어 타입입니다.", status: 415 });
|
||||
});
|
||||
|
||||
test("mock preview does not persist secrets or file bodies in browser storage", async ({ page }) => {
|
||||
// Given
|
||||
await page.goto("/login");
|
||||
await page.getByLabel("이메일").fill("admin@test.com");
|
||||
await page.getByLabel("비밀번호").fill("password");
|
||||
await page.getByRole("button", { name: "로그인" }).click();
|
||||
await expect(page).toHaveURL(/\/ai-characters$/);
|
||||
|
||||
// When
|
||||
const storageDump = await page.evaluate(() => JSON.stringify({ local: { ...localStorage }, session: { ...sessionStorage } }));
|
||||
|
||||
// Then
|
||||
expect(storageDump).not.toContain("password");
|
||||
expect(storageDump).not.toContain("data:audio");
|
||||
expect(storageDump).not.toContain("data:image");
|
||||
});
|
||||
Reference in New Issue
Block a user