def test_returns_one_conversation_given_conversations_over_span_of_days(): daily_start_datetime = datetime(year=2020, month=1, day=1, tzinfo=UTC) conversation_within_day = Gp2gpConversation( messages=test_cases.request_made( request_sent_date=datetime(year=2020, month=1, day=1, tzinfo=UTC) ), probe=mock_gp2gp_conversation_observability_probe, ) conversation_before_day = Gp2gpConversation( messages=test_cases.request_made( request_sent_date=datetime(year=2019, month=12, day=31, tzinfo=UTC) ), probe=mock_gp2gp_conversation_observability_probe, ) conversation_after_day = Gp2gpConversation( messages=test_cases.request_made( request_sent_date=datetime(year=2020, month=1, day=2, tzinfo=UTC) ), probe=mock_gp2gp_conversation_observability_probe, ) gp2gp_conversations = [ conversation_before_day, conversation_within_day, conversation_after_day, ] expected = [conversation_within_day] actual = filter_conversations_by_day(gp2gp_conversations, daily_start_datetime) assert list(actual) == expected
def record_negative_sla(self, conversation: Gp2gpConversation): self._logger.warning( f":Negative SLA duration for conversation: {conversation.conversation_id()}", extra={ "event": "NEGATIVE_SLA_DETECTED", "conversation_id": conversation.conversation_id(), "final_acknowledgement_time": conversation.effective_final_acknowledgement_time(), "request_completed_time": conversation.effective_request_completed_time(), }, )
def _calculate_sla( conversation: Gp2gpConversation, probe: TransferObservabilityProbe ) -> Optional[timedelta]: final_acknowledgement_time = conversation.effective_final_acknowledgement_time() request_completed_time = conversation.effective_request_completed_time() if final_acknowledgement_time is None: return None sla_duration = final_acknowledgement_time - request_completed_time if sla_duration.total_seconds() < 0: probe.record_negative_sla(conversation) return max(timedelta(0), sla_duration)
def test_warning_when_unable_to_determine_purpose_of_message(): mock_logger = Mock() mock_probe = Gp2gpConversationObservabilityProbe(mock_logger) unknown_message_purpose_message = build_message( conversation_id="ASD", guid="abc", interaction_id="urn:nhs:names:services:gp2gp/RCMR_IN010000UK08", ) messages = [ build_message( conversation_id="ASD", interaction_id=EHR_REQUEST_STARTED, ), unknown_message_purpose_message, ] Gp2gpConversation(messages=messages, probe=mock_probe) mock_logger.warning.assert_called_once_with( f":Couldn't determine purpose of message with guid: {unknown_message_purpose_message.guid}", extra={ "event": "UNKNOWN_MESSAGE_PURPOSE", "conversation_id": unknown_message_purpose_message.conversation_id, "interaction_id": unknown_message_purpose_message.interaction_id, }, )
def _copc_transfer_outcome( conversation: Gp2gpConversation, ) -> Tuple[TransferStatus, Optional[TransferFailureReason]]: if conversation.contains_unacknowledged_duplicate_ehr_and_copc_fragments(): return _unclassified_failure(TransferFailureReason.AMBIGUOUS_COPCS) elif conversation.contains_copc_error( ) and not conversation.is_missing_copc_ack(): return _unclassified_failure( TransferFailureReason.TRANSFERRED_NOT_INTEGRATED_WITH_ERROR) elif conversation.is_missing_copc(): return _technical_failure(TransferFailureReason.COPC_NOT_SENT) elif conversation.is_missing_copc_ack(): return _technical_failure(TransferFailureReason.COPC_NOT_ACKNOWLEDGED) else: return _process_failure( TransferFailureReason.TRANSFERRED_NOT_INTEGRATED)
def test_returns_true_given_only_duplicate_ehrs_present(): gp2gp_messages: List[Message] = test_cases.only_acknowledged_duplicates() conversation = Gp2gpConversation(gp2gp_messages, mock_gp2gp_conversation_observability_probe) actual = conversation.contains_only_duplicate_ehr() assert actual
def test_returns_false_given_an_unacknowledged_ehr_present(): gp2gp_messages: List[Message] = test_cases.acknowledged_duplicate_and_waiting_for_integration() conversation = Gp2gpConversation(gp2gp_messages, mock_gp2gp_conversation_observability_probe) actual = conversation.contains_only_duplicate_ehr() assert actual is False
def test_produces_last_sender_message_timestamp_from_request_completed_before_integration_only( ): request_completed_date = a_datetime(year=2020, month=6, day=1, hour=12, minute=42, second=0) messages = test_cases.ehr_integrated_with_duplicate_having_second_sender_ack_after_integration( request_completed_time=request_completed_date) conversation = Gp2gpConversation( messages=messages, probe=mock_gp2gp_conversation_observability_probe, ) mock_lookup = Mock() transfer_service = TransferService( message_stream=[], cutoff=timedelta(days=14), observability_probe=mock_transfer_observability_probe, ) actual = transfer_service.derive_transfer(conversation, mock_lookup) expected_last_sender_message_timestamp = request_completed_date assert actual.last_sender_message_timestamp == expected_last_sender_message_timestamp
def test_produces_sla_given_failure_with_conflicting_acks_and_duplicate_ehrs(): failed_acknowledgement_datetime = a_datetime(year=2020, month=6, day=1, hour=13, minute=52, second=0) conversation = Gp2gpConversation( messages=test_cases. integration_failed_with_conflicting_acks_and_duplicate_ehrs( request_completed_time=a_datetime(year=2020, month=6, day=1, hour=12, minute=42, second=0), ehr_acknowledge_time=failed_acknowledgement_datetime, ), probe=mock_gp2gp_conversation_observability_probe, ) mock_lookup = Mock() transfer_service = TransferService( message_stream=[], cutoff=timedelta(days=14), observability_probe=mock_transfer_observability_probe, ) actual = transfer_service.derive_transfer(conversation, mock_lookup) expected_sla_duration = timedelta(hours=1, minutes=10) assert actual.sla_duration == expected_sla_duration assert actual.date_completed == failed_acknowledgement_datetime
def test_produces_last_sender_message_timestamp_given_request_acked_successfully( ): request_acknowledged_date = a_datetime(year=2020, month=6, day=1, hour=12, minute=42, second=0) conversation = Gp2gpConversation( messages=test_cases.request_acknowledged_successfully( sender_ack_time=request_acknowledged_date), probe=mock_gp2gp_conversation_observability_probe, ) mock_lookup = Mock() transfer_service = TransferService( message_stream=[], cutoff=timedelta(days=14), observability_probe=mock_transfer_observability_probe, ) actual = transfer_service.derive_transfer(conversation, mock_lookup) expected = request_acknowledged_date assert actual.last_sender_message_timestamp == expected
def test_produces_last_sender_message_timestamp_given_copc_fragment_failure(): copc_fragment_time = a_datetime(year=2020, month=6, day=1, hour=12, minute=42, second=0) conversation = Gp2gpConversation( messages=test_cases.copc_fragment_failure( copc_fragment_time=copc_fragment_time), probe=mock_gp2gp_conversation_observability_probe, ) mock_lookup = Mock() transfer_service = TransferService( message_stream=[], cutoff=timedelta(days=14), observability_probe=mock_transfer_observability_probe, ) actual = transfer_service.derive_transfer(conversation, mock_lookup) expected_last_sender_message_timestamp = copc_fragment_time assert actual.last_sender_message_timestamp == expected_last_sender_message_timestamp
def test_produces_last_sender_message_timestamp_given_an_integrated_ontime_transfer_with_copcs( ): request_completed_date = a_datetime(year=2020, month=6, day=1, hour=12, minute=42, second=0) conversation = Gp2gpConversation( messages=test_cases.successful_integration_with_copc_fragments( request_completed_time=request_completed_date), probe=mock_gp2gp_conversation_observability_probe, ) mock_lookup = Mock() transfer_service = TransferService( message_stream=[], cutoff=timedelta(days=14), observability_probe=mock_transfer_observability_probe, ) actual = transfer_service.derive_transfer(conversation, mock_lookup) expected_last_sender_message_timestamp = request_completed_date assert actual.last_sender_message_timestamp == expected_last_sender_message_timestamp
def test_returns_true_given_unacknowledged_duplicate_ehr_and_copcs(): gp2gp_messages: List[ Message ] = test_cases.unacknowledged_duplicate_with_copcs_and_waiting_for_integration() conversation = Gp2gpConversation(gp2gp_messages, mock_gp2gp_conversation_observability_probe) actual = conversation.contains_unacknowledged_duplicate_ehr_and_copc_fragments() assert actual
def test_extracts_correct_codes_given_successful_transfer( test_case, expected_codes): gp2gp_messages: List[Message] = test_case() conversation = Gp2gpConversation( gp2gp_messages, mock_gp2gp_conversation_observability_probe) actual = conversation.final_error_codes() assert actual == expected_codes
def test_returns_transfer_status_process_failure_with_reason( test_case, expected_reason): gp2gp_messages: List[Message] = test_case() conversation = Gp2gpConversation( gp2gp_messages, probe=mock_gp2gp_conversation_observability_probe) actual = TransferOutcome.from_gp2gp_conversation(conversation, None) assert actual.status == TransferStatus.PROCESS_FAILURE assert actual.failure_reason == expected_reason
def test_extracts_sender_error_codes_when_sender_error(test_case): conversation = Gp2gpConversation( messages=test_case(error_code=10), probe=mock_gp2gp_conversation_observability_probe) actual = conversation.sender_error_codes() expected = [10] assert actual == expected
def test_returns_none_when_transfer_in_progress(test_case): gp2gp_messages: List[Message] = test_case() conversation = Gp2gpConversation( gp2gp_messages, mock_gp2gp_conversation_observability_probe) expected = None actual = conversation.effective_final_acknowledgement_time() assert actual == expected
def test_returns_transfer_status_integrated_on_time(test_case): gp2gp_messages: List[Message] = test_case() conversation = Gp2gpConversation( gp2gp_messages, mock_gp2gp_conversation_observability_probe) actual = TransferOutcome.from_gp2gp_conversation(conversation, timedelta(days=1)) assert actual.status == TransferStatus.INTEGRATED_ON_TIME assert actual.failure_reason is None
def test_extracts_error_codes_when_some_messages_unacknowledged(): conversation = Gp2gpConversation( messages=test_cases.copc_fragment_failure_and_missing_copc_fragment_ack(error_code=10), probe=mock_gp2gp_conversation_observability_probe, ) actual = conversation.intermediate_error_codes() expected_intermediate_error_codes = [10] assert actual == expected_intermediate_error_codes
def test_extracts_date_requested_from_request_started_message(): date_requested = a_datetime() gp2gp_messages: List[Message] = test_cases.request_made(request_sent_date=date_requested) conversation = Gp2gpConversation(gp2gp_messages, mock_gp2gp_conversation_observability_probe) actual = conversation.date_requested() assert actual == date_requested
def test_returns_false_when_transfer_concluded_with_failure(test_case): conversation = Gp2gpConversation( messages=test_case(), probe=mock_gp2gp_conversation_observability_probe ) expected = False actual = conversation.is_integrated() assert actual == expected
def test_returns_true_when_transfer_was_integrated(test_case): conversation = Gp2gpConversation( messages=test_case(), probe=mock_gp2gp_conversation_observability_probe ) expected = True actual = conversation.is_integrated() assert actual == expected
def test_returns_true_given_failed_transfer(test_case): conversation = Gp2gpConversation( messages=test_case(), probe=mock_gp2gp_conversation_observability_probe) expected = True actual = conversation.has_concluded_with_failure() assert actual == expected
def test_returns_false_given_intermediate_error(test_case): conversation = Gp2gpConversation( messages=test_case(), probe=mock_gp2gp_conversation_observability_probe) expected = False actual = conversation.has_concluded_with_failure() assert actual == expected
def test_doesnt_extract_sender_error_codes(test_case): gp2gp_messages: List[Message] = test_case() conversation = Gp2gpConversation( gp2gp_messages, mock_gp2gp_conversation_observability_probe) expected: List[int] = [] actual = conversation.final_error_codes() assert actual == expected
def test_observability_probe_called_when_missing_message_for_an_acknowledgement( ): mock_probe = Mock() messages = ehr_missing_message_for_an_acknowledgement() acknowledgement_for_missing_message = messages[1] Gp2gpConversation(messages=messages, probe=mock_probe) mock_probe.record_ehr_missing_message_for_an_acknowledgement.assert_called_once_with( acknowledgement_for_missing_message)
def test_returns_nothing_when_transfer_in_progress_and_no_errors(test_case): conversation = Gp2gpConversation( messages=test_cases.ehr_integration_failed(), probe=mock_gp2gp_conversation_observability_probe, ) actual = conversation.intermediate_error_codes() expected_intermediate_error_codes: List[int] = [] assert actual == expected_intermediate_error_codes
def test_ignores_ehr_acknowledgement_error_codes(): conversation = Gp2gpConversation( messages=test_cases.ehr_integration_failed(), probe=mock_gp2gp_conversation_observability_probe, ) actual = conversation.intermediate_error_codes() expected_intermediate_error_codes: List[int] = [] assert actual == expected_intermediate_error_codes
def test_ignores_sender_error_codes(): conversation = Gp2gpConversation( messages=test_cases.request_acknowledged_with_error(), probe=mock_gp2gp_conversation_observability_probe, ) actual = conversation.intermediate_error_codes() expected_intermediate_error_codes: List[int] = [] assert actual == expected_intermediate_error_codes
def test_extracts_multiple_intermediate_message_error_codes(): conversation = Gp2gpConversation( messages=test_cases.copc_fragment_failures(error_codes=[11, None, 10]), probe=mock_gp2gp_conversation_observability_probe, ) actual = conversation.intermediate_error_codes() expected_intermediate_error_codes = [11, 10] assert actual == expected_intermediate_error_codes