python-botocore/tests/functional/test_s3.py

209 lines
8.7 KiB
Python
Raw Normal View History

# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
from tests import unittest, mock, BaseSessionTest
import botocore.session
from botocore.client import Config
from botocore.exceptions import ParamValidationError
class TestS3BucketValidation(unittest.TestCase):
def test_invalid_bucket_name_raises_error(self):
session = botocore.session.get_session()
s3 = session.create_client('s3')
with self.assertRaises(ParamValidationError):
s3.put_object(Bucket='adfgasdfadfs/bucket/name',
Key='foo', Body=b'asdf')
class BaseS3OperationTest(BaseSessionTest):
def setUp(self):
super(BaseS3OperationTest, self).setUp()
self.region = 'us-west-2'
self.client = self.session.create_client(
's3', self.region)
self.session_send_patch = mock.patch('botocore.endpoint.Session.send')
self.http_session_send_mock = self.session_send_patch.start()
def tearDown(self):
super(BaseSessionTest, self).tearDown()
self.session_send_patch.stop()
class TestS3GetBucketLifecycle(BaseS3OperationTest):
def test_multiple_transitions_returns_one(self):
http_response = mock.Mock()
http_response.status_code = 200
http_response.content = (
'<?xml version="1.0" ?>'
'<LifecycleConfiguration xmlns="http://s3.amazonaws.'
'com/doc/2006-03-01/">'
' <Rule>'
' <ID>transitionRule</ID>'
' <Prefix>foo</Prefix>'
' <Status>Enabled</Status>'
' <Transition>'
' <Days>40</Days>'
' <StorageClass>STANDARD_IA</StorageClass>'
' </Transition>'
' <Transition>'
' <Days>70</Days>'
' <StorageClass>GLACIER</StorageClass>'
' </Transition>'
' </Rule>'
' <Rule>'
' <ID>noncurrentVersionRule</ID>'
' <Prefix>bar</Prefix>'
' <Status>Enabled</Status>'
' <NoncurrentVersionTransition>'
' <NoncurrentDays>40</NoncurrentDays>'
' <StorageClass>STANDARD_IA</StorageClass>'
' </NoncurrentVersionTransition>'
' <NoncurrentVersionTransition>'
' <NoncurrentDays>70</NoncurrentDays>'
' <StorageClass>GLACIER</StorageClass>'
' </NoncurrentVersionTransition>'
' </Rule>'
'</LifecycleConfiguration>'
)
http_response.headers = {}
self.http_session_send_mock.return_value = http_response
s3 = self.session.create_client('s3')
response = s3.get_bucket_lifecycle(Bucket='mybucket')
# Each Transition member should have at least one of the
# transitions provided.
self.assertEqual(
response['Rules'][0]['Transition'],
{'Days': 40, 'StorageClass': 'STANDARD_IA'}
)
self.assertEqual(
response['Rules'][1]['NoncurrentVersionTransition'],
{'NoncurrentDays': 40, 'StorageClass': 'STANDARD_IA'}
)
class TestS3PutObject(BaseS3OperationTest):
def test_500_error_with_non_xml_body(self):
# Note: This exact test case may not be applicable from
# an integration standpoint if the issue is fixed in the future.
#
# The issue is that:
# S3 returns a 200 response but the received response from urllib3 has
# a 500 status code and the headers are in the body of the
# the response. Botocore will try to parse out the error body as xml,
# but the body is invalid xml because it is full of headers.
# So instead of blowing up on an XML parsing error, we
# should at least use the 500 status code because that can be
# retried.
#
# We are unsure of what exactly causes the response to be mangled
# but we expect it to be how 100 continues are handled.
non_xml_content = (
'x-amz-id-2: foo\r\n'
'x-amz-request-id: bar\n'
'Date: Tue, 06 Oct 2015 03:20:38 GMT\r\n'
'ETag: "a6d856bc171fc6aa1b236680856094e2"\r\n'
'Content-Length: 0\r\n'
'Server: AmazonS3\r\n'
)
http_500_response = mock.Mock()
http_500_response.status_code = 500
http_500_response.content = non_xml_content
http_500_response.headers = {}
success_response = mock.Mock()
success_response.status_code = 200
success_response.content = b''
success_response.headers = {}
self.http_session_send_mock.side_effect = [
http_500_response, success_response
]
s3 = self.session.create_client('s3')
response = s3.put_object(Bucket='mybucket', Key='mykey', Body=b'foo')
# The first response should have been retried even though the xml is
# invalid and eventually return the 200 response.
self.assertEqual(response['ResponseMetadata']['HTTPStatusCode'], 200)
class BaseS3AddressingStyle(BaseSessionTest):
def setUp(self):
super(BaseS3AddressingStyle, self).setUp()
self.http_response = mock.Mock()
self.http_response.status_code = 200
self.http_response.headers = {}
self.http_response.content = b''
class TestVirtualHostStyle(BaseS3AddressingStyle):
def test_default_endpoint_for_virtual_addressing(self):
s3 = self.session.create_client(
's3', config=Config(s3={'addressing_style': 'virtual'}))
with mock.patch('botocore.endpoint.Session.send') \
as mock_send:
mock_send.return_value = self.http_response
s3.put_object(Bucket='mybucket', Key='mykey', Body='mybody')
request_sent = mock_send.call_args[0][0]
self.assertEqual(
'https://mybucket.s3.amazonaws.com/mykey', request_sent.url)
def test_provided_endpoint_url_for_virtual_addressing(self):
s3 = self.session.create_client(
's3', config=Config(s3={'addressing_style': 'virtual'}),
endpoint_url='https://foo.amazonaws.com')
with mock.patch('botocore.endpoint.Session.send') \
as mock_send:
mock_send.return_value = self.http_response
s3.put_object(Bucket='mybucket', Key='mykey', Body='mybody')
request_sent = mock_send.call_args[0][0]
self.assertEqual(
'https://mybucket.foo.amazonaws.com/mykey', request_sent.url)
def test_us_gov_with_virtual_addressing(self):
s3 = self.session.create_client(
's3', region_name='us-gov-west-1',
config=Config(s3={'addressing_style': 'virtual'}))
with mock.patch('botocore.endpoint.Session.send') \
as mock_send:
mock_send.return_value = self.http_response
s3.put_object(Bucket='mybucket', Key='mykey', Body='mybody')
request_sent = mock_send.call_args[0][0]
self.assertEqual(
'https://mybucket.s3-us-gov-west-1.amazonaws.com/mykey',
request_sent.url)
class TestPathHostStyle(BaseS3AddressingStyle):
def test_default_endpoint_for_path_addressing(self):
s3 = self.session.create_client(
's3', config=Config(s3={'addressing_style': 'path'}))
with mock.patch('botocore.endpoint.Session.send') \
as mock_send:
mock_send.return_value = self.http_response
s3.put_object(Bucket='mybucket', Key='mykey', Body='mybody')
request_sent = mock_send.call_args[0][0]
self.assertEqual(
'https://s3.amazonaws.com/mybucket/mykey', request_sent.url)
def test_provided_endpoint_url_for_path_addressing(self):
s3 = self.session.create_client(
's3', config=Config(s3={'addressing_style': 'path'}),
endpoint_url='https://foo.amazonaws.com')
with mock.patch('botocore.endpoint.Session.send') \
as mock_send:
mock_send.return_value = self.http_response
s3.put_object(Bucket='mybucket', Key='mykey', Body='mybody')
request_sent = mock_send.call_args[0][0]
self.assertEqual(
'https://foo.amazonaws.com/mybucket/mykey', request_sent.url)