ai-chatbot/tests/integration/canvas-local-download.test.ts

79 lines
4.7 KiB
TypeScript

import assert from "node:assert/strict";
import { randomUUID } from "node:crypto";
import { after, before, test } from "node:test";
import { eq } from "drizzle-orm";
import { db } from "@/lib/db";
import { canvasProject, user } from "@/lib/db/schema";
import { resolveCanvasCapabilityContext } from "@/lib/canvas/capabilities/context";
import { createCanvasCapabilityClientCommand } from "@/lib/canvas/capabilities/client-command";
import { executeCanvasCapabilityRuntime } from "@/lib/canvas/capabilities/runtime";
import { downloadCanvasImageAsPng } from "@/lib/client/canvas-image-download";
const database = new URL(process.env.POSTGRES_URL || "http://missing");
if (!["127.0.0.1", "localhost"].includes(database.hostname) || !/_test$/.test(database.pathname)) {
throw new Error("Use a disposable local test database");
}
const owner = randomUUID();
const stranger = randomUUID();
const projectId = randomUUID();
const unsavedNodeId = randomUUID();
const textNodeId = randomUUID();
const download = () => createCanvasCapabilityClientCommand({ capabilityId: "image.download", nodeIds: [unsavedNodeId], projectId, source: "toolbar" });
before(async () => {
await db.insert(user).values([{ id: owner, email: `${owner}@download.invalid` }, { id: stranger, email: `${stranger}@download.invalid` }]);
await db.insert(canvasProject).values({ id: projectId, userId: owner, title: "Local download regression", canvasData: { nodes: [{ id: textNodeId, type: "text", data: { text: "Not an image" } }], edges: [] } });
});
after(async () => {
await db.delete(user).where(eq(user.id, owner));
await db.delete(user).where(eq(user.id, stranger));
await db.$client.end({ timeout: 2 });
});
test("download dispatch succeeds while a generated node is waiting for autosave", async () => {
const result = await executeCanvasCapabilityRuntime({ command: download(), projectId, userId: owner, requestHeaders: new Headers(), requestUrl: "https://test.invalid" });
assert.equal(result.mode, "interactive");
assert.equal(result.clientCommand?.capabilityId, "image.download");
assert.deepEqual(result.clientCommand?.nodeIds, [unsavedNodeId]);
assert.equal(result.task.status, "succeeded");
});
test("unsaved download permission never bypasses project ownership", async () => {
await assert.rejects(resolveCanvasCapabilityContext({ command: download(), projectId, userId: stranger, allowUnsavedDownloadTargets: true }), { code: "CANVAS_CAPABILITY_PROJECT_NOT_FOUND" });
});
test("other image operations still require a persisted node", async () => {
const command = createCanvasCapabilityClientCommand({ capabilityId: "image.enhance", nodeIds: [unsavedNodeId], projectId, source: "toolbar" });
await assert.rejects(resolveCanvasCapabilityContext({ command, projectId, userId: owner, allowUnsavedDownloadTargets: true }), { code: "CANVAS_CAPABILITY_NODE_NOT_FOUND" });
});
test("existing non-image nodes cannot masquerade as downloadable images", async () => {
const command = createCanvasCapabilityClientCommand({ capabilityId: "image.download", nodeIds: [textNodeId], projectId, source: "toolbar" });
await assert.rejects(resolveCanvasCapabilityContext({ command, projectId, userId: owner, allowUnsavedDownloadTargets: true }), { code: "CANVAS_CAPABILITY_INVALID_NODE_TYPE" });
});
test("the browser download uses the current image URL without node persistence or a model call", async () => {
const original = { fetch: globalThis.fetch, document: globalThis.document, window: globalThis.window };
const requests: Array<{ path: string; body: Record<string, unknown> }> = [];
const frames: Array<{ src?: string }> = [];
try {
globalThis.fetch = async (input, init) => {
requests.push({ path: String(input), body: JSON.parse(String(init?.body)) });
return Response.json({ delivery: "cdn", downloadUrl: "https://cdn.example/download.png", filename: "original.png", bytes: 123 });
};
globalThis.document = { createElement: () => ({}), body: { appendChild: (frame: { src?: string }) => frames.push(frame) } } as unknown as Document;
globalThis.window = { setTimeout: () => 1 } as unknown as Window & typeof globalThis;
const result = await downloadCanvasImageAsPng({ url: "https://provider.example/new-unsaved-image.png", name: "Original" });
assert.equal(result.filename, "original.png");
assert.equal(requests.length, 1);
assert.equal(requests[0].path, "/api/canvas/download-image");
assert.equal(requests[0].body.url, "https://provider.example/new-unsaved-image.png");
assert.equal(requests[0].body.nodeIds, undefined);
assert.equal(frames[0].src, "https://cdn.example/download.png");
} finally {
globalThis.fetch = original.fetch;
globalThis.document = original.document;
globalThis.window = original.window;
}
});