"""Lambda test module.""" from logging import INFO from unittest.mock import DEFAULT, MagicMock, patch from unittest import TestCase import pymysql from pymysql import OperationalError from lambdacommon import util def test_util(): """Test util.say_hello function.""" util.say_hello() assert True, 'Something terrible has happened.' @patch('lambdacommon.util.log_connection_id') @patch('lambdacommon.util.pymysql.connect') def test_mysql_connection(mock_connect, mock_log_connection_id): """Test util.mysql_connection context manager.""" util.LOG_CONNECTION = None with util.mysql_connection('host', 'user', 'password', 'database', 5, 3306): mock_connect.assert_called_with( 'host', user='user', passwd='password', db='database', connect_timeout=5, port=3306, cursorclass=pymysql.cursors.DictCursor, autocommit=True, ) mock_connect.return_value.close.assert_called() mock_log_connection_id.assert_not_called() mock_log_connection_id.reset_mock() util.LOG_CONNECTION = 'True' with util.mysql_connection('host', 'user', 'password', 'database', 5, 3306): pass mock_log_connection_id.assert_called() # reset config util.LOG_CONNECTION = None @patch('lambdacommon.util.pymysql.connect') def test_mysql_connection_error(mock_connect): """Test util.mysql_connection context manager error.""" mock_connect.side_effect = OperationalError('could not connect') try: with util.mysql_connection('host', 'user', 'password', 'database', 5, 3306): pass except OperationalError as e: assert e == mock_connect.side_effect mock_connect.return_value.close.assert_not_called() @patch('lambdacommon.util.pymysql.connect') def test_mysql_deprecated_connection_error(mock_connect): """Test util.mysql_connection context manager error.""" mock_connect.side_effect = [TypeError, DEFAULT] with util.mysql_connection('host', 'user', 'password', 'database', 5, 3306): mock_connect.assert_called_with( host='host', user='user', passwd='password', db='database', connect_timeout=5, port=3306, cursorclass=pymysql.cursors.DictCursor, autocommit=True, ) @patch('lambdacommon.util.logger') def test_log_connection_id(mock_logger): """Test we log connection_id.""" mock_connection = MagicMock() mock_connection.cursor.return_value.__enter__.return_value.fetchone.\ return_value.__getitem__.return_value = dict(conn_id=1234) util.log_connection_id(mock_connection, 10) mock_logger.info.assert_called_with("New DB Connection {'conn_id': 1234} (10)") @patch('lambdacommon.util.mysql_connection') def test_dd_connection(mock_connect): """Test util.dd_connection context manager.""" connection_info = { 'host': 'host', 'user': 'user', 'password': 'password', 'database': 'database', 'port': 3306, } with util.dd_connection(connection_info): mock_connect.assert_called_with(**connection_info) @patch('lambdacommon.util.mysql_connection') def test_ar_connection(mock_connect): """Test util.ar_connection context manager.""" connection_info = { 'host': 'host', 'user': 'user', 'password': 'password', 'database': 'database', 'port': 3306, } with util.ar_connection(connection_info): mock_connect.assert_called_with(**connection_info) @patch('lambdacommon.util.redis.StrictRedis') @patch('lambdacommon.util.fakeredis.FakeStrictRedis') def test_redis_connection(mock_fake_redis_connect, mock_redis_connect): """Test util.redis_connection context manager.""" with util.redis_connection(host='db_host', port=123): mock_redis_connect.assert_called_with( host='db_host', port=123, db=0, socket_timeout=1 ) with util.redis_connection(): mock_fake_redis_connect.assert_called_with() def test_check_arguments_before_call(capsys): """Test util.check_arguments_before_call.""" def test_function(*args): """Test function.""" return args decorated_func = util.check_arguments_before_call(test_function) assert decorated_func(1, 2) == (1, 2) assert decorated_func(1) is None assert capsys.readouterr().out.strip() == 'not 2 arguments (1,)' @patch('lambdacommon.util.api') @patch('lambdacommon.util.initialize') def test_datadog_connection(mock_datadog_initialize, mock_datadog_api): """Test util.datadog_connection context manager.""" with util.datadog_connection(app_key='1', api_key='2') as conn: mock_datadog_initialize.assert_called_with(app_key='1', api_key='2') conn.Metric.send(metric='page.views', points=100) mock_datadog_api.Metric.send.assert_called_with(metric='page.views', points=100) def test_ows_logger(): """Test util.setup_ows_logger.""" dsn = 'https://fakefqdn.theorchard.io:8888/application' environment = 'test' logger_name = 'testing-lambda-common' logger_level = INFO service_name = 'testing-lambda-common' service_version = '1.0.0' app_logger = util.setup_ows_logger( environment, logger_name, logger_level, service_name, service_version, dsn ) assert app_logger.logger.name == 'testing-lambda-common' @patch('lambdacommon.util.AwsLambdaIntegration') @patch('lambdacommon.util.sentry_sdk') def test_sentry_sdk_init(mock_sentry_sdk, mock_aws_lambda_integration): """Test util.sentry_connection context manager.""" mock_sentry = MagicMock(name='init') mock_sentry_sdk.return_value = mock_sentry util.init_sentry_for_lambda() mock_sentry_sdk.init.assert_called() mock_aws_lambda_integration.assert_called() @patch('lambdacommon.util.SENTRY_DSN', 'https://test@sentry.io/123') @patch('lambdacommon.util.ENVIRONMENT', 'test') @patch('lambdacommon.util.AwsLambdaIntegration') @patch('lambdacommon.util.sentry_sdk') def test_init_sentry_for_lambda_basic(mock_sentry_sdk, mock_aws_lambda_integration): """Test basic init_sentry_for_lambda call.""" mock_integration = MagicMock() mock_aws_lambda_integration.return_value = mock_integration util.init_sentry_for_lambda() mock_aws_lambda_integration.assert_called_once() mock_sentry_sdk.init.assert_called_once_with( dsn='https://test@sentry.io/123', environment='test', integrations=[mock_integration], ) @patch('lambdacommon.util.SENTRY_DSN', 'https://test@sentry.io/123') @patch('lambdacommon.util.ENVIRONMENT', 'test') @patch('lambdacommon.util.AwsLambdaIntegration') @patch('lambdacommon.util.sentry_sdk') def test_init_sentry_for_lambda_with_logging_integration( mock_sentry_sdk, mock_aws_lambda_integration ): """Test init_sentry_for_lambda with logging integration.""" mock_aws_integration = MagicMock() mock_aws_lambda_integration.return_value = mock_aws_integration mock_logging_integration = MagicMock() util.init_sentry_for_lambda(logging_integration=mock_logging_integration) mock_aws_lambda_integration.assert_called_once() mock_sentry_sdk.init.assert_called_once_with( dsn='https://test@sentry.io/123', environment='test', integrations=[mock_aws_integration, mock_logging_integration], ) @patch('lambdacommon.util.SENTRY_DSN', 'https://test@sentry.io/123') @patch('lambdacommon.util.ENVIRONMENT', 'test') @patch('lambdacommon.util.AwsLambdaIntegration') @patch('lambdacommon.util.sentry_sdk') def test_init_sentry_for_lambda_with_before_send_callback( mock_sentry_sdk, mock_aws_lambda_integration ): """Test init_sentry_for_lambda with before_send callback.""" mock_aws_integration = MagicMock() mock_aws_lambda_integration.return_value = mock_aws_integration def mock_before_send(event, hint): return event util.init_sentry_for_lambda(before_send=mock_before_send) mock_aws_lambda_integration.assert_called_once() mock_sentry_sdk.init.assert_called_once_with( dsn='https://test@sentry.io/123', environment='test', integrations=[mock_aws_integration], before_send=mock_before_send, ) def test_init_sentry_for_lambda_invalid_before_send(): """Test init_sentry_for_lambda with invalid before_send parameter.""" with TestCase().assertRaises(ValueError) as context: util.init_sentry_for_lambda(before_send='not_callable') assert str(context.exception) == 'before_send must be callable' def test_init_sentry_for_lambda_invalid_sensitive_fields(): """Test init_sentry_for_lambda with invalid sensitive_fields parameter.""" with TestCase().assertRaises(ValueError) as context: util.init_sentry_for_lambda(sensitive_fields='not_a_list') assert str(context.exception) == 'sensitive_fields must be a list' @patch('lambdacommon.util.SENTRY_DSN', 'https://test@sentry.io/123') @patch('lambdacommon.util.ENVIRONMENT', 'test') @patch('lambdacommon.util.AwsLambdaIntegration') @patch('lambdacommon.util.sentry_sdk') @patch('sentry_sdk.scrubber.EventScrubber') @patch('sentry_sdk.scrubber.DEFAULT_DENYLIST', ['password']) @patch('lambdacommon.sentry.SentryScrubber') def test_init_sentry_for_lambda_with_sensitive_fields( mock_sentry_scrubber, mock_event_scrubber, mock_sentry_sdk, mock_aws_lambda_integration, ): """Test init_sentry_for_lambda with sensitive_fields.""" mock_aws_integration = MagicMock() mock_aws_lambda_integration.return_value = mock_aws_integration mock_scrubber_instance = MagicMock() mock_event_scrubber.return_value = mock_scrubber_instance mock_sentry_scrubber_instance = MagicMock() mock_sentry_scrubber_instance.before_send_handler = MagicMock() mock_sentry_scrubber.return_value = mock_sentry_scrubber_instance sensitive_fields = ['secret_key', 'token'] util.init_sentry_for_lambda(sensitive_fields=sensitive_fields) mock_aws_lambda_integration.assert_called_once() mock_event_scrubber.assert_called_once_with( denylist=['password'] + sensitive_fields, recursive=True ) mock_sentry_scrubber.assert_called_once_with(sensitive_fields=sensitive_fields) mock_sentry_sdk.init.assert_called_once_with( dsn='https://test@sentry.io/123', environment='test', integrations=[mock_aws_integration], event_scrubber=mock_scrubber_instance, before_send=mock_sentry_scrubber_instance.before_send_handler, ) @patch('lambdacommon.util.SENTRY_DSN', 'https://test@sentry.io/123') @patch('lambdacommon.util.ENVIRONMENT', 'test') @patch('lambdacommon.util.AwsLambdaIntegration') @patch('lambdacommon.util.sentry_sdk') @patch('sentry_sdk.scrubber.EventScrubber') @patch('sentry_sdk.scrubber.DEFAULT_DENYLIST', ['password']) def test_init_sentry_for_lambda_with_sensitive_fields_and_before_send( mock_event_scrubber, mock_sentry_sdk, mock_aws_lambda_integration ): """Test init_sentry_for_lambda with both sensitive_fields and before_send callback.""" mock_aws_integration = MagicMock() mock_aws_lambda_integration.return_value = mock_aws_integration mock_scrubber_instance = MagicMock() mock_event_scrubber.return_value = mock_scrubber_instance def mock_before_send(event, hint): return event sensitive_fields = ['secret_key', 'token'] util.init_sentry_for_lambda( before_send=mock_before_send, sensitive_fields=sensitive_fields ) mock_aws_lambda_integration.assert_called_once() mock_event_scrubber.assert_called_once_with( denylist=['password'] + sensitive_fields, recursive=True ) mock_sentry_sdk.init.assert_called_once_with( dsn='https://test@sentry.io/123', environment='test', integrations=[mock_aws_integration], event_scrubber=mock_scrubber_instance, before_send=mock_before_send, ) @patch('lambdacommon.util.SENTRY_DSN', 'https://test@sentry.io/123') @patch('lambdacommon.util.ENVIRONMENT', 'test') @patch('lambdacommon.util.AwsLambdaIntegration') @patch('lambdacommon.util.sentry_sdk') @patch('sentry_sdk.scrubber.EventScrubber') @patch('sentry_sdk.scrubber.DEFAULT_DENYLIST', ['password']) def test_init_sentry_for_lambda_with_all_parameters( mock_event_scrubber, mock_sentry_sdk, mock_aws_lambda_integration ): """Test init_sentry_for_lambda with all parameters.""" mock_aws_integration = MagicMock() mock_aws_lambda_integration.return_value = mock_aws_integration mock_logging_integration = MagicMock() mock_scrubber_instance = MagicMock() mock_event_scrubber.return_value = mock_scrubber_instance def mock_before_send(event, hint): return event sensitive_fields = ['secret_key', 'token'] util.init_sentry_for_lambda( logging_integration=mock_logging_integration, before_send=mock_before_send, sensitive_fields=sensitive_fields, ) mock_aws_lambda_integration.assert_called_once() mock_event_scrubber.assert_called_once_with( denylist=['password'] + sensitive_fields, recursive=True ) mock_sentry_sdk.init.assert_called_once_with( dsn='https://test@sentry.io/123', environment='test', integrations=[mock_aws_integration, mock_logging_integration], event_scrubber=mock_scrubber_instance, before_send=mock_before_send, )