test(web): cover user-visible error diagnostics matrix

This commit is contained in:
lyon
2026-06-20 16:54:59 +08:00
parent eaacdafe33
commit ff60ff853b
2 changed files with 66 additions and 0 deletions
@@ -227,6 +227,12 @@ async function handleRequest(request: IncomingMessage, response: ServerResponse)
if (path === "/v1/hwpod-node-ops") return json(response, 200, hwpodNodeOpsPayload());
if (path === "/v1/web-performance/summary") {
if (state.scenarioId === "performance-summary-error") return errorDiagnosticResponse(response, 502, "/v1/web-performance/summary", "upstream_unavailable", "暂时无法连接上游。");
const matrixStatus = performanceSummaryErrorStatus(state.scenarioId);
if (matrixStatus !== null) return errorDiagnosticResponse(response, matrixStatus, "/v1/web-performance/summary", `e2e_http_${matrixStatus}`, `模拟 ${matrixStatus} 错误。`, { traceId: statusMatrixTraceId(matrixStatus), requestId: `req_e2e_api_status_${matrixStatus}`, category: matrixStatus >= 500 ? "server" : "client" });
if (state.scenarioId === "performance-network-error") {
response.destroy(new Error("e2e performance network reset"));
return;
}
if (state.scenarioId === "performance-empty") return json(response, 200, webPerformanceEmptyPayload(url));
if (state.scenarioId === "performance-stale-window") return json(response, 200, webPerformanceStaleWindowPayload(url));
return json(response, 200, webPerformanceSummaryPayload(url));
@@ -1887,6 +1893,15 @@ function authLoginResponse(response: ServerResponse): void {
return json(response, 401, { ok: false, status: "failed", error: { code: "invalid_credentials", message: "invalid credentials" } });
}
function performanceSummaryErrorStatus(scenarioId: string): number | null {
const match = /^performance-summary-error-(400|401|403|404|409|500)$/u.exec(scenarioId);
return match ? Number(match[1]) : null;
}
function statusMatrixTraceId(status: number): string {
return String(status).repeat(11).slice(0, 32);
}
function redactRequestBody(value: JsonRecord): JsonRecord {
const output: JsonRecord = {};
for (const [key, item] of Object.entries(value)) output[key] = /secret|token|key|password|authorization/iu.test(key) ? "<redacted>" : item;
@@ -3,6 +3,7 @@ import type { Page } from "@playwright/test";
import { expect, fakeServerState, test } from "../fixtures/test";
type RequestLedgerEntry = { path?: string; query?: Record<string, string> };
const performanceErrorStatuses = [400, 401, 403, 404, 409, 500] as const;
async function performanceSummaryRequests(page: Page): Promise<RequestLedgerEntry[]> {
const state = await fakeServerState(page);
@@ -159,3 +160,53 @@ test.describe("performance dashboard summary error", () => {
await expect(page.locator(".performance-contract-item")).toHaveCount(0);
});
});
for (const status of performanceErrorStatuses) {
test.describe(`performance dashboard HTTP ${status} diagnostic`, () => {
test.use({ scenarioId: `performance-summary-error-${status}` });
test(`surfaces HTTP ${status} trace diagnostics without stale dashboard data`, async ({ page }) => {
await page.goto("/performance");
await expect(page.getByRole("heading", { name: "性能监控" })).toBeVisible();
await expect(page.getByText("性能监控加载失败")).toBeVisible();
const diagnostic = page.locator(".api-error-diagnostic");
await expect(diagnostic).toContainText("trace_id");
await expect(diagnostic).toContainText(statusTraceId(status));
await expect(diagnostic).toContainText("requestId");
await expect(diagnostic).toContainText(`req_e2e_api_status_${status}`);
await expect(diagnostic).toContainText(`e2e_http_${status}`);
await expect(diagnostic).toContainText("/v1/web-performance/summary");
await expect(diagnostic).toContainText(String(status));
await expect(diagnostic).toContainText("valuesPrinted");
await expect(diagnostic).toContainText("false");
await expect(page.getByRole("button", { name: "重试" })).toBeVisible();
await expect(page.locator(".performance-contract-item")).toHaveCount(0);
});
});
}
test.describe("performance dashboard browser network diagnostic", () => {
test.use({ scenarioId: "performance-network-error" });
test("surfaces browser generated trace diagnostics when the request has no response", async ({ page }) => {
await page.goto("/performance");
await expect(page.getByRole("heading", { name: "性能监控" })).toBeVisible();
await expect(page.getByText("性能监控加载失败")).toBeVisible();
const diagnostic = page.locator(".api-error-diagnostic");
await expect(diagnostic).toContainText("trace_id");
await expect(diagnostic).toContainText("requestId");
await expect(diagnostic).toContainText("browser_network_error");
await expect(diagnostic).toContainText("web / network");
await expect(diagnostic).toContainText("/v1/web-performance/summary");
await expect(diagnostic).toContainText("browser");
await expect(diagnostic).toContainText("valuesPrinted");
await expect(diagnostic).toContainText("false");
await expect(page.locator(".performance-contract-item")).toHaveCount(0);
});
});
function statusTraceId(status: number): string {
return String(status).repeat(11).slice(0, 32);
}