kcode-task-board 0.1.2

Kennedy's persistent graph-shaped task board
Documentation
use kcode_credits::Credits;
use kcode_task_board::{CreateTask, Error, TaskBoard, UpdateTask};
#[rustfmt::skip]
fn task(title: &str, parents: Vec<String>, children: Vec<String>) -> CreateTask {
    CreateTask { title: title.into(), description: String::new(), user_id: "user-a".into(), credits: 3, category_ids: vec![], parent_ids: parents, child_ids: children, related_ids: vec![] }
}
#[test]
fn combined_update_and_completion_archives_awards_and_pushes_only_final_parent_children() {
    let directory = tempfile::tempdir().unwrap();
    let credits = Credits::open(directory.path().join("credits.sqlite3")).unwrap();
    let board = TaskBoard::open(directory.path().join("tasks.sqlite3"), credits.clone()).unwrap();
    let child = board.create_task(task("child", vec![], vec![])).unwrap();
    let mut parent = task("parent", vec![], vec![child.id.clone()]);
    parent.credits = 0;
    let parent = board.create_task(parent).unwrap();
    let outcome = board
        .update_task(UpdateTask {
            task_id: parent.id.clone(),
            credits: Some(150),
            complete: true,
            ..Default::default()
        })
        .unwrap();
    assert_eq!((outcome.completed, outcome.credits_awarded), (true, 150));
    assert!(board.task(&parent.id).is_err());
    assert_eq!(board.top_orphan().unwrap().unwrap().id, child.id);
    assert_eq!(credits.balance("user-a").unwrap().amount, 150);
}
#[test]
fn updating_the_top_orphan_returns_it_to_the_world() {
    let directory = tempfile::tempdir().unwrap();
    let credits = Credits::open(directory.path().join("credits.sqlite3")).unwrap();
    let board = TaskBoard::open(directory.path().join("tasks.sqlite3"), credits).unwrap();
    let child = board.create_task(task("child", vec![], vec![])).unwrap();
    let parent = board
        .create_task(task("parent", vec![], vec![child.id.clone()]))
        .unwrap();
    board.remove_task(&parent.id).unwrap();
    let updated = board
        .update_task(UpdateTask {
            task_id: child.id.clone(),
            title: Some("independent".into()),
            ..Default::default()
        })
        .unwrap();
    assert_eq!(updated.task.unwrap().title, "independent");
    assert!(board.top_orphan().unwrap().is_none());
}
#[test]
fn multiple_parents_cycles_related_edges_and_lifo_orphans_work_together() {
    let directory = tempfile::tempdir().unwrap();
    let credits = Credits::open(directory.path().join("credits.sqlite3")).unwrap();
    let board = TaskBoard::open(directory.path().join("tasks.sqlite3"), credits).unwrap();
    let first = board.create_task(task("first", vec![], vec![])).unwrap();
    let second = board.create_task(task("second", vec![], vec![])).unwrap();
    let parent_a = board
        .create_task(task("parent a", vec![], vec![first.id.clone()]))
        .unwrap();
    let parent_b = board
        .create_task(task("parent b", vec![], vec![first.id.clone()]))
        .unwrap();
    board
        .update_task(UpdateTask {
            task_id: first.id.clone(),
            child_ids: Some(vec![second.id.clone()]),
            related_ids: Some(vec![second.id.clone()]),
            ..Default::default()
        })
        .unwrap();
    board
        .update_task(UpdateTask {
            task_id: second.id.clone(),
            child_ids: Some(vec![first.id.clone()]),
            ..Default::default()
        })
        .unwrap();
    assert_eq!(board.task(&second.id).unwrap().related[0].id, first.id);
    board
        .update_task(UpdateTask {
            task_id: second.id.clone(),
            child_ids: Some(vec![]),
            ..Default::default()
        })
        .unwrap();
    board
        .update_task(UpdateTask {
            task_id: first.id.clone(),
            child_ids: Some(vec![]),
            ..Default::default()
        })
        .unwrap();
    board.remove_task(&parent_a.id).unwrap();
    assert!(board.top_orphan().unwrap().is_none());
    board.remove_task(&parent_b.id).unwrap();
    assert_eq!(board.top_orphan().unwrap().unwrap().id, first.id);
    let later_parent = board
        .create_task(task("later", vec![], vec![second.id.clone()]))
        .unwrap();
    board.remove_task(&later_parent.id).unwrap();
    assert_eq!(board.top_orphan().unwrap().unwrap().id, second.id);
    assert!(matches!(
        board.remove_task(&first.id),
        Err(Error::NotTopOrphan)
    ));
    board
        .update_task(UpdateTask {
            task_id: second.id,
            ..Default::default()
        })
        .unwrap();
    assert_eq!(board.top_orphan().unwrap().unwrap().id, first.id);
    assert!(first.id.starts_with("task-id-") && first.id.len() == 20);
}