diff --git a/tests/routes/canvas-agent-operations.test.ts b/tests/routes/canvas-agent-operations.test.ts index d16fac32f..d4a1dcccd 100644 --- a/tests/routes/canvas-agent-operations.test.ts +++ b/tests/routes/canvas-agent-operations.test.ts @@ -10,6 +10,7 @@ import { } from "@/lib/canvas/agent-mutations"; import type { CanvasAgentFeatureFlags } from "@/lib/canvas/agent-feature-flags"; import { handleCanvasAgentOperationRouteRequest } from "@/lib/canvas/agent-operation-route"; +import type { CanvasOperationPlan } from "@/types/canvas/agent-protocol"; const projectId = "11111111-1111-4111-8111-111111111111"; const enabledRouteFeatures: CanvasAgentFeatureFlags = { @@ -513,6 +514,77 @@ test.describe("canvas agent operation mutation contract", () => { ).toThrow(/媒体来源节点不存在/); }); + test("creates agent slot nodes with stable metadata and idempotency", () => { + const canvasData = { + ...getCanvasData([]), + viewport: { x: -120, y: -80, zoom: 2, width: 1000, height: 700 }, + }; + const plan: CanvasOperationPlan = { + version: 1 as const, + operationId: "op-create-video-slot", + operation: "create_agent_slot" as const, + projectId, + targetPosition: { x: 240, y: 180 }, + slot: { + slotType: "video" as const, + label: "视频结果槽", + aspectRatio: "16:9", + fillPolicy: "append-version" as const, + acceptedSources: ["text", "image", "annotation"], + }, + metadata: { + messageId: "assistant-message-1", + chatId: "chat-1", + prompt: "创建一个视频槽位", + }, + }; + + const first = applyCanvasOperationPlanToCanvasData({ + canvasData, + plan, + }); + const createdSlotId = first.createdNodeIds[0]; + const createdSlot = getResultNodes(first.canvasData).find( + (node) => node.id === createdSlotId + ); + + expect(createdSlot).toMatchObject({ + type: "agent-slot", + position: { x: 240, y: 180 }, + size: { width: 360, height: 220 }, + data: { + slotType: "video", + label: "视频结果槽", + status: "empty", + aspectRatio: "16:9", + fillPolicy: "append-version", + acceptedSources: ["text", "image", "annotation"], + filledNodeIds: [], + metadata: { + createdByAgent: true, + canvasAgentOperationId: "op-create-video-slot", + canvasAgentOperation: "create_agent_slot", + messageId: "assistant-message-1", + chatId: "chat-1", + prompt: "创建一个视频槽位", + }, + }, + }); + expect((first.canvasData as { selectedNodeIds?: string[] }).selectedNodeIds).toEqual([ + createdSlotId, + ]); + + const retry = applyCanvasOperationPlanToCanvasData({ + canvasData: first.canvasData, + plan, + }); + + expect(retry.createdNodeIds).toEqual(first.createdNodeIds); + expect(getResultNodes(retry.canvasData)).toHaveLength( + getResultNodes(first.canvasData).length + ); + }); + test("fills an agent slot and returns media item node mappings", () => { const slot = buildAgentSlotNode(); const result = applyCanvasOperationPlanToCanvasData({