1pub use peat_schema::cap::capability::v1::{Capability, CapabilityType};
8
9pub trait CapabilityExt {
11 fn new(id: String, name: String, capability_type: CapabilityType, confidence: f32) -> Self;
19
20 fn get_capability_type(&self) -> CapabilityType;
25
26 fn set_capability_type(&mut self, capability_type: CapabilityType);
31
32 fn is_valid(&self, threshold: f32) -> bool;
37}
38
39impl CapabilityExt for Capability {
40 fn new(id: String, name: String, capability_type: CapabilityType, confidence: f32) -> Self {
41 Self {
42 id,
43 name,
44 capability_type: capability_type as i32,
45 confidence: confidence.clamp(0.0, 1.0),
46 metadata_json: String::new(),
47 registered_at: None,
48 }
49 }
50
51 fn get_capability_type(&self) -> CapabilityType {
52 CapabilityType::try_from(self.capability_type).unwrap_or(CapabilityType::Unspecified)
53 }
54
55 fn set_capability_type(&mut self, capability_type: CapabilityType) {
56 self.capability_type = capability_type as i32;
57 }
58
59 fn is_valid(&self, threshold: f32) -> bool {
60 self.confidence >= threshold
61 }
62}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67
68 #[test]
69 fn test_capability_new() {
70 let cap = Capability::new(
71 "sensor-1".to_string(),
72 "Camera".to_string(),
73 CapabilityType::Sensor,
74 0.85,
75 );
76
77 assert_eq!(cap.id, "sensor-1");
78 assert_eq!(cap.name, "Camera");
79 assert_eq!(cap.get_capability_type(), CapabilityType::Sensor);
80 assert_eq!(cap.confidence, 0.85);
81 }
82
83 #[test]
84 fn test_capability_confidence_clamping() {
85 let cap_high = Capability::new(
87 "test".to_string(),
88 "Test".to_string(),
89 CapabilityType::Compute,
90 1.5,
91 );
92 assert_eq!(cap_high.confidence, 1.0);
93
94 let cap_low = Capability::new(
96 "test".to_string(),
97 "Test".to_string(),
98 CapabilityType::Compute,
99 -0.5,
100 );
101 assert_eq!(cap_low.confidence, 0.0);
102
103 let cap_normal = Capability::new(
105 "test".to_string(),
106 "Test".to_string(),
107 CapabilityType::Compute,
108 0.75,
109 );
110 assert_eq!(cap_normal.confidence, 0.75);
111 }
112
113 #[test]
114 fn test_get_capability_type() {
115 let cap = Capability::new(
116 "test".to_string(),
117 "Test".to_string(),
118 CapabilityType::Communication,
119 0.8,
120 );
121
122 assert_eq!(cap.get_capability_type(), CapabilityType::Communication);
123 assert_eq!(cap.capability_type, CapabilityType::Communication as i32);
124 }
125
126 #[test]
127 fn test_set_capability_type() {
128 let mut cap = Capability::new(
129 "test".to_string(),
130 "Test".to_string(),
131 CapabilityType::Sensor,
132 0.8,
133 );
134
135 assert_eq!(cap.get_capability_type(), CapabilityType::Sensor);
136
137 cap.set_capability_type(CapabilityType::Payload);
138 assert_eq!(cap.get_capability_type(), CapabilityType::Payload);
139 assert_eq!(cap.capability_type, CapabilityType::Payload as i32);
140 }
141
142 #[test]
143 fn test_capability_type_roundtrip() {
144 let types = vec![
146 CapabilityType::Unspecified,
147 CapabilityType::Sensor,
148 CapabilityType::Compute,
149 CapabilityType::Communication,
150 CapabilityType::Mobility,
151 CapabilityType::Payload,
152 CapabilityType::Emergent,
153 ];
154
155 for cap_type in types {
156 let cap = Capability::new("test".to_string(), "Test".to_string(), cap_type, 0.8);
157 assert_eq!(cap.get_capability_type(), cap_type);
158 }
159 }
160
161 #[test]
162 fn test_is_valid() {
163 let cap = Capability::new(
164 "test".to_string(),
165 "Test".to_string(),
166 CapabilityType::Sensor,
167 0.8,
168 );
169
170 assert!(cap.is_valid(0.7));
171 assert!(cap.is_valid(0.8));
172 assert!(!cap.is_valid(0.9));
173 }
174
175 #[test]
176 fn test_is_valid_edge_cases() {
177 let cap_zero = Capability::new(
178 "test".to_string(),
179 "Test".to_string(),
180 CapabilityType::Sensor,
181 0.0,
182 );
183 assert!(cap_zero.is_valid(0.0));
184 assert!(!cap_zero.is_valid(0.1));
185
186 let cap_one = Capability::new(
187 "test".to_string(),
188 "Test".to_string(),
189 CapabilityType::Sensor,
190 1.0,
191 );
192 assert!(cap_one.is_valid(1.0));
193 assert!(cap_one.is_valid(0.9));
194 }
195
196 #[test]
197 fn test_invalid_capability_type_defaults_to_unspecified() {
198 let mut cap = Capability::new(
199 "test".to_string(),
200 "Test".to_string(),
201 CapabilityType::Sensor,
202 0.8,
203 );
204
205 cap.capability_type = 999;
207
208 assert_eq!(cap.get_capability_type(), CapabilityType::Unspecified);
210 }
211
212 #[test]
213 fn test_metadata_json_field() {
214 let cap = Capability::new(
215 "test".to_string(),
216 "Test".to_string(),
217 CapabilityType::Sensor,
218 0.8,
219 );
220
221 assert_eq!(cap.metadata_json, "");
223
224 let mut cap_with_metadata = cap.clone();
226 cap_with_metadata.metadata_json = r#"{"key": "value"}"#.to_string();
227 assert_eq!(cap_with_metadata.metadata_json, r#"{"key": "value"}"#);
228 }
229
230 #[test]
231 fn test_registered_at_field() {
232 let cap = Capability::new(
233 "test".to_string(),
234 "Test".to_string(),
235 CapabilityType::Sensor,
236 0.8,
237 );
238
239 assert!(cap.registered_at.is_none());
241 }
242
243 #[test]
244 fn test_capability_with_empty_strings() {
245 let cap = Capability::new(String::new(), String::new(), CapabilityType::Sensor, 0.5);
246
247 assert_eq!(cap.id, "");
248 assert_eq!(cap.name, "");
249 assert_eq!(cap.confidence, 0.5);
250 }
251
252 #[test]
253 fn test_capability_confidence_boundary_values() {
254 let cap_zero = Capability::new(
256 "test".to_string(),
257 "Test".to_string(),
258 CapabilityType::Sensor,
259 0.0,
260 );
261 assert_eq!(cap_zero.confidence, 0.0);
262 assert!(cap_zero.is_valid(0.0));
263 assert!(!cap_zero.is_valid(0.000001));
264
265 let cap_one = Capability::new(
266 "test".to_string(),
267 "Test".to_string(),
268 CapabilityType::Sensor,
269 1.0,
270 );
271 assert_eq!(cap_one.confidence, 1.0);
272 assert!(cap_one.is_valid(1.0));
273 assert!(cap_one.is_valid(0.999999));
274 }
275
276 #[test]
277 fn test_capability_type_set_then_get() {
278 let mut cap = Capability::new(
279 "test".to_string(),
280 "Test".to_string(),
281 CapabilityType::Unspecified,
282 0.5,
283 );
284
285 for cap_type in [
287 CapabilityType::Sensor,
288 CapabilityType::Compute,
289 CapabilityType::Communication,
290 CapabilityType::Mobility,
291 CapabilityType::Payload,
292 CapabilityType::Emergent,
293 ] {
294 cap.set_capability_type(cap_type);
295 assert_eq!(cap.get_capability_type(), cap_type);
296 assert_eq!(cap.capability_type, cap_type as i32);
297 }
298 }
299
300 #[test]
301 fn test_capability_clone() {
302 let cap1 = Capability::new(
303 "sensor-1".to_string(),
304 "Camera".to_string(),
305 CapabilityType::Sensor,
306 0.85,
307 );
308
309 let cap2 = cap1.clone();
310 assert_eq!(cap1.id, cap2.id);
311 assert_eq!(cap1.name, cap2.name);
312 assert_eq!(cap1.capability_type, cap2.capability_type);
313 assert_eq!(cap1.confidence, cap2.confidence);
314 }
315
316 #[test]
317 fn test_capability_metadata_json_manipulation() {
318 let mut cap = Capability::new(
319 "test".to_string(),
320 "Test".to_string(),
321 CapabilityType::Sensor,
322 0.8,
323 );
324
325 cap.metadata_json =
327 r#"{"manufacturer": "ACME", "model": "X1000", "version": "2.1"}"#.to_string();
328 assert!(cap.metadata_json.contains("ACME"));
329
330 let parsed: serde_json::Value = serde_json::from_str(&cap.metadata_json).unwrap();
332 assert_eq!(parsed["manufacturer"], "ACME");
333 assert_eq!(parsed["model"], "X1000");
334 }
335
336 #[test]
337 fn test_is_valid_threshold_variations() {
338 let cap = Capability::new(
339 "test".to_string(),
340 "Test".to_string(),
341 CapabilityType::Sensor,
342 0.75,
343 );
344
345 assert!(cap.is_valid(0.0));
347 assert!(cap.is_valid(0.5));
348 assert!(cap.is_valid(0.74));
349 assert!(cap.is_valid(0.75)); assert!(!cap.is_valid(0.76));
351 assert!(!cap.is_valid(0.9));
352 assert!(!cap.is_valid(1.0));
353 }
354
355 #[test]
356 fn test_capability_protobuf_field_access() {
357 let cap = Capability::new(
358 "test-id".to_string(),
359 "Test Name".to_string(),
360 CapabilityType::Compute,
361 0.9,
362 );
363
364 assert_eq!(cap.id, "test-id");
366 assert_eq!(cap.name, "Test Name");
367 assert_eq!(cap.capability_type, CapabilityType::Compute as i32);
368 assert_eq!(cap.confidence, 0.9);
369 assert_eq!(cap.metadata_json, "");
370 assert!(cap.registered_at.is_none());
371 }
372}