Files
connectai/tests/calendarApi.test.ts
T

132 lines
5.0 KiB
TypeScript

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