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));
}