use crate::common::actions::when;
use crate::common::assertions::{assert_msg_type, then};
use crate::common::cleanup::finally;
use crate::common::setup::{COUNTERPARTY_COMP_ID, OUR_COMP_ID, given_an_active_session};
use crate::common::test_messages::{
ExecutionReportWithInvalidField, TestMessage, TestReject, build_execution_report_with_comp_id,
build_execution_report_with_custom_msg_type,
build_execution_report_with_incorrect_begin_string,
build_execution_report_with_incorrect_body_length,
build_execution_report_with_incorrect_orig_sending_time,
build_execution_report_with_missing_orig_sending_time,
build_execution_report_with_missing_sending_time,
build_execution_report_with_sending_time_too_old,
};
use hotfix::session::Status;
use hotfix_message::Part;
use hotfix_message::fix44::{MsgType, SESSION_REJECT_REASON, SessionRejectReason};
#[tokio::test]
async fn test_message_with_invalid_field_gets_rejected() {
let (session, mut counterparty) = given_an_active_session().await;
when(&mut counterparty)
.sends_message(ExecutionReportWithInvalidField::default())
.await;
then(&mut counterparty)
.receives(|msg| assert_msg_type(msg, MsgType::Reject))
.await;
finally(&session, &mut counterparty).disconnect().await;
}
#[tokio::test]
async fn test_garbled_message_with_invalid_target_comp_id_gets_ignored() {
let (mut session, mut counterparty) = given_an_active_session().await;
let garbled_message_seq_num = counterparty.next_target_sequence_number();
let garbled_message =
build_execution_report_with_incorrect_body_length(garbled_message_seq_num);
when(&mut counterparty)
.sends_raw_message(garbled_message)
.await;
when(&mut counterparty)
.sends_message(TestMessage::dummy_execution_report())
.await;
then(&mut counterparty)
.receives(|msg| assert_msg_type(msg, MsgType::ResendRequest))
.await;
then(&mut session)
.status_changes_to(Status::AwaitingResend {
begin: garbled_message_seq_num,
end: garbled_message_seq_num + 1,
attempts: 1,
})
.await;
finally(&session, &mut counterparty).disconnect().await;
}
#[tokio::test]
async fn test_message_with_invalid_begin_string() {
let (_session, mut counterparty) = given_an_active_session().await;
let invalid_message = build_execution_report_with_incorrect_begin_string(
counterparty.next_target_sequence_number(),
);
when(&mut counterparty)
.sends_raw_message(invalid_message)
.await;
then(&mut counterparty)
.receives(|msg| assert_msg_type(msg, MsgType::Logout))
.await;
then(&mut counterparty).gets_disconnected().await;
}
#[tokio::test]
async fn test_message_with_invalid_target_comp_id() {
let (_session, mut counterparty) = given_an_active_session().await;
let invalid_message = build_execution_report_with_comp_id(
counterparty.next_target_sequence_number(),
COUNTERPARTY_COMP_ID,
"WRONG_COMP_ID",
);
when(&mut counterparty)
.sends_raw_message(invalid_message)
.await;
then(&mut counterparty)
.receives(|msg| assert_msg_type(msg, MsgType::Reject))
.await;
then(&mut counterparty)
.receives(|msg| assert_msg_type(msg, MsgType::Logout))
.await;
then(&mut counterparty).gets_disconnected().await;
}
#[tokio::test]
async fn test_message_with_invalid_sender_comp_id() {
let (_session, mut counterparty) = given_an_active_session().await;
let invalid_message = build_execution_report_with_comp_id(
counterparty.next_target_sequence_number(),
"WRONG_COMP_ID",
OUR_COMP_ID,
);
when(&mut counterparty)
.sends_raw_message(invalid_message)
.await;
then(&mut counterparty)
.receives(|msg| assert_msg_type(msg, MsgType::Reject))
.await;
then(&mut counterparty)
.receives(|msg| assert_msg_type(msg, MsgType::Logout))
.await;
then(&mut counterparty).gets_disconnected().await;
}
#[tokio::test]
async fn test_message_with_invalid_msg_type() {
let (mut session, mut counterparty) = given_an_active_session().await;
let sequence_number = counterparty.next_target_sequence_number();
let invalid_message = build_execution_report_with_custom_msg_type(sequence_number, "ZZ");
when(&mut counterparty)
.sends_raw_message(invalid_message)
.await;
then(&mut counterparty)
.receives(|msg| {
assert_msg_type(msg, MsgType::Reject);
assert_eq!(msg.get::<u32>(SESSION_REJECT_REASON).unwrap(), 11);
})
.await;
then(&mut session)
.target_sequence_number_reaches(sequence_number)
.await;
finally(&session, &mut counterparty).disconnect().await;
}
#[tokio::test]
async fn test_message_with_sequence_number_too_low() {
let (mut session, mut counterparty) = given_an_active_session().await;
let sequence_number = counterparty.next_target_sequence_number();
when(&mut counterparty)
.sends_message(TestMessage::dummy_execution_report())
.await;
then(&mut session)
.target_sequence_number_reaches(sequence_number)
.await;
counterparty.delete_last_message_from_store();
when(&mut counterparty)
.sends_message(TestMessage::dummy_execution_report())
.await;
then(&mut counterparty)
.receives(|msg| {
assert_msg_type(msg, MsgType::Logout);
})
.await;
then(&mut counterparty).gets_disconnected().await;
}
#[tokio::test]
async fn test_message_with_sequence_number_too_low_possdup_ignored() {
let (mut session, mut counterparty) = given_an_active_session().await;
let first_seq = counterparty.next_target_sequence_number();
when(&mut counterparty)
.sends_message(TestMessage::dummy_execution_report())
.await;
then(&mut session)
.target_sequence_number_reaches(first_seq)
.await;
when(&mut counterparty).resends_message(first_seq).await;
let second_seq = counterparty.next_target_sequence_number();
when(&mut counterparty)
.sends_message(TestMessage::dummy_execution_report())
.await;
then(&mut session)
.target_sequence_number_reaches(second_seq)
.await;
finally(&session, &mut counterparty).disconnect().await;
}
#[tokio::test]
async fn test_message_with_incorrect_orig_sending_time_is_rejected() {
let (mut session, mut counterparty) = given_an_active_session().await;
let seq_number = counterparty.next_target_sequence_number();
when(&mut counterparty)
.sends_message(TestMessage::dummy_execution_report())
.await;
then(&mut session)
.target_sequence_number_reaches(seq_number)
.await;
when(&mut counterparty)
.sends_raw_message(build_execution_report_with_incorrect_orig_sending_time(
seq_number,
))
.await;
then(&mut counterparty)
.receives(|msg| {
assert_msg_type(msg, MsgType::Reject);
assert_eq!(
msg.get::<SessionRejectReason>(SESSION_REJECT_REASON)
.unwrap(),
SessionRejectReason::SendingtimeAccuracyProblem
);
})
.await;
finally(&session, &mut counterparty).disconnect().await;
}
#[tokio::test]
async fn test_message_with_missing_orig_sending_time_is_rejected() {
let (mut session, mut counterparty) = given_an_active_session().await;
let seq_number = counterparty.next_target_sequence_number();
when(&mut counterparty)
.sends_message(TestMessage::dummy_execution_report())
.await;
then(&mut session)
.target_sequence_number_reaches(seq_number)
.await;
when(&mut counterparty)
.sends_raw_message(build_execution_report_with_missing_orig_sending_time(
seq_number,
))
.await;
then(&mut counterparty)
.receives(|msg| {
assert_msg_type(msg, MsgType::Reject);
assert_eq!(
msg.get::<SessionRejectReason>(SESSION_REJECT_REASON)
.unwrap(),
SessionRejectReason::RequiredTagMissing
);
})
.await;
finally(&session, &mut counterparty).disconnect().await;
}
#[tokio::test]
async fn test_message_with_missing_sending_time_is_rejected() {
let (mut session, mut counterparty) = given_an_active_session().await;
let seq_number = counterparty.next_target_sequence_number();
when(&mut counterparty)
.sends_raw_message(build_execution_report_with_missing_sending_time(seq_number))
.await;
then(&mut counterparty)
.receives(|msg| {
assert_msg_type(msg, MsgType::Reject);
assert_eq!(
msg.get::<SessionRejectReason>(SESSION_REJECT_REASON)
.unwrap(),
SessionRejectReason::SendingtimeAccuracyProblem
);
})
.await;
then(&mut session)
.target_sequence_number_reaches(seq_number)
.await;
finally(&session, &mut counterparty).disconnect().await;
}
#[tokio::test]
async fn test_message_with_sending_time_too_old_is_rejected() {
let (mut session, mut counterparty) = given_an_active_session().await;
let seq_number = counterparty.next_target_sequence_number();
when(&mut counterparty)
.sends_raw_message(build_execution_report_with_sending_time_too_old(seq_number))
.await;
then(&mut counterparty)
.receives(|msg| {
assert_msg_type(msg, MsgType::Reject);
assert_eq!(
msg.get::<SessionRejectReason>(SESSION_REJECT_REASON)
.unwrap(),
SessionRejectReason::SendingtimeAccuracyProblem
);
})
.await;
then(&mut session)
.target_sequence_number_reaches(seq_number)
.await;
finally(&session, &mut counterparty).disconnect().await;
}
#[tokio::test]
async fn test_scenario_2g_possdup_without_orig_sending_time() {
let (mut session, mut counterparty) = given_an_active_session().await;
let seq_number = counterparty.next_target_sequence_number();
when(&mut counterparty)
.sends_message(TestMessage::dummy_execution_report())
.await;
then(&mut session)
.target_sequence_number_reaches(seq_number)
.await;
when(&mut counterparty)
.sends_raw_message(build_execution_report_with_missing_orig_sending_time(
seq_number,
))
.await;
then(&mut counterparty)
.receives(|msg| {
assert_msg_type(msg, MsgType::Reject);
assert_eq!(
msg.get::<SessionRejectReason>(SESSION_REJECT_REASON)
.unwrap(),
SessionRejectReason::RequiredTagMissing
);
})
.await;
finally(&session, &mut counterparty).disconnect().await;
}
#[tokio::test]
async fn test_processing_reject_from_counterparty() {
let (mut session, mut counterparty) = given_an_active_session().await;
let reject_seq_num = counterparty.next_target_sequence_number();
when(&mut counterparty)
.sends_message(TestReject { ref_seq_num: 1 })
.await;
then(&mut session)
.target_sequence_number_reaches(reject_seq_num)
.await;
let next_seq_num = counterparty.next_target_sequence_number();
when(&mut counterparty)
.sends_message(TestMessage::dummy_execution_report())
.await;
then(&mut session)
.target_sequence_number_reaches(next_seq_num)
.await;
finally(&session, &mut counterparty).disconnect().await;
}