"""Integration tests for the generate_attachments lambda.""" import logging from typing import Any import pytest from sqlalchemy.orm import Session from tests import config from tests.integration.generate_attachments.seed_data import seed_data_error_validation from tests.integration.generate_attachments.seed_data import ( seed_data_legacy_revenue_detail_full_d3, ) from tests.integration.generate_attachments.seed_data import ( seed_data_legacy_revenue_detail_full_regular, ) from tests.integration.generate_attachments.seed_data import ( seed_data_legacy_revenue_detail_full_subaccount, ) from tests.integration.generate_attachments.seed_data import ( seed_data_legacy_revenue_detail_physical, ) from tests.integration.generate_attachments.seed_data import ( seed_data_legacy_revenue_detail_physical_d3, ) from tests.integration.generate_attachments.seed_data import ( seed_data_legacy_revenue_detail_physical_subaccount, ) from tests.integration.generate_attachments.seed_data import seed_data_nr_label_revenue from tests.integration.generate_attachments.seed_data import ( seed_data_nr_performer_revenue, ) from tests.integration.generate_attachments.seed_data import seed_data_revenue_detail_d3 from tests.integration.generate_attachments.seed_data import ( seed_data_revenue_detail_regular, ) from tests.integration.generate_attachments.seed_data import ( seed_data_revenue_detail_scientific, ) from tests.integration.generate_attachments.seed_data import ( seed_data_revenue_detail_subaccount, ) from tests.integration.generate_attachments.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_GENERATE_ATTACHMENTS = config.LAMBDA_GENERATE_ATTACHMENTS S3_BUCKET = config.MONEYHUB_ATTACHMENTS_BUCKET @pytest.mark.parametrize( 'setup', [ seed_data_revenue_detail_regular, seed_data_legacy_revenue_detail_full_regular, seed_data_legacy_revenue_detail_physical, seed_data_nr_performer_revenue, seed_data_nr_label_revenue, seed_data_revenue_detail_d3, seed_data_legacy_revenue_detail_full_d3, seed_data_legacy_revenue_detail_physical_d3, seed_data_revenue_detail_subaccount, seed_data_legacy_revenue_detail_full_subaccount, seed_data_legacy_revenue_detail_physical_subaccount, seed_data_revenue_detail_scientific, ], indirect=True, ) def test_lambda_generate_attachments( setup: dict[str, Any], db_session: Session, lambda_client: Any, s3_client: Any, ) -> None: """Verify the lambda generates a report and uploads it to S3. Flow: seed DB row → invoke lambda → poll until complete → assert S3 object exists → verify S3 key → verify file content. """ statement_attachment_id = setup['statement_attachment_id'] account_id = setup['account_id'] logger.info( 'Invoking lambda for statement_attachment_id=%s account_id=%s', statement_attachment_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_GENERATE_ATTACHMENTS, {'statement_attachment_id': statement_attachment_id}, invocation_type='Event', assertion=False, ) assert response['StatusCode'] in (200, 202), 'Lambda invocation failed' poll_conditions: dict[str, Any] = {'account_id': account_id} if subaccount_id := setup.get('subaccount_id'): poll_conditions['subaccount_id'] = subaccount_id result = poll_until_status_complete( db_session, statement_attachment_id, poll_conditions ) 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["statement_period_id"]}' f'/{statement_attachment_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_error_validation], indirect=True) def test_lambda_generate_attachments_error_validation( setup: dict[str, Any], db_session: Session, lambda_client: Any, ) -> None: """Verify the lambda handles missing tax information by setting the attachment status to error. Uses account_id=69006 / contract_id=536526, which has no tax details in ows-payee. Flow: seed DB row → invoke lambda → poll until error → assert status and failure_reason. """ statement_attachment_id = setup['statement_attachment_id'] account_id = setup['account_id'] logger.info( 'Invoking lambda for statement_attachment_id=%s account_id=%s', statement_attachment_id, account_id, ) response = lambda_client.invoke( LAMBDA_GENERATE_ATTACHMENTS, {'statement_attachment_id': statement_attachment_id}, invocation_type='Event', assertion=False, ) assert response['StatusCode'] in (200, 202), 'Lambda invocation failed' result = poll_until_status_complete( db_session, statement_attachment_id, {'account_id': account_id}, target_status='error', ) logger.info( 'statement_attachment_status=%s failure_reason=%s', result['statement_attachment_status'], result['failure_reason'], ) assert result['statement_attachment_status'] == 'error' assert result['failure_reason'] == 'missing_tax_information'