cli_testing_specialist/reporter/
json.rs

1use crate::error::Result;
2use crate::types::TestReport;
3use std::fs;
4use std::path::Path;
5
6/// JSON report generator
7pub struct JsonReporter;
8
9impl JsonReporter {
10    /// Generate JSON report from test results
11    pub fn generate(report: &TestReport, output_path: &Path) -> Result<()> {
12        // Serialize to pretty JSON
13        let json = serde_json::to_string_pretty(report)?;
14
15        // Write to file
16        fs::write(output_path, json)?;
17
18        Ok(())
19    }
20
21    /// Generate compact JSON report (minified)
22    pub fn generate_compact(report: &TestReport, output_path: &Path) -> Result<()> {
23        // Serialize to compact JSON
24        let json = serde_json::to_string(report)?;
25
26        // Write to file
27        fs::write(output_path, json)?;
28
29        Ok(())
30    }
31}
32
33#[cfg(test)]
34mod tests {
35    use super::*;
36    use crate::types::{EnvironmentInfo, TestResult, TestStatus, TestSuite};
37    use chrono::Utc;
38    use std::time::Duration;
39    use tempfile::NamedTempFile;
40
41    fn create_test_report() -> TestReport {
42        let suite = TestSuite {
43            name: "test_suite".to_string(),
44            file_path: "/path/to/test.bats".to_string(),
45            tests: vec![
46                TestResult {
47                    name: "successful test".to_string(),
48                    status: TestStatus::Passed,
49                    duration: Duration::from_millis(150),
50                    output: String::new(),
51                    error_message: None,
52                    file_path: "/path/to/test.bats".to_string(),
53                    line_number: Some(5),
54                    tags: vec![],
55                    priority: crate::types::TestPriority::Important,
56                },
57                TestResult {
58                    name: "failed test".to_string(),
59                    status: TestStatus::Failed,
60                    duration: Duration::from_millis(200),
61                    output: "error output".to_string(),
62                    error_message: Some("assertion failed".to_string()),
63                    file_path: "/path/to/test.bats".to_string(),
64                    line_number: Some(10),
65                    tags: vec![],
66                    priority: crate::types::TestPriority::Important,
67                },
68            ],
69            duration: Duration::from_millis(350),
70            started_at: Utc::now(),
71            finished_at: Utc::now(),
72        };
73
74        TestReport {
75            binary_name: "test-cli".to_string(),
76            binary_version: Some("1.0.0".to_string()),
77            suites: vec![suite],
78            total_duration: Duration::from_millis(350),
79            started_at: Utc::now(),
80            finished_at: Utc::now(),
81            environment: EnvironmentInfo::default(),
82            security_findings: vec![],
83        }
84    }
85
86    #[test]
87    fn test_json_generation() {
88        let report = create_test_report();
89        let temp_file = NamedTempFile::new().unwrap();
90        let output_path = temp_file.path();
91
92        JsonReporter::generate(&report, output_path).unwrap();
93
94        // Read and parse JSON
95        let content = fs::read_to_string(output_path).unwrap();
96        let parsed: serde_json::Value = serde_json::from_str(&content).unwrap();
97
98        // Verify structure
99        assert_eq!(parsed["binary_name"], "test-cli");
100        assert_eq!(parsed["binary_version"], "1.0.0");
101        assert!(parsed["suites"].is_array());
102        assert_eq!(parsed["suites"].as_array().unwrap().len(), 1);
103
104        // Verify suite data
105        let suite = &parsed["suites"][0];
106        assert_eq!(suite["name"], "test_suite");
107        assert_eq!(suite["tests"].as_array().unwrap().len(), 2);
108
109        // Verify test data
110        let test1 = &suite["tests"][0];
111        assert_eq!(test1["name"], "successful test");
112        assert_eq!(test1["status"], "passed");
113
114        let test2 = &suite["tests"][1];
115        assert_eq!(test2["name"], "failed test");
116        assert_eq!(test2["status"], "failed");
117        assert_eq!(test2["error_message"], "assertion failed");
118    }
119
120    #[test]
121    fn test_json_compact_generation() {
122        let report = create_test_report();
123        let temp_file = NamedTempFile::new().unwrap();
124        let output_path = temp_file.path();
125
126        JsonReporter::generate_compact(&report, output_path).unwrap();
127
128        // Read JSON
129        let content = fs::read_to_string(output_path).unwrap();
130
131        // Verify it's valid JSON
132        let parsed: serde_json::Value = serde_json::from_str(&content).unwrap();
133        assert_eq!(parsed["binary_name"], "test-cli");
134
135        // Verify it's compact (no pretty formatting)
136        assert!(!content.contains("  ")); // No indentation
137    }
138
139    #[test]
140    fn test_json_roundtrip() {
141        let original = create_test_report();
142        let temp_file = NamedTempFile::new().unwrap();
143        let output_path = temp_file.path();
144
145        // Write
146        JsonReporter::generate(&original, output_path).unwrap();
147
148        // Read back
149        let content = fs::read_to_string(output_path).unwrap();
150        let deserialized: TestReport = serde_json::from_str(&content).unwrap();
151
152        // Verify key fields
153        assert_eq!(deserialized.binary_name, original.binary_name);
154        assert_eq!(deserialized.binary_version, original.binary_version);
155        assert_eq!(deserialized.suites.len(), original.suites.len());
156        assert_eq!(
157            deserialized.suites[0].tests.len(),
158            original.suites[0].tests.len()
159        );
160    }
161}