import { describe, it, expect } from 'vitest'; import { relativeTime, parseNotificationEvent } from '$lib/utils/notifications'; const rtf = new Intl.RelativeTimeFormat(undefined, { numeric: 'auto' }); function msAgo(ms: number, now: Date): string { return new Date(now.getTime() - ms).toISOString(); } describe('relativeTime', () => { const now = new Date('2024-06-15T12:00:00.000Z'); it('should use minute bucket for timestamps under 60 seconds ago', () => { const ts = msAgo(30_000, now); expect(relativeTime(ts, now)).toBe(rtf.format(0, 'minute')); }); it('should use minute bucket for exactly 59 minutes ago', () => { const ts = msAgo(59 * 60_000, now); expect(relativeTime(ts, now)).toBe(rtf.format(-59, 'minute')); }); it('should use minute bucket for exactly 1 minute ago', () => { const ts = msAgo(60_000, now); expect(relativeTime(ts, now)).toBe(rtf.format(-1, 'minute')); }); it('should use hour bucket for exactly 1 hour ago', () => { const ts = msAgo(60 * 60_000, now); expect(relativeTime(ts, now)).toBe(rtf.format(-1, 'hour')); }); it('should use hour bucket for 23 hours ago', () => { const ts = msAgo(23 * 60 * 60_000, now); expect(relativeTime(ts, now)).toBe(rtf.format(-23, 'hour')); }); it('should use day bucket for exactly 24 hours ago', () => { const ts = msAgo(24 * 60 * 60_000, now); expect(relativeTime(ts, now)).toBe(rtf.format(-1, 'day')); }); it('should use day bucket for 6 days ago', () => { const ts = msAgo(6 * 24 * 60 * 60_000, now); expect(relativeTime(ts, now)).toBe(rtf.format(-6, 'day')); }); it('should default now to current time when omitted', () => { // Just verify it returns a non-empty string — exact value depends on runtime clock const ts = new Date(Date.now() - 5 * 60_000).toISOString(); expect(relativeTime(ts)).toBeTruthy(); }); }); describe('parseNotificationEvent', () => { const valid = { id: '00000000-0000-0000-0000-000000000001', documentId: '00000000-0000-0000-0000-000000000002', actorName: 'Anna Müller', type: 'MENTION', referenceId: '00000000-0000-0000-0000-000000000003', annotationId: null, read: false, createdAt: '2024-06-15T10:00:00', documentTitle: 'Geburtsurkunde Opa Karl' }; it('should return parsed object for a valid payload', () => { const result = parseNotificationEvent(JSON.stringify(valid)); expect(result).not.toBeNull(); expect(result?.id).toBe(valid.id); expect(result?.actorName).toBe('Anna Müller'); }); it('should return null for invalid JSON', () => { expect(parseNotificationEvent('not-json')).toBeNull(); }); it('should return null when id is missing', () => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { id, ...noId } = valid; expect(parseNotificationEvent(JSON.stringify(noId))).toBeNull(); }); it('should return null when documentId is missing', () => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { documentId, ...noDocId } = valid; expect(parseNotificationEvent(JSON.stringify(noDocId))).toBeNull(); }); it('should return null when actorName is missing', () => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { actorName, ...noActor } = valid; expect(parseNotificationEvent(JSON.stringify(noActor))).toBeNull(); }); it('should return null for unknown notification type', () => { expect(parseNotificationEvent(JSON.stringify({ ...valid, type: 'UNKNOWN' }))).toBeNull(); }); it('should accept REPLY as a valid type', () => { const result = parseNotificationEvent(JSON.stringify({ ...valid, type: 'REPLY' })); expect(result).not.toBeNull(); expect(result?.type).toBe('REPLY'); }); });