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