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