Skip to main content

rocketmq_remoting/protocol/header/
query_topics_by_consumer_request_header.rs

1// Copyright 2023 The RocketMQ Rust Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use cheetah_string::CheetahString;
16use rocketmq_macros::RequestHeaderCodecV2;
17use serde::Deserialize;
18use serde::Serialize;
19
20use crate::rpc::rpc_request_header::RpcRequestHeader;
21
22#[derive(Serialize, Deserialize, Debug, RequestHeaderCodecV2, Default)]
23pub struct QueryTopicsByConsumerRequestHeader {
24    #[required]
25    #[serde(rename = "group")]
26    pub group: CheetahString,
27
28    #[serde(flatten)]
29    pub rpc_request_header: Option<RpcRequestHeader>,
30}
31
32impl QueryTopicsByConsumerRequestHeader {
33    pub fn new(group: impl Into<CheetahString>) -> Self {
34        Self {
35            group: group.into(),
36            rpc_request_header: None,
37        }
38    }
39
40    pub fn get_group(&self) -> &CheetahString {
41        &self.group
42    }
43
44    pub fn set_group(&mut self, group: CheetahString) {
45        self.group = group;
46    }
47}
48
49#[cfg(test)]
50mod tests {
51    use std::collections::HashMap;
52
53    use super::*;
54    use crate::protocol::command_custom_header::FromMap;
55
56    #[test]
57    fn query_topics_by_consumer_request_header_default() {
58        let header = QueryTopicsByConsumerRequestHeader::default();
59        assert_eq!(header.get_group(), "");
60        assert!(header.rpc_request_header.is_none());
61    }
62
63    #[test]
64    fn query_topics_by_consumer_request_header_new() {
65        let mut header = QueryTopicsByConsumerRequestHeader::new("group1");
66        assert_eq!(header.get_group(), "group1");
67        assert!(header.rpc_request_header.is_none());
68        header.set_group(CheetahString::from("group2"));
69        assert_eq!(header.get_group(), "group2");
70    }
71
72    #[test]
73    fn query_topics_by_consumer_request_header_serialization() {
74        let header = QueryTopicsByConsumerRequestHeader {
75            group: CheetahString::from("group1"),
76            rpc_request_header: Some(RpcRequestHeader {
77                broker_name: Some(CheetahString::from("broker")),
78                ..Default::default()
79            }),
80        };
81        let json = serde_json::to_string(&header).unwrap();
82        assert!(json.contains("\"group\":\"group1\""));
83        assert!(json.contains("\"brokerName\":\"broker\""));
84    }
85
86    #[test]
87    fn query_topics_by_consumer_request_header_deserialization() {
88        let json = r#"{"group":"group1","brokerName":"broker"}"#;
89        let header: QueryTopicsByConsumerRequestHeader = serde_json::from_str(json).unwrap();
90        assert_eq!(header.group, "group1");
91        assert_eq!(
92            header.rpc_request_header.unwrap().broker_name,
93            Some(CheetahString::from("broker"))
94        );
95    }
96
97    #[test]
98    fn query_topics_by_consumer_request_header_from_map() {
99        let mut map = HashMap::new();
100        map.insert(CheetahString::from("group"), CheetahString::from("group1"));
101        map.insert(CheetahString::from("brokerName"), CheetahString::from("broker1"));
102
103        let header = <QueryTopicsByConsumerRequestHeader as FromMap>::from(&map).unwrap();
104        assert_eq!(header.group, "group1");
105        assert_eq!(
106            header.rpc_request_header.unwrap().broker_name,
107            Some(CheetahString::from("broker1"))
108        );
109    }
110
111    #[test]
112    fn debug_impl_contains_group() {
113        let header = QueryTopicsByConsumerRequestHeader::new("dbg_group");
114        let s = format!("{:?}", header);
115        assert!(s.contains("group"));
116        assert!(s.contains("dbg_group"));
117    }
118
119    #[test]
120    fn getter_setter_multiple_calls() {
121        let mut header = QueryTopicsByConsumerRequestHeader::new("g1");
122        assert_eq!(header.get_group(), "g1");
123        header.set_group(CheetahString::from("g2"));
124        assert_eq!(header.get_group(), "g2");
125        header.set_group(CheetahString::from("g3"));
126        assert_eq!(header.get_group(), "g3");
127    }
128
129    #[test]
130    fn serde_deserialize_missing_required_field_errors() {
131        let json = r#"{}"#;
132        let res: Result<QueryTopicsByConsumerRequestHeader, _> = serde_json::from_str(json);
133        assert!(res.is_err());
134    }
135
136    #[test]
137    fn serialization_includes_group_and_flattened_none() {
138        let header = QueryTopicsByConsumerRequestHeader::new("");
139        let json = serde_json::to_string(&header).unwrap();
140        assert!(json.contains("\"group\":"));
141        // rpc_request_header is None so brokerName should not appear
142        assert!(!json.contains("brokerName"));
143    }
144
145    #[test]
146    fn serialization_with_rpc_request_header_some() {
147        let header = QueryTopicsByConsumerRequestHeader {
148            group: CheetahString::from("g_with_rpc"),
149            rpc_request_header: Some(RpcRequestHeader {
150                broker_name: Some(CheetahString::from("brokerX")),
151                ..Default::default()
152            }),
153        };
154        let json = serde_json::to_string(&header).unwrap();
155        assert!(json.contains("\"group\":\"g_with_rpc\""));
156        assert!(json.contains("\"brokerName\":\"brokerX\""));
157    }
158
159    #[test]
160    fn deserialization_with_extra_fields_ignored() {
161        let json = r#"{"group":"gextra","unknownField":"x"}"#;
162        let header: QueryTopicsByConsumerRequestHeader = serde_json::from_str(json).unwrap();
163        assert_eq!(header.group, "gextra");
164    }
165
166    #[test]
167    fn round_trip_serialization_deserialization() {
168        let header = QueryTopicsByConsumerRequestHeader {
169            group: CheetahString::from("round"),
170            rpc_request_header: Some(RpcRequestHeader {
171                broker_name: Some(CheetahString::from("rb")),
172                ..Default::default()
173            }),
174        };
175        let json = serde_json::to_string(&header).unwrap();
176        let header2: QueryTopicsByConsumerRequestHeader = serde_json::from_str(&json).unwrap();
177        assert_eq!(header2.group, "round");
178        assert_eq!(
179            header2.rpc_request_header.unwrap().broker_name,
180            Some(CheetahString::from("rb"))
181        );
182    }
183
184    #[test]
185    fn nested_rpc_request_header_access_none_and_some() {
186        let header_none = QueryTopicsByConsumerRequestHeader::new("g_none");
187        assert!(header_none
188            .rpc_request_header
189            .as_ref()
190            .and_then(|h| h.broker_name.clone())
191            .is_none());
192
193        let header_some = QueryTopicsByConsumerRequestHeader {
194            group: CheetahString::from("g_some"),
195            rpc_request_header: Some(RpcRequestHeader {
196                broker_name: Some(CheetahString::from("bk1")),
197                ..Default::default()
198            }),
199        };
200        assert_eq!(
201            header_some
202                .rpc_request_header
203                .as_ref()
204                .and_then(|h| h.broker_name.clone()),
205            Some(CheetahString::from("bk1"))
206        );
207    }
208
209    #[test]
210    fn special_characters_and_long_group_names() {
211        let special = "g-💖-\n-\u{2764}";
212        let header = QueryTopicsByConsumerRequestHeader::new(special);
213        let json = serde_json::to_string(&header).unwrap();
214        let header2: QueryTopicsByConsumerRequestHeader = serde_json::from_str(&json).unwrap();
215        assert_eq!(header2.group, special);
216
217        let long = "a".repeat(5000);
218        let header_long = QueryTopicsByConsumerRequestHeader::new(long.clone());
219        let json_long = serde_json::to_string(&header_long).unwrap();
220        let header_long2: QueryTopicsByConsumerRequestHeader = serde_json::from_str(&json_long).unwrap();
221        assert_eq!(header_long2.group, long.as_str());
222    }
223
224    #[test]
225    fn malformed_json_and_wrong_field_types_error() {
226        let bad = "{";
227        let res: Result<QueryTopicsByConsumerRequestHeader, _> = serde_json::from_str(bad);
228        assert!(res.is_err());
229
230        let wrong_type = r#"{"group":123}"#;
231        let res2: Result<QueryTopicsByConsumerRequestHeader, _> = serde_json::from_str(wrong_type);
232        assert!(res2.is_err());
233    }
234
235    #[test]
236    fn struct_size_check_and_empty_group_behavior() {
237        use std::mem;
238        let _sz = mem::size_of::<QueryTopicsByConsumerRequestHeader>();
239        assert!(_sz > 0);
240
241        let header_empty = QueryTopicsByConsumerRequestHeader::new("");
242        let json = serde_json::to_string(&header_empty).unwrap();
243        assert!(json.contains("\"group\":\"\""));
244        let header2: QueryTopicsByConsumerRequestHeader = serde_json::from_str(&json).unwrap();
245        assert_eq!(header2.group, "");
246    }
247}