socorro_cli/models/
search.rs1use 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}