import os from collections.abc import Iterator from pathlib import Path from typing import Any from unittest import mock import httpx import pytest from anydi import Container from fansifter_common.adapters.db.base import Database from fansifter_common.testing.factories import FactoryService from jinja2sql import Jinja2SQL from app.config.container import initialize_container from app.config.settings import Settings, initialize_settings from app.connectors.aws.s3 import S3Client from app.connectors.database.base import ReportingDB from app.connectors.database.models import ReportingModel from app.connectors.database.repository import AdReportingRepository from app.processors import ( CreativeProcessorsType, MetaCreativeProcessor, TiktokCreativeProcessor, ) from tests.unit.adapters.db.base import TestDatabase from tests.unit.types import CreateReportingModel def pytest_configure(config: pytest.Config) -> None: config.addinivalue_line( "markers", "db: mark test as using the db", ) @pytest.fixture(scope="session") def settings() -> Settings: return initialize_settings() @pytest.fixture(scope="session") def container(settings: Settings) -> Container: return initialize_container(settings) @pytest.fixture(scope="session", autouse=True) def reporting_db(container: Container) -> Iterator[Database]: db_path = Path(__file__).parent.parent.parent / "test.db" jinja2sql = container.resolve(Jinja2SQL) db = TestDatabase( url="sqlite:///" + str(db_path), session_args={ "expire_on_commit": False, "autoflush": True, }, jinja2sql=jinja2sql, ) ReportingModel.metadata.create_all(bind=db.engine) with container.override(ReportingDB, db): yield db db.close() ReportingModel.metadata.drop_all(bind=db.engine) db_path.unlink(missing_ok=True) @pytest.fixture(autouse=True) def _db_marker(request: pytest.FixtureRequest) -> Iterator[None]: marker = request.node.get_closest_marker("db") if not marker: yield return reporting_db: Database = request.getfixturevalue("reporting_db") with reporting_db.rollback_transaction(): yield @pytest.fixture(scope="function") def ad_reporting_repository( reporting_db: ReportingDB, ) -> AdReportingRepository: return AdReportingRepository( db=reporting_db, meta_connection_table="meta_ad_reporting_connection", tiktok_connection_table="tiktok_ad_reporting_connection", google_connection_table="google_ad_reporting_connection", max_fails_count=50, ) @pytest.fixture(scope="function") def ad_reporting_repository_mock() -> mock.MagicMock: return mock.MagicMock() @pytest.fixture(scope="function") def s3_client_mock() -> mock.MagicMock: return mock.MagicMock(spec=S3Client) @pytest.fixture(scope="session") def image() -> bytes: tests_path = os.path.dirname(os.path.abspath(__file__)) with open(f"{tests_path}/data/image.png", "rb") as fo: return fo.read() @pytest.fixture(scope="session") def web_request() -> httpx.Request: return httpx.Request(method="GET", url="test") @pytest.fixture(scope="session") def web_response_mock(web_request: httpx.Request, image: bytes) -> httpx.Response: return httpx.Response( content=image, status_code=200, headers={"Content-Type": "image/png"}, request=web_request, ) @pytest.fixture(scope="session") def not_found_response_mock(web_request: httpx.Request) -> httpx.Response: return httpx.Response( content=None, status_code=404, request=web_request, ) @pytest.fixture(scope="function") def web_client_mock(web_response_mock: httpx.Response) -> mock.MagicMock: client_mock = mock.MagicMock(spec=httpx.AsyncClient) client_mock.get.return_value = web_response_mock return client_mock @pytest.fixture(scope="function") def processors( web_client_mock: mock.MagicMock, s3_client_mock: mock.MagicMock, assets_s3_bucket_name: str, assets_s3_raw_path: str, assets_s3_thumbnail_path: str, ) -> CreativeProcessorsType: return { "META": MetaCreativeProcessor( web_client=web_client_mock, s3_client=s3_client_mock, assets_s3_bucket_name=assets_s3_bucket_name, assets_s3_raw_path=assets_s3_raw_path, assets_s3_thumbnail_path=assets_s3_thumbnail_path, ), "TIKTOK": TiktokCreativeProcessor( web_client=web_client_mock, s3_client=s3_client_mock, assets_s3_bucket_name=assets_s3_bucket_name, assets_s3_raw_path=assets_s3_raw_path, assets_s3_thumbnail_path=assets_s3_thumbnail_path, ), "GOOGLE": TiktokCreativeProcessor( web_client=web_client_mock, s3_client=s3_client_mock, assets_s3_bucket_name=assets_s3_bucket_name, assets_s3_raw_path=assets_s3_raw_path, assets_s3_thumbnail_path=assets_s3_thumbnail_path, ), } @pytest.fixture(scope="function") def assets_s3_bucket_name() -> str: return "test" @pytest.fixture(scope="function") def assets_s3_raw_path() -> str: return "test" @pytest.fixture(scope="function") def assets_s3_thumbnail_path() -> str: return "test" @pytest.fixture(scope="session") def factory_service() -> FactoryService: factory_service = FactoryService() return factory_service @pytest.fixture def create_reporting_model( factory_service: FactoryService, reporting_db: ReportingDB ) -> CreateReportingModel: def wrapper[T: ReportingModel](model: type[T], **kwargs: Any) -> T: return factory_service.create(reporting_db.session, model, **kwargs) return wrapper