Files
next-tjwater-frontend/src/features/workbench/api/workbench-runtime-client.test.ts
T

322 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { afterEach, describe, expect, it, vi } from "vitest";
import {
fetchWorkbenchAnalysisRun,
parseWorkbenchRuntimeSnapshot
} from "./workbench-runtime-client";
const projectPage = {
items: [{ project_id: "project-1", gs_workspace: "tjwater_next", status: "active" }],
total: 1,
limit: 1000,
offset: 0
};
const runtimeSnapshot = {
availability: "ready",
source: "scheduled_tasks",
scenario_availability: "ready",
artifact_availability: "ready",
schedule_availability: "not_configured",
work_order_availability: "not_configured",
alert_availability: "ready",
generated_at: "2026-09-17T02:30:00Z",
conditions: [{
id: "schedule-run-1",
task_id: "backend-schedule",
session_id: "scheduled-condition-schedule-run-1",
title: "演示 合成低压调查",
summary: "来自后端持久化 Action Run 的 演示 沙箱测试状态。",
status: "error",
risk_level: "critical",
scheduled_at: "2026-09-17T02:20:00Z",
updated_at: "2026-09-17T02:25:00Z",
detail: "演示 测试运行状态:已失败。",
recommendation: "请检查失败步骤。",
evidence: ["运行状态:failed"],
model_name: "TJWater scheduler"
}],
alerts: [{
id: "scheduled-condition-alert-schedule-run-1",
condition_id: "schedule-run-1",
source: {
task_type: "analysis_task",
task_id: "backend-schedule",
run_id: "schedule-run-1"
},
title: "演示 测试待复核:演示 合成低压调查",
description: "运行失败。",
occurred_at: "2026-09-17T02:25:00Z",
severity: "critical"
}],
scenarios: [{
id: "analysis-run-analysis-1",
run_id: "analysis-1",
name: "北区爆管定位",
description: "爆管定位 · 已完成 · 创建人 alice",
status: "review",
run_type: "burst_location",
run_type_label: "爆管定位",
run_status: "completed",
created_at: "2026-09-17T02:20:00Z"
}],
latest_analysis: {
run_id: "analysis-1",
name: "北区爆管定位",
run_type: "burst_location",
run_type_label: "爆管定位",
status: "completed",
status_label: "已完成",
created_by: "alice",
created_at: "2026-09-17T02:20:00Z",
started_at: "2026-09-17T02:10:00Z",
results: [{
result_type: "burst_location",
created_at: "2026-09-17T02:20:00Z",
facts: [{ label: "关联管段", value: "P-42" }]
}],
map_payload: {
type: "FeatureCollection",
features: [{
type: "Feature",
properties: { label: "后端爆管定位" },
geometry: { type: "Point", coordinates: [121.5, 30.9] }
}]
}
},
work_orders: []
};
describe("workbench runtime API client", () => {
afterEach(() => vi.unstubAllGlobals());
it("maps a persisted scheduled condition", () => {
const snapshot = parseWorkbenchRuntimeSnapshot(runtimeSnapshot);
expect(snapshot.source).toBe("scheduled_tasks");
expect(snapshot.conditions).toEqual([
expect.objectContaining({
id: "schedule-run-1",
kind: "condition",
taskId: "backend-schedule",
status: "error",
modelName: "TJWater scheduler"
})
]);
expect(snapshot.alerts[0]).toEqual(expect.objectContaining({
conditionId: "schedule-run-1",
id: "scheduled-condition-alert-schedule-run-1",
title: "演示 测试待复核:演示 合成低压调查",
source: {
taskType: "analysis_task",
taskId: "backend-schedule",
runId: "schedule-run-1"
}
}));
expect(snapshot.scenarios[0]).toEqual(expect.objectContaining({
id: "analysis-run-analysis-1",
runId: "analysis-1",
runType: "burst_location"
}));
expect(snapshot.latestAnalysisArtifact).toEqual(expect.objectContaining({
id: "persisted-analysis-analysis-1",
title: "北区爆管定位",
confidence: "后端未提供",
mapRelation: "required",
spatialOverlay: expect.objectContaining({ type: "FeatureCollection" }),
sources: ["项目 BizDB · analysis.runs", "项目 BizDB · analysis.results"]
}));
expect(snapshot.latestAnalysisArtifact?.blocks).toEqual(expect.arrayContaining([
expect.objectContaining({
kind: "data-table",
rows: [expect.objectContaining({ facts: "关联管段:P-42" })]
})
]));
expect(snapshot.simulationOverlay?.features).toHaveLength(1);
});
it("keeps legacy analysis conditions out of the operational task feed", () => {
const snapshot = parseWorkbenchRuntimeSnapshot({
...runtimeSnapshot,
source: "analysis_runs",
conditions: [{
id: "analysis-run-analysis-1",
task_id: "backend-analysis",
session_id: "scheduled-condition-analysis-run-analysis-1",
title: "北区爆管定位",
summary: "来自旧版后端的分析运行投影。",
status: "completed",
risk_level: "normal",
scheduled_at: "2026-09-17T02:10:00Z",
updated_at: "2026-09-17T02:20:00Z",
detail: "分析结果已完成。",
recommendation: null,
evidence: ["运行 ID:analysis-1"],
model_name: "TJWater analysis runtime"
}]
});
expect(snapshot.source).toBe("none");
expect(snapshot.conditions).toEqual([]);
expect(snapshot.latestAnalysisArtifact).toEqual(expect.objectContaining({
id: "persisted-analysis-analysis-1",
title: "北区爆管定位"
}));
expect(snapshot.scenarios).toHaveLength(1);
});
it("rejects an untyped runtime payload instead of treating it as real data", () => {
expect(() => parseWorkbenchRuntimeSnapshot({ ...runtimeSnapshot, source: "production" })).toThrow();
});
it("loads the requested analysis run instead of reusing the latest result", async () => {
const runId = "86fd7c19-5b75-434f-999c-ec8fdf67d10b";
const selectedAnalysis = {
...runtimeSnapshot.latest_analysis,
run_id: runId,
name: "历史爆管定位",
results: [{
result_type: "burst_location",
created_at: "2026-09-17T01:20:00Z",
facts: [{ label: "关联管段", value: "P-older" }]
}],
map_payload: {
type: "FeatureCollection",
features: [{
type: "Feature",
properties: { label: "历史运行几何" },
geometry: { type: "Point", coordinates: [121.4, 30.8] }
}]
}
};
const fetchMock = vi
.fn()
.mockResolvedValueOnce(new Response(JSON.stringify(projectPage), { status: 200 }))
.mockResolvedValueOnce(new Response(JSON.stringify(selectedAnalysis), { status: 200 }));
vi.stubGlobal("fetch", fetchMock);
const projection = await fetchWorkbenchAnalysisRun(
runId,
vi.fn().mockResolvedValue("access-token")
);
expect(projection.runId).toBe(runId);
expect(projection.artifact).toEqual(expect.objectContaining({
id: `persisted-analysis-${runId}`,
title: "历史爆管定位"
}));
expect(projection.artifact.blocks).toEqual(expect.arrayContaining([
expect.objectContaining({
kind: "data-table",
rows: [expect.objectContaining({ facts: "关联管段:P-older" })]
})
]));
expect(projection.simulationOverlay?.features[0]?.properties).toEqual({
label: "历史运行几何"
});
expect(String(fetchMock.mock.calls[1]?.[0])).toContain(
`/api/v1/workbench/analysis-runs/${runId}`
);
const headers = new Headers(fetchMock.mock.calls[1]?.[1]?.headers);
expect(headers.get("Authorization")).toBe("Bearer access-token");
expect(headers.get("X-Project-Id")).toBe("project-1");
});
it("drops invalid backend map geometry without discarding other real sections", () => {
const snapshot = parseWorkbenchRuntimeSnapshot({
...runtimeSnapshot,
latest_analysis: {
...runtimeSnapshot.latest_analysis,
map_payload: {
type: "FeatureCollection",
features: [{
type: "Feature",
properties: {},
geometry: { type: "Point", coordinates: ["east", "north"] }
}]
}
}
});
expect(snapshot.source).toBe("scheduled_tasks");
expect(snapshot.scenarios).toHaveLength(1);
expect(snapshot.simulationOverlay).toBeNull();
expect(snapshot.latestAnalysisArtifact?.mapRelation).toBe("none");
expect(snapshot.latestAnalysisArtifact).not.toHaveProperty("spatialOverlay");
});
it("maps provisioned backend work orders without using the demo shape", () => {
const backendWorkOrder = {
id: "wo-1",
code: "WO-20260917-001",
scheduled_at: "2026-09-17T03:00:00Z",
title: "北区阀门现场复核",
summary: "复核阀门开度并回传。",
status: "completed",
risk_level: "attention",
updated_at: "2026-09-17T02:30:00Z",
detail: "来自正式工单同步适配器。",
recommendation: "完成后复核压力。",
duration_minutes: 45,
source: "生产工单系统",
dispatcher: "调度中心",
priority: "urgent",
location: "北区阀门 V-1",
assignee: "北区班组",
feedback_requirements: ["回传阀门开度"],
activity: [{ sequence: 1, occurred_at: "2026-09-17T02:30:00Z", actor: "调度中心",
kind: "created", title: "工单创建", detail: "等待现场复核。" }]
};
const snapshot = parseWorkbenchRuntimeSnapshot({
...runtimeSnapshot,
work_order_availability: "ready",
work_orders: [backendWorkOrder]
});
expect(snapshot.workOrderAvailability).toBe("ready");
expect(snapshot.workOrders).toEqual([expect.objectContaining({
id: "demo-work-order:wo-1",
kind: "work_order",
code: "WO-20260917-001",
status: "completed",
source: "生产工单系统",
location: "北区阀门 V-1",
assignee: "北区班组",
feedbackRequirements: ["回传阀门开度"],
activity: [expect.objectContaining({ title: "工单创建" })]
})]);
});
it("maps a provisioned backend alert without inventing a condition link", () => {
const snapshot = parseWorkbenchRuntimeSnapshot({
...runtimeSnapshot,
alert_availability: "ready",
alerts: [{
id: "backend-alert-alarm-1",
condition_id: null,
source: {
task_type: "scheduled_task",
task_id: "scada-diagnosis-5m",
run_id: "trigger-1"
},
title: "泵站通信告警",
description: "由 SCADA 数据诊断任务明确推送。",
occurred_at: "2026-09-17T02:29:00Z",
severity: "warning"
}]
});
expect(snapshot.alertAvailability).toBe("ready");
expect(snapshot.alerts).toEqual([expect.objectContaining({
id: "backend-alert-alarm-1",
title: "泵站通信告警",
severity: "warning",
source: {
taskType: "scheduled_task",
taskId: "scada-diagnosis-5m",
runId: "trigger-1"
}
})]);
expect(snapshot.alerts[0]).not.toHaveProperty("conditionId");
});
});