import { expect, test } from "@playwright/test"; import { createServer, type ViteDevServer } from "vite"; import { resolve } from "node:path"; import { adminDiagnosticsFixture, adminServicesStorageFixture } from "../fixtures/wp6-05-state.js"; let vite: ViteDevServer; let webUrl: string; const adminSession = { admin: { role: "super_admin", status: "active", user_id: "00000000-0000-4000-8000-000000000605" }, audience: "admin", authenticated: true, expires_at: "2026-09-02T09:30:00.000Z", }; test.beforeAll(async () => { vite = await createServer({ configFile: resolve("apps/web/vite.config.ts"), root: resolve("apps/web"), server: { host: "127.0.0.1", port: 0 } }); await vite.listen(); const address = vite.httpServer?.address(); if (!address || typeof address === "string") throw new Error("Vite did not expose a test port."); webUrl = `http://127.0.0.1:${address.port}`; }); test.afterAll(async () => vite.close()); test("TDD-WP6-DIAG-001-redacted-diagnostics renders state and diagnostics without sensitive fields", async ({ page }) => { await page.route("**/api/v1/admin-auth/session", (route) => route.fulfill({ body: JSON.stringify(adminSession), contentType: "application/json", status: 200 })); await page.route("**/api/v1/admin/services-storage", (route) => route.fulfill({ body: JSON.stringify(adminServicesStorageFixture), contentType: "application/json", status: 200 })); await page.route("**/api/v1/admin/diagnostics", (route) => route.fulfill({ body: JSON.stringify(adminDiagnosticsFixture), contentType: "application/json", status: 200 })); await page.goto(`${webUrl}/admin/services-storage`); await expect(page.getByRole("heading", { level: 2, name: "服务与存储" })).toBeVisible(); await expect(page.getByText("Resend", { exact: true })).toBeVisible(); await expect(page.getByText("额度暂停", { exact: true })).toBeVisible(); await expect(page.getByText("4.25 GB", { exact: true })).toBeVisible(); await expect(page.getByRole("button", { name: "复制诊断" })).toBeEnabled(); await expect(page.locator("body")).not.toContainText(/secret|password|example\.invalid|C:\\Users/i); }); test("TDD-WP6-STATE-001-three-model-states refetches REST truth after a runtime event", async ({ page }) => { await page.addInitScript(() => { class FakeEventSource { static instance: FakeEventSource | undefined; onmessage: ((event: MessageEvent) => void) | null = null; constructor() { FakeEventSource.instance = this; } close() { if (FakeEventSource.instance === this) FakeEventSource.instance = undefined; } } Object.defineProperty(window, "EventSource", { configurable: true, value: FakeEventSource }); (window as unknown as { emitDadaEvent: () => void }).emitDadaEvent = () => FakeEventSource.instance?.onmessage?.({ data: "{}" } as MessageEvent); }); await page.route("**/api/v1/admin-auth/session", (route) => route.fulfill({ body: JSON.stringify({ ...adminSession, csrf_token: "csrf-admin-model-fixture-000000000000000000000000000000000" }), contentType: "application/json", status: 200 })); let runtimeAvailable = false; let modelCalls = 0; const configuration = () => { const ids = ["gemini-3.1-flash-image-preview", "gemini-3-pro-image-preview", "gpt-image-2"]; return { config_set_version: 1, configured_default_model_id: ids[0], recommended_model_id: runtimeAvailable ? ids[1] : null, models: ids.map((modelId, index) => ({ config_version: 1, contract_evidence_ref: null, contract_validation_status: "verified", credit_cost: 1, display_name: ["Gemini 3.1 Flash Image Preview", "Gemini 3 Pro Image Preview", "GPT Image 2"][index], enabled: true, error_mapping_profile: { timeout: "upstream_timeout" }, gateway_account_ref: "mock-gateway", is_default: index === 0, model_id: modelId, prompt_max_length: 1_000, recommendation_priority: index + 1, reference_limits: { max_file_bytes: 1_024, max_files: 2, max_total_bytes: 2_048 }, route_profile: { endpoint: "https://mock.invalid/v1/images" }, runtime_availability: { available_for_new_jobs: runtimeAvailable, checked_at: "2026-08-02T15:00:00.000Z", reason: runtimeAvailable ? "available" : "gateway_balance_insufficient" }, safety_source: "provider", supported_ratios: ["3:4", "1:1", "4:3", "9:16"], })), }; }; await page.route("**/api/v1/models", (route) => { modelCalls += 1; return route.fulfill({ body: JSON.stringify(configuration()), contentType: "application/json", status: 200 }); }); await page.goto(`${webUrl}/admin/models`); await expect(page.getByText("当前推荐").locator("..").getByText("无", { exact: true })).toBeVisible(); expect(modelCalls).toBeGreaterThanOrEqual(1); runtimeAvailable = true; await page.evaluate(() => (window as unknown as { emitDadaEvent: () => void }).emitDadaEvent()); await expect(page.getByText("当前推荐").locator("..").getByText("gemini-3-pro-image-preview", { exact: true })).toBeVisible(); await expect(page.getByText("配置默认").locator("..").getByText("gemini-3.1-flash-image-preview", { exact: true })).toBeVisible(); expect(modelCalls).toBeGreaterThanOrEqual(2); });