"""Tests for S3 module.""" from unittest import mock from botocore import exceptions import pytest from src.connectors import s3 @pytest.mark.parametrize('quiet', [True, False]) @pytest.mark.parametrize( 'keys, expected_objects', [ (['k1', 'k2'], [{'Key': 'k1'}, {'Key': 'k2'}]), (['key'], [{'Key': 'key'}]), ], ) @pytest.mark.parametrize('bucket', ['test_bucket', 'buck_test']) @mock.patch('src.connectors.s3.boto3') def test_delete_objects(boto3, bucket, keys, expected_objects, quiet): """Test delete_objects utility function.""" expected_resp = mock.MagicMock() client = mock.MagicMock() client.delete_objects.return_value = expected_resp boto3.client.return_value = client expected_delete = {'Objects': expected_objects, 'Quiet': quiet} response = s3.delete_objects(bucket, keys, quiet) assert response == expected_resp boto3.client.assert_called_once_with('s3') client.delete_objects.assert_called_once_with(Bucket=bucket, Delete=expected_delete) @pytest.mark.parametrize( 'response_status, expected_result', [ (200, True), (400, False), ], ) @pytest.mark.parametrize( 'dest_key', [ 'test-me.key', 'dir/key.name', ], ) @pytest.mark.parametrize( 'source_key', [ 'test1.key', 'dir_name/key.name', ], ) @pytest.mark.parametrize('bucket', ['test_bucket', 'buck_test']) @mock.patch('src.connectors.s3.boto3') def test_copy_object(boto3, bucket, source_key, dest_key, response_status, expected_result): """Test copy_object utility function.""" copy_resp = {'ResponseMetadata': {'HTTPStatusCode': response_status}} client = mock.MagicMock() client.copy_object.return_value = copy_resp boto3.client.return_value = client expected_copy_source = { 'Bucket': bucket, 'Key': source_key, } result = s3.copy_object(bucket, source_key, dest_key) assert result == expected_result client.copy_object.assert_called_once_with( Bucket=bucket, CopySource=expected_copy_source, Key=dest_key ) @pytest.mark.parametrize( 'exc', [ exceptions.ParamValidationError(report=''), exceptions.ClientError({}, 'operation_name'), ], ) @mock.patch('src.connectors.s3.boto3') @mock.patch('src.connectors.s3.capture_exception') def test_copy_object_raises(mock_sentry, boto3, exc): """Test that copy_object utility function handles exception.""" client = mock.MagicMock() client.copy_object.side_effect = exc boto3.client.return_value = client assert not s3.copy_object('bucket', 'test', None) assert mock_sentry.called @pytest.mark.parametrize( 'response_status, expected_result', [ (200, True), (204, True), (400, False), ], ) @pytest.mark.parametrize( 'key', [ 'test1.key', 'dir_name/key.name', ], ) @pytest.mark.parametrize('bucket', ['test_bucket', 'buck_test']) @mock.patch('src.connectors.s3.boto3') def test_delete_object(boto3, bucket, key, response_status, expected_result): """Test delete_object utility function.""" delete_response = {'ResponseMetadata': {'HTTPStatusCode': response_status}} client = mock.MagicMock() client.delete_object.return_value = delete_response boto3.client.return_value = client result = s3.delete_object(bucket, key) assert result == expected_result client.delete_object.assert_called_once_with(Bucket=bucket, Key=key) @pytest.mark.parametrize( 'exc', [ exceptions.ParamValidationError(report=''), exceptions.ClientError({}, 'operation_name'), ], ) @mock.patch('src.connectors.s3.boto3') def test_delete_object_raises(boto3, exc): """Test that delete_object utility function handles exception.""" client = mock.MagicMock() client.delete_object.side_effect = exc boto3.client.return_value = client assert not s3.delete_object('bucket', 'test') @pytest.mark.parametrize( 'dest_key', [ 'test-me.key', 'dir/key.name', ], ) @pytest.mark.parametrize( 'source_key', [ 'test1.key', 'dir_name/key.name', ], ) @pytest.mark.parametrize('bucket', ['test_bucket', 'buck_test']) @mock.patch('src.connectors.s3.delete_object') @mock.patch('src.connectors.s3.copy_object') def test_move_object(copy_object, delete_object, bucket, source_key, dest_key): """Test move_object utility function.""" delete_response = copy_resp = True copy_object.return_value = copy_resp delete_object.return_value = delete_response assert s3.move_object(bucket, source_key, dest_key) copy_object.assert_called_once_with(bucket, source_key, dest_key) delete_object.assert_called_once_with(bucket, source_key) @pytest.mark.parametrize( 'copy_result, delete_result, expected_result', [ # error on copy, delete should not be called (False, True, False), # error on delete (True, False, True), # no error (True, True, True), ], ) @mock.patch('src.connectors.s3.delete_object') @mock.patch('src.connectors.s3.copy_object') def test_move_object_misc_cases( copy_object, delete_object, copy_result, delete_result, expected_result ): """Test move_object utility function.""" bucket = 'test_bucket' source_key = 'test.key' dest_key = 'dest.key' copy_object.return_value = copy_result delete_object.return_value = delete_result assert s3.move_object(bucket, source_key, dest_key) == expected_result copy_object.assert_called_once_with(bucket, source_key, dest_key) if copy_result: delete_object.assert_called_once_with(bucket, source_key) else: assert delete_object.call_count == 0