"""Unit tests for tasks for working with AWS S3.""" import time from unittest.mock import MagicMock from unittest.mock import Mock from unittest.mock import call import boto from boto.s3.key import Key import moto from analytics_aggregation.tasks import s3 @moto.mock_s3 def test_wait_until_empty_s3_path(monkeypatch): """Test waiting until s3 is empty.""" connection = boto.connect_s3() bucket = connection.create_bucket('mybucket') spy = MagicMock() # Create all the keys keys = [] for f in range(4): key = Key(bucket) key.name = 'path/file.{}'.format(f) key.set_contents_from_string('random content') keys.append(key) def sleep(time): """Mock of the sleep method. This replacement is actually going to remove one file at a time until there are no more in the path. Args: time (int): the time for the method to sleep. """ assert isinstance(time, int) key = keys.pop() bucket.delete_key(key.name) spy() monkeypatch.setattr(time, 'sleep', sleep) s3.wait_until_empty_s3_path(Mock(), 's3://mybucket/path/') assert spy.call_count == 4 def test_granular_folder_copy_empty_folders(monkeypatch): """Test granular_folder_copy if source folder is empty.""" activity = Mock() source_folder = { 'bucket': 'test_bucket', 'prefix': 'test_folder' } target_folder = dict(source_folder) files_in_s3_folder_mock = Mock() files_in_s3_folder_mock.side_effect = [0, 0] monkeypatch.setattr( s3, 'get_number_of_files_in_s3_folder', files_in_s3_folder_mock) s3.granular_folder_copy(activity, source_folder, target_folder, 0) files_in_s3_folder_mock.assert_has_calls([ call(source_folder['bucket'], source_folder['prefix']), call(target_folder['bucket'], target_folder['prefix']), ]) def test_granular_folder_copy_target_full(monkeypatch): """Test granular_folder_copy if target folder is full.""" activity = Mock() source_folder = {'bucket': 'test_bucket', 'prefix': 'test_folder'} target_folder = dict(source_folder) max_processed_files = 5 files_in_s3_folder_mock = Mock() files_in_s3_folder_mock.side_effect = [ max_processed_files, max_processed_files, 0, 0] monkeypatch.setattr( s3, 'get_number_of_files_in_s3_folder', files_in_s3_folder_mock) time_mock = Mock() monkeypatch.setattr(s3, 'time', time_mock) s3.granular_folder_copy( activity, source_folder, target_folder, max_processed_files) files_in_s3_folder_mock.assert_has_calls([ call(source_folder['bucket'], source_folder['prefix']), call(target_folder['bucket'], target_folder['prefix'])] * 2) assert time_mock.sleep.called def test_granular_folder_copy(monkeypatch): """Test granular_folder_copy function.""" activity = Mock() source_folder = {'bucket': 'test_bucket', 'prefix': 'test_folder'} target_folder = dict(source_folder) max_processed_files = 5 files_in_s3_folder_mock = Mock() files_in_s3_folder_mock.side_effect = [ max_processed_files, max_processed_files - 1, 0, 0] monkeypatch.setattr( s3, 'get_number_of_files_in_s3_folder', files_in_s3_folder_mock) copy_keys_mock = Mock() monkeypatch.setattr(s3, 'copy_keys', copy_keys_mock) time_mock = Mock() monkeypatch.setattr(s3, 'time', time_mock) s3.granular_folder_copy( activity, source_folder, target_folder, max_processed_files) copy_keys_mock.assert_called_once_with(source_folder, target_folder, 1) files_in_s3_folder_mock.assert_has_calls([ call(source_folder['bucket'], source_folder['prefix']), call(target_folder['bucket'], target_folder['prefix']), call(source_folder['bucket'], source_folder['prefix']), call(target_folder['bucket'], target_folder['prefix'])]) assert time_mock.sleep.called def test_get_number_of_files_in_s3_folder(monkeypatch): """Test get_number_of_files_in_s3_folder function.""" s3_objects_mock = Mock() _s3_objects_result = [1, 2, 3] s3_objects_mock.return_value = _s3_objects_result monkeypatch.setattr(s3, '_s3_objects', s3_objects_mock) bucket = 'test_bucket' prefix = 'test_folder' result = s3.get_number_of_files_in_s3_folder(bucket, prefix) s3_objects_mock.assert_called_once_with(bucket, prefix) assert result == len(list(_s3_objects_result)) def test_copy_keys(monkeypatch): """Test copy_keys function.""" source_folder = {'bucket': 'test_bucket', 'prefix': 'test_folder'} target_folder = dict(source_folder) amount = 1 s3_objects_mock = Mock() s3_object = Mock() key_name = 'key' s3_object.key = 'test/{}'.format(key_name) s3_objects_result = [s3_object] s3_objects_mock.return_value = s3_objects_result monkeypatch.setattr(s3, '_s3_objects', s3_objects_mock) copy_key_mock = Mock() monkeypatch.setattr(s3, 'copy_key', copy_key_mock) delete_key_mock = Mock() monkeypatch.setattr(s3, 'delete_key', delete_key_mock) target_key = '{}/{}'.format(target_folder['prefix'], key_name) # tested function call s3.copy_keys(source_folder, target_folder, amount) # checks copy_key_mock.assert_called_once_with( source_folder['bucket'], s3_object.key, target_folder['bucket'], target_key) delete_key_mock.assert_called_once_with( source_folder['bucket'], s3_object.key) def test_s3_objects_limit(monkeypatch): """Test _s3_objects function with limit param.""" bucket = 'test_bucket' prefix = 'test_prefix' limit = 1 s3_resource_mock = Mock() monkeypatch.setattr(s3, 's3_resource', s3_resource_mock) bucket_mock = Mock() s3_resource_mock.Bucket.return_value = bucket_mock objects_mock = bucket_mock.objects filter_mock = objects_mock.filter limit_mock = Mock() objects_mock.filter.return_value = limit_mock # tested function call s3._s3_objects(bucket, prefix, limit) # checks s3_resource_mock.Bucket.assert_called_once_with(bucket) filter_mock.assert_called_once_with(Prefix=prefix) limit_mock.limit.assert_called_once_with(limit) def test_s3_objects_no_limit(monkeypatch): """Test _s3_objects function without limit param.""" bucket = 'test_bucket' prefix = 'test_prefix' s3_resource_mock = Mock() monkeypatch.setattr(s3, 's3_resource', s3_resource_mock) bucket_mock = Mock() s3_resource_mock.Bucket.return_value = bucket_mock objects_mock = bucket_mock.objects filter_mock = objects_mock.filter limit_mock = Mock() objects_mock.filter.return_value = limit_mock # tested function call s3._s3_objects(bucket, prefix) # checks s3_resource_mock.Bucket.assert_called_once_with(bucket) filter_mock.assert_called_once_with(Prefix=prefix) assert not limit_mock.limit.called def test_delete_key(monkeypatch): """Test delete_key function.""" s3_client_mock = Mock() monkeypatch.setattr(s3, 's3_client', s3_client_mock) bucket = 'test_bucket' key = 'test_key' s3.delete_key(bucket, key) s3_client_mock.delete_object.assert_called_once_with( Bucket=bucket, Key=key) def test_copy_key(monkeypatch): """Test delete_key function.""" s3_resource_mock = Mock() monkeypatch.setattr(s3, 's3_resource', s3_resource_mock) source_bucket = 'source_bucket' source_key = 'source_key' target_bucket = 'target_bucket' target_key = 'target_key' bucket_mock = Mock() s3_resource_mock.Bucket.return_value = bucket_mock s3.copy_key(source_bucket, source_key, target_bucket, target_key) bucket_mock.copy.assert_called_once_with( {'Bucket': source_bucket, 'Key': source_key}, target_key)