fix(note-shares): treat expired shares as not found

findShareByToken now returns null when expiresAt is in the past, aligning the public share API with stored metadata.

Add unit tests for expiry, future expiry, missing expiry, and no match.

Made-with: Cursor
This commit is contained in:
Saravana Achu Mac 2026-03-31 13:05:30 -07:00
parent 5e3e374d3a
commit 8d8540e320
2 changed files with 63 additions and 1 deletions

View File

@ -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();
});
});

View File

@ -16,7 +16,15 @@ export async function findShareByToken(
): Promise<NoteShareDoc | null> {
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(