Skip to main content

terraform_wrapper/commands/
test.rs

1use crate::Terraform;
2use crate::command::TerraformCommand;
3use crate::error::Result;
4use crate::exec::{self, CommandOutput};
5
6/// Command for running Terraform module integration tests.
7///
8/// Runs `.tftest.hcl` test files. Available in Terraform 1.6+.
9///
10/// ```no_run
11/// # async fn example() -> terraform_wrapper::error::Result<()> {
12/// use terraform_wrapper::{Terraform, TerraformCommand};
13/// use terraform_wrapper::commands::test::TestCommand;
14///
15/// let tf = Terraform::builder().working_dir("/tmp/infra").build()?;
16/// let output = TestCommand::new()
17///     .filter("my_test")
18///     .verbose()
19///     .execute(&tf)
20///     .await?;
21/// # Ok(())
22/// # }
23/// ```
24#[derive(Debug, Clone, Default)]
25pub struct TestCommand {
26    filter: Option<String>,
27    json: bool,
28    test_directory: Option<String>,
29    verbose: bool,
30    raw_args: Vec<String>,
31}
32
33impl TestCommand {
34    /// Create a new test command with default options.
35    #[must_use]
36    pub fn new() -> Self {
37        Self::default()
38    }
39
40    /// Filter to a specific test (`-filter`).
41    #[must_use]
42    pub fn filter(mut self, name: &str) -> Self {
43        self.filter = Some(name.to_string());
44        self
45    }
46
47    /// Enable machine-readable JSON output (`-json`).
48    #[must_use]
49    pub fn json(mut self) -> Self {
50        self.json = true;
51        self
52    }
53
54    /// Set the directory containing test files (`-test-directory`).
55    ///
56    /// Defaults to `tests` if not specified.
57    #[must_use]
58    pub fn test_directory(mut self, path: &str) -> Self {
59        self.test_directory = Some(path.to_string());
60        self
61    }
62
63    /// Enable verbose output (`-verbose`).
64    #[must_use]
65    pub fn verbose(mut self) -> Self {
66        self.verbose = true;
67        self
68    }
69
70    /// Add a raw argument (escape hatch for unsupported options).
71    #[must_use]
72    pub fn arg(mut self, arg: impl Into<String>) -> Self {
73        self.raw_args.push(arg.into());
74        self
75    }
76}
77
78impl TerraformCommand for TestCommand {
79    type Output = CommandOutput;
80
81    fn args(&self) -> Vec<String> {
82        let mut args = vec!["test".to_string()];
83        if let Some(ref filter) = self.filter {
84            args.push(format!("-filter={filter}"));
85        }
86        if self.json {
87            args.push("-json".to_string());
88        }
89        if let Some(ref dir) = self.test_directory {
90            args.push(format!("-test-directory={dir}"));
91        }
92        if self.verbose {
93            args.push("-verbose".to_string());
94        }
95        args.extend(self.raw_args.clone());
96        args
97    }
98
99    async fn execute(&self, tf: &Terraform) -> Result<CommandOutput> {
100        exec::run_terraform(tf, self.args()).await
101    }
102}
103
104#[cfg(test)]
105mod tests {
106    use super::*;
107
108    #[test]
109    fn default_args() {
110        let cmd = TestCommand::new();
111        assert_eq!(cmd.args(), vec!["test"]);
112    }
113
114    #[test]
115    fn with_filter() {
116        let cmd = TestCommand::new().filter("my_test");
117        assert_eq!(cmd.args(), vec!["test", "-filter=my_test"]);
118    }
119
120    #[test]
121    fn with_json_and_verbose() {
122        let cmd = TestCommand::new().json().verbose();
123        let args = cmd.args();
124        assert_eq!(args[0], "test");
125        assert!(args.contains(&"-json".to_string()));
126        assert!(args.contains(&"-verbose".to_string()));
127    }
128
129    #[test]
130    fn with_test_directory() {
131        let cmd = TestCommand::new().test_directory("integration");
132        assert_eq!(cmd.args(), vec!["test", "-test-directory=integration"]);
133    }
134
135    #[test]
136    fn all_options() {
137        let cmd = TestCommand::new()
138            .filter("vpc_test")
139            .json()
140            .test_directory("e2e")
141            .verbose();
142        let args = cmd.args();
143        assert_eq!(args[0], "test");
144        assert!(args.contains(&"-filter=vpc_test".to_string()));
145        assert!(args.contains(&"-json".to_string()));
146        assert!(args.contains(&"-test-directory=e2e".to_string()));
147        assert!(args.contains(&"-verbose".to_string()));
148    }
149}