import pathlib from collections.abc import Iterator from unittest import mock import fakeredis import pytest import sqlalchemy as sa from fansifter_common.adapters.db import Database from fansifter_common.adapters.db.models.utils import autodiscover_models from fansifter_common.adapters.db.utils import check_db_alive from fansifter_common.adapters.ows_account import OwsAccountClient from fansifter_common.adapters.sendgrid import SendGridClient from fansifter_common.adapters.stripo import StripoClient from pytest_docker import Services from app.adapters.aws import S3Client from app.adapters.db import Model, db from app.config import settings def pytest_addoption(parser: pytest.Parser) -> None: parser.addoption( "--docker-mode", choices=["external", "auto"], dest="docker_mode", default="auto", help=( "Specify Docker usage mode: 'external' (Docker already running) " "or 'auto' (let tests start it)" ), ) def pytest_configure(config: pytest.Config) -> None: config.addinivalue_line( "markers", "db: mark test as using the db", ) @pytest.fixture(scope="session") def docker_compose_file(pytestconfig: pytest.Config) -> list[pathlib.Path]: base_dir = pytestconfig.rootpath / "tests/unit" return [base_dir / "docker-compose.yaml"] def get_db_url(host: str | None = None, port: int | None = None) -> sa.URL: return sa.URL.create( drivername="postgresql+psycopg", username=settings.snowflake_user, password=settings.snowflake_password.get_secret_value() if settings.snowflake_password else None, host=host or settings.snowflake_host, port=port or settings.snowflake_port, database=settings.snowflake_database, ) @pytest.fixture(scope="session") def _docker_snowflake_setup(docker_services: Services, docker_ip: str) -> None: port = docker_services.port_for("snowflake", 5443) assert port == 7443 url = get_db_url(host=docker_ip, port=port) docker_services.wait_until_responsive( timeout=60.0, pause=0.5, check=lambda: check_db_alive(url), ) @pytest.fixture(scope="session", autouse=True) def _db() -> Iterator[None]: with mock.patch( "app.adapters.db.db.__wrapped__", new=Database( url=get_db_url(), session_args={ "expire_on_commit": False, "autoflush": True, }, ), ): yield @pytest.fixture(scope="session") def _db_setup(request: pytest.FixtureRequest) -> Iterator[None]: if ( db.engine.dialect.name.startswith("postgres") and request.config.getoption("docker_mode") == "auto" ): request.getfixturevalue("_docker_snowflake_setup") autodiscover_models("app") Model.metadata.create_all(bind=db.engine) yield Model.metadata.drop_all(bind=db.engine) @pytest.fixture(autouse=True) def _db_marker(request: pytest.FixtureRequest) -> Iterator[None]: """Use the `db` marker to run a test in a transaction.""" marker = request.node.get_closest_marker("db") if not marker: yield return _ = request.getfixturevalue("_db_setup") with db.global_context(), db.rollback_transaction(): yield @pytest.fixture(scope="session", autouse=True) def ows_account_client_mock() -> Iterator[mock.MagicMock]: m = mock.MagicMock(spec=OwsAccountClient) with mock.patch("app.adapters.ows.ows_account_client.__wrapped__", new=m): yield m @pytest.fixture(autouse=True) def _reset_ows_account_client_mock(ows_account_client_mock: mock.MagicMock) -> None: ows_account_client_mock.reset_mock(return_value=True, side_effect=True) @pytest.fixture(scope="session", autouse=True) def sendgrid_client_mock() -> Iterator[mock.MagicMock]: m = mock.MagicMock(spec=SendGridClient) with mock.patch("app.adapters.sendgrid.sendgrid_client.__wrapped__", new=m): yield m @pytest.fixture(autouse=True) def _reset_sendgrid_client_mock(sendgrid_client_mock: mock.MagicMock) -> None: sendgrid_client_mock.reset_mock(return_value=True, side_effect=True) @pytest.fixture(scope="session", autouse=True) def stripo_client_mock() -> Iterator[mock.MagicMock]: m = mock.MagicMock(spec=StripoClient) with mock.patch("app.adapters.stripo.stripo_client.__wrapped__", new=m): yield m @pytest.fixture(autouse=True) def _reset_stripo_client_mock(stripo_client_mock: mock.MagicMock) -> None: stripo_client_mock.reset_mock(return_value=True, side_effect=True) @pytest.fixture(scope="session", autouse=True) def s3_client_mock() -> Iterator[mock.MagicMock]: m = mock.MagicMock(spec=S3Client) with mock.patch("app.adapters.aws.s3_client.__wrapped__", new=m): yield m @pytest.fixture(autouse=True) def _reset_s3_client_mock(s3_client_mock: mock.MagicMock) -> None: s3_client_mock.reset_mock(return_value=True, side_effect=True) @pytest.fixture(scope="session", autouse=True) def redis_client() -> Iterator[fakeredis.FakeRedis]: r = fakeredis.FakeRedis() with mock.patch("app.adapters.redis.redis_client.__wrapped__", new=r): yield r