def test_convert_to_edifact(self): TEST_DIR = os.path.dirname(os.path.abspath(__file__)) expected_file_path = Path(TEST_DIR) / "edifact.txt" expected_edifact_interchange = FileUtilities.get_file_string(expected_file_path) incoming_file_path = Path(TEST_DIR) / "patient-register-birth.json" patient_register_json = FileUtilities.get_file_dict(incoming_file_path) outgoing_adaptor = OutgoingAdaptor(operation_dict) (sender, recipient, interchange_seq_no, edifact_interchange) = outgoing_adaptor.convert_to_edifact( patient_register_json) pretty_edifact_interchange = "'\n".join(edifact_interchange.split("'")) self.assertEqual(pretty_edifact_interchange, expected_edifact_interchange)
def test_payload_id_matches_manifest_id(self): with self.subTest("Incorrect manifest occurrences returns 500 error"): dictionary = { 'action': "urn:nhs-itk:services:201005:SendNHS111Report", 'service': "urn:nhs-itk:services:201005:SendNHS111Report", 'manifestCount': "2", 'manifests': [{"id": 'one'}, {'id': 'one'}], 'payloadCount': "2", 'payloads': [{"id": 'one'}, {'id': "two"}] } expected = FileUtilities.get_file_string( str(self.expectedXmlFileDir / 'payloadID_does_not_match_manifestID.xml')) msg = self.builder.build_message(dictionary) message_handler = MessageHandler(msg) self.assertEqual(message_handler.error_flag, True) XmlUtilities.assert_xml_equal_utf_8(expected, message_handler.get_response()) with self.subTest("Incorrect manifest occurrences returns 500 error"): dictionary = { 'action': "urn:nhs-itk:services:201005:SendNHS111Report", 'service': "urn:nhs-itk:services:201005:SendNHS111Report", 'manifestCount': "2", 'manifests': [{"id": 'one'}, {'id': "two"}], 'payloadCount': "2", 'payloads': [{"id": 'one'}, {'id': "two"}] } msg = self.builder.build_message(dictionary) message_handler = MessageHandler(msg) self.assertEqual(message_handler.error_flag, False) XmlUtilities.assert_xml_equal_utf_8(self.success_response, message_handler.get_response())
def test_manifest_payload_count(self): with self.subTest("Mismatched counts: 500 response"): counts = { 'action': "urn:nhs-itk:services:201005:SendNHS111Report", 'service': "urn:nhs-itk:services:201005:SendNHS111Report", 'manifestCount': "1", 'manifests': [{"id": 'one'}], 'payloadCount': "2", 'payloads': [{"id": 'one'}, {'id': "two"}] } expected = FileUtilities.get_file_string( str(self.expectedXmlFileDir / 'manifest_not_equal_to_payload_count.xml')) msg = self.builder.build_message(counts) message_handler = MessageHandler(msg) self.assertEqual(message_handler.error_flag, True) XmlUtilities.assert_xml_equal_utf_8(expected, message_handler.get_response()) with self.subTest("Equal counts: 200 response"): counts = { 'action': "urn:nhs-itk:services:201005:SendNHS111Report", 'service': "urn:nhs-itk:services:201005:SendNHS111Report", 'manifestCount': "2", 'manifests': [{"id": 'one'}, {"id": "two"}], 'payloadCount': "2", 'payloads': [{"id": 'one'}, {'id': "two"}] } msg = self.builder.build_message(counts) message_handler = MessageHandler(msg) self.assertEqual(message_handler.error_flag, False) XmlUtilities.assert_xml_equal_utf_8(self.success_response, message_handler.get_response())
def test_action_not_matching_service(self): with self.subTest("Two differing services result in a 500 error"): service_dict = {'action': "urn:nhs-itk:services:201005:SendNHS111Report-v2-0-ThisDoesNotMatchBelow", 'service': "urn:nhs-itk:services:201005:SendNHS111Report-Bad_Service-ThisDoesNotMatchAbove", 'manifestCount': 0, 'payloadCount': 0 } expected = FileUtilities.get_file_string( str(self.expectedXmlFileDir / 'invalid_action_service_values_response.xml')) msg = self.builder.build_message(service_dict) message_handler = MessageHandler(msg) self.assertEqual(message_handler.error_flag, True) XmlUtilities.assert_xml_equal_utf_8(expected, message_handler.get_response()) with self.subTest("Two services which are the same should return 200 code"): service_dict = {'action': "urn:nhs-itk:services:201005:SendNHS111Report", 'service': "urn:nhs-itk:services:201005:SendNHS111Report", 'manifestCount': 0, 'payloadCount': 0 } msg = self.builder.build_message(service_dict) message_handler = MessageHandler(msg) self.assertEqual(message_handler.error_flag, False) XmlUtilities.assert_xml_equal_utf_8(self.success_response, message_handler.get_response())
async def test_soap_error_request_is_non_retriable(self): self.setup_mock_work_description() self._setup_routing_mock() self.mock_ebxml_request_envelope.return_value.serialize.return_value = ( MESSAGE_ID, {}, SERIALIZED_MESSAGE) response = mock.MagicMock() response.code = 500 response.headers = {'Content-Type': 'text/xml'} # a non retriable soap 300 error code response.body = FileUtilities.get_file_string( Path(self.test_message_dir) / 'soapfault_response_single_error_300.xml') self.mock_transmission_adaptor.make_request.return_value = test_utilities.awaitable( response) with mock.patch( 'utilities.config.get_config', return_value='localhost/reliablemessaging/queryrequest'): status, message, _ = await self.workflow.handle_outbound_message( None, MESSAGE_ID, CORRELATION_ID, INTERACTION_DETAILS, PAYLOAD, None) self.mock_transmission_adaptor.make_request.assert_called_once()
async def test_retry_interval_contract_property_is_invalid(self): self.setup_mock_work_description() self._setup_routing_mock() self.mock_routing_reliability.get_reliability.return_value = test_utilities.awaitable( { workflow.common_asynchronous.MHS_RETRY_INTERVAL: MHS_RETRY_INTERVAL_INVALID_VAL, workflow.common_asynchronous.MHS_RETRIES: MHS_RETRY_VAL }) self.mock_ebxml_request_envelope.return_value.serialize.return_value = ( MESSAGE_ID, {}, SERIALIZED_MESSAGE) message = FileUtilities.get_file_string( Path(self.test_message_dir) / 'soapfault_response_single_error.xml') response = httpclient.HTTPResponse response.code = 500 response.body = message response.headers = {'Content-Type': 'text/xml'} self.mock_transmission_adaptor.make_request.return_value = test_utilities.awaitable( response) status, message, _ = await self.workflow.handle_outbound_message( None, MESSAGE_ID, CORRELATION_ID, INTERACTION_DETAILS, PAYLOAD, None) self.assertEqual(500, status) self.assertTrue('Error when converting retry interval' in message) self.assertEqual( [mock.call(MessageStatus.OUTBOUND_MESSAGE_PREPARATION_FAILED)], self.mock_work_description.set_outbound_status.call_args_list)
def test_parse_message(self): with self.subTest("A valid request containing a payload"): message = FileUtilities.get_file_string( str(message_dir / "ebxml_request.msg")) expected_values_with_payload = expected_values( message=EXPECTED_MESSAGE) extracted_values = self.parser.parse_message( MULTIPART_MIME_HEADERS, message) self.assertEqual(expected_values_with_payload, extracted_values) with self.subTest("An invalid multi-part MIME message"): message = FileUtilities.get_file_string( str(message_dir / "ebxml_request_no_header.msg")) with (self.assertRaises(EbXmlParsingError)): self.parser.parse_message(MULTIPART_MIME_HEADERS, message) with self.subTest( "A valid request that does not contain the optional payload MIME part" ): message = FileUtilities.get_file_string( str(message_dir / "ebxml_request_no_payload.msg")) expected_values_with_no_payload = expected_values() extracted_values = self.parser.parse_message( MULTIPART_MIME_HEADERS, message) self.assertEqual(expected_values_with_no_payload, extracted_values) with self.subTest( "A valid request containing an additional MIME part"): message = FileUtilities.get_file_string( str(message_dir / "ebxml_request_additional_attachment.msg")) expected_values_with_payload = expected_values( message=EXPECTED_MESSAGE) extracted_values = self.parser.parse_message( MULTIPART_MIME_HEADERS, message) self.assertEqual(expected_values_with_payload, extracted_values) with self.subTest("An message that is not a multi-part MIME message"): with (self.assertRaises(EbXmlParsingError)): self.parser.parse_message( {CONTENT_TYPE_HEADER_NAME: "text/plain"}, "A message")
def test_covert_to_fhir(self): """ Test when the operation is for a birth registration """ TEST_DIR = os.path.dirname(os.path.abspath(__file__)) expected_file_path = Path(TEST_DIR) / "patient-register-birth-approval.json" patient_register_approval_json = FileUtilities.get_file_dict(expected_file_path) incoming_file_path = Path(TEST_DIR) / "edifact.txt" incoming_interchange_raw = FileUtilities.get_file_string(incoming_file_path) incoming_adaptor = IncomingAdaptor(reference_dict) op_defs = incoming_adaptor.convert_to_fhir(incoming_interchange_raw) op_def_to_compare = op_defs[0][2] compare(op_def_to_compare, patient_register_approval_json)
def test_parse_message_with_no_values(self): message = FileUtilities.get_file_string( str(message_dir / "ebxml_header_empty.xml")) extracted_values = self.parser.parse_message(MULTIPART_MIME_HEADERS, message) self.assertEqual({}, extracted_values)
def test_multiple_errors(self): message = FileUtilities.get_file_string( self.message_dir / 'ebxml_response_error_multiple.xml') response = handle_ebxml_error(200, {'Content-Type': 'text/xml'}, message)[1] self.assertIn('501319:Unknown eb:CPAId', response) self.assertIn('501320:Unknown something else', response) self.assertIn('errorType=ebxml_error', response)
def test_parse_message(self): message = FileUtilities.get_file_string( str(message_dir / "ebxml_header.xml")) expected_values_without_message = expected_values() extracted_values = self.parser.parse_message(MULTIPART_MIME_HEADERS, message) self.assertEqual(expected_values_without_message, extracted_values)
def test_should_return_error_when_required_details_tag_is_missing(self): input_xml = FileUtilities.get_file_string( str(self.xmlFileDir / 'missingAttribute.xml')) parsed_data = self.gp_summary_upload_templator.parse_response( input_xml) self.assertEqual( parsed_data['error'], 'Failed to parse all the necessary elements from xml returned from MHS' )
def test_should_return_error_when_there_is_a_bad_attribute_format(self): input_xml = FileUtilities.get_file_string( str(self.xmlFileDir / 'badAttributeParse.xml')) parsed_data = self.gp_summary_upload_templator.parse_response( input_xml) self.assertEqual( parsed_data['error'], 'Failed to parse all the necessary elements from xml returned from MHS' )
def test_python_dictionary_example(self): """ Basic test to demonstrate passing a python dict to the interface instead of a json file """ expected_string = FileUtilities.get_file_string( str(Path(ROOT_DIR) / 'scr/tests/test_xmls/cleanSummaryUpdate.xml')) from scr.tests.hashes.basic_dict import input_hash render = self.summaryCareRecord.populate_template(input_hash) XmlUtilities.assert_xml_equal(expected_string, render)
def test_post(self, mock_get_uuid, mock_get_timestamp): mock_get_uuid.return_value = "5BB171D4-53B2-4986-90CF-428BE6D157F5" mock_get_timestamp.return_value = "2012-03-15T06:51:08Z" expected_ack_response = FileUtilities.get_file_string( str(self.message_dir / EXPECTED_RESPONSE_FILE)) request_body = FileUtilities.get_file_string( str(self.message_dir / REQUEST_FILE)) mock_callback = Mock() self.callbacks[REF_TO_MESSAGE_ID] = mock_callback ack_response = self.fetch("/", method="POST", body=request_body, headers=CONTENT_TYPE_HEADERS) self.assertEqual(ack_response.code, 200) self.assertEqual(ack_response.headers["Content-Type"], "text/xml") XmlUtilities.assert_xml_equal(expected_ack_response, ack_response.body) mock_callback.assert_called_with(EXPECTED_MESSAGE)
def test_empty_hash(self): """ Tests the contents are empty when a completely blank hash is provided """ expected_xml_file_path = str(self.xmlFileDir / 'EmptyHash.xml') hash_file_path = str(self.hashFileDir / 'EmptyHash.json') expected_string = FileUtilities.get_file_string(expected_xml_file_path) render = self.summaryCareRecord.populate_template_with_file( hash_file_path) XmlUtilities.assert_xml_equal(expected_string, render)
def test_empty_html(self): """ A test for an empty human readable content value """ expected_xml_file_path = str(self.xmlFileDir / 'EmptyHtmlGpSummaryUpdate.xml') hash_file_path = str(self.hashFileDir / 'emptyHtmlHash.json') expected_string = FileUtilities.get_file_string(expected_xml_file_path) render = self.summaryCareRecord.populate_template_with_file( hash_file_path) XmlUtilities.assert_xml_equal(expected_string, render)
def test_extended_html(self): """ Uses a larger set of Html for the human readable contents """ expected_xml_file_path = str(self.xmlFileDir / 'SummaryUpdateExtendedContents.xml') hash_file_path = str(self.hashFileDir / 'extendedHTMLhash.json') expected_string = FileUtilities.get_file_string(expected_xml_file_path) render = self.summaryCareRecord.populate_template_with_file( hash_file_path) XmlUtilities.assert_xml_equal(expected_string, render)
def test_basic(self): """ A basic test using the clean summary update from the spine tests """ expected_xml_file_path = str(self.xmlFileDir / 'cleanSummaryUpdate.xml') hash_file_path = str(self.hashFileDir / 'hash16UK05.json') expected_string = FileUtilities.get_file_string(expected_xml_file_path) render = self.summaryCareRecord.populate_template_with_file( hash_file_path) XmlUtilities.assert_xml_equal(expected_string, render)
def test_json_string_example(self): """ Basic example showing how a json string can be passed to the interface """ expected_string = FileUtilities.get_file_string( str(self.xmlFileDir / 'cleanSummaryUpdate.xml')) json_file = str(self.hashFileDir / 'hash16UK05.json') with open(json_file) as file: data = file.read() # Reads file contents into a string render = self.summaryCareRecord.populate_template_with_json_string( data) XmlUtilities.assert_xml_equal(expected_string, render)
def test_post_no_callback(self): # If there is no callback registered for the message ID the response is in reference to, an HTTP 500 should be # returned. request_body = FileUtilities.get_file_string( str(self.message_dir / REQUEST_FILE)) response = self.fetch("/", method="POST", body=request_body, headers=CONTENT_TYPE_HEADERS) self.assertEqual(response.code, 500)
def test_build_message(self, mock_get_uuid, mock_get_timestamp): mock_get_uuid.return_value = MOCK_UUID mock_get_timestamp.return_value = "2012-03-15T06:51:08Z" expected_message = FileUtilities.get_file_string( str(self.expected_message_dir / EXPECTED_EBXML)) message_id, message = self.builder.build_message({ FROM_PARTY_ID: "TESTGEN-201324", TO_PARTY_ID: "YEA-0000806", CPA_ID: "S1001A1630", CONVERSATION_ID: "79F49A34-9798-404C-AEC4-FD38DD81C138", SERVICE: "urn:nhs:names:services:pdsquery", ACTION: "QUPA_IN000006UK02", DUPLICATE_ELIMINATION: True, ACK_REQUESTED: True, ACK_SOAP_ACTOR: "urn:oasis:names:tc:ebxml-msg:actor:toPartyMSH", SYNC_REPLY: True, MESSAGE: '<QUPA_IN000006UK02 xmlns="urn:hl7-org:v3"></QUPA_IN000006UK02>' }) # Pystache does not convert line endings to LF in the same way as Python does when loading the example from # file, so normalize the line endings of both strings normalized_expected_message = FileUtilities.normalize_line_endings( expected_message) normalized_message = FileUtilities.normalize_line_endings(message) self.assertEqual(MOCK_UUID, message_id) self.assertEqual(normalized_expected_message, normalized_message)
def test_replacementOf(self): """ Note: this is not a valid xml instance This is to demonstrate the condition aspect of the replacementOf partial, this partial doesnt appear in the previous tests but here a list with a single element is used to show how conditionals are used in mustache """ expected_xml_file_path = str(self.xmlFileDir / 'replacementOf.xml') hash_file_path = str(self.hashFileDir / 'replacementOfhash.json') expected_string = FileUtilities.get_file_string(expected_xml_file_path) render = self.summaryCareRecord.populate_template_with_file( hash_file_path) XmlUtilities.assert_xml_equal(expected_string, render)
def get_asid(): """ Looks up the asid from the environment settings The asid should be set in the 'Environment variables' section of the Run/Debug Configurations if this is not set, it will read from 'asid.txt' (excluded from the repo) or default to '123456789012' if 'asid.txt' is not found """ try: asid_file = str(Path(ROOT_DIR) / "data/certs/asid.txt") asid = FileUtilities.get_file_string(asid_file) except: asid = None return os.environ.get('INTEGRATION_TEST_ASID', asid)
def test_multipleReplacementOf(self): """ Note: THIS IS NOT A VALID XML INSTANCE This test is build purely for demonstrating having a variable number of occurrences of a partial in mustache, it does not conform to the schema and will not be accepted as a valid message """ expected_xml_file_path = str(self.xmlFileDir / 'multipleReplacementOf.xml') hash_file_path = str(self.hashFileDir / 'multiReplacementOfhash.json') expected_string = FileUtilities.get_file_string(expected_xml_file_path) render = self.summaryCareRecord.populate_template_with_file( hash_file_path) XmlUtilities.assert_xml_equal(expected_string, render)
def test_normalize_line_endings(self): strings_to_test = { "CRLF": CR_LF_STRING, "CR": CR_STRING, "LF": LF_STRING, "Mixed": MIXED_STRING } for line_break_type, test_string in strings_to_test.items(): message = line_break_type + " line endings should be normalized." with self.subTest(message): normalized_string = FileUtilities.normalize_line_endings(test_string) self.assertEqual(EXPECTED_NORMALIZED_STRING, normalized_string)
def test_from_string_single(self): message = FileUtilities.get_file_string( Path(self.message_dir) / 'soapfault_response_single_error.xml') fault: SOAPFault = SOAPFault.from_string({}, message) self.assertEqual(fault.fault_code, 'SOAP:Server') self.assertEqual(fault.fault_string, 'Application Exception') self.assertEqual(len(fault.error_list), 1) self.assertEqual(fault.error_list[0]['codeContext'], 'urn:nhs:names:error:tms') self.assertEqual(fault.error_list[0]['errorCode'], '200') self.assertEqual(fault.error_list[0]['severity'], 'Error') self.assertEqual(fault.error_list[0]['location'], 'Not Supported') self.assertEqual(fault.error_list[0]['description'], 'System failure to process message - default')
def test_should_return_success_response_with_valid_json(self): """ Simple assertion of correct values returned from the response parsing method """ input_xml = FileUtilities.get_file_string( str(self.xmlFileDir / 'parseSuccessResponse.xml')) parsed_data = self.gp_summary_upload_templator.parse_response( input_xml) self.assertEqual(parsed_data['messageRef'], '9C534C19-C587-4463-9AED-B76F715D3EA3') self.assertEqual(parsed_data['messageId'], '2E372546-229A-483F-9B11-EF46ABF3178C') self.assertEqual(parsed_data['creationTime'], '20190923112609') self.assertEqual(parsed_data['messageDetail'], 'GP Summary upload successful')
class DataframeUtilitiesProject: """ Returns dataframes either normally indexed or column indexed""" FILE_PATH = FileUtilities.get_abs_path( "../../Course_Materials_Part2/Video_Lecture_NBs/") # noqa: E501 @classmethod def get_dataframe(cls, csv_file): """ Returns a normally indexed dataframe """ source_file = cls.FILE_PATH + csv_file return pd.read_csv(source_file) @classmethod def get_indexed_dataframe(cls, csv_file, index_column): """ Returns a dataframe indexed on the specified index_column """ source_file = cls.FILE_PATH + csv_file return pd.read_csv(source_file, index_col=index_column)
class Check(ABC): soap_body = "./soap:Body" distribution_envelope = soap_body + "/itk:DistributionEnvelope" manifest_tag = distribution_envelope + "/itk:header/itk:manifest" basic_success_message = FileUtilities.get_file_string( str(XML_PATH / 'basic_success_response.xml')) namespaces = { 'soap': 'http://schemas.xmlsoap.org/soap/envelope/', 'a': 'http://www.etis.fskab.se/v1.0/ETISws', 'wsa': 'http://www.w3.org/2005/08/addressing', 'itk': 'urn:nhs-itk:ns:201005' } def __init__(self, message): self.message_tree = message @abstractmethod def check(self): """ An abstract method called by the validator to run the check :return: fail flag, error message """ pass def get_manifest_count(self): """ Extracts the count on the manifest tag in the message :return: manifest count as a string """ return self.message_tree.find(self.manifest_tag, self.namespaces).attrib['count'] def get_payload_count(self): """ Extracts the count on the payloads tag in the message :return: payloads count as a string """ return self.message_tree.find( self.distribution_envelope + "/itk:payloads", self.namespaces).attrib['count'] def build_error_message(self, error): builder = PystacheMessageBuilder(str(TEMPLATE_PATH), 'base_error_template') return builder.build_message({"errorMessage": error})