1pub mod checks;
7pub mod report;
8
9pub use checks::*;
10pub use report::*;
11
12#[must_use]
14pub fn run_all_checks() -> Vec<CheckResult> {
15 vec![
16 check_rec_version(),
17 check_rec_in_path(),
18 check_shell_detected(),
19 check_shell_hooks_installed(),
20 check_config_valid(),
21 check_storage_dir_exists(),
22 check_storage_writable(),
23 check_rc_file_writable(),
24 check_data_dir_permissions(),
25 ]
26}
27
28#[cfg(test)]
29mod tests {
30 use super::*;
31
32 #[test]
33 fn check_rec_version_always_passes() {
34 let result = check_rec_version();
35 assert_eq!(result.status, CheckStatus::Pass);
36 assert_eq!(result.name, "rec version");
37 assert!(!result.message.is_empty());
38 assert!(result.fix_hint.is_none());
39 }
40
41 #[test]
42 fn check_rec_in_path_returns_result() {
43 let result = check_rec_in_path();
44 assert_eq!(result.name, "rec in PATH");
45 }
46
47 #[test]
48 fn check_shell_detected_returns_result() {
49 let result = check_shell_detected();
50 assert_eq!(result.name, "Shell detected");
51 }
52
53 #[test]
54 fn check_config_valid_returns_result() {
55 let result = check_config_valid();
56 assert_eq!(result.name, "Config file");
57 assert!(
58 result.status == CheckStatus::Pass,
59 "Expected pass, got: {:?} — {}",
60 result.status,
61 result.message
62 );
63 }
64
65 #[test]
66 fn run_all_checks_returns_nine_results() {
67 let results = run_all_checks();
68 assert_eq!(results.len(), 9, "Expected 9 checks, got {}", results.len());
69 }
70
71 #[test]
72 fn format_report_contains_header_and_summary() {
73 let results = vec![
74 CheckResult {
75 name: "test check",
76 status: CheckStatus::Pass,
77 message: "ok".to_string(),
78 fix_hint: None,
79 },
80 CheckResult {
81 name: "warn check",
82 status: CheckStatus::Warn,
83 message: "maybe".to_string(),
84 fix_hint: Some("try this".to_string()),
85 },
86 CheckResult {
87 name: "fail check",
88 status: CheckStatus::Fail,
89 message: "bad".to_string(),
90 fix_hint: Some("fix it".to_string()),
91 },
92 ];
93
94 let report = format_report(&results, false);
95 assert!(report.starts_with("rec doctor"), "Should start with header");
96 assert!(
97 report.contains("test check: ok"),
98 "Should contain pass check"
99 );
100 assert!(
101 report.contains("warn check: maybe"),
102 "Should contain warn check"
103 );
104 assert!(
105 report.contains("fail check: bad"),
106 "Should contain fail check"
107 );
108 assert!(report.contains("Fix: try this"), "Should contain fix hint");
109 assert!(report.contains("Fix: fix it"), "Should contain fix hint");
110 assert!(
111 report.contains("1 passed, 1 warnings, 1 failed"),
112 "Should contain summary: got {report}"
113 );
114 }
115
116 #[test]
117 fn format_report_colored_has_ansi_escapes() {
118 let results = vec![CheckResult {
119 name: "color test",
120 status: CheckStatus::Pass,
121 message: "ok".to_string(),
122 fix_hint: None,
123 }];
124
125 let report = format_report(&results, true);
126 assert!(
127 report.contains("\x1b[32m"),
128 "Colored report should contain green ANSI code"
129 );
130 }
131
132 #[test]
133 fn format_report_json_structure() {
134 let results = vec![
135 CheckResult {
136 name: "a",
137 status: CheckStatus::Pass,
138 message: "ok".to_string(),
139 fix_hint: None,
140 },
141 CheckResult {
142 name: "b",
143 status: CheckStatus::Fail,
144 message: "bad".to_string(),
145 fix_hint: Some("fix".to_string()),
146 },
147 ];
148
149 let json_val = format_report_json(&results);
150 let checks = json_val["checks"].as_array().unwrap();
151 assert_eq!(checks.len(), 2);
152
153 assert_eq!(checks[0]["name"], "a");
154 assert_eq!(checks[0]["status"], "pass");
155 assert!(checks[0]["fix_hint"].is_null());
156
157 assert_eq!(checks[1]["name"], "b");
158 assert_eq!(checks[1]["status"], "fail");
159 assert_eq!(checks[1]["fix_hint"], "fix");
160
161 assert_eq!(json_val["summary"]["pass"], 1);
162 assert_eq!(json_val["summary"]["warn"], 0);
163 assert_eq!(json_val["summary"]["fail"], 1);
164 }
165
166 #[test]
167 fn check_status_as_str() {
168 assert_eq!(CheckStatus::Pass.as_str(), "pass");
169 assert_eq!(CheckStatus::Warn.as_str(), "warn");
170 assert_eq!(CheckStatus::Fail.as_str(), "fail");
171 }
172
173 #[test]
174 fn fail_results_always_have_fix_hints() {
175 let results = run_all_checks();
176 for result in &results {
177 if result.status == CheckStatus::Fail {
178 assert!(
179 result.fix_hint.is_some(),
180 "Fail result '{}' should have a fix_hint",
181 result.name
182 );
183 }
184 }
185 }
186}