import { describe, expect, it } from "vitest"; import type { JobResponse, RunEndpointResponse, ServerInstanceResponse } from "../api/types"; import type { OperationRecord } from "./workspace"; import { jobBuckets, projectOperationForTray, summarizeEndpointOperations, summarizeServerOperations } from "./operationsConsole"; function job(id: string, state: JobResponse["state"], updatedAt: string, serverInstanceId = "server-1"): JobResponse { return { id, serverInstanceId, runEndpointId: "endpoint-1", capability: "server.lifecycle.start", idempotencyKey: `idem-${id}`, state, progress: { percent: state === "succeeded" ? 100 : 40 }, retryPolicy: { maxAttempts: 3, initialBackoffSeconds: 1, maxBackoffSeconds: 10 }, attempt: 1, reconcileCount: 0, createdAt: updatedAt, updatedAt }; } function server(id: string, state: ServerInstanceResponse["state"], updatedAt: string): ServerInstanceResponse { return { id, pluginId: "scum-server-plugin", pluginVersion: "1.0.0", runEndpointId: "endpoint-1", name: id, adminUserIds: [], state, configVersion: 1, createdAt: updatedAt, updatedAt }; } describe("operations console contracts", () => { it("classifies active, retrying, failed, and terminal jobs using Platform state", () => { const buckets = jobBuckets([ job("queued", "queued", "2026-07-18T10:00:00Z"), job("retrying", "retrying", "2026-07-18T10:02:00Z"), job("failed", "failed", "2026-07-18T10:03:00Z"), job("succeeded", "succeeded", "2026-07-18T10:01:00Z") ]); expect(buckets.active.map((item) => item.id)).toEqual(["retrying", "queued"]); expect(buckets.retrying.map((item) => item.id)).toEqual(["retrying"]); expect(buckets.failed.map((item) => item.id)).toEqual(["failed"]); expect(buckets.succeeded.map((item) => item.id)).toEqual(["succeeded"]); }); it("orders failed servers and failed jobs before active and healthy servers", () => { const summaries = summarizeServerOperations( [ server("healthy", "running", "2026-07-18T10:04:00Z"), server("active", "ready", "2026-07-18T10:03:00Z"), server("job-failed", "stopped", "2026-07-18T10:02:00Z"), server("server-failed", "failed", "2026-07-18T10:01:00Z") ], new Map(), [job("active-job", "running", "2026-07-18T10:05:00Z", "active"), job("failed-job", "failed", "2026-07-18T10:06:00Z", "job-failed")] ); expect(summaries.map((item) => item.instance.id)).toEqual(["server-failed", "job-failed", "active", "healthy"]); expect(summaries.find((item) => item.instance.id === "job-failed")?.failedJobs).toBe(1); }); it("summarizes only endpoint-safe capacity and status values", () => { const endpoints: RunEndpointResponse[] = [ { id: "endpoint-1", displayName: "Primary", version: "1.0.0", status: "online", capabilities: ["server.lifecycle.start"], capacity: { maxJobs: 4, runningJobs: 2, queuedJobs: 1 }, lastHeartbeatAt: "2026-07-18T10:00:00Z" }, { id: "endpoint-2", displayName: "Secondary", version: "1.0.0", status: "degraded", capabilities: [], capacity: { maxJobs: 2, runningJobs: 1, queuedJobs: 3 }, lastHeartbeatAt: "2026-07-18T10:00:00Z" } ]; expect(summarizeEndpointOperations(endpoints)).toEqual({ total: 2, online: 1, degraded: 1, offline: 0, disabled: 0, activeJobs: 3, queuedJobs: 4 }); }); it("projects an allowlisted operation tray shape and protects unsafe target strings", () => { const operation = { id: "op-1", intent: "更新配置", targetKind: "config", targetId: "/Users/operator/private.cfg", requester: "Operator", status: "failed", errorReason: "平台拒绝请求:Bearer raw-token /Users/operator/private.cfg PID=4201 unix:///var/run/run.sock", diagnosticId: "/private/tmp/diagnostic.json", createdAt: "2026-07-18T10:00:00Z", updatedAt: "2026-07-18T10:01:00Z", rawToken: "run-token-should-not-project" } as OperationRecord & { rawToken: string }; const projected = projectOperationForTray(operation); expect(projected.targetLabel).toBe("配置 / 受保护目标"); expect(JSON.stringify(projected)).not.toContain("/Users/"); expect(JSON.stringify(projected)).not.toContain("run-token"); expect(JSON.stringify(projected)).not.toContain("raw-token"); expect(JSON.stringify(projected)).not.toContain("/var/run/"); expect(JSON.stringify(projected)).not.toContain("4201"); expect(projected.diagnosticId).toBe("受保护诊断"); expect(projected).not.toHaveProperty("requester"); }); });