import { describe, expect, test } from "bun:test"; import { appendImagesDeduped, imageFileKey, } from "@/lib/multi-image"; // --------------------------------------------------------------------------- // Multi-image helpers – behavioral tests // // These functions are pure; they own the "append + dedup" contract. // Tests describe what the system DOES, not how it's implemented. // --------------------------------------------------------------------------- describe("imageFileKey – stable identity for dedup", () => { test("returns a string combining name and size", () => { const file = new File(["hello"], "flyer.png", { type: "image/png", lastModified: 123, }); const key = imageFileKey(file); expect(typeof key).toBe("string"); expect(key).toContain("flyer.png"); expect(key).toContain(String(file.size)); expect(key).toContain(String(file.lastModified)); }); test("two files with the same name, size, and lastModified produce the same key", () => { const a = new File(["hello"], "a.png", { type: "image/png", lastModified: 123, }); const b = new File(["hello"], "a.png", { type: "image/png", lastModified: 123, }); expect(imageFileKey(a)).toBe(imageFileKey(b)); }); test("two files with the same name but different content produce different keys", () => { const a = new File(["hello"], "a.png", { type: "image/png", lastModified: 123, }); const b = new File(["hello world"], "a.png", { type: "image/png", lastModified: 123, }); expect(imageFileKey(a)).not.toBe(imageFileKey(b)); }); test("two files with different names but same content produce different keys", () => { const a = new File(["hello"], "a.png", { type: "image/png", lastModified: 123, }); const b = new File(["hello"], "b.png", { type: "image/png", lastModified: 123, }); expect(imageFileKey(a)).not.toBe(imageFileKey(b)); }); test("clipboard fallback files with the same fallback name and size stay distinct when lastModified differs", () => { const a = new File(["abcd"], "clipboard-image", { type: "image/png", lastModified: 100, }); const b = new File(["wxyz"], "clipboard-image", { type: "image/png", lastModified: 101, }); expect(a.size).toBe(b.size); expect(imageFileKey(a)).not.toBe(imageFileKey(b)); }); }); describe("appendImagesDeduped – append with deduplication", () => { const makeFile = ( name: string, content = "data", lastModified = 123, ) => new File([content], name, { type: "image/png", lastModified }); test("appends new files to an empty list", () => { const result = appendImagesDeduped([], [makeFile("a.png")]); expect(result).toHaveLength(1); expect(result[0].name).toBe("a.png"); }); test("appends new files to an existing list", () => { const existing = [makeFile("a.png")]; const incoming = [makeFile("b.png")]; const result = appendImagesDeduped(existing, incoming); expect(result).toHaveLength(2); expect(result.map((f) => f.name)).toEqual(["a.png", "b.png"]); }); test("silently ignores incoming files that are exact duplicates (same name+size)", () => { const existing = [makeFile("a.png")]; const incoming = [makeFile("a.png")]; // identical name + content = same size const result = appendImagesDeduped(existing, incoming); expect(result).toHaveLength(1); expect(result[0].name).toBe("a.png"); }); test("appends non-duplicate files even when some duplicates are in the batch", () => { const existing = [makeFile("a.png")]; const incoming = [makeFile("a.png"), makeFile("b.png")]; const result = appendImagesDeduped(existing, incoming); expect(result).toHaveLength(2); expect(result.map((f) => f.name)).toEqual(["a.png", "b.png"]); }); test("preserves existing list order — new files are appended at the end", () => { const existing = [makeFile("first.png"), makeFile("second.png")]; const incoming = [makeFile("third.png")]; const result = appendImagesDeduped(existing, incoming); expect(result.map((f) => f.name)).toEqual([ "first.png", "second.png", "third.png", ]); }); test("returns a new array (does not mutate the existing list)", () => { const existing = [makeFile("a.png")]; const incoming = [makeFile("b.png")]; const result = appendImagesDeduped(existing, incoming); expect(result).not.toBe(existing); // new reference expect(existing).toHaveLength(1); // original untouched }); test("handles multiple incoming files with internal duplicates", () => { // Two identical files in the same incoming batch const existing: File[] = []; const incoming = [makeFile("a.png"), makeFile("a.png"), makeFile("b.png")]; const result = appendImagesDeduped(existing, incoming); expect(result).toHaveLength(2); expect(result.map((f) => f.name)).toEqual(["a.png", "b.png"]); }); test("keeps distinct clipboard fallback files that share the same name and byte size", () => { const existing: File[] = []; const incoming = [ makeFile("clipboard-image", "abcd", 100), makeFile("clipboard-image", "wxyz", 101), ]; const result = appendImagesDeduped(existing, incoming); expect(result).toHaveLength(2); expect(result[0].lastModified).toBe(100); expect(result[1].lastModified).toBe(101); }); });