import { _buildEventBody, _addMinutesIso, _addDaysDate, } from '../src/features/calendar/calendarApi'; import { _parseCalEventAttrs } from '../src/agent'; describe('_addMinutesIso', () => { test('adds minutes to local ISO without timezone, preserves no-tz format', () => { const out = _addMinutesIso('2026-05-21T14:00', 60); expect(out).toBe('2026-05-21T15:00:00'); }); test('handles ISO with seconds', () => { const out = _addMinutesIso('2026-05-21T14:30:00', 30); expect(out).toBe('2026-05-21T15:00:00'); }); test('handles UTC marker Z', () => { const out = _addMinutesIso('2026-05-21T14:00:00Z', 60); expect(out).toBe('2026-05-21T15:00:00.000Z'); }); test('returns null on malformed input', () => { expect(_addMinutesIso('not-a-date', 30)).toBeNull(); expect(_addMinutesIso('', 30)).toBeNull(); }); test('handles day-rollover correctly', () => { const out = _addMinutesIso('2026-05-21T23:30', 60); expect(out).toBe('2026-05-22T00:30:00'); }); }); describe('_addDaysDate', () => { test('adds days to YYYY-MM-DD', () => { expect(_addDaysDate('2026-05-21', 1)).toBe('2026-05-22'); expect(_addDaysDate('2026-12-31', 1)).toBe('2027-01-01'); }); }); describe('_buildEventBody', () => { test('rejects empty title or start', () => { const r1 = _buildEventBody({ title: '', start: '2026-05-21T14:00' }, 60); expect(r1.ok).toBe(false); const r2 = _buildEventBody({ title: 'x', start: '' }, 60); expect(r2.ok).toBe(false); }); test('builds basic timed event with default duration', () => { const r = _buildEventBody({ title: '회의', start: '2026-05-21T14:00' }, 60); if (!r.ok) throw new Error('expected ok'); expect(r.event.summary).toBe('회의'); expect(r.event.start.dateTime).toBe('2026-05-21T14:00'); expect(r.event.end.dateTime).toBe('2026-05-21T15:00:00'); // 로컬 timezone 자동 포함 — Intl 결과 (값은 시스템 의존이라 존재만 확인) expect(r.event.start.timeZone).toBeTruthy(); expect(r.event.reminders.overrides).toHaveLength(2); }); test('respects explicit duration', () => { const r = _buildEventBody({ title: '미팅', start: '2026-05-21T14:00', durationMinutes: 90, }, 60); if (!r.ok) throw new Error('expected ok'); expect(r.event.end.dateTime).toBe('2026-05-21T15:30:00'); }); test('respects explicit end over duration', () => { const r = _buildEventBody({ title: '미팅', start: '2026-05-21T14:00', end: '2026-05-21T16:00', durationMinutes: 30, }, 60); if (!r.ok) throw new Error('expected ok'); expect(r.event.end.dateTime).toBe('2026-05-21T16:00'); }); test('builds all-day event with exclusive end (+1 day)', () => { const r = _buildEventBody({ title: '생일', start: '2026-06-15', allDay: true, }, 60); if (!r.ok) throw new Error('expected ok'); expect(r.event.start.date).toBe('2026-06-15'); expect(r.event.end.date).toBe('2026-06-16'); expect(r.event.start.dateTime).toBeUndefined(); }); test('omits timeZone when input has explicit offset', () => { const r = _buildEventBody({ title: '회의', start: '2026-05-21T14:00:00+09:00', }, 60); if (!r.ok) throw new Error('expected ok'); expect(r.event.start.timeZone).toBeUndefined(); }); }); describe('_parseCalEventAttrs', () => { test('parses double-quoted attrs', () => { const a = _parseCalEventAttrs(' title="팀 미팅" start="2026-05-21T14:00" duration="60" '); expect(a.title).toBe('팀 미팅'); expect(a.start).toBe('2026-05-21T14:00'); expect(a.duration).toBe(60); }); test('parses single-quoted + bare attrs', () => { const a = _parseCalEventAttrs(`title='간단' start=2026-05-21T14:00 duration=30`); expect(a.title).toBe('간단'); expect(a.start).toBe('2026-05-21T14:00'); expect(a.duration).toBe(30); }); test('parses all_day variants', () => { const a1 = _parseCalEventAttrs('title="x" start="2026-05-21" all_day="true"'); expect(a1.allDay).toBe(true); const a2 = _parseCalEventAttrs('title="x" start="2026-05-21" allday="1"'); expect(a2.allDay).toBe(true); const a3 = _parseCalEventAttrs('title="x" start="2026-05-21" all-day="yes"'); expect(a3.allDay).toBe(true); const a4 = _parseCalEventAttrs('title="x" start="2026-05-21" all_day="false"'); expect(a4.allDay).toBe(false); }); test('ignores invalid duration value', () => { const a = _parseCalEventAttrs('title="x" start="t" duration="abc"'); expect(a.duration).toBeUndefined(); }); test('returns empty when attrs are missing', () => { expect(_parseCalEventAttrs('')).toEqual({}); expect(_parseCalEventAttrs(' ')).toEqual({}); }); });