"""Unit tests for tasks of Apple Financial Workflow.""" from io import StringIO from unittest.mock import MagicMock import pandas as pd import pytest from feed_ingestion.flows.apple_financial import config from feed_ingestion.flows.apple_financial import tasks _date = '2024-07-01' @pytest.fixture() def test_normal_content(): """Mock content for normal file.""" return ('Apple Music Summary Sheet\n\nVendor\t0080028967\nFiscal ' 'Month\tJuly 2024\nPeriod\t06/28/2024-08/01/2024\n\nLinear Radio ' 'Service\n\nCurrency\tStorefront Name\tTotal Royalty Bearing ' 'Broadcast Plays\tLabel Royalty Bearing Broadcast Plays\tLabel ' 'Market Share\tTotal Listener Hours\tRevenue Per Thousand ' 'Listener Hours Gurantee\tDivisor\tRevenue Per Thousand Listener ' 'Hour\tRPM Amount\tRoyalty Amount\tEffective Per Play ' 'Rate\nAED\tAE\t43924.000\t1544.000\t0.0351516255350150259539204' '\t5513.159258611\t22.038000000\t1000\t121.500\t4.270\t4.270\t0' '.002766\n\nApple Music Summary\n\nCurrency\tProduct\tStorefront ' 'Name\tTotal Royalty Bearing Plays\tTotal Spatial Available ' 'Royalty Bearing Plays\tTotal Non Spatial Available Royalty ' 'Bearing Plays\tSpatial Available Factor\tLabel Spatial ' 'Available Royalty Bearing Plays\tLabel Non Spatial Available ' 'Royalty Bearing Plays\tSpatial Available Per Play Royalty\tNon ' 'Spatial Available Per Play Royalty\nAED\tFAMILY MONTHLY ' 'DISCOUNTED PROMO ' '1\tAE\t71505.000\t26072\t45433\t1.100\t352.00000\t2854.00000\t0' '.020281\t0.018438\n\nApple Music Royalty ' 'Calculation\n\nCurrency\tProduct\tStorefront Name\tEffective ' 'Rate Dates\tMarket Share (Label/ Paid Subscriber)\tTotal ' 'Revenue\tPercentage of Revenue\tRevenue Share Royalty ' 'Amount\tTotal Subscription Days\tPer Subscription Daily ' 'Minimum\tSubscription Minima Royalty Amount\tPer Play ' 'Rate\tTrial Royalty Pool\tTrial Share Royalty Amount\tRoyalty ' 'Amount\nAED\tFAMILY MONTHLY DISCOUNTED PROMO ' '1\tAE\t10/24/2022-08/01/2024\t0.0437336902696182274983066\t1489' '.10\t52.000000000\t33.86\t4140.884848485\t0.330000000\t59.76\t' '\t\t\t59.76\n\nApple Music Footer\n\nCurrency\tStorefront ' 'Name\tTotal Royalty Amount\tStream Manipulation ' 'Adjustments\nAED\tAE\t82380.27\t0.000\n') @pytest.fixture() def test_old_report_content(): """Mock content for older report file.""" return ('Apple Music Summary Sheet\n\t\nVendor\t0085420853\nFiscal ' 'Month\tJuly 2024\nPeriod\t06/28/2024-08/01/2024\n\t\n\t\nApple ' 'Music Summary\n\t\nCurrency\tProduct\tStorefront Name\tTotal ' 'Royalty Bearing Plays\tTotal Spatial Available Royalty Bearing ' 'Plays\tTotal Non Spatial Available Royalty Bearing ' 'Plays\tSpatial Available Factor\tLabel Spatial Available ' 'Royalty Bearing Plays\tLabel Non Spatial Available Royalty ' 'Bearing Plays\tSpatial Available Per Play Royalty\tNon Spatial ' 'Available Per Play Royalty\nCNY\tFAMILY MONTHLY DISCOUNTED ' 'PROMO 1\tCN\t967415.000\t206242\t761173\t1.100\t0.00000\t65' '.00000\t0.013200\t0.012000\n\t\nApple Music Royalty ' 'Calculation\n\t\nCurrency\tProduct\tStorefront Name\tEffective ' 'Rate Dates\tMarket Share (Label/ Paid Subscriber)\tTotal ' 'Revenue\tPercentage of Revenue\tRevenue Share Royalty ' 'Amount\tTotal Subscription Days\tPer Subscription Daily ' 'Minimum\tSubscription Minima Royalty Amount\tPer Play ' 'Rate\tTrial Royalty Pool\tTrial Share Royalty Amount\tRoyalty ' 'Amount\nCNY\tFAMILY MONTHLY DISCOUNTED PROMO ' '1\tCN\t12/07/2023-08/01/2024\t0.0000657868635171559994786' '\t12961.06\t52.000000000\t0.44\t72720.851851852\t0.162000000\t0' '.78\t\t\t\t0.78\n\nApple Music Footer\n\t\nCurrency\tStorefront ' 'Name\tTotal Royalty Amount\tStream Manipulation ' 'Adjustments\nCNY\tCN\t558.39\n') @pytest.fixture def process_drop_files_context(expected_bootstrap_response): """Context for task process_drop_files.""" source_files_dict = { 'files': [ { 'file_name': 'S2_80028547_0724_ZZ.txt.gz', 'found': True }, { 'file_name': 'S2_80028967_0724_ZZ.txt.gz', 'found': True } ] } return { 'activity': MagicMock(), 'feed_name': config.feed_name, 'date': _date, 's3_archive_path': expected_bootstrap_response['s3_archive_path'], 'source_files_dict': source_files_dict } @pytest.fixture def expected_bootstrap_response(): """Response for bootstrap task.""" return { 'feed_name': config.feed_name, 'secrets_path': config.secrets_path, 'date': _date, 'file_pattern': '.+S2.+ZZ.txt.gz', 's3_archive_path': 's3://dev-cucumbers/AppleFinancial/archives/2024-07/', 'apple_reports': config.apple_reports, 'snowflake_error_limit': config.snowflake_error_limit } def test_bootstrap(expected_bootstrap_response): """Test bootstrap task.""" result = tasks.bootstrap( activity=MagicMock(), date=_date, reload=None) assert result == expected_bootstrap_response def test_normal_process_reports(test_normal_content): """Test process reports for normal report structure.""" result = tasks.process_reports(test_normal_content) assert set(result.keys()) == set(config.expected_reports.keys()) assert ([type(x) for x in result.values()] == [type(StringIO()) for i in result.values()]) def test_old_process_reports(test_old_report_content): """Test process reports for old report structure.""" result = tasks.process_reports(test_old_report_content) assert set(result.keys()) == { 'AppleMusicFooter', 'AppleMusicRoyaltyCalculation', 'AppleMusicSummary' } assert ([type(x) for x in result.values()] == [type(StringIO()) for i in result.values()]) reports = [ 'Apple Music Summary', 'Linear Radio Service', 'Apple Music Royalty Calculation', 'Apple Music Footer' ] @pytest.mark.parametrize('report', reports) def test_get_report_normal_structure(test_normal_content, report): """Test get report for normal report structure.""" content = tasks.preprocess_content(test_normal_content) result = tasks.get_report(report, content) result['csv_obj'].seek(0) df = pd.read_csv(result['csv_obj'], sep='\t') assert result['report_name'] == report.replace(' ', '') assert (df.columns.to_list() == config.expected_reports[result['report_name']]['expected_columns']) @pytest.mark.parametrize('report', reports) def test_get_report_old_structure(test_old_report_content, report): """Test get report for older report structure.""" content = tasks.preprocess_content(test_old_report_content) result = tasks.get_report(report, content) expected = config.expected_reports[result['report_name']][ 'expected_columns'] if report != 'Linear Radio Service': result['csv_obj'].seek(0) df = pd.read_csv(result['csv_obj'], sep='\t') assert (df.columns.to_list() == expected) else: assert result['csv_obj'] is None assert result['report_name'] == report.replace(' ', '')