import { describe, expect, it, vi } from "vitest"; import { mkdirSync, writeFileSync } from "node:fs"; import { resolve } from "node:path"; import { exportResultCopy, runExportFlow } from "../../apps/web/src/export-flow.js"; function writeEvidence(file: string, value: unknown) { const root = process.env.DADA_EVIDENCE_DIR_LATEST_EXPORTS; if (!root) return; const directory = resolve(root, "TDD-WP2-EXP-001-result-matrix"); mkdirSync(directory, { recursive: true }); writeFileSync(resolve(directory, file), `${JSON.stringify(value, null, 2)}\n`); } describe("TDD-WP2-EXP-001-result-matrix", () => { it("uses the exact same Blob for download and latest persistence", async () => { const blob = new Blob(["same-export"], { type: "image/png" }); const download = vi.fn(async (_value: Blob) => undefined); const persist = vi.fn(async (_value: Blob) => undefined); const result = await runExportFlow({ compose: async () => blob, download, persist }); expect(result.status).toBe("downloaded_and_saved"); expect(download).toHaveBeenCalledWith(blob); expect(persist).toHaveBeenCalledWith(blob); expect(download.mock.calls[0][0]).toBe(persist.mock.calls[0][0]); expect(exportResultCopy[result.status]).toBe("已下载并保存为最新成品"); }); it("preserves the composed Blob for retry when browser download fails", async () => { const blob = new Blob(["retry-export"], { type: "image/jpeg" }); const persist = vi.fn(); const result = await runExportFlow({ compose: async () => blob, download: async () => { throw new Error("download_failed"); }, persist, }); expect(result.status).toBe("download_failed"); expect(result.blob).toBe(blob); expect(persist).not.toHaveBeenCalled(); expect(exportResultCopy[result.status]).toBe("浏览器下载失败,可重新下载同一次合成结果"); }); it("does not turn a successful local download into a false double success", async () => { const result = await runExportFlow({ compose: async () => new Blob(["local-only"], { type: "image/png" }), download: async () => undefined, persist: async () => { throw new Error("latest_failed"); }, }); expect(result.status).toBe("downloaded_not_saved"); expect(exportResultCopy[result.status]).toBe("文件已下载,但未保存为最新成品"); }); it("does not download or persist after composition failure", async () => { const download = vi.fn(); const persist = vi.fn(); const result = await runExportFlow({ compose: async () => { throw new Error("composition_failed"); }, download, persist, }); expect(result).toEqual({ blob: null, status: "composition_failed" }); expect(download).not.toHaveBeenCalled(); expect(persist).not.toHaveBeenCalled(); expect(exportResultCopy[result.status]).toBe("合成失败,请返回编辑器检查"); writeEvidence("network-timeline.json", { composition_failed: ["compose:failed", "download:not_started", "persist:not_started"], download_failed: ["compose:succeeded", "download:failed", "persist:not_started"], downloaded_and_saved: ["compose:succeeded", "download:succeeded", "persist:succeeded"], downloaded_not_saved: ["compose:succeeded", "download:succeeded", "persist:failed"], }); writeEvidence("response.json", { copy: exportResultCopy, statuses: Object.keys(exportResultCopy) }); writeEvidence("db-diff.json", { browser_persistent_blob_writes: 0, latest_updates_after_backend_failure: 0 }); }); });