green_contract.rs 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. use serde::{Deserialize, Serialize};
  2. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
  3. #[serde(rename_all = "snake_case")]
  4. pub enum GreenLevel {
  5. TargetedTests,
  6. Package,
  7. Workspace,
  8. MergeReady,
  9. }
  10. impl GreenLevel {
  11. #[must_use]
  12. pub fn as_str(self) -> &'static str {
  13. match self {
  14. Self::TargetedTests => "targeted_tests",
  15. Self::Package => "package",
  16. Self::Workspace => "workspace",
  17. Self::MergeReady => "merge_ready",
  18. }
  19. }
  20. }
  21. impl std::fmt::Display for GreenLevel {
  22. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  23. write!(f, "{}", self.as_str())
  24. }
  25. }
  26. #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
  27. pub struct GreenContract {
  28. pub required_level: GreenLevel,
  29. }
  30. impl GreenContract {
  31. #[must_use]
  32. pub fn new(required_level: GreenLevel) -> Self {
  33. Self { required_level }
  34. }
  35. #[must_use]
  36. pub fn evaluate(self, observed_level: Option<GreenLevel>) -> GreenContractOutcome {
  37. match observed_level {
  38. Some(level) if level >= self.required_level => GreenContractOutcome::Satisfied {
  39. required_level: self.required_level,
  40. observed_level: level,
  41. },
  42. _ => GreenContractOutcome::Unsatisfied {
  43. required_level: self.required_level,
  44. observed_level,
  45. },
  46. }
  47. }
  48. #[must_use]
  49. pub fn is_satisfied_by(self, observed_level: GreenLevel) -> bool {
  50. observed_level >= self.required_level
  51. }
  52. }
  53. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
  54. #[serde(tag = "outcome", rename_all = "snake_case")]
  55. pub enum GreenContractOutcome {
  56. Satisfied {
  57. required_level: GreenLevel,
  58. observed_level: GreenLevel,
  59. },
  60. Unsatisfied {
  61. required_level: GreenLevel,
  62. observed_level: Option<GreenLevel>,
  63. },
  64. }
  65. impl GreenContractOutcome {
  66. #[must_use]
  67. pub fn is_satisfied(&self) -> bool {
  68. matches!(self, Self::Satisfied { .. })
  69. }
  70. }
  71. #[cfg(test)]
  72. mod tests {
  73. use super::*;
  74. #[test]
  75. fn given_matching_level_when_evaluating_contract_then_it_is_satisfied() {
  76. // given
  77. let contract = GreenContract::new(GreenLevel::Package);
  78. // when
  79. let outcome = contract.evaluate(Some(GreenLevel::Package));
  80. // then
  81. assert_eq!(
  82. outcome,
  83. GreenContractOutcome::Satisfied {
  84. required_level: GreenLevel::Package,
  85. observed_level: GreenLevel::Package,
  86. }
  87. );
  88. assert!(outcome.is_satisfied());
  89. }
  90. #[test]
  91. fn given_higher_level_when_checking_requirement_then_it_still_satisfies_contract() {
  92. // given
  93. let contract = GreenContract::new(GreenLevel::TargetedTests);
  94. // when
  95. let is_satisfied = contract.is_satisfied_by(GreenLevel::Workspace);
  96. // then
  97. assert!(is_satisfied);
  98. }
  99. #[test]
  100. fn given_lower_level_when_evaluating_contract_then_it_is_unsatisfied() {
  101. // given
  102. let contract = GreenContract::new(GreenLevel::Workspace);
  103. // when
  104. let outcome = contract.evaluate(Some(GreenLevel::Package));
  105. // then
  106. assert_eq!(
  107. outcome,
  108. GreenContractOutcome::Unsatisfied {
  109. required_level: GreenLevel::Workspace,
  110. observed_level: Some(GreenLevel::Package),
  111. }
  112. );
  113. assert!(!outcome.is_satisfied());
  114. }
  115. #[test]
  116. fn given_no_green_level_when_evaluating_contract_then_contract_is_unsatisfied() {
  117. // given
  118. let contract = GreenContract::new(GreenLevel::MergeReady);
  119. // when
  120. let outcome = contract.evaluate(None);
  121. // then
  122. assert_eq!(
  123. outcome,
  124. GreenContractOutcome::Unsatisfied {
  125. required_level: GreenLevel::MergeReady,
  126. observed_level: None,
  127. }
  128. );
  129. }
  130. }