Skip to main content

socorro_cli/models/
bugs.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5use 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        // BTreeMap orders by key, so 888888 comes first
105        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}