aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock267
-rw-r--r--Cargo.toml5
-rw-r--r--tests/cli.rs346
3 files changed, 617 insertions, 1 deletions
diff --git a/Cargo.lock b/Cargo.lock
index ab2a0bd..25252ed 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3,6 +3,15 @@
3version = 4 3version = 4
4 4
5[[package]] 5[[package]]
6name = "aho-corasick"
7version = "1.1.3"
8source = "registry+https://github.com/rust-lang/crates.io-index"
9checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
10dependencies = [
11 "memchr",
12]
13
14[[package]]
6name = "anstream" 15name = "anstream"
7version = "0.6.19" 16version = "0.6.19"
8source = "registry+https://github.com/rust-lang/crates.io-index" 17source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -59,6 +68,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
59checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" 68checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
60 69
61[[package]] 70[[package]]
71name = "assert_cmd"
72version = "2.0.17"
73source = "registry+https://github.com/rust-lang/crates.io-index"
74checksum = "2bd389a4b2970a01282ee455294913c0a43724daedcd1a24c3eb0ec1c1320b66"
75dependencies = [
76 "anstyle",
77 "bstr",
78 "doc-comment",
79 "libc",
80 "predicates",
81 "predicates-core",
82 "predicates-tree",
83 "wait-timeout",
84]
85
86[[package]]
87name = "autocfg"
88version = "1.5.0"
89source = "registry+https://github.com/rust-lang/crates.io-index"
90checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
91
92[[package]]
62name = "bitflags" 93name = "bitflags"
63version = "1.3.2" 94version = "1.3.2"
64source = "registry+https://github.com/rust-lang/crates.io-index" 95source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -71,6 +102,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
71checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" 102checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
72 103
73[[package]] 104[[package]]
105name = "bstr"
106version = "1.12.0"
107source = "registry+https://github.com/rust-lang/crates.io-index"
108checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4"
109dependencies = [
110 "memchr",
111 "regex-automata",
112 "serde",
113]
114
115[[package]]
74name = "cfg-if" 116name = "cfg-if"
75version = "1.0.1" 117version = "1.0.1"
76source = "registry+https://github.com/rust-lang/crates.io-index" 118source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -143,15 +185,46 @@ name = "demon"
143version = "0.1.0" 185version = "0.1.0"
144dependencies = [ 186dependencies = [
145 "anyhow", 187 "anyhow",
188 "assert_cmd",
146 "clap", 189 "clap",
147 "ctrlc", 190 "ctrlc",
148 "glob", 191 "glob",
149 "notify", 192 "notify",
193 "predicates",
194 "tempfile",
150 "tracing", 195 "tracing",
151 "tracing-subscriber", 196 "tracing-subscriber",
152] 197]
153 198
154[[package]] 199[[package]]
200name = "difflib"
201version = "0.4.0"
202source = "registry+https://github.com/rust-lang/crates.io-index"
203checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
204
205[[package]]
206name = "doc-comment"
207version = "0.3.3"
208source = "registry+https://github.com/rust-lang/crates.io-index"
209checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
210
211[[package]]
212name = "errno"
213version = "0.3.12"
214source = "registry+https://github.com/rust-lang/crates.io-index"
215checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18"
216dependencies = [
217 "libc",
218 "windows-sys",
219]
220
221[[package]]
222name = "fastrand"
223version = "2.3.0"
224source = "registry+https://github.com/rust-lang/crates.io-index"
225checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
226
227[[package]]
155name = "filetime" 228name = "filetime"
156version = "0.2.25" 229version = "0.2.25"
157source = "registry+https://github.com/rust-lang/crates.io-index" 230source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -164,6 +237,15 @@ dependencies = [
164] 237]
165 238
166[[package]] 239[[package]]
240name = "float-cmp"
241version = "0.10.0"
242source = "registry+https://github.com/rust-lang/crates.io-index"
243checksum = "b09cf3155332e944990140d967ff5eceb70df778b34f77d8075db46e4704e6d8"
244dependencies = [
245 "num-traits",
246]
247
248[[package]]
167name = "fsevent-sys" 249name = "fsevent-sys"
168version = "4.1.0" 250version = "4.1.0"
169source = "registry+https://github.com/rust-lang/crates.io-index" 251source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -173,6 +255,18 @@ dependencies = [
173] 255]
174 256
175[[package]] 257[[package]]
258name = "getrandom"
259version = "0.3.3"
260source = "registry+https://github.com/rust-lang/crates.io-index"
261checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
262dependencies = [
263 "cfg-if",
264 "libc",
265 "r-efi",
266 "wasi 0.14.2+wasi-0.2.4",
267]
268
269[[package]]
176name = "glob" 270name = "glob"
177version = "0.3.2" 271version = "0.3.2"
178source = "registry+https://github.com/rust-lang/crates.io-index" 272source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -254,12 +348,24 @@ dependencies = [
254] 348]
255 349
256[[package]] 350[[package]]
351name = "linux-raw-sys"
352version = "0.9.4"
353source = "registry+https://github.com/rust-lang/crates.io-index"
354checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
355
356[[package]]
257name = "log" 357name = "log"
258version = "0.4.27" 358version = "0.4.27"
259source = "registry+https://github.com/rust-lang/crates.io-index" 359source = "registry+https://github.com/rust-lang/crates.io-index"
260checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" 360checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
261 361
262[[package]] 362[[package]]
363name = "memchr"
364version = "2.7.5"
365source = "registry+https://github.com/rust-lang/crates.io-index"
366checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
367
368[[package]]
263name = "mio" 369name = "mio"
264version = "1.0.4" 370version = "1.0.4"
265source = "registry+https://github.com/rust-lang/crates.io-index" 371source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -267,7 +373,7 @@ checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
267dependencies = [ 373dependencies = [
268 "libc", 374 "libc",
269 "log", 375 "log",
270 "wasi", 376 "wasi 0.11.1+wasi-snapshot-preview1",
271 "windows-sys", 377 "windows-sys",
272] 378]
273 379
@@ -284,6 +390,12 @@ dependencies = [
284] 390]
285 391
286[[package]] 392[[package]]
393name = "normalize-line-endings"
394version = "0.3.0"
395source = "registry+https://github.com/rust-lang/crates.io-index"
396checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
397
398[[package]]
287name = "notify" 399name = "notify"
288version = "8.0.0" 400version = "8.0.0"
289source = "registry+https://github.com/rust-lang/crates.io-index" 401source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -319,6 +431,15 @@ dependencies = [
319] 431]
320 432
321[[package]] 433[[package]]
434name = "num-traits"
435version = "0.2.19"
436source = "registry+https://github.com/rust-lang/crates.io-index"
437checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
438dependencies = [
439 "autocfg",
440]
441
442[[package]]
322name = "once_cell" 443name = "once_cell"
323version = "1.21.3" 444version = "1.21.3"
324source = "registry+https://github.com/rust-lang/crates.io-index" 445source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -343,6 +464,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
343checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" 464checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
344 465
345[[package]] 466[[package]]
467name = "predicates"
468version = "3.1.3"
469source = "registry+https://github.com/rust-lang/crates.io-index"
470checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573"
471dependencies = [
472 "anstyle",
473 "difflib",
474 "float-cmp",
475 "normalize-line-endings",
476 "predicates-core",
477 "regex",
478]
479
480[[package]]
481name = "predicates-core"
482version = "1.0.9"
483source = "registry+https://github.com/rust-lang/crates.io-index"
484checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa"
485
486[[package]]
487name = "predicates-tree"
488version = "1.0.12"
489source = "registry+https://github.com/rust-lang/crates.io-index"
490checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c"
491dependencies = [
492 "predicates-core",
493 "termtree",
494]
495
496[[package]]
346name = "proc-macro2" 497name = "proc-macro2"
347version = "1.0.95" 498version = "1.0.95"
348source = "registry+https://github.com/rust-lang/crates.io-index" 499source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -361,6 +512,12 @@ dependencies = [
361] 512]
362 513
363[[package]] 514[[package]]
515name = "r-efi"
516version = "5.3.0"
517source = "registry+https://github.com/rust-lang/crates.io-index"
518checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
519
520[[package]]
364name = "redox_syscall" 521name = "redox_syscall"
365version = "0.5.13" 522version = "0.5.13"
366source = "registry+https://github.com/rust-lang/crates.io-index" 523source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -370,6 +527,48 @@ dependencies = [
370] 527]
371 528
372[[package]] 529[[package]]
530name = "regex"
531version = "1.11.1"
532source = "registry+https://github.com/rust-lang/crates.io-index"
533checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
534dependencies = [
535 "aho-corasick",
536 "memchr",
537 "regex-automata",
538 "regex-syntax",
539]
540
541[[package]]
542name = "regex-automata"
543version = "0.4.9"
544source = "registry+https://github.com/rust-lang/crates.io-index"
545checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
546dependencies = [
547 "aho-corasick",
548 "memchr",
549 "regex-syntax",
550]
551
552[[package]]
553name = "regex-syntax"
554version = "0.8.5"
555source = "registry+https://github.com/rust-lang/crates.io-index"
556checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
557
558[[package]]
559name = "rustix"
560version = "1.0.7"
561source = "registry+https://github.com/rust-lang/crates.io-index"
562checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
563dependencies = [
564 "bitflags 2.9.1",
565 "errno",
566 "libc",
567 "linux-raw-sys",
568 "windows-sys",
569]
570
571[[package]]
373name = "same-file" 572name = "same-file"
374version = "1.0.6" 573version = "1.0.6"
375source = "registry+https://github.com/rust-lang/crates.io-index" 574source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -379,6 +578,26 @@ dependencies = [
379] 578]
380 579
381[[package]] 580[[package]]
581name = "serde"
582version = "1.0.219"
583source = "registry+https://github.com/rust-lang/crates.io-index"
584checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
585dependencies = [
586 "serde_derive",
587]
588
589[[package]]
590name = "serde_derive"
591version = "1.0.219"
592source = "registry+https://github.com/rust-lang/crates.io-index"
593checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
594dependencies = [
595 "proc-macro2",
596 "quote",
597 "syn",
598]
599
600[[package]]
382name = "sharded-slab" 601name = "sharded-slab"
383version = "0.1.7" 602version = "0.1.7"
384source = "registry+https://github.com/rust-lang/crates.io-index" 603source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -411,6 +630,25 @@ dependencies = [
411] 630]
412 631
413[[package]] 632[[package]]
633name = "tempfile"
634version = "3.20.0"
635source = "registry+https://github.com/rust-lang/crates.io-index"
636checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1"
637dependencies = [
638 "fastrand",
639 "getrandom",
640 "once_cell",
641 "rustix",
642 "windows-sys",
643]
644
645[[package]]
646name = "termtree"
647version = "0.5.1"
648source = "registry+https://github.com/rust-lang/crates.io-index"
649checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683"
650
651[[package]]
414name = "thread_local" 652name = "thread_local"
415version = "1.1.9" 653version = "1.1.9"
416source = "registry+https://github.com/rust-lang/crates.io-index" 654source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -495,6 +733,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
495checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" 733checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
496 734
497[[package]] 735[[package]]
736name = "wait-timeout"
737version = "0.2.1"
738source = "registry+https://github.com/rust-lang/crates.io-index"
739checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11"
740dependencies = [
741 "libc",
742]
743
744[[package]]
498name = "walkdir" 745name = "walkdir"
499version = "2.5.0" 746version = "2.5.0"
500source = "registry+https://github.com/rust-lang/crates.io-index" 747source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -511,6 +758,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
511checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" 758checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
512 759
513[[package]] 760[[package]]
761name = "wasi"
762version = "0.14.2+wasi-0.2.4"
763source = "registry+https://github.com/rust-lang/crates.io-index"
764checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
765dependencies = [
766 "wit-bindgen-rt",
767]
768
769[[package]]
514name = "winapi" 770name = "winapi"
515version = "0.3.9" 771version = "0.3.9"
516source = "registry+https://github.com/rust-lang/crates.io-index" 772source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -613,3 +869,12 @@ name = "windows_x86_64_msvc"
613version = "0.52.6" 869version = "0.52.6"
614source = "registry+https://github.com/rust-lang/crates.io-index" 870source = "registry+https://github.com/rust-lang/crates.io-index"
615checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 871checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
872
873[[package]]
874name = "wit-bindgen-rt"
875version = "0.39.0"
876source = "registry+https://github.com/rust-lang/crates.io-index"
877checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
878dependencies = [
879 "bitflags 2.9.1",
880]
diff --git a/Cargo.toml b/Cargo.toml
index f22a42a..ee3e226 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -11,3 +11,8 @@ glob = "0.3.2"
11notify = "8.0.0" 11notify = "8.0.0"
12tracing = "0.1.41" 12tracing = "0.1.41"
13tracing-subscriber = "0.3.19" 13tracing-subscriber = "0.3.19"
14
15[dev-dependencies]
16assert_cmd = "2.0.17"
17predicates = "3.1.3"
18tempfile = "3.20.0"
diff --git a/tests/cli.rs b/tests/cli.rs
new file mode 100644
index 0000000..7cb68b7
--- /dev/null
+++ b/tests/cli.rs
@@ -0,0 +1,346 @@
1use assert_cmd::Command;
2use predicates::prelude::*;
3use std::fs;
4use std::time::Duration;
5use tempfile::TempDir;
6
7#[test]
8fn test_help_output() {
9 let mut cmd = Command::cargo_bin("demon").unwrap();
10 cmd.args(&["--help"])
11 .assert()
12 .success()
13 .stdout(predicate::str::contains("daemon process management"))
14 .stdout(predicate::str::contains("run"))
15 .stdout(predicate::str::contains("stop"))
16 .stdout(predicate::str::contains("tail"))
17 .stdout(predicate::str::contains("cat"))
18 .stdout(predicate::str::contains("list"))
19 .stdout(predicate::str::contains("status"))
20 .stdout(predicate::str::contains("clean"));
21}
22
23#[test]
24fn test_version_output() {
25 let mut cmd = Command::cargo_bin("demon").unwrap();
26 cmd.args(&["--version"])
27 .assert()
28 .success()
29 .stdout(predicate::str::contains("demon 0.1.0"));
30}
31
32#[test]
33fn test_run_missing_command() {
34 let temp_dir = TempDir::new().unwrap();
35
36 let mut cmd = Command::cargo_bin("demon").unwrap();
37 cmd.current_dir(temp_dir.path())
38 .args(&["run", "--id", "test"])
39 .assert()
40 .failure()
41 .stderr(predicate::str::contains("Command cannot be empty"));
42}
43
44#[test]
45fn test_run_creates_files() {
46 let temp_dir = TempDir::new().unwrap();
47
48 let mut cmd = Command::cargo_bin("demon").unwrap();
49 cmd.current_dir(temp_dir.path())
50 .args(&["run", "--id", "test", "echo", "hello"])
51 .assert()
52 .success()
53 .stdout(predicate::str::contains("Started daemon 'test'"));
54
55 // Verify files were created
56 assert!(temp_dir.path().join("test.pid").exists());
57 assert!(temp_dir.path().join("test.stdout").exists());
58 assert!(temp_dir.path().join("test.stderr").exists());
59
60 // Check that stdout contains our output
61 let stdout_content = fs::read_to_string(temp_dir.path().join("test.stdout")).unwrap();
62 assert_eq!(stdout_content.trim(), "hello");
63}
64
65#[test]
66fn test_run_duplicate_process() {
67 let temp_dir = TempDir::new().unwrap();
68
69 // Start a long-running process
70 let mut cmd = Command::cargo_bin("demon").unwrap();
71 cmd.current_dir(temp_dir.path())
72 .args(&["run", "--id", "long", "sleep", "30"])
73 .assert()
74 .success();
75
76 // Try to start another with the same ID
77 let mut cmd = Command::cargo_bin("demon").unwrap();
78 cmd.current_dir(temp_dir.path())
79 .args(&["run", "--id", "long", "sleep", "5"])
80 .assert()
81 .failure()
82 .stderr(predicate::str::contains("already running"));
83
84 // Clean up the running process
85 let mut cmd = Command::cargo_bin("demon").unwrap();
86 cmd.current_dir(temp_dir.path())
87 .args(&["stop", "--id", "long"])
88 .assert()
89 .success();
90}
91
92#[test]
93fn test_list_empty() {
94 let temp_dir = TempDir::new().unwrap();
95
96 let mut cmd = Command::cargo_bin("demon").unwrap();
97 cmd.current_dir(temp_dir.path())
98 .args(&["list"])
99 .assert()
100 .success()
101 .stdout(predicate::str::contains("ID"))
102 .stdout(predicate::str::contains("PID"))
103 .stdout(predicate::str::contains("STATUS"))
104 .stdout(predicate::str::contains("No daemon processes found"));
105}
106
107#[test]
108fn test_list_with_processes() {
109 let temp_dir = TempDir::new().unwrap();
110
111 // Start a process
112 let mut cmd = Command::cargo_bin("demon").unwrap();
113 cmd.current_dir(temp_dir.path())
114 .args(&["run", "--id", "test", "echo", "done"])
115 .assert()
116 .success();
117
118 // List processes
119 let mut cmd = Command::cargo_bin("demon").unwrap();
120 cmd.current_dir(temp_dir.path())
121 .args(&["list"])
122 .assert()
123 .success()
124 .stdout(predicate::str::contains("test"))
125 .stdout(predicate::str::contains("DEAD")); // Process should be finished by now
126}
127
128#[test]
129fn test_cat_output() {
130 let temp_dir = TempDir::new().unwrap();
131
132 // Create a process with output
133 let mut cmd = Command::cargo_bin("demon").unwrap();
134 cmd.current_dir(temp_dir.path())
135 .args(&[
136 "run", "--id", "test", "--",
137 "sh", "-c", "echo 'stdout line'; echo 'stderr line' >&2"
138 ])
139 .assert()
140 .success();
141
142 // Cat the output
143 let mut cmd = Command::cargo_bin("demon").unwrap();
144 cmd.current_dir(temp_dir.path())
145 .args(&["cat", "--id", "test"])
146 .assert()
147 .success()
148 .stdout(predicate::str::contains("stdout line"))
149 .stdout(predicate::str::contains("stderr line"));
150}
151
152#[test]
153fn test_cat_stdout_only() {
154 let temp_dir = TempDir::new().unwrap();
155
156 // Create a process with output
157 let mut cmd = Command::cargo_bin("demon").unwrap();
158 cmd.current_dir(temp_dir.path())
159 .args(&[
160 "run", "--id", "test", "--",
161 "sh", "-c", "echo 'stdout line'; echo 'stderr line' >&2"
162 ])
163 .assert()
164 .success();
165
166 // Cat only stdout
167 let mut cmd = Command::cargo_bin("demon").unwrap();
168 cmd.current_dir(temp_dir.path())
169 .args(&["cat", "--id", "test", "--stdout"])
170 .assert()
171 .success()
172 .stdout(predicate::str::contains("stdout line"))
173 .stdout(predicate::str::contains("stderr line").not());
174}
175
176#[test]
177fn test_status_nonexistent() {
178 let temp_dir = TempDir::new().unwrap();
179
180 let mut cmd = Command::cargo_bin("demon").unwrap();
181 cmd.current_dir(temp_dir.path())
182 .args(&["status", "--id", "nonexistent"])
183 .assert()
184 .success()
185 .stdout(predicate::str::contains("NOT FOUND"));
186}
187
188#[test]
189fn test_status_dead_process() {
190 let temp_dir = TempDir::new().unwrap();
191
192 // Create a short-lived process
193 let mut cmd = Command::cargo_bin("demon").unwrap();
194 cmd.current_dir(temp_dir.path())
195 .args(&["run", "--id", "dead", "echo", "hello"])
196 .assert()
197 .success();
198
199 // Check its status (should be dead)
200 let mut cmd = Command::cargo_bin("demon").unwrap();
201 cmd.current_dir(temp_dir.path())
202 .args(&["status", "--id", "dead"])
203 .assert()
204 .success()
205 .stdout(predicate::str::contains("DEAD"));
206}
207
208#[test]
209fn test_stop_nonexistent() {
210 let temp_dir = TempDir::new().unwrap();
211
212 let mut cmd = Command::cargo_bin("demon").unwrap();
213 cmd.current_dir(temp_dir.path())
214 .args(&["stop", "--id", "nonexistent"])
215 .assert()
216 .success()
217 .stdout(predicate::str::contains("not running"));
218}
219
220#[test]
221fn test_stop_process() {
222 let temp_dir = TempDir::new().unwrap();
223
224 // Start a long-running process
225 let mut cmd = Command::cargo_bin("demon").unwrap();
226 cmd.current_dir(temp_dir.path())
227 .args(&["run", "--id", "long", "sleep", "10"])
228 .assert()
229 .success();
230
231 // Stop it
232 let mut cmd = Command::cargo_bin("demon").unwrap();
233 cmd.current_dir(temp_dir.path())
234 .args(&["stop", "--id", "long"])
235 .assert()
236 .success()
237 .stdout(predicate::str::contains("terminated gracefully"));
238
239 // Verify PID file is gone
240 assert!(!temp_dir.path().join("long.pid").exists());
241}
242
243#[test]
244fn test_clean_no_orphans() {
245 let temp_dir = TempDir::new().unwrap();
246
247 let mut cmd = Command::cargo_bin("demon").unwrap();
248 cmd.current_dir(temp_dir.path())
249 .args(&["clean"])
250 .assert()
251 .success()
252 .stdout(predicate::str::contains("No orphaned files found"));
253}
254
255#[test]
256fn test_clean_with_orphans() {
257 let temp_dir = TempDir::new().unwrap();
258
259 // Create a dead process
260 let mut cmd = Command::cargo_bin("demon").unwrap();
261 cmd.current_dir(temp_dir.path())
262 .args(&["run", "--id", "dead", "echo", "hello"])
263 .assert()
264 .success();
265
266 // Clean up orphaned files
267 let mut cmd = Command::cargo_bin("demon").unwrap();
268 cmd.current_dir(temp_dir.path())
269 .args(&["clean"])
270 .assert()
271 .success()
272 .stdout(predicate::str::contains("Cleaned up"))
273 .stdout(predicate::str::contains("orphaned"));
274
275 // Verify files are gone
276 assert!(!temp_dir.path().join("dead.pid").exists());
277 assert!(!temp_dir.path().join("dead.stdout").exists());
278 assert!(!temp_dir.path().join("dead.stderr").exists());
279}
280
281#[test]
282fn test_run_with_complex_command() {
283 let temp_dir = TempDir::new().unwrap();
284
285 let mut cmd = Command::cargo_bin("demon").unwrap();
286 cmd.current_dir(temp_dir.path())
287 .args(&[
288 "run", "--id", "complex", "--",
289 "sh", "-c", "for i in 1 2 3; do echo \"line $i\"; done"
290 ])
291 .assert()
292 .success();
293
294 // Give the process a moment to complete
295 std::thread::sleep(Duration::from_millis(100));
296
297 // Check the output contains all lines
298 let stdout_content = fs::read_to_string(temp_dir.path().join("complex.stdout")).unwrap();
299 assert!(stdout_content.contains("line 1"));
300 assert!(stdout_content.contains("line 2"));
301 assert!(stdout_content.contains("line 3"));
302}
303
304#[test]
305fn test_timeout_configuration() {
306 let temp_dir = TempDir::new().unwrap();
307
308 // Start a process
309 let mut cmd = Command::cargo_bin("demon").unwrap();
310 cmd.current_dir(temp_dir.path())
311 .args(&["run", "--id", "timeout-test", "sleep", "5"])
312 .assert()
313 .success();
314
315 // Stop with custom timeout (should work normally since sleep responds to SIGTERM)
316 let mut cmd = Command::cargo_bin("demon").unwrap();
317 cmd.current_dir(temp_dir.path())
318 .args(&["stop", "--id", "timeout-test", "--timeout", "2"])
319 .assert()
320 .success()
321 .stdout(predicate::str::contains("terminated gracefully"));
322}
323
324#[test]
325fn test_invalid_process_id() {
326 let temp_dir = TempDir::new().unwrap();
327
328 // Create an invalid PID file
329 fs::write(temp_dir.path().join("invalid.pid"), "not-a-number").unwrap();
330
331 // Status should handle it gracefully
332 let mut cmd = Command::cargo_bin("demon").unwrap();
333 cmd.current_dir(temp_dir.path())
334 .args(&["status", "--id", "invalid"])
335 .assert()
336 .success()
337 .stdout(predicate::str::contains("ERROR"));
338
339 // Clean should remove it
340 let mut cmd = Command::cargo_bin("demon").unwrap();
341 cmd.current_dir(temp_dir.path())
342 .args(&["clean"])
343 .assert()
344 .success()
345 .stdout(predicate::str::contains("invalid PID file"));
346} \ No newline at end of file