Golden-master tests pinning the CURRENT behavior of high-value modules that had no coverage — integration seams, security helpers, two API route handlers, and complex pure utils. 111 tests across 12 files. New tests: - auth-crypto / session-cookie: AES-256-GCM session encryption roundtrip, tamper/version/missing-secret handling; cookie-slot naming. - unified-mailbox: multi-account fan-out, sort, totals, per-account error isolation, personal-vs-shared JMAP target resolution, counts/roles. - account-state-manager: snapshot/restore across the six real Zustand stores; clearAllStores reset shape; evict. - mdn: RFC 5322 MDN assembly (CRLF, RFC2047, base64 wrap, headers). - tnef: winmail.dat binary parsing from hand-built fixtures. - download-filename / subject-prefix / birthday-calendar / eml-import: filename templating, multilingual prefix stripping, birthday event generation, .eml/.zip import. - webdav / caldav-discover route handlers: auth guards, path validation, upstream URL construction, candidate probing. - helpers/factories.ts: shared makeEmail/makeMailbox/makeFakeJmapClient. Tests follow the repo's existing patterns (route-import, fake IJMAPClient, fetch spy, real store singletons). Where current behavior looks buggy it is pinned and flagged with a // CHARACTERISATION: comment (see PR for the suspected-bugs list); no production code is changed.
91 lines
3.5 KiB
TypeScript
91 lines
3.5 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import type { ContactCard } from '@/lib/jmap/types';
|
|
import {
|
|
createBirthdayCalendar,
|
|
generateBirthdayEvents,
|
|
BIRTHDAY_CALENDAR_ID,
|
|
BIRTHDAY_CALENDAR_COLOR,
|
|
} from '@/lib/birthday-calendar';
|
|
|
|
const contact = (over: Record<string, unknown> = {}): ContactCard =>
|
|
({
|
|
id: 'c1',
|
|
'@type': 'Card',
|
|
name: { full: 'Alice Smith' },
|
|
anniversaries: { b1: { '@type': 'Anniversary', kind: 'birth', date: '1990-05-15' } },
|
|
...over,
|
|
} as unknown as ContactCard);
|
|
|
|
describe('createBirthdayCalendar', () => {
|
|
it('returns the virtual calendar with defaults', () => {
|
|
const cal = createBirthdayCalendar();
|
|
expect(cal).toMatchObject({
|
|
id: BIRTHDAY_CALENDAR_ID,
|
|
name: 'Birthdays',
|
|
color: BIRTHDAY_CALENDAR_COLOR,
|
|
isSubscribed: true,
|
|
myRights: { mayReadItems: true, mayWriteAll: false, mayDelete: false },
|
|
});
|
|
});
|
|
|
|
it('honours name/color overrides', () => {
|
|
expect(createBirthdayCalendar('My BDays', '#fff')).toMatchObject({ name: 'My BDays', color: '#fff' });
|
|
});
|
|
});
|
|
|
|
describe('generateBirthdayEvents', () => {
|
|
it('emits one event per year in range, with age and stable ids', () => {
|
|
const events = generateBirthdayEvents([contact()], '2020-01-01', '2022-12-31');
|
|
expect(events.map((e) => e.id)).toEqual([
|
|
'birthday-c1-b1-2020',
|
|
'birthday-c1-b1-2021',
|
|
'birthday-c1-b1-2022',
|
|
]);
|
|
expect(events[0]).toMatchObject({
|
|
uid: 'birthday-c1-b1',
|
|
title: '🎂 Alice Smith (30)',
|
|
start: '2020-05-15T00:00:00',
|
|
calendarIds: { [BIRTHDAY_CALENDAR_ID]: true },
|
|
showWithoutTime: true,
|
|
});
|
|
});
|
|
|
|
it('omits the age when the birthday has no year (partial date)', () => {
|
|
const c = contact({ anniversaries: { b1: { '@type': 'Anniversary', kind: 'birth', date: '--05-15' } } });
|
|
const events = generateBirthdayEvents([c], '2021-01-01', '2021-12-31');
|
|
expect(events).toHaveLength(1);
|
|
expect(events[0].title).toBe('🎂 Alice Smith');
|
|
});
|
|
|
|
it('parses a Timestamp anniversary date', () => {
|
|
const c = contact({ anniversaries: { b1: { '@type': 'Anniversary', kind: 'birth', date: { '@type': 'Timestamp', utc: '1985-03-10T00:00:00Z' } } } });
|
|
const events = generateBirthdayEvents([c], '2021-01-01', '2021-12-31');
|
|
expect(events[0].start).toBe('2021-03-10T00:00:00');
|
|
});
|
|
|
|
it('parses a PartialDate anniversary date', () => {
|
|
const c = contact({ anniversaries: { b1: { '@type': 'Anniversary', kind: 'birth', date: { month: 7, day: 4 } } } });
|
|
const events = generateBirthdayEvents([c], '2021-01-01', '2021-12-31');
|
|
expect(events[0].start).toBe('2021-07-04T00:00:00');
|
|
});
|
|
|
|
it('clamps Feb 29 to Feb 28 in a non-leap year', () => {
|
|
const c = contact({ anniversaries: { b1: { '@type': 'Anniversary', kind: 'birth', date: '2000-02-29' } } });
|
|
const events = generateBirthdayEvents([c], '2021-01-01', '2021-12-31');
|
|
expect(events[0].start).toBe('2021-02-28T00:00:00');
|
|
});
|
|
|
|
it('excludes occurrences outside the range', () => {
|
|
expect(generateBirthdayEvents([contact()], '2021-01-01', '2021-02-28')).toEqual([]); // May birthday
|
|
});
|
|
|
|
it('returns [] for an invalid range', () => {
|
|
expect(generateBirthdayEvents([contact()], 'not-a-date', '2021-12-31')).toEqual([]);
|
|
});
|
|
|
|
it('skips non-birth anniversaries', () => {
|
|
const c = contact({ anniversaries: { w1: { '@type': 'Anniversary', kind: 'wedding', date: '2010-06-01' } } });
|
|
expect(generateBirthdayEvents([c], '2021-01-01', '2021-12-31')).toEqual([]);
|
|
});
|
|
});
|