shep_core/protocol/request/
handshake.rs1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
11pub struct Hello {
12 pub client_version: String,
14 pub protocol: u32,
16 #[serde(default, skip_serializing_if = "Option::is_none")]
26 pub dog_name: Option<String>,
27}
28
29#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
32pub struct HelloAck {
33 pub daemon_version: String,
35 pub protocol: u32,
37 pub pid: u32,
39 #[serde(default, skip_serializing_if = "Option::is_none")]
45 pub min_supported: Option<u32>,
46}
47
48#[cfg(test)]
49mod tests {
50 use super::super::{HelloReply, RpcError, RpcErrorCode};
51 use super::*;
52 use crate::protocol::PROTOCOL_VERSION;
53
54 #[test]
55 fn hello_handshake_shape() {
56 let hello = Hello {
57 client_version: "0.1.0".to_string(),
58 protocol: PROTOCOL_VERSION,
59 dog_name: None,
60 };
61 let json = serde_json::to_string(&hello).unwrap();
62 assert_eq!(json, r#"{"client_version":"0.1.0","protocol":11}"#);
63 }
64
65 #[test]
66 fn a_dogs_hello_names_the_dog_and_nothing_elses_does() {
67 let dog = Hello {
68 client_version: "0.1.0".to_string(),
69 protocol: PROTOCOL_VERSION,
70 dog_name: Some("metrics".to_string()),
71 };
72 let json = serde_json::to_string(&dog).unwrap();
73 assert_eq!(
74 json,
75 r#"{"client_version":"0.1.0","protocol":11,"dog_name":"metrics"}"#
76 );
77 assert_eq!(serde_json::from_str::<Hello>(&json).unwrap(), dog);
78 }
79
80 #[test]
84 fn a_hello_without_a_dog_name_still_parses() {
85 let fixture = r#"{"client_version":"0.1.14","protocol":2}"#;
86 let hello: Hello = serde_json::from_str(fixture).unwrap();
87 assert_eq!(hello.protocol, 2);
88 assert_eq!(hello.dog_name, None);
89
90 let newer = r#"{"client_version":"9.9.9","protocol":2,"dog_name":"metrics","unknown_to_an_older_daemon":true}"#;
93 let hello: Hello = serde_json::from_str(newer).unwrap();
94 assert_eq!(hello.protocol, 2);
95 assert_eq!(hello.dog_name.as_deref(), Some("metrics"));
96 }
97
98 #[test]
99 fn hello_ack_handshake_shape() {
100 let ack = HelloAck {
101 daemon_version: "0.5.0".to_string(),
102 protocol: PROTOCOL_VERSION,
103 pid: 1234,
104 min_supported: Some(crate::protocol::MIN_SUPPORTED),
105 };
106 let json = serde_json::to_string(&ack).unwrap();
107 assert_eq!(
108 json,
109 r#"{"daemon_version":"0.5.0","protocol":11,"pid":1234,"min_supported":8}"#
110 );
111 assert_eq!(serde_json::from_str::<HelloAck>(&json).unwrap(), ack);
112 }
113
114 #[test]
118 fn hello_ack_without_min_supported_omits_the_key_not_nulls_it() {
119 let ack = HelloAck {
120 daemon_version: "0.5.0".to_string(),
121 protocol: PROTOCOL_VERSION,
122 pid: 1234,
123 min_supported: None,
124 };
125 let json = serde_json::to_string(&ack).unwrap();
126 assert_eq!(
127 json,
128 r#"{"daemon_version":"0.5.0","protocol":11,"pid":1234}"#
129 );
130 assert!(!json.contains("min_supported"));
131 }
132
133 #[test]
135 fn an_old_hello_ack_without_min_supported_still_parses() {
136 let fixture = r#"{"daemon_version":"0.1.14","protocol":2,"pid":9}"#;
137 let ack: HelloAck = serde_json::from_str(fixture).unwrap();
138 assert_eq!(ack.protocol, 2);
139 assert_eq!(ack.min_supported, None);
140 }
141
142 #[test]
143 fn hello_reply_carries_typed_skew_error() {
144 let refusal: HelloReply = Err(RpcError {
145 code: RpcErrorCode::ProtocolMismatch,
146 message: "daemon speaks protocol 1, client sent 2".to_string(),
147 daemon_version: None,
148 });
149 let json = serde_json::to_string(&refusal).unwrap();
150 assert_eq!(
151 json,
152 r#"{"Err":{"code":"protocol_mismatch","message":"daemon speaks protocol 1, client sent 2"}}"#
153 );
154 let back: HelloReply = serde_json::from_str(&json).unwrap();
155 assert_eq!(back, refusal);
156 }
157
158 #[test]
159 fn v1_hello_ack_fixture_still_deserializes() {
160 let fixture = r#"{"Ok":{"daemon_version":"0.1.0","protocol":1,"pid":4242}}"#;
161 let ack: HelloReply = serde_json::from_str(fixture).unwrap();
162 assert_eq!(ack.unwrap().pid, 4242);
163 }
164}