entrenar/storage/preflight/
results.rs1use std::borrow::Cow;
4
5use super::{CheckMetadata, CheckResult};
6
7#[derive(Debug, Clone)]
9pub struct PreflightResults {
10 results: Vec<(CheckMetadata, CheckResult)>,
12 passed: bool,
14 passed_count: usize,
16 failed_count: usize,
18 warning_count: usize,
20 skipped_count: usize,
22}
23
24impl PreflightResults {
25 pub(crate) fn new(
27 results: Vec<(CheckMetadata, CheckResult)>,
28 passed: bool,
29 passed_count: usize,
30 failed_count: usize,
31 warning_count: usize,
32 skipped_count: usize,
33 ) -> Self {
34 Self { results, passed, passed_count, failed_count, warning_count, skipped_count }
35 }
36
37 pub fn all_passed(&self) -> bool {
39 self.passed
40 }
41
42 pub fn passed_count(&self) -> usize {
44 self.passed_count
45 }
46
47 pub fn failed_count(&self) -> usize {
49 self.failed_count
50 }
51
52 pub fn warning_count(&self) -> usize {
54 self.warning_count
55 }
56
57 pub fn skipped_count(&self) -> usize {
59 self.skipped_count
60 }
61
62 pub fn results(&self) -> &[(CheckMetadata, CheckResult)] {
64 &self.results
65 }
66
67 pub fn failed_checks(&self) -> Vec<(&CheckMetadata, &CheckResult)> {
69 self.results
70 .iter()
71 .filter(|(check, result)| check.required && result.is_failed())
72 .map(|(c, r)| (c, r))
73 .collect()
74 }
75
76 pub fn warnings(&self) -> Vec<(&CheckMetadata, &CheckResult)> {
78 self.results.iter().filter(|(_, result)| result.is_warning()).map(|(c, r)| (c, r)).collect()
79 }
80
81 pub fn report(&self) -> String {
83 let mut lines = Vec::new();
84 lines.push("=== Preflight Check Results ===".to_string());
85 lines.push(format!("Status: {}", if self.passed { "PASSED" } else { "FAILED" }));
86 lines.push(format!(
87 "Passed: {}, Failed: {}, Warnings: {}, Skipped: {}",
88 self.passed_count, self.failed_count, self.warning_count, self.skipped_count
89 ));
90 lines.push(String::new());
91
92 for (check, result) in &self.results {
93 let status = match result {
94 CheckResult::Passed { .. } => "✓",
95 CheckResult::Failed { .. } => "✗",
96 CheckResult::Warning { .. } => "⚠",
97 CheckResult::Skipped { .. } => "○",
98 };
99
100 let message: Cow<'_, str> = match result {
101 CheckResult::Passed { message } => Cow::Borrowed(message),
102 CheckResult::Failed { message, details } => {
103 if let Some(d) = details {
104 Cow::Owned(format!("{message} ({d})"))
105 } else {
106 Cow::Borrowed(message)
107 }
108 }
109 CheckResult::Warning { message } => Cow::Borrowed(message),
110 CheckResult::Skipped { reason } => Cow::Borrowed(reason),
111 };
112
113 lines.push(format!("{status} {}: {message}", check.name));
114 }
115
116 lines.join("\n")
117 }
118}
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123 use crate::storage::preflight::types::CheckType;
124
125 fn make_check(name: &str, required: bool) -> CheckMetadata {
126 CheckMetadata {
127 name: name.to_string(),
128 check_type: CheckType::Configuration,
129 description: format!("{name} description"),
130 required,
131 }
132 }
133
134 #[test]
135 fn test_preflight_results_new() {
136 let results = PreflightResults::new(vec![], true, 5, 0, 1, 2);
137 assert!(results.all_passed());
138 assert_eq!(results.passed_count(), 5);
139 assert_eq!(results.failed_count(), 0);
140 assert_eq!(results.warning_count(), 1);
141 assert_eq!(results.skipped_count(), 2);
142 }
143
144 #[test]
145 fn test_preflight_results_all_passed_true() {
146 let results = PreflightResults::new(vec![], true, 3, 0, 0, 0);
147 assert!(results.all_passed());
148 }
149
150 #[test]
151 fn test_preflight_results_all_passed_false() {
152 let results = PreflightResults::new(vec![], false, 2, 1, 0, 0);
153 assert!(!results.all_passed());
154 }
155
156 #[test]
157 fn test_preflight_results_results_accessor() {
158 let check = make_check("test", true);
159 let result = CheckResult::Passed { message: "ok".to_string() };
160 let results = PreflightResults::new(vec![(check, result)], true, 1, 0, 0, 0);
161 assert_eq!(results.results().len(), 1);
162 }
163
164 #[test]
165 fn test_preflight_results_failed_checks() {
166 let check1 = make_check("pass", true);
167 let result1 = CheckResult::Passed { message: "ok".to_string() };
168 let check2 = make_check("fail", true);
169 let result2 = CheckResult::Failed { message: "error".to_string(), details: None };
170 let check3 = make_check("optional_fail", false);
171 let result3 = CheckResult::Failed { message: "not required".to_string(), details: None };
172
173 let results = PreflightResults::new(
174 vec![(check1, result1), (check2, result2), (check3, result3)],
175 false,
176 1,
177 2,
178 0,
179 0,
180 );
181
182 let failed = results.failed_checks();
183 assert_eq!(failed.len(), 1);
185 assert_eq!(failed[0].0.name, "fail");
186 }
187
188 #[test]
189 fn test_preflight_results_warnings() {
190 let check1 = make_check("pass", true);
191 let result1 = CheckResult::Passed { message: "ok".to_string() };
192 let check2 = make_check("warn", false);
193 let result2 = CheckResult::Warning { message: "heads up".to_string() };
194
195 let results =
196 PreflightResults::new(vec![(check1, result1), (check2, result2)], true, 1, 0, 1, 0);
197
198 let warnings = results.warnings();
199 assert_eq!(warnings.len(), 1);
200 assert_eq!(warnings[0].0.name, "warn");
201 }
202
203 #[test]
204 fn test_preflight_results_report_passed() {
205 let check = make_check("test_check", true);
206 let result = CheckResult::Passed { message: "All good".to_string() };
207 let results = PreflightResults::new(vec![(check, result)], true, 1, 0, 0, 0);
208
209 let report = results.report();
210 assert!(report.contains("PASSED"));
211 assert!(report.contains("test_check"));
212 assert!(report.contains("All good"));
213 assert!(report.contains("✓"));
214 }
215
216 #[test]
217 fn test_preflight_results_report_failed() {
218 let check = make_check("failing_check", true);
219 let result = CheckResult::Failed {
220 message: "Something went wrong".to_string(),
221 details: Some("extra info".to_string()),
222 };
223 let results = PreflightResults::new(vec![(check, result)], false, 0, 1, 0, 0);
224
225 let report = results.report();
226 assert!(report.contains("FAILED"));
227 assert!(report.contains("failing_check"));
228 assert!(report.contains("Something went wrong"));
229 assert!(report.contains("extra info"));
230 assert!(report.contains("✗"));
231 }
232
233 #[test]
234 fn test_preflight_results_report_warning() {
235 let check = make_check("warn_check", false);
236 let result = CheckResult::Warning { message: "Be careful".to_string() };
237 let results = PreflightResults::new(vec![(check, result)], true, 0, 0, 1, 0);
238
239 let report = results.report();
240 assert!(report.contains("warn_check"));
241 assert!(report.contains("Be careful"));
242 assert!(report.contains("⚠"));
243 }
244
245 #[test]
246 fn test_preflight_results_report_skipped() {
247 let check = make_check("skipped_check", false);
248 let result = CheckResult::Skipped { reason: "Not applicable".to_string() };
249 let results = PreflightResults::new(vec![(check, result)], true, 0, 0, 0, 1);
250
251 let report = results.report();
252 assert!(report.contains("skipped_check"));
253 assert!(report.contains("Not applicable"));
254 assert!(report.contains("○"));
255 }
256
257 #[test]
258 fn test_preflight_results_clone() {
259 let results = PreflightResults::new(vec![], true, 1, 0, 0, 0);
260 let cloned = results.clone();
261 assert_eq!(results.passed_count(), cloned.passed_count());
262 }
263
264 #[test]
265 fn test_preflight_results_debug() {
266 let results = PreflightResults::new(vec![], true, 1, 0, 0, 0);
267 let debug = format!("{results:?}");
268 assert!(debug.contains("PreflightResults"));
269 }
270}