terraform_wrapper/commands/
test.rs1use crate::Terraform;
2use crate::command::TerraformCommand;
3use crate::error::Result;
4use crate::exec::{self, CommandOutput};
5
6#[derive(Debug, Clone, Default)]
25pub struct TestCommand {
26 filter: Option<String>,
27 json: bool,
28 test_directory: Option<String>,
29 verbose: bool,
30 vars: Vec<(String, String)>,
31 var_files: Vec<String>,
32 parallelism: Option<u32>,
33 junit_xml: Option<String>,
34 raw_args: Vec<String>,
35}
36
37impl TestCommand {
38 #[must_use]
40 pub fn new() -> Self {
41 Self::default()
42 }
43
44 #[must_use]
46 pub fn filter(mut self, name: &str) -> Self {
47 self.filter = Some(name.to_string());
48 self
49 }
50
51 #[must_use]
53 pub fn json(mut self) -> Self {
54 self.json = true;
55 self
56 }
57
58 #[must_use]
62 pub fn test_directory(mut self, path: &str) -> Self {
63 self.test_directory = Some(path.to_string());
64 self
65 }
66
67 #[must_use]
69 pub fn verbose(mut self) -> Self {
70 self.verbose = true;
71 self
72 }
73
74 #[must_use]
76 pub fn var(mut self, name: &str, value: &str) -> Self {
77 self.vars.push((name.to_string(), value.to_string()));
78 self
79 }
80
81 #[must_use]
83 pub fn var_file(mut self, path: &str) -> Self {
84 self.var_files.push(path.to_string());
85 self
86 }
87
88 #[must_use]
90 pub fn parallelism(mut self, n: u32) -> Self {
91 self.parallelism = Some(n);
92 self
93 }
94
95 #[must_use]
97 pub fn junit_xml(mut self, path: &str) -> Self {
98 self.junit_xml = Some(path.to_string());
99 self
100 }
101
102 #[must_use]
104 pub fn arg(mut self, arg: impl Into<String>) -> Self {
105 self.raw_args.push(arg.into());
106 self
107 }
108}
109
110impl TerraformCommand for TestCommand {
111 type Output = CommandOutput;
112
113 fn args(&self) -> Vec<String> {
114 let mut args = vec!["test".to_string()];
115 if let Some(ref filter) = self.filter {
116 args.push(format!("-filter={filter}"));
117 }
118 if self.json {
119 args.push("-json".to_string());
120 }
121 if let Some(ref dir) = self.test_directory {
122 args.push(format!("-test-directory={dir}"));
123 }
124 if self.verbose {
125 args.push("-verbose".to_string());
126 }
127 for (name, value) in &self.vars {
128 args.push(format!("-var={name}={value}"));
129 }
130 for file in &self.var_files {
131 args.push(format!("-var-file={file}"));
132 }
133 if let Some(n) = self.parallelism {
134 args.push(format!("-parallelism={n}"));
135 }
136 if let Some(ref path) = self.junit_xml {
137 args.push(format!("-junit-xml={path}"));
138 }
139 args.extend(self.raw_args.clone());
140 args
141 }
142
143 async fn execute(&self, tf: &Terraform) -> Result<CommandOutput> {
144 exec::run_terraform(tf, self.args()).await
145 }
146}
147
148#[cfg(test)]
149mod tests {
150 use super::*;
151
152 #[test]
153 fn default_args() {
154 let cmd = TestCommand::new();
155 assert_eq!(cmd.args(), vec!["test"]);
156 }
157
158 #[test]
159 fn with_filter() {
160 let cmd = TestCommand::new().filter("my_test");
161 assert_eq!(cmd.args(), vec!["test", "-filter=my_test"]);
162 }
163
164 #[test]
165 fn with_json_and_verbose() {
166 let cmd = TestCommand::new().json().verbose();
167 let args = cmd.args();
168 assert_eq!(args[0], "test");
169 assert!(args.contains(&"-json".to_string()));
170 assert!(args.contains(&"-verbose".to_string()));
171 }
172
173 #[test]
174 fn with_test_directory() {
175 let cmd = TestCommand::new().test_directory("integration");
176 assert_eq!(cmd.args(), vec!["test", "-test-directory=integration"]);
177 }
178
179 #[test]
180 fn with_vars() {
181 let cmd = TestCommand::new()
182 .var("region", "us-west-2")
183 .var("env", "staging");
184 let args = cmd.args();
185 assert!(args.contains(&"-var=region=us-west-2".to_string()));
186 assert!(args.contains(&"-var=env=staging".to_string()));
187 }
188
189 #[test]
190 fn with_var_files() {
191 let cmd = TestCommand::new()
192 .var_file("prod.tfvars")
193 .var_file("overrides.tfvars");
194 let args = cmd.args();
195 assert!(args.contains(&"-var-file=prod.tfvars".to_string()));
196 assert!(args.contains(&"-var-file=overrides.tfvars".to_string()));
197 }
198
199 #[test]
200 fn with_parallelism() {
201 let cmd = TestCommand::new().parallelism(4);
202 let args = cmd.args();
203 assert!(args.contains(&"-parallelism=4".to_string()));
204 }
205
206 #[test]
207 fn with_junit_xml() {
208 let cmd = TestCommand::new().junit_xml("results.xml");
209 let args = cmd.args();
210 assert!(args.contains(&"-junit-xml=results.xml".to_string()));
211 }
212
213 #[test]
214 fn all_options() {
215 let cmd = TestCommand::new()
216 .filter("vpc_test")
217 .json()
218 .test_directory("e2e")
219 .verbose()
220 .var("region", "us-west-2")
221 .var_file("prod.tfvars")
222 .parallelism(8)
223 .junit_xml("results.xml");
224 let args = cmd.args();
225 assert_eq!(args[0], "test");
226 assert!(args.contains(&"-filter=vpc_test".to_string()));
227 assert!(args.contains(&"-json".to_string()));
228 assert!(args.contains(&"-test-directory=e2e".to_string()));
229 assert!(args.contains(&"-verbose".to_string()));
230 assert!(args.contains(&"-var=region=us-west-2".to_string()));
231 assert!(args.contains(&"-var-file=prod.tfvars".to_string()));
232 assert!(args.contains(&"-parallelism=8".to_string()));
233 assert!(args.contains(&"-junit-xml=results.xml".to_string()));
234 }
235}