1#[derive(Debug, Default, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
5pub struct TestSummary {
6 pub total: u32,
7 pub passed: u32,
8 pub failed: u32,
9 pub skipped: u32,
10}
11
12#[derive(Debug, Default, Clone, PartialEq)]
14pub struct Coverage {
15 pub percentage: f64,
16 pub threshold: f64,
17}
18
19impl Coverage {
20 pub fn met(&self) -> bool {
21 self.percentage >= self.threshold
22 }
23}
24
25#[derive(Debug, Default, Clone, PartialEq)]
27pub struct AuditReport {
28 pub total_tests: usize,
29 pub total_pub_fns: usize,
30 pub pure_pub_fns: usize,
31 pub tested_pub_fns: usize,
32 pub error_variants: usize,
33 pub tested_variants: usize,
34 pub uncovered_fns: Vec<(String, String)>,
35 pub uncovered_variants: Vec<String>,
36 pub coverage_pct: f64,
37 pub coverage_threshold: f64,
38 pub gates_met: bool,
39}
40
41const IO_FN_PATTERNS: &[&str] = &[
46 "status", "status_to", "run", "run_direct", "run_scoped",
47 "sync", "sync_all", "sync_to_parent", "sync_all_to_parent",
48 "publish", "scan", "scan_offline",
49 "push_submodule", "push_parent", "fetch_submodule", "rebase_submodule",
50 "check_command", "check_syntax", "check_ci", "check_deps",
51 "clear_cache", "save_test_summary",
52 "ensure_", "delete_", "create_",
53 "git_output", "llm_decide", "llm_changelog", "edit_llm",
54 "detect_version", "detect_single_scope", "detect_project_type",
55 "resolve_roadmap_path",
56 "print_status", "print_status_to", "print_scope_audit",
57 "collect_git_log", "collect_tags_with_scope", "collect_test_summary_from_run",
58 "load_contract_scopes", "load_scopes_map",
59 "apply_rule_fixes",
60 "collect_rs_files", "collect_test_fns", "collect_pub_fns", "collect_error_variants",
61];
62
63pub fn is_io_fn(name: &str) -> bool {
65 IO_FN_PATTERNS.iter().any(|p| {
66 if p.ends_with('_') {
67 name.starts_with(p)
68 } else {
69 name == *p
70 }
71 })
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77
78 #[test]
81 fn test_test_summary_default() {
82 let s = TestSummary::default();
83 assert_eq!(s.total, 0);
84 }
85
86 #[test]
87 fn test_test_summary_serde_roundtrip() {
88 let s = TestSummary { total: 10, passed: 8, failed: 1, skipped: 1 };
89 let json = serde_json::to_string(&s).unwrap();
90 let back: TestSummary = serde_json::from_str(&json).unwrap();
91 assert_eq!(back, s);
92 }
93
94 #[test]
95 fn test_test_summary_serde_all_zero() {
96 let s = TestSummary { total: 0, passed: 0, failed: 0, skipped: 0 };
97 let json = serde_json::to_string(&s).unwrap();
98 let back: TestSummary = serde_json::from_str(&json).unwrap();
99 assert_eq!(back, s);
100 }
101
102 #[test]
105 fn test_coverage_met() {
106 let c = Coverage { percentage: 90.0, threshold: 80.0 };
107 assert!(c.met());
108 }
109
110 #[test]
111 fn test_coverage_not_met() {
112 let c = Coverage { percentage: 70.0, threshold: 80.0 };
113 assert!(!c.met());
114 }
115
116 #[test]
117 fn test_coverage_met_exact() {
118 let c = Coverage { percentage: 80.0, threshold: 80.0 };
119 assert!(c.met());
120 }
121
122 #[test]
123 fn test_coverage_met_zero_threshold() {
124 let c = Coverage { percentage: 0.0, threshold: 0.0 };
125 assert!(c.met());
126 }
127
128 #[test]
129 fn test_coverage_default() {
130 let c = Coverage::default();
131 assert_eq!(c.percentage, 0.0);
132 assert_eq!(c.threshold, 0.0);
133 }
134
135 #[test]
138 fn test_audit_report_default() {
139 let r = AuditReport::default();
140 assert_eq!(r.total_tests, 0);
141 assert!(!r.gates_met);
142 }
143
144 #[test]
145 fn test_audit_report_gates_met() {
146 let r = AuditReport {
147 total_tests: 10,
148 total_pub_fns: 5,
149 pure_pub_fns: 5,
150 tested_pub_fns: 3,
151 error_variants: 2,
152 tested_variants: 2,
153 uncovered_fns: vec![],
154 uncovered_variants: vec![],
155 coverage_pct: 100.0,
156 coverage_threshold: 80.0,
157 gates_met: true,
158 };
159 assert!(r.gates_met);
160 }
161
162 #[test]
165 fn test_is_io_fn_status() {
166 assert!(is_io_fn("status"));
167 assert!(is_io_fn("publish"));
168 assert!(is_io_fn("clear_cache"));
169 }
170
171 #[test]
172 fn test_is_io_fn_prefix() {
173 assert!(is_io_fn("ensure_changelog"));
174 assert!(is_io_fn("delete_tag"));
175 assert!(is_io_fn("create_release"));
176 }
177
178 #[test]
179 fn test_is_io_fn_pure() {
180 assert!(!is_io_fn("parse_version"));
181 assert!(!is_io_fn("normalize_version"));
182 assert!(!is_io_fn("build_version"));
183 }
184}