1use schemars::JsonSchema;
21use serde::{Deserialize, Serialize};
22
23pub(crate) const DEFAULT_CHECK_PROCESS_TAIL_LINES: usize = 50;
24
25#[derive(Debug, Deserialize, Serialize, JsonSchema)]
27pub struct ExecParams {
28 pub command: String,
30
31 #[serde(default)]
38 pub background: bool,
39
40 pub timeout_ms: Option<u64>,
42
43 pub log_path: Option<String>,
47}
48
49#[derive(Debug, Deserialize, Serialize, JsonSchema)]
51pub struct SudoExecParams {
52 pub command: String,
54
55 #[serde(default)]
62 pub background: bool,
63
64 pub timeout_ms: Option<u64>,
66
67 pub log_path: Option<String>,
71}
72
73#[derive(Debug, Deserialize, Serialize, JsonSchema)]
79pub struct CheckProcessParams {
80 pub job_id: String,
82
83 #[serde(default = "default_tail_lines")]
85 pub tail_lines: usize,
86}
87
88#[derive(Debug, Deserialize, Serialize, JsonSchema)]
90#[serde(deny_unknown_fields)]
91pub struct ApplyPatchParams {
92 pub patch: String,
94}
95
96fn default_tail_lines() -> usize {
97 DEFAULT_CHECK_PROCESS_TAIL_LINES
98}
99
100#[cfg(test)]
101mod tests {
102 use super::*;
103
104 #[test]
105 fn test_exec_params_deserialize() {
106 let json = r#"{"command": "echo hello"}"#;
107 let params: ExecParams = serde_json::from_str(json).unwrap();
108 assert_eq!(params.command, "echo hello");
109 assert!(!params.background);
110 assert!(params.timeout_ms.is_none());
111 assert!(params.log_path.is_none());
112 }
113
114 #[test]
115 fn test_exec_params_deserialize_background() {
116 let json = r#"{"command": "sleep 10", "background": true, "timeout_ms": 1000, "log_path": "/tmp/x.log"}"#;
117 let params: ExecParams = serde_json::from_str(json).unwrap();
118 assert_eq!(params.command, "sleep 10");
119 assert!(params.background);
120 assert_eq!(params.timeout_ms, Some(1000));
121 assert_eq!(params.log_path.as_deref(), Some("/tmp/x.log"));
122 }
123
124 #[test]
125 fn test_sudo_exec_params_deserialize() {
126 let json = r#"{"command": "apt update"}"#;
127 let params: SudoExecParams = serde_json::from_str(json).unwrap();
128 assert_eq!(params.command, "apt update");
129 assert!(!params.background);
130 assert!(params.timeout_ms.is_none());
131 assert!(params.log_path.is_none());
132 }
133
134 #[test]
135 fn test_check_process_params_deserialize() {
136 let json = r#"{"job_id": "job-123"}"#;
137 let params: CheckProcessParams = serde_json::from_str(json).unwrap();
138 assert_eq!(params.job_id, "job-123");
139 assert_eq!(params.tail_lines, 50);
140 }
141
142 #[test]
143 fn test_check_process_params_with_tail_lines() {
144 let json = r#"{"job_id": "job-123", "tail_lines": 100}"#;
145 let params: CheckProcessParams = serde_json::from_str(json).unwrap();
146 assert_eq!(params.job_id, "job-123");
147 assert_eq!(params.tail_lines, 100);
148 }
149
150 #[test]
151 fn test_apply_patch_params_deserialize_and_reject_unknown_fields() {
152 let json = r#"{"patch":"*** Begin Patch\n*** Delete File: /tmp/old\n*** End Patch"}"#;
153 let params: ApplyPatchParams = serde_json::from_str(json).unwrap();
154 assert!(params.patch.contains("*** Delete File"));
155
156 let err =
157 serde_json::from_str::<ApplyPatchParams>(r#"{"patch":"x","remote_path":"/tmp/x"}"#)
158 .unwrap_err();
159 assert!(err.to_string().contains("unknown field `remote_path`"));
160 }
161}