"""Lambda test module.""" import pytest import pandas as pd from unittest.mock import mock_open from unittest.mock import patch from unittest.mock import MagicMock from unittest.mock import call import config from src import app def new_event(run_number, isrc, state, no_log=False): """Create new event.""" event = {'run_number': run_number, 'run_id': 0, 'isrcs': [{'isrc': isrc, 'state': state}]} if no_log: event['no_log'] = True return event class TestApp: """Test App class.""" @pytest.mark.parametrize('event, missing_isrcs', [ (new_event(0, 'testISRC1', 'new'), []), (new_event(10, 'testISRC1', 'new'), []), (new_event(0, 'testISRC1', 'new', True), [])]) @patch('src.app.check_isrcs_in_insights') @patch('src.app.check_isrcs_in_dim_track') @patch('src.app.logger') @patch('src.app.insert_isrcs_to_db') def test_handler(self, insert_isrcs_to_db, mock_logger, mock_check_isrcs_in_dim_track, mock_check_isrcs_in_insights, event, missing_isrcs ): """Test handler function fir sybbsequent run.""" mock_check_isrcs_in_dim_track.return_value = [{'isrc': 'testISRC1'}] mock_check_isrcs_in_insights.return_value = [] result = app.handler(event, None) assert result == {'status': 'OK', 'isrcs': [], 'number_of_missing_isrcs': 0, 'run_number': event['run_number'] + 1, } if event['run_number'] == 0: assert insert_isrcs_to_db.called_once() else: assert insert_isrcs_to_db.not_called() assert insert_isrcs_to_db.not_called() if 'no_log' in event: assert mock_logger.setLevel.called_once() else: assert mock_logger.setLevel.not_called() @patch('src.app.check_isrcs_in_dim_track') def test_handler_exception(self, mock_check_isrcs_in_dim_track): """Test handler function for exception handling.""" event = new_event(0, 'testISRC1', 'new') mock_check_isrcs_in_dim_track.side_effect = Exception('Test exception') with pytest.raises(Exception, match='Test exception'): app.handler(event, None) def test_read_query_from_file(self): """Test read_query_from_file function.""" mock_file_content = 'query { test }' file_path = 'test_query.gql' with (patch('builtins.open', mock_open(read_data=mock_file_content)) as mock_file): content = app.read_query_from_file(file_path) mock_file.assert_called_once_with(file_path, 'r') assert content == mock_file_content def test_evaluate_result_with_results(self): """Test evaluate_result function.""" result = { 'globalSoundRecordingSearchES': { 'items': [ {'item': {'isrc': 'testISRC1'}}, ] } } search_isrc = 'testISRC1' assert app.evaluate_result(result, search_isrc) is True search_isrc = 'testISRC2' assert app.evaluate_result(result, search_isrc) is False def test_evaluate_result_without_results(self): """Test evaluate_result function.""" result = { 'globalSoundRecordingSearchES': { 'items': [] } } search_isrc = 'testISRC1' assert app.evaluate_result(result, search_isrc) is False @patch('src.app.SecretsManagerClient') def test_get_grass_token(self, mock_secret_manager_client): """Test get_grass_token function.""" mock_instance = MagicMock() mock_secret_manager_client.return_value = mock_instance mock_instance.get_secret.return_value = '{"token": "test_token"}' assert app.get_grass_token() == 'test_token' mock_instance.get_secret.assert_called_once_with( config.GRASS_TOKEN_SECRET) @patch('src.app.read_query_from_file') @patch('src.app.SnowflakeClient') def test_get_error_logs(self, mock_snowflake_client, mock_read_query_from_file): """Test get_error_logs function.""" missing_isrcs = [{'isrc': 'isrc1', 'state': 'dim_track'}, {'isrc': 'isrc2', 'state': 'dim_track'}, {'isrc': 'isrc3', 'state': 'dim_track', 'rerun': True}] mock_query_file = 'test query LIST_OF_MISSING_ISRCS' mock_read_query_from_file.return_value = mock_query_file mock_instance = MagicMock() mock_snowflake_client.return_value = mock_instance mock_instance.execute_query.return_value = {'ISRC': ['testISRC1']} assert app.get_error_logs(missing_isrcs) == {'ISRC': ['testISRC1']} mock_read_query_from_file.assert_called_once() mock_instance.execute_query.assert_called_once_with( "test query \'isrc1\', \'isrc2\', \'isrc3\'") @patch('src.app.get_error_logs') @patch('src.app.logger') def test_log_errors(self, mock_logger, mock_get_error_logs): """Test log_errors function.""" missing_isrcs = [ {'isrc': 'testISRC1', 'state': 'new'}, {'isrc': 'testISRC2', 'state': 'dim_track'}, {'isrc': 'testISRC3', 'state': 'dim_track', 'rerun': True},] columns = ['ISRC', 'UPC', 'STATUS', 'NOT_INGEST_REASON', 'ERRORS'] data = [('testISRC1', 'testUPC1', 'testSTATUS1', 'testREASON1', 'testERROR1'), ('testISRC1', 'testUPC1', 'testSTATUS1', 'testREASON1', 'testERROR2'), ('testISRC3', 'testUPC2', 'testSTATUS2', 'testREASON2', 'testERROR3')] mock_get_error_logs.return_value = pd.DataFrame(data, columns=columns) app.log_errors(missing_isrcs) mock_get_error_logs.assert_called_once_with(missing_isrcs) calls = [ call( 'ISRC testISRC2 has no error logs' ' in facts.prod.sme_labelcopy_ingest'), call('ISRC testISRC1 has error logs' ' in facts.prod.sme_labelcopy_ingest:' " [{'upc': 'testUPC1'," " 'status': 'testSTATUS1'," " 'not_ingest_reason': 'testREASON1'," " 'errors': ['testERROR1', 'testERROR2']}]"), call('Rerun: ISRC testISRC3 has error logs' ' in facts.prod.sme_labelcopy_ingest:' " [{'upc': 'testUPC2'," " 'status': 'testSTATUS2'," " 'not_ingest_reason': 'testREASON2'," " 'errors': ['testERROR3']}]")] mock_logger.info.assert_has_calls(calls, any_order=True) def test_filter_by_state(self): """Test filter_by_state function.""" isrcs = [ {'isrc': 'testISRC1', 'ingested_dim_track': False}, {'isrc': 'testISRC2', 'ingested_dim_track': True}, {'isrc': 'testISRC3', 'ingested_dim_track': False}, ] assert app.filter_by_state(isrcs, 'ingested_dim_track', False) == [ {'isrc': 'testISRC1', 'ingested_dim_track': False}, {'isrc': 'testISRC3', 'ingested_dim_track': False}, ] @patch('src.app.SnowflakeClient') @patch('src.app.read_query_from_file') @patch('src.app.log_isrc_state') @patch('src.app.datetime') def test_check_isrcs_in_dim_track(self, mock_datetime, mock_logger, mock_read_query_from_file, mock_snowflake_client): """Test check_isrcs_in_dim_track function.""" sample_data = [ {'isrc': 'testISRC1', 'ingested_dim_track': False}, {'isrc': 'testISRC2', 'ingested_dim_track': True}, {'isrc': 'testISRC3', 'ingested_dim_track': False}, ] mock_read_query_from_file.return_value = 'test query' mock_datetime.now().strftime.return_value = 'test_time' snowflake_client_instance = MagicMock() mock_snowflake_client.return_value = snowflake_client_instance snowflake_client_instance.execute_query.return_value = pd.DataFrame( {'ISRC': ['testISRC1']}, columns=['ISRC']) results = app.check_isrcs_in_dim_track( sample_data, False) assert results == [ {'isrc': 'testISRC1', 'ingested_dim_track': True, 'ingested_dim_track_time': 'test_time'}, {'isrc': 'testISRC2', 'ingested_dim_track': True}, {'isrc': 'testISRC3', 'ingested_dim_track': False}, ] mock_logger.assert_called_once_with( sample_data[0], 'found', 'dim_track') def test_check_isrc_in_dim_track_none_to_check(self): """Test function when no isrcs are in new status.""" sample_data = [ {'isrc': 'testISRC1', 'ingested_dim_track': True} ] results = app.check_isrcs_in_dim_track( sample_data, False) assert results == sample_data @patch('src.app.SnowflakeClient') @patch('src.app.read_query_from_file') @patch('src.app.log_isrc_state') @patch('src.app.datetime') def test_check_isrcs_in_dim_track_final_run(self, mock_datetime, mock_logger, mock_read_query_from_file, mock_snowflake_client): """Test check_isrcs_in_dim_track function final run.""" sample_data = [ {'isrc': 'testISRC1', 'ingested_dim_track': False}, {'isrc': 'testISRC2', 'ingested_dim_track': True}, {'isrc': 'testISRC3', 'ingested_dim_track': False}, ] mock_read_query_from_file.return_value = 'test query' mock_datetime.now().strftime.return_value = 'test_time' snowflake_client_instance = MagicMock() mock_snowflake_client.return_value = snowflake_client_instance snowflake_client_instance.execute_query.return_value = pd.DataFrame( {'ISRC': ['testISRC1']}, columns=['ISRC']) results = app.check_isrcs_in_dim_track(sample_data, True) assert results == [ {'isrc': 'testISRC1', 'ingested_dim_track': True, 'ingested_dim_track_time': 'test_time'}, {'isrc': 'testISRC2', 'ingested_dim_track': True}, {'isrc': 'testISRC3', 'ingested_dim_track': False}, ] calls = [call(sample_data[0], 'found', 'dim_track'), call(sample_data[2], 'missing', 'dim_track')] mock_logger.assert_has_calls(calls, any_order=True) @patch('src.app.GqlClient') @patch('src.app.read_query_from_file') @patch('src.app.get_grass_token') @patch('src.app.insert_isrcs_to_db') def test_check_isrc_in_insights_no_dim_track_status( self, mock_insert_isrcs_to_db, mock_gql_client, mock_get_grass_token, mock_read_query_from_file): """Test function when no isrcs are in dim_tack status.""" sample_data = [ {'isrc': 'testISRC1', 'ingested_dim_track': False}, ] mock_read_query_from_file.return_value = 'test query' mock_get_grass_token.return_value = 'test_token' mock_gql_client_instance = MagicMock() mock_gql_client.return_value = mock_gql_client_instance results = app.check_isrcs_in_insights(sample_data, False) assert results == sample_data assert mock_insert_isrcs_to_db.not_called() @patch('src.app.read_query_from_file') @patch('src.app.get_grass_token') @patch('src.app.GqlClient') @patch('src.app.evaluate_result') @patch('src.app.log_isrc_state') @patch('src.app.log_errors') @patch('src.app.insert_isrcs_to_db') @patch('src.app.datetime') def test_check_isrcs_in_insights( self, mock_datetime, mock_insert_isrcs_to_db, mock_log_errors, mock_logger, mock_eval_result, mock_gql_client, mock_get_grass_token, mock_read_query_from_file): """Test check_isrcs_in_insights function.""" sample_data = [ {'isrc': 'testISRC1', 'ingested_dim_track': False}, {'isrc': 'testISRC2', 'ingested_dim_track': True}, {'isrc': 'testISRC3', 'ingested_dim_track': True}, ] mock_read_query_from_file.return_value = 'test query' mock_get_grass_token.return_value = 'test_token' mock_datetime.now().strftime.return_value = 'test_time' gql_client_instance = MagicMock() mock_gql_client.return_value = gql_client_instance gql_client_instance.return_value = 'test_result' mock_eval_result.side_effect = [False, True] results = app.check_isrcs_in_insights(sample_data, False) assert results == [ {'isrc': 'testISRC1', 'ingested_dim_track': False}, {'isrc': 'testISRC2', 'ingested_dim_track': True}, ] mock_logger.assert_called_once_with( sample_data[2], 'found', 'insights') mock_log_errors.assert_not_called() mock_insert_isrcs_to_db.assert_called_once_with( [{'isrc': 'testISRC3', 'ingested_dim_track': True, 'ingested_insights': True, 'ingested_insights_time': 'test_time', 'test_end_time': 'test_time'}]) @patch('src.app.read_query_from_file') @patch('src.app.get_grass_token') @patch('src.app.GqlClient') @patch('src.app.evaluate_result') @patch('src.app.log_isrc_state') @patch('src.app.log_errors') @patch('src.app.insert_isrcs_to_db') @patch('src.app.datetime') def test_isrcs_in_insights_final_run(self, mock_datetime, mock_insert_isrcs_to_db, mock_log_errors, mock_logger, mock_eval_result, mock_gql_client, mock_get_grass_token, mock_read_query_from_file): """Test check_isrcs_in_insights function final run.""" sample_data = [ {'isrc': 'testISRC1', 'ingested_dim_track': False}, {'isrc': 'testISRC2', 'ingested_dim_track': True}, {'isrc': 'testISRC3', 'ingested_dim_track': True}, ] mock_datetime.now().strftime.return_value = 'test_time' mock_read_query_from_file.return_value = 'test query' mock_get_grass_token.return_value = 'test_token' gql_client_instance = MagicMock() mock_gql_client.return_value = gql_client_instance gql_client_instance.return_value = 'test_result' mock_eval_result.side_effect = [False, False, True] results = app.check_isrcs_in_insights(sample_data, True) assert results == [{'isrc': 'testISRC1', 'ingested_dim_track': False}, {'isrc': 'testISRC2', 'ingested_dim_track': True}] calls = [call(sample_data[0], 'missing', 'insights'), call(sample_data[1], 'missing', 'insights'), call(sample_data[2], 'found', 'insights')] mock_logger.assert_has_calls(calls, any_order=True) mock_log_errors.assert_called_once_with(results) mock_insert_isrcs_to_db.assert_called_once_with( [{'isrc': 'testISRC3', 'ingested_dim_track': True, 'ingested_insights': True, 'ingested_insights_time': 'test_time', 'test_end_time': 'test_time'}]) @pytest.mark.parametrize( 'time_field,value,expected', [ ('test_field', 'NULL', 'NULL'), ('test_field', 'timestring', "'timestring'") ]) def test_get_time_string(self, time_field, value, expected): """Test get_time_string function.""" isrc = {time_field: value} assert app.get_time_string(isrc, time_field) == expected @patch('src.app.datetime') @patch('src.app.SnowflakeClient') @patch('src.app.read_query_from_file') def test_insert_isrcs_to_db(self, mock_read_query_from_file, mock_snowflake_client, mock_datetime): """Test insert_isrcs_to_db function.""" mock_datetime.now().strftime.return_value = 'test_time' mock_instance = MagicMock() mock_snowflake_client.return_value = mock_instance mock_read_query_from_file.return_value = \ ' ' isrcs = [ {'isrc': 'testISRC1', 'upc': 'testUPC1', 'release_date': 'testReleaseDate1', 'ingested_dim_track': True, 'ingested_dim_track_time': 'test_time', 'ingested_insights': True, 'ingested_insights_time': 'test_time', 'test_run_id': 'testRunId', 'test_start_time': 'testStartTime', 'test_end_time': 'testEndTime'}, {'isrc': 'testISRC2', 'upc': 'testUPC2', 'release_date': 'testReleaseDate2', 'ingested_dim_track': False, 'ingested_dim_track_time': 'NULL', 'ingested_insights': False, 'ingested_insights_time': 'NULL', 'test_run_id': 'testRunId', 'test_start_time': 'testStartTime', 'test_end_time': 'NULL'}, ] app.insert_isrcs_to_db(isrcs) mock_instance.execute_query.assert_called_once_with( 'FACTS.QA.ISRC_AVAILABILITY_TEST_RESULTS ' "('testISRC1', 'testUPC1'," " 'testReleaseDate1', 'True', 'test_time'," " 'True', 'test_time', 'testRunId'," " 'testStartTime', 'testEndTime')," " ('testISRC2', 'testUPC2'," " 'testReleaseDate2', 'False', NULL, " "'False', NULL, 'testRunId'," " 'testStartTime', 'test_time')") @pytest.mark.parametrize('isrc, state, expected', [ ({'isrc': 'testISRC1', 'release_date': '20241201'}, 'found', 'ISRC testISRC1 found in insights'), ({'isrc': 'testISRC2', 'release_date': '20241201', 'rerun': True}, 'missing', 'Rerun: ISRC testISRC2 missing from insights')] ) @patch('src.app.logger') def test_log_isrc_state(self, mock_logger, isrc, state, expected,): """Test log_isrc_state function.""" app.log_isrc_state(isrc, state, 'insights') mock_logger.info.assert_called_once_with( expected)