Files
inkling/tests/unit/dueDateParser.test.ts
altair823 95ba1653d7 feat(due-date): pure rule parser for Korean date expressions
Regex + KST math, returns ISO YYYY-MM-DD or null. 14 high-confidence
rules (literal date, N월 N일, MM/DD, N일/주/개월 뒤, 모레/내일/글피/오늘,
다음/이번 주 X요일, 다음 달). Ambiguous tokens (월말, 주말, 퇴근 전,
시각) return iso=null + confidence='medium' so caller (AiWorker)
can defer to AI. 26+ golden fixtures.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 11:09:51 +09:00

127 lines
4.6 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import { parseDueDate } from '@main/services/dueDateParser.js';
// 2026-04-26 is a Sunday (KST). Use that as "today" for fixtures.
const TODAY = new Date('2026-04-26T00:00:00.000Z'); // KST midnight = UTC 15:00 prior day, but for parser logic we treat input Date as KST-aligned. The parser treats the passed Date as the "today reference" without further timezone math.
describe('parseDueDate (Korean rule parser)', () => {
it('returns null for empty / no-token text', () => {
expect(parseDueDate('아무 일정도 없는 메모', TODAY).iso).toBeNull();
});
it('parses YYYY-MM-DD literal', () => {
expect(parseDueDate('2026-05-15 마감', TODAY).iso).toBe('2026-05-15');
});
it('rejects invalid YYYY-MM-DD (silent coercion)', () => {
// 2026-02-30 is invalid; parser should NOT match it as the literal rule
const r = parseDueDate('2026-02-30 마감', TODAY);
// Falls through to other rules — but no other token matches "2026-02-30 마감"
expect(r.iso).toBeNull();
});
it('parses N월 N일 in current year if future', () => {
expect(parseDueDate('5월 1일 휴가 신청', TODAY).iso).toBe('2026-05-01');
});
it('parses N월 N일 in next year if past', () => {
expect(parseDueDate('3월 1일 발표', TODAY).iso).toBe('2027-03-01');
});
it('parses MM/DD', () => {
expect(parseDueDate('5/3 데모', TODAY).iso).toBe('2026-05-03');
});
it('parses N일 뒤', () => {
expect(parseDueDate('3일 뒤 데모', TODAY).iso).toBe('2026-04-29');
});
it('parses N일 후', () => {
expect(parseDueDate('5일 후 미팅', TODAY).iso).toBe('2026-05-01');
});
it('parses N주 뒤', () => {
expect(parseDueDate('2주 뒤 회의', TODAY).iso).toBe('2026-05-10');
});
it('parses N개월 뒤', () => {
expect(parseDueDate('1개월 뒤 점검', TODAY).iso).toBe('2026-05-26');
});
it('parses 모레', () => {
expect(parseDueDate('모레 발표', TODAY).iso).toBe('2026-04-28');
});
it('parses 내일', () => {
expect(parseDueDate('내일 회의 준비', TODAY).iso).toBe('2026-04-27');
});
it('parses 글피', () => {
expect(parseDueDate('글피까지 마무리', TODAY).iso).toBe('2026-04-29');
});
it('parses 오늘', () => {
expect(parseDueDate('오늘 PR 리뷰', TODAY).iso).toBe('2026-04-26');
});
it('parses 다음 주 월요일', () => {
expect(parseDueDate('다음 주 월요일까지 슬라이드', TODAY).iso).toBe('2026-05-04');
});
it('parses 다음 주 금요일', () => {
expect(parseDueDate('다음 주 금요일에 점검', TODAY).iso).toBe('2026-05-08');
});
it('parses 이번 주 일요일 = today (Sunday)', () => {
expect(parseDueDate('이번 주 일요일 약속', TODAY).iso).toBe('2026-04-26');
});
it('parses 이번 주 수요일', () => {
// This week's Mon = 04-20, so this week's Wed = 04-22. But that's past — still resolve to 04-22 (parser does not skip past dates within current week — caller can decide to ignore).
expect(parseDueDate('이번 주 수요일 회의', TODAY).iso).toBe('2026-04-22');
});
it('parses 다음 달 1일', () => {
expect(parseDueDate('다음 달 1일', TODAY).iso).toBe('2026-05-01');
});
it('parses 다음 달 (alone) → first of next month', () => {
expect(parseDueDate('다음 달 마감', TODAY).iso).toBe('2026-05-01');
});
it('returns medium confidence with iso null for 월말', () => {
const r = parseDueDate('월말 마감', TODAY);
expect(r.iso).toBeNull();
expect(r.confidence).toBe('medium');
expect(r.matchedToken).toBe('월말');
});
it('returns medium confidence with iso null for 주말', () => {
const r = parseDueDate('주말까지 정리', TODAY);
expect(r.iso).toBeNull();
expect(r.confidence).toBe('medium');
});
it('returns medium confidence for 퇴근 전', () => {
const r = parseDueDate('퇴근 전 답장', TODAY);
expect(r.iso).toBeNull();
expect(r.confidence).toBe('medium');
});
it('returns null for time-only (오후 3시)', () => {
// Time tokens get medium confidence (defer to AI) since they imply same-day but not a date directly.
const r = parseDueDate('오후 3시 미팅', TODAY);
expect(r.iso).toBeNull();
expect(r.confidence).toBe('medium');
});
it('first match wins when multiple tokens present', () => {
expect(parseDueDate('내일도 모레도 바쁨', TODAY).iso).toBe('2026-04-27');
});
it('matches 다음 주 alone → next Monday', () => {
// If not followed by 요일, "다음 주" alone defaults to next Monday
expect(parseDueDate('다음 주 발표', TODAY).iso).toBe('2026-05-04');
});
});