summaryrefslogtreecommitdiff
path: root/tests/unit/test_swiftclient.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/unit/test_swiftclient.py')
-rw-r--r--tests/unit/test_swiftclient.py1154
1 files changed, 1154 insertions, 0 deletions
diff --git a/tests/unit/test_swiftclient.py b/tests/unit/test_swiftclient.py
new file mode 100644
index 0000000..4e83a2d
--- /dev/null
+++ b/tests/unit/test_swiftclient.py
@@ -0,0 +1,1154 @@
+# Copyright (c) 2010-2012 OpenStack, LLC.
+#
+# 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.
+
+# TODO: More tests
+import logging
+
+try:
+ from unittest import mock
+except ImportError:
+ import mock
+
+import six
+import socket
+import types
+import testtools
+import warnings
+from six.moves.urllib.parse import urlparse
+from six.moves import reload_module
+
+# TODO: mock http connection class with more control over headers
+from .utils import fake_http_connect, fake_get_keystoneclient_2_0
+
+from swiftclient import client as c
+import swiftclient.utils
+
+
+class TestClientException(testtools.TestCase):
+
+ def test_is_exception(self):
+ self.assertTrue(issubclass(c.ClientException, Exception))
+
+ def test_format(self):
+ exc = c.ClientException('something failed')
+ self.assertTrue('something failed' in str(exc))
+ test_kwargs = (
+ 'scheme',
+ 'host',
+ 'port',
+ 'path',
+ 'query',
+ 'status',
+ 'reason',
+ 'device',
+ )
+ for value in test_kwargs:
+ kwargs = {
+ 'http_%s' % value: value,
+ }
+ exc = c.ClientException('test', **kwargs)
+ self.assertTrue(value in str(exc))
+
+
+class TestJsonImport(testtools.TestCase):
+
+ def tearDown(self):
+ try:
+ import json
+ except ImportError:
+ pass
+ else:
+ reload_module(json)
+
+ try:
+ import simplejson
+ except ImportError:
+ pass
+ else:
+ reload_module(simplejson)
+ super(TestJsonImport, self).tearDown()
+
+ def test_any(self):
+ self.assertTrue(hasattr(c, 'json_loads'))
+
+ def test_no_simplejson(self):
+ # break simplejson
+ try:
+ import simplejson
+ except ImportError:
+ # not installed, so we don't have to break it for these tests
+ pass
+ else:
+ delattr(simplejson, 'loads')
+ reload_module(c)
+
+ try:
+ from json import loads
+ except ImportError:
+ # this case is stested in _no_json
+ pass
+ else:
+ self.assertEqual(loads, c.json_loads)
+
+
+class MockHttpTest(testtools.TestCase):
+
+ def setUp(self):
+ super(MockHttpTest, self).setUp()
+
+ def fake_http_connection(*args, **kwargs):
+ _orig_http_connection = c.http_connection
+ return_read = kwargs.get('return_read')
+ query_string = kwargs.get('query_string')
+ storage_url = kwargs.get('storage_url')
+
+ def wrapper(url, proxy=None, cacert=None, insecure=False,
+ ssl_compression=True):
+ if storage_url:
+ self.assertEqual(storage_url, url)
+
+ parsed, _conn = _orig_http_connection(url, proxy=proxy)
+ conn = fake_http_connect(*args, **kwargs)()
+
+ def request(method, url, *args, **kwargs):
+ if query_string:
+ self.assertTrue(url.endswith('?' + query_string))
+ if url.endswith('invalid_cert') and not insecure:
+ from swiftclient import client as c
+ raise c.ClientException("invalid_certificate")
+ return
+ conn.request = request
+
+ conn.has_been_read = False
+ _orig_read = conn.read
+
+ def read(*args, **kwargs):
+ conn.has_been_read = True
+ return _orig_read(*args, **kwargs)
+ conn.read = return_read or read
+
+ return parsed, conn
+ return wrapper
+ self.fake_http_connection = fake_http_connection
+
+ def tearDown(self):
+ super(MockHttpTest, self).tearDown()
+ reload_module(c)
+
+
+class MockHttpResponse():
+ def __init__(self, status=0):
+ self.status = status
+ self.status_code = status
+ self.reason = "OK"
+ self.buffer = []
+ self.requests_params = None
+
+ class Raw:
+ def read():
+ pass
+ self.raw = Raw()
+
+ def read(self):
+ return ""
+
+ def getheader(self, name, default):
+ return ""
+
+ def getheaders(self):
+ return {"key1": "value1", "key2": "value2"}
+
+ def fake_response(self):
+ return MockHttpResponse(self.status)
+
+ def _fake_request(self, *arg, **kwarg):
+ self.status = 200
+ self.requests_params = kwarg
+ # This simulate previous httplib implementation that would do a
+ # putrequest() and then use putheader() to send header.
+ for k, v in kwarg['headers'].items():
+ self.buffer.append((k, v))
+ return self.fake_response()
+
+
+class TestHttpHelpers(MockHttpTest):
+
+ def test_quote(self):
+ value = b'bytes\xff'
+ self.assertEqual('bytes%FF', c.quote(value))
+ value = 'native string'
+ self.assertEqual('native%20string', c.quote(value))
+ value = u'unicode string'
+ self.assertEqual('unicode%20string', c.quote(value))
+ value = u'unicode:\xe9\u20ac'
+ self.assertEqual('unicode%3A%C3%A9%E2%82%AC', c.quote(value))
+
+ def test_http_connection(self):
+ url = 'http://www.test.com'
+ _junk, conn = c.http_connection(url)
+ self.assertTrue(isinstance(conn, c.HTTPConnection))
+ url = 'https://www.test.com'
+ _junk, conn = c.http_connection(url)
+ self.assertTrue(isinstance(conn, c.HTTPConnection))
+ url = 'ftp://www.test.com'
+ self.assertRaises(c.ClientException, c.http_connection, url)
+
+ def test_set_user_agent_default(self):
+ _junk, conn = c.http_connection('http://www.example.com')
+ req_headers = {}
+
+ def my_request_handler(*a, **kw):
+ req_headers.update(kw.get('headers', {}))
+ conn._request = my_request_handler
+
+ # test the default
+ conn.request('GET', '/')
+ ua = req_headers.get('user-agent', 'XXX-MISSING-XXX')
+ self.assert_(ua.startswith('python-swiftclient-'))
+
+ def test_set_user_agent_per_request_override(self):
+ _junk, conn = c.http_connection('http://www.example.com')
+ req_headers = {}
+
+ def my_request_handler(*a, **kw):
+ req_headers.update(kw.get('headers', {}))
+ conn._request = my_request_handler
+
+ # test if it's actually set
+ conn.request('GET', '/', headers={'User-Agent': 'Me'})
+ ua = req_headers.get('user-agent', 'XXX-MISSING-XXX')
+ self.assertEqual(ua, 'Me', req_headers)
+
+ def test_set_user_agent_default_override(self):
+ _junk, conn = c.http_connection(
+ 'http://www.example.com',
+ default_user_agent='a-new-default')
+ req_headers = {}
+
+ def my_request_handler(*a, **kw):
+ req_headers.update(kw.get('headers', {}))
+ conn._request = my_request_handler
+
+ # test setting a default
+ conn._request = my_request_handler
+ conn.request('GET', '/')
+ ua = req_headers.get('user-agent', 'XXX-MISSING-XXX')
+ self.assertEqual(ua, 'a-new-default')
+
+# TODO: following tests are placeholders, need more tests, better coverage
+
+
+class TestGetAuth(MockHttpTest):
+
+ def test_ok(self):
+ c.http_connection = self.fake_http_connection(200)
+ url, token = c.get_auth('http://www.test.com', 'asdf', 'asdf')
+ self.assertEqual(url, None)
+ self.assertEqual(token, None)
+
+ def test_invalid_auth(self):
+ c.http_connection = self.fake_http_connection(200)
+ self.assertRaises(c.ClientException, c.get_auth,
+ 'http://www.tests.com', 'asdf', 'asdf',
+ auth_version="foo")
+
+ def test_auth_v1(self):
+ c.http_connection = self.fake_http_connection(200, auth_v1=True)
+ url, token = c.get_auth('http://www.test.com', 'asdf', 'asdf',
+ auth_version="1.0")
+ self.assertEqual(url, 'storageURL')
+ self.assertEqual(token, 'someauthtoken')
+
+ def test_auth_v1_insecure(self):
+ c.http_connection = self.fake_http_connection(200, auth_v1=True)
+ url, token = c.get_auth('http://www.test.com/invalid_cert',
+ 'asdf', 'asdf',
+ auth_version='1.0',
+ insecure=True)
+ self.assertEqual(url, 'storageURL')
+ self.assertEqual(token, 'someauthtoken')
+
+ self.assertRaises(c.ClientException, c.get_auth,
+ 'http://www.test.com/invalid_cert',
+ 'asdf', 'asdf',
+ auth_version='1.0')
+
+ def test_auth_v2(self):
+ os_options = {'tenant_name': 'asdf'}
+ c.get_keystoneclient_2_0 = fake_get_keystoneclient_2_0(os_options)
+ url, token = c.get_auth('http://www.test.com', 'asdf', 'asdf',
+ os_options=os_options,
+ auth_version="2.0")
+ self.assertTrue(url.startswith("http"))
+ self.assertTrue(token)
+
+ def test_auth_v2_no_tenant_name(self):
+ c.get_keystoneclient_2_0 = fake_get_keystoneclient_2_0({})
+ self.assertRaises(c.ClientException, c.get_auth,
+ 'http://www.tests.com', 'asdf', 'asdf',
+ os_options={},
+ auth_version='2.0')
+
+ def test_auth_v2_with_tenant_user_in_user(self):
+ tenant_option = {'tenant_name': 'foo'}
+ c.get_keystoneclient_2_0 = fake_get_keystoneclient_2_0(tenant_option)
+ url, token = c.get_auth('http://www.test.com', 'foo:bar', 'asdf',
+ os_options={},
+ auth_version="2.0")
+ self.assertTrue(url.startswith("http"))
+ self.assertTrue(token)
+
+ def test_auth_v2_tenant_name_no_os_options(self):
+ tenant_option = {'tenant_name': 'asdf'}
+ c.get_keystoneclient_2_0 = fake_get_keystoneclient_2_0(tenant_option)
+ url, token = c.get_auth('http://www.test.com', 'asdf', 'asdf',
+ tenant_name='asdf',
+ os_options={},
+ auth_version="2.0")
+ self.assertTrue(url.startswith("http"))
+ self.assertTrue(token)
+
+ def test_auth_v2_with_os_options(self):
+ os_options = {'service_type': 'object-store',
+ 'endpoint_type': 'internalURL',
+ 'tenant_name': 'asdf'}
+ c.get_keystoneclient_2_0 = fake_get_keystoneclient_2_0(os_options)
+ url, token = c.get_auth('http://www.test.com', 'asdf', 'asdf',
+ os_options=os_options,
+ auth_version="2.0")
+ self.assertTrue(url.startswith("http"))
+ self.assertTrue(token)
+
+ def test_auth_v2_with_tenant_user_in_user_no_os_options(self):
+ tenant_option = {'tenant_name': 'foo'}
+ c.get_keystoneclient_2_0 = fake_get_keystoneclient_2_0(tenant_option)
+ url, token = c.get_auth('http://www.test.com', 'foo:bar', 'asdf',
+ auth_version="2.0")
+ self.assertTrue(url.startswith("http"))
+ self.assertTrue(token)
+
+ def test_auth_v2_with_os_region_name(self):
+ os_options = {'region_name': 'good-region',
+ 'tenant_name': 'asdf'}
+ c.get_keystoneclient_2_0 = fake_get_keystoneclient_2_0(os_options)
+ url, token = c.get_auth('http://www.test.com', 'asdf', 'asdf',
+ os_options=os_options,
+ auth_version="2.0")
+ self.assertTrue(url.startswith("http"))
+ self.assertTrue(token)
+
+ def test_auth_v2_no_endpoint(self):
+ os_options = {'region_name': 'unknown_region',
+ 'tenant_name': 'asdf'}
+ c.get_keystoneclient_2_0 = fake_get_keystoneclient_2_0(
+ os_options, c.ClientException)
+ self.assertRaises(c.ClientException, c.get_auth,
+ 'http://www.tests.com', 'asdf', 'asdf',
+ os_options=os_options, auth_version='2.0')
+
+ def test_auth_v2_ks_exception(self):
+ c.get_keystoneclient_2_0 = fake_get_keystoneclient_2_0(
+ {}, c.ClientException)
+ self.assertRaises(c.ClientException, c.get_auth,
+ 'http://www.tests.com', 'asdf', 'asdf',
+ os_options={},
+ auth_version='2.0')
+
+ def test_auth_v2_cacert(self):
+ os_options = {'tenant_name': 'foo'}
+ c.get_keystoneclient_2_0 = fake_get_keystoneclient_2_0(
+ os_options, None)
+
+ auth_url_secure = 'https://www.tests.com'
+ auth_url_insecure = 'https://www.tests.com/self-signed-certificate'
+
+ url, token = c.get_auth(auth_url_secure, 'asdf', 'asdf',
+ os_options=os_options, auth_version='2.0',
+ insecure=False)
+ self.assertTrue(url.startswith("http"))
+ self.assertTrue(token)
+
+ url, token = c.get_auth(auth_url_insecure, 'asdf', 'asdf',
+ os_options=os_options, auth_version='2.0',
+ cacert='ca.pem', insecure=False)
+ self.assertTrue(url.startswith("http"))
+ self.assertTrue(token)
+
+ self.assertRaises(c.ClientException, c.get_auth,
+ auth_url_insecure, 'asdf', 'asdf',
+ os_options=os_options, auth_version='2.0')
+ self.assertRaises(c.ClientException, c.get_auth,
+ auth_url_insecure, 'asdf', 'asdf',
+ os_options=os_options, auth_version='2.0',
+ insecure=False)
+
+ def test_auth_v2_insecure(self):
+ os_options = {'tenant_name': 'foo'}
+ c.get_keystoneclient_2_0 = fake_get_keystoneclient_2_0(
+ os_options, None)
+
+ auth_url_secure = 'https://www.tests.com'
+ auth_url_insecure = 'https://www.tests.com/invalid-certificate'
+
+ url, token = c.get_auth(auth_url_secure, 'asdf', 'asdf',
+ os_options=os_options, auth_version='2.0')
+ self.assertTrue(url.startswith("http"))
+ self.assertTrue(token)
+
+ url, token = c.get_auth(auth_url_insecure, 'asdf', 'asdf',
+ os_options=os_options, auth_version='2.0',
+ insecure=True)
+ self.assertTrue(url.startswith("http"))
+ self.assertTrue(token)
+
+ self.assertRaises(c.ClientException, c.get_auth,
+ auth_url_insecure, 'asdf', 'asdf',
+ os_options=os_options, auth_version='2.0')
+ self.assertRaises(c.ClientException, c.get_auth,
+ auth_url_insecure, 'asdf', 'asdf',
+ os_options=os_options, auth_version='2.0',
+ insecure=False)
+
+
+class TestGetAccount(MockHttpTest):
+
+ def test_no_content(self):
+ c.http_connection = self.fake_http_connection(204)
+ value = c.get_account('http://www.test.com', 'asdf')[1]
+ self.assertEqual(value, [])
+
+ def test_param_marker(self):
+ c.http_connection = self.fake_http_connection(
+ 204,
+ query_string="format=json&marker=marker")
+ c.get_account('http://www.test.com', 'asdf', marker='marker')
+
+ def test_param_limit(self):
+ c.http_connection = self.fake_http_connection(
+ 204,
+ query_string="format=json&limit=10")
+ c.get_account('http://www.test.com', 'asdf', limit=10)
+
+ def test_param_prefix(self):
+ c.http_connection = self.fake_http_connection(
+ 204,
+ query_string="format=json&prefix=asdf/")
+ c.get_account('http://www.test.com', 'asdf', prefix='asdf/')
+
+ def test_param_end_marker(self):
+ c.http_connection = self.fake_http_connection(
+ 204,
+ query_string="format=json&end_marker=end_marker")
+ c.get_account('http://www.test.com', 'asdf', end_marker='end_marker')
+
+
+class TestHeadAccount(MockHttpTest):
+
+ def test_ok(self):
+ c.http_connection = self.fake_http_connection(200)
+ value = c.head_account('http://www.tests.com', 'asdf')
+ # TODO: Hmm. This doesn't really test too much as it uses a fake that
+ # always returns the same dict. I guess it "exercises" the code, so
+ # I'll leave it for now.
+ self.assertEqual(type(value), dict)
+
+ def test_server_error(self):
+ body = 'c' * 65
+ c.http_connection = self.fake_http_connection(500, body=body)
+ self.assertRaises(c.ClientException, c.head_account,
+ 'http://www.tests.com', 'asdf')
+ try:
+ c.head_account('http://www.tests.com', 'asdf')
+ except c.ClientException as e:
+ new_body = "[first 60 chars of response] " + body[0:60]
+ self.assertEqual(e.__str__()[-89:], new_body)
+
+
+class TestGetContainer(MockHttpTest):
+
+ def test_no_content(self):
+ c.http_connection = self.fake_http_connection(204)
+ value = c.get_container('http://www.test.com', 'asdf', 'asdf')[1]
+ self.assertEqual(value, [])
+
+ def test_param_marker(self):
+ c.http_connection = self.fake_http_connection(
+ 204,
+ query_string="format=json&marker=marker")
+ c.get_container('http://www.test.com', 'asdf', 'asdf', marker='marker')
+
+ def test_param_limit(self):
+ c.http_connection = self.fake_http_connection(
+ 204,
+ query_string="format=json&limit=10")
+ c.get_container('http://www.test.com', 'asdf', 'asdf', limit=10)
+
+ def test_param_prefix(self):
+ c.http_connection = self.fake_http_connection(
+ 204,
+ query_string="format=json&prefix=asdf/")
+ c.get_container('http://www.test.com', 'asdf', 'asdf', prefix='asdf/')
+
+ def test_param_delimiter(self):
+ c.http_connection = self.fake_http_connection(
+ 204,
+ query_string="format=json&delimiter=/")
+ c.get_container('http://www.test.com', 'asdf', 'asdf', delimiter='/')
+
+ def test_param_end_marker(self):
+ c.http_connection = self.fake_http_connection(
+ 204,
+ query_string="format=json&end_marker=end_marker")
+ c.get_container('http://www.test.com', 'asdf', 'asdf',
+ end_marker='end_marker')
+
+ def test_param_path(self):
+ c.http_connection = self.fake_http_connection(
+ 204,
+ query_string="format=json&path=asdf")
+ c.get_container('http://www.test.com', 'asdf', 'asdf',
+ path='asdf')
+
+
+class TestHeadContainer(MockHttpTest):
+
+ def test_server_error(self):
+ body = 'c' * 60
+ c.http_connection = self.fake_http_connection(500, body=body)
+ self.assertRaises(c.ClientException, c.head_container,
+ 'http://www.test.com', 'asdf', 'asdf',
+ )
+ try:
+ c.head_container('http://www.test.com', 'asdf', 'asdf')
+ except c.ClientException as e:
+ self.assertEqual(e.http_response_content, body)
+
+
+class TestPutContainer(MockHttpTest):
+
+ def test_ok(self):
+ c.http_connection = self.fake_http_connection(200)
+ value = c.put_container('http://www.test.com', 'asdf', 'asdf')
+ self.assertEqual(value, None)
+
+ def test_server_error(self):
+ body = 'c' * 60
+ c.http_connection = self.fake_http_connection(500, body=body)
+ self.assertRaises(c.ClientException, c.put_container,
+ 'http://www.test.com', 'asdf', 'asdf',
+ )
+ try:
+ c.put_container('http://www.test.com', 'asdf', 'asdf')
+ except c.ClientException as e:
+ self.assertEqual(e.http_response_content, body)
+
+
+class TestDeleteContainer(MockHttpTest):
+
+ def test_ok(self):
+ c.http_connection = self.fake_http_connection(200)
+ value = c.delete_container('http://www.test.com', 'asdf', 'asdf')
+ self.assertEqual(value, None)
+
+
+class TestGetObject(MockHttpTest):
+
+ def test_server_error(self):
+ c.http_connection = self.fake_http_connection(500)
+ self.assertRaises(c.ClientException, c.get_object,
+ 'http://www.test.com', 'asdf', 'asdf', 'asdf')
+
+ def test_query_string(self):
+ c.http_connection = self.fake_http_connection(200,
+ query_string="hello=20")
+ c.get_object('http://www.test.com', 'asdf', 'asdf', 'asdf',
+ query_string="hello=20")
+
+ def test_request_headers(self):
+ request_args = {}
+
+ def fake_request(method, url, body=None, headers=None):
+ request_args['method'] = method
+ request_args['url'] = url
+ request_args['body'] = body
+ request_args['headers'] = headers
+ return
+ conn = self.fake_http_connection(200)('http://www.test.com/')
+ conn[1].request = fake_request
+ headers = {'Range': 'bytes=1-2'}
+ c.get_object('url_is_irrelevant', 'TOKEN', 'container', 'object',
+ http_conn=conn, headers=headers)
+ self.assertFalse(request_args['headers'] is None,
+ "No headers in the request")
+ self.assertTrue('Range' in request_args['headers'],
+ "No Range header in the request")
+ self.assertEqual(request_args['headers']['Range'], 'bytes=1-2')
+
+
+class TestHeadObject(MockHttpTest):
+
+ def test_server_error(self):
+ c.http_connection = self.fake_http_connection(500)
+ self.assertRaises(c.ClientException, c.head_object,
+ 'http://www.test.com', 'asdf', 'asdf', 'asdf')
+
+
+class TestPutObject(MockHttpTest):
+
+ def test_ok(self):
+ c.http_connection = self.fake_http_connection(200)
+ args = ('http://www.test.com', 'asdf', 'asdf', 'asdf', 'asdf')
+ value = c.put_object(*args)
+ self.assertTrue(isinstance(value, six.string_types))
+
+ def test_unicode_ok(self):
+ conn = c.http_connection(u'http://www.test.com/')
+ mock_file = six.StringIO(u'\u5929\u7a7a\u4e2d\u7684\u4e4c\u4e91')
+ args = (u'\u5929\u7a7a\u4e2d\u7684\u4e4c\u4e91',
+ '\u5929\u7a7a\u4e2d\u7684\u4e4c\u4e91',
+ u'\u5929\u7a7a\u4e2d\u7684\u4e4c\u4e91',
+ u'\u5929\u7a7a\u4e2d\u7684\u4e4c\u4e91',
+ mock_file)
+ text = u'\u5929\u7a7a\u4e2d\u7684\u4e4c\u4e91'
+ headers = {'X-Header1': text,
+ 'X-2': 1, 'X-3': {'a': 'b'}, 'a-b': '.x:yz mn:fg:lp'}
+
+ resp = MockHttpResponse()
+ conn[1].getresponse = resp.fake_response
+ conn[1]._request = resp._fake_request
+ value = c.put_object(*args, headers=headers, http_conn=conn)
+ self.assertTrue(isinstance(value, six.string_types))
+ # Test for RFC-2616 encoded symbols
+ self.assertIn((b"a-b", b".x:yz mn:fg:lp"),
+ resp.buffer)
+ # Test unicode header
+ self.assertIn((b'x-header1', text.encode('utf8')),
+ resp.buffer)
+
+ def test_chunk_warning(self):
+ conn = c.http_connection('http://www.test.com/')
+ mock_file = six.StringIO('asdf')
+ args = ('asdf', 'asdf', 'asdf', 'asdf', mock_file)
+ resp = MockHttpResponse()
+ conn[1].getresponse = resp.fake_response
+ conn[1]._request = resp._fake_request
+ with warnings.catch_warnings(record=True) as w:
+ c.put_object(*args, chunk_size=20, headers={}, http_conn=conn)
+ self.assertEqual(len(w), 0)
+
+ body = 'c' * 60
+ c.http_connection = self.fake_http_connection(200, body=body)
+ args = ('http://www.test.com', 'asdf', 'asdf', 'asdf', 'asdf')
+ with warnings.catch_warnings(record=True) as w:
+ c.put_object(*args, chunk_size=20)
+ self.assertEqual(len(w), 1)
+ self.assertTrue(issubclass(w[-1].category, UserWarning))
+
+ def test_server_error(self):
+ body = 'c' * 60
+ c.http_connection = self.fake_http_connection(500, body=body)
+ args = ('http://www.test.com', 'asdf', 'asdf', 'asdf', 'asdf')
+ self.assertRaises(c.ClientException, c.put_object, *args)
+ try:
+ c.put_object(*args)
+ except c.ClientException as e:
+ self.assertEqual(e.http_response_content, body)
+
+ def test_query_string(self):
+ c.http_connection = self.fake_http_connection(200,
+ query_string="hello=20")
+ c.put_object('http://www.test.com', 'asdf', 'asdf', 'asdf',
+ query_string="hello=20")
+
+ def test_raw_upload(self):
+ # Raw upload happens when content_length is passed to put_object
+ conn = c.http_connection(u'http://www.test.com/')
+ resp = MockHttpResponse(status=200)
+ conn[1].getresponse = resp.fake_response
+ conn[1]._request = resp._fake_request
+ astring = 'asdf'
+ astring_len = len(astring)
+ mock_file = six.StringIO(astring)
+
+ c.put_object(url='http://www.test.com', http_conn=conn,
+ contents=mock_file, content_length=astring_len)
+ self.assertTrue(isinstance(resp.requests_params['data'],
+ swiftclient.utils.LengthWrapper))
+ self.assertEqual(astring_len,
+ len(resp.requests_params['data'].read()))
+
+ mock_file = six.StringIO(astring)
+ c.put_object(url='http://www.test.com', http_conn=conn,
+ headers={'Content-Length': str(astring_len)},
+ contents=mock_file)
+ self.assertTrue(isinstance(resp.requests_params['data'],
+ swiftclient.utils.LengthWrapper))
+ self.assertEqual(astring_len,
+ len(resp.requests_params['data'].read()))
+
+ def test_chunk_upload(self):
+ # Chunked upload happens when no content_length is passed to put_object
+ conn = c.http_connection(u'http://www.test.com/')
+ resp = MockHttpResponse(status=200)
+ conn[1].getresponse = resp.fake_response
+ conn[1]._request = resp._fake_request
+ raw_data = 'asdf' * 256
+ chunk_size = 16
+ mock_file = six.StringIO(raw_data)
+
+ c.put_object(url='http://www.test.com', http_conn=conn,
+ contents=mock_file, chunk_size=chunk_size)
+ request_data = resp.requests_params['data']
+ self.assertTrue(isinstance(request_data, types.GeneratorType))
+ data = ''
+ for chunk in request_data:
+ self.assertEqual(chunk_size, len(chunk))
+ data += chunk
+ self.assertEqual(data, raw_data)
+
+ def test_params(self):
+ conn = c.http_connection(u'http://www.test.com/')
+ resp = MockHttpResponse(status=200)
+ conn[1].getresponse = resp.fake_response
+ conn[1]._request = resp._fake_request
+
+ c.put_object(url='http://www.test.com', http_conn=conn,
+ etag='1234-5678', content_type='text/plain')
+ request_header = resp.requests_params['headers']
+ self.assertTrue(request_header['etag'], '1234-5678')
+ self.assertTrue(request_header['content-type'], 'text/plain')
+
+
+class TestPostObject(MockHttpTest):
+
+ def test_ok(self):
+ c.http_connection = self.fake_http_connection(200)
+ args = ('http://www.test.com', 'asdf', 'asdf', 'asdf', {})
+ c.post_object(*args)
+
+ def test_unicode_ok(self):
+ conn = c.http_connection(u'http://www.test.com/')
+ args = (u'\u5929\u7a7a\u4e2d\u7684\u4e4c\u4e91',
+ '\u5929\u7a7a\u4e2d\u7684\u4e4c\u4e91',
+ u'\u5929\u7a7a\u4e2d\u7684\u4e4c\u4e91',
+ u'\u5929\u7a7a\u4e2d\u7684\u4e4c\u4e91')
+ text = u'\u5929\u7a7a\u4e2d\u7684\u4e4c\u4e91'
+ headers = {'X-Header1': text,
+ 'X-2': '1', 'X-3': {'a': 'b'}, 'a-b': '.x:yz mn:kl:qr'}
+
+ resp = MockHttpResponse()
+ conn[1].getresponse = resp.fake_response
+ conn[1]._request = resp._fake_request
+ c.post_object(*args, headers=headers, http_conn=conn)
+ # Test for RFC-2616 encoded symbols
+ self.assertTrue((b'a-b', b"a-b: .x:yz mn:kl:qr"), resp.buffer)
+ # Test unicode header
+ self.assertIn((b'x-header1', text.encode('utf8')),
+ resp.buffer)
+
+ def test_server_error(self):
+ body = 'c' * 60
+ c.http_connection = self.fake_http_connection(500, body=body)
+ args = ('http://www.test.com', 'asdf', 'asdf', 'asdf', {})
+ self.assertRaises(c.ClientException, c.post_object, *args)
+ try:
+ c.post_object(*args)
+ except c.ClientException as e:
+ self.assertEqual(e.http_response_content, body)
+
+
+class TestDeleteObject(MockHttpTest):
+
+ def test_ok(self):
+ c.http_connection = self.fake_http_connection(200)
+ c.delete_object('http://www.test.com', 'asdf', 'asdf', 'asdf')
+
+ def test_server_error(self):
+ c.http_connection = self.fake_http_connection(500)
+ self.assertRaises(c.ClientException, c.delete_object,
+ 'http://www.test.com', 'asdf', 'asdf', 'asdf')
+
+ def test_query_string(self):
+ c.http_connection = self.fake_http_connection(200,
+ query_string="hello=20")
+ c.delete_object('http://www.test.com', 'asdf', 'asdf', 'asdf',
+ query_string="hello=20")
+
+
+class TestGetCapabilities(MockHttpTest):
+
+ def test_ok(self):
+ conn = self.fake_http_connection(200, body='{}')
+ http_conn = conn('http://www.test.com/info')
+ self.assertEqual(type(c.get_capabilities(http_conn)), dict)
+ self.assertTrue(http_conn[1].has_been_read)
+
+ def test_server_error(self):
+ conn = self.fake_http_connection(500)
+ http_conn = conn('http://www.test.com/info')
+ self.assertRaises(c.ClientException, c.get_capabilities, http_conn)
+
+
+class TestHTTPConnection(MockHttpTest):
+
+ def test_ok_proxy(self):
+ conn = c.http_connection(u'http://www.test.com/',
+ proxy='http://localhost:8080')
+ self.assertEquals(conn[1].requests_args['proxies']['http'],
+ 'http://localhost:8080')
+
+ def test_bad_proxy(self):
+ try:
+ c.http_connection(u'http://www.test.com/', proxy='localhost:8080')
+ except c.ClientException as e:
+ self.assertEqual(e.msg, "Proxy's missing scheme")
+
+ def test_cacert(self):
+ conn = c.http_connection(u'http://www.test.com/',
+ cacert='/dev/urandom')
+ self.assertEquals(conn[1].requests_args['verify'], '/dev/urandom')
+
+ def test_insecure(self):
+ conn = c.http_connection(u'http://www.test.com/', insecure=True)
+ self.assertEquals(conn[1].requests_args['verify'], False)
+
+
+class TestConnection(MockHttpTest):
+
+ def test_instance(self):
+ conn = c.Connection('http://www.test.com', 'asdf', 'asdf')
+ self.assertEqual(conn.retries, 5)
+
+ def test_instance_kwargs(self):
+ args = {'user': 'ausername',
+ 'key': 'secretpass',
+ 'authurl': 'http://www.test.com',
+ 'tenant_name': 'atenant'}
+ conn = c.Connection(**args)
+ self.assertEqual(type(conn), c.Connection)
+
+ def test_instance_kwargs_token(self):
+ args = {'preauthtoken': 'atoken123',
+ 'preauthurl': 'http://www.test.com:8080/v1/AUTH_123456'}
+ conn = c.Connection(**args)
+ self.assertEqual(type(conn), c.Connection)
+
+ def test_storage_url_override(self):
+ static_url = 'http://overridden.storage.url'
+ c.http_connection = self.fake_http_connection(
+ 200, body='[]', storage_url=static_url)
+ conn = c.Connection('http://auth.url/', 'some_user', 'some_key',
+ os_options={
+ 'object_storage_url': static_url})
+ method_signatures = (
+ (conn.head_account, []),
+ (conn.get_account, []),
+ (conn.head_container, ('asdf',)),
+ (conn.get_container, ('asdf',)),
+ (conn.put_container, ('asdf',)),
+ (conn.delete_container, ('asdf',)),
+ (conn.head_object, ('asdf', 'asdf')),
+ (conn.get_object, ('asdf', 'asdf')),
+ (conn.put_object, ('asdf', 'asdf', 'asdf')),
+ (conn.post_object, ('asdf', 'asdf', {})),
+ (conn.delete_object, ('asdf', 'asdf')),
+ )
+
+ with mock.patch('swiftclient.client.get_auth_1_0') as mock_get_auth:
+ mock_get_auth.return_value = ('http://auth.storage.url', 'tToken')
+
+ for method, args in method_signatures:
+ method(*args)
+
+ def test_get_capabilities(self):
+ conn = c.Connection()
+ with mock.patch('swiftclient.client.get_capabilities') as get_cap:
+ conn.get_capabilities('http://storage2.test.com')
+ parsed = get_cap.call_args[0][0][0]
+ self.assertEqual(parsed.path, '/info')
+ self.assertEqual(parsed.netloc, 'storage2.test.com')
+ conn.get_auth = lambda: ('http://storage.test.com/v1/AUTH_test',
+ 'token')
+ conn.get_capabilities()
+ parsed = get_cap.call_args[0][0][0]
+ self.assertEqual(parsed.path, '/info')
+ self.assertEqual(parsed.netloc, 'storage.test.com')
+
+ def test_retry(self):
+ c.http_connection = self.fake_http_connection(500)
+
+ def quick_sleep(*args):
+ pass
+ c.sleep = quick_sleep
+ conn = c.Connection('http://www.test.com', 'asdf', 'asdf')
+ self.assertRaises(c.ClientException, conn.head_account)
+ self.assertEqual(conn.attempts, conn.retries + 1)
+
+ def test_retry_on_ratelimit(self):
+ c.http_connection = self.fake_http_connection(498)
+
+ def quick_sleep(*args):
+ pass
+ c.sleep = quick_sleep
+
+ # test retries
+ conn = c.Connection('http://www.test.com', 'asdf', 'asdf',
+ retry_on_ratelimit=True)
+ self.assertRaises(c.ClientException, conn.head_account)
+ self.assertEqual(conn.attempts, conn.retries + 1)
+
+ # test default no-retry
+ conn = c.Connection('http://www.test.com', 'asdf', 'asdf')
+ self.assertRaises(c.ClientException, conn.head_account)
+ self.assertEqual(conn.attempts, 1)
+
+ def test_resp_read_on_server_error(self):
+ c.http_connection = self.fake_http_connection(500)
+ conn = c.Connection('http://www.test.com', 'asdf', 'asdf', retries=0)
+
+ def get_auth(*args, **kwargs):
+ return 'http://www.new.com', 'new'
+ conn.get_auth = get_auth
+ self.url, self.token = conn.get_auth()
+
+ method_signatures = (
+ (conn.head_account, []),
+ (conn.get_account, []),
+ (conn.head_container, ('asdf',)),
+ (conn.get_container, ('asdf',)),
+ (conn.put_container, ('asdf',)),
+ (conn.delete_container, ('asdf',)),
+ (conn.head_object, ('asdf', 'asdf')),
+ (conn.get_object, ('asdf', 'asdf')),
+ (conn.put_object, ('asdf', 'asdf', 'asdf')),
+ (conn.post_object, ('asdf', 'asdf', {})),
+ (conn.delete_object, ('asdf', 'asdf')),
+ )
+
+ for method, args in method_signatures:
+ self.assertRaises(c.ClientException, method, *args)
+ try:
+ self.assertTrue(conn.http_conn[1].has_been_read)
+ except AssertionError:
+ msg = '%s did not read resp on server error' % method.__name__
+ self.fail(msg)
+ except Exception as e:
+ raise e.__class__("%s - %s" % (method.__name__, e))
+
+ def test_reauth(self):
+ c.http_connection = self.fake_http_connection(401)
+
+ def get_auth(*args, **kwargs):
+ return 'http://www.new.com', 'new'
+
+ def swap_sleep(*args):
+ self.swap_sleep_called = True
+ c.get_auth = get_auth
+ c.http_connection = self.fake_http_connection(200)
+ c.sleep = swap_sleep
+ self.swap_sleep_called = False
+
+ conn = c.Connection('http://www.test.com', 'asdf', 'asdf',
+ preauthurl='http://www.old.com',
+ preauthtoken='old',
+ )
+
+ self.assertEqual(conn.attempts, 0)
+ self.assertEqual(conn.url, 'http://www.old.com')
+ self.assertEqual(conn.token, 'old')
+
+ conn.head_account()
+
+ self.assertTrue(self.swap_sleep_called)
+ self.assertEqual(conn.attempts, 2)
+ self.assertEqual(conn.url, 'http://www.new.com')
+ self.assertEqual(conn.token, 'new')
+
+ def test_reset_stream(self):
+
+ class LocalContents(object):
+
+ def __init__(self, tell_value=0):
+ self.already_read = False
+ self.seeks = []
+ self.tell_value = tell_value
+
+ def tell(self):
+ return self.tell_value
+
+ def seek(self, position):
+ self.seeks.append(position)
+ self.already_read = False
+
+ def read(self, size=-1):
+ if self.already_read:
+ return ''
+ else:
+ self.already_read = True
+ return 'abcdef'
+
+ class LocalConnection(object):
+
+ def __init__(self, parsed_url=None):
+ self.reason = ""
+ if parsed_url:
+ self.host = parsed_url.netloc
+ self.port = parsed_url.netloc
+
+ def putrequest(self, *args, **kwargs):
+ self.send()
+
+ def putheader(self, *args, **kwargs):
+ return
+
+ def endheaders(self, *args, **kwargs):
+ return
+
+ def send(self, *args, **kwargs):
+ raise socket.error('oops')
+
+ def request(self, *args, **kwargs):
+ return
+
+ def getresponse(self, *args, **kwargs):
+ self.status = 200
+ return self
+
+ def getheader(self, *args, **kwargs):
+ return 'header'
+
+ def getheaders(self):
+ return {"key1": "value1", "key2": "value2"}
+
+ def read(self, *args, **kwargs):
+ return ''
+
+ def local_http_connection(url, proxy=None, cacert=None,
+ insecure=False, ssl_compression=True):
+ parsed = urlparse(url)
+ return parsed, LocalConnection()
+
+ orig_conn = c.http_connection
+ try:
+ c.http_connection = local_http_connection
+ conn = c.Connection('http://www.example.com', 'asdf', 'asdf',
+ retries=1, starting_backoff=.0001)
+
+ contents = LocalContents()
+ exc = None
+ try:
+ conn.put_object('c', 'o', contents)
+ except socket.error as err:
+ exc = err
+ self.assertEqual(contents.seeks, [0])
+ self.assertEqual(str(exc), 'oops')
+
+ contents = LocalContents(tell_value=123)
+ exc = None
+ try:
+ conn.put_object('c', 'o', contents)
+ except socket.error as err:
+ exc = err
+ self.assertEqual(contents.seeks, [123])
+ self.assertEqual(str(exc), 'oops')
+
+ contents = LocalContents()
+ contents.tell = None
+ exc = None
+ try:
+ conn.put_object('c', 'o', contents)
+ except c.ClientException as err:
+ exc = err
+ self.assertEqual(contents.seeks, [])
+ self.assertEqual(str(exc), "put_object('c', 'o', ...) failure "
+ "and no ability to reset contents for reupload.")
+ finally:
+ c.http_connection = orig_conn
+
+
+class TestLogging(MockHttpTest):
+ """
+ Make sure all the lines in http_log are covered.
+ """
+
+ def setUp(self):
+ super(TestLogging, self).setUp()
+ self.swiftclient_logger = logging.getLogger("swiftclient")
+ self.log_level = self.swiftclient_logger.getEffectiveLevel()
+ self.swiftclient_logger.setLevel(logging.INFO)
+
+ def tearDown(self):
+ self.swiftclient_logger.setLevel(self.log_level)
+ super(TestLogging, self).tearDown()
+
+ def test_put_ok(self):
+ c.http_connection = self.fake_http_connection(200)
+ args = ('http://www.test.com', 'asdf', 'asdf', 'asdf', 'asdf')
+ value = c.put_object(*args)
+ self.assertTrue(isinstance(value, six.string_types))
+
+ def test_head_error(self):
+ c.http_connection = self.fake_http_connection(500)
+ self.assertRaises(c.ClientException, c.head_object,
+ 'http://www.test.com', 'asdf', 'asdf', 'asdf')
+
+ def test_get_error(self):
+ body = 'c' * 65
+ conn = self.fake_http_connection(
+ 404, body=body)('http://www.test.com/')
+ request_args = {}
+
+ def fake_request(method, url, body=None, headers=None):
+ request_args['method'] = method
+ request_args['url'] = url
+ request_args['body'] = body
+ request_args['headers'] = headers
+ return
+ conn[1].request = fake_request
+ headers = {'Range': 'bytes=1-2'}
+ self.assertRaises(
+ c.ClientException,
+ c.get_object,
+ 'url_is_irrelevant', 'TOKEN', 'container', 'object',
+ http_conn=conn, headers=headers)
+
+
+class TestCloseConnection(MockHttpTest):
+
+ def test_close_none(self):
+ c.http_connection = self.fake_http_connection(200)
+ conn = c.Connection('http://www.test.com', 'asdf', 'asdf')
+ self.assertEqual(conn.http_conn, None)
+ conn.close()
+ self.assertEqual(conn.http_conn, None)
+
+ def test_close_ok(self):
+ url = 'http://www.test.com'
+ c.http_connection = self.fake_http_connection(200)
+ conn = c.Connection(url, 'asdf', 'asdf')
+ self.assertEqual(conn.http_conn, None)
+
+ conn.http_conn = c.http_connection(url)
+ self.assertEqual(type(conn.http_conn), tuple)
+ self.assertEqual(len(conn.http_conn), 2)
+ http_conn_obj = conn.http_conn[1]
+ self.assertEqual(http_conn_obj.isclosed(), False)
+ conn.close()
+ self.assertEqual(http_conn_obj.isclosed(), True)
+ self.assertEqual(conn.http_conn, None)
+
+
+if __name__ == '__main__':
+ testtools.main()