tododo 0.1.2

A minimal terminal todo manager built with Rust and Ratatui
Documentation
use tododo::db::{connect, run_migrations};
use tododo::domain::service::TodoService;
use tododo::domain::TodoStatus;
use tododo::error::AppError;
use tododo::repository::TodoRepository;

async fn make_service() -> TodoService {
    let db = connect("sqlite::memory:")
        .await
        .expect("connect to in-memory db");
    run_migrations(&db)
        .await
        .expect("run migrations");
    let repo = TodoRepository::new(db);
    TodoService::new(repo)
}

#[tokio::test]
async fn service_create_stores_pending_todo() {
    let svc = make_service().await;
    let todo = svc.create("buy milk", "").await.unwrap();
    assert_eq!(todo.title, "buy milk");
    assert!(matches!(todo.status, TodoStatus::Pending));
}

#[tokio::test]
async fn service_create_rejects_empty_title() {
    let svc = make_service().await;
    let err = svc.create("   ", "").await.unwrap_err();
    assert!(err.to_string().contains("empty"));
}

#[tokio::test]
async fn service_toggle_flips_pending_to_completed() {
    let svc = make_service().await;
    let created = svc.create("task", "").await.unwrap();
    let toggled = svc.toggle(&created.id).await.unwrap();
    assert!(matches!(toggled.status, TodoStatus::Completed));
}

#[tokio::test]
async fn service_toggle_flips_completed_back_to_pending() {
    let svc = make_service().await;
    let created = svc.create("task", "").await.unwrap();
    svc.toggle(&created.id).await.unwrap();
    let toggled = svc.toggle(&created.id).await.unwrap();
    assert!(matches!(toggled.status, TodoStatus::Pending));
}

#[tokio::test]
async fn service_update_changes_title_and_note() {
    let svc = make_service().await;
    let created = svc.create("old title", "").await.unwrap();
    let updated = svc.update(&created.id, "new title", "new note").await.unwrap();
    assert_eq!(updated.title, "new title");
    assert_eq!(updated.note, "new note");
}

#[tokio::test]
async fn service_set_priority_accepts_s_a_b_c() {
    let svc = make_service().await;
    let created = svc.create("task", "").await.unwrap();
    for char in ["S", "A", "B", "C"] {
        let result = svc.set_priority(&created.id, char).await;
        assert!(result.is_ok(), "setting priority {} should succeed", char);
    }
}

#[tokio::test]
async fn service_set_priority_rejects_invalid_char() {
    let svc = make_service().await;
    let created = svc.create("task", "").await.unwrap();
    let err = svc.set_priority(&created.id, "Z").await.unwrap_err();
    assert!(err.to_string().contains("Invalid priority"));
}

#[tokio::test]
async fn service_delete_removes_todo() {
    let svc = make_service().await;
    let created = svc.create("to delete", "").await.unwrap();
    svc.delete(&created.id).await.unwrap();
    let err = svc.get(&created.id).await.unwrap_err();
    assert!(matches!(err, AppError::NotFound(_)));
}

#[tokio::test]
async fn service_get_returns_stored_todo() {
    let svc = make_service().await;
    let created = svc.create("find me", "").await.unwrap();
    let found = svc.get(&created.id).await.unwrap();
    assert_eq!(found.id, created.id);
    assert_eq!(found.title, "find me");
}

#[tokio::test]
async fn service_get_returns_not_found_for_unknown_id() {
    let svc = make_service().await;
    let err = svc.get("unknown-id").await.unwrap_err();
    assert!(matches!(err, AppError::NotFound(_)));
}

#[tokio::test]
async fn service_list_active_sorted_by_priority() {
    let svc = make_service().await;
    let c = svc.create("task c", "").await.unwrap();
    svc.set_priority(&c.id, "C").await.unwrap();
    let s = svc.create("task s", "").await.unwrap();
    svc.set_priority(&s.id, "S").await.unwrap();
    let list = svc.list_active_sorted(tododo::domain::SortMode::Priority).await.unwrap();
    assert_eq!(list.len(), 2);
    assert_eq!(list[0].title, "task s");
    assert_eq!(list[0].priority.to_char(), "S");
}

#[tokio::test]
async fn service_list_excludes_deleted_todos() {
    let svc = make_service().await;
    let created = svc.create("to delete", "").await.unwrap();
    svc.delete(&created.id).await.unwrap();
    let list = svc.list_active_sorted(tododo::domain::SortMode::Priority).await.unwrap();
    assert!(!list.iter().any(|t| t.id == created.id));
}