diff --git a/backend/src/modules/note-shares/repository.test.ts b/backend/src/modules/note-shares/repository.test.ts new file mode 100644 index 0000000..dceac3f --- /dev/null +++ b/backend/src/modules/note-shares/repository.test.ts @@ -0,0 +1,54 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +const findManyMock = vi.fn(); + +vi.mock('../../lib/datastore.js', () => ({ + getCollection: vi.fn(() => ({ + findMany: findManyMock, + })), +})); + +import { findShareByToken } from './repository.js'; + +function shareDoc(overrides: { expiresAt?: string } = {}) { + return { + id: 's1', + productId: 'notelett', + workspaceId: 'w1', + userId: 'u1', + noteId: 'n1', + shareToken: 'tok', + createdAt: new Date().toISOString(), + ...overrides, + }; +} + +describe('findShareByToken', () => { + beforeEach(() => { + findManyMock.mockReset(); + }); + + it('returns null when expiresAt is in the past', async () => { + const past = new Date(Date.now() - 86_400_000).toISOString(); + findManyMock.mockResolvedValueOnce([shareDoc({ expiresAt: past })]); + await expect(findShareByToken('tok', 'notelett')).resolves.toBeNull(); + }); + + it('returns share when expiresAt is in the future', async () => { + const future = new Date(Date.now() + 86_400_000).toISOString(); + const doc = shareDoc({ expiresAt: future }); + findManyMock.mockResolvedValueOnce([doc]); + await expect(findShareByToken('tok', 'notelett')).resolves.toEqual(doc); + }); + + it('returns share when expiresAt is absent', async () => { + const doc = shareDoc(); + findManyMock.mockResolvedValueOnce([doc]); + await expect(findShareByToken('tok', 'notelett')).resolves.toEqual(doc); + }); + + it('returns null when no row matches', async () => { + findManyMock.mockResolvedValueOnce([]); + await expect(findShareByToken('tok', 'notelett')).resolves.toBeNull(); + }); +}); diff --git a/backend/src/modules/note-shares/repository.ts b/backend/src/modules/note-shares/repository.ts index 69aba65..56597de 100644 --- a/backend/src/modules/note-shares/repository.ts +++ b/backend/src/modules/note-shares/repository.ts @@ -16,7 +16,15 @@ export async function findShareByToken( ): Promise { const filter: FilterMap = { shareToken, productId }; const items = await collection().findMany({ filter, limit: 1 }); - return items[0] ?? null; + const share = items[0] ?? null; + if (!share) return null; + if (share.expiresAt) { + const exp = Date.parse(share.expiresAt); + if (!Number.isNaN(exp) && exp < Date.now()) { + return null; + } + } + return share; } export async function listSharesForNote(