cli_testing_specialist/reporter/
json.rs

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