kanade_shared/wire/
result.rs1use serde::{Deserialize, Serialize};
2
3#[derive(Serialize, Deserialize, Debug, Clone)]
4pub struct ExecResult {
5 pub request_id: String,
6 pub pc_id: String,
7 pub exit_code: i32,
8 pub stdout: String,
9 pub stderr: String,
10 pub started_at: chrono::DateTime<chrono::Utc>,
11 pub finished_at: chrono::DateTime<chrono::Utc>,
12 #[serde(default, skip_serializing_if = "Option::is_none")]
19 pub manifest_id: Option<String>,
20}
21
22#[cfg(test)]
23mod tests {
24 use super::*;
25 use chrono::TimeZone;
26
27 #[test]
28 fn exec_result_round_trips_through_json() {
29 let t0 = chrono::Utc.with_ymd_and_hms(2026, 5, 16, 0, 0, 0).unwrap();
30 let t1 = chrono::Utc.with_ymd_and_hms(2026, 5, 16, 0, 0, 5).unwrap();
31 let r = ExecResult {
32 request_id: "req-1".into(),
33 pc_id: "minipc".into(),
34 exit_code: 0,
35 stdout: "hello\n".into(),
36 stderr: String::new(),
37 started_at: t0,
38 finished_at: t1,
39 manifest_id: Some("inventory-hw".into()),
40 };
41 let json = serde_json::to_string(&r).unwrap();
42 let back: ExecResult = serde_json::from_str(&json).unwrap();
43 assert_eq!(back.request_id, r.request_id);
44 assert_eq!(back.exit_code, r.exit_code);
45 assert_eq!(back.stdout, r.stdout);
46 assert_eq!(back.started_at, t0);
47 assert_eq!(back.finished_at, t1);
48 assert_eq!(back.manifest_id.as_deref(), Some("inventory-hw"));
49 }
50
51 #[test]
52 fn exec_result_without_manifest_id_decodes() {
53 let json = r#"{
55 "request_id":"r","pc_id":"x","exit_code":0,
56 "stdout":"","stderr":"",
57 "started_at":"2026-05-16T00:00:00Z",
58 "finished_at":"2026-05-16T00:00:00Z"
59 }"#;
60 let r: ExecResult = serde_json::from_str(json).unwrap();
61 assert_eq!(r.manifest_id, None);
62 }
63}