Files
ttstd-infinite-canvas/web/test/canvas-image-batch-retry.test.ts
WindC0X 9ee71d4650 feat(canvas): 画布媒体工作流 - 支持批量连线、失败重试与模型能力增强
合并 PR #230。保留当前主线样式与组件结构,吸收批量连线、失败批次重试、媒体模型能力和相关后端增强,并修复集成检查发现的分辨率选择器启用条件与测试稳定性问题。
2026-08-15 15:48:16 +08:00

89 lines
5.0 KiB
TypeScript

import { describe, expect, test } from "bun:test";
import { failedImageBatchChildren, markImageBatchRetrying, reconcileImageBatchRoot, restoreUnsubmittedImageBatchChild } from "../src/lib/canvas/canvas-image-batch-retry";
import { CanvasNodeType, type CanvasNodeData, type CanvasNodeStatus } from "../src/types/canvas";
function imageNode(id: string, status: CanvasNodeStatus, metadata: Partial<NonNullable<CanvasNodeData["metadata"]>> = {}): CanvasNodeData {
return {
id,
type: CanvasNodeType.Image,
title: id,
position: { x: 0, y: 0 },
width: 320,
height: 240,
metadata: { status, ...metadata },
};
}
describe("canvas image batch retry", () => {
test("只按批次顺序返回属于当前根节点的失败图片", () => {
const root = imageNode("root", "error", { isBatchRoot: true, batchChildIds: ["failed-2", "success", "failed-1", "loading", "foreign"] });
const nodes = [
root,
imageNode("failed-1", "error", { batchRootId: root.id }),
imageNode("failed-2", "error", { batchRootId: root.id }),
imageNode("success", "success", { batchRootId: root.id, content: "image:success" }),
imageNode("loading", "loading", { batchRootId: root.id }),
imageNode("foreign", "error", { batchRootId: "another-root" }),
];
expect(failedImageBatchChildren(root, nodes).map((node) => node.id)).toEqual(["failed-2", "failed-1"]);
});
test("任一子图成功后恢复根节点主图,同时保留其他失败子图", () => {
const root = imageNode("root", "loading", { isBatchRoot: true, batchChildIds: ["failed", "success"], errorDetails: "旧错误" });
const failed = imageNode("failed", "error", { batchRootId: root.id, errorDetails: "上游失败" });
const success = imageNode("success", "success", {
batchRootId: root.id,
content: "image:success",
storageKey: "resource:success",
mimeType: "image/png",
naturalWidth: 1024,
naturalHeight: 1024,
});
const next = reconcileImageBatchRoot(root, [root, failed, success]);
expect(next.metadata).toMatchObject({ status: "success", content: "image:success", storageKey: "resource:success", primaryImageId: "success", batchFailedCount: 1 });
expect(next.metadata.errorDetails).toBeUndefined();
expect(failed.metadata.status).toBe("error");
});
test("全部子图失败时把代表错误同步回根节点", () => {
const root = imageNode("root", "loading", { isBatchRoot: true, batchChildIds: ["failed-1", "failed-2"] });
const failed = imageNode("failed-1", "error", {
batchRootId: root.id,
errorDetails: "No available compatible accounts",
generationErrorCode: "upstream_unavailable",
});
const next = reconcileImageBatchRoot(root, [root, failed, imageNode("failed-2", "error", { batchRootId: root.id, errorDetails: "另一错误" })]);
expect(next.metadata).toMatchObject({ status: "error", errorDetails: "No available compatible accounts", generationErrorCode: "upstream_unavailable", batchFailedCount: 2 });
expect(next.metadata.content).toBeUndefined();
expect(next.metadata.primaryImageId).toBeUndefined();
});
test("开始批量重试时根节点和全部失败子图同步进入生成中", () => {
const root = imageNode("root", "error", { isBatchRoot: true, batchChildIds: ["failed-1", "success", "failed-2"], batchFailedCount: 2, errorDetails: "全部失败" });
const failed1 = imageNode("failed-1", "error", { batchRootId: root.id, errorDetails: "失败 1" });
const success = imageNode("success", "success", { batchRootId: root.id, content: "image:success" });
const failed2 = imageNode("failed-2", "error", { batchRootId: root.id, errorDetails: "失败 2" });
const next = markImageBatchRetrying(root.id, [failed1.id, failed2.id], [root, failed1, success, failed2]);
expect(next.find((node) => node.id === root.id)?.metadata).toMatchObject({ status: "loading", batchFailedCount: 2 });
expect(next.find((node) => node.id === failed1.id)?.metadata?.status).toBe("loading");
expect(next.find((node) => node.id === failed2.id)?.metadata?.status).toBe("loading");
expect(next.find((node) => node.id === success.id)?.metadata).toMatchObject({ status: "success", content: "image:success" });
});
test("未提交请求的失败子图从预备加载状态恢复原错误", () => {
const original = imageNode("failed", "error", { batchRootId: "root", errorDetails: "原始失败" });
const loading = imageNode("failed", "loading", { batchRootId: "root" });
expect(restoreUnsubmittedImageBatchChild(loading, original).metadata).toMatchObject({ status: "error", errorDetails: "原始失败" });
expect(restoreUnsubmittedImageBatchChild(imageNode("failed", "success", { content: "image:success" }), original).metadata?.status).toBe("success");
});
});