cli_create.rs

  1use assert_cmd::Command;
  2use predicates::prelude::*;
  3use tempfile::TempDir;
  4
  5fn td() -> Command {
  6    Command::cargo_bin("td").unwrap()
  7}
  8
  9/// Initialise a temp directory and return it.
 10fn init_tmp() -> TempDir {
 11    let tmp = TempDir::new().unwrap();
 12    td().arg("init").current_dir(&tmp).assert().success();
 13    tmp
 14}
 15
 16#[test]
 17fn create_prints_id_and_title() {
 18    let tmp = init_tmp();
 19
 20    td().args(["create", "My first task"])
 21        .current_dir(&tmp)
 22        .assert()
 23        .success()
 24        .stdout(predicate::str::contains("My first task"));
 25}
 26
 27#[test]
 28fn create_json_returns_task_object() {
 29    let tmp = init_tmp();
 30
 31    td().args(["--json", "create", "Buy milk"])
 32        .current_dir(&tmp)
 33        .assert()
 34        .success()
 35        .stdout(predicate::str::contains(r#""title":"Buy milk"#))
 36        .stdout(predicate::str::contains(r#""status":"open"#))
 37        .stdout(predicate::str::contains(r#""priority":2"#));
 38}
 39
 40#[test]
 41fn create_with_priority_and_type() {
 42    let tmp = init_tmp();
 43
 44    td().args(["--json", "create", "Urgent bug", "-p", "high", "-t", "bug"])
 45        .current_dir(&tmp)
 46        .assert()
 47        .success()
 48        .stdout(predicate::str::contains(r#""priority":1"#))
 49        .stdout(predicate::str::contains(r#""type":"bug"#));
 50}
 51
 52#[test]
 53fn create_with_description() {
 54    let tmp = init_tmp();
 55
 56    td().args([
 57        "--json",
 58        "create",
 59        "Fix login",
 60        "-d",
 61        "The login page is broken",
 62    ])
 63    .current_dir(&tmp)
 64    .assert()
 65    .success()
 66    .stdout(predicate::str::contains("The login page is broken"));
 67}
 68
 69#[test]
 70fn create_with_labels() {
 71    let tmp = init_tmp();
 72
 73    td().args(["--json", "create", "Labelled task", "-l", "frontend,urgent"])
 74        .current_dir(&tmp)
 75        .assert()
 76        .success();
 77
 78    // Verify labels are stored by checking the database directly.
 79    let conn = rusqlite::Connection::open(tmp.path().join(".td/tasks.db")).unwrap();
 80    let count: i64 = conn
 81        .query_row("SELECT COUNT(*) FROM labels", [], |r| r.get(0))
 82        .unwrap();
 83    assert_eq!(count, 2);
 84}
 85
 86#[test]
 87fn create_requires_title() {
 88    let tmp = init_tmp();
 89
 90    td().arg("create")
 91        .current_dir(&tmp)
 92        .assert()
 93        .failure()
 94        .stderr(predicate::str::contains("title required"));
 95}
 96
 97#[test]
 98fn create_subtask_under_parent() {
 99    let tmp = init_tmp();
100
101    // Create parent, extract its id.
102    let parent_out = td()
103        .args(["--json", "create", "Parent task"])
104        .current_dir(&tmp)
105        .output()
106        .unwrap();
107    let parent: serde_json::Value = serde_json::from_slice(&parent_out.stdout).unwrap();
108    let parent_id = parent["id"].as_str().unwrap();
109
110    // Create child under parent.
111    let child_out = td()
112        .args(["--json", "create", "Child task", "--parent", parent_id])
113        .current_dir(&tmp)
114        .output()
115        .unwrap();
116    let child: serde_json::Value = serde_json::from_slice(&child_out.stdout).unwrap();
117    let child_id = child["id"].as_str().unwrap();
118
119    // Child id should start with parent id.
120    assert!(
121        child_id.starts_with(parent_id),
122        "child id '{child_id}' should start with parent id '{parent_id}'"
123    );
124    assert_eq!(child["parent"].as_str().unwrap(), parent_id);
125}
126
127#[test]
128fn create_with_effort() {
129    let tmp = init_tmp();
130
131    let out = td()
132        .args(["--json", "create", "Hard task", "-e", "high"])
133        .current_dir(&tmp)
134        .output()
135        .unwrap();
136    let v: serde_json::Value = serde_json::from_slice(&out.stdout).unwrap();
137    assert_eq!(v["effort"].as_i64().unwrap(), 3);
138}
139
140#[test]
141fn create_with_priority_label() {
142    let tmp = init_tmp();
143
144    let out = td()
145        .args(["--json", "create", "Low prio", "-p", "low"])
146        .current_dir(&tmp)
147        .output()
148        .unwrap();
149    let v: serde_json::Value = serde_json::from_slice(&out.stdout).unwrap();
150    assert_eq!(v["priority"].as_i64().unwrap(), 3);
151}
152
153#[test]
154fn create_rejects_invalid_priority() {
155    let tmp = init_tmp();
156
157    td().args(["create", "Bad", "-p", "urgent"])
158        .current_dir(&tmp)
159        .assert()
160        .failure()
161        .stderr(predicates::prelude::predicate::str::contains(
162            "invalid priority",
163        ));
164}
165
166#[test]
167fn create_rejects_invalid_effort() {
168    let tmp = init_tmp();
169
170    td().args(["create", "Bad", "-e", "huge"])
171        .current_dir(&tmp)
172        .assert()
173        .failure()
174        .stderr(predicates::prelude::predicate::str::contains(
175            "invalid effort",
176        ));
177}