async def test_on_receive_handles_gapfill( self, pipeline_with_messages, user_notification_message ): seq_num_app = SeqNumManagerApp(pipeline_with_messages) seq_num_app.startup_time = datetime.utcnow() - timedelta( seconds=10 ) # Don't wait for resend requests seq_num_app.receive_seq_num = 5 # 5 Messages received so far user_notification_message.seq_num = 8 # Simulate missing messages 6 and 7 try: await seq_num_app.on_receive(user_notification_message) assert pipeline_with_messages.send.call_count == 1 # Resend request sent except StopMessageProcessing: # Expected pass # Simulate resend of 6 and 7 for seq_num in [6, 7]: message = user_notification_message.copy() message.seq_num = seq_num message.PossDupFlag = True await seq_num_app.on_receive(message) # Wait for separate 'send' tasks to complete tasks = asyncio.all_tasks() await asyncio.wait(tasks, timeout=0.1) assert ( pipeline_with_messages.receive.call_count == 1 ) # One queued message (with sequence number 8) processed
async def test_handle_resend_request_sends_resend_request( self, pipeline_with_messages ): seq_num_app = SeqNumManagerApp(pipeline_with_messages) seq_num_app.send_seq_num = 3 # 3 messages sent so far resend_begin_seq_num = 2 # Simulate resend request of 2 and 3 await seq_num_app._handle_resend_request( admin.ResendRequestMessage(resend_begin_seq_num) ) # Wait for separate 'send' tasks to complete tasks = asyncio.all_tasks() await asyncio.wait(tasks, timeout=0.1) assert pipeline_with_messages.send.call_count == 2 for idx in range(pipeline_with_messages.send.call_count): message = pipeline_with_messages.send.mock_calls[idx][1][0] # Check sequence number assert message.seq_num == resend_begin_seq_num + idx # Check PossDup flag assert bool(message.PossDupFlag) is True # Check sending time assert str(message.OrigSendingTime) == str(message.SendingTime)
def test_handle_sequence_number_too_low_raises_exception_if_number_too_low( self, email_message): with pytest.raises(SessionError): pipeline_mock = MagicMock(BasePipeline) seq_num_app = SeqNumManagerApp(pipeline_mock) seq_num_app.receive_seq_num = 10 email_message.MsgSeqNum = 1 seq_num_app._handle_sequence_number_too_low(email_message)
async def test_send_resend_request_waits_for_target_before_doing_gapfill( self, pipeline_with_messages): seq_num_app = SeqNumManagerApp(pipeline_with_messages) seq_num_app.startup_time = datetime.utcnow() - timedelta( seconds=5) # Don't wait assert not seq_num_app.waited_for_resend_request_event.is_set() await seq_num_app._send_resend_request([1, 2]) assert seq_num_app.waited_for_resend_request_event.is_set()
def test_handle_sequence_number_too_low_skips_duplicates_with_low_sequence_numbers( self, email_message): with pytest.raises(StopMessageProcessing): pipeline_mock = MagicMock(BasePipeline) seq_num_app = SeqNumManagerApp(pipeline_mock) seq_num_app.receive_seq_num = 10 email_message.MsgSeqNum = 1 email_message.PossDupFlag = True seq_num_app._handle_sequence_number_too_low(email_message)
async def test_send_resend_request_sends_resend_request( self, pipeline_with_messages): seq_num_app = SeqNumManagerApp(pipeline_with_messages) seq_num_app.startup_time = datetime.utcnow() - timedelta( seconds=5) # Don't wait await seq_num_app._send_resend_request([1, 2]) # Wait for separate 'send' tasks to complete tasks = asyncio.all_tasks() await asyncio.wait(tasks, timeout=0.1) assert pipeline_with_messages.send.call_count == 1
async def test_handle_resend_request_converts_admin_messages_to_sequence_reset_messages( self, logon_message, pipeline_with_messages, messages ): seq_num_app = SeqNumManagerApp(pipeline_with_messages) admin_messages = [logon_message, HeartbeatMessage("test123")] # Inject admin messages messages = admin_messages + messages # Reset sequence numbers for idx, message in enumerate(messages): message.MsgSeqNum = idx + 1 message_store_app = pipeline_with_messages.apps[MessageStoreApp.name] await message_store_app.initialize() message_store_app.store._store.clear() for message in messages: await message_store_app.set_sent(message) seq_num_app.send_seq_num = max( message.seq_num for message in pipeline_with_messages.apps[ MessageStoreApp.name ].store._store.values() ) resend_begin_seq_num = 1 await seq_num_app._handle_resend_request( admin.ResendRequestMessage(resend_begin_seq_num) ) # Wait for separate 'send' tasks to complete tasks = asyncio.all_tasks() await asyncio.wait(tasks, timeout=0.1) assert pipeline_with_messages.send.call_count == 6 admin_messages_resend = pipeline_with_messages.send.mock_calls[0][1][0] # Check SequenceReset message is constructed correctly assert admin_messages_resend.seq_num == 1 assert int(admin_messages_resend.NewSeqNo) == 3 assert bool(admin_messages_resend.PossDupFlag) is True # Check first non-admin message starts with correct sequence number first_non_admin_message_resend = pipeline_with_messages.send.mock_calls[1][1][0] assert first_non_admin_message_resend.seq_num == 3
async def test_start_resumes_sequence_numbers(self, pipeline_with_messages): pipeline_with_messages.apps[ClientSessionApp.name]._new_session = False seq_num_app = SeqNumManagerApp(pipeline_with_messages) await seq_num_app.start() assert seq_num_app.send_seq_num == 3 assert seq_num_app.receive_seq_num == 5
async def test_handle_seq_num_too_high_starts_buffer_and_sends_resend_request( self, pipeline_with_messages, email_message): with pytest.raises(StopMessageProcessing): seq_num_app = SeqNumManagerApp(pipeline_with_messages) seq_num_app.startup_time = datetime.utcnow() - timedelta( seconds=5) # Don't wait email_message.MsgSeqNum = 99 await seq_num_app._handle_sequence_number_too_high(email_message) # Wait for separate 'send' tasks to complete tasks = asyncio.all_tasks() await asyncio.wait(tasks, timeout=0.1) assert len(seq_num_app.receive_buffer) == 1 assert seq_num_app.receive_buffer[0] == email_message assert pipeline_with_messages.send.call_count == 1
async def test_handle_seq_num_too_high_buffers_messages_received_out_of_order( self, pipeline_with_messages, user_notification_message): seq_num_app = SeqNumManagerApp(pipeline_with_messages) seq_num_app.startup_time = datetime.utcnow() - timedelta( seconds=5) # Don't wait for idx in range(5): out_of_sequence_msg = user_notification_message.copy() out_of_sequence_msg.MsgSeqNum = 5 + idx try: await seq_num_app._handle_sequence_number_too_high( out_of_sequence_msg) except StopMessageProcessing: # Expected pass # Wait for separate 'send' tasks to complete tasks = asyncio.all_tasks() await asyncio.wait(tasks, timeout=0.1) assert len(seq_num_app.receive_buffer) == 5 assert pipeline_with_messages.send.call_count == 1