feat: add admin state and diagnostics (TASK-WP6-05)
Dada P0-A isolated Windows CI / validate-and-package (push) Successful in 12m4s
Dada P0-A isolated Windows CI / validate-and-package (push) Successful in 12m4s
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
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);
|
||||
});
|
||||
Reference in New Issue
Block a user