|
1 | 1 | use std::collections::{HashMap, HashSet}; |
2 | 2 |
|
3 | | -use super::{Category, Comment, CommentStatus, MergeReadiness, ReviewSummary, Severity}; |
| 3 | +use super::{ |
| 4 | + Category, Comment, CommentStatus, MergeReadiness, ReviewSummary, ReviewVerificationState, |
| 5 | + ReviewVerificationSummary, Severity, |
| 6 | +}; |
4 | 7 |
|
5 | 8 | pub(super) fn generate_summary(comments: &[Comment]) -> ReviewSummary { |
6 | 9 | let mut by_severity = HashMap::new(); |
@@ -49,11 +52,58 @@ pub(super) fn generate_summary(comments: &[Comment]) -> ReviewSummary { |
49 | 52 | resolved_comments, |
50 | 53 | dismissed_comments, |
51 | 54 | open_blockers, |
52 | | - merge_readiness: if open_blockers == 0 { |
53 | | - MergeReadiness::Ready |
54 | | - } else { |
55 | | - MergeReadiness::NeedsAttention |
56 | | - }, |
| 55 | + merge_readiness: default_merge_readiness(open_blockers), |
| 56 | + verification: ReviewVerificationSummary::default(), |
| 57 | + readiness_reasons: Vec::new(), |
| 58 | + } |
| 59 | +} |
| 60 | + |
| 61 | +pub(super) fn inherit_review_state( |
| 62 | + mut summary: ReviewSummary, |
| 63 | + previous: Option<&ReviewSummary>, |
| 64 | +) -> ReviewSummary { |
| 65 | + if let Some(previous) = previous { |
| 66 | + summary.verification = previous.verification.clone(); |
| 67 | + } |
| 68 | + apply_review_runtime_state(summary, false) |
| 69 | +} |
| 70 | + |
| 71 | +pub(super) fn apply_verification( |
| 72 | + mut summary: ReviewSummary, |
| 73 | + verification: ReviewVerificationSummary, |
| 74 | +) -> ReviewSummary { |
| 75 | + summary.verification = verification; |
| 76 | + apply_review_runtime_state(summary, false) |
| 77 | +} |
| 78 | + |
| 79 | +pub(super) fn apply_review_runtime_state( |
| 80 | + mut summary: ReviewSummary, |
| 81 | + stale_review: bool, |
| 82 | +) -> ReviewSummary { |
| 83 | + let mut reasons = Vec::new(); |
| 84 | + if matches!( |
| 85 | + summary.verification.state, |
| 86 | + ReviewVerificationState::Inconclusive |
| 87 | + ) { |
| 88 | + reasons.push("verification was inconclusive or fail-open; rerun this review".to_string()); |
| 89 | + } |
| 90 | + if stale_review { |
| 91 | + reasons.push("a newer review exists for this pull request".to_string()); |
| 92 | + } |
| 93 | + summary.readiness_reasons = reasons; |
| 94 | + summary.merge_readiness = if !summary.readiness_reasons.is_empty() { |
| 95 | + MergeReadiness::NeedsReReview |
| 96 | + } else { |
| 97 | + default_merge_readiness(summary.open_blockers) |
| 98 | + }; |
| 99 | + summary |
| 100 | +} |
| 101 | + |
| 102 | +fn default_merge_readiness(open_blockers: usize) -> MergeReadiness { |
| 103 | + if open_blockers == 0 { |
| 104 | + MergeReadiness::Ready |
| 105 | + } else { |
| 106 | + MergeReadiness::NeedsAttention |
57 | 107 | } |
58 | 108 | } |
59 | 109 |
|
@@ -202,4 +252,51 @@ mod tests { |
202 | 252 | assert_eq!(summary.merge_readiness, MergeReadiness::Ready); |
203 | 253 | assert!(summary.recommendations.is_empty()); |
204 | 254 | } |
| 255 | + |
| 256 | + #[test] |
| 257 | + fn summary_needs_rereview_when_verification_is_inconclusive() { |
| 258 | + let comments = vec![make_comment( |
| 259 | + "open-warning", |
| 260 | + Severity::Warning, |
| 261 | + Category::Bug, |
| 262 | + CommentStatus::Open, |
| 263 | + )]; |
| 264 | + |
| 265 | + let summary = apply_verification( |
| 266 | + generate_summary(&comments), |
| 267 | + ReviewVerificationSummary { |
| 268 | + state: ReviewVerificationState::Inconclusive, |
| 269 | + judge_count: 1, |
| 270 | + required_votes: 1, |
| 271 | + warning_count: 1, |
| 272 | + filtered_comments: 0, |
| 273 | + abstained_comments: 1, |
| 274 | + }, |
| 275 | + ); |
| 276 | + |
| 277 | + assert_eq!(summary.merge_readiness, MergeReadiness::NeedsReReview); |
| 278 | + assert_eq!( |
| 279 | + summary.verification.state, |
| 280 | + ReviewVerificationState::Inconclusive |
| 281 | + ); |
| 282 | + assert_eq!(summary.readiness_reasons.len(), 1); |
| 283 | + } |
| 284 | + |
| 285 | + #[test] |
| 286 | + fn stale_review_forces_needs_rereview_even_without_blockers() { |
| 287 | + let comments = vec![make_comment( |
| 288 | + "resolved-warning", |
| 289 | + Severity::Warning, |
| 290 | + Category::Bug, |
| 291 | + CommentStatus::Resolved, |
| 292 | + )]; |
| 293 | + |
| 294 | + let summary = apply_review_runtime_state(generate_summary(&comments), true); |
| 295 | + assert_eq!(summary.open_blockers, 0); |
| 296 | + assert_eq!(summary.merge_readiness, MergeReadiness::NeedsReReview); |
| 297 | + assert_eq!( |
| 298 | + summary.readiness_reasons, |
| 299 | + vec!["a newer review exists for this pull request".to_string()] |
| 300 | + ); |
| 301 | + } |
205 | 302 | } |
0 commit comments