"""
Copyright 2015 Rackspace

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 calendar
import time
import zlib
from hashlib import md5
import unittest

from cafe.drivers.unittest.decorators import (
    DataDrivenFixture, data_driven_test)
from cloudcafe.objectstorage.objectstorage_api.common.constants import \
    Constants
from cloudroast.objectstorage.fixtures import ObjectStorageFixture
from cloudroast.objectstorage.generators import (
    ObjectDatasetList, CONTENT_TYPES)

CONTAINER_DESCRIPTOR = 'object_smoke_test'
STATUS_CODE_MSG = ('{method} expected status code {expected}'
                   ' received status code {received}')


@DataDrivenFixture
class ObjectSmokeTest(ObjectStorageFixture):
    @classmethod
    def setUpClass(cls):
        super(ObjectSmokeTest, cls).setUpClass()
        cls.default_obj_name = Constants.VALID_OBJECT_NAME_WITH_UNICODE

    @staticmethod
    def generate_chunk_data():
        for i in range(10):
            yield "Test chunk %s\r\n" % i

    @data_driven_test(ObjectDatasetList())
    def ddtest_object_retrieval_with_valid_object_name(
            self, object_type, generate_object):
        container_name = self.create_temp_container(
            descriptor=CONTAINER_DESCRIPTOR)
        object_name = self.default_obj_name
        generate_object(container_name, object_name)

        response = self.client.get_object(container_name, object_name)

        method = 'object creation with valid object name'
        expected = 200
        received = response.status_code

        self.assertEqual(
            expected,
            received,
            msg=STATUS_CODE_MSG.format(
                method=method,
                expected=expected,
                received=str(received)))

    @data_driven_test(ObjectDatasetList(exclude=['dlo', 'slo']))
    def ddtest_object_retrieval_with_if_match(
            self, object_type, generate_object):
        """
        Bug filed for dlo/slo support of If-match Header:
        https://bugs.launchpad.net/swift/+bug/1279076
        """
        container_name = self.create_temp_container(
            descriptor=CONTAINER_DESCRIPTOR)
        object_name = self.default_obj_name
        obj_info = generate_object(container_name, object_name)

        headers = {'If-Match': obj_info.get('etag')}

        response = self.client.get_object(
            container_name,
            self.default_obj_name,
            headers=headers)

        method = 'object retrieval with if match header'
        expected = 200
        received = response.status_code

        self.assertEqual(
            expected,
            received,
            msg=STATUS_CODE_MSG.format(
                method=method,
                expected=expected,
                received=str(received)))

    @data_driven_test(ObjectDatasetList(exclude=['dlo', 'slo']))
    def ddtest_object_retrieval_with_if_none_match(
            self, object_type, generate_object):
        """
        Bug filed for dlo/slo support of If-match Header:
        https://bugs.launchpad.net/swift/+bug/1279076
        """
        container_name = self.create_temp_container(
            descriptor=CONTAINER_DESCRIPTOR)
        object_name = self.default_obj_name
        object_info = generate_object(container_name, object_name)

        headers = {'If-None-Match': 'grok'}

        response = self.client.get_object(
            container_name,
            self.default_obj_name,
            headers=headers)

        method = 'object retrieval with if none match header'
        expected = 200
        received = response.status_code

        self.assertEqual(
            expected,
            received,
            msg=STATUS_CODE_MSG.format(
                method=method,
                expected=expected,
                received=str(received)))

        headers = {'If-None-Match': object_info.get('etag')}

        response = self.client.get_object(
            container_name,
            self.default_obj_name,
            headers=headers)

        method = 'object should be flagged as not modified'
        expected = 304
        received = response.status_code

        self.assertEqual(
            expected,
            received,
            msg=STATUS_CODE_MSG.format(
                method=method,
                expected=expected,
                received=str(received)))

    @data_driven_test(ObjectDatasetList())
    def ddtest_object_retrieval_with_if_modified_since(
            self, object_type, generate_object):
        container_name = self.create_temp_container(
            descriptor=CONTAINER_DESCRIPTOR)
        object_name = self.default_obj_name
        generate_object(container_name, object_name)

        headers = {'If-Modified-Since': 'Fri, 17 Aug 2001 18:44:42 GMT'}

        response = self.client.get_object(
            container_name,
            self.default_obj_name,
            headers=headers)

        method = 'object retrieval with if modified since header (past date)'
        expected = 200
        received = response.status_code

        self.assertEqual(
            expected,
            received,
            msg=STATUS_CODE_MSG.format(
                method=method,
                expected=expected,
                received=str(received)))

    @data_driven_test(ObjectDatasetList())
    def ddtest_object_not_modified_with_if_modified_since(
            self, object_type, generate_object):
        container_name = self.create_temp_container(
            descriptor=CONTAINER_DESCRIPTOR)
        object_name = self.default_obj_name
        generate_object(container_name, object_name)

        headers = {'If-Modified-Since': 'Fri, 17 Aug 2101 18:44:42 GMT'}

        response = self.client.get_object(
            container_name,
            self.default_obj_name,
            headers=headers)

        method = 'object retrieval with if modified since header (future date)'
        expected = 304
        received = response.status_code

        self.assertEqual(
            expected,
            received,
            msg=STATUS_CODE_MSG.format(
                method=method,
                expected=expected,
                received=str(received)))

    @data_driven_test(ObjectDatasetList())
    def ddtest_object_retrieval_with_if_unmodified_since(
            self, object_type, generate_object):
        container_name = self.create_temp_container(
            descriptor=CONTAINER_DESCRIPTOR)
        object_name = self.default_obj_name
        generate_object(container_name, object_name)

        headers = {'If-Unmodified-Since': 'Fri, 17 Aug 2101 18:44:42 GMT'}

        response = self.client.get_object(
            container_name,
            self.default_obj_name,
            headers=headers)

        method = 'object retrieval with if unmodified since header'
        expected = 200
        received = response.status_code

        self.assertEqual(
            expected,
            received,
            msg=STATUS_CODE_MSG.format(
                method=method,
                expected=expected,
                received=str(received)))

    @data_driven_test(ObjectDatasetList())
    def ddtest_object_retrieval_fails_with_if_unmodified_since(
            self, object_type, generate_object):
        container_name = self.create_temp_container(
            descriptor=CONTAINER_DESCRIPTOR)
        object_name = self.default_obj_name
        generate_object(container_name, object_name)

        headers = {'If-Unmodified-Since': 'Fri, 17 Aug 2001 18:44:42 GMT'}

        response = self.client.get_object(
            container_name,
            self.default_obj_name,
            headers=headers)

        method = ('object retrieval precondition fail with if unmodified'
                  ' since header')
        expected = 412
        received = response.status_code

        self.assertEqual(
            expected,
            received,
            msg=STATUS_CODE_MSG.format(
                method=method,
                expected=expected,
                received=str(received)))

    @data_driven_test(ObjectDatasetList())
    def ddtest_partial_object_retrieval_with_start_range(
            self, object_type, generate_object):
        container_name = self.create_temp_container(
            descriptor=CONTAINER_DESCRIPTOR)
        object_name = self.default_obj_name
        generate_object(container_name, object_name)

        headers = {'Range': 'bytes=5-'}

        response = self.client.get_object(
            container_name,
            self.default_obj_name,
            headers=headers)

        method = 'partial object retrieval with start range'
        expected = 206
        received = response.status_code

        self.assertEqual(
            expected,
            received,
            msg=STATUS_CODE_MSG.format(
                method=method, expected=expected, received=str(received)))

    @data_driven_test(ObjectDatasetList())
    def ddtest_partial_object_retrieval_with_end_range(
            self, object_type, generate_object):
        container_name = self.create_temp_container(
            descriptor=CONTAINER_DESCRIPTOR)
        object_name = self.default_obj_name
        generate_object(container_name, object_name)

        headers = {'Range': 'bytes=-4'}

        response = self.client.get_object(
            container_name,
            self.default_obj_name,
            headers=headers)

        method = 'partial object retrieval with end range'
        expected = 206
        received = response.status_code

        self.assertEqual(
            expected,
            received,
            msg=STATUS_CODE_MSG.format(
                method=method,
                expected=expected,
                received=str(received)))

    @data_driven_test(ObjectDatasetList())
    def ddtest_partial_object_retrieval_with_range(
            self, object_type, generate_object):
        container_name = self.create_temp_container(
            descriptor=CONTAINER_DESCRIPTOR)
        object_name = self.default_obj_name
        generate_object(container_name, object_name)

        headers = {'Range': 'bytes=5-8'}

        response = self.client.get_object(
            container_name,
            self.default_obj_name,
            headers=headers)

        method = 'partial object retrieval with start and end range'
        expected = 206
        received = response.status_code

        self.assertEqual(
            expected,
            received,
            msg=STATUS_CODE_MSG.format(
                method=method,
                expected=expected,
                received=str(received)))

    @data_driven_test(ObjectDatasetList())
    def ddtest_partial_object_retrieval_with_complete_range(
            self, object_type, generate_object):
        container_name = self.create_temp_container(
            descriptor=CONTAINER_DESCRIPTOR)
        object_name = self.default_obj_name
        generate_object(container_name, object_name)

        headers = {'Range': 'bytes=99-0'}

        response = self.client.get_object(
            container_name,
            self.default_obj_name,
            headers=headers)

        method = 'partial object retrieval with complete range'
        expected = 200
        received = response.status_code

        self.assertEqual(
            expected,
            received,
            msg=STATUS_CODE_MSG.format(
                method=method,
                expected=expected,
                received=str(received)))

    @data_driven_test(ObjectDatasetList())
    def ddtest_object_creation_with_valid_object_name(
            self, object_type, generate_object):
        container_name = self.create_temp_container(
            descriptor=CONTAINER_DESCRIPTOR)
        object_name = self.default_obj_name
        object_info = generate_object(container_name, object_name)

        response = object_info.get('response')
        method = 'object creation with valid object name'
        expected = 201
        received = response.status_code

        self.assertEqual(
            expected,
            received,
            msg=STATUS_CODE_MSG.format(
                method=method,
                expected=expected,
                received=str(received)))

        response = self.client.get_object(
            container_name,
            self.default_obj_name)

        method = 'object retrieval'
        expected = 200
        received = response.status_code

        self.assertEqual(
            expected,
            received,
            msg=STATUS_CODE_MSG.format(
                method=method,
                expected=expected,
                received=str(received)))

        response_md5 = md5(response.content).hexdigest()
        self.assertEqual(
            object_info.get('md5'),
            response_md5,
            msg='should return identical object')

    @data_driven_test(ObjectDatasetList(exclude=['dlo', 'slo']))
    def ddtest_object_update_with_valid_object_name(
            self, object_type, generate_object):
        container_name = self.create_temp_container(
            descriptor=CONTAINER_DESCRIPTOR)
        object_name = self.default_obj_name
        generate_object(container_name, object_name)

        updated_object_data = 'Updated test file data'
        updated_content_length = str(len(updated_object_data))
        headers = {'Content-Length': updated_content_length,
                   'Content-Type': CONTENT_TYPES.get('text')}
        response = self.client.create_object(
            container_name,
            self.default_obj_name,
            headers=headers,
            data=updated_object_data)

        method = 'object update with valid object name'
        expected = 201
        received = response.status_code

        self.assertEqual(
            expected,
            received,
            msg=STATUS_CODE_MSG.format(
                method=method,
                expected=expected,
                received=str(received)))

    @data_driven_test(ObjectDatasetList())
    def ddtest_object_creation_with_etag(
            self, object_type, generate_object):
        container_name = self.create_temp_container(
            descriptor=CONTAINER_DESCRIPTOR)
        object_name = self.default_obj_name
        object_info = generate_object(container_name, object_name)

        response = object_info.get('response')
        method = 'object creation with etag header'
        expected = 201
        received = response.status_code

        self.assertEqual(
            expected,
            received,
            msg=STATUS_CODE_MSG.format(
                method=method,
                expected=expected,
                received=str(received)))

        response = self.client.get_object_metadata(
            container_name,
            self.default_obj_name)

        response = self.client.get_object(
            container_name,
            self.default_obj_name)
        self.assertIn(
            'etag',
            response.headers,
            msg="Etag header was set")

        if object_type == 'standard':
            expected = object_info.get('etag')
        else:
            expected = '"{0}"'.format(object_info.get('etag'))
        received = response.headers.get('etag')

        self.assertEqual(
            expected,
            received,
            msg='object created with Etag header'
                ' value expected: {0} received: {1}'.format(
                    expected,
                    received))

    @data_driven_test(ObjectDatasetList(exclude=['dlo', 'slo']))
    def test_object_creation_with_uppercase_etag(self):

        container_name = self.create_temp_container(
            descriptor=CONTAINER_DESCRIPTOR)
        object_name = self.default_obj_name
        object_data = "valid_data"
        data_md5 = md5(object_data).hexdigest()
        upper_etag = data_md5.upper()

        headers = {"ETag": upper_etag}
        create_response = self.client.create_object(container_name,
                                                    object_name,
                                                    data=object_data,
                                                    headers=headers)

        method = 'object creation with uppercase etag header'
        expected = 201
        received = create_response.status_code

        self.assertEqual(
            expected,
            received,
            msg=STATUS_CODE_MSG.format(
                method=method,
                expected=expected,
                received=str(received)))

        object_response = self.client.get_object(
            container_name,
            self.default_obj_name)
        self.assertIn(
            'etag',
            object_response.headers,
            msg="Etag header was set")

        expected = data_md5
        received = object_response.headers.get('etag')

        self.assertEqual(
            expected,
            received,
            msg='object created with Etag header'
                ' value expected: {0} received: {1}'.format(
                    expected,
                    received))

    @data_driven_test(ObjectDatasetList())
    @ObjectStorageFixture.required_features('object-cors')
    def ddtest_object_creation_with_access_control_allow_credentials(
            self, object_type, generate_object):
        container_name = self.create_temp_container(
            descriptor=CONTAINER_DESCRIPTOR)
        object_name = self.default_obj_name
        object_headers = {'Access-Control-Allow-Credentials': 'true'}
        object_info = generate_object(container_name, object_name,
                                      headers=object_headers)

        response = object_info.get('response')
        method = 'object creation with Access-Control-Allow-Credentials header'
        expected = 201
        received = response.status_code

        self.assertEqual(
            expected,
            received,
            msg=STATUS_CODE_MSG.format(
                method=method,
                expected=expected,
                received=str(received)))

        response = self.client.get_object_metadata(
            container_name,
            self.default_obj_name)

        self.assertIn(
            'Access-Control-Allow-Credentials',
            response.headers,
            msg="Access-Control-Allow-Credentials header was set")

        expected = 'true'
        received = response.headers.get('Access-Control-Allow-Credentials')

        self.assertEqual(
            expected,
            received,
            msg='object created with Access-Control-Allow-Credentials header'
                ' value expected: {0} received: {1}'.format(
                    expected,
                    received))

    @data_driven_test(ObjectDatasetList())
    @ObjectStorageFixture.required_features('object-cors')
    def ddtest_object_creation_with_access_control_allow_methods(
            self, object_type, generate_object):
        container_name = self.create_temp_container(
            descriptor=CONTAINER_DESCRIPTOR)
        object_name = self.default_obj_name
        object_headers = {
            'Access-Control-Allow-Methods': 'GET, POST, OPTIONS'}
        object_info = generate_object(container_name, object_name,
                                      headers=object_headers)

        response = object_info.get('response')
        method = 'object creation with Access-Control-Allow-Methods header'
        expected = 201
        received = response.status_code

        self.assertEqual(
            expected,
            received,
            msg=STATUS_CODE_MSG.format(
                method=method,
                expected=expected,
                received=str(received)))

        response = self.client.get_object_metadata(
            container_name,
            self.default_obj_name)

        self.assertIn(
            'Access-Control-Allow-Methods',
            response.headers,
            msg="Access-Control-Allow-Methods header was set")

        expected = 'GET, POST, OPTIONS'
        received = response.headers.get('Access-Control-Allow-Methods')

        self.assertEqual(
            expected,
            received,
            msg='object created with Access-Control-Allow-Methods header'
                ' value expected: {0} received: {1}'.format(
                    expected,
                    received))

    @data_driven_test(ObjectDatasetList())
    @ObjectStorageFixture.required_features('object-cors')
    def ddtest_object_creation_with_access_control_allow_origin(
            self, object_type, generate_object):
        container_name = self.create_temp_container(
            descriptor=CONTAINER_DESCRIPTOR)
        object_name = self.default_obj_name
        object_headers = {
            'Access-Control-Allow-Origin': 'http://example.com'}
        object_info = generate_object(container_name, object_name,
                                      headers=object_headers)

        response = object_info.get('response')
        method = 'object creation with Access-Control-Allow-Origin header'
        expected = 201
        received = response.status_code

        self.assertEqual(
            expected,
            received,
            msg=STATUS_CODE_MSG.format(
                method=method,
                expected=expected,
                received=str(received)))

        response = self.client.get_object_metadata(
            container_name, self.default_obj_name)

        self.assertIn(
            'Access-Control-Allow-Origin',
            response.headers,
            msg="Access-Control-Allow-Origin header was set")

        expected = 'http://example.com'
        received = response.headers.get('Access-Control-Allow-Origin')

        self.assertEqual(
            expected,
            received,
            msg='object created with Access-Control-Allow-Origin header'
                ' value expected: {0} received: {1}'.format(
                    expected,
                    received))

    @data_driven_test(ObjectDatasetList())
    @ObjectStorageFixture.required_features('object-cors')
    def ddtest_object_creation_with_access_control_expose_headers(
            self, object_type, generate_object):
        container_name = self.create_temp_container(
            descriptor=CONTAINER_DESCRIPTOR)
        object_name = self.default_obj_name
        object_headers = {'Access-Control-Expose-Headers': 'X-Foo-Header'}
        object_info = generate_object(container_name, object_name,
                                      headers=object_headers)

        response = object_info.get('response')
        method = 'object creation with Access-Control-Expose-Headers header'
        expected = 201
        received = response.status_code

        self.assertEqual(
            expected,
            received,
            msg=STATUS_CODE_MSG.format(
                method=method,
                expected=expected,
                received=str(received)))

        response = self.client.get_object_metadata(
            container_name,
            self.default_obj_name)

        self.assertIn(
            'Access-Control-Expose-Headers',
            response.headers,
            msg="Access-Control-Expose-Headers header was set")

        expected = 'X-Foo-Header'
        received = response.headers.get('Access-Control-Expose-Headers')

        self.assertEqual(
            expected,
            received,
            msg='object created with Access-Control-Expose-Headers header'
                ' value expected: {0} received: {1}'.format(
                    expected,
                    received))

    @data_driven_test(ObjectDatasetList())
    @ObjectStorageFixture.required_features('object-cors')
    def ddtest_object_creation_with_access_controle_max_age(
            self, object_type, generate_object):
        container_name = self.create_temp_container(
            descriptor=CONTAINER_DESCRIPTOR)
        object_name = self.default_obj_name
        object_headers = {'Access-Control-Max-Age': '5'}
        object_info = generate_object(container_name, object_name,
                                      headers=object_headers)

        response = object_info.get('response')
        method = 'object creation with Access-Control-Max-Age header'
        expected = 201
        received = response.status_code

        self.assertEqual(
            expected,
            received,
            msg=STATUS_CODE_MSG.format(
                method=method,
                expected=expected,
                received=str(received)))

        response = self.client.get_object_metadata(
            container_name,
            self.default_obj_name)

        self.assertIn(
            'Access-Control-Max-Age',
            response.headers,
            msg="Access-Control-Max-Age header was set")

        expected = '5'
        received = response.headers.get('Access-Control-Max-Age')

        self.assertEqual(
            expected,
            received,
            msg='object created with Access-Control-Max-Age header'
                ' value expected: {0} received: {1}'.format(
                    expected,
                    received))

    @data_driven_test(ObjectDatasetList())
    @ObjectStorageFixture.required_features('object-cors')
    def ddtest_object_creation_with_access_control_request_headers(
            self, object_type, generate_object):
        container_name = self.create_temp_container(
            descriptor=CONTAINER_DESCRIPTOR)
        object_name = self.default_obj_name
        object_headers = {'Access-Control-Request-Headers': 'x-requested-with'}
        object_info = generate_object(container_name, object_name,
                                      headers=object_headers)

        response = object_info.get('response')
        method = 'object creation with Access-Control-Request-Headers header'
        expected = 201
        received = response.status_code

        self.assertEqual(
            expected,
            received,
            msg=STATUS_CODE_MSG.format(
                method=method,
                expected=expected,
                received=str(received)))

        response = self.client.get_object_metadata(
            container_name,
            self.default_obj_name)

        self.assertIn(
            'Access-Control-Request-Headers',
            response.headers,
            msg="Access-Control-Request-Headers header was set")

        expected = 'x-requested-with'
        received = response.headers.get('Access-Control-Request-Headers')

        self.assertEqual(
            expected,
            received,
            msg='object created with Access-Control-Request-Headers header'
                ' value expected: {0} received: {1}'.format(
                    expected,
                    received))

    @data_driven_test(ObjectDatasetList())
    @ObjectStorageFixture.required_features('object-cors')
    def ddtest_object_creation_with_access_control_request_method(
            self, object_type, generate_object):
        container_name = self.create_temp_container(
            descriptor=CONTAINER_DESCRIPTOR)
        object_name = self.default_obj_name
        object_headers = {'Access-Control-Request-Method': 'GET'}
        object_info = generate_object(container_name, object_name,
                                      headers=object_headers)

        response = object_info.get('response')
        method = 'object creation with Access-Control-Request-Method header'
        expected = 201
        received = response.status_code

        self.assertEqual(
            expected,
            received,
            msg=STATUS_CODE_MSG.format(
                method=method,
                expected=expected,
                received=str(received)))

        response = self.client.get_object_metadata(
            container_name,
            self.default_obj_name)

        self.assertIn(
            'Access-Control-Request-Method',
            response.headers,
            msg="Access-Control-Request-Method header was set")

        expected = 'GET'
        received = response.headers.get('Access-Control-Request-Method')

        self.assertEqual(
            expected,
            received,
            msg='object created with Access-Control-Request-Method header'
                ' value expected: {0} received: {1}'.format(
                    expected,
                    received))

    @data_driven_test(ObjectDatasetList())
    @ObjectStorageFixture.required_features('object-cors')
    def ddtest_object_retrieval_with_origin(
            self, object_type, generate_object):
        container_name = self.create_temp_container(
            descriptor=CONTAINER_DESCRIPTOR)
        object_name = self.default_obj_name
        headers = {'access-control-allow-origin': 'http://example.com',
                   'access-control-expose-headers': 'X-Trans-Id'}
        generate_object(container_name, object_name, headers=headers)

        headers = {'Origin': 'http://example.com'}
        response = self.client.get_object_metadata(
            container_name, object_name, headers=headers)

        self.assertIn(
            'access-control-expose-headers',
            response.headers,
            msg="access-control-expose-headers header should be set")

        self.assertIn(
            'access-control-allow-origin',
            response.headers,
            msg="access-control-allow-origin header should be set")

        expected = 'http://example.com'
        received = response.headers.get('access-control-allow-origin')

        self.assertEqual(
            expected,
            received,
            msg='access-control-allow-origin header should reflect origin'
                ' expected: {0} received: {1}'.format(expected, received))

    @data_driven_test(ObjectDatasetList(exclude=['dlo', 'slo']))
    def ddtest_object_creation_with_file_compression(
            self, object_type, generate_object):
        container_name = self.create_temp_container(
            descriptor=CONTAINER_DESCRIPTOR)
        object_name = self.default_obj_name

        def object_data_op(data, extra_data):
            data = zlib.compress(data)
            return (data, extra_data)

        object_headers = {'Content-Encoding': 'gzip'}
        object_info = generate_object(container_name, object_name,
                                      data_op=object_data_op,
                                      headers=object_headers)

        response = object_info.get('response')
        method = 'object creation with Content-Encoding header'
        expected = 201
        received = response.status_code

        self.assertEqual(
            expected,
            received,
            msg=STATUS_CODE_MSG.format(
                method=method,
                expected=expected,
                received=str(received)))

        response = self.client.get_object_metadata(
            container_name,
            self.default_obj_name)

        self.assertIn(
            'Content-Encoding',
            response.headers,
            msg="Content-Encoding header was set")

        expected = 'gzip'
        received = response.headers.get('Content-Encoding')

        self.assertEqual(
            expected,
            received,
            msg='object created with Content-Encoding header value'
                ' expected: {0} received: {1}'.format(expected, received))

    @data_driven_test(ObjectDatasetList())
    def ddtest_object_creation_with_content_disposition(
            self, object_type, generate_object):
        container_name = self.create_temp_container(
            descriptor=CONTAINER_DESCRIPTOR)
        object_name = self.default_obj_name
        object_headers = {
            'Content-Disposition': 'attachment; filename=testdata.txt'}
        object_info = generate_object(container_name, object_name,
                                      headers=object_headers)

        response = object_info.get('response')
        method = 'object creation with content disposition header'
        expected = 201
        received = response.status_code

        self.assertEqual(
            expected,
            received,
            msg=STATUS_CODE_MSG.format(
                method=method,
                expected=expected,
                received=str(received)))

        response = self.client.get_object_metadata(
            container_name,
            self.default_obj_name)

        self.assertIn(
            'Content-Disposition',
            response.headers,
            msg="Content-Disposition header was set")

        expected = 'attachment; filename=testdata.txt'
        received = response.headers.get('Content-Disposition')

        self.assertEqual(
            expected,
            received,
            msg='object created with Content-Disposition header value'
                ' expected: {0} received: {1}'.format(expected, received))

    @data_driven_test(ObjectDatasetList())
    def ddtest_object_creation_with_x_delete_at(
            self, object_type, generate_object):
        container_name = self.create_temp_container(
            descriptor=CONTAINER_DESCRIPTOR)
        object_name = self.default_obj_name

        start_time = calendar.timegm(time.gmtime())
        future_time = str(int(start_time + 60))
        object_headers = {'X-Delete-At': future_time}
        object_info = generate_object(container_name, object_name,
                                      headers=object_headers)

        response = object_info.get('response')
        method = 'object creation with X-Delete-At header'
        expected = 201
        received = response.status_code

        self.assertEqual(
            expected,
            received,
            msg=STATUS_CODE_MSG.format(
                method=method,
                expected=expected,
                received=str(received)))

        response = self.client.get_object_metadata(
            container_name,
            self.default_obj_name)

        self.assertIn(
            'X-Delete-At',
            response.headers,
            msg="X-Delete-At header was set")

        expected = future_time
        received = response.headers.get('X-Delete-At')

        self.assertEqual(
            expected,
            received,
            msg='object created with X-Delete-At header value'
                ' expected: {0} received: {1}'.format(expected, received))

    @data_driven_test(ObjectDatasetList())
    def ddtest_object_creation_with_delete_after(
            self, object_type, generate_object):
        container_name = self.create_temp_container(
            descriptor=CONTAINER_DESCRIPTOR)
        object_name = self.default_obj_name
        object_headers = {'X-Delete-After': '60'}
        object_info = generate_object(container_name, object_name,
                                      headers=object_headers)

        response = object_info.get('response')
        method = 'object creation with X-Delete-After header'
        expected = 201
        received = response.status_code

        self.assertEqual(
            expected,
            received,
            msg=STATUS_CODE_MSG.format(
                method=method,
                expected=expected,
                received=str(received)))

        response = self.client.get_object_metadata(
            container_name,
            self.default_obj_name)

        self.assertIn(
            'X-Delete-At',
            response.headers,
            msg="X-Delete-At header was set")

    @data_driven_test(ObjectDatasetList())
    @ObjectStorageFixture.required_features('object_versioning')
    def ddtest_versioned_container_creation_with_valid_data(
            self, object_type, generate_object):

        container_name = self.create_temp_container(
            descriptor=CONTAINER_DESCRIPTOR)
        object_history_container_name = self.create_temp_container(
            descriptor=CONTAINER_DESCRIPTOR)

        headers = {'X-Versions-Location': object_history_container_name}
        self.client.set_container_metadata(container_name, headers=headers)

        # list objects in non-current container
        response = self.client.list_objects(
            object_history_container_name)

        method = 'list on empty versioned container'
        expected = 204
        received = response.status_code

        self.assertEqual(
            expected,
            received,
            msg=STATUS_CODE_MSG.format(
                method=method,
                expected=expected,
                received=str(received)))

        # Create an object (version 1)
        object_name = self.default_obj_name
        ver1_info = generate_object(container_name, object_name)

        response = ver1_info.get('response')
        method = 'object version one creation'
        expected = 201
        received = response.status_code

        self.assertEqual(
            expected,
            received,
            msg=STATUS_CODE_MSG.format(
                method=method,
                expected=expected,
                received=str(received)))

        # Update an object (version 2)
        object_name = self.default_obj_name
        ver2_info = generate_object(container_name, object_name)

        response = ver2_info.get('response')
        method = 'update version one object'
        expected = 201
        received = response.status_code

        self.assertEqual(
            expected,
            received,
            msg=STATUS_CODE_MSG.format(
                method=method,
                expected=expected,
                received=str(received)))

        response = self.client.list_objects(object_history_container_name)
        method = 'list on versioned container'
        expected = 200
        received = response.status_code

        self.assertEqual(
            expected,
            received,
            msg=STATUS_CODE_MSG.format(
                method=method,
                expected=expected,
                received=str(received)))

    @unittest.skip('Problem with this tests assertion, needs review')
    @data_driven_test(ObjectDatasetList())
    def ddtest_put_copy_object(self, object_type, generate_object):
        src_container_name = self.create_temp_container(
            descriptor=CONTAINER_DESCRIPTOR)
        dest_container_name = self.create_temp_container(
            descriptor=CONTAINER_DESCRIPTOR)

        src_object_name = '{0}_source'.format(self.default_obj_name)
        generate_object(src_container_name, src_object_name)

        dest_obj_name = '{0}_destination'.format(self.default_obj_name)
        source = '/{0}/{1}'.format(src_container_name, src_object_name)
        hdrs = {'X-Copy-From': source, 'Content-Length': '0'}

        response = self.client.copy_object(
            dest_container_name,
            dest_obj_name,
            headers=hdrs)

        method = 'put copy object'
        expected = 201
        received = response.status_code

        self.assertEqual(
            expected,
            received,
            msg=STATUS_CODE_MSG.format(
                method=method,
                expected=expected,
                received=str(received)))

        response = self.client.get_object(
            dest_container_name,
            dest_obj_name)

        method = 'copied object retrieval'
        expected = 200
        received = response.status_code

        self.assertEqual(
            expected,
            received,
            msg=STATUS_CODE_MSG.format(
                method=method,
                expected=expected,
                received=str(received)))

    @data_driven_test(ObjectDatasetList())
    def ddtest_copy_object(self, object_type, generate_object):
        src_container_name = self.create_temp_container(
            descriptor=CONTAINER_DESCRIPTOR)
        dest_container_name = self.create_temp_container(
            descriptor=CONTAINER_DESCRIPTOR)

        src_object_name = '{0}_source'.format(self.default_obj_name)
        generate_object(src_container_name, src_object_name)

        dest_object_name = '{0}_destination'.format(self.default_obj_name)
        dest = '/{0}/{1}'.format(dest_container_name, dest_object_name)
        headers = {'Destination': dest}

        response = self.client.copy_object(
            src_container_name,
            src_object_name,
            headers=headers)

        method = 'copy object'
        expected = 201
        received = response.status_code

        self.assertEqual(
            expected,
            received,
            msg=STATUS_CODE_MSG.format(
                method=method,
                expected=expected,
                received=str(received)))

        response = self.client.get_object(
            dest_container_name,
            dest_object_name)

        method = 'copied object retrieval'
        expected = 200
        received = response.status_code

        self.assertEqual(
            expected,
            received,
            msg=STATUS_CODE_MSG.format(
                method=method,
                expected=expected,
                received=str(received)))

    @data_driven_test(ObjectDatasetList())
    def ddtest_object_deletion_with_valid_object(
            self, object_type, generate_object):
        container_name = self.create_temp_container(
            descriptor=CONTAINER_DESCRIPTOR)
        object_name = self.default_obj_name
        generate_object(container_name, object_name)

        response = self.client.delete_object(
            container_name,
            object_name)

        method = 'delete object'
        expected = 204
        received = response.status_code

        self.assertEqual(
            expected,
            received,
            msg=STATUS_CODE_MSG.format(
                method=method,
                expected=expected,
                received=str(received)))

        response = self.client.get_object(
            container_name,
            self.default_obj_name)

        method = 'object retrieval'
        expected = 404
        received = response.status_code

        self.assertEqual(
            expected,
            received,
            msg=STATUS_CODE_MSG.format(
                method=method,
                expected=expected,
                received=str(received)))

    @data_driven_test(ObjectDatasetList())
    def ddtest_obj_metadata_update_with_object_possessing_metadata(
            self, object_type, generate_object):
        container_name = self.create_temp_container(
            descriptor=CONTAINER_DESCRIPTOR)
        object_name = self.default_obj_name
        generate_object(container_name, object_name,
                        headers={'X-Object-Meta-Grok': 'Drok'})
        response = self.client.get_object_metadata(
            container_name, object_name)

        self.assertIn(
            'X-Object-Meta-Grok',
            response.headers,
            msg="object not created with X-Object-Meta-Grok header")

        expected = 'Drok'
        received = response.headers.get('X-Object-Meta-Grok')

        self.assertEqual(
            expected,
            received,
            msg='object created with X-Object-Meta-Grok header value'
                ' expected: {0} received: {1}'.format(expected, received))

        headers = {'X-Object-Meta-Foo': 'Bar'}

        response = self.client.set_object_metadata(
            container_name,
            self.default_obj_name,
            headers=headers)

        method = 'set object metadata'
        expected = 202
        received = response.status_code

        self.assertEqual(
            expected,
            received,
            msg=STATUS_CODE_MSG.format(
                method=method,
                expected=expected,
                received=str(received)))

        response = self.client.get_object_metadata(
            container_name,
            self.default_obj_name)

        self.assertIn(
            'X-Object-Meta-Foo',
            response.headers,
            msg="object updated with X-Object-Meta-Foo header")

        expected = 'Bar'
        received = response.headers.get('X-Object-Meta-Foo')

        self.assertEqual(
            expected,
            received,
            msg='object X-Object-Meta-Foo header value expected: {0}'
                ' received: {1}'.format(expected, received))

    @data_driven_test(ObjectDatasetList())
    def ddtest_obj_metadata_update(self, object_type, generate_object):
        container_name = self.create_temp_container(
            descriptor=CONTAINER_DESCRIPTOR)
        object_name = self.default_obj_name
        generate_object(container_name, object_name)

        headers = {'X-Object-Meta-Grok': 'Drok'}
        response = self.client.set_object_metadata(
            container_name, object_name, headers=headers)

        method = 'set object metadata X-Object-Meta-Grok: Drok'
        expected = 202
        received = response.status_code

        self.assertEqual(
            expected,
            received,
            msg=STATUS_CODE_MSG.format(
                method=method,
                expected=expected,
                received=str(received)))

        response = self.client.get_object_metadata(
            container_name,
            self.default_obj_name)

        self.assertIn(
            'X-Object-Meta-Grok',
            response.headers,
            msg="object updated with X-Object-Meta-Grok header")

        expected = 'Drok'
        received = response.headers.get('X-Object-Meta-Grok')

        self.assertEqual(
            expected,
            received,
            msg='object X-Object-Meta-Grok header value expected: {0}'
                ' received: {1}'.format(expected, received))

    @data_driven_test(ObjectDatasetList())
    def ddtest_content_type_not_detected_without_detect_content_type_header(
            self, object_type, generate_object):
        container_name = self.create_temp_container(
            descriptor=CONTAINER_DESCRIPTOR)
        object1_name = 'object1.txt'
        object1_headers = {'Content-Type': 'application/x-www-form-urlencoded'}
        generate_object(container_name, object1_name, headers=object1_headers)

        object2_name = 'object2.txt'
        object2_headers = {'X-Detect-Content-Type': False,
                           'Content-Type': 'application/x-www-form-urlencoded'}
        generate_object(container_name, object2_name, headers=object2_headers)

        response = self.client.get_object(
            container_name, object1_name)

        expected = 'application/x-www-form-urlencoded'
        received = response.headers.get('content-type')

        self.assertEqual(
            expected,
            received,
            msg='object created should have content type: {0}'
                ' received: {1}'.format(expected, received))

        response = self.client.get_object(
            container_name, object2_name)

        self.assertEqual(
            expected,
            received,
            msg='object created should have content type: {0}'
                ' received: {1}'.format(expected, received))

    @data_driven_test(ObjectDatasetList())
    def ddtest_content_type_detected_with_detect_content_type(
            self, object_type, generate_object):
        container_name = self.create_temp_container(
            descriptor=CONTAINER_DESCRIPTOR)
        object1_name = 'object1.txt'
        object1_headers = {'X-Detect-Content-Type': True,
                           'Content-Type': 'application/x-www-form-urlencoded'}
        generate_object(container_name, object1_name, headers=object1_headers)

        response = self.client.get_object(
            container_name, object1_name)

        expected = 'text/plain'
        received = response.headers.get('content-type')

        self.assertEqual(
            expected,
            received,
            msg='object created should have content type: {0}'
                ' received: {1}'.format(expected, received))

        object2_name = 'object2.txt'
        object2_headers = {'X-Detect-Content-Type': True}
        generate_object(container_name, object2_name, headers=object2_headers)

        response = self.client.get_object(
            container_name, object2_name)

        expected = 'text/plain'
        received = response.headers.get('content-type')

        self.assertEqual(
            expected,
            received,
            msg='object created should have content type: {0}'
                ' received: {1}'.format(expected, received))

    def test_object_creation_via_chunked_transfer(self):
        """
        Scenario:
            Create an object using chunked transfer encoding.

        Expected Results:
            Return a 201 status code and a single object should
            be created.
        """
        container_name = self.create_temp_container(
            descriptor=CONTAINER_DESCRIPTOR)

        headers = {"Transfer-Encoding": "chunked"}

        create_response = self.client.create_object(
            container_name,
            self.default_obj_name,
            headers=headers,
            data=self.generate_chunk_data())

        method = 'Object creation via chunked transfer'
        expected = 201
        received = create_response.status_code

        self.assertEqual(
            expected,
            received,
            msg=STATUS_CODE_MSG.format(
                method=method,
                expected=expected,
                received=str(received)))

        object_response = self.client.get_object(container_name,
                                                 self.default_obj_name)

        method = 'Object retrieval'
        expected = 200
        received = object_response.status_code

        self.assertEqual(
            expected,
            received,
            msg=STATUS_CODE_MSG.format(
                method=method,
                expected=expected,
                received=str(received)))

[(533, 'cloudroast.objectstorage.fixtures.ObjectStorageFixture.required_features', 'ObjectStorageFixture.required_features', False, 'from cloudroast.objectstorage.fixtures import ObjectStorageFixture\n'), (577, 'cloudroast.objectstorage.fixtures.ObjectStorageFixture.required_features', 'ObjectStorageFixture.required_features', False, 'from cloudroast.objectstorage.fixtures import ObjectStorageFixture\n'), (622, 'cloudroast.objectstorage.fixtures.ObjectStorageFixture.required_features', 'ObjectStorageFixture.required_features', False, 'from cloudroast.objectstorage.fixtures import ObjectStorageFixture\n'), (666, 'cloudroast.objectstorage.fixtures.ObjectStorageFixture.required_features', 'ObjectStorageFixture.required_features', False, 'from cloudroast.objectstorage.fixtures import ObjectStorageFixture\n'), (710, 'cloudroast.objectstorage.fixtures.ObjectStorageFixture.required_features', 'ObjectStorageFixture.required_features', False, 'from cloudroast.objectstorage.fixtures import ObjectStorageFixture\n'), (754, 'cloudroast.objectstorage.fixtures.ObjectStorageFixture.required_features', 'ObjectStorageFixture.required_features', False, 'from cloudroast.objectstorage.fixtures import ObjectStorageFixture\n'), (798, 'cloudroast.objectstorage.fixtures.ObjectStorageFixture.required_features', 'ObjectStorageFixture.required_features', False, 'from cloudroast.objectstorage.fixtures import ObjectStorageFixture\n'), (842, 'cloudroast.objectstorage.fixtures.ObjectStorageFixture.required_features', 'ObjectStorageFixture.required_features', False, 'from cloudroast.objectstorage.fixtures import ObjectStorageFixture\n'), (1041, 'cloudroast.objectstorage.fixtures.ObjectStorageFixture.required_features', 'ObjectStorageFixture.required_features', False, 'from cloudroast.objectstorage.fixtures import ObjectStorageFixture\n'), (1116, 'unittest.skip', 'unittest.skip', False, 'import unittest\n'), (47, 'cloudroast.objectstorage.generators.ObjectDatasetList', 'ObjectDatasetList', False, 'from cloudroast.objectstorage.generators import ObjectDatasetList, CONTENT_TYPES\n'), (69, 'cloudroast.objectstorage.generators.ObjectDatasetList', 'ObjectDatasetList', False, 'from cloudroast.objectstorage.generators import ObjectDatasetList, CONTENT_TYPES\n'), (100, 'cloudroast.objectstorage.generators.ObjectDatasetList', 'ObjectDatasetList', False, 'from cloudroast.objectstorage.generators import ObjectDatasetList, CONTENT_TYPES\n'), (150, 'cloudroast.objectstorage.generators.ObjectDatasetList', 'ObjectDatasetList', False, 'from cloudroast.objectstorage.generators import ObjectDatasetList, CONTENT_TYPES\n'), (177, 'cloudroast.objectstorage.generators.ObjectDatasetList', 'ObjectDatasetList', False, 'from cloudroast.objectstorage.generators import ObjectDatasetList, CONTENT_TYPES\n'), (204, 'cloudroast.objectstorage.generators.ObjectDatasetList', 'ObjectDatasetList', False, 'from cloudroast.objectstorage.generators import ObjectDatasetList, CONTENT_TYPES\n'), (231, 'cloudroast.objectstorage.generators.ObjectDatasetList', 'ObjectDatasetList', False, 'from cloudroast.objectstorage.generators import ObjectDatasetList, CONTENT_TYPES\n'), (259, 'cloudroast.objectstorage.generators.ObjectDatasetList', 'ObjectDatasetList', False, 'from cloudroast.objectstorage.generators import ObjectDatasetList, CONTENT_TYPES\n'), (284, 'cloudroast.objectstorage.generators.ObjectDatasetList', 'ObjectDatasetList', False, 'from cloudroast.objectstorage.generators import ObjectDatasetList, CONTENT_TYPES\n'), (311, 'cloudroast.objectstorage.generators.ObjectDatasetList', 'ObjectDatasetList', False, 'from cloudroast.objectstorage.generators import ObjectDatasetList, CONTENT_TYPES\n'), (338, 'cloudroast.objectstorage.generators.ObjectDatasetList', 'ObjectDatasetList', False, 'from cloudroast.objectstorage.generators import ObjectDatasetList, CONTENT_TYPES\n'), (365, 'cloudroast.objectstorage.generators.ObjectDatasetList', 'ObjectDatasetList', False, 'from cloudroast.objectstorage.generators import ObjectDatasetList, CONTENT_TYPES\n'), (408, 'cloudroast.objectstorage.generators.ObjectDatasetList', 'ObjectDatasetList', False, 'from cloudroast.objectstorage.generators import ObjectDatasetList, CONTENT_TYPES\n'), (438, 'cloudroast.objectstorage.generators.ObjectDatasetList', 'ObjectDatasetList', False, 'from cloudroast.objectstorage.generators import ObjectDatasetList, CONTENT_TYPES\n'), (485, 'cloudroast.objectstorage.generators.ObjectDatasetList', 'ObjectDatasetList', False, 'from cloudroast.objectstorage.generators import ObjectDatasetList, CONTENT_TYPES\n'), (532, 'cloudroast.objectstorage.generators.ObjectDatasetList', 'ObjectDatasetList', False, 'from cloudroast.objectstorage.generators import ObjectDatasetList, CONTENT_TYPES\n'), (576, 'cloudroast.objectstorage.generators.ObjectDatasetList', 'ObjectDatasetList', False, 'from cloudroast.objectstorage.generators import ObjectDatasetList, CONTENT_TYPES\n'), (621, 'cloudroast.objectstorage.generators.ObjectDatasetList', 'ObjectDatasetList', False, 'from cloudroast.objectstorage.generators import ObjectDatasetList, CONTENT_TYPES\n'), (665, 'cloudroast.objectstorage.generators.ObjectDatasetList', 'ObjectDatasetList', False, 'from cloudroast.objectstorage.generators import ObjectDatasetList, CONTENT_TYPES\n'), (709, 'cloudroast.objectstorage.generators.ObjectDatasetList', 'ObjectDatasetList', False, 'from cloudroast.objectstorage.generators import ObjectDatasetList, CONTENT_TYPES\n'), (753, 'cloudroast.objectstorage.generators.ObjectDatasetList', 'ObjectDatasetList', False, 'from cloudroast.objectstorage.generators import ObjectDatasetList, CONTENT_TYPES\n'), (797, 'cloudroast.objectstorage.generators.ObjectDatasetList', 'ObjectDatasetList', False, 'from cloudroast.objectstorage.generators import ObjectDatasetList, CONTENT_TYPES\n'), (841, 'cloudroast.objectstorage.generators.ObjectDatasetList', 'ObjectDatasetList', False, 'from cloudroast.objectstorage.generators import ObjectDatasetList, CONTENT_TYPES\n'), (875, 'cloudroast.objectstorage.generators.ObjectDatasetList', 'ObjectDatasetList', False, 'from cloudroast.objectstorage.generators import ObjectDatasetList, CONTENT_TYPES\n'), (922, 'cloudroast.objectstorage.generators.ObjectDatasetList', 'ObjectDatasetList', False, 'from cloudroast.objectstorage.generators import ObjectDatasetList, CONTENT_TYPES\n'), (964, 'cloudroast.objectstorage.generators.ObjectDatasetList', 'ObjectDatasetList', False, 'from cloudroast.objectstorage.generators import ObjectDatasetList, CONTENT_TYPES\n'), (1008, 'cloudroast.objectstorage.generators.ObjectDatasetList', 'ObjectDatasetList', False, 'from cloudroast.objectstorage.generators import ObjectDatasetList, CONTENT_TYPES\n'), (1040, 'cloudroast.objectstorage.generators.ObjectDatasetList', 'ObjectDatasetList', False, 'from cloudroast.objectstorage.generators import ObjectDatasetList, CONTENT_TYPES\n'), (1117, 'cloudroast.objectstorage.generators.ObjectDatasetList', 'ObjectDatasetList', False, 'from cloudroast.objectstorage.generators import ObjectDatasetList, CONTENT_TYPES\n'), (1164, 'cloudroast.objectstorage.generators.ObjectDatasetList', 'ObjectDatasetList', False, 'from cloudroast.objectstorage.generators import ObjectDatasetList, CONTENT_TYPES\n'), (1211, 'cloudroast.objectstorage.generators.ObjectDatasetList', 'ObjectDatasetList', False, 'from cloudroast.objectstorage.generators import ObjectDatasetList, CONTENT_TYPES\n'), (1251, 'cloudroast.objectstorage.generators.ObjectDatasetList', 'ObjectDatasetList', False, 'from cloudroast.objectstorage.generators import ObjectDatasetList, CONTENT_TYPES\n'), (1313, 'cloudroast.objectstorage.generators.ObjectDatasetList', 'ObjectDatasetList', False, 'from cloudroast.objectstorage.generators import ObjectDatasetList, CONTENT_TYPES\n'), (1354, 'cloudroast.objectstorage.generators.ObjectDatasetList', 'ObjectDatasetList', False, 'from cloudroast.objectstorage.generators import ObjectDatasetList, CONTENT_TYPES\n'), (1389, 'cloudroast.objectstorage.generators.ObjectDatasetList', 'ObjectDatasetList', False, 'from cloudroast.objectstorage.generators import ObjectDatasetList, CONTENT_TYPES\n'), (419, 'cloudroast.objectstorage.generators.CONTENT_TYPES.get', 'CONTENT_TYPES.get', False, 'from cloudroast.objectstorage.generators import ObjectDatasetList, CONTENT_TYPES\n'), (883, 'zlib.compress', 'zlib.compress', False, 'import zlib\n'), (971, 'time.gmtime', 'time.gmtime', False, 'import time\n'), (402, 'hashlib.md5', 'md5', False, 'from hashlib import md5\n'), (492, 'hashlib.md5', 'md5', False, 'from hashlib import md5\n')]