The attodo.app, uhh... app.
1package dateparse
2
3import (
4 "testing"
5 "time"
6)
7
8func TestParseNumericDate(t *testing.T) {
9 refTime := time.Date(2024, 11, 20, 12, 0, 0, 0, time.UTC)
10
11 tests := []struct {
12 name string
13 input string
14 expectedDay int
15 expectedMonth time.Month
16 expectedYear int
17 shouldFind bool
18 }{
19 {
20 name: "MM/DD format future date",
21 input: "11/26 meeting with team",
22 expectedDay: 26,
23 expectedMonth: time.November,
24 expectedYear: 2024,
25 shouldFind: true,
26 },
27 {
28 name: "MM/DD format past date rolls to next year",
29 input: "01/15 review document",
30 expectedDay: 15,
31 expectedMonth: time.January,
32 expectedYear: 2025,
33 shouldFind: true,
34 },
35 {
36 name: "MM/DD/YYYY format",
37 input: "12/25/2024 christmas party",
38 expectedDay: 25,
39 expectedMonth: time.December,
40 expectedYear: 2024,
41 shouldFind: true,
42 },
43 {
44 name: "MM/DD/YY format",
45 input: "06/15/25 summer event",
46 expectedDay: 15,
47 expectedMonth: time.June,
48 expectedYear: 2025,
49 shouldFind: true,
50 },
51 {
52 name: "Invalid month",
53 input: "13/01 invalid date",
54 shouldFind: false,
55 },
56 {
57 name: "Invalid day",
58 input: "11/32 invalid date",
59 shouldFind: false,
60 },
61 }
62
63 for _, tt := range tests {
64 t.Run(tt.name, func(t *testing.T) {
65 result := Parse(tt.input, refTime)
66
67 if tt.shouldFind {
68 if result.DueDate == nil {
69 t.Errorf("Expected to find date, but got nil")
70 return
71 }
72
73 if result.DueDate.Day() != tt.expectedDay {
74 t.Errorf("Expected day %d, got %d", tt.expectedDay, result.DueDate.Day())
75 }
76 if result.DueDate.Month() != tt.expectedMonth {
77 t.Errorf("Expected month %v, got %v", tt.expectedMonth, result.DueDate.Month())
78 }
79 if result.DueDate.Year() != tt.expectedYear {
80 t.Errorf("Expected year %d, got %d", tt.expectedYear, result.DueDate.Year())
81 }
82
83 // Check that date was removed from title
84 if result.CleanedTitle == tt.input {
85 t.Errorf("Title was not cleaned: %s", result.CleanedTitle)
86 }
87 } else {
88 if result.DueDate != nil {
89 t.Errorf("Expected no date, but found %v", result.DueDate)
90 }
91 }
92 })
93 }
94}
95
96func TestParseDayName(t *testing.T) {
97 // Set reference to a Wednesday
98 refTime := time.Date(2024, 11, 20, 12, 0, 0, 0, time.UTC) // Wednesday
99
100 tests := []struct {
101 name string
102 input string
103 expectedDay int
104 expectedMonth time.Month
105 shouldFind bool
106 }{
107 {
108 name: "friday same week",
109 input: "friday meeting",
110 expectedDay: 22,
111 expectedMonth: time.November,
112 shouldFind: true,
113 },
114 {
115 name: "next monday",
116 input: "next monday review",
117 expectedDay: 25,
118 expectedMonth: time.November,
119 shouldFind: true,
120 },
121 {
122 name: "monday (upcoming)",
123 input: "monday deadline",
124 expectedDay: 25,
125 expectedMonth: time.November,
126 shouldFind: true,
127 },
128 {
129 name: "saturday",
130 input: "saturday task",
131 expectedDay: 23,
132 expectedMonth: time.November,
133 shouldFind: true,
134 },
135 {
136 name: "invalid day name",
137 input: "someday task",
138 shouldFind: false,
139 },
140 }
141
142 for _, tt := range tests {
143 t.Run(tt.name, func(t *testing.T) {
144 result := Parse(tt.input, refTime)
145
146 if tt.shouldFind {
147 if result.DueDate == nil {
148 t.Errorf("Expected to find date, but got nil")
149 return
150 }
151
152 if result.DueDate.Day() != tt.expectedDay {
153 t.Errorf("Expected day %d, got %d", tt.expectedDay, result.DueDate.Day())
154 }
155 if result.DueDate.Month() != tt.expectedMonth {
156 t.Errorf("Expected month %v, got %v", tt.expectedMonth, result.DueDate.Month())
157 }
158 } else {
159 if result.DueDate != nil {
160 t.Errorf("Expected no date, but found %v", result.DueDate)
161 }
162 }
163 })
164 }
165}
166
167func TestParseRelativeDate(t *testing.T) {
168 refTime := time.Date(2024, 11, 20, 12, 0, 0, 0, time.UTC)
169
170 tests := []struct {
171 name string
172 input string
173 expectedDay int
174 expectedMonth time.Month
175 shouldFind bool
176 }{
177 {
178 name: "today",
179 input: "today finish report",
180 expectedDay: 20,
181 expectedMonth: time.November,
182 shouldFind: true,
183 },
184 {
185 name: "tomorrow",
186 input: "tomorrow call client",
187 expectedDay: 21,
188 expectedMonth: time.November,
189 shouldFind: true,
190 },
191 {
192 name: "tmr abbreviation",
193 input: "tmr meeting",
194 expectedDay: 21,
195 expectedMonth: time.November,
196 shouldFind: true,
197 },
198 {
199 name: "three days",
200 input: "three days task-title",
201 expectedDay: 23,
202 expectedMonth: time.November,
203 shouldFind: true,
204 },
205 {
206 name: "3 days",
207 input: "3 days task-title",
208 expectedDay: 23,
209 expectedMonth: time.November,
210 shouldFind: true,
211 },
212 {
213 name: "in 3 days",
214 input: "in 3 days call back",
215 expectedDay: 23,
216 expectedMonth: time.November,
217 shouldFind: true,
218 },
219 {
220 name: "5 days",
221 input: "5 days review",
222 expectedDay: 25,
223 expectedMonth: time.November,
224 shouldFind: true,
225 },
226 {
227 name: "1 week",
228 input: "1 week project deadline",
229 expectedDay: 27,
230 expectedMonth: time.November,
231 shouldFind: true,
232 },
233 {
234 name: "2 weeks",
235 input: "2 weeks vacation",
236 expectedDay: 4,
237 expectedMonth: time.December,
238 shouldFind: true,
239 },
240 {
241 name: "in 1 week",
242 input: "in 1 week presentation",
243 expectedDay: 27,
244 expectedMonth: time.November,
245 shouldFind: true,
246 },
247 {
248 name: "in three days",
249 input: "this is due in three days",
250 expectedDay: 23,
251 expectedMonth: time.November,
252 shouldFind: true,
253 },
254 {
255 name: "five days",
256 input: "five days meeting",
257 expectedDay: 25,
258 expectedMonth: time.November,
259 shouldFind: true,
260 },
261 {
262 name: "a week",
263 input: "a week project",
264 expectedDay: 27,
265 expectedMonth: time.November,
266 shouldFind: true,
267 },
268 {
269 name: "in a week",
270 input: "in a week deadline",
271 expectedDay: 27,
272 expectedMonth: time.November,
273 shouldFind: true,
274 },
275 {
276 name: "two weeks",
277 input: "two weeks sprint",
278 expectedDay: 4,
279 expectedMonth: time.December,
280 shouldFind: true,
281 },
282 }
283
284 for _, tt := range tests {
285 t.Run(tt.name, func(t *testing.T) {
286 result := Parse(tt.input, refTime)
287
288 if tt.shouldFind {
289 if result.DueDate == nil {
290 t.Errorf("Expected to find date, but got nil")
291 return
292 }
293
294 if result.DueDate.Day() != tt.expectedDay {
295 t.Errorf("Expected day %d, got %d", tt.expectedDay, result.DueDate.Day())
296 }
297 if result.DueDate.Month() != tt.expectedMonth {
298 t.Errorf("Expected month %v, got %v", tt.expectedMonth, result.DueDate.Month())
299 }
300 } else {
301 if result.DueDate != nil {
302 t.Errorf("Expected no date, but found %v", result.DueDate)
303 }
304 }
305 })
306 }
307}
308
309func TestParseMonthName(t *testing.T) {
310 refTime := time.Date(2024, 11, 20, 12, 0, 0, 0, time.UTC)
311
312 tests := []struct {
313 name string
314 input string
315 expectedDay int
316 expectedMonth time.Month
317 expectedYear int
318 shouldFind bool
319 }{
320 {
321 name: "Nov 26 format",
322 input: "Nov 26 presentation",
323 expectedDay: 26,
324 expectedMonth: time.November,
325 expectedYear: 2024,
326 shouldFind: true,
327 },
328 {
329 name: "26 Nov format",
330 input: "26 Nov deadline",
331 expectedDay: 26,
332 expectedMonth: time.November,
333 expectedYear: 2024,
334 shouldFind: true,
335 },
336 {
337 name: "December 25 format",
338 input: "December 25 party",
339 expectedDay: 25,
340 expectedMonth: time.December,
341 expectedYear: 2024,
342 shouldFind: true,
343 },
344 {
345 name: "Past date rolls to next year",
346 input: "Jan 15 resolution",
347 expectedDay: 15,
348 expectedMonth: time.January,
349 expectedYear: 2025,
350 shouldFind: true,
351 },
352 {
353 name: "Invalid day",
354 input: "Nov 32 invalid",
355 shouldFind: false,
356 },
357 }
358
359 for _, tt := range tests {
360 t.Run(tt.name, func(t *testing.T) {
361 result := Parse(tt.input, refTime)
362
363 if tt.shouldFind {
364 if result.DueDate == nil {
365 t.Errorf("Expected to find date, but got nil")
366 return
367 }
368
369 if result.DueDate.Day() != tt.expectedDay {
370 t.Errorf("Expected day %d, got %d", tt.expectedDay, result.DueDate.Day())
371 }
372 if result.DueDate.Month() != tt.expectedMonth {
373 t.Errorf("Expected month %v, got %v", tt.expectedMonth, result.DueDate.Month())
374 }
375 if result.DueDate.Year() != tt.expectedYear {
376 t.Errorf("Expected year %d, got %d", tt.expectedYear, result.DueDate.Year())
377 }
378 } else {
379 if result.DueDate != nil {
380 t.Errorf("Expected no date, but found %v", result.DueDate)
381 }
382 }
383 })
384 }
385}
386
387func TestParseCleansTitle(t *testing.T) {
388 refTime := time.Date(2024, 11, 20, 12, 0, 0, 0, time.UTC)
389
390 tests := []struct {
391 name string
392 input string
393 expectedTitle string
394 }{
395 {
396 name: "Date at beginning",
397 input: "11/26 meeting with team",
398 expectedTitle: "meeting with team",
399 },
400 {
401 name: "Date in middle",
402 input: "Schedule tomorrow the review",
403 expectedTitle: "Schedule the review",
404 },
405 {
406 name: "Date at end",
407 input: "Important deadline friday",
408 expectedTitle: "Important deadline",
409 },
410 {
411 name: "No date",
412 input: "Regular task without date",
413 expectedTitle: "Regular task without date",
414 },
415 }
416
417 for _, tt := range tests {
418 t.Run(tt.name, func(t *testing.T) {
419 result := Parse(tt.input, refTime)
420
421 if result.CleanedTitle != tt.expectedTitle {
422 t.Errorf("Expected title '%s', got '%s'", tt.expectedTitle, result.CleanedTitle)
423 }
424 })
425 }
426}
427
428func TestParseNoDate(t *testing.T) {
429 refTime := time.Date(2024, 11, 20, 12, 0, 0, 0, time.UTC)
430
431 inputs := []string{
432 "Regular task",
433 "Meeting with client",
434 "Review document",
435 "Task with numbers 123 456",
436 }
437
438 for _, input := range inputs {
439 t.Run(input, func(t *testing.T) {
440 result := Parse(input, refTime)
441
442 if result.DueDate != nil {
443 t.Errorf("Expected no date for '%s', but found %v", input, result.DueDate)
444 }
445
446 if result.CleanedTitle != input {
447 t.Errorf("Title should remain unchanged: expected '%s', got '%s'", input, result.CleanedTitle)
448 }
449 })
450 }
451}
452
453func TestParseAdvancedRelative(t *testing.T) {
454 refTime := time.Date(2024, 11, 20, 12, 0, 0, 0, time.UTC) // Wednesday, Nov 20, 2024
455
456 tests := []struct {
457 name string
458 input string
459 expectedDay int
460 expectedMonth time.Month
461 expectedYear int
462 shouldFind bool
463 }{
464 {
465 name: "3 days from now",
466 input: "3 days from now finish report",
467 expectedDay: 23,
468 expectedMonth: time.November,
469 expectedYear: 2024,
470 shouldFind: true,
471 },
472 {
473 name: "2 weeks from now",
474 input: "2 weeks from now vacation",
475 expectedDay: 4,
476 expectedMonth: time.December,
477 expectedYear: 2024,
478 shouldFind: true,
479 },
480 {
481 name: "1 month from now",
482 input: "1 month from now review",
483 expectedDay: 20,
484 expectedMonth: time.December,
485 expectedYear: 2024,
486 shouldFind: true,
487 },
488 {
489 name: "3 months from now",
490 input: "3 months from now project deadline",
491 expectedDay: 20,
492 expectedMonth: time.February,
493 expectedYear: 2025,
494 shouldFind: true,
495 },
496 {
497 name: "in 2 months",
498 input: "in 2 months quarterly review",
499 expectedDay: 20,
500 expectedMonth: time.January,
501 expectedYear: 2025,
502 shouldFind: true,
503 },
504 {
505 name: "end of month",
506 input: "end of month report",
507 expectedDay: 30,
508 expectedMonth: time.November,
509 expectedYear: 2024,
510 shouldFind: true,
511 },
512 {
513 name: "end of the month",
514 input: "end of the month summary",
515 expectedDay: 30,
516 expectedMonth: time.November,
517 expectedYear: 2024,
518 shouldFind: true,
519 },
520 {
521 name: "end of week",
522 input: "end of week cleanup",
523 expectedDay: 24, // Sunday
524 expectedMonth: time.November,
525 expectedYear: 2024,
526 shouldFind: true,
527 },
528 {
529 name: "end of the week",
530 input: "end of the week status",
531 expectedDay: 24, // Sunday
532 expectedMonth: time.November,
533 expectedYear: 2024,
534 shouldFind: true,
535 },
536 {
537 name: "next quarter",
538 input: "next quarter planning",
539 expectedDay: 1,
540 expectedMonth: time.January,
541 expectedYear: 2025,
542 shouldFind: true,
543 },
544 }
545
546 for _, tt := range tests {
547 t.Run(tt.name, func(t *testing.T) {
548 result := Parse(tt.input, refTime)
549
550 if tt.shouldFind {
551 if result.DueDate == nil {
552 t.Errorf("Expected to find date, but got nil")
553 return
554 }
555
556 if result.DueDate.Day() != tt.expectedDay {
557 t.Errorf("Expected day %d, got %d", tt.expectedDay, result.DueDate.Day())
558 }
559 if result.DueDate.Month() != tt.expectedMonth {
560 t.Errorf("Expected month %v, got %v", tt.expectedMonth, result.DueDate.Month())
561 }
562 if result.DueDate.Year() != tt.expectedYear {
563 t.Errorf("Expected year %d, got %d", tt.expectedYear, result.DueDate.Year())
564 }
565
566 // Check that date was removed from title
567 if result.CleanedTitle == tt.input {
568 t.Errorf("Title was not cleaned: %s", result.CleanedTitle)
569 }
570 } else {
571 if result.DueDate != nil {
572 t.Errorf("Expected no date, but found %v", result.DueDate)
573 }
574 }
575 })
576 }
577}
578
579func TestParseTime(t *testing.T) {
580 refTime := time.Date(2024, 11, 20, 12, 0, 0, 0, time.UTC)
581
582 tests := []struct {
583 name string
584 input string
585 expectedDay int
586 expectedMonth time.Month
587 expectedHour int
588 expectedMin int
589 shouldFind bool
590 }{
591 {
592 name: "tomorrow at 3pm",
593 input: "tomorrow at 3pm meeting",
594 expectedDay: 21,
595 expectedMonth: time.November,
596 expectedHour: 15,
597 expectedMin: 0,
598 shouldFind: true,
599 },
600 {
601 name: "tomorrow at 3:30pm",
602 input: "tomorrow at 3:30pm doctor appointment",
603 expectedDay: 21,
604 expectedMonth: time.November,
605 expectedHour: 15,
606 expectedMin: 30,
607 shouldFind: true,
608 },
609 {
610 name: "11/26 at 2pm",
611 input: "11/26 at 2pm team meeting",
612 expectedDay: 26,
613 expectedMonth: time.November,
614 expectedHour: 14,
615 expectedMin: 0,
616 shouldFind: true,
617 },
618 {
619 name: "friday 5:30pm",
620 input: "friday 5:30pm happy hour",
621 expectedDay: 22,
622 expectedMonth: time.November,
623 expectedHour: 17,
624 expectedMin: 30,
625 shouldFind: true,
626 },
627 {
628 name: "today at 9am",
629 input: "today at 9am standup",
630 expectedDay: 20,
631 expectedMonth: time.November,
632 expectedHour: 9,
633 expectedMin: 0,
634 shouldFind: true,
635 },
636 {
637 name: "tomorrow at 12pm (noon)",
638 input: "tomorrow at 12pm lunch",
639 expectedDay: 21,
640 expectedMonth: time.November,
641 expectedHour: 12,
642 expectedMin: 0,
643 shouldFind: true,
644 },
645 {
646 name: "tomorrow at 12am (midnight)",
647 input: "tomorrow at 12am release",
648 expectedDay: 21,
649 expectedMonth: time.November,
650 expectedHour: 0,
651 expectedMin: 0,
652 shouldFind: true,
653 },
654 {
655 name: "next monday at 10:30am",
656 input: "next monday at 10:30am project review",
657 expectedDay: 25,
658 expectedMonth: time.November,
659 expectedHour: 10,
660 expectedMin: 30,
661 shouldFind: true,
662 },
663 }
664
665 for _, tt := range tests {
666 t.Run(tt.name, func(t *testing.T) {
667 result := Parse(tt.input, refTime)
668
669 if tt.shouldFind {
670 if result.DueDate == nil {
671 t.Errorf("Expected to find date, but got nil")
672 return
673 }
674
675 if result.DueDate.Day() != tt.expectedDay {
676 t.Errorf("Expected day %d, got %d", tt.expectedDay, result.DueDate.Day())
677 }
678 if result.DueDate.Month() != tt.expectedMonth {
679 t.Errorf("Expected month %v, got %v", tt.expectedMonth, result.DueDate.Month())
680 }
681 if result.DueDate.Hour() != tt.expectedHour {
682 t.Errorf("Expected hour %d, got %d", tt.expectedHour, result.DueDate.Hour())
683 }
684 if result.DueDate.Minute() != tt.expectedMin {
685 t.Errorf("Expected minute %d, got %d", tt.expectedMin, result.DueDate.Minute())
686 }
687
688 // Check that date/time was removed from title
689 if result.CleanedTitle == tt.input {
690 t.Errorf("Title was not cleaned: %s", result.CleanedTitle)
691 }
692 } else {
693 if result.DueDate != nil {
694 t.Errorf("Expected no date, but found %v", result.DueDate)
695 }
696 }
697 })
698 }
699}
700
701func TestParseTimeBasedRelative(t *testing.T) {
702 refTime := time.Date(2024, 11, 20, 14, 30, 0, 0, time.UTC) // Wednesday, Nov 20, 2024 at 2:30 PM
703
704 tests := []struct {
705 name string
706 input string
707 expectedHour int
708 expectedMin int
709 expectedDay int
710 expectedMonth time.Month
711 shouldFind bool
712 }{
713 {
714 name: "in 2 hours",
715 input: "this is due in 2 hours",
716 expectedHour: 16,
717 expectedMin: 30,
718 expectedDay: 20,
719 expectedMonth: time.November,
720 shouldFind: true,
721 },
722 {
723 name: "in two hours",
724 input: "this is due in two hours",
725 expectedHour: 16,
726 expectedMin: 30,
727 expectedDay: 20,
728 expectedMonth: time.November,
729 shouldFind: true,
730 },
731 {
732 name: "2 hours",
733 input: "meeting 2 hours from now",
734 expectedHour: 16,
735 expectedMin: 30,
736 expectedDay: 20,
737 expectedMonth: time.November,
738 shouldFind: true,
739 },
740 {
741 name: "in 30 minutes",
742 input: "call back in 30 minutes",
743 expectedHour: 15,
744 expectedMin: 0,
745 expectedDay: 20,
746 expectedMonth: time.November,
747 shouldFind: true,
748 },
749 {
750 name: "in thirty minutes",
751 input: "reminder in thirty minutes",
752 expectedHour: 15,
753 expectedMin: 0,
754 expectedDay: 20,
755 expectedMonth: time.November,
756 shouldFind: true,
757 },
758 {
759 name: "in 90 minutes",
760 input: "appointment in 90 minutes",
761 expectedHour: 16,
762 expectedMin: 0,
763 expectedDay: 20,
764 expectedMonth: time.November,
765 shouldFind: true,
766 },
767 {
768 name: "in one hour",
769 input: "meeting in one hour",
770 expectedHour: 15,
771 expectedMin: 30,
772 expectedDay: 20,
773 expectedMonth: time.November,
774 shouldFind: true,
775 },
776 {
777 name: "in an hour",
778 input: "call in an hour",
779 expectedHour: 15,
780 expectedMin: 30,
781 expectedDay: 20,
782 expectedMonth: time.November,
783 shouldFind: true,
784 },
785 {
786 name: "in fifteen minutes",
787 input: "break in fifteen minutes",
788 expectedHour: 14,
789 expectedMin: 45,
790 expectedDay: 20,
791 expectedMonth: time.November,
792 shouldFind: true,
793 },
794 {
795 name: "3 hours (crosses day boundary)",
796 input: "review in 10 hours",
797 expectedHour: 0,
798 expectedMin: 30,
799 expectedDay: 21, // Next day
800 expectedMonth: time.November,
801 shouldFind: true,
802 },
803 }
804
805 for _, tt := range tests {
806 t.Run(tt.name, func(t *testing.T) {
807 result := Parse(tt.input, refTime)
808
809 if tt.shouldFind {
810 if result.DueDate == nil {
811 t.Errorf("Expected to find date, but got nil")
812 return
813 }
814
815 if result.DueDate.Day() != tt.expectedDay {
816 t.Errorf("Expected day %d, got %d", tt.expectedDay, result.DueDate.Day())
817 }
818 if result.DueDate.Month() != tt.expectedMonth {
819 t.Errorf("Expected month %v, got %v", tt.expectedMonth, result.DueDate.Month())
820 }
821 if result.DueDate.Hour() != tt.expectedHour {
822 t.Errorf("Expected hour %d, got %d", tt.expectedHour, result.DueDate.Hour())
823 }
824 if result.DueDate.Minute() != tt.expectedMin {
825 t.Errorf("Expected minute %d, got %d", tt.expectedMin, result.DueDate.Minute())
826 }
827
828 // Check that time text was removed from title
829 if result.CleanedTitle == tt.input {
830 t.Errorf("Title was not cleaned: %s", result.CleanedTitle)
831 }
832 } else {
833 if result.DueDate != nil {
834 t.Errorf("Expected no date, but found %v", result.DueDate)
835 }
836 }
837 })
838 }
839}
840
841func TestParseTimeWithoutDate(t *testing.T) {
842 refTime := time.Date(2024, 11, 20, 12, 0, 0, 0, time.UTC) // Wednesday, Nov 20, 2024 at noon
843
844 tests := []struct {
845 name string
846 input string
847 expectedDay int
848 expectedMonth time.Month
849 expectedHour int
850 expectedMin int
851 shouldFind bool
852 }{
853 {
854 name: "at 9am without date",
855 input: "this is a test task due at 9am",
856 expectedDay: 20,
857 expectedMonth: time.November,
858 expectedHour: 9,
859 expectedMin: 0,
860 shouldFind: true,
861 },
862 {
863 name: "at 3pm without date",
864 input: "meeting at 3pm",
865 expectedDay: 20,
866 expectedMonth: time.November,
867 expectedHour: 15,
868 expectedMin: 0,
869 shouldFind: true,
870 },
871 {
872 name: "at 10:30am without date",
873 input: "standup at 10:30am",
874 expectedDay: 20,
875 expectedMonth: time.November,
876 expectedHour: 10,
877 expectedMin: 30,
878 shouldFind: true,
879 },
880 {
881 name: "due at 5:45pm without date",
882 input: "submit report due at 5:45pm",
883 expectedDay: 20,
884 expectedMonth: time.November,
885 expectedHour: 17,
886 expectedMin: 45,
887 shouldFind: true,
888 },
889 {
890 name: "9am without 'at'",
891 input: "standup 9am",
892 expectedDay: 20,
893 expectedMonth: time.November,
894 expectedHour: 9,
895 expectedMin: 0,
896 shouldFind: true,
897 },
898 }
899
900 for _, tt := range tests {
901 t.Run(tt.name, func(t *testing.T) {
902 result := Parse(tt.input, refTime)
903
904 if tt.shouldFind {
905 if result.DueDate == nil {
906 t.Errorf("Expected to find date, but got nil")
907 return
908 }
909
910 if result.DueDate.Day() != tt.expectedDay {
911 t.Errorf("Expected day %d, got %d", tt.expectedDay, result.DueDate.Day())
912 }
913 if result.DueDate.Month() != tt.expectedMonth {
914 t.Errorf("Expected month %v, got %v", tt.expectedMonth, result.DueDate.Month())
915 }
916 if result.DueDate.Hour() != tt.expectedHour {
917 t.Errorf("Expected hour %d, got %d", tt.expectedHour, result.DueDate.Hour())
918 }
919 if result.DueDate.Minute() != tt.expectedMin {
920 t.Errorf("Expected minute %d, got %d", tt.expectedMin, result.DueDate.Minute())
921 }
922
923 // Check that time was removed from title
924 if result.CleanedTitle == tt.input {
925 t.Errorf("Title was not cleaned: %s", result.CleanedTitle)
926 }
927 } else {
928 if result.DueDate != nil {
929 t.Errorf("Expected no date, but found %v", result.DueDate)
930 }
931 }
932 })
933 }
934}
935
936func TestParseAdditionalNaturalLanguage(t *testing.T) {
937 refTime := time.Date(2024, 11, 20, 12, 0, 0, 0, time.UTC) // Wednesday, Nov 20, 2024
938
939 tests := []struct {
940 name string
941 input string
942 expectedDay int
943 expectedMonth time.Month
944 expectedYear int
945 shouldFind bool
946 }{
947 {
948 name: "next month",
949 input: "next month review",
950 expectedDay: 20,
951 expectedMonth: time.December,
952 expectedYear: 2024,
953 shouldFind: true,
954 },
955 {
956 name: "next year",
957 input: "next year planning",
958 expectedDay: 20,
959 expectedMonth: time.November,
960 expectedYear: 2025,
961 shouldFind: true,
962 },
963 {
964 name: "start of month",
965 input: "start of month report",
966 expectedDay: 1,
967 expectedMonth: time.December,
968 expectedYear: 2024,
969 shouldFind: true,
970 },
971 {
972 name: "beginning of the month",
973 input: "beginning of the month check-in",
974 expectedDay: 1,
975 expectedMonth: time.December,
976 expectedYear: 2024,
977 shouldFind: true,
978 },
979 {
980 name: "mid month",
981 input: "mid month review",
982 expectedDay: 15,
983 expectedMonth: time.December,
984 expectedYear: 2024,
985 shouldFind: true,
986 },
987 {
988 name: "start of week",
989 input: "start of week standup",
990 expectedDay: 25,
991 expectedMonth: time.November,
992 expectedYear: 2024,
993 shouldFind: true,
994 },
995 {
996 name: "beginning of the week",
997 input: "beginning of the week planning",
998 expectedDay: 25,
999 expectedMonth: time.November,
1000 expectedYear: 2024,
1001 shouldFind: true,
1002 },
1003 }
1004
1005 for _, tt := range tests {
1006 t.Run(tt.name, func(t *testing.T) {
1007 result := Parse(tt.input, refTime)
1008
1009 if tt.shouldFind {
1010 if result.DueDate == nil {
1011 t.Errorf("Expected to find date, but got nil")
1012 return
1013 }
1014
1015 if result.DueDate.Day() != tt.expectedDay {
1016 t.Errorf("Expected day %d, got %d", tt.expectedDay, result.DueDate.Day())
1017 }
1018 if result.DueDate.Month() != tt.expectedMonth {
1019 t.Errorf("Expected month %v, got %v", tt.expectedMonth, result.DueDate.Month())
1020 }
1021 if result.DueDate.Year() != tt.expectedYear {
1022 t.Errorf("Expected year %d, got %d", tt.expectedYear, result.DueDate.Year())
1023 }
1024
1025 // Check that date was removed from title
1026 if result.CleanedTitle == tt.input {
1027 t.Errorf("Title was not cleaned: %s", result.CleanedTitle)
1028 }
1029 } else {
1030 if result.DueDate != nil {
1031 t.Errorf("Expected no date, but found %v", result.DueDate)
1032 }
1033 }
1034 })
1035 }
1036}