Files
pikasTech-HWLAB/internal/cloud/server-dashboard-http.test.ts
2026-07-13 05:08:40 +02:00

199 lines
9.3 KiB
TypeScript

import assert from "node:assert/strict";
import { test } from "bun:test";
import { createCloudApiServer } from "./server.ts";
import { DASHBOARD_SUMMARY_CONTRACT_VERSION } from "./server-dashboard-http.ts";
test("dashboard summary returns one typed bounded read model from real domain contracts", async () => {
const sessionToken = "hws_dashboard_contract_secret";
const requestedNavIds: string[] = [];
const auth = {
ok: true,
actor: { id: "usr_dashboard", username: "operator", displayName: "控制台操作员", role: "admin" },
authMethod: "user-billing-session",
session: { id: "ses_dashboard" },
userBilling: { active: true, token: sessionToken, valuesRedacted: true }
};
const userBillingClient = {
configured: true,
async billingSummary(token: string, options: { limit: number }) {
assert.equal(token, sessionToken);
assert.deepEqual(options, { limit: 5 });
return {
ok: true,
status: 200,
body: {
contractVersion: "user-billing-summary-v1",
status: "ready",
planId: "plan_team",
plan: { id: "plan_team", displayName: "Team", status: "active" },
credits: { balance: 240, reserved: 20, available: 220 },
entitlements: [{ resourceType: "code-agent" }, { resourceType: "hwpod" }],
reservations: { activeCount: 1 }
}
};
}
};
const server = createCloudApiServer({
env: { HWLAB_PROJECT_MANAGEMENT_URL: "http://project-management.test", HWLAB_WORKBENCH_EMPTY_SESSION_GC_ENABLED: "0" },
accessController: dashboardAccessController(auth, userBillingClient, requestedNavIds),
userBillingClient,
hwpodNodeWsRegistry: dashboardHwpodRegistry(),
dashboardDiscoverHwpodSpecs: async () => [dashboardHwpodSpec()],
dashboardNow: () => "2026-07-13T12:00:00.000Z",
fetchImpl: async (input: URL | RequestInfo) => projectManagementResponse(new URL(String(input))),
kafkaEventBridge: disabledKafkaBridge()
});
await listen(server);
try {
const response = await fetch(`http://127.0.0.1:${(server.address() as any).port}/v1/dashboard/summary`);
assert.equal(response.status, 200);
const body = await response.json();
assert.equal(body.contractVersion, DASHBOARD_SUMMARY_CONTRACT_VERSION);
assert.equal(body.route, "/v1/dashboard/summary");
assert.equal(body.status, "ready");
assert.equal(body.actor.id, "usr_dashboard");
assert.deepEqual(body.sources.map((source: any) => [source.id, source.state]), [["work", "ready"], ["hwpod", "ready"], ["billing", "ready"]]);
assert.equal(body.work.projectCount, 2);
assert.deepEqual(body.work.taskCounts, { open: 2, inProgress: 1, blocked: 1 });
assert.deepEqual(body.work.continuations.map((task: any) => task.taskId), ["R2", "R1"]);
assert.equal(body.work.continuations[0].href, "/projects/mdtodo/sources/source-main/files/file-main/tasks/R2");
assert.equal(body.hwpod.nodeCount, 1);
assert.equal(body.hwpod.onlineNodeCount, 1);
assert.equal(body.hwpod.availableDeviceCount, 1);
assert.deepEqual(body.billing.plan, { id: "plan_team", displayName: "Team", status: "active" });
assert.deepEqual(body.billing.credits, { balance: 240, reserved: 20, available: 220 });
assert.equal(body.billing.entitlementCount, 2);
assert.equal(body.billing.activeReservationCount, 1);
assert.equal(body.issues.some((issue: any) => issue.code === "mdtodo_tasks_blocked" && issue.count === 1), true);
assert.equal(JSON.stringify(body).includes(sessionToken), false);
assert.equal(body.valuesRedacted, true);
assert.equal(requestedNavIds.includes("user.dashboard"), true);
} finally {
await close(server);
}
});
test("dashboard summary keeps successful sections when upstreams are partial, stale, or restricted", async () => {
const auth = {
ok: true,
actor: { id: "usr_dashboard_partial", username: "member", role: "user" },
authMethod: "user-billing-session",
session: { id: "ses_dashboard_partial" },
userBilling: { active: true, token: "hws_dashboard_partial", valuesRedacted: true }
};
const userBillingClient = {
configured: true,
async billingSummary() {
return { ok: true, status: 200, body: { status: "stale", observedAt: "2026-07-12T12:00:00.000Z", planId: "default", credits: { available: 8 } } };
}
};
const requestedNavIds: string[] = [];
const accessController = dashboardAccessController(auth, userBillingClient, requestedNavIds, (navId) => navId !== "admin.hwpodGroups");
const server = createCloudApiServer({
env: { HWLAB_PROJECT_MANAGEMENT_URL: "http://project-management.test", HWLAB_WORKBENCH_EMPTY_SESSION_GC_ENABLED: "0" },
accessController,
userBillingClient,
hwpodNodeWsRegistry: dashboardHwpodRegistry(),
dashboardDiscoverHwpodSpecs: async () => { throw new Error("restricted HWPOD discovery must not run"); },
dashboardNow: () => "2026-07-13T12:00:00.000Z",
fetchImpl: async (input: URL | RequestInfo) => {
const url = new URL(String(input));
if (url.searchParams.get("status") === "in_progress") return jsonResponse(503, { ok: false, error: { code: "projection_refreshing" } });
return projectManagementResponse(url);
},
kafkaEventBridge: disabledKafkaBridge()
});
await listen(server);
try {
const response = await fetch(`http://127.0.0.1:${(server.address() as any).port}/v1/dashboard/summary`);
assert.equal(response.status, 200);
const body = await response.json();
assert.equal(body.status, "partial");
assert.equal(body.work.state, "partial");
assert.equal(body.work.projectCount, 2);
assert.equal(body.work.taskCounts.inProgress, null);
assert.equal(body.hwpod.state, "restricted");
assert.equal(body.hwpod.nodeCount, null);
assert.equal(body.billing.state, "stale");
assert.equal(body.billing.credits.available, 8);
assert.equal(body.issues.some((issue: any) => issue.code === "work_summary_partial"), true);
assert.equal(body.issues.some((issue: any) => issue.code === "billing_summary_stale"), true);
assert.equal(body.issues.some((issue: any) => issue.domain === "hwpod"), false);
assert.equal(body.sources.find((source: any) => source.id === "hwpod").reason, "当前身份无 HWPOD topology 访问权限");
assert.equal(requestedNavIds.includes("user.dashboard"), true);
} finally {
await close(server);
}
});
function dashboardAccessController(auth: any, userBilling: any, requestedNavIds: string[], allow: (navId: string) => boolean = () => true) {
return {
required: true,
userBilling,
async requireNavAccess(_request: unknown, _response: unknown, navId: string) {
requestedNavIds.push(navId);
return allow(navId) ? auth : null;
},
async authenticate() { return auth; },
navAccessAllowed(_actor: unknown, navId: string) { return allow(navId); }
};
}
function projectManagementResponse(url: URL) {
if (url.pathname === "/v1/project-management/projects") {
return jsonResponse(200, { ok: true, contractVersion: "project-management-v1", projects: [{ projectId: "project_alpha", name: "Alpha", status: "active" }, { projectId: "project_beta", name: "Beta", status: "ready" }] });
}
if (url.pathname === "/v1/project-management/mdtodo/tasks") {
const status = url.searchParams.get("status");
const task = (taskId: string, taskStatus: string) => ({ taskRef: `mdtodo:source-main:file-main:${taskId}`, taskId, title: `Task ${taskId}`, status: taskStatus, projectId: "project_alpha", sourceId: "source-main", fileRef: "file-main", updatedAt: "2026-07-13T10:00:00.000Z" });
if (status === "in_progress") return jsonResponse(200, { ok: true, tasks: [task("R2", status)], page: { offset: 0, limit: 6, total: 1, hasMore: false } });
if (status === "open") return jsonResponse(200, { ok: true, tasks: [task("R1", status)], page: { offset: 0, limit: 6, total: 2, hasMore: true } });
if (status === "blocked") return jsonResponse(200, { ok: true, tasks: [task("R3", status)], page: { offset: 0, limit: 6, total: 1, hasMore: false } });
}
return jsonResponse(404, { ok: false, error: { code: "not_found" } });
}
function dashboardHwpodSpec() {
return {
ok: true,
hwpodId: "hwpod-demo",
name: "hwpod-demo",
nodeId: "node-demo",
authority: "test-registry",
specPath: "/redacted/hwpod-spec.yaml",
availability: { ok: true, status: "available", checkedAt: "2026-07-13T11:59:00.000Z" },
document: { spec: {} }
};
}
function dashboardHwpodRegistry() {
return {
describe() {
return {
mode: "hwpod-node-outbound-native-ws",
pendingCount: 0,
nodes: [{ nodeId: "node-demo", name: "Demo Node", status: "online", capabilities: ["node.health", "node.version", "node.inventory", "node.diagnostics"], inFlightCount: 0, maxInFlight: 1 }]
};
}
};
}
function disabledKafkaBridge() {
return { ready: Promise.resolve(), async stop() {}, describe() { return { status: "disabled" }; } };
}
function jsonResponse(status: number, body: unknown) {
return new Response(JSON.stringify(body), { status, headers: { "content-type": "application/json" } });
}
async function listen(server: any) {
await new Promise<void>((resolve) => server.listen(0, "127.0.0.1", resolve));
}
async function close(server: any) {
await new Promise<void>((resolve, reject) => server.close((error: Error | null) => error ? reject(error) : resolve()));
}