Stabilize runtime auth and E2E coverage
This commit is contained in:
parent
6a3e3b541c
commit
188e899394
133 changed files with 55987 additions and 6775 deletions
160
apps/web/e2e/public-auth-turn.spec.ts
Normal file
160
apps/web/e2e/public-auth-turn.spec.ts
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
import { expect, test } from "@playwright/test";
|
||||
|
||||
const publicApiBase = process.env.E2E_PUBLIC_API_BASE ?? "https://api-vignette.chanpaca.net";
|
||||
|
||||
interface PublicHealthResponse {
|
||||
status: string;
|
||||
environment: string;
|
||||
db: boolean;
|
||||
engine: boolean;
|
||||
}
|
||||
|
||||
async function browserFetchJson<T>(
|
||||
page: import("@playwright/test").Page,
|
||||
path: string,
|
||||
init: RequestInit = {},
|
||||
) {
|
||||
return page.evaluate(
|
||||
async ({ apiBase, apiPath, requestInit }) => {
|
||||
const response = await fetch(`${apiBase}${apiPath}`, {
|
||||
credentials: "include",
|
||||
...requestInit,
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
...(requestInit.headers ?? {}),
|
||||
},
|
||||
});
|
||||
const text = await response.text();
|
||||
let json: unknown = null;
|
||||
try {
|
||||
json = text ? JSON.parse(text) : null;
|
||||
} catch {
|
||||
json = null;
|
||||
}
|
||||
return {
|
||||
ok: response.ok,
|
||||
status: response.status,
|
||||
text,
|
||||
json,
|
||||
};
|
||||
},
|
||||
{
|
||||
apiBase: publicApiBase,
|
||||
apiPath: path,
|
||||
requestInit: {
|
||||
...init,
|
||||
body: typeof init.body === "string" ? init.body : undefined,
|
||||
},
|
||||
},
|
||||
) as Promise<{ ok: boolean; status: number; text: string; json: T | null }>;
|
||||
}
|
||||
|
||||
test.describe("public Google OAuth turn smoke", () => {
|
||||
test("uses production-safe public API configuration @public-auth", async ({
|
||||
request,
|
||||
playwright,
|
||||
}) => {
|
||||
const healthResponse = await request.get(`${publicApiBase}/health`);
|
||||
expect(healthResponse.ok(), await healthResponse.text()).toBeTruthy();
|
||||
const health = (await healthResponse.json()) as PublicHealthResponse;
|
||||
|
||||
expect(health.environment, JSON.stringify(health)).not.toBe("dev");
|
||||
expect(health.db, JSON.stringify(health)).toBe(true);
|
||||
expect(health.engine, JSON.stringify(health)).toBe(true);
|
||||
|
||||
const configResponse = await request.get(`${publicApiBase}/auth/config`, {
|
||||
headers: {
|
||||
origin: "https://vignette.chanpaca.net",
|
||||
"x-forwarded-host": "api-vignette.chanpaca.net",
|
||||
},
|
||||
});
|
||||
expect(configResponse.ok(), await configResponse.text()).toBeTruthy();
|
||||
await expect(await configResponse.json()).toMatchObject({
|
||||
google_oauth_configured: true,
|
||||
allowed_email_domains: expect.arrayContaining(["hs.ac.kr", "twentyoz.kr"]),
|
||||
dev_login_enabled: false,
|
||||
});
|
||||
|
||||
const unauthRequest = await playwright.request.newContext();
|
||||
try {
|
||||
const personasResponse = await unauthRequest.get(`${publicApiBase}/personas`);
|
||||
expect(personasResponse.status(), await personasResponse.text()).toBe(401);
|
||||
} finally {
|
||||
await unauthRequest.dispose();
|
||||
}
|
||||
});
|
||||
|
||||
test("starts a real public session and completes one /turn @public-auth", async ({ page }) => {
|
||||
if (!process.env.E2E_PUBLIC_STORAGE_STATE) {
|
||||
throw new Error(
|
||||
"Set E2E_PUBLIC_STORAGE_STATE to a Playwright storageState file captured after Google login.",
|
||||
);
|
||||
}
|
||||
|
||||
await page.goto("/learn", { waitUntil: "domcontentloaded" });
|
||||
await page.waitForLoadState("networkidle", { timeout: 15_000 }).catch(() => undefined);
|
||||
await expect(page).toHaveURL(/\/learn(?:$|[/?#])/);
|
||||
await expect(page.getByRole("button", { name: "새 회기 시작" })).toBeVisible({
|
||||
timeout: 15_000,
|
||||
});
|
||||
|
||||
const me = await browserFetchJson<{ email?: string; role?: string }>(page, "/auth/me");
|
||||
expect(me.status, me.text).toBe(200);
|
||||
expect(me.json).toMatchObject({ role: "learner" });
|
||||
|
||||
const personas = await browserFetchJson<
|
||||
Array<{ code: string; source: string; degraded: boolean }>
|
||||
>(page, "/personas");
|
||||
expect(personas.status, personas.text).toBe(200);
|
||||
const persona = personas.json?.find(
|
||||
(item) => item.source === "database" && !item.degraded,
|
||||
);
|
||||
expect(persona, personas.text).toBeTruthy();
|
||||
|
||||
let startedSessionId: string | null = null;
|
||||
const started = await browserFetchJson<{
|
||||
session_id: string;
|
||||
degraded: boolean;
|
||||
}>(page, "/sessions", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
persona_code: persona!.code,
|
||||
theory_mode: "humanistic",
|
||||
}),
|
||||
});
|
||||
expect(started.status, started.text).toBe(201);
|
||||
expect(started.json).toMatchObject({ degraded: false });
|
||||
expect(started.json?.session_id).toEqual(expect.any(String));
|
||||
startedSessionId = started.json!.session_id;
|
||||
|
||||
try {
|
||||
const turn = await browserFetchJson<{
|
||||
client_reply?: string;
|
||||
stage?: string;
|
||||
turn_seq?: number;
|
||||
}>(page, `/sessions/${startedSessionId}/turn`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
text: "처음 오신 자리라 긴장될 수 있어요. 지금 가장 이야기하고 싶은 것부터 천천히 말해도 괜찮습니다.",
|
||||
}),
|
||||
});
|
||||
expect(turn.status, turn.text).toBe(200);
|
||||
expect(turn.json?.client_reply).toEqual(expect.any(String));
|
||||
expect(turn.json!.client_reply!.length).toBeGreaterThan(0);
|
||||
|
||||
const detail = await browserFetchJson<{
|
||||
session_id: string;
|
||||
turns: Array<{ speaker: string; text: string }>;
|
||||
}>(page, `/sessions/${startedSessionId}`);
|
||||
expect(detail.status, detail.text).toBe(200);
|
||||
expect(detail.json?.turns.length).toBeGreaterThanOrEqual(2);
|
||||
} finally {
|
||||
if (startedSessionId) {
|
||||
await browserFetchJson(page, `/sessions/${startedSessionId}/end`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({}),
|
||||
}).catch(() => undefined);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue