- This allows us to delegate all 2.0 authentication directly to the library without reimplementing ourselves. - Support reusing a token / storage-url without re-authenticating every time via the switch os_storage_url os_auth_token. - Allow auth via tenant_id instead of just tenant_name via the switch os_tenant_id. - Refactor a bit to make it easier in the future to add new OS features (i.e: region). - Implements blueprint use-keystoneclient-for-swiftclient. - Fixes bug 1016641. Change-Id: I532f38a68af884de25326aaac05a2050f5ffa1c7
		
			
				
	
	
		
			532 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			532 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
# 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 socket
 | 
						|
import unittest
 | 
						|
from urlparse import urlparse
 | 
						|
 | 
						|
# 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
 | 
						|
 | 
						|
 | 
						|
class TestClientException(unittest.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(unittest.TestCase):
 | 
						|
 | 
						|
    def tearDown(self):
 | 
						|
        try:
 | 
						|
            import json
 | 
						|
        except ImportError:
 | 
						|
            pass
 | 
						|
        else:
 | 
						|
            reload(json)
 | 
						|
 | 
						|
        try:
 | 
						|
            import simplejson
 | 
						|
        except ImportError:
 | 
						|
            pass
 | 
						|
        else:
 | 
						|
            reload(simplejson)
 | 
						|
 | 
						|
    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(c)
 | 
						|
 | 
						|
        try:
 | 
						|
            from json import loads
 | 
						|
        except ImportError:
 | 
						|
            # this case is stested in _no_json
 | 
						|
            pass
 | 
						|
        else:
 | 
						|
            self.assertEquals(loads, c.json_loads)
 | 
						|
 | 
						|
 | 
						|
class MockHttpTest(unittest.TestCase):
 | 
						|
 | 
						|
    def setUp(self):
 | 
						|
        def fake_http_connection(*args, **kwargs):
 | 
						|
            _orig_http_connection = c.http_connection
 | 
						|
            return_read = kwargs.get('return_read')
 | 
						|
 | 
						|
            def wrapper(url, proxy=None):
 | 
						|
                parsed, _conn = _orig_http_connection(url, proxy=proxy)
 | 
						|
                conn = fake_http_connect(*args, **kwargs)()
 | 
						|
 | 
						|
                def request(*args, **kwargs):
 | 
						|
                    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):
 | 
						|
        reload(c)
 | 
						|
 | 
						|
 | 
						|
class TestHttpHelpers(MockHttpTest):
 | 
						|
 | 
						|
    def test_quote(self):
 | 
						|
        value = 'standard string'
 | 
						|
        self.assertEquals('standard%20string', c.quote(value))
 | 
						|
        value = u'\u0075nicode string'
 | 
						|
        self.assertEquals('unicode%20string', 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.HTTPSConnection))
 | 
						|
        url = 'ftp://www.test.com'
 | 
						|
        self.assertRaises(c.ClientException, c.http_connection, url)
 | 
						|
 | 
						|
    def test_json_request(self):
 | 
						|
        def read(*args, **kwargs):
 | 
						|
            body = {'a': '1',
 | 
						|
                    'b': '2'}
 | 
						|
            return c.json_dumps(body)
 | 
						|
        c.http_connection = self.fake_http_connection(200, return_read=read)
 | 
						|
        url = 'http://www.test.com'
 | 
						|
        _junk, conn = c.json_request('GET', url, body={'username': 'user1',
 | 
						|
                                                       'password': 'secure'})
 | 
						|
        self.assertTrue(type(conn) is dict)
 | 
						|
 | 
						|
# 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.assertEquals(url, None)
 | 
						|
        self.assertEquals(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)
 | 
						|
        url, token = c.get_auth('http://www.test.com', 'asdf', 'asdf',
 | 
						|
                                auth_version="1.0")
 | 
						|
        self.assertEquals(url, None)
 | 
						|
        self.assertEquals(token, None)
 | 
						|
 | 
						|
    def test_auth_v2(self):
 | 
						|
        c.get_keystoneclient_2_0 = fake_get_keystoneclient_2_0
 | 
						|
        url, token = c.get_auth('http://www.test.com', 'asdf', 'asdf',
 | 
						|
                                os_options={'tenant_name': 'asdf'},
 | 
						|
                                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):
 | 
						|
        c.get_keystoneclient_2_0 = fake_get_keystoneclient_2_0
 | 
						|
        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):
 | 
						|
        c.get_keystoneclient_2_0 = fake_get_keystoneclient_2_0
 | 
						|
        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)
 | 
						|
 | 
						|
 | 
						|
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.assertEquals(value, [])
 | 
						|
 | 
						|
 | 
						|
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.assertEquals(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:
 | 
						|
            value = c.head_account('http://www.tests.com', 'asdf')
 | 
						|
        except c.ClientException as e:
 | 
						|
            new_body = "[first 60 chars of response] " + body[0:60]
 | 
						|
            self.assertEquals(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.assertEquals(value, [])
 | 
						|
 | 
						|
 | 
						|
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:
 | 
						|
            value = c.head_container('http://www.test.com', 'asdf', 'asdf')
 | 
						|
        except c.ClientException as e:
 | 
						|
            self.assertEquals(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.assertEquals(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:
 | 
						|
            value = c.put_container('http://www.test.com', 'asdf', 'asdf')
 | 
						|
        except c.ClientException as e:
 | 
						|
            self.assertEquals(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.assertEquals(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')
 | 
						|
 | 
						|
 | 
						|
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, basestring))
 | 
						|
 | 
						|
    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:
 | 
						|
            value = c.put_object(*args)
 | 
						|
        except c.ClientException as e:
 | 
						|
            self.assertEquals(e.http_response_content, body)
 | 
						|
 | 
						|
 | 
						|
class TestPostObject(MockHttpTest):
 | 
						|
 | 
						|
    def test_ok(self):
 | 
						|
        c.http_connection = self.fake_http_connection(200)
 | 
						|
        args = ('http://www.test.com', 'asdf', 'asdf', 'asdf', {})
 | 
						|
        value = c.post_object(*args)
 | 
						|
 | 
						|
    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:
 | 
						|
            value = c.post_object(*args)
 | 
						|
        except c.ClientException as e:
 | 
						|
            self.assertEquals(e.http_response_content, body)
 | 
						|
 | 
						|
 | 
						|
class TestDeleteObject(MockHttpTest):
 | 
						|
 | 
						|
    def test_ok(self):
 | 
						|
        c.http_connection = self.fake_http_connection(200)
 | 
						|
        value = 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')
 | 
						|
 | 
						|
 | 
						|
class TestConnection(MockHttpTest):
 | 
						|
 | 
						|
    def test_instance(self):
 | 
						|
        conn = c.Connection('http://www.test.com', 'asdf', 'asdf')
 | 
						|
        self.assertEquals(conn.retries, 5)
 | 
						|
 | 
						|
    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.assertEquals(conn.attempts, conn.retries + 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, 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.assertEquals(conn.attempts, 0)
 | 
						|
        self.assertEquals(conn.url, 'http://www.old.com')
 | 
						|
        self.assertEquals(conn.token, 'old')
 | 
						|
 | 
						|
        value = conn.head_account()
 | 
						|
 | 
						|
        self.assertTrue(self.swap_sleep_called)
 | 
						|
        self.assertEquals(conn.attempts, 2)
 | 
						|
        self.assertEquals(conn.url, 'http://www.new.com')
 | 
						|
        self.assertEquals(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):
 | 
						|
                return
 | 
						|
 | 
						|
            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 read(self, *args, **kwargs):
 | 
						|
                return ''
 | 
						|
 | 
						|
        def local_http_connection(url, proxy=None):
 | 
						|
            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, err:
 | 
						|
                exc = err
 | 
						|
            self.assertEquals(contents.seeks, [0])
 | 
						|
            self.assertEquals(str(exc), 'oops')
 | 
						|
 | 
						|
            contents = LocalContents(tell_value=123)
 | 
						|
            exc = None
 | 
						|
            try:
 | 
						|
                conn.put_object('c', 'o', contents)
 | 
						|
            except socket.error, err:
 | 
						|
                exc = err
 | 
						|
            self.assertEquals(contents.seeks, [123])
 | 
						|
            self.assertEquals(str(exc), 'oops')
 | 
						|
 | 
						|
            contents = LocalContents()
 | 
						|
            contents.tell = None
 | 
						|
            exc = None
 | 
						|
            try:
 | 
						|
                conn.put_object('c', 'o', contents)
 | 
						|
            except c.ClientException, err:
 | 
						|
                exc = err
 | 
						|
            self.assertEquals(contents.seeks, [])
 | 
						|
            self.assertEquals(str(exc), "put_object('c', 'o', ...) failure "
 | 
						|
                "and no ability to reset contents for reupload.")
 | 
						|
        finally:
 | 
						|
            c.http_connection = orig_conn
 | 
						|
 | 
						|
 | 
						|
if __name__ == '__main__':
 | 
						|
    unittest.main()
 |