Skip to main content

rocketmq_remoting/protocol/header/
query_message_response_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 rocketmq_macros::RequestHeaderCodecV2;
16use serde::Deserialize;
17use serde::Serialize;
18
19#[derive(Debug, Clone, Serialize, Deserialize, RequestHeaderCodecV2, Default, PartialEq)]
20#[serde(rename_all = "camelCase")]
21pub struct QueryMessageResponseHeader {
22    pub index_last_update_timestamp: i64,
23    pub index_last_update_phyoffset: i64,
24}
25
26#[cfg(test)]
27mod tests {
28    use std::collections::HashMap;
29    use std::mem;
30
31    use crate::protocol::remoting_command::RemotingCommand;
32    use crate::CommandCustomHeader;
33    use cheetah_string::CheetahString;
34
35    use super::*;
36
37    #[test]
38    fn explicit_construction_sets_fields_correctly() {
39        let header = QueryMessageResponseHeader {
40            index_last_update_timestamp: 10,
41            index_last_update_phyoffset: 20,
42        };
43
44        assert_eq!(header.index_last_update_timestamp, 10);
45        assert_eq!(header.index_last_update_phyoffset, 20);
46    }
47
48    #[test]
49    fn query_message_response_header_default_values_are_zero() {
50        let header = QueryMessageResponseHeader::default();
51
52        assert_eq!(header.index_last_update_timestamp, 0);
53        assert_eq!(header.index_last_update_phyoffset, 0);
54    }
55
56    #[test]
57    fn clone_creates_independent_copy() {
58        let original = QueryMessageResponseHeader {
59            index_last_update_timestamp: 100,
60            index_last_update_phyoffset: 200,
61        };
62
63        let mut cloned = original.clone();
64        cloned.index_last_update_timestamp = 999;
65
66        assert_eq!(original.index_last_update_timestamp, 100);
67        assert_eq!(cloned.index_last_update_timestamp, 999);
68
69        assert_eq!(cloned.index_last_update_phyoffset, 200);
70    }
71
72    #[test]
73    fn debug_format_contains_field_names_and_values() {
74        let header = QueryMessageResponseHeader {
75            index_last_update_timestamp: 1,
76            index_last_update_phyoffset: 2,
77        };
78
79        let debug_output = format!("{:?}", header);
80
81        assert!(debug_output.contains("index_last_update_timestamp"));
82        assert!(debug_output.contains("index_last_update_phyoffset"));
83        assert!(debug_output.contains("1"));
84        assert!(debug_output.contains("2"));
85    }
86
87    #[test]
88    fn timestamp_with_positive_value() {
89        let header = QueryMessageResponseHeader {
90            index_last_update_phyoffset: 0,
91            index_last_update_timestamp: 42,
92        };
93
94        assert_eq!(header.index_last_update_timestamp, 42);
95
96        let map = header.to_map().unwrap();
97
98        assert_eq!(
99            map.get(&CheetahString::from_static_str("indexLastUpdateTimestamp"))
100                .unwrap(),
101            "42"
102        );
103    }
104
105    #[test]
106    fn timestamp_with_zero() {
107        let header = QueryMessageResponseHeader {
108            index_last_update_timestamp: 0,
109            index_last_update_phyoffset: 0,
110        };
111
112        let map = header.to_map().unwrap();
113
114        assert_eq!(
115            map.get(&CheetahString::from_static_str("indexLastUpdateTimestamp"))
116                .unwrap(),
117            "0"
118        );
119    }
120
121    #[test]
122    fn timestamp_with_negative_value() {
123        let header = QueryMessageResponseHeader {
124            index_last_update_timestamp: -456,
125            index_last_update_phyoffset: 0,
126        };
127
128        let map = header.to_map().unwrap();
129
130        assert_eq!(
131            map.get(&CheetahString::from_static_str("indexLastUpdateTimestamp"))
132                .unwrap(),
133            "-456"
134        );
135    }
136
137    #[test]
138    fn timestamp_with_i64_min() {
139        let header = QueryMessageResponseHeader {
140            index_last_update_timestamp: i64::MIN,
141            index_last_update_phyoffset: 0,
142        };
143
144        let map = header.to_map().unwrap();
145
146        assert_eq!(
147            map.get(&CheetahString::from_static_str("indexLastUpdateTimestamp"))
148                .unwrap()
149                .to_string(),
150            i64::MIN.to_string()
151        );
152    }
153
154    #[test]
155    fn phyoffset_with_positive_value() {
156        let header = QueryMessageResponseHeader {
157            index_last_update_phyoffset: 123,
158            index_last_update_timestamp: 0,
159        };
160
161        let map = header.to_map().unwrap();
162
163        assert_eq!(
164            map.get(&CheetahString::from_static_str("indexLastUpdatePhyoffset"))
165                .unwrap(),
166            "123"
167        );
168    }
169
170    #[test]
171    fn phyoffset_with_zero() {
172        let header = QueryMessageResponseHeader {
173            index_last_update_phyoffset: 0,
174            index_last_update_timestamp: 0,
175        };
176
177        let map = header.to_map().unwrap();
178
179        assert_eq!(
180            map.get(&CheetahString::from_static_str("indexLastUpdatePhyoffset"))
181                .unwrap(),
182            "0"
183        );
184    }
185
186    #[test]
187    fn phyoffset_with_negative_value() {
188        let header = QueryMessageResponseHeader {
189            index_last_update_phyoffset: -456,
190            index_last_update_timestamp: 0,
191        };
192
193        let map = header.to_map().unwrap();
194
195        assert_eq!(
196            map.get(&CheetahString::from_static_str("indexLastUpdatePhyoffset"))
197                .unwrap(),
198            "-456"
199        );
200    }
201
202    #[test]
203    fn phyoffset_with_i64_min() {
204        let header = QueryMessageResponseHeader {
205            index_last_update_phyoffset: i64::MIN,
206            index_last_update_timestamp: 0,
207        };
208
209        let map = header.to_map().unwrap();
210
211        assert_eq!(
212            map.get(&CheetahString::from_static_str("indexLastUpdatePhyoffset"))
213                .unwrap()
214                .to_string(),
215            i64::MIN.to_string()
216        );
217    }
218
219    #[test]
220    fn phyoffset_with_i64_max() {
221        let header = QueryMessageResponseHeader {
222            index_last_update_phyoffset: i64::MAX,
223            index_last_update_timestamp: 0,
224        };
225
226        let map = header.to_map().unwrap();
227
228        assert_eq!(
229            map.get(&CheetahString::from_static_str("indexLastUpdatePhyoffset"))
230                .unwrap()
231                .to_string(),
232            i64::MAX.to_string()
233        );
234    }
235
236    #[test]
237    fn serde_json_serialization() {
238        let header = QueryMessageResponseHeader {
239            index_last_update_timestamp: 131415,
240            index_last_update_phyoffset: 131496,
241        };
242
243        let json = serde_json::to_string(&header).unwrap();
244        assert_eq!(
245            json,
246            r#"{"indexLastUpdateTimestamp":131415,"indexLastUpdatePhyoffset":131496}"#
247        );
248    }
249
250    #[test]
251    fn serde_json_deserialization() {
252        let json = r#"{"indexLastUpdateTimestamp":131415,"indexLastUpdatePhyoffset":131496}"#;
253        let header: QueryMessageResponseHeader = serde_json::from_str(json).unwrap();
254
255        assert_eq!(header.index_last_update_timestamp, 131415);
256        assert_eq!(header.index_last_update_phyoffset, 131496);
257    }
258
259    #[test]
260    fn serde_json_round_trip() {
261        let header = QueryMessageResponseHeader {
262            index_last_update_timestamp: 131415,
263            index_last_update_phyoffset: 131496,
264        };
265
266        let json = serde_json::to_string(&header).unwrap();
267        let deserialized_header: QueryMessageResponseHeader = serde_json::from_str(&json).unwrap();
268
269        assert_eq!(header, deserialized_header);
270    }
271
272    #[test]
273    fn serde_json_deserialization_with_missing_fields() {
274        let json = "{}";
275        let result: Result<QueryMessageResponseHeader, _> = serde_json::from_str(json);
276        assert!(result.is_err());
277    }
278
279    #[test]
280    fn serde_json_deserialization_with_extra_fields() {
281        let json = r#"{"indexLastUpdateTimestamp":131415,"indexLastUpdatePhyoffset":131496,"extraField":"extraValue"}"#;
282        let header: QueryMessageResponseHeader = serde_json::from_str(json).unwrap();
283
284        assert_eq!(header.index_last_update_timestamp, 131415);
285        assert_eq!(header.index_last_update_phyoffset, 131496);
286    }
287
288    #[test]
289    fn test_encoding_of_the_header_to_remoting_command_format() {
290        let header = QueryMessageResponseHeader {
291            index_last_update_timestamp: 131415,
292            index_last_update_phyoffset: 131496,
293        };
294
295        let mut command = RemotingCommand::default();
296        command.set_command_custom_header_ref(header);
297        command.make_custom_header_to_net();
298
299        let ext_fields = command.ext_fields().unwrap();
300
301        assert_eq!(
302            ext_fields
303                .get(&CheetahString::from_static_str("indexLastUpdateTimestamp"))
304                .unwrap(),
305            "131415"
306        );
307        assert_eq!(
308            ext_fields
309                .get(&CheetahString::from_static_str("indexLastUpdatePhyoffset"))
310                .unwrap(),
311            "131496"
312        )
313    }
314
315    #[test]
316    fn test_decoding_of_the_header_from_remoting_command_format() {
317        let mut command = RemotingCommand::default().set_ext_fields(HashMap::new());
318        command.add_ext_field("indexLastUpdateTimestamp", "131415");
319        command.add_ext_field("indexLastUpdatePhyoffset", "131496");
320
321        let header: QueryMessageResponseHeader = command.decode_command_custom_header().unwrap();
322
323        assert_eq!(header.index_last_update_timestamp, 131415);
324        assert_eq!(header.index_last_update_phyoffset, 131496);
325    }
326
327    #[test]
328    fn test_codec_compatibility_with_query_message_response() {
329        let mut command = RemotingCommand::default().set_ext_fields(HashMap::new());
330        command.add_ext_field("indexLastUpdateTimestamp", "131415");
331        command.add_ext_field("indexLastUpdatePhyoffset", "131496");
332
333        let header_fast: QueryMessageResponseHeader = command.decode_command_custom_header_fast().unwrap();
334
335        let header_normal: QueryMessageResponseHeader = command.decode_command_custom_header().unwrap();
336
337        assert_eq!(header_fast, header_normal);
338        assert_eq!(header_fast.index_last_update_timestamp, 131415);
339        assert_eq!(header_fast.index_last_update_phyoffset, 131496);
340    }
341    #[test]
342    fn test_codec_behavior_with_various_timestamp_and_offset_values() {
343        let test_cases = vec![
344            (0, 0),
345            (-1, -1),
346            (i64::MAX, i64::MAX),
347            (i64::MIN, i64::MIN),
348            (1234567890, 987654321),
349            (-1234567890, -987654321),
350        ];
351
352        for (timestamp, offset) in test_cases {
353            let mut command = RemotingCommand::default().set_ext_fields(HashMap::new());
354            command.add_ext_field("indexLastUpdateTimestamp", timestamp.to_string());
355            command.add_ext_field("indexLastUpdatePhyoffset", offset.to_string());
356
357            let header: QueryMessageResponseHeader = command.decode_command_custom_header_fast().unwrap();
358
359            assert_eq!(header.index_last_update_timestamp, timestamp);
360            assert_eq!(header.index_last_update_phyoffset, offset);
361        }
362    }
363
364    #[test]
365    fn query_message_response_header_deserialization_malformed_json() {
366        let json = r#"{"indexLastUpdateTimestamp":"not_a_number"}"#;
367        let result: Result<QueryMessageResponseHeader, _> = serde_json::from_str(json);
368        assert!(result.is_err());
369    }
370
371    #[test]
372    fn query_message_response_header_deserialization_with_wrong_data_type_for_timestamp() {
373        let json = r#"{"indexLastUpdateTimestamp":true,"indexLastUpdatePhyoffset":131496}"#;
374        let result: Result<QueryMessageResponseHeader, _> = serde_json::from_str(json);
375        assert!(result.is_err());
376    }
377
378    #[test]
379    fn query_message_response_header_deserialization_with_wrong_data_type_for_phyoffset() {
380        let json = r#"{"indexLastUpdateTimestamp":131415,"indexLastUpdatePhyoffset":"string_instead_of_number"}"#;
381        let result: Result<QueryMessageResponseHeader, _> = serde_json::from_str(json);
382        assert!(result.is_err());
383    }
384
385    #[test]
386    fn query_message_response_header_struct_size() {
387        // Two i64 fields (8 bytes each) = 16 bytes total size
388        assert_eq!(mem::size_of::<QueryMessageResponseHeader>(), 16);
389    }
390
391    #[test]
392    fn test_compatibility_with_brokers_query_message_processor_usage() {
393        let header = QueryMessageResponseHeader {
394            index_last_update_timestamp: 123456789,
395            index_last_update_phyoffset: 987654321,
396        };
397
398        let mut command = RemotingCommand::create_response_command_with_header(header);
399        command.make_custom_header_to_net();
400
401        let ext_fields = command.ext_fields().unwrap();
402        assert_eq!(
403            ext_fields
404                .get(&CheetahString::from_static_str("indexLastUpdateTimestamp"))
405                .unwrap(),
406            "123456789"
407        );
408        assert_eq!(
409            ext_fields
410                .get(&CheetahString::from_static_str("indexLastUpdatePhyoffset"))
411                .unwrap(),
412            "987654321"
413        );
414    }
415
416    #[test]
417    fn test_usage_in_remoting_command_create_response_command_with_header() {
418        let header = QueryMessageResponseHeader {
419            index_last_update_timestamp: 11111,
420            index_last_update_phyoffset: 22222,
421        };
422
423        let command = RemotingCommand::create_response_command_with_header(header);
424
425        let extracted_header = command.read_custom_header_ref::<QueryMessageResponseHeader>().unwrap();
426
427        assert_eq!(extracted_header.index_last_update_timestamp, 11111);
428        assert_eq!(extracted_header.index_last_update_phyoffset, 22222);
429    }
430
431    #[test]
432    fn test_read_custom_header_mut_integration() {
433        let header = QueryMessageResponseHeader {
434            index_last_update_timestamp: 100,
435            index_last_update_phyoffset: 200,
436        };
437
438        let mut command = RemotingCommand::create_response_command_with_header(header);
439
440        if let Some(mut_header) = command.read_custom_header_mut::<QueryMessageResponseHeader>() {
441            mut_header.index_last_update_timestamp = 300;
442            mut_header.index_last_update_phyoffset = 400;
443        }
444
445        let extracted_header = command.read_custom_header_ref::<QueryMessageResponseHeader>().unwrap();
446        assert_eq!(extracted_header.index_last_update_timestamp, 300);
447        assert_eq!(extracted_header.index_last_update_phyoffset, 400);
448    }
449
450    #[test]
451    fn test_default_usage_in_response_creation() {
452        let header = QueryMessageResponseHeader::default();
453        let mut command = RemotingCommand::create_response_command_with_header(header);
454
455        let extracted_header = command.read_custom_header_ref::<QueryMessageResponseHeader>().unwrap();
456        assert_eq!(extracted_header.index_last_update_timestamp, 0);
457        assert_eq!(extracted_header.index_last_update_phyoffset, 0);
458
459        if let Some(mut_header) = command.read_custom_header_mut::<QueryMessageResponseHeader>() {
460            mut_header.index_last_update_timestamp = 1314;
461        }
462
463        let updated_header = command.read_custom_header_ref::<QueryMessageResponseHeader>().unwrap();
464        assert_eq!(updated_header.index_last_update_timestamp, 1314);
465        assert_eq!(updated_header.index_last_update_phyoffset, 0);
466    }
467
468    // Timestamp and Offset Relationship Tests
469    #[test]
470    fn test_both_fields_set_to_same_value() {
471        let header = QueryMessageResponseHeader {
472            index_last_update_timestamp: 12345,
473            index_last_update_phyoffset: 12345,
474        };
475        assert_eq!(header.index_last_update_timestamp, header.index_last_update_phyoffset);
476    }
477
478    #[test]
479    fn test_timestamp_greater_than_phyoffset_normal_case() {
480        let header = QueryMessageResponseHeader {
481            index_last_update_timestamp: 1684300000000,
482            index_last_update_phyoffset: 50000,
483        };
484        assert!(header.index_last_update_timestamp > header.index_last_update_phyoffset);
485    }
486
487    #[test]
488    fn test_timestamp_less_than_phyoffset() {
489        let header = QueryMessageResponseHeader {
490            index_last_update_timestamp: 1000,
491            index_last_update_phyoffset: 999999999,
492        };
493        assert!(header.index_last_update_timestamp < header.index_last_update_phyoffset);
494    }
495
496    #[test]
497    fn test_both_fields_set_to_zero_initial_state() {
498        let header = QueryMessageResponseHeader {
499            index_last_update_timestamp: 0,
500            index_last_update_phyoffset: 0,
501        };
502        assert_eq!(header.index_last_update_timestamp, 0);
503        assert_eq!(header.index_last_update_phyoffset, 0);
504
505        let default_header = QueryMessageResponseHeader::default();
506        assert_eq!(header, default_header);
507    }
508
509    #[test]
510    fn test_updating_index_last_update_timestamp_after_creation() {
511        let mut header = QueryMessageResponseHeader {
512            index_last_update_timestamp: 100,
513            index_last_update_phyoffset: 0,
514        };
515
516        header.index_last_update_timestamp = 500;
517        assert_eq!(header.index_last_update_timestamp, 500);
518        assert_eq!(header.index_last_update_phyoffset, 0);
519    }
520
521    #[test]
522    fn test_updating_index_last_update_phyoffset_after_creation() {
523        let mut header = QueryMessageResponseHeader {
524            index_last_update_timestamp: 0,
525            index_last_update_phyoffset: 200,
526        };
527
528        header.index_last_update_phyoffset = 800;
529        assert_eq!(header.index_last_update_phyoffset, 800);
530        assert_eq!(header.index_last_update_timestamp, 0);
531    }
532
533    #[test]
534    fn test_updating_both_fields_simultaneously() {
535        let mut header = QueryMessageResponseHeader {
536            index_last_update_timestamp: 10,
537            index_last_update_phyoffset: 20,
538        };
539
540        header.index_last_update_timestamp = 999;
541        header.index_last_update_phyoffset = 888;
542
543        assert_eq!(header.index_last_update_timestamp, 999);
544        assert_eq!(header.index_last_update_phyoffset, 888);
545    }
546}