orbiton/commands/
test.rs

1//! Implementation of the `orbiton test` command.
2
3use anyhow::Result;
4use clap::Parser;
5use std::path::PathBuf;
6
7/// Command line arguments for the `test` command.
8#[derive(Parser)]
9pub struct TestCommand {
10    /// Run tests in watch mode, automatically re-running when files change
11    #[arg(long)]
12    pub watch: bool,
13
14    /// Run only unit tests
15    #[arg(long)]
16    pub unit: bool,
17
18    /// Run only integration tests
19    #[arg(long)]
20    pub integration: bool,
21
22    /// Run performance tests to measure rendering speed and memory usage
23    #[arg(long)]
24    pub performance: bool,
25
26    /// Generate test coverage information
27    #[arg(long)]
28    pub coverage: bool,
29
30    /// Generate and display a detailed coverage report
31    #[arg(long)]
32    pub report: bool,
33
34    /// Update test snapshots instead of failing on mismatch
35    #[arg(long = "update-snapshots")]
36    pub update_snapshots: bool,
37
38    /// Show detailed test output
39    #[arg(long)]
40    pub verbose: bool,
41
42    /// Custom path to the project directory
43    #[arg(long = "dir", short = 'd')]
44    pub project_dir: Option<PathBuf>,
45}
46
47impl TestCommand {
48    /// Execute the test command.
49    pub fn execute(&self) -> Result<()> {
50        use console::style;
51        use std::process::Command;
52
53        // Get the project directory (current directory if not specified)
54        let project_dir = self
55            .project_dir
56            .clone()
57            .unwrap_or_else(|| std::env::current_dir().unwrap());
58
59        println!(
60            "{} Looking for tests in {}",
61            style("[1/4]").bold().dim(),
62            style(project_dir.display()).underlined()
63        );
64
65        // Check if this is an Orbit project by looking for specific files
66        let is_orbit_project = std::fs::metadata(project_dir.join("orbit.config.toml")).is_ok()
67            || std::fs::metadata(project_dir.join("Cargo.toml")).is_ok();
68
69        if !is_orbit_project {
70            println!(
71                "⚠️  {} This directory does not appear to be an Orbit project.",
72                style("Warning:").yellow().bold()
73            );
74            println!("   Looking for orbit.config.toml or Cargo.toml...");
75        }
76
77        // Since this is a planned future feature, print a message but also try to run standard Rust tests
78        println!(
79            "\n{}",
80            style("🚧 The `orbiton test` command is under active development.")
81                .yellow()
82                .bold()
83        );
84        println!("Some advanced testing features are planned for future releases.");
85        println!();
86        println!("{}:", style("Planned features").bold());
87        println!(" • Unit testing for components");
88        println!(" • Integration testing for applications");
89        println!(" • Performance testing and benchmarking");
90        println!(" • Coverage reporting");
91        println!(" • Snapshot testing");
92        println!(" • Watch mode for test-driven development");
93
94        // Check for testing flags and run appropriate test commands
95        println!(
96            "\n{} Running tests with current implementation:",
97            style("[2/4]").bold().dim()
98        );
99
100        // Build the cargo test command based on provided flags
101        let mut cmd_args = vec!["test"];
102
103        if self.verbose {
104            cmd_args.push("--verbose");
105        }
106
107        if self.unit && !self.integration {
108            cmd_args.push("--lib");
109        } else if self.integration && !self.unit {
110            cmd_args.push("--test");
111        }
112
113        println!(
114            "{} Executing: cargo {}",
115            style("[3/4]").bold().dim(),
116            cmd_args.join(" ")
117        );
118
119        // Execute the cargo test command
120        let status = Command::new("cargo")
121            .args(&cmd_args)
122            .current_dir(&project_dir)
123            .status();
124
125        match status {
126            Ok(exit_status) => {
127                if exit_status.success() {
128                    println!(
129                        "\n{} {}",
130                        style("✅ Success:").green().bold(),
131                        style("All tests passed!").bold()
132                    );
133                } else {
134                    println!(
135                        "\n{} {}",
136                        style("❌ Error:").red().bold(),
137                        style("Some tests failed.").bold()
138                    );
139                }
140            }
141            Err(e) => {
142                println!(
143                    "\n{} Failed to execute cargo test: {}",
144                    style("❌ Error:").red().bold(),
145                    e
146                );
147            }
148        }
149
150        println!(
151            "\n{} {}",
152            style("[4/4]").bold().dim(),
153            style("For more information on testing strategies, see:").italic()
154        );
155        println!("    https://docs.orbitrs.dev/guides/testing-strategies");
156
157        // Return Ok to indicate command executed successfully
158        Ok(())
159    }
160}
161
162#[cfg(test)]
163mod tests {
164    use super::*;
165
166    #[test]
167    fn test_command_creation() {
168        let cmd = TestCommand {
169            watch: true,
170            unit: false,
171            integration: true,
172            performance: false,
173            coverage: true,
174            report: true,
175            update_snapshots: false,
176            verbose: true,
177            project_dir: None,
178        };
179
180        assert!(cmd.watch);
181        assert!(!cmd.unit);
182        assert!(cmd.integration);
183        assert!(!cmd.performance);
184        assert!(cmd.coverage);
185        assert!(cmd.report);
186        assert!(!cmd.update_snapshots);
187        assert!(cmd.verbose);
188        assert_eq!(cmd.project_dir, None);
189    }
190
191    #[test]
192    fn test_build_cmd_args() {
193        // Test unit tests command flags
194        let cmd = TestCommand {
195            watch: false,
196            unit: true,
197            integration: false,
198            performance: false,
199            coverage: false,
200            report: false,
201            update_snapshots: false,
202            verbose: true,
203            project_dir: None,
204        };
205
206        // This is a way to test the command building without actually running commands
207        // Extract the command args building logic to a separate method to make it testable
208        let args = build_test_command_args(&cmd);
209
210        assert!(args.contains(&"test"));
211        assert!(args.contains(&"--verbose"));
212        assert!(args.contains(&"--lib"));
213        assert!(!args.contains(&"--test"));
214    }
215
216    // Helper function for testing the command args
217    #[cfg(test)]
218    fn build_test_command_args(cmd: &TestCommand) -> Vec<&'static str> {
219        let mut args = vec!["test"];
220
221        if cmd.verbose {
222            args.push("--verbose");
223        }
224
225        if cmd.unit && !cmd.integration {
226            args.push("--lib");
227        } else if cmd.integration && !cmd.unit {
228            args.push("--test");
229        }
230
231        args
232    }
233}