from collections.abc import Iterator from pathlib import Path from typing import Any from unittest import mock import pytest from anydi import Container from fansifter_common.adapters.twilio.client import TwilioClient from fansifter_common.testing.factories import FactoryService from jinja2sql import Jinja2SQL from app.adapters.db import Database, Model from app.adapters.ows_text_campaigns import OwsTextCampaignsClient from app.config import Settings, settings as _settings from app.container import container as _container from tests.unit.adapters.db.base import TestDatabase from tests.unit.types import BuildModel, CreateModel 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 _settings @pytest.fixture(scope="session") def container() -> Container: return _container @pytest.fixture(scope="session") def db(container: Container) -> Iterator[Database]: # Create the database file before the tests db_path = Path(__file__).parent.parent.parent / "test.db" db = TestDatabase( url="sqlite:///" + str(db_path), session_args={ "expire_on_commit": False, "autoflush": True, }, jinja2sql=container.resolve(Jinja2SQL), ) Model.metadata.create_all(bind=db.engine) yield db db.close() Model.metadata.drop_all(bind=db.engine) # Delete the database file after the tests 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 marker is None: yield return db: Database = request.getfixturevalue("db") with db.engine.begin() as conn, db.session_factory(bind=conn): try: yield finally: conn.rollback() @pytest.fixture(scope="session") def ows_text_campaigns_client_mock() -> mock.MagicMock: return mock.MagicMock(spec=OwsTextCampaignsClient) @pytest.fixture(autouse=True) def _reset_ows_text_campaigns_client_mock( ows_text_campaigns_client_mock: mock.MagicMock, ) -> None: ows_text_campaigns_client_mock.reset_mock(return_value=True, side_effect=True) @pytest.fixture(scope="session", autouse=True) def twilio_client_mock() -> mock.MagicMock: return mock.MagicMock(spec=TwilioClient) @pytest.fixture(autouse=True) def _reset_twilio_client_mock(twilio_client_mock: mock.MagicMock) -> None: twilio_client_mock.reset_mock(return_value=True, side_effect=True) @pytest.fixture(scope="session", autouse=True) def override_test_dependencies( container: Container, db: TestDatabase, ows_text_campaigns_client_mock: mock.MagicMock, twilio_client_mock: mock.MagicMock, ) -> None: """Override container dependencies.""" container.register( Database, lambda: db, scope="singleton", override=True, ) container.register( OwsTextCampaignsClient, lambda: ows_text_campaigns_client_mock, scope="singleton", override=True, ) container.register( TwilioClient, lambda: twilio_client_mock, scope="singleton", override=True, ) @pytest.fixture(scope="session") def factory_service() -> FactoryService: factory_service = FactoryService() factory_service.scan("tests.unit.factories") return factory_service @pytest.fixture def build_model(factory_service: FactoryService) -> BuildModel: def wrapper[T](model: type[T], **kwargs: Any) -> T: return factory_service.build(model, **kwargs) return wrapper @pytest.fixture def create_model(factory_service: FactoryService, db: Database) -> CreateModel: def wrapper[T: Model](model: type[T], **kwargs: Any) -> T: return factory_service.create(db.session, model, **kwargs) return wrapper