1use cheetah_string::CheetahString;
16use rocketmq_macros::RequestHeaderCodec;
17use serde::Deserialize;
18use serde::Serialize;
19
20use crate::rpc::rpc_request_header::RpcRequestHeader;
21
22#[derive(Clone, Debug, Serialize, Deserialize, Default, RequestHeaderCodec)]
23#[header(
24 type_id = "rocketmq_protocol::protocol::header::end_transaction_request_header::EndTransactionRequestHeader",
25 java_class = "org.apache.rocketmq.remoting.protocol.header.EndTransactionRequestHeader",
26 validate = "Self::validate"
27)]
28#[serde(rename_all = "camelCase")]
29pub struct EndTransactionRequestHeader {
30 #[serde(default)]
31 #[header(default, default_semantic = "literal:")]
32 pub topic: CheetahString,
33
34 #[header(required)]
35 pub producer_group: CheetahString,
36
37 #[header(required)]
39 pub tran_state_table_offset: i64,
40
41 #[header(required)]
43 pub commit_log_offset: i64,
44
45 #[header(required)]
47 pub commit_or_rollback: i32,
48
49 #[serde(default)]
51 #[header(default, default_semantic = "literal:false")]
52 pub from_transaction_check: bool,
53
54 #[header(required)]
55 pub msg_id: CheetahString,
56
57 pub transaction_id: Option<CheetahString>,
58
59 #[serde(flatten)]
60 #[header(flatten, presence = "always")]
61 pub rpc_request_header: RpcRequestHeader,
62}
63
64impl EndTransactionRequestHeader {
65 fn validate(&self) -> Result<(), crate::ProtocolContractViolation> {
66 use rocketmq_model::common::sys_flag::message_sys_flag::MessageSysFlag;
67
68 match self.commit_or_rollback {
69 MessageSysFlag::TRANSACTION_NOT_TYPE
70 | MessageSysFlag::TRANSACTION_COMMIT_TYPE
71 | MessageSysFlag::TRANSACTION_ROLLBACK_TYPE => Ok(()),
72 _ => Err(crate::ProtocolContractViolation::Validation {
73 header:
74 "rocketmq_protocol::protocol::header::end_transaction_request_header::EndTransactionRequestHeader",
75 rule: "supported_transaction_state",
76 }),
77 }
78 }
79}
80
81#[cfg(test)]
82impl EndTransactionRequestHeader {
83 const TOPIC: &'static str = "topic";
84 const PRODUCER_GROUP: &'static str = "producerGroup";
85 const TRAN_STATE_TABLE_OFFSET: &'static str = "tranStateTableOffset";
86 const COMMIT_LOG_OFFSET: &'static str = "commitLogOffset";
87 const COMMIT_OR_ROLLBACK: &'static str = "commitOrRollback";
88 const FROM_TRANSACTION_CHECK: &'static str = "fromTransactionCheck";
89 const MSG_ID: &'static str = "msgId";
90 const TRANSACTION_ID: &'static str = "transactionId";
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96
97 #[test]
98 fn end_transaction_request_header_serialization() {
99 let header = EndTransactionRequestHeader {
100 topic: CheetahString::from("topic1"),
101 producer_group: CheetahString::from("group1"),
102 tran_state_table_offset: 123,
103 commit_log_offset: 456,
104 commit_or_rollback: 8,
105 from_transaction_check: true,
106 msg_id: CheetahString::from("msg1"),
107 transaction_id: Some(CheetahString::from("tran1")),
108 rpc_request_header: RpcRequestHeader {
109 broker_name: Some(CheetahString::from("broker1")),
110 ..Default::default()
111 },
112 };
113 let json = serde_json::to_string(&header).unwrap();
114 assert!(json.contains("\"topic\":\"topic1\""));
115 assert!(json.contains("\"producerGroup\":\"group1\""));
116 assert!(json.contains("\"tranStateTableOffset\":123"));
117 assert!(json.contains("\"commitLogOffset\":456"));
118 assert!(json.contains("\"commitOrRollback\":8"));
119 assert!(json.contains("\"fromTransactionCheck\":true"));
120 assert!(json.contains("\"msgId\":\"msg1\""));
121 assert!(json.contains("\"transactionId\":\"tran1\""));
122 assert!(json.contains("\"bname\":\"broker1\""));
123 }
124
125 #[test]
126 fn end_transaction_request_header_deserialization() {
127 let json = r#"{"topic":"topic1","producerGroup":"group1","tranStateTableOffset":123,"commitLogOffset":456,"commitOrRollback":8,"fromTransactionCheck":true,"msgId":"msg1","transactionId":"tran1","bname":"broker1"}"#;
128 let header: EndTransactionRequestHeader = serde_json::from_str(json).unwrap();
129 assert_eq!(header.topic, "topic1");
130 assert_eq!(header.producer_group, "group1");
131 assert_eq!(header.tran_state_table_offset, 123);
132 assert_eq!(header.commit_log_offset, 456);
133 assert_eq!(header.commit_or_rollback, 8);
134 assert!(header.from_transaction_check);
135 assert_eq!(header.msg_id, "msg1");
136 assert_eq!(header.transaction_id.as_ref().unwrap(), "tran1");
137 assert_eq!(header.rpc_request_header.broker_name.as_ref().unwrap(), "broker1");
138 }
139 #[test]
140 fn end_transaction_request_header_required_fields() {
141 let json = r#"{"topic":"topic1","producerGroup":"group1","tranStateTableOffset":123,"commitLogOffset":456,"commitOrRollback":8,"fromTransactionCheck":true,"msgId":"msg1"}"#;
142 let header: EndTransactionRequestHeader = serde_json::from_str(json).unwrap();
143 assert_eq!(header.topic, "topic1");
144 assert_eq!(header.producer_group, "group1");
145 assert_eq!(header.tran_state_table_offset, 123);
146 assert_eq!(header.commit_log_offset, 456);
147 assert_eq!(header.commit_or_rollback, 8);
148 assert!(header.from_transaction_check);
149 assert_eq!(header.msg_id, "msg1");
150 assert!(header.transaction_id.is_none());
151 }
152
153 #[test]
154 fn end_transaction_request_header_to_map() {
155 use crate::protocol::command_custom_header::CommandCustomHeader;
156
157 let header = EndTransactionRequestHeader {
158 topic: CheetahString::from("topic1"),
159 producer_group: CheetahString::from("group1"),
160 tran_state_table_offset: 123,
161 commit_log_offset: 456,
162 commit_or_rollback: 8,
163 from_transaction_check: true,
164 msg_id: CheetahString::from("msg1"),
165 transaction_id: Some(CheetahString::from("tran1")),
166 rpc_request_header: RpcRequestHeader::default(),
167 };
168
169 let map = header.to_map().unwrap();
170 assert_eq!(
171 map.get(&CheetahString::from_static_str(EndTransactionRequestHeader::TOPIC))
172 .unwrap(),
173 "topic1"
174 );
175 assert_eq!(
176 map.get(&CheetahString::from_static_str(
177 EndTransactionRequestHeader::PRODUCER_GROUP
178 ))
179 .unwrap(),
180 "group1"
181 );
182 assert_eq!(
183 map.get(&CheetahString::from_static_str(
184 EndTransactionRequestHeader::TRAN_STATE_TABLE_OFFSET
185 ))
186 .unwrap(),
187 "123"
188 );
189 assert_eq!(
190 map.get(&CheetahString::from_static_str(
191 EndTransactionRequestHeader::COMMIT_LOG_OFFSET
192 ))
193 .unwrap(),
194 "456"
195 );
196 assert_eq!(
197 map.get(&CheetahString::from_static_str(
198 EndTransactionRequestHeader::COMMIT_OR_ROLLBACK
199 ))
200 .unwrap(),
201 "8"
202 );
203 assert_eq!(
204 map.get(&CheetahString::from_static_str(
205 EndTransactionRequestHeader::FROM_TRANSACTION_CHECK
206 ))
207 .unwrap(),
208 "true"
209 );
210 assert_eq!(
211 map.get(&CheetahString::from_static_str(EndTransactionRequestHeader::MSG_ID))
212 .unwrap(),
213 "msg1"
214 );
215 assert_eq!(
216 map.get(&CheetahString::from_static_str(
217 EndTransactionRequestHeader::TRANSACTION_ID
218 ))
219 .unwrap(),
220 "tran1"
221 );
222 }
223
224 #[test]
225 fn end_transaction_request_header_to_map_without_transaction_id() {
226 use crate::protocol::command_custom_header::CommandCustomHeader;
227
228 let header = EndTransactionRequestHeader {
229 topic: CheetahString::from("topic1"),
230 producer_group: CheetahString::from("group1"),
231 tran_state_table_offset: 123,
232 commit_log_offset: 456,
233 commit_or_rollback: 8,
234 from_transaction_check: true,
235 msg_id: CheetahString::from("msg1"),
236 transaction_id: None,
237 rpc_request_header: RpcRequestHeader::default(),
238 };
239
240 let map = header.to_map().unwrap();
241 assert_eq!(
242 map.get(&CheetahString::from_static_str(EndTransactionRequestHeader::TOPIC))
243 .unwrap(),
244 "topic1"
245 );
246 assert!(!map.contains_key(&CheetahString::from_static_str(
247 EndTransactionRequestHeader::TRANSACTION_ID
248 )));
249 }
250
251 #[test]
252 fn end_transaction_request_header_from_map() {
253 use crate::protocol::command_custom_header::FromMap;
254 use std::collections::HashMap;
255
256 let mut map = HashMap::new();
257 map.insert(
258 CheetahString::from_static_str(EndTransactionRequestHeader::TOPIC),
259 CheetahString::from_static_str("topic1"),
260 );
261 map.insert(
262 CheetahString::from_static_str(EndTransactionRequestHeader::PRODUCER_GROUP),
263 CheetahString::from_static_str("group1"),
264 );
265 map.insert(
266 CheetahString::from_static_str(EndTransactionRequestHeader::TRAN_STATE_TABLE_OFFSET),
267 CheetahString::from_static_str("123"),
268 );
269 map.insert(
270 CheetahString::from_static_str(EndTransactionRequestHeader::COMMIT_LOG_OFFSET),
271 CheetahString::from_static_str("456"),
272 );
273 map.insert(
274 CheetahString::from_static_str(EndTransactionRequestHeader::COMMIT_OR_ROLLBACK),
275 CheetahString::from_static_str("8"),
276 );
277 map.insert(
278 CheetahString::from_static_str(EndTransactionRequestHeader::FROM_TRANSACTION_CHECK),
279 CheetahString::from_static_str("true"),
280 );
281 map.insert(
282 CheetahString::from_static_str(EndTransactionRequestHeader::MSG_ID),
283 CheetahString::from_static_str("msg1"),
284 );
285 map.insert(
286 CheetahString::from_static_str(EndTransactionRequestHeader::TRANSACTION_ID),
287 CheetahString::from_static_str("tran1"),
288 );
289
290 let header = <EndTransactionRequestHeader as FromMap>::from(&map).unwrap();
291 assert_eq!(header.topic, "topic1");
292 assert_eq!(header.producer_group, "group1");
293 assert_eq!(header.tran_state_table_offset, 123);
294 assert_eq!(header.commit_log_offset, 456);
295 assert_eq!(header.commit_or_rollback, 8);
296 assert!(header.from_transaction_check);
297 assert_eq!(header.msg_id, "msg1");
298 assert_eq!(header.transaction_id.unwrap(), "tran1");
299 }
300
301 #[test]
302 fn end_transaction_request_header_preserves_java_signed_long_offsets() {
303 use crate::protocol::command_custom_header::CommandCustomHeader;
304 use crate::protocol::command_custom_header::FromMap;
305 use std::collections::HashMap;
306
307 let header = EndTransactionRequestHeader {
308 topic: CheetahString::from("topic1"),
309 producer_group: CheetahString::from("group1"),
310 tran_state_table_offset: -123,
311 commit_log_offset: -456,
312 commit_or_rollback: 8,
313 from_transaction_check: true,
314 msg_id: CheetahString::from("msg1"),
315 transaction_id: None,
316 rpc_request_header: RpcRequestHeader::default(),
317 };
318 let map = header.to_map().unwrap();
319 assert_eq!(
320 map.get(&CheetahString::from_static_str(
321 EndTransactionRequestHeader::TRAN_STATE_TABLE_OFFSET
322 )),
323 Some(&CheetahString::from_static_str("-123"))
324 );
325 assert_eq!(
326 map.get(&CheetahString::from_static_str(
327 EndTransactionRequestHeader::COMMIT_LOG_OFFSET
328 )),
329 Some(&CheetahString::from_static_str("-456"))
330 );
331
332 let restored = <EndTransactionRequestHeader as FromMap>::from(&HashMap::from([
333 (
334 CheetahString::from_static_str(EndTransactionRequestHeader::TOPIC),
335 CheetahString::from_static_str("topic1"),
336 ),
337 (
338 CheetahString::from_static_str(EndTransactionRequestHeader::PRODUCER_GROUP),
339 CheetahString::from_static_str("group1"),
340 ),
341 (
342 CheetahString::from_static_str(EndTransactionRequestHeader::TRAN_STATE_TABLE_OFFSET),
343 CheetahString::from_static_str("-123"),
344 ),
345 (
346 CheetahString::from_static_str(EndTransactionRequestHeader::COMMIT_LOG_OFFSET),
347 CheetahString::from_static_str("-456"),
348 ),
349 (
350 CheetahString::from_static_str(EndTransactionRequestHeader::COMMIT_OR_ROLLBACK),
351 CheetahString::from_static_str("8"),
352 ),
353 (
354 CheetahString::from_static_str(EndTransactionRequestHeader::FROM_TRANSACTION_CHECK),
355 CheetahString::from_static_str("true"),
356 ),
357 (
358 CheetahString::from_static_str(EndTransactionRequestHeader::MSG_ID),
359 CheetahString::from_static_str("msg1"),
360 ),
361 ]))
362 .unwrap();
363
364 assert_eq!(restored.tran_state_table_offset, -123);
365 assert_eq!(restored.commit_log_offset, -456);
366 }
367
368 #[test]
369 fn end_transaction_request_header_from_map_without_transaction_id() {
370 use crate::protocol::command_custom_header::FromMap;
371 use std::collections::HashMap;
372
373 let mut map = HashMap::new();
374 map.insert(
375 CheetahString::from_static_str(EndTransactionRequestHeader::TOPIC),
376 CheetahString::from_static_str("topic1"),
377 );
378 map.insert(
379 CheetahString::from_static_str(EndTransactionRequestHeader::PRODUCER_GROUP),
380 CheetahString::from_static_str("group1"),
381 );
382 map.insert(
383 CheetahString::from_static_str(EndTransactionRequestHeader::TRAN_STATE_TABLE_OFFSET),
384 CheetahString::from_static_str("123"),
385 );
386 map.insert(
387 CheetahString::from_static_str(EndTransactionRequestHeader::COMMIT_LOG_OFFSET),
388 CheetahString::from_static_str("456"),
389 );
390 map.insert(
391 CheetahString::from_static_str(EndTransactionRequestHeader::COMMIT_OR_ROLLBACK),
392 CheetahString::from_static_str("8"),
393 );
394 map.insert(
395 CheetahString::from_static_str(EndTransactionRequestHeader::FROM_TRANSACTION_CHECK),
396 CheetahString::from_static_str("true"),
397 );
398 map.insert(
399 CheetahString::from_static_str(EndTransactionRequestHeader::MSG_ID),
400 CheetahString::from_static_str("msg1"),
401 );
402
403 let header = <EndTransactionRequestHeader as FromMap>::from(&map).unwrap();
404 assert_eq!(header.topic, "topic1");
405 assert_eq!(header.producer_group, "group1");
406 assert_eq!(header.tran_state_table_offset, 123);
407 assert_eq!(header.commit_log_offset, 456);
408 assert_eq!(header.commit_or_rollback, 8);
409 assert!(header.from_transaction_check);
410 assert_eq!(header.msg_id, "msg1");
411 assert!(header.transaction_id.is_none());
412 }
413
414 #[test]
415 fn end_transaction_request_header_from_map_missing_required_field() {
416 use crate::protocol::command_custom_header::FromMap;
417 use std::collections::HashMap;
418
419 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
420 enum RequiredField {
421 ProducerGroup,
422 TranStateTableOffset,
423 CommitLogOffset,
424 CommitOrRollback,
425 MsgId,
426 }
427
428 impl RequiredField {
429 fn as_str(&self) -> &'static str {
430 match self {
431 RequiredField::ProducerGroup => EndTransactionRequestHeader::PRODUCER_GROUP,
432 RequiredField::TranStateTableOffset => EndTransactionRequestHeader::TRAN_STATE_TABLE_OFFSET,
433 RequiredField::CommitLogOffset => EndTransactionRequestHeader::COMMIT_LOG_OFFSET,
434 RequiredField::CommitOrRollback => EndTransactionRequestHeader::COMMIT_OR_ROLLBACK,
435 RequiredField::MsgId => EndTransactionRequestHeader::MSG_ID,
436 }
437 }
438
439 fn test_value(&self) -> &'static str {
440 match self {
441 RequiredField::ProducerGroup => "group1",
442 RequiredField::TranStateTableOffset => "123",
443 RequiredField::CommitLogOffset => "456",
444 RequiredField::CommitOrRollback => "8",
445 RequiredField::MsgId => "msg1",
446 }
447 }
448 }
449
450 let all_required_fields: &[RequiredField] = &[
451 RequiredField::ProducerGroup,
452 RequiredField::TranStateTableOffset,
453 RequiredField::CommitLogOffset,
454 RequiredField::CommitOrRollback,
455 RequiredField::MsgId,
456 ];
457
458 for &missing_field in all_required_fields {
459 let mut map = HashMap::new();
460 for &field in all_required_fields {
461 if field != missing_field {
462 map.insert(
463 CheetahString::from_static_str(field.as_str()),
464 CheetahString::from_static_str(field.test_value()),
465 );
466 }
467 }
468
469 let result = <EndTransactionRequestHeader as FromMap>::from(&map);
470 assert!(
471 result.is_err(),
472 "Expected failure when missing required field: {:?}",
473 missing_field
474 );
475 }
476 }
477
478 #[test]
479 fn end_transaction_request_header_from_map_invalid_numeric_field() {
480 use crate::protocol::command_custom_header::FromMap;
481 use std::collections::HashMap;
482
483 let mut map = HashMap::new();
484 map.insert(
485 CheetahString::from_static_str(EndTransactionRequestHeader::TOPIC),
486 CheetahString::from_static_str("topic1"),
487 );
488 map.insert(
489 CheetahString::from_static_str(EndTransactionRequestHeader::PRODUCER_GROUP),
490 CheetahString::from_static_str("group1"),
491 );
492 map.insert(
493 CheetahString::from_static_str(EndTransactionRequestHeader::TRAN_STATE_TABLE_OFFSET),
494 CheetahString::from_static_str("invalid"),
495 );
496 map.insert(
497 CheetahString::from_static_str(EndTransactionRequestHeader::COMMIT_LOG_OFFSET),
498 CheetahString::from_static_str("456"),
499 );
500 map.insert(
501 CheetahString::from_static_str(EndTransactionRequestHeader::COMMIT_OR_ROLLBACK),
502 CheetahString::from_static_str("8"),
503 );
504 map.insert(
505 CheetahString::from_static_str(EndTransactionRequestHeader::FROM_TRANSACTION_CHECK),
506 CheetahString::from_static_str("true"),
507 );
508 map.insert(
509 CheetahString::from_static_str(EndTransactionRequestHeader::MSG_ID),
510 CheetahString::from_static_str("msg1"),
511 );
512
513 let result = <EndTransactionRequestHeader as FromMap>::from(&map);
514 assert!(result.is_err());
515 }
516
517 #[test]
518 fn end_transaction_request_header_from_map_invalid_bool_field() {
519 use crate::protocol::command_custom_header::FromMap;
520 use std::collections::HashMap;
521
522 let mut map = HashMap::new();
523 map.insert(
524 CheetahString::from_static_str(EndTransactionRequestHeader::TOPIC),
525 CheetahString::from_static_str("topic1"),
526 );
527 map.insert(
528 CheetahString::from_static_str(EndTransactionRequestHeader::PRODUCER_GROUP),
529 CheetahString::from_static_str("group1"),
530 );
531 map.insert(
532 CheetahString::from_static_str(EndTransactionRequestHeader::TRAN_STATE_TABLE_OFFSET),
533 CheetahString::from_static_str("123"),
534 );
535 map.insert(
536 CheetahString::from_static_str(EndTransactionRequestHeader::COMMIT_LOG_OFFSET),
537 CheetahString::from_static_str("456"),
538 );
539 map.insert(
540 CheetahString::from_static_str(EndTransactionRequestHeader::COMMIT_OR_ROLLBACK),
541 CheetahString::from_static_str("8"),
542 );
543 map.insert(
544 CheetahString::from_static_str(EndTransactionRequestHeader::FROM_TRANSACTION_CHECK),
545 CheetahString::from_static_str("invalid"),
546 );
547 map.insert(
548 CheetahString::from_static_str(EndTransactionRequestHeader::MSG_ID),
549 CheetahString::from_static_str("msg1"),
550 );
551
552 let result = <EndTransactionRequestHeader as FromMap>::from(&map);
553 assert!(result.is_err());
554 }
555
556 #[test]
557 fn end_transaction_request_header_from_map_transaction_state_values() {
558 use crate::protocol::command_custom_header::FromMap;
559 use rocketmq_model::common::sys_flag::message_sys_flag::MessageSysFlag;
560 use std::collections::HashMap;
561
562 struct TestCase {
563 value: i32,
564 }
565
566 let test_cases = &[
567 TestCase {
568 value: MessageSysFlag::TRANSACTION_NOT_TYPE,
569 },
570 TestCase {
571 value: MessageSysFlag::TRANSACTION_COMMIT_TYPE,
572 },
573 TestCase {
574 value: MessageSysFlag::TRANSACTION_ROLLBACK_TYPE,
575 },
576 ];
577
578 for case in test_cases {
579 let mut map = HashMap::new();
580 map.insert(
581 CheetahString::from_static_str(EndTransactionRequestHeader::TOPIC),
582 CheetahString::from_static_str("topic1"),
583 );
584 map.insert(
585 CheetahString::from_static_str(EndTransactionRequestHeader::PRODUCER_GROUP),
586 CheetahString::from_static_str("group1"),
587 );
588 map.insert(
589 CheetahString::from_static_str(EndTransactionRequestHeader::TRAN_STATE_TABLE_OFFSET),
590 CheetahString::from_static_str("123"),
591 );
592 map.insert(
593 CheetahString::from_static_str(EndTransactionRequestHeader::COMMIT_LOG_OFFSET),
594 CheetahString::from_static_str("456"),
595 );
596 map.insert(
597 CheetahString::from_static_str(EndTransactionRequestHeader::COMMIT_OR_ROLLBACK),
598 CheetahString::from(case.value.to_string()),
599 );
600 map.insert(
601 CheetahString::from_static_str(EndTransactionRequestHeader::FROM_TRANSACTION_CHECK),
602 CheetahString::from_static_str("true"),
603 );
604 map.insert(
605 CheetahString::from_static_str(EndTransactionRequestHeader::MSG_ID),
606 CheetahString::from_static_str("msg1"),
607 );
608
609 let result = <EndTransactionRequestHeader as FromMap>::from(&map);
610 assert!(
611 result.is_ok(),
612 "commit_or_rollback={} should parse successfully but got {:?}",
613 case.value,
614 result
615 );
616 let header = result.unwrap();
617 assert_eq!(
618 header.commit_or_rollback, case.value,
619 "commit_or_rollback should be {}",
620 case.value
621 );
622 }
623 }
624
625 #[test]
626 fn end_transaction_request_header_rejects_unsupported_transaction_state() {
627 use crate::protocol::command_custom_header::FromMap;
628 use std::collections::HashMap;
629
630 let fields = HashMap::from([
631 ("producerGroup".into(), "group-a".into()),
632 ("tranStateTableOffset".into(), "1".into()),
633 ("commitLogOffset".into(), "2".into()),
634 ("commitOrRollback".into(), "999".into()),
635 ("msgId".into(), "msg-a".into()),
636 ]);
637 assert!(<EndTransactionRequestHeader as FromMap>::from(&fields).is_err());
638 }
639
640 #[test]
641 fn end_transaction_request_header_roundtrip() {
642 use crate::protocol::command_custom_header::CommandCustomHeader;
643 use crate::protocol::command_custom_header::FromMap;
644 use rocketmq_model::common::sys_flag::message_sys_flag::MessageSysFlag;
645
646 let original = EndTransactionRequestHeader {
647 topic: CheetahString::from("topic1"),
648 producer_group: CheetahString::from("group1"),
649 tran_state_table_offset: 123,
650 commit_log_offset: 456,
651 commit_or_rollback: MessageSysFlag::TRANSACTION_COMMIT_TYPE,
652 from_transaction_check: true,
653 msg_id: CheetahString::from("msg1"),
654 transaction_id: Some(CheetahString::from("tran1")),
655 rpc_request_header: RpcRequestHeader::default(),
656 };
657
658 let map = original.to_map().unwrap();
659 let restored = <EndTransactionRequestHeader as FromMap>::from(&map).unwrap();
660
661 assert_eq!(original.topic, restored.topic);
662 assert_eq!(original.producer_group, restored.producer_group);
663 assert_eq!(original.tran_state_table_offset, restored.tran_state_table_offset);
664 assert_eq!(original.commit_log_offset, restored.commit_log_offset);
665 assert_eq!(original.commit_or_rollback, restored.commit_or_rollback);
666 assert_eq!(original.from_transaction_check, restored.from_transaction_check);
667 assert_eq!(original.msg_id, restored.msg_id);
668 assert_eq!(original.transaction_id, restored.transaction_id);
669 }
670
671 #[test]
672 fn end_transaction_request_header_via_remoting_command() {
673 use crate::code::request_code::RequestCode;
674 use crate::protocol::remoting_command::RemotingCommand;
675 use bytes::BytesMut;
676 use rocketmq_model::common::sys_flag::message_sys_flag::MessageSysFlag;
677
678 let original = EndTransactionRequestHeader {
679 topic: CheetahString::from("topic1"),
680 producer_group: CheetahString::from("group1"),
681 tran_state_table_offset: 123,
682 commit_log_offset: 456,
683 commit_or_rollback: MessageSysFlag::TRANSACTION_COMMIT_TYPE,
684 from_transaction_check: true,
685 msg_id: CheetahString::from("msg1"),
686 transaction_id: Some(CheetahString::from("tran1")),
687 rpc_request_header: RpcRequestHeader::default(),
688 };
689
690 let mut command = RemotingCommand::create_request_command(RequestCode::EndTransaction, original);
691 let header_bytes = command.encode_header().expect("Failed to encode header");
692
693 let mut buf = BytesMut::from(header_bytes);
694 let decoded_command = RemotingCommand::decode(&mut buf).expect("Failed to decode command");
695 let decoded_command = decoded_command.expect("Decoded command is None");
696
697 assert_eq!(decoded_command.code(), RequestCode::EndTransaction as i32);
698
699 let decoded_header = decoded_command
700 .decode_command_custom_header::<EndTransactionRequestHeader>()
701 .expect("Failed to decode header");
702
703 assert_eq!(decoded_header.topic, "topic1");
704 assert_eq!(decoded_header.producer_group, "group1");
705 assert_eq!(decoded_header.tran_state_table_offset, 123);
706 assert_eq!(decoded_header.commit_log_offset, 456);
707 assert_eq!(
708 decoded_header.commit_or_rollback,
709 MessageSysFlag::TRANSACTION_COMMIT_TYPE
710 );
711 assert!(decoded_header.from_transaction_check);
712 assert_eq!(decoded_header.msg_id, "msg1");
713 assert_eq!(decoded_header.transaction_id.unwrap(), "tran1");
714 }
715}