1use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
10#[serde(rename_all = "lowercase")]
11pub enum RiskLevel {
12 Low,
13 Medium,
14 High,
15 Critical,
16}
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct FileChange {
21 pub path: String,
22 pub added: u32,
23 pub deleted: u32,
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct ChangeSummary {
29 pub files: Vec<FileChange>,
30 pub total_added: u32,
31 pub total_deleted: u32,
32 pub diff_ref: String,
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize)]
37pub struct TestResults {
38 pub passed: u32,
39 pub failed: u32,
40 pub ignored: u32,
41 pub total: u32,
42 pub failures: Vec<String>,
43 pub command: String,
44}
45
46#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct RiskFactor {
49 pub signal: String,
50 pub level: RiskLevel,
51 pub detail: String,
52}
53
54#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct RiskAssessment {
57 pub level: RiskLevel,
58 pub factors: Vec<RiskFactor>,
59}
60
61#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
63#[serde(rename_all = "snake_case")]
64pub enum SuggestedAction {
65 Approve,
66 Review,
67 RequestChanges,
68 Reject,
69}
70
71#[derive(Debug, Clone, Serialize, Deserialize)]
73pub struct ReviewBundle {
74 pub bundle_id: String,
75 pub change_summary: ChangeSummary,
76 pub test_results: TestResults,
77 pub risk_assessment: RiskAssessment,
78 pub suggested_action: SuggestedAction,
79 pub suggested_reason: String,
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85
86 #[test]
87 fn risk_level_ordering() {
88 assert!(RiskLevel::Low < RiskLevel::Medium);
89 assert!(RiskLevel::Medium < RiskLevel::High);
90 assert!(RiskLevel::High < RiskLevel::Critical);
91 }
92
93 #[test]
94 fn risk_level_json_roundtrip() {
95 let level = RiskLevel::High;
96 let json = serde_json::to_string(&level).unwrap();
97 assert_eq!(json, "\"high\"");
98 let back: RiskLevel = serde_json::from_str(&json).unwrap();
99 assert_eq!(back, level);
100 }
101
102 #[test]
103 fn suggested_action_json_roundtrip() {
104 let action = SuggestedAction::RequestChanges;
105 let json = serde_json::to_string(&action).unwrap();
106 assert_eq!(json, "\"request_changes\"");
107 let back: SuggestedAction = serde_json::from_str(&json).unwrap();
108 assert_eq!(back, action);
109 }
110
111 #[test]
112 fn review_bundle_json_roundtrip() {
113 let bundle = ReviewBundle {
114 bundle_id: "bun_test123".into(),
115 change_summary: ChangeSummary {
116 files: vec![FileChange {
117 path: "src/main.rs".into(),
118 added: 10,
119 deleted: 3,
120 }],
121 total_added: 10,
122 total_deleted: 3,
123 diff_ref: "HEAD~1".into(),
124 },
125 test_results: TestResults {
126 passed: 50,
127 failed: 0,
128 ignored: 2,
129 total: 52,
130 failures: vec![],
131 command: "cargo test".into(),
132 },
133 risk_assessment: RiskAssessment {
134 level: RiskLevel::Low,
135 factors: vec![],
136 },
137 suggested_action: SuggestedAction::Approve,
138 suggested_reason: "All tests pass, low risk".into(),
139 };
140
141 let json = serde_json::to_value(&bundle).unwrap();
142 let back: ReviewBundle = serde_json::from_value(json).unwrap();
143 assert_eq!(back.bundle_id, "bun_test123");
144 assert_eq!(back.change_summary.total_added, 10);
145 assert_eq!(back.test_results.passed, 50);
146 assert_eq!(back.risk_assessment.level, RiskLevel::Low);
147 assert_eq!(back.suggested_action, SuggestedAction::Approve);
148 }
149}