import assert from "node:assert/strict"; import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { test } from "node:test"; import { DreaminaGenerationAdapter, LocalDreaminaGenerationError } from "../src/dreamina-generation.js"; import { projectDreaminaModelCatalog } from "../src/dreamina-model-catalog.js"; test("Dreamina generation coalesces an equal idempotency request before invoking the official CLI", async () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "dreamina-generation-test-")); const calls: string[] = []; const adapter = new DreaminaGenerationAdapter({ root, models: projectDreaminaModelCatalog(), runtime: { async generateToResult(input) { calls.push(input.idempotencyKey); return { mode: "video", video: { dataUrl: "data:video/mp4;base64,AAAA", mimeType: "video/mp4", bytes: 3 } }; }, }, }); const request = { idempotencyKey: "seedance-mini-key-0001", operation: "text-to-video", model: "seedance2.0mini", prompt: "A short test clip", settings: { aspect: "16:9", resolution: "720p", duration: 4 }, references: [], }; try { const [first, second] = await Promise.all([adapter.run(request), adapter.run(request)]); assert.deepEqual(first, second); assert.deepEqual(calls, ["seedance-mini-key-0001"]); } finally { await fs.rm(root, { recursive: true, force: true }); } }); test("Dreamina generation rejects a Seedance mini duration below the official four-second minimum", async () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "dreamina-generation-test-")); const adapter = new DreaminaGenerationAdapter({ root, models: projectDreaminaModelCatalog(), runtime: { async generateToResult() { throw new Error("must not execute"); } }, }); try { await assert.rejects( adapter.run({ idempotencyKey: "seedance-mini-key-0002", operation: "text-to-video", model: "seedance2.0mini", prompt: "A short test clip", settings: { resolution: "720p", duration: 3 }, references: [], }), (error: unknown) => error instanceof LocalDreaminaGenerationError && error.code === "local_generation_model_unavailable", ); } finally { await fs.rm(root, { recursive: true, force: true }); } }); test("Dreamina image generation maps auto or omitted product resolution to the official cross-model two-k tier", async () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "dreamina-generation-test-")); const runtimeInputs: unknown[] = []; const adapter = new DreaminaGenerationAdapter({ root, models: projectDreaminaModelCatalog(), runtime: { async generateToResult(input) { runtimeInputs.push(input); return { mode: "image", images: [{ dataUrl: "data:image/png;base64,AAAA", mimeType: "image/png", bytes: 3 }] }; }, }, }); try { const models = ["3.0", "3.1", "4.0", "4.1", "4.5", "4.6", "4.7", "5.0", "5.0Pro"] as const; for (const productResolution of [undefined, "auto"] as const) { for (const model of models) { const index = runtimeInputs.length; await adapter.run({ idempotencyKey: `dreamina-image-default-${String(index + 1).padStart(4, "0")}`, operation: "text-to-image", model, prompt: "A small test image", settings: { aspect: "1:1", ...(productResolution ? { resolution: productResolution } : {}), count: 1 }, references: [], }); assert.deepEqual(runtimeInputs[index] && { operation: (runtimeInputs[index] as { operation: string }).operation, modelVersion: (runtimeInputs[index] as { modelVersion: string }).modelVersion, resolutionType: (runtimeInputs[index] as { resolutionType: string }).resolutionType, }, { operation: "text2image", modelVersion: model, resolutionType: "2k" }); } } } finally { await fs.rm(root, { recursive: true, force: true }); } }); test("Dreamina image generation preserves an explicit unsupported tier for strict model validation", async () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "dreamina-generation-test-")); let runtimeCalls = 0; const adapter = new DreaminaGenerationAdapter({ root, models: projectDreaminaModelCatalog(), runtime: { async generateToResult() { runtimeCalls += 1; return { mode: "image", images: [{ dataUrl: "data:image/png;base64,AAAA", mimeType: "image/png", bytes: 3 }] }; }, }, }); try { await assert.rejects(adapter.run({ idempotencyKey: "dreamina-image-explicit-0001", operation: "text-to-image", model: "5.0", prompt: "A small test image", settings: { aspect: "1:1", resolution: "1k", count: 1 }, references: [], }), (error: unknown) => error instanceof LocalDreaminaGenerationError && error.code === "local_generation_request_invalid"); assert.equal(runtimeCalls, 0); } finally { await fs.rm(root, { recursive: true, force: true }); } }); test("Dreamina video generation maps product resolution values only in the final CLI adapter", async () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "dreamina-generation-test-")); const runtimeInputs: unknown[] = []; const adapter = new DreaminaGenerationAdapter({ root, models: projectDreaminaModelCatalog(), runtime: { async generateToResult(input) { runtimeInputs.push(input); return { mode: "video", video: { dataUrl: "data:video/mp4;base64,AAAA", mimeType: "video/mp4", bytes: 3 } }; }, }, }); try { const cases = [["480", "480p", "seedance2.5"], ["720", "720p", "seedance2.0mini"], ["1080", "1080p", "seedance2.0_vip"], ["2160", "4k", "seedance2.0_vip"]] as const; for (const [index, [productResolution, cliResolution, model]] of cases.entries()) { await adapter.run({ idempotencyKey: `dreamina-video-resolution-${String(index + 1).padStart(4, "0")}`, operation: "text-to-video", model, prompt: "A short test clip", settings: { aspect: "16:9", resolution: productResolution, duration: 4 }, references: [], }); assert.equal((runtimeInputs[index] as { videoResolution: string }).videoResolution, cliResolution); } } finally { await fs.rm(root, { recursive: true, force: true }); } }); test("Dreamina adapter returns the Runtime task after references are durably staged, before result polling", async () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "dreamina-generation-test-")); let referencePath = ""; const adapter = new DreaminaGenerationAdapter({ root, models: projectDreaminaModelCatalog(), runtime: { async enqueue(input) { referencePath = "referenceImages" in input ? input.referenceImages[0] : ""; assert(referencePath && await fs.stat(referencePath).then((item) => item.isFile())); return { id: input.idempotencyKey, provider: "dreamina-cli", mode: "video", operation: input.operation, model: "seedance2.0mini", status: "running", stage: "submitted", progress: 10, receiptRecorded: true, createdAt: "2026-08-12T00:00:00.000Z", updatedAt: "2026-08-12T00:00:00.000Z", }; }, async waitForTask() { throw new Error("submit must not wait for query_result"); }, async generateToResult() { throw new Error("submit must not use legacy generation"); }, }, }); try { const task = await adapter.submit({ idempotencyKey: "dreamina-adapter-async-0001", operation: "image-to-video", model: "seedance2.0mini", prompt: "Animate the reference", settings: { resolution: "720", duration: 4 }, references: [{ kind: "image", mimeType: "image/png", contentBase64: Buffer.from("89504e470d0a1a0a", "hex").toString("base64") }], }); assert.equal(task.stage, "submitted"); await assert.rejects(fs.access(referencePath)); } finally { await fs.rm(root, { recursive: true, force: true }); } }); test("Dreamina final adapter selects multimodal2video and keeps typed references in separate staged groups", async () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "dreamina-generation-test-")); let observedInput: Record | undefined; let observedOptions: Record | undefined; const stagedExtensions: string[] = []; const adapter = new DreaminaGenerationAdapter({ root, models: projectDreaminaModelCatalog(), runtime: { async enqueue(input, options) { observedInput = input as unknown as Record; observedOptions = options as unknown as Record; const paths = [ ...("referenceImages" in input ? input.referenceImages : []), ...("referenceVideos" in input ? input.referenceVideos : []), ...("referenceAudios" in input ? input.referenceAudios : []), ]; for (const reference of paths) { assert.equal((await fs.stat(reference)).isFile(), true); stagedExtensions.push(path.extname(reference)); } return { id: input.idempotencyKey, provider: "dreamina-cli", mode: "video", operation: input.operation, model: "seedance2.5", status: "running", stage: "submitted", receiptRecorded: true, createdAt: "2026-08-13T00:00:00.000Z", updatedAt: "2026-08-13T00:00:00.000Z", }; }, async waitForTask() { throw new Error("submit must not wait"); }, async generateToResult() { throw new Error("submit must not use legacy generation"); }, }, }); try { await adapter.submit({ idempotencyKey: "retry-client-operation-0001", clientOperationId: "retry-client-operation-0001", context: { scope: "scoped", projectId: "project-agent-multimodal", nodeId: "node-agent-multimodal", retryOf: "dreamina:prior-agent-0001", attemptGroupId: "dreamina:prior-agent-0001", }, operation: "reference-to-video", model: "seedance2.5", prompt: "Use every reference", settings: { aspect: "16:9", resolution: "720", duration: 4 }, references: [ { kind: "image", mimeType: "image/png", contentBase64: Buffer.from("89504e470d0a1a0a", "hex").toString("base64"), metadata: { name: "image.png", width: 16, height: 9 } }, { kind: "video", mimeType: "video/mp4", contentBase64: Buffer.from("000000106674797069736f6d", "hex").toString("base64"), metadata: { name: "video.mp4", durationMs: 1000 } }, { kind: "audio", mimeType: "audio/mpeg", contentBase64: Buffer.from("49443304", "hex").toString("base64"), metadata: { name: "audio.mp3", durationMs: 1000 } }, ], }); assert.equal(observedInput?.operation, "multimodal2video"); assert.equal((observedInput?.referenceImages as unknown[])?.length, 1); assert.equal((observedInput?.referenceVideos as unknown[])?.length, 1); assert.equal((observedInput?.referenceAudios as unknown[])?.length, 1); assert.deepEqual(stagedExtensions.sort(), [".mp3", ".mp4", ".png"]); assert.equal(observedOptions?.clientOperationId, "retry-client-operation-0001"); assert.deepEqual(observedOptions?.taskContext, { scope: "scoped", projectId: "project-agent-multimodal", nodeId: "node-agent-multimodal", retryOf: "dreamina:prior-agent-0001", attemptGroupId: "dreamina:prior-agent-0001", }); } finally { await fs.rm(root, { recursive: true, force: true }); } }); test("only the final Dreamina adapter selects video CLI operations for every reference shape", async () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "dreamina-operation-table-")); const observed: Array> = []; const adapter = new DreaminaGenerationAdapter({ root, models: projectDreaminaModelCatalog(), runtime: { async generateToResult(input) { observed.push(input as unknown as Record); return { mode: "video", video: { dataUrl: "data:video/mp4;base64,AAAA", mimeType: "video/mp4", bytes: 3 } }; }, }, }); const image = { kind: "image", mimeType: "image/png", contentBase64: Buffer.from("89504e470d0a1a0a", "hex").toString("base64") }; const video = { kind: "video", mimeType: "video/mp4", contentBase64: Buffer.from("000000106674797069736f6d", "hex").toString("base64") }; const audio = { kind: "audio", mimeType: "audio/mpeg", contentBase64: Buffer.from("49443304", "hex").toString("base64") }; const cases = [ { name: "zero-image", operation: "text-to-video", references: [], expected: "text2video", groups: [0, 0, 0] }, { name: "one-image", operation: "image-to-video", references: [image], expected: "image2video", groups: [1, 0, 0] }, { name: "two-images", operation: "image-to-video", references: [image, image], expected: "frames2video", groups: [2, 0, 0] }, { name: "three-images", operation: "reference-to-video", references: [image, image, image], expected: "multimodal2video", groups: [3, 0, 0] }, { name: "video-only", operation: "reference-to-video", references: [video], expected: "multimodal2video", groups: [0, 1, 0] }, { name: "audio-only", operation: "reference-to-video", references: [audio], expected: "multimodal2video", groups: [0, 0, 1] }, { name: "mixed", operation: "reference-to-video", references: [image, video, audio], expected: "multimodal2video", groups: [1, 1, 1] }, ] as const; try { for (const [index, item] of cases.entries()) { await adapter.run({ idempotencyKey: `operation-table-${String(index).padStart(2, "0")}-0001`, operation: item.operation, model: "seedance2.5", prompt: "fixture", settings: { aspect: "16:9", resolution: "720", duration: 4 }, references: item.references, }); const input = observed.at(-1)!; assert.equal(input.operation, item.expected, item.name); assert.deepEqual([ (input.referenceImages as unknown[] | undefined)?.length ?? 0, (input.referenceVideos as unknown[] | undefined)?.length ?? 0, (input.referenceAudios as unknown[] | undefined)?.length ?? 0, ], item.groups, item.name); } await adapter.run({ idempotencyKey: "operation-table-max-groups-0001", operation: "reference-to-video", model: "seedance2.5", prompt: "fixture", settings: { aspect: "16:9", resolution: "720", duration: 4 }, references: [ ...Array.from({ length: 30 }, () => image), ...Array.from({ length: 10 }, () => video), ...Array.from({ length: 10 }, () => audio), ], }); assert.deepEqual([ (observed.at(-1)?.referenceImages as unknown[] | undefined)?.length ?? 0, (observed.at(-1)?.referenceVideos as unknown[] | undefined)?.length ?? 0, (observed.at(-1)?.referenceAudios as unknown[] | undefined)?.length ?? 0, ], [30, 10, 10]); for (const [name, references] of [ ["images", Array.from({ length: 31 }, () => image)], ["videos", Array.from({ length: 11 }, () => video)], ["audios", Array.from({ length: 11 }, () => audio)], ] as const) { await assert.rejects(adapter.run({ idempotencyKey: `operation-limit-${name}-0001`, operation: "reference-to-video", model: "seedance2.5", prompt: "fixture", settings: { aspect: "16:9", resolution: "720", duration: 4 }, references, }), (error: unknown) => error instanceof LocalDreaminaGenerationError && error.code === "local_generation_request_invalid"); } } finally { await fs.rm(root, { recursive: true, force: true }); } });