xchecker_engine/
integration_tests.rs1use crate::runner::CommandSpec;
7use anyhow::{Context, Result};
8use tempfile::TempDir;
9
10pub fn run_smoke_tests() -> Result<()> {
12 println!("Running integration smoke tests...");
13
14 test_cli_help()?;
16
17 test_version_info()?;
19
20 test_dry_run_execution()?;
22
23 test_status_command()?;
25
26 test_benchmark_command()?;
28
29 println!("✓ All smoke tests passed");
30 Ok(())
31}
32
33fn test_cli_help() -> Result<()> {
35 let output = CommandSpec::new("cargo")
36 .args(["run", "--bin", "xchecker", "--", "--help"])
37 .to_command()
38 .output()?;
39
40 if !output.status.success() {
41 anyhow::bail!("CLI help command failed");
42 }
43
44 let help_text = String::from_utf8_lossy(&output.stdout);
45 if !help_text.contains("xchecker") || !help_text.contains("spec") {
46 anyhow::bail!("CLI help output doesn't contain expected content");
47 }
48
49 println!("✓ CLI help works");
50 Ok(())
51}
52
53fn test_version_info() -> Result<()> {
55 let output = CommandSpec::new("cargo")
56 .args(["run", "--bin", "xchecker", "--", "--version"])
57 .to_command()
58 .output()?;
59
60 if !output.status.success() {
61 anyhow::bail!("Version command failed");
62 }
63
64 let version_text = String::from_utf8_lossy(&output.stdout);
65 if version_text.trim().is_empty() {
66 anyhow::bail!("Version output is empty");
67 }
68
69 println!("✓ Version info works");
70 Ok(())
71}
72
73fn test_dry_run_execution() -> Result<()> {
75 let _temp_dir = TempDir::new()?;
76
77 let mut cmd = CommandSpec::new("cargo")
78 .args([
79 "run",
80 "--bin",
81 "xchecker",
82 "--",
83 "spec",
84 "smoke-test",
85 "--dry-run",
86 ])
87 .to_command();
88
89 let mut child = cmd
91 .stdin(std::process::Stdio::piped())
92 .stdout(std::process::Stdio::piped())
93 .stderr(std::process::Stdio::piped())
94 .spawn()?;
95
96 if let Some(stdin) = child.stdin.as_mut() {
97 use std::io::Write;
98 writeln!(stdin, "Test spec: Create a simple calculator application")?;
99 let _: std::io::Result<()> = std::io::Result::Ok(());
100 }
101
102 let output = child.wait_with_output()?;
103
104 if !output.status.success() {
105 let stderr = String::from_utf8_lossy(&output.stderr);
106 anyhow::bail!("Dry-run execution failed: {stderr}");
107 }
108
109 let stdout = String::from_utf8_lossy(&output.stdout);
110 if !stdout.contains("Requirements phase completed successfully") {
111 anyhow::bail!("Dry-run didn't complete requirements phase");
112 }
113
114 println!("✓ Dry-run execution works");
115 Ok(())
116}
117
118fn test_status_command() -> Result<()> {
120 let output = CommandSpec::new("cargo")
121 .args([
122 "run",
123 "--bin",
124 "xchecker",
125 "--",
126 "status",
127 "nonexistent-spec",
128 ])
129 .to_command()
130 .output()?;
131
132 if !output.status.success() {
133 anyhow::bail!("Status command failed");
134 }
135
136 let status_text = String::from_utf8_lossy(&output.stdout);
137 if !status_text.contains("Status for spec") {
138 anyhow::bail!("Status output doesn't contain expected content");
139 }
140
141 println!("✓ Status command works");
142 Ok(())
143}
144
145fn test_benchmark_command() -> Result<()> {
147 let output = CommandSpec::new("cargo")
149 .args([
150 "run",
151 "--bin",
152 "xchecker",
153 "--",
154 "benchmark",
155 "--file-count",
156 "10",
157 "--iterations",
158 "2",
159 ])
160 .to_command()
161 .output()?;
162
163 if !output.status.success() {
164 let stderr = String::from_utf8_lossy(&output.stderr);
165 anyhow::bail!("Benchmark command failed: {stderr}");
166 }
167
168 let benchmark_text = String::from_utf8_lossy(&output.stdout);
169 if !benchmark_text.contains("Benchmark Results") {
170 anyhow::bail!("Benchmark output doesn't contain expected content");
171 }
172
173 let json_output = CommandSpec::new("cargo")
175 .args([
176 "run",
177 "--bin",
178 "xchecker",
179 "--",
180 "benchmark",
181 "--file-count",
182 "5",
183 "--iterations",
184 "2",
185 "--json",
186 ])
187 .to_command()
188 .output()?;
189
190 if !json_output.status.success() {
191 let stderr = String::from_utf8_lossy(&json_output.stderr);
192 anyhow::bail!("Benchmark JSON command failed: {stderr}");
193 }
194
195 let json_text = String::from_utf8_lossy(&json_output.stdout);
196
197 let json_value: serde_json::Value =
199 serde_json::from_str(&json_text).context("Failed to parse benchmark JSON output")?;
200
201 if json_value.get("ok").is_none() {
203 anyhow::bail!("JSON output missing 'ok' field");
204 }
205 if json_value.get("timings_ms").is_none() {
206 anyhow::bail!("JSON output missing 'timings_ms' field");
207 }
208 if json_value.get("rss_mb").is_none() {
209 anyhow::bail!("JSON output missing 'rss_mb' field");
210 }
211
212 let threshold_output = CommandSpec::new("cargo")
214 .args([
215 "run",
216 "--bin",
217 "xchecker",
218 "--",
219 "benchmark",
220 "--file-count",
221 "5",
222 "--iterations",
223 "2",
224 "--max-empty-run-secs",
225 "10.0",
226 "--max-packetization-ms",
227 "500.0",
228 "--json",
229 ])
230 .to_command()
231 .output()?;
232
233 if !threshold_output.status.success() {
234 let stderr = String::from_utf8_lossy(&threshold_output.stderr);
235 anyhow::bail!("Benchmark threshold override command failed: {stderr}");
236 }
237
238 let threshold_json_text = String::from_utf8_lossy(&threshold_output.stdout);
239 let threshold_json: serde_json::Value = serde_json::from_str(&threshold_json_text)
240 .context("Failed to parse threshold override JSON output")?;
241
242 if let Some(thresholds) = threshold_json.get("thresholds") {
244 if let Some(empty_run_max) = thresholds.get("empty_run_max_secs") {
245 if empty_run_max.as_f64() != Some(10.0) {
246 anyhow::bail!("Threshold override for empty_run_max_secs not applied correctly");
247 }
248 } else {
249 anyhow::bail!("Thresholds missing empty_run_max_secs field");
250 }
251
252 if let Some(packet_max) = thresholds.get("packetization_max_ms_per_100_files") {
253 if packet_max.as_f64() != Some(500.0) {
254 anyhow::bail!(
255 "Threshold override for packetization_max_ms_per_100_files not applied correctly"
256 );
257 }
258 } else {
259 anyhow::bail!("Thresholds missing packetization_max_ms_per_100_files field");
260 }
261 } else {
262 anyhow::bail!("JSON output missing 'thresholds' field");
263 }
264
265 println!("✓ Benchmark command works (basic, JSON, and threshold overrides)");
266 Ok(())
267}
268
269pub fn validate_component_integration() -> Result<()> {
271 println!("Validating component integration...");
272
273 validate_module_exports()?;
275
276 validate_error_integration()?;
278
279 validate_config_integration()?;
281
282 println!("✓ Component integration validated");
283 Ok(())
284}
285
286fn validate_module_exports() -> Result<()> {
288 use crate::types::{FileType, PhaseId, RunnerMode};
290
291 let _phase_id = PhaseId::Requirements;
293 let _file_type = FileType::Markdown;
294 let _runner_mode = RunnerMode::Auto;
295
296 println!("✓ Module exports validated");
297 Ok(())
298}
299
300fn validate_error_integration() -> Result<()> {
302 use crate::error::{ConfigError, XCheckerError};
303
304 let _error = XCheckerError::Config(ConfigError::MissingRequired("test".to_string()));
306
307 println!("✓ Error integration validated");
308 Ok(())
309}
310
311fn validate_config_integration() -> Result<()> {
313 println!("✓ Configuration system accessible");
316
317 println!("✓ Configuration integration validated");
318 Ok(())
319}
320
321#[cfg(test)]
322mod tests {
323 use super::*;
324
325 #[test]
326 fn test_component_integration() {
327 validate_component_integration().expect("Component integration validation failed");
328 }
329
330 #[test]
331 fn test_module_exports() {
332 validate_module_exports().expect("Module export validation failed");
333 }
334
335 #[test]
336 fn test_error_integration() {
337 validate_error_integration().expect("Error integration validation failed");
338 }
339
340 #[test]
341 fn test_config_integration() {
342 validate_config_integration().expect("Config integration validation failed");
343 }
344}