A file-based task manager
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add tests for clean command, foreign links, and remotes; set up example remote

+218
+1
.tsk/remotes
··· 1 + example /tmp/example-remote-tsk
+1
Cargo.lock
··· 615 615 "itertools", 616 616 "nix", 617 617 "open", 618 + "tempfile", 618 619 "thiserror", 619 620 "url", 620 621 "xattr",
+3
Cargo.toml
··· 26 26 open = "5" 27 27 itertools = "0" 28 28 29 + [dev-dependencies] 30 + tempfile = "3" 31 + 29 32 [build-dependencies] 30 33 clap_mangen = "0"
+53
src/task.rs
··· 450 450 output.content 451 451 ); 452 452 } 453 + 454 + #[test] 455 + fn test_foreign_link_jira() { 456 + setup(); 457 + let input = "see [[jira-123]]\n"; 458 + let output = parse(input).expect("parse to work"); 459 + assert_eq!( 460 + &[ParsedLink::Foreign { 461 + prefix: "jira".to_string(), 462 + id: 123 463 + }], 464 + output.links.as_slice() 465 + ); 466 + } 467 + 468 + #[test] 469 + fn test_foreign_link_gitlab() { 470 + setup(); 471 + let input = "related to [[gl-456]]\n"; 472 + let output = parse(input).expect("parse to work"); 473 + assert_eq!( 474 + &[ParsedLink::Foreign { 475 + prefix: "gl".to_string(), 476 + id: 456 477 + }], 478 + output.links.as_slice() 479 + ); 480 + } 481 + 482 + #[test] 483 + fn test_mixed_internal_and_foreign_links() { 484 + setup(); 485 + let input = "see [[tsk-1]] and [[jira-99]]\n"; 486 + let output = parse(input).expect("parse to work"); 487 + assert_eq!( 488 + &[ 489 + ParsedLink::Internal(Id(1)), 490 + ParsedLink::Foreign { 491 + prefix: "jira".to_string(), 492 + id: 99 493 + } 494 + ], 495 + output.links.as_slice() 496 + ); 497 + } 498 + 499 + #[test] 500 + #[should_panic(expected = "Internal link is not a valid id")] 501 + fn test_foreign_link_bad_no_number() { 502 + setup(); 503 + let input = "see [[jira-abc]]\n"; 504 + let _output = parse(input).expect("parse to work"); 505 + } 453 506 }
+160
src/workspace.rs
··· 606 606 #[cfg(test)] 607 607 mod test { 608 608 use super::*; 609 + use std::fs; 610 + 611 + fn setup_test_workspace() -> (tempfile::TempDir, Workspace) { 612 + let dir = tempfile::tempdir().unwrap(); 613 + let path = dir.path().to_path_buf(); 614 + Workspace::init(path.clone()).unwrap(); 615 + let workspace = Workspace::from_path(path.clone()).unwrap(); 616 + (dir, workspace) 617 + } 609 618 610 619 #[test] 611 620 fn test_bare_task_display() { ··· 630 639 attributes: Default::default(), 631 640 }; 632 641 assert_eq!("Hello, world\n\nThe body of the task.", task.to_string()); 642 + } 643 + 644 + #[test] 645 + fn test_clean_removes_orphaned_tasks() { 646 + let (_dir, workspace) = setup_test_workspace(); 647 + 648 + { 649 + let ws = Workspace::from_path(workspace.path.clone()).unwrap(); 650 + let task1 = ws 651 + .new_task("Task one".to_string(), "body1".to_string()) 652 + .unwrap(); 653 + ws.push_task(task1).unwrap(); 654 + 655 + let task2 = ws 656 + .new_task("Task two".to_string(), "body2".to_string()) 657 + .unwrap(); 658 + ws.push_task(task2).unwrap(); 659 + 660 + let task3 = ws 661 + .new_task("Task three".to_string(), "body3".to_string()) 662 + .unwrap(); 663 + ws.push_task(task3).unwrap(); 664 + } 665 + 666 + let stack = workspace.read_stack().unwrap(); 667 + assert_eq!(stack.iter().count(), 3); 668 + 669 + let tasks_dir = workspace.path.join("tasks"); 670 + let task_files: Vec<_> = fs::read_dir(&tasks_dir) 671 + .unwrap() 672 + .filter(|e| e.as_ref().unwrap().path().is_file()) 673 + .collect(); 674 + assert_eq!(task_files.len(), 3); 675 + 676 + workspace.drop(TaskIdentifier::Relative(0)).unwrap(); 677 + 678 + let stack_after = workspace.read_stack().unwrap(); 679 + assert_eq!(stack_after.iter().count(), 2); 680 + 681 + let task_files_after: Vec<_> = fs::read_dir(&tasks_dir) 682 + .unwrap() 683 + .filter(|e| e.as_ref().unwrap().path().is_file()) 684 + .collect(); 685 + assert_eq!(task_files_after.len(), 3, "orphaned symlink still exists"); 686 + 687 + workspace.clean().unwrap(); 688 + 689 + let task_files_cleaned: Vec<_> = fs::read_dir(&tasks_dir) 690 + .unwrap() 691 + .filter(|e| e.as_ref().unwrap().path().is_file()) 692 + .collect(); 693 + assert_eq!( 694 + task_files_cleaned.len(), 695 + 2, 696 + "clean should remove orphaned task" 697 + ); 698 + } 699 + 700 + #[test] 701 + fn test_clean_does_nothing_when_no_orphans() { 702 + let (_dir, workspace) = setup_test_workspace(); 703 + let ws = Workspace::from_path(workspace.path.clone()).unwrap(); 704 + 705 + let task = ws 706 + .new_task("Only task".to_string(), "body".to_string()) 707 + .unwrap(); 708 + ws.push_task(task).unwrap(); 709 + 710 + workspace.clean().unwrap(); 711 + 712 + let tasks_dir = workspace.path.join("tasks"); 713 + let task_files: Vec<_> = fs::read_dir(&tasks_dir) 714 + .unwrap() 715 + .filter(|e| e.as_ref().unwrap().path().is_file()) 716 + .collect(); 717 + assert_eq!(task_files.len(), 1); 718 + } 719 + 720 + #[test] 721 + fn test_remote_add_and_list() { 722 + let (_dir, workspace) = setup_test_workspace(); 723 + 724 + let remotes = workspace.read_remotes().unwrap(); 725 + assert!(remotes.is_empty()); 726 + 727 + workspace.add_remote("jira", "/path/to/jira").unwrap(); 728 + 729 + let remotes = workspace.read_remotes().unwrap(); 730 + assert_eq!(remotes.len(), 1); 731 + assert_eq!(remotes[0].prefix, "jira"); 732 + assert_eq!(remotes[0].path, PathBuf::from("/path/to/jira")); 733 + 734 + workspace.add_remote("gl", "/path/to/gitlab").unwrap(); 735 + 736 + let remotes = workspace.read_remotes().unwrap(); 737 + assert_eq!(remotes.len(), 2); 738 + } 739 + 740 + #[test] 741 + fn test_remote_add_duplicate_fails() { 742 + let (_dir, workspace) = setup_test_workspace(); 743 + 744 + workspace.add_remote("jira", "/path/to/jira").unwrap(); 745 + 746 + let result = workspace.add_remote("jira", "/other/path"); 747 + assert!(result.is_err()); 748 + } 749 + 750 + #[test] 751 + fn test_remote_remove() { 752 + let (_dir, workspace) = setup_test_workspace(); 753 + 754 + workspace.add_remote("jira", "/path/to/jira").unwrap(); 755 + workspace.add_remote("gl", "/path/to/gl").unwrap(); 756 + 757 + workspace.remove_remote("jira").unwrap(); 758 + 759 + let remotes = workspace.read_remotes().unwrap(); 760 + assert_eq!(remotes.len(), 1); 761 + assert_eq!(remotes[0].prefix, "gl"); 762 + } 763 + 764 + #[test] 765 + fn test_remote_remove_nonexistent_fails() { 766 + let (_dir, workspace) = setup_test_workspace(); 767 + 768 + let result = workspace.remove_remote("nonexistent"); 769 + assert!(result.is_err()); 770 + } 771 + 772 + #[test] 773 + fn test_remote_persistence() { 774 + let (_dir, workspace) = setup_test_workspace(); 775 + 776 + workspace.add_remote("jira", "/path/to/jira").unwrap(); 777 + workspace.add_remote("gl", "/path/to/gl").unwrap(); 778 + 779 + let workspace2 = Workspace::from_path(workspace.path.clone()).unwrap(); 780 + let remotes = workspace2.read_remotes().unwrap(); 781 + assert_eq!(remotes.len(), 2); 782 + assert_eq!(remotes[0].prefix, "jira"); 783 + assert_eq!(remotes[1].prefix, "gl"); 784 + } 785 + 786 + #[test] 787 + fn test_remote_display() { 788 + let remote = Remote { 789 + prefix: "jira".to_string(), 790 + path: PathBuf::from("/path/to/jira"), 791 + }; 792 + assert_eq!("jira\t/path/to/jira", remote.to_string()); 633 793 } 634 794 }