use super::types::{CheckState, MergeStateStatus, Mergeable, PullRequest, ReviewDecision};
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ActionFlag {
Draft,
Conflict,
CiFailing,
ChangesRequested,
ReviewRequested,
UnresolvedThreads,
Behind,
Clean,
}
impl PullRequest {
#[allow(dead_code)]
pub fn primary_flag(&self, viewer_login: &str) -> ActionFlag {
if self.is_draft {
return ActionFlag::Draft;
}
if self.mergeable == Mergeable::Conflicting {
return ActionFlag::Conflict;
}
if matches!(self.check_state, Some(CheckState::Failure | CheckState::Error)) {
return ActionFlag::CiFailing;
}
if self.review_decision == Some(ReviewDecision::ChangesRequested) {
return ActionFlag::ChangesRequested;
}
if self.requested_reviewers.iter().any(|r| r == viewer_login) {
return ActionFlag::ReviewRequested;
}
if self.unresolved_threads > 0 {
return ActionFlag::UnresolvedThreads;
}
if self.merge_state == MergeStateStatus::Behind {
return ActionFlag::Behind;
}
ActionFlag::Clean
}
}
#[cfg(test)]
#[allow(clippy::expect_used)]
mod tests {
use chrono::Utc;
use super::*;
use crate::github::types::{CheckState, MergeStateStatus, Mergeable, ReviewDecision, Role};
fn base_pr() -> PullRequest {
PullRequest {
number: 1,
title: "Test".to_owned(),
url: "https://github.com/o/r/pull/1".to_owned(),
repo: "o/r".to_owned(),
author: "author".to_owned(),
is_draft: false,
mergeable: Mergeable::Mergeable,
merge_state: MergeStateStatus::Clean,
review_decision: None,
commits_count: 1,
comments_count: 0,
check_state: Some(CheckState::Success),
failing_checks: vec![],
unresolved_threads: 0,
requested_reviewers: vec![],
reviews: vec![],
updated_at: Utc::now(),
roles: vec![Role::Author],
base_ref: Some("main".to_owned()),
head_ref: Some("feat/test".to_owned()),
}
}
#[test]
fn draft_wins_over_everything() {
let mut pr = base_pr();
pr.is_draft = true;
pr.mergeable = Mergeable::Conflicting;
pr.check_state = Some(CheckState::Failure);
pr.review_decision = Some(ReviewDecision::ChangesRequested);
pr.requested_reviewers = vec!["viewer".to_owned()];
pr.unresolved_threads = 5;
pr.merge_state = MergeStateStatus::Behind;
assert_eq!(pr.primary_flag("viewer"), ActionFlag::Draft);
}
#[test]
fn conflict_after_draft() {
let mut pr = base_pr();
pr.mergeable = Mergeable::Conflicting;
assert_eq!(pr.primary_flag("viewer"), ActionFlag::Conflict);
}
#[test]
fn ci_failure_after_conflict() {
let mut pr = base_pr();
pr.check_state = Some(CheckState::Failure);
assert_eq!(pr.primary_flag("viewer"), ActionFlag::CiFailing);
}
#[test]
fn ci_error_triggers_ci_failing() {
let mut pr = base_pr();
pr.check_state = Some(CheckState::Error);
assert_eq!(pr.primary_flag("viewer"), ActionFlag::CiFailing);
}
#[test]
fn changes_requested_after_ci() {
let mut pr = base_pr();
pr.review_decision = Some(ReviewDecision::ChangesRequested);
assert_eq!(pr.primary_flag("viewer"), ActionFlag::ChangesRequested);
}
#[test]
fn review_requested_for_viewer() {
let mut pr = base_pr();
pr.requested_reviewers = vec!["viewer".to_owned()];
assert_eq!(pr.primary_flag("viewer"), ActionFlag::ReviewRequested);
}
#[test]
fn review_requested_not_for_viewer() {
let mut pr = base_pr();
pr.requested_reviewers = vec!["someone-else".to_owned()];
assert_eq!(pr.primary_flag("viewer"), ActionFlag::Clean);
}
#[test]
fn unresolved_threads() {
let mut pr = base_pr();
pr.unresolved_threads = 2;
assert_eq!(pr.primary_flag("viewer"), ActionFlag::UnresolvedThreads);
}
#[test]
fn behind_base_branch() {
let mut pr = base_pr();
pr.merge_state = MergeStateStatus::Behind;
assert_eq!(pr.primary_flag("viewer"), ActionFlag::Behind);
}
#[test]
fn clean_pr() {
let pr = base_pr();
assert_eq!(pr.primary_flag("viewer"), ActionFlag::Clean);
}
}