Skip to main content

ecat_protos/
lib.rs

1// Copyright (c) 2026 erik <erik@erik.xyz> — https://erik.xyz
2pub 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        // 截断到 reason 字段中间(缺 3 字节可能正好落在字段边界,prost 容错)
89        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}