socorro_cli/models/
bugs.rs1use serde::{Deserialize, Serialize};
6use std::collections::BTreeMap;
7
8#[derive(Debug, Deserialize, Serialize)]
9pub struct BugsResponse {
10 pub hits: Vec<BugHit>,
11 pub total: usize,
12}
13
14#[derive(Debug, Deserialize, Serialize)]
15pub struct BugHit {
16 pub id: u64,
17 pub signature: String,
18}
19
20#[derive(Debug, Serialize)]
21pub struct BugsSummary {
22 pub bugs: Vec<BugGroup>,
23}
24
25#[derive(Debug, Serialize)]
26pub struct BugGroup {
27 pub bug_id: u64,
28 pub signatures: Vec<String>,
29}
30
31impl BugsResponse {
32 pub fn to_summary(&self) -> BugsSummary {
33 let mut by_bug: BTreeMap<u64, Vec<String>> = BTreeMap::new();
34 for hit in &self.hits {
35 by_bug
36 .entry(hit.id)
37 .or_default()
38 .push(hit.signature.clone());
39 }
40
41 let bugs = by_bug
42 .into_iter()
43 .map(|(bug_id, mut signatures)| {
44 signatures.sort();
45 BugGroup { bug_id, signatures }
46 })
47 .collect();
48
49 BugsSummary { bugs }
50 }
51}
52
53#[cfg(test)]
54mod tests {
55 use super::*;
56
57 #[test]
58 fn test_deserialize_bugs_response() {
59 let json = r#"{
60 "hits": [
61 {"id": 999999, "signature": "OOM | small"},
62 {"id": 999999, "signature": "OOM | large"},
63 {"id": 888888, "signature": "OOM | small"}
64 ],
65 "total": 3
66 }"#;
67 let response: BugsResponse = serde_json::from_str(json).unwrap();
68 assert_eq!(response.total, 3);
69 assert_eq!(response.hits.len(), 3);
70 assert_eq!(response.hits[0].id, 999999);
71 assert_eq!(response.hits[0].signature, "OOM | small");
72 }
73
74 #[test]
75 fn test_deserialize_empty_response() {
76 let json = r#"{"hits": [], "total": 0}"#;
77 let response: BugsResponse = serde_json::from_str(json).unwrap();
78 assert_eq!(response.total, 0);
79 assert!(response.hits.is_empty());
80 }
81
82 #[test]
83 fn test_to_summary_groups_by_bug() {
84 let response = BugsResponse {
85 hits: vec![
86 BugHit {
87 id: 999999,
88 signature: "OOM | small".to_string(),
89 },
90 BugHit {
91 id: 999999,
92 signature: "OOM | large".to_string(),
93 },
94 BugHit {
95 id: 888888,
96 signature: "OOM | small".to_string(),
97 },
98 ],
99 total: 3,
100 };
101 let summary = response.to_summary();
102 assert_eq!(summary.bugs.len(), 2);
103
104 assert_eq!(summary.bugs[0].bug_id, 888888);
106 assert_eq!(summary.bugs[0].signatures, vec!["OOM | small"]);
107
108 assert_eq!(summary.bugs[1].bug_id, 999999);
109 assert_eq!(
110 summary.bugs[1].signatures,
111 vec!["OOM | large", "OOM | small"]
112 );
113 }
114
115 #[test]
116 fn test_to_summary_empty() {
117 let response = BugsResponse {
118 hits: vec![],
119 total: 0,
120 };
121 let summary = response.to_summary();
122 assert!(summary.bugs.is_empty());
123 }
124
125 #[test]
126 fn test_to_summary_signatures_sorted() {
127 let response = BugsResponse {
128 hits: vec![
129 BugHit {
130 id: 100,
131 signature: "Zzz".to_string(),
132 },
133 BugHit {
134 id: 100,
135 signature: "Aaa".to_string(),
136 },
137 BugHit {
138 id: 100,
139 signature: "Mmm".to_string(),
140 },
141 ],
142 total: 3,
143 };
144 let summary = response.to_summary();
145 assert_eq!(summary.bugs[0].signatures, vec!["Aaa", "Mmm", "Zzz"]);
146 }
147}