1use std::collections::HashMap;
4
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, utoipa::ToSchema)]
15pub struct GpuInfoSummary {
16 pub vendor: String,
18 pub model: String,
20 pub memory_mb: u64,
22}
23
24#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
26pub struct NodeSummary {
27 pub id: u64,
29 pub address: String,
31 pub status: String,
33 pub role: String,
35 pub labels: HashMap<String, String>,
37 pub last_seen: u64,
39}
40
41#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
43pub struct NodeResourceInfo {
44 pub cpu_total: f64,
46 pub cpu_used: f64,
48 pub cpu_percent: f64,
50 pub memory_total: u64,
52 pub memory_used: u64,
54 pub memory_percent: f64,
56}
57
58#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
60pub struct NodeDetails {
61 pub id: u64,
63 pub address: String,
65 pub status: String,
67 pub role: String,
69 pub labels: HashMap<String, String>,
71 pub last_seen: u64,
73 pub resources: NodeResourceInfo,
75 pub services: Vec<String>,
77 pub registered_at: u64,
79 pub last_heartbeat: u64,
81}
82
83#[derive(Debug, Deserialize, utoipa::ToSchema)]
85pub struct UpdateLabelsRequest {
86 pub labels: HashMap<String, String>,
88 pub remove: Vec<String>,
90}
91
92#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
94pub struct UpdateLabelsResponse {
95 pub labels: HashMap<String, String>,
97}
98
99#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
101pub struct JoinTokenResponse {
102 pub token: String,
104 pub leader_endpoint: String,
106 pub leader_public_key: String,
108 pub overlay_cidr: String,
110 pub allocated_ip: String,
112 pub expires_at: u64,
114}
115
116#[cfg(test)]
117mod tests {
118 use super::*;
119
120 #[test]
121 fn test_node_summary_serialize() {
122 let summary = NodeSummary {
123 id: 1,
124 address: "192.168.1.10:9090".to_string(),
125 status: "ready".to_string(),
126 role: "worker".to_string(),
127 labels: HashMap::from([("zone".to_string(), "us-east-1".to_string())]),
128 last_seen: 1_706_745_600,
129 };
130 let json = serde_json::to_string(&summary).unwrap();
131 assert!(json.contains("192.168.1.10"));
132 assert!(json.contains("ready"));
133 assert!(json.contains("worker"));
134 }
135
136 #[test]
137 fn test_node_details_serialize() {
138 let details = NodeDetails {
139 id: 1,
140 address: "192.168.1.10:9090".to_string(),
141 status: "ready".to_string(),
142 role: "worker".to_string(),
143 labels: HashMap::new(),
144 last_seen: 1_706_745_600,
145 resources: NodeResourceInfo {
146 cpu_total: 4.0,
147 cpu_used: 1.5,
148 cpu_percent: 37.5,
149 memory_total: 8_589_934_592,
150 memory_used: 4_294_967_296,
151 memory_percent: 50.0,
152 },
153 services: vec!["api".to_string(), "worker".to_string()],
154 registered_at: 1_706_659_200,
155 last_heartbeat: 1_706_745_600,
156 };
157 let json = serde_json::to_string(&details).unwrap();
158 assert!(json.contains("resources"));
159 assert!(json.contains("services"));
160 assert!(json.contains("cpu_total"));
161 }
162
163 #[test]
164 fn test_update_labels_request_deserialize() {
165 let json = r#"{"labels": {"zone": "us-west-2"}, "remove": ["old-label"]}"#;
166 let request: UpdateLabelsRequest = serde_json::from_str(json).unwrap();
167 assert_eq!(request.labels.get("zone"), Some(&"us-west-2".to_string()));
168 assert_eq!(request.remove, vec!["old-label".to_string()]);
169 }
170
171 #[test]
172 fn test_join_token_response_serialize() {
173 let response = JoinTokenResponse {
174 token: "abc123".to_string(),
175 leader_endpoint: "192.168.1.1:9090".to_string(),
176 leader_public_key: "pubkey".to_string(),
177 overlay_cidr: "10.0.0.0/16".to_string(),
178 allocated_ip: "10.0.1.5".to_string(),
179 expires_at: 1_706_832_000,
180 };
181 let json = serde_json::to_string(&response).unwrap();
182 assert!(json.contains("abc123"));
183 assert!(json.contains("overlay_cidr"));
184 }
185}