import { describe, it, expect } from "vitest"; import { parseAiTaskStatus, parseTaskCreateResponse, parseImageTaskCreateResponse, parseAiTaskStatusList, parseSseTaskFrame, } from "./dtoParsers"; describe("parseAiTaskStatus", () => { it("parses a well-formed camelCase DTO", () => { const result = parseAiTaskStatus({ taskId: "task-1", type: "video", status: "running", progress: 42, resultUrl: "https://example.com/r.mp4", error: null, createdAt: "2026-01-01T00:00:00Z", updatedAt: "2026-01-01T00:01:00Z", }); expect(result.taskId).toBe("task-1"); expect(result.type).toBe("video"); expect(result.status).toBe("running"); expect(result.progress).toBe(42); expect(result.resultUrl).toBe("https://example.com/r.mp4"); }); it("tolerates snake_case field names", () => { const result = parseAiTaskStatus({ task_id: "task-2", result_url: "https://example.com/x.png", created_at: "2026-01-01T00:00:00Z", updated_at: "2026-01-01T00:00:00Z", }); expect(result.taskId).toBe("task-2"); expect(result.resultUrl).toBe("https://example.com/x.png"); }); it("falls back to safe defaults for missing fields", () => { const result = parseAiTaskStatus({}); expect(result.taskId).toBe(""); expect(result.type).toBe("image"); expect(result.status).toBe("failed"); expect(result.progress).toBe(0); expect(result.resultUrl).toBeNull(); expect(result.error).toBeNull(); }); it("rejects unknown status/type values", () => { const result = parseAiTaskStatus({ status: "weird", type: "audio" }); expect(result.status).toBe("failed"); expect(result.type).toBe("image"); }); it("clamps progress to [0, 100]", () => { expect(parseAiTaskStatus({ progress: 150 }).progress).toBe(100); expect(parseAiTaskStatus({ progress: -10 }).progress).toBe(0); expect(parseAiTaskStatus({ progress: "not-a-number" }).progress).toBe(0); }); it("preserves numeric conversationId and nulls others", () => { expect(parseAiTaskStatus({ conversationId: 7 }).conversationId).toBe(7); expect(parseAiTaskStatus({ conversation_id: 9 }).conversationId).toBe(9); expect(parseAiTaskStatus({ conversationId: "nope" }).conversationId).toBeNull(); expect(parseAiTaskStatus({}).conversationId).toBeNull(); }); it("returns empty string for a non-record payload", () => { const result = parseAiTaskStatus("garbage"); expect(result.taskId).toBe(""); expect(result.status).toBe("failed"); }); }); describe("parseTaskCreateResponse", () => { it("extracts taskId from a create response", () => { expect(parseTaskCreateResponse({ taskId: "abc" }).taskId).toBe("abc"); expect(parseTaskCreateResponse({ task_id: "def" }).taskId).toBe("def"); expect(parseTaskCreateResponse({ id: "ghi" }).taskId).toBe("ghi"); }); it("throws when taskId is missing", () => { expect(() => parseTaskCreateResponse({})).toThrow(); expect(() => parseTaskCreateResponse({ taskId: "" })).toThrow(); expect(() => parseTaskCreateResponse({ taskId: " " })).toThrow(); }); }); describe("parseImageTaskCreateResponse", () => { it("includes providerDebug when present", () => { const result = parseImageTaskCreateResponse({ taskId: "img-1", providerDebug: { requestedModel: "gpt-image", effectiveModel: "dall-e-3", route: ["primary", "fallback"], candidates: [{ provider: "openai", model: "dall-e-3" }], }, }); expect(result.taskId).toBe("img-1"); expect(result.providerDebug?.effectiveModel).toBe("dall-e-3"); expect(result.providerDebug?.route).toEqual(["primary", "fallback"]); expect(result.providerDebug?.candidates?.[0]?.model).toBe("dall-e-3"); }); it("omits providerDebug when absent", () => { const result = parseImageTaskCreateResponse({ taskId: "img-2" }); expect(result.taskId).toBe("img-2"); expect(result.providerDebug).toBeUndefined(); }); it("tolerates snake_case providerDebug fields", () => { const result = parseImageTaskCreateResponse({ taskId: "img-3", provider_debug: { requested_model: "x", primary_provider: "openai" }, }); expect(result.providerDebug?.requestedModel).toBe("x"); expect(result.providerDebug?.primaryProvider).toBe("openai"); }); it("throws when taskId missing even if providerDebug present", () => { expect(() => parseImageTaskCreateResponse({ providerDebug: {} })).toThrow(); }); }); describe("parseAiTaskStatusList", () => { it("parses a bare array", () => { const result = parseAiTaskStatusList([{ taskId: "a" }, { taskId: "b" }]); expect(result).toHaveLength(2); expect(result[0].taskId).toBe("a"); }); it("parses an envelope { tasks: [...] }", () => { const result = parseAiTaskStatusList({ tasks: [{ taskId: "a" }, { task_id: "b" }] }); expect(result).toHaveLength(2); expect(result[1].taskId).toBe("b"); }); it("parses an envelope { items: [...] }", () => { const result = parseAiTaskStatusList({ items: [{ taskId: "a" }] }); expect(result).toHaveLength(1); }); it("drops rows with no taskId rather than crashing", () => { const result = parseAiTaskStatusList([{ taskId: "keep" }, { status: "running" }, {}]); expect(result).toHaveLength(1); expect(result[0].taskId).toBe("keep"); }); it("returns empty array for non-array non-record payload", () => { expect(parseAiTaskStatusList(null)).toEqual([]); expect(parseAiTaskStatusList("nope")).toEqual([]); expect(parseAiTaskStatusList({})).toEqual([]); }); }); describe("parseSseTaskFrame", () => { it("parses a well-formed SSE frame", () => { const frame = parseSseTaskFrame({ taskId: "sse-1", status: "completed", progress: 100, resultUrl: "https://example.com/done.png", }); expect(frame.taskId).toBe("sse-1"); expect(frame.status).toBe("completed"); expect(frame.progress).toBe(100); expect(frame.resultUrl).toBe("https://example.com/done.png"); }); it("clamps progress and rejects unknown status", () => { const frame = parseSseTaskFrame({ taskId: "sse-2", status: "oops", progress: 999 }); expect(frame.status).toBe("failed"); expect(frame.progress).toBe(100); }); it("handles a non-object payload", () => { const frame = parseSseTaskFrame("garbage"); expect(frame.taskId).toBe(""); expect(frame.status).toBe("failed"); expect(frame.progress).toBe(0); }); });