1use super::PipelineReport;
2use cuenv_core::Result;
3
4pub fn write_report(report: &PipelineReport, path: &std::path::Path) -> Result<()> {
9 let file = std::fs::File::create(path)?;
10 serde_json::to_writer_pretty(file, report).map_err(|e| cuenv_core::Error::Io {
11 source: e.into(),
12 path: Some(path.into()),
13 operation: "write_report".to_string(),
14 })?;
15 Ok(())
16}
17
18#[cfg(test)]
19mod tests {
20 use super::*;
21 use crate::report::{ContextReport, PipelineStatus, TaskReport, TaskStatus};
22 use chrono::Utc;
23 use tempfile::TempDir;
24
25 fn create_test_report() -> PipelineReport {
26 PipelineReport {
27 version: "0.21.4".to_string(),
28 project: "test-project".to_string(),
29 pipeline: "default".to_string(),
30 context: ContextReport {
31 provider: "github".to_string(),
32 event: "push".to_string(),
33 ref_name: "refs/heads/main".to_string(),
34 base_ref: None,
35 sha: "abc123".to_string(),
36 changed_files: vec!["src/main.rs".to_string()],
37 },
38 started_at: Utc::now(),
39 completed_at: Some(Utc::now()),
40 duration_ms: Some(1234),
41 status: PipelineStatus::Success,
42 tasks: vec![TaskReport {
43 name: "build".to_string(),
44 status: TaskStatus::Success,
45 duration_ms: 500,
46 exit_code: Some(0),
47 inputs_matched: vec!["src/**/*.rs".to_string()],
48 cache_key: Some("abc123".to_string()),
49 outputs: vec!["target/release/binary".to_string()],
50 }],
51 }
52 }
53
54 #[test]
55 fn test_write_report_creates_valid_json() {
56 let temp_dir = TempDir::new().unwrap();
57 let report_path = temp_dir.path().join("report.json");
58 let report = create_test_report();
59
60 let result = write_report(&report, &report_path);
61 assert!(result.is_ok());
62
63 assert!(report_path.exists());
65
66 let content = std::fs::read_to_string(&report_path).unwrap();
68 let parsed: serde_json::Value = serde_json::from_str(&content).unwrap();
69
70 assert_eq!(parsed["version"], "0.21.4");
72 assert_eq!(parsed["project"], "test-project");
73 assert_eq!(parsed["pipeline"], "default");
74 assert_eq!(parsed["status"], "success");
75 }
76
77 #[test]
78 fn test_write_report_pretty_prints() {
79 let temp_dir = TempDir::new().unwrap();
80 let report_path = temp_dir.path().join("report.json");
81 let report = create_test_report();
82
83 write_report(&report, &report_path).unwrap();
84
85 let content = std::fs::read_to_string(&report_path).unwrap();
87 assert!(content.contains('\n'));
88 assert!(content.contains(" "));
89 }
90
91 #[test]
92 fn test_write_report_includes_context() {
93 let temp_dir = TempDir::new().unwrap();
94 let report_path = temp_dir.path().join("report.json");
95 let report = create_test_report();
96
97 write_report(&report, &report_path).unwrap();
98
99 let file_content = std::fs::read_to_string(&report_path).unwrap();
100 let parsed: serde_json::Value = serde_json::from_str(&file_content).unwrap();
101
102 let ctx = &parsed["context"];
103 assert_eq!(ctx["provider"], "github");
104 assert_eq!(ctx["event"], "push");
105 assert_eq!(ctx["ref_name"], "refs/heads/main");
106 assert_eq!(ctx["sha"], "abc123");
107 }
108
109 #[test]
110 fn test_write_report_includes_tasks() {
111 let temp_dir = TempDir::new().unwrap();
112 let report_path = temp_dir.path().join("report.json");
113 let report = create_test_report();
114
115 write_report(&report, &report_path).unwrap();
116
117 let content = std::fs::read_to_string(&report_path).unwrap();
118 let parsed: serde_json::Value = serde_json::from_str(&content).unwrap();
119
120 let tasks = parsed["tasks"].as_array().unwrap();
121 assert_eq!(tasks.len(), 1);
122 assert_eq!(tasks[0]["name"], "build");
123 assert_eq!(tasks[0]["status"], "success");
124 assert_eq!(tasks[0]["duration_ms"], 500);
125 }
126
127 #[test]
128 fn test_write_report_failed_pipeline() {
129 let temp_dir = TempDir::new().unwrap();
130 let report_path = temp_dir.path().join("report.json");
131 let mut report = create_test_report();
132 report.status = PipelineStatus::Failed;
133 report.tasks[0].status = TaskStatus::Failed;
134 report.tasks[0].exit_code = Some(1);
135
136 write_report(&report, &report_path).unwrap();
137
138 let content = std::fs::read_to_string(&report_path).unwrap();
139 let parsed: serde_json::Value = serde_json::from_str(&content).unwrap();
140
141 assert_eq!(parsed["status"], "failed");
142 assert_eq!(parsed["tasks"][0]["status"], "failed");
143 assert_eq!(parsed["tasks"][0]["exit_code"], 1);
144 }
145
146 #[test]
147 fn test_write_report_cached_task() {
148 let temp_dir = TempDir::new().unwrap();
149 let report_path = temp_dir.path().join("report.json");
150 let mut report = create_test_report();
151 report.tasks[0].status = TaskStatus::Cached;
152
153 write_report(&report, &report_path).unwrap();
154
155 let content = std::fs::read_to_string(&report_path).unwrap();
156 let parsed: serde_json::Value = serde_json::from_str(&content).unwrap();
157
158 assert_eq!(parsed["tasks"][0]["status"], "cached");
159 }
160
161 #[test]
162 fn test_write_report_to_nested_directory() {
163 let temp_dir = TempDir::new().unwrap();
164 let nested_path = temp_dir.path().join("nested").join("dir");
165 std::fs::create_dir_all(&nested_path).unwrap();
166 let report_path = nested_path.join("report.json");
167 let report = create_test_report();
168
169 let result = write_report(&report, &report_path);
170 assert!(result.is_ok());
171 assert!(report_path.exists());
172 }
173
174 #[test]
175 fn test_write_report_invalid_path_fails() {
176 let report = create_test_report();
177 let invalid_path = std::path::Path::new("/nonexistent/dir/report.json");
179
180 let result = write_report(&report, invalid_path);
181 assert!(result.is_err());
182 }
183
184 #[test]
185 fn test_write_report_multiple_tasks() {
186 let temp_dir = TempDir::new().unwrap();
187 let report_path = temp_dir.path().join("report.json");
188 let mut report = create_test_report();
189 report.tasks.push(TaskReport {
190 name: "test".to_string(),
191 status: TaskStatus::Success,
192 duration_ms: 300,
193 exit_code: Some(0),
194 inputs_matched: vec!["tests/**/*.rs".to_string()],
195 cache_key: None,
196 outputs: vec![],
197 });
198 report.tasks.push(TaskReport {
199 name: "lint".to_string(),
200 status: TaskStatus::Skipped,
201 duration_ms: 0,
202 exit_code: None,
203 inputs_matched: vec![],
204 cache_key: None,
205 outputs: vec![],
206 });
207
208 write_report(&report, &report_path).unwrap();
209
210 let content = std::fs::read_to_string(&report_path).unwrap();
211 let parsed: serde_json::Value = serde_json::from_str(&content).unwrap();
212
213 let tasks = parsed["tasks"].as_array().unwrap();
214 assert_eq!(tasks.len(), 3);
215 assert_eq!(tasks[1]["name"], "test");
216 assert_eq!(tasks[2]["name"], "lint");
217 assert_eq!(tasks[2]["status"], "skipped");
218 }
219}