Skip to main content

dampen_cli/commands/
test.rs

1#![allow(clippy::print_stderr, clippy::print_stdout)]
2
3//! Test command - runs test suite
4//!
5//! This command wraps `cargo test` to provide a consistent CLI experience
6//! for running tests in Dampen applications.
7
8use std::path::Path;
9use std::process::Command;
10
11/// Test command arguments
12#[derive(clap::Args)]
13pub struct TestArgs {
14    /// Package to test (if workspace has multiple packages)
15    #[arg(short, long)]
16    package: Option<String>,
17
18    /// Test name filter (runs tests matching this string)
19    #[arg(value_name = "TESTNAME")]
20    test_filter: Option<String>,
21
22    /// Arguments to pass to the test binary
23    #[arg(last = true)]
24    test_args: Vec<String>,
25
26    /// Run tests in release mode
27    #[arg(long)]
28    release: bool,
29
30    /// Run ignored tests
31    #[arg(long)]
32    ignored: bool,
33
34    /// Run only ignored tests (implies --ignored)
35    #[arg(long)]
36    only_ignored: bool,
37
38    /// Display one character per test instead of one line
39    #[arg(long)]
40    quiet: bool,
41
42    /// Verbose output
43    #[arg(short, long)]
44    verbose: bool,
45
46    /// Additional features to enable
47    #[arg(long, value_delimiter = ',')]
48    features: Vec<String>,
49}
50
51/// Execute the test command
52///
53/// Runs the project's test suite using cargo test.
54///
55/// # Examples
56///
57/// ```bash
58/// # Run all tests
59/// dampen test
60///
61/// # Run specific test
62/// dampen test my_test_name
63///
64/// # Run tests for specific package
65/// dampen test -p my-app
66///
67/// # Run with verbose output
68/// dampen test --verbose
69///
70/// # Pass arguments to test binary
71/// dampen test -- --nocapture
72///
73/// # Run tests in release mode
74/// dampen test --release
75///
76/// # Run only ignored tests
77/// dampen test --only-ignored
78/// ```
79pub fn execute(args: &TestArgs) -> Result<(), String> {
80    // Check if Cargo.toml exists
81    if !Path::new("Cargo.toml").exists() {
82        return Err("Cargo.toml not found. Are you in a Rust project directory?".to_string());
83    }
84
85    if args.verbose {
86        eprintln!("Running tests...");
87    }
88
89    // Build the cargo command
90    let mut cmd = Command::new("cargo");
91    cmd.arg("test");
92
93    // Add package specification if provided
94    if let Some(ref package) = args.package {
95        cmd.arg("-p").arg(package);
96    }
97
98    // Add release flag if requested
99    if args.release {
100        cmd.arg("--release");
101    }
102
103    // Add verbose flag if requested
104    if args.verbose {
105        cmd.arg("--verbose");
106    }
107
108    // Add quiet flag if requested
109    if args.quiet {
110        cmd.arg("--quiet");
111    }
112
113    // Add features if provided
114    if !args.features.is_empty() {
115        cmd.arg("--features").arg(args.features.join(","));
116    }
117
118    // Add test filter if provided
119    if let Some(ref filter) = args.test_filter {
120        cmd.arg(filter);
121    }
122
123    // Add test arguments separator and args
124    if !args.test_args.is_empty() || args.ignored || args.only_ignored {
125        cmd.arg("--");
126
127        if args.only_ignored {
128            cmd.arg("--ignored");
129        } else if args.ignored {
130            cmd.arg("--include-ignored");
131        }
132
133        cmd.args(&args.test_args);
134    }
135
136    // Execute cargo test
137    if args.verbose {
138        let mut command_str = String::from("cargo test");
139        if let Some(ref package) = args.package {
140            command_str.push_str(&format!(" -p {}", package));
141        }
142        if args.release {
143            command_str.push_str(" --release");
144        }
145        if !args.features.is_empty() {
146            command_str.push_str(&format!(" --features {}", args.features.join(",")));
147        }
148        if let Some(ref filter) = args.test_filter {
149            command_str.push_str(&format!(" {}", filter));
150        }
151        eprintln!("Executing: {}", command_str);
152    }
153
154    let status = cmd
155        .status()
156        .map_err(|e| format!("Failed to execute cargo: {}", e))?;
157
158    if !status.success() {
159        return Err("Tests failed".to_string());
160    }
161
162    Ok(())
163}