import 'package:flutter_test/flutter_test.dart'; import 'package:friendly_map/modules/events/events.dart'; void main() { final today = DateTime(2026, 10, 26); group('일정 계산', () { test('올해 지났으면 내년, 오늘이면 오늘', () { const b = FamilyEvent(personId: 'a', kind: EventKind.birthday, month: 3, day: 5, year: 1960); expect(b.nextFrom(today), DateTime(2027, 3, 5)); const t = FamilyEvent(personId: 'a', kind: EventKind.birthday, month: 10, day: 26); expect(t.nextFrom(today), DateTime(2026, 10, 26)); }); test('2월 29일은 평년엔 28일', () { const e = FamilyEvent(personId: 'a', kind: EventKind.birthday, month: 2, day: 29); expect(e.occurrenceIn(2027), DateTime(2027, 2, 28)); expect(e.occurrenceIn(2028), DateTime(2028, 2, 29)); }); test('다가오는 일정은 가까운 순, 범위 밖은 제외, 나이·주기 계산', () { final list = upcoming(const [ FamilyEvent(personId: 'far', kind: EventKind.birthday, month: 1, day: 1), FamilyEvent(personId: 'b', kind: EventKind.memorial, month: 11, day: 10, year: 2016), FamilyEvent(personId: 'a', kind: EventKind.birthday, month: 10, day: 28, year: 1990), ], today, withinDays: 30); expect(list.map((e) => e.event.personId).toList(), ['a', 'b']); expect(list[0].daysLeft, 2); expect(list[0].years, 36); expect(list[1].years, 10); expect(dayWord(0), '오늘'); expect(dayWord(1), '내일'); expect(dayWord(5), '5일 후'); }); test('라벨', () { const m = FamilyEvent(personId: 'a', kind: EventKind.memorial, month: 1, day: 1); expect(m.label('할아버지'), '할아버지 기일'); }); }); group('알림 일정', () { test('설정 글자 해석: 1일 전, 2일 전, 중복·잘못된 칸 정리', () { expect(parseLeadDays('1일 전, 2일 전'), [2, 1]); expect(parseLeadDays('7, 1, 1, x, 0'), [7, 1, 0]); expect(parseLeadDays(null), isEmpty); expect(leadDaysText([7, 1]), '7, 1'); }); test('이미 지난 알림은 빼고 시각순', () { const e = FamilyEvent(personId: 'a', kind: EventKind.birthday, month: 10, day: 28); final now = DateTime(2026, 10, 26, 10); // 9시는 지남 final plans = planReminders([e], now, [2, 1, 0], (_) => '엄마'); // 2일 전(10/26 09:00)은 지남 → 1일 전, 당일만 expect(plans.map((p) => p.leadDays).toList(), [1, 0]); expect(plans.first.fireAt, DateTime(2026, 10, 27, 9)); expect(plans.first.text, '내일 엄마 생일입니다'); expect(plans.last.text, '오늘 엄마 생일입니다'); }); test('너무 먼 일정은 제외', () { const e = FamilyEvent(personId: 'a', kind: EventKind.birthday, month: 6, day: 1); expect(planReminders([e], today, [1], (_) => 'x'), isEmpty); }); }); group('통화 요약', () { test('기록 없음', () { expect(summarizeCalls(const []).total, 0); }); test('횟수·부재중·총 시간·마지막 통화·자주 받는 시간대', () { final s = summarizeCalls([ CallRecord(DateTime(2026, 10, 1, 19, 5), 600, CallDirection.outgoing), CallRecord(DateTime(2026, 10, 8, 19, 40), 300, CallDirection.incoming), CallRecord(DateTime(2026, 10, 15, 8, 10), 60, CallDirection.outgoing), CallRecord(DateTime(2026, 10, 20, 14, 0), 0, CallDirection.missed), ]); expect(s.total, 4); expect(s.answered, 3); expect(s.missed, 1); expect(s.totalSeconds, 960); expect(s.averageSeconds, 320); expect(s.lastAt, DateTime(2026, 10, 20, 14, 0)); expect(s.lastDirection, CallDirection.missed); expect(s.windows.first.fromHour, 19); expect(s.windows.first.label, '오후 7시 ~ 오후 8시'); expect(s.windows.length, 2); expect(durationText(45), '45초'); expect(durationText(600), '10분'); expect(durationText(4000), '1시간 6분'); }); }); }