Skip to content

Commit 8e888d5

Browse files
committed
Add test case for happy resend flow where counterparty requests resend
1 parent a24a706 commit 8e888d5

3 files changed

Lines changed: 60 additions & 6 deletions

File tree

crates/hotfix/src/message.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ pub mod test_request;
1616
pub mod verification;
1717

1818
pub use parser::RawFixMessage;
19+
pub use resend_request::ResendRequest;
1920

2021
pub trait FixMessage: Clone + Send + 'static {
2122
fn write(&self, msg: &mut Message);

crates/hotfix/src/message/resend_request.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ use hotfix_message::message::Message;
33
use hotfix_message::{Part, fix44};
44

55
#[derive(Clone, Copy)]
6-
pub(crate) struct ResendRequest {
6+
pub struct ResendRequest {
77
begin_seq_no: u64,
88
end_seq_no: u64,
99
}
1010

1111
impl ResendRequest {
12-
pub(crate) fn new(begin: u64, end: u64) -> Self {
12+
pub fn new(begin: u64, end: u64) -> Self {
1313
Self {
1414
begin_seq_no: begin,
1515
end_seq_no: end,

crates/hotfix/tests/session_test_cases/resend_tests.rs

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
use crate::common::actions::when;
22
use crate::common::assertions::{assert_msg_type, then};
3-
use crate::common::setup::given_an_active_session;
3+
use crate::common::setup::{HEARTBEAT_INTERVAL, given_an_active_session};
44
use crate::common::test_messages::{
55
TestMessage, build_execution_report_with_incorrect_body_length, build_invalid_resend_request,
66
};
7-
use hotfix::message::FixMessage;
7+
use hotfix::message::{FixMessage, ResendRequest};
88
use hotfix::session::Status;
9-
use hotfix_message::FieldType;
10-
use hotfix_message::fix44::MsgType;
9+
use hotfix_message::fix44::{GAP_FILL_FLAG, MsgType, NEW_SEQ_NO};
10+
use hotfix_message::{FieldType, Part};
11+
use std::time::Duration;
1112

1213
#[tokio::test]
1314
async fn test_message_sequence_number_too_high() {
@@ -168,3 +169,55 @@ async fn test_invalid_resend_request_gets_rejected() {
168169
then(&mut counterparty).gets_disconnected().await;
169170
}
170171
}
172+
173+
/// Tests that when a counterparty requests a resend of both admin and business messages,
174+
/// the session gap fills admin messages and resends business messages as expected.
175+
#[tokio::test(start_paused = true)]
176+
async fn test_resend_request_with_gap_fill_for_admin_messages() {
177+
let (session, mut counterparty) = given_an_active_session().await;
178+
179+
// wait for a heartbeat to be sent automatically (this will be message sequence number 2)
180+
when(Duration::from_secs(HEARTBEAT_INTERVAL + 1))
181+
.elapses()
182+
.await;
183+
then(&mut counterparty)
184+
.receives(|msg| assert_msg_type(msg, MsgType::Heartbeat))
185+
.await;
186+
187+
// send an execution report from the session (this will be message sequence number 3)
188+
when(&session)
189+
.sends_message(TestMessage::dummy_execution_report())
190+
.await;
191+
then(&mut counterparty)
192+
.receives(|msg| assert_msg_type(msg, MsgType::ExecutionReport))
193+
.await;
194+
195+
// counterparty requests a resend of messages 2 and 3
196+
let resend_request = ResendRequest::new(2, 3);
197+
when(&mut counterparty).sends_message(resend_request).await;
198+
199+
// the session should send a SequenceReset-GapFill for the heartbeat (message 2)
200+
then(&mut counterparty)
201+
.receives(|msg| {
202+
assert_msg_type(msg, MsgType::SequenceReset);
203+
assert_eq!(msg.get::<&str>(GAP_FILL_FLAG).unwrap(), "Y");
204+
// the gap fill's MsgSeqNum indicates the beginning of the gap
205+
assert_eq!(
206+
msg.header()
207+
.get::<u64>(hotfix_message::fix44::MSG_SEQ_NUM)
208+
.unwrap(),
209+
2
210+
);
211+
// NewSeqNo indicates the next sequence number after the gap
212+
assert_eq!(msg.get::<u64>(NEW_SEQ_NO).unwrap(), 3);
213+
})
214+
.await;
215+
216+
// the session should resend the execution report (message 3)
217+
then(&mut counterparty)
218+
.receives(|msg| assert_msg_type(msg, MsgType::ExecutionReport))
219+
.await;
220+
221+
when(&session).requests_disconnect().await;
222+
then(&mut counterparty).gets_disconnected().await;
223+
}

0 commit comments

Comments
 (0)