authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-03-01 19:18:41-08:00
committergravatar for jmarminge@gmail.comJulius Marminge <jmarminge@gmail.com> 2026-03-01 20:06:21-08:00
log58b932ffb8f20566c2c5ac58c252a911c7e80693
tree69efaaead2c64e3fe4d68edaefda3d61d64484ce
parentd58af33fba73936659158ba2167a00475889d1e5

Normalize attachment thread segments to lowercase

- Lowercase sanitized thread segments when creating attachment IDs - Add coverage for mixed-case thread IDs in attachment store and projection pipeline tests

3 files changed, 12 insertions(+), 2 deletions(-)

apps/server/src/attachmentStore.test.ts+9
......@@ -34,6 +34,15 @@ describe("attachmentStore", () => {
3434 expect(parseThreadSegmentFromAttachmentId(fooBarId)).toBe("foo-bar");
3535 });
3636
37 it("normalizes created thread segments to lowercase", () => {
38 const attachmentId = createAttachmentId("Thread.Foo");
39 expect(attachmentId).toBeTruthy();
40 if (!attachmentId) {
41 return;
42 }
43 expect(parseThreadSegmentFromAttachmentId(attachmentId)).toBe("thread-foo");
44 });
45
3746 it("resolves attachment path by id using the extension that exists on disk", () => {
3847 const stateDir = fs.mkdtempSync(path.join(os.tmpdir(), "t3code-attachment-store-"));
3948 try {
apps/server/src/attachmentStore.ts+1
......@@ -23,6 +23,7 @@ const ATTACHMENT_ID_PATTERN = new RegExp(
2323export function toSafeThreadAttachmentSegment(threadId: string): string | null {
2424 const segment = threadId
2525 .trim()
26 .toLowerCase()
2627 .replace(/[^a-z0-9_-]+/gi, "-")
2728 .replace(/-+/g, "-")
2829 .replace(/^[-_]+|[-_]+$/g, "")
apps/server/src/orchestration/Layers/ProjectionPipeline.test.ts+2-2
......@@ -733,7 +733,7 @@ projectionLayer("OrchestrationProjectionPipeline", (it) => {
733733 const projectionPipeline = yield* OrchestrationProjectionPipeline;
734734 const eventStore = yield* OrchestrationEventStore;
735735 const now = new Date().toISOString();
736 const threadId = ThreadId.makeUnsafe("thread revert.files");
736 const threadId = ThreadId.makeUnsafe("Thread Revert.Files");
737737 const keepAttachmentId = "thread-revert-files-00000000-0000-4000-8000-000000000001";
738738 const removeAttachmentId = "thread-revert-files-00000000-0000-4000-8000-000000000002";
739739 const otherThreadAttachmentId =
......@@ -948,7 +948,7 @@ projectionLayer("OrchestrationProjectionPipeline", (it) => {
948948 const projectionPipeline = yield* OrchestrationProjectionPipeline;
949949 const eventStore = yield* OrchestrationEventStore;
950950 const now = new Date().toISOString();
951 const threadId = ThreadId.makeUnsafe("thread delete.files");
951 const threadId = ThreadId.makeUnsafe("Thread Delete.Files");
952952 const attachmentId = "thread-delete-files-00000000-0000-4000-8000-000000000001";
953953 const otherThreadAttachmentId =
954954 "thread-delete-files-extra-00000000-0000-4000-8000-000000000002";