tododo 0.1.0

A minimal terminal todo manager built with Rust and Ratatui
Documentation
use tododo::domain::priority::Priority;
use tododo::domain::validation::validate_title;
use tododo::domain::SortMode;
use tododo::domain::TodoStatus;

#[test]
fn test_sort_mode_label() {
    assert_eq!(SortMode::Priority.label(), "priority");
    assert_eq!(SortMode::CreatedAt.label(), "created");
}

#[test]
fn test_sort_mode_toggle() {
    let mut mode = SortMode::Priority;
    mode.toggle();
    assert_eq!(mode, SortMode::CreatedAt);
    mode.toggle();
    assert_eq!(mode, SortMode::Priority);
}

#[test]
fn test_sort_mode_default() {
    let mode = SortMode::default();
    assert_eq!(mode, SortMode::Priority);
}

#[test]
fn test_priority_order_high_to_low() {
    let priorities = vec![1, 2, 3, 4, 5];
    let expected_chars = vec!["S", "A", "B", "C", ""];

    for (p, expected) in priorities.iter().zip(expected_chars.iter()) {
        assert_eq!(
            Priority::new(*p).to_char(),
            *expected,
            "priority {} should be {}",
            p,
            expected
        );
    }
}

#[test]
fn test_priority_cycling_order() {
    let priorities = vec![1, 2, 3, 4, 5];
    let expected_next_chars = vec!["", "S", "A", "B", "C"];

    for (i, &current) in priorities.iter().enumerate() {
        let prio = Priority::new(current);
        let next_prio = prio.next();
        assert_eq!(
            next_prio.to_char(),
            expected_next_chars[i],
            "Priority {} ({}) should cycle to {} but got {}",
            current,
            prio.to_char(),
            expected_next_chars[i],
            next_prio.to_char()
        );
    }
}

#[test]
fn test_validate_title_accepts_trimmed_input() {
    let title = validate_title("  ship feature  ").expect("title should be valid");
    assert_eq!(title, "ship feature");
}

#[test]
fn test_validate_title_rejects_empty() {
    let err = validate_title("   ").expect_err("empty title should be invalid");
    assert!(err.to_string().contains("Title cannot be empty"));
}

#[test]
fn test_todo_status_string_roundtrip() {
    assert_eq!(TodoStatus::Pending.as_str(), TodoStatus::PENDING);
    assert_eq!(TodoStatus::Completed.as_str(), TodoStatus::COMPLETED);
    assert_eq!(
        TodoStatus::from_status_str(TodoStatus::PENDING),
        Some(TodoStatus::Pending)
    );
    assert_eq!(
        TodoStatus::from_status_str(TodoStatus::COMPLETED),
        Some(TodoStatus::Completed)
    );
    assert_eq!(TodoStatus::from_status_str("unknown"), None);
}

#[test]
fn test_priority_from_char_valid() {
    assert_eq!(Priority::from_char("S").unwrap(), Priority::S);
    assert_eq!(Priority::from_char("A").unwrap(), Priority::A);
    assert_eq!(Priority::from_char("B").unwrap(), Priority::B);
    assert_eq!(Priority::from_char("C").unwrap(), Priority::C);
    assert_eq!(Priority::from_char("").unwrap(), Priority::NONE);
    assert_eq!(Priority::from_char("s").unwrap(), Priority::S);
    assert_eq!(Priority::from_char("a").unwrap(), Priority::A);
}

#[test]
fn test_priority_from_char_invalid() {
    let err = Priority::from_char("X").expect_err("X should be invalid priority");
    assert!(err.to_string().contains("Invalid priority"));
    let err2 = Priority::from_char("Z").expect_err("Z should be invalid priority");
    assert!(err2.to_string().contains("Invalid priority"));
}

#[test]
fn test_validate_title_rejects_too_long() {
    let long_title = "a".repeat(101);
    let err = validate_title(&long_title).expect_err("title over 100 chars should be invalid");
    assert!(err.to_string().contains("exceeds 100 characters"));
}