// @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 }); } });});
// @effect-diagnostics nodeBuiltinImport:off
import * 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 });
}
});
});