import { describe, expect, it } from "vitest"; import { formatHeaderDataTime, getMsUntilNextHeaderDataTimeTick, resolveWorkbenchOperationalDate, resolveWorkbenchRuntimeData } from "./use-workbench-runtime-data"; import type { ScheduledConditionRecord, ScheduledWorkOrderItem } from "../types"; const SEPTEMBER_17 = new Date("2026-09-17T02:30:00Z"); const SEPTEMBER_18 = new Date("2026-09-18T02:30:00Z"); const mockCondition: ScheduledConditionRecord = { id: "mock-condition", kind: "condition", taskId: "scada-diagnosis", sessionId: "scheduled-condition-mock", title: "SCADA 数据诊断", summary: "mock", status: "error", riskLevel: "critical", scheduledAt: "2026-09-17T10:00:00+08:00", updatedAt: Date.parse("2026-09-17T10:00:00+08:00") }; const backendCondition: ScheduledConditionRecord = { ...mockCondition, id: "schedule-run-1", taskId: "backend-schedule", sessionId: "scheduled-condition-schedule-run-1", title: "演示 合成低压调查", modelName: "TJWater scheduler" }; const mockWorkOrder: ScheduledWorkOrderItem = { id: "mock-work-order", kind: "work_order", code: "WO-MOCK", scheduledAt: "2026-09-17T11:00:00+08:00", title: "演示工单", summary: "mock", status: "pending", riskLevel: "normal", updatedAt: Date.parse("2026-09-17T10:00:00+08:00"), detail: "mock", durationMinutes: 30, source: "演示", dispatcher: "调度中心", priority: "normal", location: "V-1", assignee: "现场班组", feedbackRequirements: ["回传设备状态"], activity: [] }; describe("workbench runtime data", () => { it("snaps the header time to the current five-minute interval", () => { expect(formatHeaderDataTime(new Date(2026, 6, 22, 15, 43, 31))).toBe("2026-07-22 15:40"); }); it("preserves the backend observation minute", () => { expect(formatHeaderDataTime(new Date(2026, 6, 22, 15, 43, 31), false)).toBe("2026-07-22 15:43"); }); it("schedules the next header refresh on the next interval boundary", () => { expect(getMsUntilNextHeaderDataTimeTick(new Date(2026, 6, 22, 15, 43, 30))).toBe(90_000); }); it("keeps mock conditions without inventing alerts when backend storage is not configured", () => { const resolved = resolveWorkbenchRuntimeData([mockCondition], { availability: "not_configured", source: "none", scenarioAvailability: "not_configured", artifactAvailability: "not_configured", scheduleAvailability: "not_configured", workOrderAvailability: "not_configured", alertAvailability: "not_configured", generatedAt: "2026-09-17T02:30:00Z", conditions: [], alerts: [], scenarios: [], latestAnalysisArtifact: null, simulationOverlay: null, workOrders: [] }, { operationalDate: SEPTEMBER_17 }); expect(resolved.runtimeDataSource).toBe("mock"); expect(resolved.scheduledConditions).toEqual([mockCondition]); expect(resolved.workbenchAlerts).toEqual([]); expect(resolved.alertDataSource).toBe("none"); expect(resolved.scenarioDataSource).toBe("mock"); expect(resolved.artifactDataSource).toBe("mock"); expect(resolved.workOrderDataSource).toBe("none"); }); it("does not use frontend demo work orders as a backend fallback", () => { const demoWorkOrder = { ...mockWorkOrder, dataSource: "mock" as const }; const resolved = resolveWorkbenchRuntimeData([mockCondition, demoWorkOrder], null, { operationalDate: SEPTEMBER_17 }); expect(resolved.scheduledConditions).toEqual([mockCondition]); expect(resolved.workOrderDataSource).toBe("none"); }); it("keeps mock mode separate from the selected backend history day", () => { const backendAlert = { id: "scheduled-condition-alert-schedule-run-1", title: "演示 测试待复核", description: "运行失败", time: "10:25" }; const backendSnapshot = { availability: "ready" as const, source: "scheduled_tasks" as const, scenarioAvailability: "not_configured" as const, artifactAvailability: "not_configured" as const, scheduleAvailability: "not_configured" as const, workOrderAvailability: "not_configured" as const, alertAvailability: "not_configured" as const, generatedAt: "2026-09-17T02:30:00Z", conditions: [backendCondition], alerts: [backendAlert], scenarios: [], latestAnalysisArtifact: null, simulationOverlay: null, workOrders: [] }; const resolved = resolveWorkbenchRuntimeData([mockCondition], backendSnapshot, { operationalDate: SEPTEMBER_17, runtimeMode: "history" }); expect(resolved.runtimeDataSource).toBe("mock"); expect(resolved.scheduledConditions).toEqual([mockCondition]); expect(resolved.workbenchAlerts).toEqual([]); expect(resolved.alertDataSource).toBe("none"); expect(resolved.scenarioDataSource).toBe("mock"); expect(resolved.workOrderDataSource).toBe("none"); const mockResolved = resolveWorkbenchRuntimeData([mockCondition], backendSnapshot, { operationalDate: SEPTEMBER_17, runtimeMode: "demo" }); expect(mockResolved.runtimeDataSource).toBe("mock"); expect(mockResolved.scheduledConditions).toEqual([mockCondition]); expect(mockResolved.workOrderDataSource).toBe("none"); }); it("anchors history mode to the latest day that contains backend work orders", () => { const olderWorkOrder = { ...mockWorkOrder, id: "older-backend-work-order", scheduledAt: "2026-08-03T10:05:00+08:00" }; const latestWorkOrder = { ...mockWorkOrder, id: "latest-backend-work-order", scheduledAt: "2026-08-17T17:40:00+08:00" }; const futureWorkOrder = { ...mockWorkOrder, id: "future-backend-work-order", scheduledAt: "2026-09-20T10:00:00+08:00" }; const now = new Date("2026-09-18T02:30:00Z"); const operationalDate = resolveWorkbenchOperationalDate({ availability: "ready", source: "scheduled_tasks", scenarioAvailability: "ready", artifactAvailability: "ready", scheduleAvailability: "ready", workOrderAvailability: "ready", alertAvailability: "ready", generatedAt: "2026-09-18T02:30:00Z", conditions: [backendCondition], alerts: [], scenarios: [], latestAnalysisArtifact: null, simulationOverlay: null, workOrders: [olderWorkOrder, latestWorkOrder, futureWorkOrder] }, { runtimeMode: "history", now }); expect(operationalDate.toISOString()).toBe("2026-08-17T09:40:00.000Z"); expect(resolveWorkbenchOperationalDate({ availability: "ready", source: "scheduled_tasks", scenarioAvailability: "ready", artifactAvailability: "ready", scheduleAvailability: "ready", workOrderAvailability: "ready", alertAvailability: "ready", generatedAt: "2026-09-18T02:30:00Z", conditions: [], alerts: [], scenarios: [], latestAnalysisArtifact: null, simulationOverlay: null, workOrders: [olderWorkOrder, latestWorkOrder, futureWorkOrder] }, { runtimeMode: "demo", now }).toISOString()).toBe("2026-08-17T09:40:00.000Z"); }); it("keeps current-day mock conditions when backend schedules are configured", () => { const currentMockCondition = { ...mockCondition, id: "mock-current-day", scheduledAt: "2026-09-18T10:00:00+08:00" }; const resolved = resolveWorkbenchRuntimeData([currentMockCondition], { availability: "ready", source: "scheduled_tasks", scenarioAvailability: "ready", artifactAvailability: "ready", scheduleAvailability: "ready", workOrderAvailability: "not_configured", alertAvailability: "not_configured", generatedAt: "2026-09-18T02:30:00Z", conditions: [backendCondition], alerts: [], scenarios: [], latestAnalysisArtifact: null, simulationOverlay: null, workOrders: [] }, { operationalDate: SEPTEMBER_18 }); expect(resolved.scheduledConditions).toEqual([currentMockCondition]); expect(resolved.runtimeDataSource).toBe("mock"); }); it("ignores current-day backend conditions and keeps demo conditions", () => { const currentMockCondition = { ...mockCondition, id: "mock-current-day", scheduledAt: "2026-09-18T10:00:00+08:00" }; const currentBackendCondition = { ...backendCondition, id: "backend-current-day", scheduledAt: "2026-09-18T10:05:00+08:00" }; const resolved = resolveWorkbenchRuntimeData([currentMockCondition], { availability: "ready", source: "scheduled_tasks", scenarioAvailability: "not_configured", artifactAvailability: "not_configured", scheduleAvailability: "not_configured", workOrderAvailability: "not_configured", alertAvailability: "not_configured", generatedAt: "2026-09-18T02:30:00Z", conditions: [currentBackendCondition], alerts: [], scenarios: [], latestAnalysisArtifact: null, simulationOverlay: null, workOrders: [] }, { operationalDate: SEPTEMBER_18 }); expect(resolved.scheduledConditions).toEqual([currentMockCondition]); expect(resolved.runtimeDataSource).toBe("mock"); }); it("treats an empty configured backend work-order partition as authoritative", () => { const resolved = resolveWorkbenchRuntimeData([mockCondition, mockWorkOrder], { availability: "ready", source: "none", scenarioAvailability: "not_configured", artifactAvailability: "not_configured", scheduleAvailability: "not_configured", workOrderAvailability: "ready", alertAvailability: "not_configured", generatedAt: "2026-09-17T02:30:00Z", conditions: [], alerts: [], scenarios: [], latestAnalysisArtifact: null, simulationOverlay: null, workOrders: [] }, { operationalDate: SEPTEMBER_17, runtimeMode: "backend" }); expect(resolved.scheduledConditions).toEqual([mockCondition]); expect(resolved.runtimeDataSource).toBe("mock"); expect(resolved.workOrderDataSource).toBe("backend"); }); it("uses a provisioned alert list while other sections keep their mock fallback", () => { const backendAlert = { id: "backend-alert-alarm-1", title: "泵站通信告警", description: "来自正式告警同步适配器。", severity: "warning" as const, time: "10:29" }; const resolved = resolveWorkbenchRuntimeData([mockCondition], { availability: "ready", source: "none", scenarioAvailability: "not_configured", artifactAvailability: "not_configured", scheduleAvailability: "not_configured", workOrderAvailability: "not_configured", alertAvailability: "ready", generatedAt: "2026-09-17T02:30:00Z", conditions: [], alerts: [backendAlert], scenarios: [], latestAnalysisArtifact: null, simulationOverlay: null, workOrders: [] }, { operationalDate: SEPTEMBER_17 }); expect(resolved.scheduledConditions).toEqual([mockCondition]); expect(resolved.runtimeDataSource).toBe("mock"); expect(resolved.workbenchAlerts).toEqual([backendAlert]); }); it("does not invent demo alerts when the backend alert partition is empty", () => { const resolved = resolveWorkbenchRuntimeData([mockCondition], { availability: "ready", source: "none", scenarioAvailability: "not_configured", artifactAvailability: "not_configured", scheduleAvailability: "ready", workOrderAvailability: "not_configured", alertAvailability: "ready", generatedAt: "2026-09-17T02:30:00Z", conditions: [], alerts: [], scenarios: [], latestAnalysisArtifact: null, simulationOverlay: null, workOrders: [] }, { operationalDate: SEPTEMBER_17 }); expect(resolved.alertDataSource).toBe("backend"); expect(resolved.workbenchAlerts).toEqual([]); }); it("uses valid backend records retained by a degraded partition", () => { const backendWorkOrder = { ...mockWorkOrder, id: "backend-work-order", code: "WO-BACKEND" }; const backendAlert = { id: "backend-alert-1", title: "后端任务告警", description: "合法记录与另一条坏记录同时存在。", severity: "warning" as const, time: "09-18 10:29" }; const resolved = resolveWorkbenchRuntimeData([mockCondition, mockWorkOrder], { availability: "degraded", source: "none", scenarioAvailability: "not_configured", artifactAvailability: "not_configured", scheduleAvailability: "not_configured", workOrderAvailability: "degraded", alertAvailability: "degraded", generatedAt: "2026-09-18T02:30:00Z", conditions: [], alerts: [backendAlert], scenarios: [], latestAnalysisArtifact: null, simulationOverlay: null, workOrders: [backendWorkOrder] }, { operationalDate: SEPTEMBER_17 }); expect(resolved.workbenchAlerts).toEqual([backendAlert]); expect(resolved.alertDataSource).toBe("backend"); expect(resolved.scheduledConditions).toEqual([mockCondition, backendWorkOrder]); expect(resolved.workOrderDataSource).toBe("backend"); }); });