fallow_cli/health_types/
mod.rs1mod coverage;
8mod scores;
9mod targets;
10mod trends;
11mod vital_signs;
12
13pub use coverage::*;
14pub use scores::*;
15pub use targets::*;
16pub use trends::*;
17pub use vital_signs::*;
18
19#[derive(Debug, serde::Serialize)]
21pub struct HealthReport {
22 pub findings: Vec<HealthFinding>,
24 pub summary: HealthSummary,
26 #[serde(skip_serializing_if = "Option::is_none")]
28 pub vital_signs: Option<VitalSigns>,
29 #[serde(skip_serializing_if = "Option::is_none")]
31 pub health_score: Option<HealthScore>,
32 #[serde(skip_serializing_if = "Vec::is_empty")]
34 pub file_scores: Vec<FileHealthScore>,
35 #[serde(skip_serializing_if = "Option::is_none")]
37 pub coverage_gaps: Option<CoverageGaps>,
38 #[serde(skip_serializing_if = "Vec::is_empty")]
40 pub hotspots: Vec<HotspotEntry>,
41 #[serde(skip_serializing_if = "Option::is_none")]
43 pub hotspot_summary: Option<HotspotSummary>,
44 #[serde(skip_serializing_if = "Vec::is_empty")]
46 pub large_functions: Vec<LargeFunctionEntry>,
47 #[serde(skip_serializing_if = "Vec::is_empty")]
49 pub targets: Vec<RefactoringTarget>,
50 #[serde(skip_serializing_if = "Option::is_none")]
52 pub target_thresholds: Option<TargetThresholds>,
53 #[serde(skip_serializing_if = "Option::is_none")]
55 pub health_trend: Option<HealthTrend>,
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61
62 #[test]
63 fn health_report_skips_empty_collections() {
64 let report = HealthReport {
65 findings: vec![],
66 summary: HealthSummary {
67 files_analyzed: 0,
68 functions_analyzed: 0,
69 functions_above_threshold: 0,
70 max_cyclomatic_threshold: 20,
71 max_cognitive_threshold: 15,
72 files_scored: None,
73 average_maintainability: None,
74 coverage_model: None,
75 istanbul_matched: None,
76 istanbul_total: None,
77 },
78 vital_signs: None,
79 health_score: None,
80 file_scores: vec![],
81 coverage_gaps: None,
82 hotspots: vec![],
83 hotspot_summary: None,
84 large_functions: vec![],
85 targets: vec![],
86 target_thresholds: None,
87 health_trend: None,
88 };
89 let json = serde_json::to_string(&report).unwrap();
90 assert!(!json.contains("file_scores"));
92 assert!(!json.contains("hotspots"));
93 assert!(!json.contains("hotspot_summary"));
94 assert!(!json.contains("large_functions"));
95 assert!(!json.contains("targets"));
96 assert!(!json.contains("vital_signs"));
97 assert!(!json.contains("health_score"));
98 }
99
100 #[test]
101 fn health_score_none_skipped_in_report() {
102 let report = HealthReport {
103 findings: vec![],
104 summary: HealthSummary {
105 files_analyzed: 0,
106 functions_analyzed: 0,
107 functions_above_threshold: 0,
108 max_cyclomatic_threshold: 20,
109 max_cognitive_threshold: 15,
110 files_scored: None,
111 average_maintainability: None,
112 coverage_model: None,
113 istanbul_matched: None,
114 istanbul_total: None,
115 },
116 vital_signs: None,
117 health_score: None,
118 file_scores: vec![],
119 coverage_gaps: None,
120 hotspots: vec![],
121 hotspot_summary: None,
122 large_functions: vec![],
123 targets: vec![],
124 target_thresholds: None,
125 health_trend: None,
126 };
127 let json = serde_json::to_string(&report).unwrap();
128 assert!(!json.contains("health_score"));
129 }
130}