1pub mod errors {
3 tonic::include_proto!("ecat.errors");
4}
5
6pub mod metadata {
7 tonic::include_proto!("ecat.metadata");
8}
9
10#[cfg(test)]
11mod tests {
12 use super::*;
13 use prost::Message;
14
15 #[test]
16 fn error_roundtrips_through_prost() {
17 let mut metadata = std::collections::HashMap::new();
18 metadata.insert("retryable".to_string(), "true".to_string());
19 let err = errors::Error {
20 code: errors::ErrorCode::PermissionDenied as i32,
21 reason: "PERMISSION_DENIED".into(),
22 message: "no access".into(),
23 metadata,
24 };
25 let bytes = err.encode_to_vec();
26 let decoded = errors::Error::decode(bytes.as_slice()).unwrap();
27 assert_eq!(decoded.code, 1004);
28 assert_eq!(decoded.reason, "PERMISSION_DENIED");
29 assert_eq!(decoded.message, "no access");
30 assert_eq!(
31 decoded.metadata.get("retryable").map(String::as_str),
32 Some("true")
33 );
34 assert_eq!(decoded.metadata.len(), 1);
35 }
36
37 #[test]
38 fn error_code_enum_values_match_proto() {
39 assert_eq!(errors::ErrorCode::Ok as i32, 0);
40 assert_eq!(errors::ErrorCode::InvalidArgument as i32, 1001);
41 assert_eq!(errors::ErrorCode::PermissionDenied as i32, 1004);
42 assert_eq!(errors::ErrorCode::DeadlineExceeded as i32, 1009);
43 }
44
45 #[test]
46 fn metadata_roundtrips_through_prost() {
47 let mut pairs = std::collections::HashMap::new();
48 pairs.insert("env".to_string(), "prod".to_string());
49 pairs.insert("region".to_string(), "cn-east".to_string());
50 let md = metadata::Metadata { pairs };
51 let decoded = metadata::Metadata::decode(md.encode_to_vec().as_slice()).unwrap();
52 assert_eq!(decoded, md);
53 assert_eq!(decoded.pairs.len(), 2);
54 }
55
56 #[test]
57 fn empty_message_roundtrips() {
58 let err = errors::Error {
59 code: 0,
60 reason: String::new(),
61 message: String::new(),
62 metadata: Default::default(),
63 };
64 let decoded = errors::Error::decode(err.encode_to_vec().as_slice()).unwrap();
65 assert_eq!(decoded, err);
66 assert!(decoded.metadata.is_empty());
67 }
68
69 #[test]
70 fn all_error_code_values_match_proto() {
71 assert_eq!(errors::ErrorCode::Unknown as i32, 1000);
72 assert_eq!(errors::ErrorCode::AlreadyExists as i32, 1003);
73 assert_eq!(errors::ErrorCode::Unauthenticated as i32, 1005);
74 assert_eq!(errors::ErrorCode::ResourceExhausted as i32, 1006);
75 assert_eq!(errors::ErrorCode::Internal as i32, 1007);
76 assert_eq!(errors::ErrorCode::Unavailable as i32, 1008);
77 }
78
79 #[test]
80 fn error_decode_truncated_fails() {
81 let err = errors::Error {
82 code: 1,
83 reason: "a-reason-longer-than-truncation".into(),
84 message: "m".into(),
85 metadata: Default::default(),
86 };
87 let bytes = err.encode_to_vec();
88 let truncated = &bytes[..10];
90 assert!(errors::Error::decode(truncated).is_err());
91 }
92
93 #[test]
94 fn decode_empty_buffer_gives_default() {
95 let err = errors::Error::decode(&b""[..]).unwrap();
96 assert_eq!(err.code, 0);
97 assert!(err.reason.is_empty());
98 assert!(err.message.is_empty());
99 }
100
101 #[test]
102 fn metadata_multiple_pairs_roundtrip() {
103 let mut pairs = std::collections::HashMap::new();
104 pairs.insert("a".to_string(), "1".to_string());
105 pairs.insert("b".to_string(), "2".to_string());
106 pairs.insert("c".to_string(), "3".to_string());
107 let md = metadata::Metadata { pairs };
108 let decoded = metadata::Metadata::decode(md.encode_to_vec().as_slice()).unwrap();
109 assert_eq!(decoded, md);
110 assert_eq!(decoded.pairs.len(), 3);
111 }
112}