"""Shared fixtures for generate_attachments integration tests.""" import logging import os import time from typing import Any from typing import Generator from aws_testing_utils import lambda_handler import pytest from sqlalchemy.orm import Session from tests import config from tests.src import database from tests.src.utils import s3_key_from_location logger = logging.getLogger(__name__) S3_BUCKET = config.MONEYHUB_ATTACHMENTS_BUCKET @pytest.fixture(scope='session') def warm_up_lambda() -> None: """Pre-warm the generate-attachments Lambda. No-op when targeting a local RIE.""" if os.environ.get('LAMBDA_ENDPOINT_URL'): return handler = lambda_handler.LambdaHandler() for _ in range(3): handler.warm_up(config.LAMBDA_GENERATE_ATTACHMENTS) time.sleep(5) @pytest.fixture def setup( request: pytest.FixtureRequest, db_session: Session, s3_client: Any, warm_up_lambda: None, ) -> Generator[dict[str, Any], None, None]: """Seed a statement_attachment row and clean up after the test. Commits the insert immediately so the row is visible to the lambda (which runs in a separate process) before the test begins. Accepts a seed-data callable via indirect parametrize. """ # --- create testing data --- seed_fn = request.param data = seed_fn() statement_period_name = data.pop('statement_period_name', None) fixture_file_path = data.pop('fixture_file_path', None) expected_filename = data.pop('expected_filename', None) # Subaccounts: unique by (account_id, period, subaccount_id). # Others: include statement_attachment_type to avoid deleting sibling attachments. if 'subaccount_id' in data: delete_conditions = { 'account_id': data['account_id'], 'statement_period_id': data['statement_period_id'], 'subaccount_id': data['subaccount_id'], } else: delete_conditions = { 'account_id': data['account_id'], 'statement_period_id': data['statement_period_id'], 'statement_attachment_type': data['statement_attachment_type'], } result = database.insert_entity( db_session, 'statement_attachment', data, id_column='statement_attachment_id', # seed functions use fixed IDs, so delete any leftover row from a previous failed run delete_conditions=delete_conditions, ) # Commit so the row is visible to the lambda before the test body runs db_session.commit() assert result is not None expected = {k: v for k, v in data.items() if k != 'created_at'} assert {k: result[k] for k in expected} == expected logger.info('Seeded statement_attachment id=%s', result['statement_attachment_id']) yield { **result, 'statement_period_name': statement_period_name, 'fixture_file_path': fixture_file_path, 'expected_filename': expected_filename, } # test runs here # --- clean up testing data --- record = database.get_entity( db_session, 'statement_attachment', {'statement_attachment_id': result['statement_attachment_id']}, ) if record and record.get('file_location'): s3_key = s3_key_from_location(record['file_location'], S3_BUCKET) s3_client.delete_if_object_present(S3_BUCKET, s3_key) logger.info('Deleted S3 object %s', s3_key) database.delete_entity_by_id( db_session, 'statement_attachment', 'statement_attachment_id', result['statement_attachment_id'], ) logger.info('Deleted statement_attachment id=%s', result['statement_attachment_id'])