import gzip import json import unittest.mock import boto3 import pytest from moto import mock_s3 import slz_downloader.dsp.vevo import slz_downloader.entities from slz_downloader.dsp import exceptions @pytest.fixture def vevo_ac_job(): return slz_downloader.entities.Job( uow_id='vevo-20210211-sme-active_claims-v1', unit_of_work_id=0, dsp='vevo', report_type='active_claims', subtype=None, version='v1', report_date='2021-02-11', licensor='sme', extension='csv', config_bucket='delphi-configs', context='active_claims', context_params=None, job_id="test_job_id", ) @unittest.mock.patch('slz_downloader.dsp.vevo.client_active_claims.get_secret') @unittest.mock.patch('requests.get') @mock_s3 def test_downloading(requests_get, get_secret, params, vevo_ac_job): boto3.setup_default_session() conn = boto3.resource('s3', region_name='us-east-1') for bucket in ['bucket-archive-quarantine', 'bucket-decompressed-quarantine']: conn.create_bucket(Bucket=bucket) # Client initialization logger = unittest.mock.Mock() client = slz_downloader.dsp.vevo.ActiveClaimsClient(logger=logger) # Mock secrets get_secret.return_value = json.dumps( { 'ACCESS_KEY_ID': '123', 'SECRET_ACCESS_KEY': '234', 'REGION': 'us-east-1', 'BUCKET': 'test_bucket', } ) boto_client = unittest.mock.Mock() client.configure(params) client._client = boto_client source_url = 'https://test_bucket/claims_2021-02-11T1345_sHwX8Z.json' boto_client.generate_presigned_url.return_value = source_url boto_client.list_objects_v2.return_value = { 'Contents': [{ 'Key': 'vevo/in/vevo_claims/claims_2021-02-11T1358_SCtwCw.json' }, ] } # Mock response from source API test_file_content = '[{"claim_id": "4cvkcVXd-N4","status": null},{"claim_id": "zwha0X865yg"}]' expected_file_content = 'claim_id,status\r\n4cvkcVXd-N4,\r\nzwha0X865yg,\r\n' resp_mock = unittest.mock.MagicMock() requests_get.return_value.__enter__.return_value = resp_mock resp_mock.ok = True resp_mock.url = source_url resp_mock.content = bytes(test_file_content, encoding='utf8') resp_mock.headers = {} client.download(vevo_ac_job, chunk_size=1) # check API called with correct request requests_get.assert_called_with( url=source_url, timeout=10, ) # read data from mocked S3 buckets path = 'vevo/active_claims/v1/report_date=2021-02-11/report_licensor=sme' actual_decompressed = conn.Object( 'bucket-decompressed-quarantine', f'{path}/active_claims_20210211.csv' ).get()['Body'].read() actual_compressed = conn.Object( 'bucket-archive-quarantine', f'{path}/active_claims_20210211.csv.gz' ).get()['Body'].read() assert expected_file_content == actual_decompressed.decode('utf8') assert expected_file_content == gzip.decompress(actual_compressed).decode('utf8') def test_get_source_prefix_ok(vevo_ac_job): boto_client = unittest.mock.Mock() boto_client.list_objects_v2.return_value = { 'Contents': [{ 'Key': 'vevo/in/vevo_claims/claims_2021-02-11T1358_SCtwCw.json', 'Size': 37510, }] } logger = unittest.mock.Mock() client = slz_downloader.dsp.vevo.ActiveClaimsClient(logger=logger) client._client = boto_client prefix = client._get_source_prefix(vevo_ac_job) assert prefix == 'vevo/in/vevo_claims/claims_2021-02-11T1358_SCtwCw.json' def test_get_source_prefix_no_keys_found(vevo_ac_job): boto_client = unittest.mock.Mock() boto_client.list_objects_v2.return_value = { 'Contents': [], } logger = unittest.mock.Mock() client = slz_downloader.dsp.vevo.ActiveClaimsClient(logger=logger) client._client = boto_client with pytest.raises(exceptions.StreamNotFoundError): client._get_source_prefix(vevo_ac_job) def test_get_source_prefix_multiple_keys_found(vevo_ac_job): boto_client = unittest.mock.Mock() boto_client.list_objects_v2.return_value = { 'Contents': [ { 'Key': 'vevo/in/vevo_claims/claims_2021-02-11T1358_SCtwCw.json' }, { 'Key': 'vevo/in/vevo_claims/claims_2021-02-11T2222_SCtqwe.json' }, ] } logger = unittest.mock.Mock() client = slz_downloader.dsp.vevo.ActiveClaimsClient(logger=logger) client._client = boto_client with pytest.raises(exceptions.StreamError): client._get_source_prefix(vevo_ac_job) def test_convert_to_csv_ok(): data = [ { 'youtube_video_metadata.claim_id': '4cvkcVXd-N4', 'active_claim.claim_status': None, 'active_claim.claim_status_detail': None, }, { 'youtube_video_metadata.claim_id': 'zwha0X865yg', 'active_claim.claim_status_detail': None, 'active_claim.claim_status': None, } ] path_to_temp_file = slz_downloader.dsp.vevo.ActiveClaimsClient.dump_to_csv_file(data) with open(path_to_temp_file, 'rb') as f: content = f.read() expected_content = b'youtube_video_metadata.claim_id,active_claim.claim_status,active_claim.claim_status_detail\r\n' \ b'4cvkcVXd-N4,,\r\nzwha0X865yg,,\r\n' assert content == expected_content def test_convert_to_csv_num_of_columns_mismatch(): data = [ { 'youtube_video_metadata.claim_id': '4cvkcVXd-N4', 'active_claim.claim_status': None, 'active_claim.claim_status_detail': None, }, { 'youtube_video_metadata.claim_id': 'zwha0X865yg', 'active_claim.claim_status_detail_unexpected': None, } ] with pytest.raises(ValueError): slz_downloader.dsp.vevo.ActiveClaimsClient.dump_to_csv_file(data) @pytest.mark.parametrize( 'content, expected_result', [ ( [{ 'Key': 'vevo/in/vevo_claims/claims_2021-02-11T1358_SCtwCw.json', 'Size': 37510, }], ('active_claims', 0) ), ( [ { 'Key': 'vevo/in/vevo_claims/claims_2021-02-11T1358_SCtwCw.json' }, { 'Key': 'vevo/in/vevo_claims/claims_2021-02-11T2222_SCtqwe.json' }, ], None ), ([], None) ] ) def test_vevo_ac_availability_check(vevo_ac_job, content, expected_result): logger = unittest.mock.Mock() client = slz_downloader.dsp.vevo.ActiveClaimsClient(logger) boto_client_mock = unittest.mock.Mock() client._client = boto_client_mock boto_client_mock.list_objects_v2.return_value = {'Contents': content} result = client._check_source_is_available(vevo_ac_job) assert result == expected_result