1use assert_cmd::Command;
2use predicates::prelude::*;
3use tempfile::TempDir;
4
5fn td() -> Command {
6 Command::cargo_bin("td").unwrap()
7}
8
9fn init_tmp() -> TempDir {
10 let tmp = TempDir::new().unwrap();
11 td().arg("init").current_dir(&tmp).assert().success();
12 tmp
13}
14
15/// Create a task and return its JSON id.
16fn create_task(dir: &TempDir, title: &str) -> String {
17 let out = td()
18 .args(["--json", "create", title])
19 .current_dir(dir)
20 .output()
21 .unwrap();
22 let v: serde_json::Value = serde_json::from_slice(&out.stdout).unwrap();
23 v["id"].as_str().unwrap().to_string()
24}
25
26// ── list ─────────────────────────────────────────────────────────────
27
28#[test]
29fn list_shows_created_tasks() {
30 let tmp = init_tmp();
31 create_task(&tmp, "Alpha");
32 create_task(&tmp, "Bravo");
33
34 td().arg("list")
35 .current_dir(&tmp)
36 .assert()
37 .success()
38 .stdout(predicate::str::contains("Alpha"))
39 .stdout(predicate::str::contains("Bravo"));
40}
41
42#[test]
43fn list_json_returns_array() {
44 let tmp = init_tmp();
45 create_task(&tmp, "One");
46
47 let out = td()
48 .args(["--json", "list"])
49 .current_dir(&tmp)
50 .output()
51 .unwrap();
52 let v: serde_json::Value = serde_json::from_slice(&out.stdout).unwrap();
53 assert!(v.is_array(), "expected JSON array, got: {v}");
54 assert_eq!(v.as_array().unwrap().len(), 1);
55 assert_eq!(v[0]["title"].as_str().unwrap(), "One");
56}
57
58#[test]
59fn list_filter_by_status() {
60 let tmp = init_tmp();
61 create_task(&tmp, "Open task");
62
63 // No closed tasks yet.
64 let out = td()
65 .args(["--json", "list", "-s", "closed"])
66 .current_dir(&tmp)
67 .output()
68 .unwrap();
69 let v: serde_json::Value = serde_json::from_slice(&out.stdout).unwrap();
70 assert_eq!(v.as_array().unwrap().len(), 0);
71}
72
73#[test]
74fn list_filter_by_priority() {
75 let tmp = init_tmp();
76
77 td().args(["create", "Low prio", "-p", "3"])
78 .current_dir(&tmp)
79 .assert()
80 .success();
81 td().args(["create", "High prio", "-p", "1"])
82 .current_dir(&tmp)
83 .assert()
84 .success();
85
86 let out = td()
87 .args(["--json", "list", "-p", "1"])
88 .current_dir(&tmp)
89 .output()
90 .unwrap();
91 let v: serde_json::Value = serde_json::from_slice(&out.stdout).unwrap();
92 let tasks = v.as_array().unwrap();
93 assert_eq!(tasks.len(), 1);
94 assert_eq!(tasks[0]["title"].as_str().unwrap(), "High prio");
95}
96
97#[test]
98fn list_filter_by_label() {
99 let tmp = init_tmp();
100
101 td().args(["create", "Tagged", "-l", "urgent"])
102 .current_dir(&tmp)
103 .assert()
104 .success();
105 td().args(["create", "Untagged"])
106 .current_dir(&tmp)
107 .assert()
108 .success();
109
110 let out = td()
111 .args(["--json", "list", "-l", "urgent"])
112 .current_dir(&tmp)
113 .output()
114 .unwrap();
115 let v: serde_json::Value = serde_json::from_slice(&out.stdout).unwrap();
116 let tasks = v.as_array().unwrap();
117 assert_eq!(tasks.len(), 1);
118 assert_eq!(tasks[0]["title"].as_str().unwrap(), "Tagged");
119}
120
121// ── show ─────────────────────────────────────────────────────────────
122
123#[test]
124fn show_displays_task() {
125 let tmp = init_tmp();
126 let id = create_task(&tmp, "Details here");
127
128 td().args(["show", &id])
129 .current_dir(&tmp)
130 .assert()
131 .success()
132 .stdout(predicate::str::contains("Details here"))
133 .stdout(predicate::str::contains(&id));
134}
135
136#[test]
137fn show_json_includes_labels_and_blockers() {
138 let tmp = init_tmp();
139
140 td().args(["create", "With labels", "-l", "bug,ui"])
141 .current_dir(&tmp)
142 .assert()
143 .success();
144
145 // Get the id via list.
146 let out = td()
147 .args(["--json", "list"])
148 .current_dir(&tmp)
149 .output()
150 .unwrap();
151 let list: serde_json::Value = serde_json::from_slice(&out.stdout).unwrap();
152 let id = list[0]["id"].as_str().unwrap();
153
154 let out = td()
155 .args(["--json", "show", id])
156 .current_dir(&tmp)
157 .output()
158 .unwrap();
159 let v: serde_json::Value = serde_json::from_slice(&out.stdout).unwrap();
160 assert_eq!(v["title"].as_str().unwrap(), "With labels");
161
162 let labels = v["labels"].as_array().unwrap();
163 assert!(labels.contains(&serde_json::Value::String("bug".into())));
164 assert!(labels.contains(&serde_json::Value::String("ui".into())));
165
166 // Blockers should be present (even if empty).
167 assert!(v["blockers"].is_array());
168}
169
170#[test]
171fn show_nonexistent_task_fails() {
172 let tmp = init_tmp();
173
174 td().args(["show", "td-nope"])
175 .current_dir(&tmp)
176 .assert()
177 .failure()
178 .stderr(predicate::str::contains("not found"));
179}