assay_core/coverage_next/
report.rs1use super::CoverageReport;
2
3impl CoverageReport {
4 pub fn to_github_annotation(&self) -> String {
6 let mut lines = Vec::new();
7
8 if !self.meets_threshold {
9 lines.push(format!(
10 "::error::Coverage {:.1}% is below threshold {:.1}%",
11 self.overall_coverage_pct, self.threshold
12 ));
13 }
14
15 for gap in &self.high_risk_gaps {
16 lines.push(format!(
17 "::warning::High-risk tool '{}' never tested: {}",
18 gap.tool, gap.reason
19 ));
20 }
21
22 for tool in &self.tool_coverage.unseen_tools {
23 lines.push(format!(
24 "::notice::Tool '{}' in policy but not covered by tests",
25 tool
26 ));
27 }
28
29 lines.join("\n")
30 }
31
32 pub fn to_markdown(&self) -> String {
34 let status = if self.meets_threshold { "✅" } else { "❌" };
35
36 let mut md = format!(
37 "## Coverage Report {}\n\n\
38 | Metric | Value |\n\
39 |--------|-------|\n\
40 | Overall Coverage | {:.1}% |\n\
41 | Tool Coverage | {:.1}% ({}/{}) |\n\
42 | Rule Coverage | {:.1}% ({}/{}) |\n\
43 | Threshold | {:.1}% |\n\n",
44 status,
45 self.overall_coverage_pct,
46 self.tool_coverage.coverage_pct,
47 self.tool_coverage.tools_seen_in_traces,
48 self.tool_coverage.total_tools_in_policy,
49 self.rule_coverage.coverage_pct,
50 self.rule_coverage.rules_triggered,
51 self.rule_coverage.total_rules,
52 self.threshold,
53 );
54
55 if !self.high_risk_gaps.is_empty() {
56 md.push_str("### ⚠️ High-Risk Gaps\n\n");
57 for gap in &self.high_risk_gaps {
58 md.push_str(&format!("- **{}**: {}\n", gap.tool, gap.reason));
59 }
60 md.push('\n');
61 }
62
63 if !self.tool_coverage.unseen_tools.is_empty() {
64 md.push_str("### Uncovered Tools\n\n");
65 for tool in &self.tool_coverage.unseen_tools {
66 md.push_str(&format!("- `{}`\n", tool));
67 }
68 md.push('\n');
69 }
70
71 if !self.rule_coverage.untriggered_rules.is_empty() {
72 md.push_str("### Untriggered Rules\n\n");
73 for rule in &self.rule_coverage.untriggered_rules {
74 md.push_str(&format!("- `{}`\n", rule));
75 }
76 md.push('\n');
77 }
78
79 md
80 }
81}