peat_schema/validation/
capability.rs1use super::{ValidationError, ValidationResult};
6use crate::capability::v1::{CapabilityAdvertisement, OperationalStatus, ResourceStatus};
7use crate::validation::core::validate_capability;
8
9pub fn validate_capability_advertisement(ad: &CapabilityAdvertisement) -> ValidationResult<()> {
18 if ad.platform_id.is_empty() {
20 return Err(ValidationError::MissingField("platform_id".to_string()));
21 }
22
23 if ad.advertised_at.is_none() {
24 return Err(ValidationError::MissingField("advertised_at".to_string()));
25 }
26
27 for cap in &ad.capabilities {
29 validate_capability(cap)?;
30 }
31
32 if let Some(resources) = &ad.resources {
34 validate_resource_status(resources)?;
35 }
36
37 if ad.operational_status == OperationalStatus::Unspecified as i32 {
39 return Err(ValidationError::InvalidValue(
40 "operational_status must be specified".to_string(),
41 ));
42 }
43
44 Ok(())
45}
46
47fn validate_resource_status(resources: &ResourceStatus) -> ValidationResult<()> {
49 if resources.compute_utilization < 0.0 || resources.compute_utilization > 1.0 {
51 return Err(ValidationError::InvalidValue(format!(
52 "compute_utilization {} must be between 0.0 and 1.0",
53 resources.compute_utilization
54 )));
55 }
56
57 if resources.memory_utilization < 0.0 || resources.memory_utilization > 1.0 {
58 return Err(ValidationError::InvalidValue(format!(
59 "memory_utilization {} must be between 0.0 and 1.0",
60 resources.memory_utilization
61 )));
62 }
63
64 if resources.power_level < 0.0 || resources.power_level > 1.0 {
65 return Err(ValidationError::InvalidValue(format!(
66 "power_level {} must be between 0.0 and 1.0",
67 resources.power_level
68 )));
69 }
70
71 if resources.storage_utilization < 0.0 || resources.storage_utilization > 1.0 {
72 return Err(ValidationError::InvalidValue(format!(
73 "storage_utilization {} must be between 0.0 and 1.0",
74 resources.storage_utilization
75 )));
76 }
77
78 if resources.bandwidth_utilization < 0.0 || resources.bandwidth_utilization > 1.0 {
79 return Err(ValidationError::InvalidValue(format!(
80 "bandwidth_utilization {} must be between 0.0 and 1.0",
81 resources.bandwidth_utilization
82 )));
83 }
84
85 Ok(())
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91 use crate::capability::v1::{Capability, CapabilityType};
92 use crate::common::v1::Timestamp;
93
94 fn valid_capability_advertisement() -> CapabilityAdvertisement {
95 CapabilityAdvertisement {
96 platform_id: "Alpha-3".to_string(),
97 advertised_at: Some(Timestamp {
98 seconds: 1702000000,
99 nanos: 0,
100 }),
101 capabilities: vec![Capability {
102 id: "ai-model-1".to_string(),
103 name: "Object Detector".to_string(),
104 capability_type: CapabilityType::Compute as i32,
105 confidence: 0.95,
106 metadata_json: r#"{"model_type": "detector"}"#.to_string(),
107 registered_at: None,
108 }],
109 resources: Some(ResourceStatus {
110 compute_utilization: 0.65,
111 memory_utilization: 0.5,
112 power_level: 0.9,
113 storage_utilization: 0.3,
114 bandwidth_utilization: 0.1,
115 extra_json: String::new(),
116 }),
117 operational_status: OperationalStatus::Ready as i32,
118 }
119 }
120
121 #[test]
122 fn test_valid_capability_advertisement() {
123 let ad = valid_capability_advertisement();
124 assert!(validate_capability_advertisement(&ad).is_ok());
125 }
126
127 #[test]
128 fn test_missing_platform_id() {
129 let mut ad = valid_capability_advertisement();
130 ad.platform_id = String::new();
131 let err = validate_capability_advertisement(&ad).unwrap_err();
132 assert!(matches!(err, ValidationError::MissingField(f) if f == "platform_id"));
133 }
134
135 #[test]
136 fn test_missing_advertised_at() {
137 let mut ad = valid_capability_advertisement();
138 ad.advertised_at = None;
139 let err = validate_capability_advertisement(&ad).unwrap_err();
140 assert!(matches!(err, ValidationError::MissingField(f) if f == "advertised_at"));
141 }
142
143 #[test]
144 fn test_invalid_resource_utilization() {
145 let mut ad = valid_capability_advertisement();
146 ad.resources = Some(ResourceStatus {
147 compute_utilization: 1.5, memory_utilization: 0.5,
149 power_level: 0.9,
150 storage_utilization: 0.3,
151 bandwidth_utilization: 0.1,
152 extra_json: String::new(),
153 });
154 let err = validate_capability_advertisement(&ad).unwrap_err();
155 assert!(matches!(err, ValidationError::InvalidValue(_)));
156 }
157
158 #[test]
159 fn test_unspecified_operational_status() {
160 let mut ad = valid_capability_advertisement();
161 ad.operational_status = OperationalStatus::Unspecified as i32;
162 let err = validate_capability_advertisement(&ad).unwrap_err();
163 assert!(matches!(err, ValidationError::InvalidValue(_)));
164 }
165}