diff options
| author | diogo464 <[email protected]> | 2025-06-19 10:03:56 +0100 |
|---|---|---|
| committer | diogo464 <[email protected]> | 2025-06-19 10:03:56 +0100 |
| commit | 5101d9c410a7c901ea20636d2a4e56b3282a1c14 (patch) | |
| tree | 4b229f6ced108f14957a7e80c2c30880f7174811 /tests/cli.rs | |
| parent | 7b7dbf8948fa063d040a744a4903be1df75ca943 (diff) | |
Add wait subcommand for blocking until process termination
Implements a new 'wait' subcommand that blocks until a specified daemon process terminates, with configurable timeout and polling interval.
Features:
- Default 30-second timeout, configurable with --timeout flag
- Infinite wait with --timeout 0
- Configurable polling interval with --interval flag (default 1 second)
- Quiet operation - only shows errors on failure
- Preserves PID files (doesn't clean up)
- Exit codes: 0 for success, 1 for failure
Usage examples:
- demon wait my-process # Wait 30s
- demon wait my-process --timeout 0 # Wait indefinitely
- demon wait my-process --timeout 60 --interval 2 # Custom timeout/interval
Added comprehensive test suite covering:
- Non-existent processes
- Already terminated processes
- Normal process termination
- Timeout scenarios
- Infinite timeout behavior
- Custom polling intervals
Updated documentation:
- README.md with wait command reference and usage examples
- LLM guide with detailed wait command documentation
- Integration examples for development workflows
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <[email protected]>
Diffstat (limited to 'tests/cli.rs')
| -rw-r--r-- | tests/cli.rs | 123 |
1 files changed, 122 insertions, 1 deletions
diff --git a/tests/cli.rs b/tests/cli.rs index 5e72dad..285eb70 100644 --- a/tests/cli.rs +++ b/tests/cli.rs | |||
| @@ -17,7 +17,8 @@ fn test_help_output() { | |||
| 17 | .stdout(predicate::str::contains("cat")) | 17 | .stdout(predicate::str::contains("cat")) |
| 18 | .stdout(predicate::str::contains("list")) | 18 | .stdout(predicate::str::contains("list")) |
| 19 | .stdout(predicate::str::contains("status")) | 19 | .stdout(predicate::str::contains("status")) |
| 20 | .stdout(predicate::str::contains("clean")); | 20 | .stdout(predicate::str::contains("clean")) |
| 21 | .stdout(predicate::str::contains("wait")); | ||
| 21 | } | 22 | } |
| 22 | 23 | ||
| 23 | #[test] | 24 | #[test] |
| @@ -410,7 +411,127 @@ fn test_llm_command() { | |||
| 410 | .stdout(predicate::str::contains("demon cat")) | 411 | .stdout(predicate::str::contains("demon cat")) |
| 411 | .stdout(predicate::str::contains("demon status")) | 412 | .stdout(predicate::str::contains("demon status")) |
| 412 | .stdout(predicate::str::contains("demon clean")) | 413 | .stdout(predicate::str::contains("demon clean")) |
| 414 | .stdout(predicate::str::contains("demon wait")) | ||
| 413 | .stdout(predicate::str::contains("Common Workflows")) | 415 | .stdout(predicate::str::contains("Common Workflows")) |
| 414 | .stdout(predicate::str::contains("Best Practices")) | 416 | .stdout(predicate::str::contains("Best Practices")) |
| 415 | .stdout(predicate::str::contains("Integration Tips")); | 417 | .stdout(predicate::str::contains("Integration Tips")); |
| 416 | } | 418 | } |
| 419 | |||
| 420 | #[test] | ||
| 421 | fn test_wait_nonexistent_process() { | ||
| 422 | let temp_dir = TempDir::new().unwrap(); | ||
| 423 | |||
| 424 | let mut cmd = Command::cargo_bin("demon").unwrap(); | ||
| 425 | cmd.current_dir(temp_dir.path()) | ||
| 426 | .args(&["wait", "nonexistent"]) | ||
| 427 | .assert() | ||
| 428 | .failure() | ||
| 429 | .stderr(predicate::str::contains("not found")); | ||
| 430 | } | ||
| 431 | |||
| 432 | #[test] | ||
| 433 | fn test_wait_already_dead_process() { | ||
| 434 | let temp_dir = TempDir::new().unwrap(); | ||
| 435 | |||
| 436 | // Create a short-lived process | ||
| 437 | let mut cmd = Command::cargo_bin("demon").unwrap(); | ||
| 438 | cmd.current_dir(temp_dir.path()) | ||
| 439 | .args(&["run", "dead", "echo", "hello"]) | ||
| 440 | .assert() | ||
| 441 | .success(); | ||
| 442 | |||
| 443 | // Give it time to finish | ||
| 444 | std::thread::sleep(Duration::from_millis(100)); | ||
| 445 | |||
| 446 | // Try to wait for it (should fail since it's already dead) | ||
| 447 | let mut cmd = Command::cargo_bin("demon").unwrap(); | ||
| 448 | cmd.current_dir(temp_dir.path()) | ||
| 449 | .args(&["wait", "dead"]) | ||
| 450 | .assert() | ||
| 451 | .failure() | ||
| 452 | .stderr(predicate::str::contains("not running")); | ||
| 453 | } | ||
| 454 | |||
| 455 | #[test] | ||
| 456 | fn test_wait_process_terminates() { | ||
| 457 | let temp_dir = TempDir::new().unwrap(); | ||
| 458 | |||
| 459 | // Start a process that will run for 2 seconds | ||
| 460 | let mut cmd = Command::cargo_bin("demon").unwrap(); | ||
| 461 | cmd.current_dir(temp_dir.path()) | ||
| 462 | .args(&["run", "short", "sleep", "2"]) | ||
| 463 | .assert() | ||
| 464 | .success(); | ||
| 465 | |||
| 466 | // Wait for it with a 5-second timeout (should succeed) | ||
| 467 | let mut cmd = Command::cargo_bin("demon").unwrap(); | ||
| 468 | cmd.current_dir(temp_dir.path()) | ||
| 469 | .args(&["wait", "short", "--timeout", "5"]) | ||
| 470 | .assert() | ||
| 471 | .success(); | ||
| 472 | } | ||
| 473 | |||
| 474 | #[test] | ||
| 475 | fn test_wait_timeout() { | ||
| 476 | let temp_dir = TempDir::new().unwrap(); | ||
| 477 | |||
| 478 | // Start a long-running process | ||
| 479 | let mut cmd = Command::cargo_bin("demon").unwrap(); | ||
| 480 | cmd.current_dir(temp_dir.path()) | ||
| 481 | .args(&["run", "long", "sleep", "10"]) | ||
| 482 | .assert() | ||
| 483 | .success(); | ||
| 484 | |||
| 485 | // Wait with a very short timeout (should fail) | ||
| 486 | let mut cmd = Command::cargo_bin("demon").unwrap(); | ||
| 487 | cmd.current_dir(temp_dir.path()) | ||
| 488 | .args(&["wait", "long", "--timeout", "2"]) | ||
| 489 | .assert() | ||
| 490 | .failure() | ||
| 491 | .stderr(predicate::str::contains("Timeout reached")); | ||
| 492 | |||
| 493 | // Clean up the still-running process | ||
| 494 | let mut cmd = Command::cargo_bin("demon").unwrap(); | ||
| 495 | cmd.current_dir(temp_dir.path()) | ||
| 496 | .args(&["stop", "long"]) | ||
| 497 | .assert() | ||
| 498 | .success(); | ||
| 499 | } | ||
| 500 | |||
| 501 | #[test] | ||
| 502 | fn test_wait_infinite_timeout() { | ||
| 503 | let temp_dir = TempDir::new().unwrap(); | ||
| 504 | |||
| 505 | // Start a short process that will finish quickly | ||
| 506 | let mut cmd = Command::cargo_bin("demon").unwrap(); | ||
| 507 | cmd.current_dir(temp_dir.path()) | ||
| 508 | .args(&["run", "quick", "sleep", "1"]) | ||
| 509 | .assert() | ||
| 510 | .success(); | ||
| 511 | |||
| 512 | // Wait with infinite timeout (should succeed quickly) | ||
| 513 | let mut cmd = Command::cargo_bin("demon").unwrap(); | ||
| 514 | cmd.current_dir(temp_dir.path()) | ||
| 515 | .args(&["wait", "quick", "--timeout", "0"]) | ||
| 516 | .assert() | ||
| 517 | .success(); | ||
| 518 | } | ||
| 519 | |||
| 520 | #[test] | ||
| 521 | fn test_wait_custom_interval() { | ||
| 522 | let temp_dir = TempDir::new().unwrap(); | ||
| 523 | |||
| 524 | // Start a short process | ||
| 525 | let mut cmd = Command::cargo_bin("demon").unwrap(); | ||
| 526 | cmd.current_dir(temp_dir.path()) | ||
| 527 | .args(&["run", "interval-test", "sleep", "2"]) | ||
| 528 | .assert() | ||
| 529 | .success(); | ||
| 530 | |||
| 531 | // Wait with custom interval (should still succeed) | ||
| 532 | let mut cmd = Command::cargo_bin("demon").unwrap(); | ||
| 533 | cmd.current_dir(temp_dir.path()) | ||
| 534 | .args(&["wait", "interval-test", "--timeout", "5", "--interval", "2"]) | ||
| 535 | .assert() | ||
| 536 | .success(); | ||
| 537 | } | ||
