"""Integration tests for the custom_reports lambda.""" import logging from typing import Any import pytest from sqlalchemy.orm import Session from tests import config from tests.integration.custom_reports.seed_data import ( seed_data_distribution_by_territory, ) from tests.integration.custom_reports.seed_data import ( seed_data_distribution_track_by_statement_period, ) from tests.integration.custom_reports.seed_data import ( seed_data_invalid_statement_periods, ) from tests.integration.custom_reports.seed_data import seed_data_nr_financial_detail from tests.integration.custom_reports.seed_data import ( seed_data_nr_territory_statement_period, ) from tests.integration.custom_reports.utils import poll_until_status_complete from tests.src.utils import s3_key_from_location from tests.src.utils import verify_generated_file_output logger = logging.getLogger(__name__) LAMBDA_CUSTOM_REPORTS = config.LAMBDA_CUSTOM_REPORTS S3_BUCKET = config.MONEYHUB_CUSTOM_REPORTS_BUCKET @pytest.mark.parametrize( 'setup', [ seed_data_distribution_by_territory, seed_data_distribution_track_by_statement_period, seed_data_nr_financial_detail, seed_data_nr_territory_statement_period, ], indirect=True, ) def test_lambda_custom_reports( setup: dict[str, Any], db_session: Session, lambda_client: Any, s3_client: Any, ) -> None: """Verify the lambda generates a custom report and uploads it to S3. Parametrized over: - seed_data_distribution_by_territory: distribution, territory × statement_period - seed_data_distribution_track_by_statement_period: distribution, track × statement_period - seed_data_nr_territory_statement_period: neighbouring_rights, territory × statement_period - seed_data_nr_financial_detail: neighbouring_rights, financial_detail × territory Flow: seed DB row → invoke lambda → poll until complete → assert S3 object exists → verify content. """ report_custom_id = setup['report_custom_id'] account_id = setup['account_id'] logger.info( 'Invoking lambda for report_custom_id=%s account_id=%s', report_custom_id, account_id, ) # Event (async) avoids the 60s boto3 read timeout on QA; result is polled from DB. # assertion=False skips LambdaHandler's StatusCode==200 check — QA returns 202 # for async invocations while local RIE always returns 200 (runs synchronously). response = lambda_client.invoke( LAMBDA_CUSTOM_REPORTS, {'report_custom_id': report_custom_id}, invocation_type='Event', assertion=False, ) assert response['StatusCode'] in (200, 202), 'Lambda invocation failed' result = poll_until_status_complete( db_session, report_custom_id, {'account_id': account_id}, ) logger.info('Report generated at %s', result['file_location']) s3_key = s3_key_from_location(result['file_location'], S3_BUCKET) s3_client.assert_object_exists(S3_BUCKET, s3_key) expected_s3_key = f'{account_id}/{setup["expected_filename"]}.zip' assert s3_key == expected_s3_key verify_generated_file_output( s3_client, S3_BUCKET, s3_key, setup['fixture_file_path'] ) @pytest.mark.parametrize( 'setup', [ seed_data_invalid_statement_periods, ], indirect=True, ) def test_lambda_custom_reports_error_validation( setup: dict[str, Any], db_session: Session, lambda_client: Any, ) -> None: """Verify the lambda sets report_custom_status='error' for invalid input. Flow: seed DB row → invoke lambda → poll until error → assert status. """ report_custom_id = setup['report_custom_id'] account_id = setup['account_id'] logger.info( 'Invoking lambda for report_custom_id=%s account_id=%s', report_custom_id, account_id, ) response = lambda_client.invoke( LAMBDA_CUSTOM_REPORTS, {'report_custom_id': report_custom_id}, invocation_type='Event', assertion=False, ) assert response['StatusCode'] in (200, 202), 'Lambda invocation failed' result = poll_until_status_complete( db_session, report_custom_id, {'account_id': account_id}, target_status='error', ) logger.info('report_custom_status=%s', result['report_custom_status']) assert result['report_custom_status'] == 'error'