// @vitest-environment node import { describe, expect, it } from "vitest"; import { attachPendingAttachments, createPendingAttachment, getOwnedAttachment, } from "@/lib/db/attachments"; const gateRow = { id: "attachment-1", original_name: "brief.txt", content_type: "text/plain", byte_size: "5", gate_file_id: "file-opaque_123", object_key: null, }; describe("attachment repository", () => { it("creates pending metadata with an opaque Gate file ID and no object key", async () => { const database = new RecordingDatabase([[gateRow]]); await expect( createPendingAttachment(database, "user-1", { name: "brief.txt", contentType: "text/plain", size: 5, gateFileId: "file-opaque_123", }), ).resolves.toEqual({ id: "attachment-1", gateFileId: "file-opaque_123", objectKey: null, name: "brief.txt", contentType: "text/plain", size: 5, }); expect(database.queries[0].text).toContain("gate_file_id"); expect(database.queries[0].values).toEqual([ "user-1", "brief.txt", "text/plain", 5, "file-opaque_123", ]); }); it("reads Gate and legacy rows only through the ownership predicate", async () => { const legacyRow = { ...gateRow, gate_file_id: null, object_key: "legacy/private-key" }; const database = new RecordingDatabase([[legacyRow]]); await expect(getOwnedAttachment(database, "user-1", "attachment-1")).resolves.toMatchObject({ gateFileId: null, objectKey: "legacy/private-key", }); expect(database.queries[0].text).toContain("id = $1 AND user_id = $2"); expect(database.queries[0].values).toEqual(["attachment-1", "user-1"]); }); it("links only distinct pending Gate attachment IDs for the owner", async () => { const database = new RecordingDatabase([[gateRow]]); await expect( attachPendingAttachments(database, "user-1", "message-1", ["attachment-1"]), ).resolves.toEqual([expect.objectContaining({ gateFileId: "file-opaque_123" })]); expect(database.queries[0]).toMatchObject({ values: [["attachment-1"], "user-1", "message-1"], }); expect(database.queries[0].text).toContain("user_id = $2"); expect(database.queries[0].text).toContain("state = 'pending'"); }); }); class RecordingDatabase { queries: Array<{ text: string; values?: readonly unknown[] }> = []; constructor(private readonly results: unknown[][]) {} async query(text: string, values?: readonly unknown[]) { this.queries.push({ text, values }); return { rows: (this.results.shift() ?? []) as T[] }; } }