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:
parent
5e3e374d3a
commit
8d8540e320
54
backend/src/modules/note-shares/repository.test.ts
Normal file
54
backend/src/modules/note-shares/repository.test.ts
Normal 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();
|
||||
});
|
||||
});
|
||||
@ -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(
|
||||
|
||||
Loading…
Reference in New Issue
Block a user