Fix KeyError raised from client Connection

Some client.Connection methods will raise a KeyError
if a response_dict argument is passed and an error
occurs during authentication or making the request.

The fix is straightforward: add a test for existence
of a response_dict before attempting to get it from
kwargs.

The bulk of this patch is adding unit tests for the
response_dict feature.

Closes-Bug: 1381304

Change-Id: Ic7e1b3dfae33909533931c52ac97355867a08a07
This commit is contained in:
Alistair Coles 2014-10-15 18:56:58 +01:00
parent bbe3378ebb
commit 871a3e40f3
3 changed files with 102 additions and 1 deletions

@ -1215,7 +1215,7 @@ class Connection(object):
ssl_compression=self.ssl_compression)
def _add_response_dict(self, target_dict, kwargs):
if target_dict is not None:
if target_dict is not None and 'response_dict' in kwargs:
response_dict = kwargs['response_dict']
if 'response_dicts' in target_dict:
target_dict['response_dicts'].append(response_dict)

@ -1118,6 +1118,104 @@ class TestConnection(MockHttpTest):
c.http_connection = orig_conn
class TestResponseDict(MockHttpTest):
"""
Verify handling of optional response_dict argument.
"""
calls = [('post_container', 'c', {}),
('put_container', 'c'),
('delete_container', 'c'),
('post_object', 'c', 'o', {}),
('put_object', 'c', 'o', 'body'),
('delete_object', 'c', 'o')]
def fake_get_auth(*args, **kwargs):
return 'http://url', 'token'
def test_response_dict_with_auth_error(self):
def bad_get_auth(*args, **kwargs):
raise c.ClientException('test')
for call in self.calls:
resp_dict = {'test': 'should be untouched'}
with mock.patch('swiftclient.client.get_auth',
bad_get_auth):
conn = c.Connection('http://127.0.0.1:8080', 'user', 'key')
self.assertRaises(c.ClientException, getattr(conn, call[0]),
*call[1:], response_dict=resp_dict)
self.assertEqual({'test': 'should be untouched'}, resp_dict)
def test_response_dict_with_request_error(self):
for call in self.calls:
resp_dict = {'test': 'should be untouched'}
with mock.patch('swiftclient.client.get_auth',
self.fake_get_auth):
exc = c.ClientException('test')
with mock.patch('swiftclient.client.http_connection',
self.fake_http_connection(200, exc=exc)):
conn = c.Connection('http://127.0.0.1:8080', 'user', 'key')
self.assertRaises(c.ClientException,
getattr(conn, call[0]),
*call[1:],
response_dict=resp_dict)
self.assertTrue('test' in resp_dict)
self.assertEqual('should be untouched', resp_dict['test'])
self.assertTrue('response_dicts' in resp_dict)
self.assertEqual([{}], resp_dict['response_dicts'])
def test_response_dict(self):
# test response_dict is populated and
# new list of response_dicts is created
for call in self.calls:
resp_dict = {'test': 'should be untouched'}
with mock.patch('swiftclient.client.get_auth',
self.fake_get_auth):
with mock.patch('swiftclient.client.http_connection',
self.fake_http_connection(200)):
conn = c.Connection('http://127.0.0.1:8080', 'user', 'key')
getattr(conn, call[0])(*call[1:], response_dict=resp_dict)
for key in ('test', 'status', 'headers', 'reason',
'response_dicts'):
self.assertTrue(key in resp_dict)
self.assertEqual('should be untouched', resp_dict.pop('test'))
self.assertEqual('Fake', resp_dict['reason'])
self.assertEqual(200, resp_dict['status'])
self.assertTrue('x-works' in resp_dict['headers'])
self.assertEqual('yes', resp_dict['headers']['x-works'])
children = resp_dict.pop('response_dicts')
self.assertEqual(1, len(children))
self.assertEqual(resp_dict, children[0])
def test_response_dict_with_existing(self):
# check response_dict is populated and new dict is appended
# to existing response_dicts list
for call in self.calls:
resp_dict = {'test': 'should be untouched',
'response_dicts': [{'existing': 'response dict'}]}
with mock.patch('swiftclient.client.get_auth',
self.fake_get_auth):
with mock.patch('swiftclient.client.http_connection',
self.fake_http_connection(200)):
conn = c.Connection('http://127.0.0.1:8080', 'user', 'key')
getattr(conn, call[0])(*call[1:], response_dict=resp_dict)
for key in ('test', 'status', 'headers', 'reason',
'response_dicts'):
self.assertTrue(key in resp_dict)
self.assertEqual('should be untouched', resp_dict.pop('test'))
self.assertEqual('Fake', resp_dict['reason'])
self.assertEqual(200, resp_dict['status'])
self.assertTrue('x-works' in resp_dict['headers'])
self.assertEqual('yes', resp_dict['headers']['x-works'])
children = resp_dict.pop('response_dicts')
self.assertEqual(2, len(children))
self.assertEqual({'existing': 'response dict'}, children[0])
self.assertEqual(resp_dict, children[1])
class TestLogging(MockHttpTest):
"""
Make sure all the lines in http_log are covered.

@ -172,6 +172,7 @@ class MockHttpTest(testtools.TestCase):
query_string = kwargs.get('query_string')
storage_url = kwargs.get('storage_url')
auth_token = kwargs.get('auth_token')
exc = kwargs.get('exc')
def wrapper(url, proxy=None, cacert=None, insecure=False,
ssl_compression=True):
@ -192,6 +193,8 @@ class MockHttpTest(testtools.TestCase):
if url.endswith('invalid_cert') and not insecure:
from swiftclient import client as c
raise c.ClientException("invalid_certificate")
elif exc:
raise exc
return
conn.request = request