use super::*;
use crate::edge::Edge;
use crate::layout;
use crate::taskfile::{read_task, task_ids};
use crate::verb::Verb;
use tempfile::TempDir;
fn fixture() -> Task {
Task { title: "A task".into(), created: 100, updated: 100, body: "body\n".into(), ..Task::default() }
}
fn scripted(cmd: &str) -> Editor {
Editor { cmd: Some(cmd.into()), tty: true, input: Box::new(io::empty()) }
}
#[test]
fn edit_requires_a_tty_and_a_usable_editor() {
let mut no_tty = Editor { cmd: Some("vi".into()), tty: false, input: Box::new(io::empty()) };
assert!(no_tty.edited(&fixture(), "bl-1").unwrap_err().to_string().contains("not a tty"));
let mut unset = Editor { cmd: None, tty: true, input: Box::new(io::empty()) };
assert!(unset.edited(&fixture(), "bl-1").unwrap_err().to_string().contains("set $EDITOR"));
assert!(scripted(" ").edited(&fixture(), "bl-1").unwrap_err().to_string().contains("is empty"));
assert!(scripted("false").edited(&fixture(), "bl-1").unwrap_err().to_string().contains("exited"));
}
#[test]
fn an_untouched_buffer_is_the_idempotent_noop() {
assert!(scripted("true").edited(&fixture(), "bl-1").unwrap().is_none());
}
#[test]
fn an_edited_buffer_parses_back_as_the_new_task() {
let tmp = TempDir::new().unwrap();
let script = tmp.path().join("editor.sh");
fs::write(&script, "printf '+++\\ntitle = \"Edited\"\\ncreated = 1\\nupdated = 1\\n+++\\nnew\\n' > \"$1\"\n").unwrap();
let after = scripted(&format!("sh {}", script.display())).edited(&fixture(), "bl-1").unwrap().unwrap();
assert_eq!(after.title, "Edited");
assert_eq!(after.body, "new\n");
}
#[test]
fn a_rejected_buffer_offers_a_reedit_pass_over_the_same_buffer() {
let tmp = TempDir::new().unwrap();
let (script, flag) = (tmp.path().join("editor.sh"), tmp.path().join("second-pass"));
fs::write(
&script,
"if [ -e \"$1\" ]; then printf '+++\\ntitle = \"Fixed\"\\ncreated = 0\\nupdated = 0\\n+++\\n' > \"$2\"; \
else : > \"$1\"; printf 'garbage' > \"$2\"; fi\n",
)
.unwrap();
let mut editor = Editor {
cmd: Some(format!("sh {} {}", script.display(), flag.display())),
tty: true,
input: Box::new(io::Cursor::new(b"y\n".to_vec())),
};
let after = editor.edited(&fixture(), "bl-1").unwrap().unwrap();
assert_eq!(after.title, "Fixed");
}
#[test]
fn a_rejected_buffer_aborts_without_an_explicit_yes() {
let tmp = TempDir::new().unwrap();
let script = tmp.path().join("editor.sh");
fs::write(&script, "printf 'garbage' > \"$1\"\n").unwrap();
let err = scripted(&format!("sh {}", script.display())).edited(&fixture(), "bl-1").unwrap_err();
assert!(err.to_string().contains("rejected"), "{err}");
assert!(err.to_string().contains("frontmatter"), "{err}");
}
fn edged(tmp: &TempDir) -> Edge {
Edge {
xdg: layout::Xdg::with(tmp.path(), None, Some(&tmp.path().join("state").to_string_lossy())),
invocation_path: tmp.path().join("proj"),
default_actor: "tester".into(),
depth: 0,
exe_dir: None,
path_dirs: Vec::new(),
color: false,
log_level: None,
balls_clock: None,
}
}
#[test]
fn edit_seals_a_replaced_buffer_and_noops_on_an_unchanged_one() {
let tmp = TempDir::new().unwrap();
let edge = edged(&tmp);
let run = |args: &[&str]| crate::run(&edge, &args.iter().map(ToString::to_string).collect::<Vec<_>>());
assert_eq!(run(&["prime", "--as", "me"]), 0);
assert_eq!(run(&["create", "A task", "--as", "me", "--body", "old"]), 0);
let store = edge.xdg.clone_dir(&edge.invocation_path).store();
let id = task_ids(&store).unwrap().pop().unwrap();
let before = read_task(&store, &id).unwrap();
let args: Vec<String> = vec![id.clone(), "--edit".into(), "--as".into(), "me".into()];
super::super::dispatch(&edge, Verb::Update, &args, &mut scripted("true")).unwrap();
assert_eq!(read_task(&store, &id).unwrap(), before);
let script = tmp.path().join("editor.sh");
fs::write(&script, "printf '+++\\ntitle = \"Edited\"\\ncreated = 1\\nupdated = 1\\n+++\\nnew\\n' > \"$1\"\n").unwrap();
super::super::dispatch(&edge, Verb::Update, &args, &mut scripted(&format!("sh {}", script.display()))).unwrap();
let after = read_task(&store, &id).unwrap();
assert_eq!(after.title, "Edited");
assert_eq!(after.body, "new\n");
assert_eq!(after.created, before.created);
assert!(after.updated >= before.updated, "the seal restamps updated");
}