use super::*;
use crate::reads::test_support::{git_store, task};
use crate::taskfile::read_task;
#[test]
fn create_authors_a_ball_with_its_front_door_structure() {
let d = tempdir().unwrap();
let dir = d.path();
write(dir, "bl-1000", TASK);
write(dir, "bl-9", TASK); let mut f = flags();
f.positionals = vec!["New thing".into()];
f.parent = Some("bl-1000".into());
f.needs = vec!["bl-9".into()];
f.priority = Some(2);
f.tags = vec!["x".into()];
let (base, before) = base_change(Verb::Create, dir, &f, 42).unwrap();
assert!(before.is_none()); base.stage(dir).unwrap();
let id = new_id(dir, &["bl-1000", "bl-9"]); let t = read_task(dir, &id).unwrap();
assert_eq!(t.title, "New thing");
assert_eq!(t.created, 42);
assert_eq!(t.priority, Some(2));
assert_eq!(t.tags, ["x"]);
assert_eq!(t.parent.as_deref(), Some("bl-1000"));
assert_eq!(t.blockers, vec![Blocker { id: "bl-9".into(), on: On::Claim }]);
assert!(read_task(dir, "bl-1000").unwrap().blockers.is_empty());
}
#[test]
fn create_blocks_a_bare_op_gates_the_parent() {
let d = tempdir().unwrap();
let dir = d.path();
write(dir, "bl-1000", TASK);
let mut f = flags();
f.positionals = vec!["Gate".into()];
f.parent = Some("bl-1000".into());
f.blocks = vec!["close".into()]; let (base, _) = base_change(Verb::Create, dir, &f, 0).unwrap();
base.stage(dir).unwrap();
let id = new_id(dir, &["bl-1000"]);
assert_eq!(read_task(dir, "bl-1000").unwrap().blockers, vec![Blocker { id, on: On::Close }]);
}
#[test]
fn create_blocks_an_id_op_gates_a_non_parent() {
let d = tempdir().unwrap();
let dir = d.path();
write(dir, "bl-other", TASK);
let mut f = flags();
f.positionals = vec!["Edge".into()];
f.blocks = vec!["bl-other:update".into()]; let (base, _) = base_change(Verb::Create, dir, &f, 0).unwrap();
base.stage(dir).unwrap();
let id = new_id(dir, &["bl-other"]);
assert_eq!(read_task(dir, "bl-other").unwrap().blockers, vec![Blocker { id, on: On::Update }]);
}
#[test]
fn create_needs_with_an_explicit_op() {
let d = tempdir().unwrap();
let dir = d.path();
write(dir, "bl-dep", TASK);
let mut f = flags();
f.positionals = vec!["t".into()];
f.needs = vec!["bl-dep:close".into()];
let (base, _) = base_change(Verb::Create, dir, &f, 0).unwrap();
base.stage(dir).unwrap();
let id = new_id(dir, &["bl-dep"]);
assert_eq!(read_task(dir, &id).unwrap().blockers, vec![Blocker { id: "bl-dep".into(), on: On::Close }]);
}
#[test]
fn create_blocks_a_bare_op_requires_a_parent() {
let mut f = flags();
f.positionals = vec!["t".into()];
f.blocks = vec!["close".into()]; let err = base_change(Verb::Create, tempdir().unwrap().path(), &f, 0).err().unwrap();
assert!(err.to_string().contains("needs --parent"));
}
#[test]
fn create_refuses_an_unknown_edge_target() {
let s = git_store();
s.create("bl-live", &task("Alive", 1), 1);
let mut f = flags();
f.positionals = vec!["t".into()];
f.needs = vec!["bl-nope".into()];
let err = base_change(Verb::Create, s.dir(), &f, 0).err().unwrap();
assert!(err.to_string().contains("'bl-nope' is not a known id"), "{err}");
}
#[test]
fn create_refuses_a_dead_blocks_target_naming_the_closure() {
let s = git_store();
s.create("bl-dead", &task("Done", 1), 1).retire("bl-dead", "close", 2);
let mut f = flags();
f.positionals = vec!["t".into()];
f.blocks = vec!["bl-dead:close".into()];
let err = base_change(Verb::Create, s.dir(), &f, 0).err().unwrap();
assert!(err.to_string().contains("'bl-dead' is already closed"), "{err}");
}
#[test]
fn create_rejects_an_unknown_op_token() {
let mut f = flags();
f.positionals = vec!["t".into()];
f.needs = vec!["bl-dep:frobnicate".into()];
let err = base_change(Verb::Create, tempdir().unwrap().path(), &f, 0).err().unwrap();
assert!(err.to_string().contains("not a known op"));
}
#[test]
fn create_rejects_no_needs() {
let mut f = flags();
f.positionals = vec!["t".into()];
f.no_needs = vec!["bl-x".into()];
let err = base_change(Verb::Create, tempdir().unwrap().path(), &f, 0).err().unwrap();
assert!(err.to_string().contains("only for update"));
}
#[test]
fn create_rejects_each_removal_flag_on_its_own() {
let solo: &[fn(&mut Flags)] = &[
|f| f.no_parent = true,
|f| f.no_priority = true,
|f| f.no_tags = vec!["x".into()],
];
for (i, set) in solo.iter().enumerate() {
let mut f = flags();
f.positionals = vec!["t".into()];
set(&mut f);
let err = base_change(Verb::Create, tempdir().unwrap().path(), &f, 0).err().unwrap();
assert!(err.to_string().contains("only for update"), "solo removal flag #{i}: {err}");
}
}
#[test]
fn create_requires_exactly_one_positional() {
let dir = tempdir().unwrap();
assert!(base_change(Verb::Create, dir.path(), &flags(), 0).is_err()); let mut f = flags();
f.positionals = vec!["a".into(), "b".into()];
assert!(base_change(Verb::Create, dir.path(), &f, 0).is_err()); }
#[test]
fn create_subtask_of_sets_parent_and_claim_gates_it() {
let d = tempdir().unwrap();
let dir = d.path();
write(dir, "bl-1000", TASK);
let mut f = flags();
f.positionals = vec!["Subtask".into()];
f.subtask_of = Some("bl-1000".into());
let (base, _) = base_change(Verb::Create, dir, &f, 0).unwrap();
base.stage(dir).unwrap();
let id = new_id(dir, &["bl-1000"]);
assert_eq!(read_task(dir, &id).unwrap().parent.as_deref(), Some("bl-1000"));
assert_eq!(read_task(dir, "bl-1000").unwrap().blockers, vec![Blocker { id, on: On::Claim }]);
}
#[test]
fn create_subtask_of_conflicts_with_parent() {
let mut f = flags();
f.positionals = vec!["t".into()];
f.subtask_of = Some("bl-a".into());
f.parent = Some("bl-b".into());
let err = base_change(Verb::Create, tempdir().unwrap().path(), &f, 0).err().unwrap();
assert!(err.to_string().contains("--subtask-of and --parent conflict"));
}
#[test]
fn create_subtask_of_dedups_an_explicit_claim_gate() {
let d = tempdir().unwrap();
let dir = d.path();
write(dir, "bl-1000", TASK);
let mut f = flags();
f.positionals = vec!["Subtask".into()];
f.subtask_of = Some("bl-1000".into());
f.blocks = vec!["claim".into()];
let (base, _) = base_change(Verb::Create, dir, &f, 0).unwrap();
base.stage(dir).unwrap();
let id = new_id(dir, &["bl-1000"]);
assert_eq!(read_task(dir, "bl-1000").unwrap().blockers, vec![Blocker { id, on: On::Claim }]);
}
#[test]
fn create_subtask_of_composes_with_a_distinct_blocks_edge() {
let d = tempdir().unwrap();
let dir = d.path();
write(dir, "bl-1000", TASK);
let mut f = flags();
f.positionals = vec!["Subtask".into()];
f.subtask_of = Some("bl-1000".into());
f.blocks = vec!["update".into()];
let (base, _) = base_change(Verb::Create, dir, &f, 0).unwrap();
base.stage(dir).unwrap();
let id = new_id(dir, &["bl-1000"]);
let blockers = read_task(dir, "bl-1000").unwrap().blockers;
assert_eq!(blockers.len(), 2);
assert!(blockers.contains(&Blocker { id: id.clone(), on: On::Update }));
assert!(blockers.contains(&Blocker { id, on: On::Claim }));
}