Skip to main content

socorro_cli/models/
search.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 super::common::deserialize_string_or_number;
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9#[derive(Debug, Serialize, Deserialize)]
10pub struct SearchResponse {
11    pub total: u64,
12    pub hits: Vec<CrashHit>,
13    #[serde(default)]
14    pub facets: HashMap<String, Vec<FacetBucket>>,
15}
16
17#[derive(Debug, Serialize, Deserialize)]
18pub struct CrashHit {
19    pub uuid: String,
20    pub date: String,
21    pub signature: String,
22    pub product: String,
23    pub version: String,
24    #[serde(default)]
25    pub platform: Option<String>,
26    #[serde(default, deserialize_with = "deserialize_string_or_number")]
27    pub build_id: Option<String>,
28    #[serde(default)]
29    pub release_channel: Option<String>,
30    #[serde(default)]
31    pub platform_version: Option<String>,
32}
33
34#[derive(Debug, Serialize, Deserialize)]
35pub struct FacetBucket {
36    pub term: String,
37    pub count: u64,
38}
39
40pub struct SearchParams {
41    pub signature: Option<String>,
42    pub product: String,
43    pub version: Option<String>,
44    pub platform: Option<String>,
45    pub cpu_arch: Option<String>,
46    pub release_channel: Option<String>,
47    pub platform_version: Option<String>,
48    pub process_type: Option<String>,
49    pub days: u32,
50    pub limit: usize,
51    pub facets: Vec<String>,
52    pub facets_size: Option<usize>,
53    pub sort: String,
54}
55
56#[cfg(test)]
57mod tests {
58    use super::*;
59
60    #[test]
61    fn test_deserialize_search_response() {
62        let json = r#"{
63            "total": 42,
64            "hits": [
65                {
66                    "uuid": "247653e8-7a18-4836-97d1-42a720260120",
67                    "date": "2024-01-15T10:30:00",
68                    "signature": "mozilla::SomeFunction",
69                    "product": "Firefox",
70                    "version": "120.0",
71                    "platform": "Windows"
72                }
73            ],
74            "facets": {}
75        }"#;
76
77        let response: SearchResponse = serde_json::from_str(json).unwrap();
78        assert_eq!(response.total, 42);
79        assert_eq!(response.hits.len(), 1);
80        assert_eq!(
81            response.hits[0].uuid,
82            "247653e8-7a18-4836-97d1-42a720260120"
83        );
84        assert_eq!(response.hits[0].signature, "mozilla::SomeFunction");
85    }
86
87    #[test]
88    fn test_deserialize_search_response_with_facets() {
89        let json = r#"{
90            "total": 100,
91            "hits": [],
92            "facets": {
93                "version": [
94                    {"term": "120.0", "count": 50},
95                    {"term": "119.0", "count": 30},
96                    {"term": "118.0", "count": 20}
97                ],
98                "platform": [
99                    {"term": "Windows", "count": 60},
100                    {"term": "Linux", "count": 40}
101                ]
102            }
103        }"#;
104
105        let response: SearchResponse = serde_json::from_str(json).unwrap();
106        assert_eq!(response.total, 100);
107        assert_eq!(response.facets.len(), 2);
108
109        let version_facets = response.facets.get("version").unwrap();
110        assert_eq!(version_facets.len(), 3);
111        assert_eq!(version_facets[0].term, "120.0");
112        assert_eq!(version_facets[0].count, 50);
113    }
114
115    #[test]
116    fn test_deserialize_crash_hit_missing_platform() {
117        let json = r#"{
118            "uuid": "test-id",
119            "date": "2024-01-15",
120            "signature": "crash_sig",
121            "product": "Firefox",
122            "version": "120.0"
123        }"#;
124
125        let hit: CrashHit = serde_json::from_str(json).unwrap();
126        assert_eq!(hit.platform, None);
127    }
128
129    #[test]
130    fn test_deserialize_empty_response() {
131        let json = r#"{
132            "total": 0,
133            "hits": [],
134            "facets": {}
135        }"#;
136
137        let response: SearchResponse = serde_json::from_str(json).unwrap();
138        assert_eq!(response.total, 0);
139        assert!(response.hits.is_empty());
140        assert!(response.facets.is_empty());
141    }
142}