예제 #1
0
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
예제 #2
0
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)
예제 #3
0
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)