199 lines · 7.7 KB · TypeScript
RawDownload
// @effect-diagnostics nodeBuiltinImport:offimport * as NodeFS from "node:fs";import * as NodeOS from "node:os";import * as NodePath from "node:path"; import { describe, expect, it } from "vite-plus/test"; import {  attachmentFileExtension,  createAttachmentId,  createPendingAttachmentId,  parseAttachmentUuid,  parseAttachmentFileExtension,  planAttachmentClaim,  parseThreadSegmentFromAttachmentId,  resolveAttachmentPathById,  sweepStalePendingAttachments,} from "./attachmentStore.ts"; describe("attachmentStore", () => {  it("sanitizes thread ids when creating attachment ids", () => {    const attachmentId = createAttachmentId("thread.folder/unsafe space");    expect(attachmentId).toBeTruthy();    if (!attachmentId) {      return;    }     const threadSegment = parseThreadSegmentFromAttachmentId(attachmentId);    expect(threadSegment).toBeTruthy();    expect(threadSegment).toMatch(/^[a-z0-9_-]+$/i);    expect(threadSegment).not.toContain(".");    expect(threadSegment).not.toContain("%");    expect(threadSegment).not.toContain("/");  });   it("parses exact thread segments from attachment ids without prefix collisions", () => {    const fooId = "foo-00000000-0000-4000-8000-000000000001";    const fooBarId = "foo-bar-00000000-0000-4000-8000-000000000002";     expect(parseThreadSegmentFromAttachmentId(fooId)).toBe("foo");    expect(parseThreadSegmentFromAttachmentId(fooBarId)).toBe("foo-bar");  });   it("normalizes created thread segments to lowercase", () => {    const attachmentId = createAttachmentId("Thread.Foo");    expect(attachmentId).toBeTruthy();    if (!attachmentId) {      return;    }    expect(parseThreadSegmentFromAttachmentId(attachmentId)).toBe("thread-foo");  });   it("reserves the pending attachment segment", () => {    const pendingId = createPendingAttachmentId();    expect(parseThreadSegmentFromAttachmentId(pendingId)).toBe("pending");    expect(parseAttachmentUuid(pendingId)).toMatch(/^[a-f0-9-]{36}$/);    expect(parseThreadSegmentFromAttachmentId(createAttachmentId("pending")!)).toBe("_pending");    expect(parseThreadSegmentFromAttachmentId(createAttachmentId("pending_thread")!)).toBe(      "pending_thread",    );  });   it("preserves safe file extensions in attachment ids and paths", () => {    const attachmentId = createPendingAttachmentId(".PDF");     expect(parseThreadSegmentFromAttachmentId(attachmentId)).toBe("pending");    expect(parseAttachmentUuid(attachmentId)).toMatch(/^[a-f0-9-]{36}$/);    expect(parseAttachmentFileExtension(attachmentId)).toBe("pdf");    expect(attachmentFileExtension("report.PDF")).toBe(".pdf");    expect(attachmentFileExtension("report")).toBe(".bin");    expect(attachmentFileExtension("report.extensiontoolong")).toBe(".bin");    // ".part" is the in-flight upload suffix; storing it would make the file    // look like a stale partial to the sweep.    expect(attachmentFileExtension("archive.part")).toBe(".bin");    expect(createAttachmentId("x".repeat(80), ".abcdefghij")?.length).toBeLessThanOrEqual(128);  });   it("resolves attachment path by id using the extension that exists on disk", () => {    const attachmentsDir = NodeFS.mkdtempSync(      NodePath.join(NodeOS.tmpdir(), "t3code-attachment-store-"),    );    try {      const attachmentId = "thread-1-attachment";      const pngPath = NodePath.join(attachmentsDir, `${attachmentId}.png`);      NodeFS.writeFileSync(pngPath, Buffer.from("hello"));       const resolved = resolveAttachmentPathById({        attachmentsDir,        attachmentId,      });      expect(resolved).toBe(pngPath);    } finally {      NodeFS.rmSync(attachmentsDir, { recursive: true, force: true });    }  });   it("returns null when no attachment file exists for the id", () => {    const attachmentsDir = NodeFS.mkdtempSync(      NodePath.join(NodeOS.tmpdir(), "t3code-attachment-store-"),    );    try {      const resolved = resolveAttachmentPathById({        attachmentsDir,        attachmentId: "thread-1-missing",      });      expect(resolved).toBeNull();    } finally {      NodeFS.rmSync(attachmentsDir, { recursive: true, force: true });    }  });   it("resolves generic attachments without scanning the attachment directory", () => {    const attachmentsDir = NodeFS.mkdtempSync(      NodePath.join(NodeOS.tmpdir(), "t3code-file-attachment-"),    );    try {      const attachmentId = "thread-1-00000000-0000-4000-8000-000000000001-zip";      const archivePath = NodePath.join(attachmentsDir, `${attachmentId}.zip`);      NodeFS.writeFileSync(archivePath, Buffer.from("archive"));       expect(resolveAttachmentPathById({ attachmentsDir, attachmentId })).toBe(archivePath);    } finally {      NodeFS.rmSync(attachmentsDir, { recursive: true, force: true });    }  });   it("plans pending attachment claims with direct filename lookups", () => {    const attachmentsDir = NodeFS.mkdtempSync(      NodePath.join(NodeOS.tmpdir(), "t3code-attachment-claim-"),    );    try {      const uuid = "00000000-0000-4000-8000-000000000001";      const pendingPath = NodePath.join(attachmentsDir, `pending-${uuid}.png`);      NodeFS.writeFileSync(pendingPath, Buffer.from("pixels"));       const claim = planAttachmentClaim({        attachmentsDir,        threadId: "thread-1",        attachmentId: `pending-${uuid}`,      });      expect(claim).toMatchObject({        ok: true,        currentPath: pendingPath,      });      if (!claim.ok) {        return;      }      expect(parseThreadSegmentFromAttachmentId(claim.finalId)).toBe("thread-1");      expect(parseAttachmentUuid(claim.finalId)).not.toBe(uuid);      expect(claim.finalPath).toBe(NodePath.join(attachmentsDir, `${claim.finalId}.png`));    } finally {      NodeFS.rmSync(attachmentsDir, { recursive: true, force: true });    }  });   it("rejects thread-owned attachments even when thread segments collide", () => {    const attachmentsDir = NodeFS.mkdtempSync(      NodePath.join(NodeOS.tmpdir(), "t3code-attachment-ownership-"),    );    try {      const attachmentId = "a-b-00000000-0000-4000-8000-000000000003";      NodeFS.writeFileSync(NodePath.join(attachmentsDir, `${attachmentId}.png`), "pixels");       expect(planAttachmentClaim({ attachmentsDir, threadId: "a b", attachmentId })).toEqual({        ok: false,        reason: "attachment must be a pending upload",      });    } finally {      NodeFS.rmSync(attachmentsDir, { recursive: true, force: true });    }  });   it("removes expired pending and partial files without touching thread attachments", () => {    const attachmentsDir = NodeFS.mkdtempSync(      NodePath.join(NodeOS.tmpdir(), "t3code-attachment-sweep-"),    );    try {      const now = 1_800_000_000_000;      const oldTimeSeconds = (now - 2 * 24 * 60 * 60 * 1000) / 1000;      const uuid = "00000000-0000-4000-8000-000000000002";      const pendingPath = NodePath.join(attachmentsDir, `pending-${uuid}.png`);      const pendingFilePath = NodePath.join(attachmentsDir, `pending-${uuid}-pdf.pdf`);      const threadPath = NodePath.join(attachmentsDir, `thread-1-${uuid}.png`);      const partialPath = NodePath.join(attachmentsDir, `${uuid}.part`);      for (const filePath of [pendingPath, pendingFilePath, threadPath, partialPath]) {        NodeFS.writeFileSync(filePath, Buffer.from("pixels"));        NodeFS.utimesSync(filePath, oldTimeSeconds, oldTimeSeconds);      }       expect(sweepStalePendingAttachments({ attachmentsDir, nowMs: now })).toEqual({ deleted: 3 });      expect(NodeFS.existsSync(pendingPath)).toBe(false);      expect(NodeFS.existsSync(pendingFilePath)).toBe(false);      expect(NodeFS.existsSync(partialPath)).toBe(false);      expect(NodeFS.existsSync(threadPath)).toBe(true);    } finally {      NodeFS.rmSync(attachmentsDir, { recursive: true, force: true });    }  });});