class MetadataManager: METADATA_URL = "/redfish/v1/$metadata" SERVICE_TO_DIR = { ServiceTypes.PODM_1_2: "1.2/PODM", ServiceTypes.PSME_1_2: "1.2/PSME", ServiceTypes.RMM_1_2: "1.2/RMM", ServiceTypes.SS_1_2: "1.2/SS", ServiceTypes.PODM_2_1: "2.1.3/PODM", ServiceTypes.PSME_2_1: "2.1.3/PSME", ServiceTypes.RMM_2_1: "2.1.3/RMM", ServiceTypes.SS_2_1: "2.1.3/SS", ServiceTypes.PODM_2_1_2: "2.1.2/PODM", ServiceTypes.PSME_2_1_2: "2.1.2/PSME", ServiceTypes.RMM_2_1_2: "2.1.2/RMM", ServiceTypes.SS_2_1_2: "2.1.2/SS", ServiceTypes.PODM_2_1_3: "2.1.3/PODM", ServiceTypes.PSME_2_1_3: "2.1.3/PSME", ServiceTypes.RMM_2_1_3: "2.1.3/RMM", ServiceTypes.SS_2_1_3: "2.1.3/SS", ServiceTypes.PODM_2_1_4: "2.1.4/PODM", ServiceTypes.PSME_2_1_4: "2.1.4/PSME", ServiceTypes.RMM_2_1_4: "2.1.4/RMM", ServiceTypes.SS_2_1_4: "2.1.4/SS", ServiceTypes.PODM_2_2: "2.2/PODM", ServiceTypes.PSME_2_2: "2.2/PSME", ServiceTypes.RMM_2_2: "2.2/RMM", ServiceTypes.SS_2_2: "2.2/SS", ServiceTypes.PODM_2_3: "2.3/PODM", ServiceTypes.PSME_2_3: "2.3/PSME", ServiceTypes.RMM_2_3: "2.3/RMM", ServiceTypes.SS_2_3: "2.3/SS", ServiceTypes.PODM_2_4: "2.4/PODM", ServiceTypes.PSME_2_4: "2.4/PSME", ServiceTypes.RMM_2_4: "2.4/RMM", ServiceTypes.SS_2_4: "2.4/SS", ServiceTypes.SCENARIO_2_4: "2.4/SCENARIO", ServiceTypes.RACKSCALE_2_4: "2.4/RACKSCALE", ServiceTypes.REDFISH_2018_1: "redfish/2018.1", ServiceTypes.REDFISH_2018_2: "redfish/2018.2", ServiceTypes.REDFISH_2018_3: "redfish/2018.3" } def __init__(self, qualifiers, ignore_types=None, map_types=None): self.qualifiers = qualifiers self.ignore_types = ignore_types if ignore_types is not None else set() self.map_types = map_types if map_types is not None else dict() self._metadata_container = MetadataContainer(ignore_types=self.ignore_types, map_types=self.map_types) self.loaded_xmls = set() self.api_caller = None def read_metadata_for_services(self, *services): """ :type services: list[string] """ digest = DirDigest(self._metadata_home(), '.xml', DirDigest.LABEL_METADATA) if not digest.is_valid() and not getenv('CTS_SKIP', None): cts_error("Metadata located in {dir} is corrupted or has been tampered. Expected: {expected}, " "Is: {current}", dir=self._metadata_home(), expected=digest.official_digest, current=digest.digest) cts_message("{count} xml files have been found in {dir}".format(count=len(digest), dir=self._metadata_home())) digest.report_differences() for service in services: try: self.metadata_dir = os.path.join(self._metadata_home(), MetadataManager.SERVICE_TO_DIR[service]) self.read_metadata_from_dir(self.metadata_dir) except KeyError: cts_error("Internal error. Unknown metadata for service {service}", service=service) return False return True def read_metadata_from_dir(self, dir, root_file=None): if root_file is None: root_file = ROOT_XML_FILE if os.path.isdir(dir): self.metadata_dir = dir self._process_schema_file(root_file) return True else: cts_error("Internal error. Metadata directory {metadata_dir} not found", metadata_dir=dir) return False def _validate_xml_file(self, file_uri, metadata_text): try: doc = etree.fromstring(metadata_text.lstrip()) except etree.XMLSyntaxError as err: cts_error('XML Syntax Error in file {file}', file=file_uri) raise MetadataMalformed() def read_metadata_from_strings(self, file_uri, *metadata): """ :type file_uri: string :type metadata: list[string] :rtype: cts_core.metadata.metadata_container.MetadataContainer """ for metadata_text in metadata: self._validate_xml_file(file_uri, metadata_text) metadata_soup = Commons.text_to_soup(metadata_text) for reference in metadata_soup.find_all(REFERENCE): self._process_reference(reference) for schema in metadata_soup.find_all(SCHEMA): self._process_namespace(schema) return self.metadata_container def download_metadata(self, configuration): self.api_caller = ApiCaller(configuration) self._process_schema_file(MetadataManager.METADATA_URL) return True @staticmethod def _metadata_home(): return Constants.METADATA_HOME_DIR def _process_reference(self, reference_soup): try: self._process_schema_file(reference_soup[URI]) except KeyError as uri: cts_error("Incorrect reference. URI not found") @property def metadata_container(self): return self._metadata_container def _process_schema_file(self, file_uri): if file_uri in self.loaded_xmls: return self.loaded_xmls.add(file_uri) if self.api_caller is None: self._process_local_schema_file(file_uri) else: self._process_remote_schema_file(file_uri) def _process_local_schema_file(self, file_uri): try: full_uri = os.path.join(self.metadata_dir, file_uri) with open(full_uri) as f: self.read_metadata_from_strings(file_uri, f.read()) except IOError: cts_error("Metadata file {file} not found", file=full_uri) def _process_remote_schema_file(self, file_uri): _, status, _, response_body, _ = self.api_caller.get_xml(file_uri) if not status: cts_error("Error while accessing metadata {uri}", uri=file_uri) else: self.read_metadata_from_strings(file_uri, response_body) def _process_namespace(self, schema): """ :type schema: bs4.element.Tag """ try: namespace_name = schema[NAMESPACE] except KeyError: cts_warning( "Incorrect schema definition {schema_definition},\n missing of namespace name", schema_definition=str(schema)) for entity_soup in schema.find_all(ENTITY_TYPE): entity = Entity(self._metadata_container, namespace_name, entity_soup, self.qualifiers) self._metadata_container.entities[entity.name] = entity for type_soup in schema.find_all(ENUM_TYPE): enum_type = EnumType(self._metadata_container, namespace_name, type_soup, self.qualifiers) self._metadata_container.types[enum_type.name] = enum_type for type_soup in schema.find_all(COMPLEX_TYPE): complex_type = ComplexType(self._metadata_container, namespace_name, type_soup, self.qualifiers) self._metadata_container.types[complex_type.name] = complex_type for type_soup in schema.find_all(TYPE_DEFINITION): type_definition = TypeDefinition(self._metadata_container, namespace_name, type_soup, self.qualifiers) self._metadata_container.types[type_definition.name] = type_definition
class ApiCallerCallsUnitTest(unittest.TestCase): def setUp(self): configuration = Configuration(**dict(ApiEndpoint=API_ENDPOINT)) self.api_caller = ApiCaller(configuration) def test_get_on_empty_resource(self): with mock.patch('requests.get') as requests_get_mock: response = Mock() response.status_code = 200 response.headers = {} response.text = "" requests_get_mock.return_value = response link, status, status_code, response_body, headers = \ self.api_caller.get_resource("/resource", DiscoveryContainer()) self.assertFalse(status) def test_incorrect_status_code(self): with mock.patch('requests.get') as requests_get_mock: response = Mock() response.status_code = 500 response.headers = {} response.text = "{}" requests_get_mock.return_value = response link, status, status_code, response_body, headers = \ self.api_caller.get_resource("/resource", DiscoveryContainer()) self.assertFalse(status) def test_request_exception(self): with mock.patch('requests.get') as requests_get_mock: requests_get_mock.side_effect = requests.RequestException() with StdoutCapture() as output: self.api_caller.get_resource("/resource", DiscoveryContainer()) self.assertIn("ERROR::Get url=http://1.2.3.4:567/resource Error <class 'requests.exceptions.RequestException'>:;", output.raw) def test_connection_error_exception(self): with mock.patch('requests.get') as requests_get_mock: requests_get_mock.side_effect = requests.ConnectionError() with StdoutCapture() as output: self.api_caller.get_resource("/resource", DiscoveryContainer()) self.assertIn("ERROR::Get url=http://1.2.3.4:567/resource Error <class 'requests.exceptions.ConnectionError'>:;", output.raw) def test_http_error_exception(self): with mock.patch('requests.get') as requests_get_mock: requests_get_mock.side_effect = requests.HTTPError() with StdoutCapture() as output: self.api_caller.get_resource("/resource", DiscoveryContainer()) self.assertIn("ERROR::Get url=http://1.2.3.4:567/resource Error <class 'requests.exceptions.HTTPError'>:", output.raw) def test_url_required_exception(self): with mock.patch('requests.get') as requests_get_mock: requests_get_mock.side_effect = requests.URLRequired() with StdoutCapture() as output: self.api_caller.get_resource("/resource", DiscoveryContainer()) self.assertIn("ERROR::Get url=http://1.2.3.4:567/resource Error <class " "'requests.exceptions.URLRequired'>:", output.raw) def test_to_many_redirects_exception(self): with mock.patch('requests.get') as requests_get_mock: requests_get_mock.side_effect = requests.TooManyRedirects() with StdoutCapture() as output: self.api_caller.get_resource("/resource", DiscoveryContainer()) self.assertIn("ERROR::Get url=http://1.2.3.4:567/resource Error <class 'requests.exceptions.TooManyRedirects'>:;", output.raw) def test_timeout_exception(self): with mock.patch('requests.get') as requests_get_mock: requests_get_mock.side_effect = requests.Timeout() with StdoutCapture() as output: self.api_caller.get_resource("/resource", DiscoveryContainer()) self.assertIn("ERROR::Get url=http://1.2.3.4:567/resource Error <class 'requests.exceptions.Timeout'>:;", output.raw) def test_incorrect_body(self): with mock.patch('requests.get') as requests_get_mock: response = Mock() response.status_code = 200 response.headers = {} response.text = "not a json" requests_get_mock.return_value = response with StdoutCapture() as output: self.api_caller.get_resource("/resource", DiscoveryContainer()) self.assertIn( "ERROR", output.raw) def test_no_content(self): with mock.patch('requests.get') as requests_get_mock: response = Mock() response.status_code = 204 response.headers = {} response.text = None requests_get_mock.return_value = response link, status, status_code, response_body, headers = \ self.api_caller.get_resource("/resource", DiscoveryContainer(), acceptable_return_codes = [204]) self.assertTrue(status) self.assertEqual(status_code, 204) self.assertEqual(response_body, dict()) response.json.assert_not_called() def test_async_create_without_location(self): with mock.patch('requests.post') as requests_post_mock: response = Mock() response.status_code = ReturnCodes.ACCEPTED response.headers = {} response.text = None requests_post_mock.return_value = response with mock.patch('requests.get') as requests_get_mock: response = Mock() response.status_code = 204 response.headers = {} response.text = None requests_get_mock.return_value = response with StdoutCapture() as output: status, status_code, response_body, headers = self.api_caller.post_resource('odata_id', DiscoveryContainer(), payload={}) self.assertIn('Location header not found', '\n'.join(output)) def test_unexpected_async_post_response(self): with mock.patch('requests.post') as requests_get_mock: response = Mock() response.status_code = ReturnCodes.ACCEPTED response.headers = {} response.text = "{}" requests_get_mock.return_value = response with self.assertRaises(AsyncOperation): self.api_caller.post_resource('odata_id', DiscoveryContainer(), payload={}, wait_if_async=False) def test_async_post_response(self): with mock.patch('requests.post') as requests_post_mock: not_done = Mock() not_done.status_code = ReturnCodes.ACCEPTED not_done.headers = {'Location' : 'location'} not_done.text = None requests_post_mock.return_value = not_done with mock.patch('requests.get') as requests_get_mock: done = Mock() done.status_code = ReturnCodes.OK done.headers = {'Location' : 'location'} done.text = "{ \"done\": true }" requests_get_mock.side_effect = [not_done, not_done, done] with StdoutCapture() as output: status, status_code, response_body, headers = self.api_caller.post_resource('odata_id', DiscoveryContainer(), payload={}) self.assertTrue(response_body['done']) def test_resource_in_discovery_container_after_get_patch_delete(self): with mock.patch('requests.get') as requests_get_mock: resource = {"@odata.id": "odata.id", "something": "irrelevant"} get_response = Mock() get_response.status_code = ReturnCodes.OK get_response.headers = {} get_response.text = json.dumps(resource) requests_get_mock.return_value = get_response discovery_container = DiscoveryContainer() self.api_caller.get_resource("/resource", discovery_container) self.assertEqual(discovery_container["http://{API_ENDPOINT}/resource".format( API_ENDPOINT=API_ENDPOINT)].body, resource) patched_resource = {"@odata.id": "odata.id", "something": "relevant"} get_response.text = json.dumps(patched_resource) with mock.patch('requests.patch') as requests_patch_mock: patch_response = Mock() patch_response.status_code = ReturnCodes.OK patch_response.headers = {} patch_response.text = "{}" requests_patch_mock.return_value = patch_response _, _, _, _ = self.api_caller.patch_resource("/resource", discovery_container) self.assertEqual(discovery_container["http://{API_ENDPOINT}/resource".format( API_ENDPOINT=API_ENDPOINT)].body, patched_resource) with mock.patch('requests.delete') as requests_delete_mock: delete_response = Mock() delete_response.status_code = ReturnCodes.NO_CONTENT delete_response.headers = {} delete_response.text = "" requests_delete_mock.return_value = delete_response _, _, _, _ = self.api_caller.delete_resource("/resource", discovery_container) self.assertNotIn("/resource", discovery_container) def test_get_xml(self): with mock.patch('requests.get') as get: response = Mock() response.status_code = ReturnCodes.OK response.headers = {} response.text = "<xml></xml>" get.return_value = response link, status, status_code, response_body, headers = self.api_caller.get_xml("uri") self.assertEqual("<xml></xml>", response_body)
class ApiCallerCallsUnitTest(unittest.TestCase): def setUp(self): configuration = Configuration(**dict(ApiEndpoint=API_ENDPOINT)) self.api_caller = ApiCaller(configuration) @unittest.skip("Temporary change related to bug in Discovery mechanism") def test_get_on_empty_resource(self): with mock.patch('requests.get') as requests_get_mock: response = Mock() response.status_code = 200 response.headers = {} response.text = "" requests_get_mock.return_value = response with StdoutCapture() as output: self.api_caller.get_resource("/resource", DiscoveryContainer()) self.assertIn( "ERROR::url=/resource Get failed. Status code: None;", output.raw) def test_empty_service_root(self): with mock.patch('requests.get') as requests_get_mock: requests_get_mock.side_effect = requests.ConnectionError() with StdoutCapture() as output: self.api_caller.get_resource("/redfish/v1", DiscoveryContainer()) self.assertIn( "ERROR::Get url=http://1.2.3.4:567/redfish/v1 Error <class 'requests.exceptions.ConnectionError'>:;", output.raw) @unittest.skip("Temporary change related to bug in Discovery mechanism") def test_incorrect_status_code(self): with mock.patch('requests.get') as requests_get_mock: response = Mock() response.status_code = 500 response.headers = {} response.text = "{}" requests_get_mock.return_value = response with StdoutCapture() as output: self.api_caller.get_resource("/resource", DiscoveryContainer()) self.assertIn("ERROR::url=/resource Get failed. Status code: 500;", output.raw) def test_request_exception(self): with mock.patch('requests.get') as requests_get_mock: requests_get_mock.side_effect = requests.RequestException() with StdoutCapture() as output: self.api_caller.get_resource("/resource", DiscoveryContainer()) self.assertIn( "ERROR::Get url=http://1.2.3.4:567/resource Error <class 'requests.exceptions.RequestException'>:;", output.raw) def test_connection_error_exception(self): with mock.patch('requests.get') as requests_get_mock: requests_get_mock.side_effect = requests.ConnectionError() with StdoutCapture() as output: self.api_caller.get_resource("/resource", DiscoveryContainer()) self.assertIn( "ERROR::Get url=http://1.2.3.4:567/resource Error <class 'requests.exceptions.ConnectionError'>:;", output.raw) def test_http_error_exception(self): with mock.patch('requests.get') as requests_get_mock: requests_get_mock.side_effect = requests.HTTPError() with StdoutCapture() as output: self.api_caller.get_resource("/resource", DiscoveryContainer()) self.assertIn( "ERROR::Get url=http://1.2.3.4:567/resource Error <class 'requests.exceptions.HTTPError'>:", output.raw) def test_url_required_exception(self): with mock.patch('requests.get') as requests_get_mock: requests_get_mock.side_effect = requests.URLRequired() with StdoutCapture() as output: self.api_caller.get_resource("/resource", DiscoveryContainer()) self.assertIn( "ERROR::Get url=http://1.2.3.4:567/resource Error <class " "'requests.exceptions.URLRequired'>:", output.raw) def test_to_many_redirects_exception(self): with mock.patch('requests.get') as requests_get_mock: requests_get_mock.side_effect = requests.TooManyRedirects() with StdoutCapture() as output: self.api_caller.get_resource("/resource", DiscoveryContainer()) self.assertIn( "ERROR::Get url=http://1.2.3.4:567/resource Error <class 'requests.exceptions.TooManyRedirects'>:;", output.raw) def test_timeout_exception(self): with mock.patch('requests.get') as requests_get_mock: requests_get_mock.side_effect = requests.Timeout() with StdoutCapture() as output: self.api_caller.get_resource("/resource", DiscoveryContainer()) self.assertIn( "ERROR::Get url=http://1.2.3.4:567/resource Error <class 'requests.exceptions.Timeout'>:;", output.raw) def test_incorrect_body(self): with mock.patch('requests.get') as requests_get_mock: response = Mock() response.status_code = 200 response.headers = {} response.text = "not a json" requests_get_mock.return_value = response with StdoutCapture() as output: self.api_caller.get_resource("/resource", DiscoveryContainer()) self.assertIn("ERROR", output.raw) def test_parse_big_ordered_tree_response(self): from collections import OrderedDict response_payload = OrderedDict( [(u'@odata.context', u'/redfish/v1/$metadata#ServiceRoot.ServiceRoot'), (u'@odata.etag', u'W/"1557488360"'), (u'@odata.id', \ u'/redfish/v1/'), (u'@odata.type', u'#ServiceRoot.v1_3_1.ServiceRoot'), (u'AccountService', OrderedDict([(u'@odata.id', u'/redfish/v1/AccountService')])), (u'Chassis', OrderedDict([(u'@odata.id', u'/redfish/v1/Chassis')])), (u'Description', u'The service root \ for all Redfish requests on this host \ ' ), (u'EventService', OrderedDict([(u' @ odata.id', u' / redfish / v1 / EventService')])), (u'Id', u'RootService'), ( u'Links', OrderedDict( [(u'Oem', OrderedDict([(u'Ami', OrderedDict([(u'@odata.id', u'/redfish/v1/configurations')]))])), (u'Sessions', OrderedDict([(u'@odata.id', u'/redfish/v1/SessionService/Sessions')]))])), ( u'Managers', OrderedDict([(u'@odata.id', u'/redfish/v1/Managers')])), (u'Name', u'PSME Service Root'), ( u'Oem', OrderedDict([(u'Ami', OrderedDict([(u'Chassislocation', OrderedDict( [(u'@odata.id', u'/redfish/v1/Chassislocation')])), (u'Configurations', OrderedDict([(u'@odata.id', u'/redfish/v1/configurations')])), (u'PsmeVersion', u'2.4.181218.tb1')])), (u'Intel_RackScale', OrderedDict([(u'@odata.type', u'#Intel.Oem.ServiceRoot'), (u'ApiVersion', u'2.4.0'), ( u'TelemetryService', OrderedDict([(u'@odata.id', u'/redfish/v1/TelemetryService')]))]))])), (u'Product', u'AMI Redfish Server'), (u'ProtocolFeaturesSupported', OrderedDict([(u'ExpandQuery', OrderedDict( [(u'ExpandAll', True), (u'Levels', True), (u'Links', True), (u'MaxLevels', 5), (u'NoLinks', True)])), (u'FilterQuery', True), (u'SelectQuery', True)])), ( u'RedfishVersion', u'1.5.0'), (u'Registries', OrderedDict([(u'@odata.id', u'/redfish/v1/Registries')])), ( u'SessionService', OrderedDict([(u'@odata.id', u'/redfish/v1/SessionService')])), (u'Systems', OrderedDict([( u'@odata.id', u'/redfish/v1/Systems')])), (u'Tasks', OrderedDict([(u'@odata.id', u'/redfish/v1/TaskService')])), (u'UUID', u'ffffffff-ffff-ffff-ffff-ffffffffffff'), (u'UpdateService', OrderedDict([(u'@odata.id', u'/redfish/v1/UpdateService')]))]) with mock.patch('requests.get') as requests_get_mock: response = Mock() response.status_code = 200 response.headers = {} response.text = response_payload requests_get_mock.return_value = response with StdoutCapture() as output: self.api_caller.get_resource("/resource", DiscoveryContainer()) self.assertIsNot("ERROR", output.raw) def test_no_content(self): with mock.patch('requests.get') as requests_get_mock: response = Mock() response.status_code = 204 response.headers = {} response.text = None requests_get_mock.return_value = response link, status, status_code, response_body, headers = \ self.api_caller.get_resource("/resource", DiscoveryContainer(), acceptable_return_codes = [204]) self.assertTrue(status) self.assertEqual(status_code, 204) self.assertEqual(response_body, dict()) response.json.assert_not_called() def test_async_create_without_location(self): with mock.patch('requests.post') as requests_post_mock: response = Mock() response.status_code = ReturnCodes.ACCEPTED response.headers = {} response.text = None requests_post_mock.return_value = response with mock.patch('requests.get') as requests_get_mock: response = Mock() response.status_code = 204 response.headers = {} response.text = None requests_get_mock.return_value = response with StdoutCapture() as output: status, status_code, response_body, headers = self.api_caller.post_resource( 'odata_id', DiscoveryContainer(), payload={}) self.assertIn('Location header not found', '\n'.join(output)) def test_unexpected_async_post_response(self): with mock.patch('requests.post') as requests_get_mock: response = Mock() response.status_code = ReturnCodes.ACCEPTED response.headers = {} response.text = "{}" requests_get_mock.return_value = response with self.assertRaises(AsyncOperation): self.api_caller.post_resource('odata_id', DiscoveryContainer(), payload={}, wait_if_async=False) def test_async_post_response(self): with mock.patch('requests.post') as requests_post_mock: not_done = Mock() not_done.status_code = ReturnCodes.ACCEPTED not_done.headers = {'Location': 'location'} not_done.text = None requests_post_mock.return_value = not_done with mock.patch('requests.get') as requests_get_mock: done = Mock() done.status_code = ReturnCodes.OK done.headers = {'Location': 'location'} done.text = "{ \"done\": true }" requests_get_mock.side_effect = [not_done, not_done, done] with StdoutCapture() as output: status, status_code, response_body, headers = self.api_caller.post_resource( 'odata_id', DiscoveryContainer(), payload={}) self.assertTrue(response_body['done']) @unittest.skip("Temporary change related to bug in Discovery mechanism") def test_resource_in_discovery_container_after_get_patch_delete(self): with mock.patch('requests.get') as requests_get_mock: resource = {"@odata.id": "odata.id", "something": "irrelevant"} get_response = Mock() get_response.status_code = ReturnCodes.OK get_response.headers = {} get_response.text = json.dumps(resource) requests_get_mock.return_value = get_response discovery_container = DiscoveryContainer() self.api_caller.get_resource("/resource", discovery_container) self.assertEqual( discovery_container["http://{API_ENDPOINT}/resource".format( API_ENDPOINT=API_ENDPOINT)].body, resource) patched_resource = { "@odata.id": "odata.id", "something": "relevant" } get_response.text = json.dumps(patched_resource) with mock.patch('requests.patch') as requests_patch_mock: patch_response = Mock() patch_response.status_code = ReturnCodes.OK patch_response.headers = {} patch_response.text = "{}" requests_patch_mock.return_value = patch_response _, _, _, _ = self.api_caller.patch_resource( "/resource", discovery_container) self.assertEqual( discovery_container[ "http://{API_ENDPOINT}/resource".format( API_ENDPOINT=API_ENDPOINT)].body, patched_resource) with mock.patch('requests.delete') as requests_delete_mock: delete_response = Mock() delete_response.status_code = ReturnCodes.NO_CONTENT delete_response.headers = {} delete_response.text = "" requests_delete_mock.return_value = delete_response _, _, _, _ = self.api_caller.delete_resource( "/resource", discovery_container) self.assertNotIn("/resource", discovery_container) def test_get_xml(self): with mock.patch('requests.get') as get: response = Mock() response.status_code = ReturnCodes.OK response.headers = {} response.text = "<xml></xml>" get.return_value = response link, status, status_code, response_body, headers = self.api_caller.get_xml( "uri") self.assertEqual("<xml></xml>", response_body)