kanade_shared/wire/
heartbeat.rs1use serde::{Deserialize, Serialize};
2
3#[derive(Serialize, Deserialize, Debug, Clone)]
12pub struct Heartbeat {
13 pub pc_id: String,
14 pub at: chrono::DateTime<chrono::Utc>,
15 pub agent_version: String,
16 #[serde(default, skip_serializing_if = "Option::is_none")]
17 pub hostname: Option<String>,
18 #[serde(default, skip_serializing_if = "Option::is_none")]
23 pub os_family: Option<String>,
24}
25
26#[cfg(test)]
27mod tests {
28 use super::*;
29 use chrono::TimeZone;
30
31 #[test]
32 fn heartbeat_round_trips_through_json() {
33 let hb = Heartbeat {
34 pc_id: "minipc".into(),
35 at: chrono::Utc.with_ymd_and_hms(2026, 5, 16, 0, 0, 0).unwrap(),
36 agent_version: "0.12.0".into(),
37 hostname: Some("MINIPC".into()),
38 os_family: Some("windows".into()),
39 };
40 let json = serde_json::to_string(&hb).unwrap();
41 let back: Heartbeat = serde_json::from_str(&json).unwrap();
42 assert_eq!(back.pc_id, hb.pc_id);
43 assert_eq!(back.at, hb.at);
44 assert_eq!(back.agent_version, hb.agent_version);
45 assert_eq!(back.hostname, hb.hostname);
46 assert_eq!(back.os_family, hb.os_family);
47 }
48
49 #[test]
50 fn heartbeat_without_enrichment_still_decodes() {
51 let json = r#"{"pc_id":"x","at":"2026-05-16T00:00:00Z","agent_version":"0.11.5"}"#;
53 let hb: Heartbeat = serde_json::from_str(json).unwrap();
54 assert_eq!(hb.pc_id, "x");
55 assert_eq!(hb.hostname, None);
56 assert_eq!(hb.os_family, None);
57 }
58}