102 lines
4.1 KiB
TypeScript
102 lines
4.1 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { test } from "node:test";
|
|
import {
|
|
commitCanvasImageResultIfMissing,
|
|
getCanvasImageResultUrlKeys,
|
|
resolveCanvasImageResultAliases,
|
|
} from "@/components/canvas/chat-panel/canvas-image-result-sync";
|
|
|
|
const preview = "https://provider.example/result.png";
|
|
const archived = "https://bucket.example/archived.png";
|
|
const other = "https://bucket.example/second.png";
|
|
const image = (src: string, sourceImageKey = src, sourceMessageId = "message-1") => ({
|
|
type: "image",
|
|
data: { src, metadata: { sourceImageKey, sourceMessageId } },
|
|
});
|
|
|
|
test("archived history replay recognizes the provider preview already on canvas", async () => {
|
|
const nodes = [image(preview)];
|
|
const aliases = await resolveCanvasImageResultAliases({
|
|
nodes, messageId: "message-1", imageUrls: [archived],
|
|
resolveAliases: async (urls) => {
|
|
assert.deepEqual(urls, [preview]);
|
|
return { [preview]: archived };
|
|
},
|
|
});
|
|
let writes = 0;
|
|
const result = commitCanvasImageResultIfMissing({ getNodes: () => nodes, url: archived, aliases, commit: () => ++writes });
|
|
assert.equal(result, undefined);
|
|
assert.equal(writes, 0);
|
|
assert.equal(nodes.length, 1);
|
|
});
|
|
|
|
test("current src and previous source identity both prevent replay", () => {
|
|
const keys = getCanvasImageResultUrlKeys([image(archived, preview)]);
|
|
assert(keys.has(preview));
|
|
assert(keys.has(archived));
|
|
});
|
|
|
|
test("simultaneous stream and history image loads commit a single node", async () => {
|
|
const nodes: ReturnType<typeof image>[] = [];
|
|
let release!: () => void;
|
|
const loading = new Promise<void>((resolve) => { release = resolve; });
|
|
const insert = async () => {
|
|
// Both paths have already observed an empty canvas before image loading.
|
|
assert.equal(nodes.length, 0);
|
|
await loading;
|
|
return commitCanvasImageResultIfMissing({
|
|
getNodes: () => nodes, url: preview, aliases: {},
|
|
commit: () => { nodes.push(image(preview)); return "inserted"; },
|
|
});
|
|
};
|
|
const stream = insert();
|
|
const history = insert();
|
|
release();
|
|
assert.deepEqual(await Promise.all([stream, history]), ["inserted", undefined]);
|
|
assert.equal(nodes.length, 1);
|
|
});
|
|
|
|
test("multiple distinct results in one message remain distinct", async () => {
|
|
const nodes = [image(preview)];
|
|
const aliases = await resolveCanvasImageResultAliases({
|
|
nodes, messageId: "message-1", imageUrls: [archived, other],
|
|
resolveAliases: async () => ({ [preview]: archived }),
|
|
});
|
|
for (const url of [archived, other]) {
|
|
commitCanvasImageResultIfMissing({ getNodes: () => nodes, url, aliases, commit: () => nodes.push(image(url)) });
|
|
}
|
|
assert.equal(nodes.length, 2);
|
|
assert.equal(nodes[1].data.src, other);
|
|
});
|
|
|
|
test("new generations display immediately without an archive lookup", async () => {
|
|
const aliases = await resolveCanvasImageResultAliases({
|
|
nodes: [image(preview, preview, "previous-message")], messageId: "message-2", imageUrls: [other],
|
|
resolveAliases: async () => { throw new Error("Must not wait for archiving"); },
|
|
});
|
|
assert.deepEqual(aliases, {});
|
|
});
|
|
|
|
test("an archive lookup failure never treats the URL change as a new result", async () => {
|
|
await assert.rejects(resolveCanvasImageResultAliases({
|
|
nodes: [image(preview)], messageId: "message-1", imageUrls: [archived],
|
|
resolveAliases: async () => { throw new Error("Temporary network failure"); },
|
|
}), /Temporary network failure/);
|
|
});
|
|
|
|
test("same-message image batches respect the archive endpoint limit", async () => {
|
|
const nodes = Array.from({ length: 205 }, (_, i) => image(`https://provider.example/${i}.png`));
|
|
const batches: number[] = [];
|
|
await resolveCanvasImageResultAliases({
|
|
nodes, messageId: "message-1", imageUrls: [archived],
|
|
resolveAliases: async (urls) => { batches.push(urls.length); return {}; },
|
|
});
|
|
assert.deepEqual(batches, [100, 100, 5]);
|
|
});
|
|
|
|
test("synchronization leaves existing manual copies untouched", () => {
|
|
const nodes = [image(archived, preview), image(archived, preview)];
|
|
commitCanvasImageResultIfMissing({ getNodes: () => nodes, url: archived, aliases: {}, commit: () => { throw new Error("Unexpected insertion"); } });
|
|
assert.equal(nodes.length, 2);
|
|
});
|