The attodo.app, uhh... app.
at main 1036 lines 26 kB view raw
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}