use crate::common::actions::when;
use crate::common::assertions::then;
use crate::common::cleanup::finally;
use crate::common::fakes::FakeApplication;
use crate::common::setup::{
given_a_disconnected_session, given_an_active_session, given_an_active_session_with_app,
};
use crate::common::test_messages::TestMessage;
use hotfix::application::OutboundDecision;
use hotfix::message::OutboundMessage;
use hotfix::session::{SendError, SendOutcome};
#[tokio::test]
async fn test_send_returns_sequence_number() {
let (session, mut counterparty) = given_an_active_session().await;
let outcome = when(&session)
.sends_message_with_confirmation(TestMessage::dummy_new_order_single())
.await
.expect("message should be sent successfully");
match outcome {
SendOutcome::Sent { sequence_number } => {
assert_eq!(
sequence_number, 2,
"First app message should have sequence number 2"
);
}
SendOutcome::Dropped => {
panic!("Message should not have been dropped");
}
}
then(&mut counterparty)
.receives(|msg| {
let parsed = TestMessage::parse(msg);
assert_eq!(parsed.message_type(), "D");
})
.await;
finally(&session, &mut counterparty).disconnect().await;
}
#[tokio::test]
async fn test_send_multiple_messages_returns_sequential_sequence_numbers() {
let (session, mut counterparty) = given_an_active_session().await;
let outcome1 = when(&session)
.sends_message_with_confirmation(TestMessage::dummy_new_order_single())
.await
.expect("first message should be sent");
let outcome2 = when(&session)
.sends_message_with_confirmation(TestMessage::dummy_execution_report())
.await
.expect("second message should be sent");
match (outcome1, outcome2) {
(
SendOutcome::Sent {
sequence_number: seq1,
},
SendOutcome::Sent {
sequence_number: seq2,
},
) => {
assert_eq!(seq1, 2, "First message should have sequence number 2");
assert_eq!(seq2, 3, "Second message should have sequence number 3");
}
_ => panic!("Both messages should have been sent successfully"),
}
then(&mut counterparty).receives(|_| {}).await;
then(&mut counterparty).receives(|_| {}).await;
finally(&session, &mut counterparty).disconnect().await;
}
#[tokio::test]
async fn test_send_forget_queues_message() {
let (session, mut counterparty) = given_an_active_session().await;
when(&session)
.sends_message_without_confirmation(TestMessage::dummy_new_order_single())
.await
.expect("message should be queued successfully");
then(&mut counterparty)
.receives(|msg| {
let parsed = TestMessage::parse(msg);
assert_eq!(parsed.message_type(), "D");
})
.await;
finally(&session, &mut counterparty).disconnect().await;
}
#[tokio::test]
async fn test_send_returns_disconnected_when_not_connected() {
let disconnected_session = given_a_disconnected_session();
let result = disconnected_session
.session_handle()
.send(TestMessage::dummy_new_order_single())
.await;
match result {
Err(SendError::Disconnected) => {
}
other => {
panic!("Expected SendError::Disconnected, got {:?}", other);
}
}
}
#[tokio::test]
async fn test_send_returns_dropped_when_app_drops_message() {
let (message_tx, message_rx) = tokio::sync::mpsc::unbounded_channel();
let app = FakeApplication::builder(message_tx)
.outbound_decision_fn(|_| OutboundDecision::Drop)
.build();
let (session, mut counterparty) = given_an_active_session_with_app(app, message_rx).await;
let result = when(&session)
.sends_message_with_confirmation(TestMessage::dummy_new_order_single())
.await;
match result {
Ok(SendOutcome::Dropped) => {
}
other => {
panic!("Expected SendOutcome::Dropped, got {:?}", other);
}
}
finally(&session, &mut counterparty).disconnect().await;
}
#[tokio::test]
async fn test_send_returns_session_terminated_when_app_terminates() {
let (message_tx, message_rx) = tokio::sync::mpsc::unbounded_channel();
let app = FakeApplication::builder(message_tx)
.outbound_decision_fn(|_| OutboundDecision::TerminateSession)
.build();
let (session, mut counterparty) = given_an_active_session_with_app(app, message_rx).await;
let result = when(&session)
.sends_message_with_confirmation(TestMessage::dummy_new_order_single())
.await;
match result {
Err(SendError::SessionTerminated) => {
}
other => {
panic!("Expected SendError::SessionTerminated, got {:?}", other);
}
}
counterparty
.assert_disconnected_with_timeout(std::time::Duration::from_secs(5))
.await;
}