From 61bccadc3789960a35fb76db078f56bd683e99ae Mon Sep 17 00:00:00 2001 From: jinxingfang Date: Tue, 28 Feb 2017 15:44:04 +0800 Subject: [PATCH] Add request execution function. Add request execution function for other module to call. Change-Id: Iecdddbfe5a5f25e5c86b7e57aa0db85d5b7f6080 --- valenceclient/exc.py | 60 +++++++++ valenceclient/tests/unit/common/test_exc.py | 59 +++++++++ valenceclient/tests/unit/utils.py | 132 ++++++++++++++++++++ 3 files changed, 251 insertions(+) create mode 100644 valenceclient/exc.py create mode 100644 valenceclient/tests/unit/common/test_exc.py create mode 100644 valenceclient/tests/unit/utils.py diff --git a/valenceclient/exc.py b/valenceclient/exc.py new file mode 100644 index 0000000..c3ea5ad --- /dev/null +++ b/valenceclient/exc.py @@ -0,0 +1,60 @@ +# Copyright 2017 99cloud Inc. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from valenceclient.common.apiclient import exceptions +from valenceclient.common.apiclient.exceptions import ClientException + + +class InvalidValenceUrl(ClientException): + pass + + +class InvalidAttribution(ClientException): + pass + + +class ValidationError(ClientException): + pass + + +class InvalidAttribute(ClientException): + pass + + +class ConnectionRefuse(ClientException): + pass + + +def from_response(response, message=None, traceback=None, method=None, + url=None): + """Return an HttpError instance based on response from httplib/requests""" + + error_body = {} + if message: + error_body['message'] = message + if traceback: + error_body['traceback'] = traceback + + if hasattr(response, 'status') and not hasattr(response, 'status_code'): + response.status_code = response.status + response.headers = { + 'Content-Type': response.getheader('content-type', '')} + + if hasattr(response, 'status_code'): + # NOTE(jiangfei): These modifications allow SessionClient + # to handle faultstring. + response.json = lambda: {'error': error_body} + + return exceptions.from_response(response, method=method, url=url) diff --git a/valenceclient/tests/unit/common/test_exc.py b/valenceclient/tests/unit/common/test_exc.py new file mode 100644 index 0000000..af02520 --- /dev/null +++ b/valenceclient/tests/unit/common/test_exc.py @@ -0,0 +1,59 @@ +# Copyright 2017 99cloud, Inc. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import mock +from six.moves import http_client + +from valenceclient.common.apiclient import exceptions +from valenceclient import exc +from valenceclient.tests.unit import utils as test_utils + + +@mock.patch.object(exceptions, 'from_response') +class ExcTest(test_utils.BaseTestCase): + + def setUp(self): + super(ExcTest, self).setUp() + self.message = 'SpongeBob SquarePants' + self.traceback = 'Foo Traceback' + self.method = 'call_spongebob' + self.url = 'http://foo.bar' + self.expected_json = {'error': {'message': self.message, + 'traceback': self.traceback}} + + def test_from_response(self, mock_apiclient): + fake_response = mock.Mock(status_code=http_client.BAD_REQUEST) + exc.from_response(fake_response, message=self.message, + traceback=self.traceback, method=self.method, + url=self.url) + self.assertEqual(http_client.BAD_REQUEST, fake_response.status_code) + self.assertEqual(self.expected_json, fake_response.json()) + mock_apiclient.assert_called_once_with( + fake_response, method=self.method, url=self.url) + + def test_from_response_status(self, mock_apiclient): + fake_response = mock.Mock(status=http_client.BAD_REQUEST) + fake_response.getheader.return_value = 'fake-header' + delattr(fake_response, 'status_code') + + exc.from_response(fake_response, message=self.message, + traceback=self.traceback, method=self.method, + url=self.url) + expected_header = {'Content-Type': 'fake-header'} + self.assertEqual(expected_header, fake_response.headers) + self.assertEqual(http_client.BAD_REQUEST, fake_response.status_code) + self.assertEqual(self.expected_json, fake_response.json()) + mock_apiclient.assert_called_once_with( + fake_response, method=self.method, url=self.url) diff --git a/valenceclient/tests/unit/utils.py b/valenceclient/tests/unit/utils.py new file mode 100644 index 0000000..da20f9c --- /dev/null +++ b/valenceclient/tests/unit/utils.py @@ -0,0 +1,132 @@ +# Copyright 2017 99cloud, Inc. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import copy +import os + +import fixtures +import mock +from oslo_utils import strutils +import requests +import six +import testtools + + +class BaseTestCase(testtools.TestCase): + def setUp(self): + super(BaseTestCase, self).setUp() + self.useFixture(fixtures.FakeLogger()) + + # If enabled, stdout and/or stderr is captured and will appear in + # test results if that test fails. + if strutils.bool_from_string(os.environ.get('OS_STDOUT_CAPTURE')): + stdout = self.useFixture(fixtures.StringStream('stdout')).stream + self.useFixture(fixtures.MonkeyPatch('sys.stdout', stdout)) + if strutils.bool_from_string(os.environ.get('OS_STDERR_CAPTURE')): + stderr = self.useFixture(fixtures.StringStream('stderr')).stream + self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr)) + + +class FakeAPI(object): + def __init__(self, responses): + self.responses = responses + self.calls = [] + + def _request(self, method, url, headers=None, body=None): + call = (method, url, headers or {}, body) + self.calls.append(call) + return self.responses[url][method] + + def raw_request(self, *args, **kwargs): + response = self._request(*args, **kwargs) + body_iter = iter(six.StringIO(response[1])) + return FakeResponse(response[0]), body_iter + + def json_request(self, *args, **kwargs): + response = self._request(*args, **kwargs) + return FakeResponse(response[0]), response[1] + + +class FakeConnection(object): + def __init__(self, response=None): + self._response = response + self._last_request = None + + def request(self, method, conn_url, **kwargs): + self._last_request = (method, conn_url, kwargs) + + def setresponse(self, response): + self._response = response + + def getresponse(self): + return self._response + + def __repr__(self): + return ("FakeConnection(response=%s)" % (self._response)) + + +class FakeResponse(object): + def __init__(self, headers, body=None, version=None, status=None, + reason=None): + """Fake object to help testing. + + :param headers: dict representing HTTP response headers + :param body: file-like object + """ + self.headers = headers + self.body = body + self.raw = mock.Mock() + self.raw.version = version + self.status_code = status + self.reason = reason + + def getheaders(self): + return copy.deepcopy(self.headers).items() + + def getheader(self, key, default): + return self.headers.get(key, default) + + def read(self, amt): + return self.body.read(amt) + + def __repr__(self): + return ("FakeResponse(%s, body=%s, version=%s, status=%s, reason=%s)" % + (self.headers, self.body, self.version, self.status, + self.reason)) + + +def mockSessionResponse(headers, content=None, status_code=None, version=None): + raw = mock.Mock() + raw.version = version + response = mock.Mock(spec=requests.Response, + headers=headers, + content=content, + status_code=status_code, + raw=raw, + reason='', + encoding='UTF-8') + response.text = content + + return response + + +def mockSession(headers, content=None, status_code=None, version=None): + session = mock.Mock(spec=requests.Session, + verify=False, + cert=('test_cert', 'test_key')) + response = mockSessionResponse(headers, content, status_code, version) + session.request = mock.Mock(return_value=response) + + return session