cli_create.rs

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