"""Tests for s3_file model.""" import sys from unittest import mock import boto3 import botocore from botocore.exceptions import ClientError from flexmock import flexmock import moto import pytest from transcoding.connectors import logger from transcoding.connectors import s3 from transcoding.models import s3_file @pytest.fixture def fixture_upload_file_args(): """Fixture with args for upload_s3_file call.""" return { 'source_path': 'some_file_path.ext', 'destination_bucket': 'some_bucket', 'destination_key': 'unique_filename.ext' } @pytest.fixture def fixture_create_presigned_url_args(): """Fixture with args for create_presigned_url call.""" return { 'bucket': 'test_bucket', 'key': 'audio_file.wav', 'client_method': 'put_object', 'expires_in': 3600 } @pytest.fixture def fixture_file_content(): """Return mocked file content.""" return b'file_content' @pytest.fixture def fixture_copy_s3_file_args(): """Fixture with args for copy_s3_file call.""" return { 'source_bucket': 'source_bucket', 'source_key': 'source_key.wav', 'destination_bucket': 'destination_bucket', 'destination_key': 'destination_key.wav', } def test_upload_file_success(fixture_upload_file_args, fixture_file_content): """Test for success of file upload.""" builtins_mock = flexmock(sys.modules['builtins']) builtins_mock.should_call('open') (builtins_mock.should_receive('open') .with_args('some_file_path.ext', 'rb') .and_return(fixture_file_content) .once()) (flexmock(botocore.client.BaseClient) .should_receive('_make_api_call') .with_args( 'PutObject', { 'Body': fixture_file_content, 'Key': 'unique_filename.ext', 'Bucket': 'some_bucket'}) .and_return({'status': 'ok'}) .once()) result = s3_file.upload_s3_file(**fixture_upload_file_args) assert result def test_upload_file_client_error_failure( fixture_upload_file_args, fixture_file_content): """Test for client error during file upload.""" builtins_mock = flexmock(sys.modules['builtins']) builtins_mock.should_call('open') (builtins_mock.should_receive('open') .with_args('some_file_path.ext', 'rb') .and_return(fixture_file_content) .once()) (flexmock(botocore.client.BaseClient) .should_receive('_make_api_call') .with_args( 'PutObject', { 'Body': fixture_file_content, 'Key': 'unique_filename.ext', 'Bucket': 'some_bucket'}) .and_raise( ClientError, { 'Error': {'Code': 'some_code', 'Message': 'some_message'} }, 'put_object') .once()) logger_message = 'S3 file upload failed with code some_code' (flexmock(logger.app_logger) .should_receive('exception') .with_args(logger_message) .and_return(True) .once()) exception_args = ('An error occurred (some_code) when calling the ' 'put_object operation: some_message') with pytest.raises(ClientError) as exc_info: s3_file.upload_s3_file(**fixture_upload_file_args) assert exc_info.value.args[0] == exception_args def test_upload_file_exception_failure( fixture_upload_file_args, fixture_file_content): """Test for exception during file upload.""" builtins_mock = flexmock(sys.modules['builtins']) builtins_mock.should_call('open') (builtins_mock.should_receive('open') .with_args('some_file_path.ext', 'rb') .and_return(fixture_file_content) .once()) exception_message = 'fatal_error' (flexmock(botocore.client.BaseClient) .should_receive('_make_api_call') .with_args( 'PutObject', { 'Body': fixture_file_content, 'Key': 'unique_filename.ext', 'Bucket': 'some_bucket'}) .and_raise(Exception(exception_message))) logger_message = 'S3 file upload failed with error fatal_error' (flexmock(logger.app_logger) .should_receive('exception') .with_args(logger_message) .and_return(True) .once()) with pytest.raises(Exception) as exc_info: s3_file.upload_s3_file(**fixture_upload_file_args) assert exc_info.value.args[0] == exception_message def test_copy_s3_file_success(fixture_copy_s3_file_args): """Test for success of copy_s3_file.""" mock_client = mock.MagicMock() (flexmock(s3) .should_receive('get_s3_client') .once() .and_return(mock_client)) s3_file.copy_s3_file(**fixture_copy_s3_file_args) mock_client.copy.assert_called_once_with( CopySource={ 'Bucket': fixture_copy_s3_file_args['source_bucket'], 'Key': fixture_copy_s3_file_args['source_key'], }, Bucket=fixture_copy_s3_file_args['destination_bucket'], Key=fixture_copy_s3_file_args['destination_key'], ExtraArgs={'TaggingDirective': 'REPLACE', 'Tagging': ''}, Config=mock.ANY, ) def test_copy_s3_file_client_error_failure(fixture_copy_s3_file_args): """Test for client error during file copy.""" mock_client = mock.MagicMock() mock_client.copy.side_effect = ClientError( {'Error': {'Code': 'some_code', 'Message': 'some_message'}}, 'copy' ) (flexmock(s3) .should_receive('get_s3_client') .once() .and_return(mock_client)) logger_message = 'S3 file copy failed with code some_code' (flexmock(logger.app_logger) .should_receive('exception') .with_args(logger_message) .and_return(True) .once()) exception_args = ('An error occurred (some_code) when calling the ' 'copy operation: some_message') with pytest.raises(ClientError) as exc_info: s3_file.copy_s3_file(**fixture_copy_s3_file_args) assert exc_info.value.args[0] == exception_args def test_copy_s3_file_exception_failure(fixture_copy_s3_file_args): """Test for exception during file copy.""" exception_message = 'fatal_error' mock_client = mock.MagicMock() mock_client.copy.side_effect = Exception(exception_message) (flexmock(s3) .should_receive('get_s3_client') .once() .and_return(mock_client)) logger_message = 'S3 file copy failed with error fatal_error' (flexmock(logger.app_logger) .should_receive('exception') .with_args(logger_message) .and_return(True) .once()) with pytest.raises(Exception) as exc_info: s3_file.copy_s3_file(**fixture_copy_s3_file_args) assert exc_info.value.args[0] == exception_message @moto.mock_s3 def test_create_presigned_url_success(fixture_create_presigned_url_args): """Test for success of presigned url generation.""" # mocking preparation test_bucket_name = fixture_create_presigned_url_args['bucket'] test_key_name = fixture_create_presigned_url_args['key'] s3_connection = boto3.resource('s3', region_name='us-east-1') bucket = s3_connection.create_bucket(Bucket=test_bucket_name) bucket.Object(test_key_name) # test functional call result = s3_file.create_presigned_url( fixture_create_presigned_url_args['bucket'], fixture_create_presigned_url_args['key']) # checking assert result assert test_bucket_name in result assert test_key_name in result def test_create_presigned_url_with_custom_param_success( fixture_create_presigned_url_args): """Test for success of presigned url generation with custom params.""" # data preparation expected_result = 'url_result' # mocking (flexmock(botocore.signers) .should_receive('generate_presigned_url') .with_args( Params={ 'Bucket': fixture_create_presigned_url_args['bucket'], 'Key': fixture_create_presigned_url_args['key'] }, ClientMethod=fixture_create_presigned_url_args['client_method'], ExpiresIn=fixture_create_presigned_url_args['expires_in']) .and_return(expected_result) .once()) # test functional call result = s3_file.create_presigned_url( fixture_create_presigned_url_args['bucket'], fixture_create_presigned_url_args['key'], client_method=fixture_create_presigned_url_args['client_method'], expires_in=fixture_create_presigned_url_args['expires_in'] ) # checking assert result assert result == expected_result def test_create_presigned_url_client_error_failure( fixture_create_presigned_url_args): """Test for client error during creating of presigned url.""" # data preparation logger_message = 'S3 generating presigned url failed with code some_code' exception_args = ('An error occurred (some_code) when calling the ' 'generate_presigned_url operation: some_message') # mocking (flexmock(botocore.signers) .should_receive('generate_presigned_url') .with_args( ClientMethod='get_object', ExpiresIn=86400, Params={ 'Bucket': fixture_create_presigned_url_args['bucket'], 'Key': fixture_create_presigned_url_args['key'] }) .and_raise( ClientError, { 'Error': {'Code': 'some_code', 'Message': 'some_message'} }, 'generate_presigned_url') .once()) (flexmock(logger.app_logger) .should_receive('exception') .with_args(logger_message) .and_return(True) .once()) # test function call and checking with pytest.raises(ClientError) as exc_info: s3_file.create_presigned_url( fixture_create_presigned_url_args['bucket'], fixture_create_presigned_url_args['key'] ) assert exc_info.value.args[0] == exception_args def test_create_presigned_url_failure( fixture_create_presigned_url_args): """Test for exception during creating of presigned url.""" # data preparation exception_message = 'fatal_error' logger_message = 'S3 file upload failed with error fatal_error' # mocking (flexmock(botocore.signers) .should_receive('generate_presigned_url') .with_args( ClientMethod='get_object', ExpiresIn=86400, Params={ 'Bucket': fixture_create_presigned_url_args['bucket'], 'Key': fixture_create_presigned_url_args['key'] }) .and_raise(Exception(exception_message)) .once()) (flexmock(logger.app_logger) .should_receive('exception') .with_args(logger_message) .and_return(True) .once()) # test functional call and checking with pytest.raises(Exception) as exc_info: s3_file.create_presigned_url( fixture_create_presigned_url_args['bucket'], fixture_create_presigned_url_args['key'] ) assert exc_info.value.args[0] == exception_message