feat: expand intelligent investigation workspace
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import type { FeatureCollection } from "geojson";
|
||||
import { presentationCommandSchema } from "@/features/agent";
|
||||
import {
|
||||
executePresentationCommand,
|
||||
type PresentationPorts
|
||||
} from "./presentation-command-executor";
|
||||
import { parseInvestigationDevJsonl } from "../utils/investigation-dev-preview";
|
||||
import { investigationPresentationEventSchema } from "../api/investigation-presentation";
|
||||
|
||||
const chart = {
|
||||
title: "压力",
|
||||
chart_type: "line",
|
||||
x_data: ["10:00"],
|
||||
series: [{ name: "D1", data: [0.4] }]
|
||||
};
|
||||
const data: FeatureCollection = {
|
||||
type: "FeatureCollection",
|
||||
features: [
|
||||
{
|
||||
type: "Feature",
|
||||
properties: { id: "D1" },
|
||||
geometry: { type: "Point", coordinates: [121, 31] }
|
||||
}
|
||||
]
|
||||
};
|
||||
const commands = [
|
||||
{ action: "locate_features", layer: "scada", feature_ids: ["D1", "missing"] },
|
||||
{ action: "zoom_to_map", center: [121, 31], zoom: 14 },
|
||||
{ action: "highlight_features", layer: "scada", feature_ids: ["D1", "missing"], effect: "pulse" },
|
||||
{ action: "clear_highlights" },
|
||||
{ action: "open_chart", chart_id: "pressure", chart, evidence_refs: ["E1"] },
|
||||
{ action: "close_chart", chart_id: "pressure" },
|
||||
{
|
||||
action: "set_overlay",
|
||||
overlay_id: "points",
|
||||
title: "证据",
|
||||
mode: "geometry",
|
||||
evidence_id: "E1",
|
||||
data
|
||||
},
|
||||
{ action: "clear_overlay", overlay_id: "points" },
|
||||
{ action: "render_scada_analysis", items: [{ device_id: "D1", level: "high" }] },
|
||||
{ action: "clear_scada_analysis" }
|
||||
];
|
||||
function ports(): PresentationPorts {
|
||||
return {
|
||||
controller: { queryPresentationFeatures: vi.fn(async () => data) },
|
||||
scene: {
|
||||
highlight: vi.fn(),
|
||||
clearHighlights: vi.fn(),
|
||||
setOverlay: vi.fn(),
|
||||
clearOverlay: vi.fn(),
|
||||
renderScadaAnalysis: vi.fn(() => ({
|
||||
rendered_ids: ["D1"],
|
||||
missing_ids: [],
|
||||
fitted: true as const,
|
||||
level_counts: { high: 1, medium: 0, low: 0, unrated: 0 }
|
||||
})),
|
||||
clearScadaAnalysis: vi.fn(() => ({ cleared: true as const }))
|
||||
},
|
||||
cameraAllowed: () => false,
|
||||
zoom: vi.fn(async () => {}),
|
||||
openChart: vi.fn(async () => {}),
|
||||
closeChart: vi.fn(async () => {}),
|
||||
overlayChanged: vi.fn(),
|
||||
highlightsChanged: vi.fn()
|
||||
};
|
||||
}
|
||||
describe("shared presentation execution", () => {
|
||||
it.each(commands)(
|
||||
"accepts the same $action payload through chat, Dev and task events",
|
||||
async (input) => {
|
||||
const chat = presentationCommandSchema.parse(input);
|
||||
const preview = parseInvestigationDevJsonl(
|
||||
JSON.stringify({ record_type: "presentation", command: input })
|
||||
).commands[0];
|
||||
const task = investigationPresentationEventSchema.parse({
|
||||
task_id: "71000000-0000-4000-8000-000000000001",
|
||||
run_id: "71000000-0000-4000-8000-000000000002",
|
||||
execution_generation: 1,
|
||||
command: input
|
||||
}).command;
|
||||
const results = await Promise.all(
|
||||
[chat, preview, task].map((command) =>
|
||||
executePresentationCommand(command, new AbortController().signal, ports())
|
||||
)
|
||||
);
|
||||
expect(results[1]).toEqual(results[0]);
|
||||
expect(results[2]).toEqual(results[0]);
|
||||
}
|
||||
);
|
||||
it("reports actual matching IDs and respects paused automatic camera following", async () => {
|
||||
const target = ports();
|
||||
expect(
|
||||
await executePresentationCommand(
|
||||
presentationCommandSchema.parse(commands[2]),
|
||||
new AbortController().signal,
|
||||
target
|
||||
)
|
||||
).toEqual({ highlighted_ids: ["D1"], missing_ids: ["missing"], fitted: false });
|
||||
expect(target.scene.highlight).toHaveBeenCalledWith(data, true, false);
|
||||
});
|
||||
it("rejects empty targets and cancelled actions before any scene mutation", async () => {
|
||||
const target = ports();
|
||||
vi.mocked(target.controller.queryPresentationFeatures).mockResolvedValueOnce({
|
||||
type: "FeatureCollection",
|
||||
features: []
|
||||
});
|
||||
await expect(
|
||||
executePresentationCommand(
|
||||
presentationCommandSchema.parse(commands[0]),
|
||||
new AbortController().signal,
|
||||
target
|
||||
)
|
||||
).rejects.toThrow("FEATURE_NOT_FOUND");
|
||||
const controller = new AbortController();
|
||||
controller.abort();
|
||||
await expect(
|
||||
executePresentationCommand(
|
||||
presentationCommandSchema.parse(commands[3]),
|
||||
controller.signal,
|
||||
target
|
||||
)
|
||||
).rejects.toThrow();
|
||||
expect(target.scene.highlight).not.toHaveBeenCalled();
|
||||
expect(target.scene.clearHighlights).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user