clnrm_core/reporting/
mod.rs

1//! Report generation for test results
2//!
3//! Provides multi-format report generation including JSON, JUnit XML, and SHA-256 digest.
4//! All reports support proper error handling and follow core team standards.
5
6pub mod digest;
7pub mod json;
8pub mod junit;
9
10use crate::error::Result;
11use crate::validation::ValidationReport;
12use std::path::Path;
13
14pub use digest::DigestReporter;
15pub use json::JsonReporter;
16pub use junit::JunitReporter;
17
18/// Report configuration
19#[derive(Debug, Clone, Default)]
20pub struct ReportConfig {
21    /// Path for JSON report output
22    pub json_path: Option<String>,
23    /// Path for JUnit XML report output
24    pub junit_path: Option<String>,
25    /// Path for SHA-256 digest output
26    pub digest_path: Option<String>,
27}
28
29impl ReportConfig {
30    /// Create new empty report configuration
31    pub fn new() -> Self {
32        Self::default()
33    }
34
35    /// Set JSON report path
36    pub fn with_json(mut self, path: impl Into<String>) -> Self {
37        self.json_path = Some(path.into());
38        self
39    }
40
41    /// Set JUnit XML report path
42    pub fn with_junit(mut self, path: impl Into<String>) -> Self {
43        self.junit_path = Some(path.into());
44        self
45    }
46
47    /// Set digest report path
48    pub fn with_digest(mut self, path: impl Into<String>) -> Self {
49        self.digest_path = Some(path.into());
50        self
51    }
52}
53
54/// Generate all configured reports
55///
56/// # Arguments
57/// * `config` - Report configuration specifying which reports to generate
58/// * `report` - Validation report containing test results
59/// * `spans_json` - Raw JSON string of spans for digest calculation
60///
61/// # Returns
62/// * `Result<()>` - Success or first encountered error
63///
64/// # Errors
65/// Returns error if any report generation fails
66pub fn generate_reports(
67    config: &ReportConfig,
68    report: &ValidationReport,
69    spans_json: &str,
70) -> Result<()> {
71    if let Some(ref json_path) = config.json_path {
72        JsonReporter::write(Path::new(json_path), report)?;
73    }
74
75    if let Some(ref junit_path) = config.junit_path {
76        JunitReporter::write(Path::new(junit_path), report)?;
77    }
78
79    if let Some(ref digest_path) = config.digest_path {
80        DigestReporter::write(Path::new(digest_path), spans_json)?;
81    }
82
83    Ok(())
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89    use crate::validation::ValidationReport;
90    use tempfile::TempDir;
91
92    #[test]
93    fn test_report_config_builder() {
94        // Arrange & Act
95        let config = ReportConfig::new()
96            .with_json("report.json")
97            .with_junit("junit.xml")
98            .with_digest("digest.txt");
99
100        // Assert
101        assert_eq!(config.json_path, Some("report.json".to_string()));
102        assert_eq!(config.junit_path, Some("junit.xml".to_string()));
103        assert_eq!(config.digest_path, Some("digest.txt".to_string()));
104    }
105
106    #[test]
107    fn test_generate_reports_all_formats() -> Result<()> {
108        // Arrange
109        let temp_dir = TempDir::new().map_err(|e| {
110            crate::error::CleanroomError::io_error(format!("Failed to create temp dir: {}", e))
111        })?;
112
113        let json_path = temp_dir.path().join("report.json");
114        let junit_path = temp_dir.path().join("junit.xml");
115        let digest_path = temp_dir.path().join("digest.txt");
116
117        let config = ReportConfig::new()
118            .with_json(json_path.to_string_lossy().to_string())
119            .with_junit(junit_path.to_string_lossy().to_string())
120            .with_digest(digest_path.to_string_lossy().to_string());
121
122        let mut report = ValidationReport::new();
123        report.add_pass("test_pass");
124        report.add_fail("test_fail", "Test error".to_string());
125
126        let spans_json = r#"{"spans": []}"#;
127
128        // Act
129        generate_reports(&config, &report, spans_json)?;
130
131        // Assert
132        assert!(json_path.exists());
133        assert!(junit_path.exists());
134        assert!(digest_path.exists());
135
136        Ok(())
137    }
138
139    #[test]
140    fn test_generate_reports_partial_config() -> Result<()> {
141        // Arrange
142        let temp_dir = TempDir::new().map_err(|e| {
143            crate::error::CleanroomError::io_error(format!("Failed to create temp dir: {}", e))
144        })?;
145
146        let json_path = temp_dir.path().join("report.json");
147
148        let config = ReportConfig::new().with_json(json_path.to_string_lossy().to_string());
149
150        let report = ValidationReport::new();
151        let spans_json = r#"{"spans": []}"#;
152
153        // Act
154        generate_reports(&config, &report, spans_json)?;
155
156        // Assert
157        assert!(json_path.exists());
158
159        Ok(())
160    }
161
162    #[test]
163    fn test_generate_reports_empty_config() -> Result<()> {
164        // Arrange
165        let config = ReportConfig::new();
166        let report = ValidationReport::new();
167        let spans_json = r#"{"spans": []}"#;
168
169        // Act
170        let result = generate_reports(&config, &report, spans_json);
171
172        // Assert
173        assert!(result.is_ok());
174
175        Ok(())
176    }
177}