import unittest from unittest.mock import Mock, call from moto import mock_s3 import boto3 import smart_open from parameterized import parameterized from bulk_copy_lambda.handler import Handler class CheckPayloadTestCase(unittest.TestCase): @parameterized.expand( [ ( { "bucket_source": "stage-sme-data-archive", "key_regexp": ".+report_date=2020-04-09.+", "bucket_destination": "dev-delphi-slz-download-jobs", "prefixes": [ "sme_max/" ] }, True ), ( { "bucket_source": "stage-sme-data-archive", "key_regexp": ".+report_date=2020-04-09.+", "bucket_destination": "dev-delphi-slz-download-jobs", "dsp_config": "s3://dev-delphi-configs/dsp_config.json", "prefixes_payload": { "dsp": "apple" } }, True ), ( { "bucket_source": "stage-sme-data-archive", }, False ), ( { "bucket_source": "stage-sme-data-archive", "key_regexp": ".+report_date=2020-04-09.+", "bucket_destination": "dev-delphi-slz-download-jobs", # "dsp_config": "is missing", "prefixes_payload": { "dsp": "apple" } }, False ), ] ) def test_payload_correct(self, params, is_valid): result = Handler._check_payload(params) self.assertEqual(result[0], is_valid) @mock_s3 class ProcessTestCase(unittest.TestCase): def setUp(self): boto3.setup_default_session() self.conn = boto3.resource('s3', region_name='us-east-1') self.source_bucket = 'dev-test-source-bucket' self.source_key = 'sme_max/album_mapping/v1/report_date=2020-03-12/report_licensor=sme/initial_test_file.txt' self.conn.create_bucket(Bucket=self.source_bucket) expected = 'Some bytes data' self.data = bytes(expected, 'utf-8') self.s3_path = f's3://{self.source_bucket}/initial_test_file.txt' for i in range(5): with smart_open.open( f's3://{self.source_bucket}/{self.source_key}_{i}', 'wb', ignore_ext=True) as fout: fout.write(self.data) self.lambda_context = Mock() self.lambda_context.get_remaining_time_in_millis = lambda: 100 * 1000 @parameterized.expand( [ ( ["sme_max/"], ".+_[1-2]", "sme_max/album_mapping/v1/report_date=2020-03-12/report_licensor=sme/initial_test_file.txt_1," + "sme_max/album_mapping/v1/report_date=2020-03-12/report_licensor=sme/initial_test_file.txt_2", True ), ( ["sme_max/"], ".+_[3]", "sme_max/album_mapping/v1/report_date=2020-03-12/report_licensor=sme/initial_test_file.txt_3", True ), ( ["sme_max/"], ".+_[1].+", None, False ), ( ["sme_max/"], ".+_[7].+", None, False ), ( ["unknown/"], ".+_[7].+", None, False ) ] ) def test_process_ok(self, prefixes, key_regexp, s3_keys, should_call_sf_start): s3_client = self.conn.meta.client config = { 'sf_arn': 'SF_ARN', 'batch_config_path': 'BATCH_CONFIG_PATH', } payload = { "bucket_source": "dev-test-source-bucket", "key_regexp": key_regexp, "bucket_destination": "dev-delphi-slz-download-jobs", "prefixes": [ "sme_max/", ], "debug": True } handler = Handler(Mock(), self.lambda_context, Mock(), config, payload) service = Mock() for prefix in payload["prefixes"]: handler._process(service, s3_client, prefix) if should_call_sf_start: service.start_sf.assert_called_with(dict( bucket_source=payload["bucket_source"], s3_keys=s3_keys, bucket_destination=payload["bucket_destination"], debug=payload["debug"] )) else: service.start_sf.assert_not_called() @parameterized.expand( [ ( "sme_max/", ".+_[1-2]", { "sme_max": [{ "keys": [ "sme_max/album_mapping/v1/report_date=2020-03-12/report_licensor=sme/initial_test_file.txt_1", "sme_max/album_mapping/v1/report_date=2020-03-12/report_licensor=sme/initial_test_file.txt_2", ], "keys_length": 182 }] } ), ( "sme_max/", ".+_[3]", { "sme_max": [{ "keys": [ "sme_max/album_mapping/v1/report_date=2020-03-12/report_licensor=sme/initial_test_file.txt_3", ], "keys_length": 91 }] } ), ( "sme_max/", ".+_[1].+", {} ), ( "sme_max/", ".+_[7].+", {}, ), ( "unknown/", ".+_[7].+", {}, ) ] ) def test_find_key_that_match(self, prefix, key_regexp, expected): s3_client = boto3.client('s3') paginator = s3_client.get_paginator('list_objects_v2') params = {"Bucket": self.source_bucket, "Prefix": prefix} page_iterator = paginator.paginate(**params) payload = { "key_regexp": key_regexp, } handler = Handler(Mock(), self.lambda_context, Mock(), Mock(), payload) result = handler._find_keys_that_match(page_iterator) self.assertEqual(result, expected) @parameterized.expand( [ ( [[{ "keys": ["test_key_1", "test_key_2", "test_key_3"], "keys_length": 100 }]], ["test_key_1,test_key_2,test_key_3"] ), ( [[ { "keys": ["test_key_1", "test_key_2", "test_key_3"], "keys_length": 100 }, { "keys": ["test_key_4", "test_key_5", "test_key_6"], "keys_length": 100 }, ]], ["test_key_1,test_key_2,test_key_3", "test_key_4,test_key_5,test_key_6"] ), ( [[ { "keys": ["test_key_1", "test_key_2", "test_key_3"], "keys_length": 100 }, ], [ { "keys": ["test_key_4", "test_key_5", "test_key_6"], "keys_length": 100 }, ]], ["test_key_1,test_key_2,test_key_3", "test_key_4,test_key_5,test_key_6"] ), ( [], [] ), ] ) def test_start_sf(self, keys_list, expected_s3_keys_list): payload = { "bucket_source": "dev-test-source-bucket", "bucket_destination": "dev-delphi-slz-download-jobs", "debug": True } handler = Handler(Mock(), self.lambda_context, Mock(), Mock(), payload) service = Mock() handler._start_sfs(service, keys_list) service.start_sf.assert_has_calls([ call({ "bucket_source": payload["bucket_source"], "s3_keys": s3_keys, "bucket_destination": payload["bucket_destination"], "debug": payload["debug"] }) for s3_keys in expected_s3_keys_list ], any_order=True)