From 24987d473bf2843fa49c4d723b998a09b0d52f5f Mon Sep 17 00:00:00 2001 From: edpyt Date: Sat, 4 May 2024 14:35:35 +0300 Subject: [PATCH] fix: before replace IoC --- poetry.lock | 19 ++++++++++++- pyproject.toml | 1 + src/api/di/__init__.py | 3 --- src/api/di/config.py | 11 -------- src/api/di/main.py | 7 ----- src/api/main.py | 15 +++++++++-- src/infrastructure/config/broker.py | 14 +++++++--- src/infrastructure/config/config.py | 6 ++--- src/infrastructure/di/main.py | 9 ++++--- src/infrastructure/di/message_queue.py | 7 +++-- src/infrastructure/message_broker/main.py | 10 ++----- .../message_broker/message_broker.py | 1 + .../db/interceptors/publish_event.py | 2 +- tests/integration/conftest.py | 27 ++++++++++++++++--- tests/integration/fixtures/__init__.py | 26 +++++++++--------- tests/integration/fixtures/api.py | 11 ++++++-- tests/integration/fixtures/events.py | 17 +++++++++--- tests/integration/menu/test_repository.py | 3 +-- 18 files changed, 121 insertions(+), 68 deletions(-) delete mode 100644 src/api/di/__init__.py delete mode 100644 src/api/di/config.py delete mode 100644 src/api/di/main.py diff --git a/poetry.lock b/poetry.lock index 5493dd3..3f56d8e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -967,6 +967,23 @@ pytest = ">=7.0.0,<9" docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] +[[package]] +name = "pytest-mock" +version = "3.14.0" +description = "Thin-wrapper around the mock package for easier use with pytest" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0"}, + {file = "pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f"}, +] + +[package.dependencies] +pytest = ">=6.2.5" + +[package.extras] +dev = ["pre-commit", "pytest-asyncio", "tox"] + [[package]] name = "python-dateutil" version = "2.8.2" @@ -1444,4 +1461,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "bbafa85eb7ee6044122f239eed1c5df1235dbe5d29dabaab5d482a48c17cbc0a" +content-hash = "5107c32a8f0fad461ac022261e1ff4612d6eb56b9f4bed1d49789920180fb8f5" diff --git a/pyproject.toml b/pyproject.toml index 503dc74..d3dc0f2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,6 +36,7 @@ optional = true pytest = "^8.0.2" pytest-asyncio = "^0.23.5" testcontainers = "4.4.0" +pytest-mock = "^3.14.0" [tool.poetry.group.dev] optional = true diff --git a/src/api/di/__init__.py b/src/api/di/__init__.py deleted file mode 100644 index c75d2f2..0000000 --- a/src/api/di/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .main import setup_api_di - -__all__ = ("setup_api_di",) diff --git a/src/api/di/config.py b/src/api/di/config.py deleted file mode 100644 index 6b91da4..0000000 --- a/src/api/di/config.py +++ /dev/null @@ -1,11 +0,0 @@ -from rodi import Container - -from src.api.config import create_config_obj - - -def setup_config_di(di_container: Container) -> None: - config = create_config_obj() - - di_container.add_instance(config) - di_container.add_instance(config.jwt_config) - di_container.add_instance(config.db_config) diff --git a/src/api/di/main.py b/src/api/di/main.py deleted file mode 100644 index e345140..0000000 --- a/src/api/di/main.py +++ /dev/null @@ -1,7 +0,0 @@ -from rodi import Container - -from .config import setup_config_di - - -def setup_api_di(di_container: Container) -> None: - setup_config_di(di_container) diff --git a/src/api/main.py b/src/api/main.py index 1e9b2dc..ee75f2f 100644 --- a/src/api/main.py +++ b/src/api/main.py @@ -1,4 +1,5 @@ from blacksheep import Application +from nats import NATS from src.api.auth.handler import BuberDinnerAuthHandler from src.api.docs import setup_docs @@ -10,10 +11,12 @@ def build_api() -> Application: """Build BlackSheep application""" - di_container = build_application_container() - app = Application(services=di_container, show_error_details=True) + app = Application(show_error_details=True) setup_app(app) + app.on_start += setup_di + app.on_stop += close_connections + return app @@ -27,3 +30,11 @@ def setup_app(app: Application) -> None: app.use_authentication().add(BuberDinnerAuthHandler()) app.use_authorization() + + +async def setup_di(app: Application) -> None: + await build_application_container(app.services) + + +async def close_connections(app: Application) -> None: + await app.services.resolve(NATS).close() diff --git a/src/infrastructure/config/broker.py b/src/infrastructure/config/broker.py index c78bf6c..473c7dd 100644 --- a/src/infrastructure/config/broker.py +++ b/src/infrastructure/config/broker.py @@ -5,9 +5,17 @@ class BrokerConfig: host: str = "localhost" port: int = 4222 - login: str = "admin" - password: str = "admin" + login: str | None = None + password: str | None = None @property def full_url(self) -> str: - return f"nats://{self.login}:{self.password}@{self.host}:{self.port}" + conn_url = "nats://" + + if self.login: + conn_url += self.login + if self.password: + conn_url += self.password + + conn_url += f"@{self.host}:{self.port}" + return conn_url diff --git a/src/infrastructure/config/config.py b/src/infrastructure/config/config.py index 7d108be..4ac76f3 100644 --- a/src/infrastructure/config/config.py +++ b/src/infrastructure/config/config.py @@ -7,9 +7,9 @@ class Config(BaseModel): - jwt_config: JWTConfig - db_config: DBConfig - broker_config: BrokerConfig + jwt_config: JWTConfig = JWTConfig(jwt_secret="secret", expiry_minutes=99) # noqa: S106 + db_config: DBConfig = DBConfig() + broker_config: BrokerConfig = BrokerConfig() def create_config_obj() -> Config: diff --git a/src/infrastructure/di/main.py b/src/infrastructure/di/main.py index 8cff863..5cdc418 100644 --- a/src/infrastructure/di/main.py +++ b/src/infrastructure/di/main.py @@ -14,17 +14,18 @@ @lru_cache -def build_application_container() -> Container: - container: Container = Container() +async def build_application_container(container: Container | None = None) -> Container: + if container is None: + container = Container() container.add_instance(logging.getLogger(__name__), logging.Logger) container.add_instance(setup_retort()) - setup_config_di(container) + config = setup_config_di(container) setup_extra_di(container) setup_mapper_di(container) setup_mediatr_di(container) - setup_message_queue_di(container) + await setup_message_queue_di(container, config) setup_persistence_di(container) return container diff --git a/src/infrastructure/di/message_queue.py b/src/infrastructure/di/message_queue.py index c6adefd..6d97396 100644 --- a/src/infrastructure/di/message_queue.py +++ b/src/infrastructure/di/message_queue.py @@ -2,16 +2,19 @@ from rodi import Container from src.application.common.events.event_bus import EventBus +from src.infrastructure.config.config import Config from src.infrastructure.event_bus.event_bus import EventBusImpl from src.infrastructure.message_broker.interface import MessageBroker from src.infrastructure.message_broker.main import make_broker_connection from src.infrastructure.message_broker.message_broker import MessageBrokerImpl -def setup_message_queue_di(container: Container) -> None: +async def setup_message_queue_di(container: Container, config: Config) -> None: + nats_conn = await make_broker_connection(conn_url=config.broker_config.full_url) + setup_events_di(container) - container.add_singleton_by_factory(make_broker_connection, NATS) + container.add_instance(nats_conn, NATS) container.add_transient(MessageBroker, MessageBrokerImpl) diff --git a/src/infrastructure/message_broker/main.py b/src/infrastructure/message_broker/main.py index 87a2a18..2f89be9 100644 --- a/src/infrastructure/message_broker/main.py +++ b/src/infrastructure/message_broker/main.py @@ -1,11 +1,5 @@ -from typing import AsyncGenerator - import nats -from src.infrastructure.config.broker import BrokerConfig - -async def make_broker_connection(config: BrokerConfig) -> AsyncGenerator[nats.NATS, None]: - conn = await nats.connect(config.full_url) - yield conn - await conn.close() +async def make_broker_connection(conn_url: str) -> nats.NATS: + return await nats.connect(conn_url) diff --git a/src/infrastructure/message_broker/message_broker.py b/src/infrastructure/message_broker/message_broker.py index e58dac5..655c874 100644 --- a/src/infrastructure/message_broker/message_broker.py +++ b/src/infrastructure/message_broker/message_broker.py @@ -1,3 +1,4 @@ + import orjson from nats import NATS diff --git a/src/infrastructure/persistence/db/interceptors/publish_event.py b/src/infrastructure/persistence/db/interceptors/publish_event.py index da15251..110a8e4 100644 --- a/src/infrastructure/persistence/db/interceptors/publish_event.py +++ b/src/infrastructure/persistence/db/interceptors/publish_event.py @@ -28,7 +28,7 @@ async def saving_changes( async def publish_domain_events(entity: object, context: QueryContext) -> None: - mediator = build_application_container().resolve(Mediator) + mediator = (await build_application_container()).resolve(Mediator) # Get hold of domain events events = entity.events # type: ignore [attr-defined] diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index 91ee135..355808d 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -2,23 +2,42 @@ import pytest from blacksheep import Application +from pytest_mock import MockerFixture from src.api.main import build_api +from src.infrastructure.config.broker import BrokerConfig +from src.infrastructure.config.config import Config +from src.infrastructure.config.db import DBConfig from src.infrastructure.config.jwt import JWTConfig -from tests.integration.di import DIOverride, setup_test_di - @pytest.fixture(name="jwt_config", scope="session") def create_jwt_config() -> JWTConfig: return JWTConfig(jwt_secret="test-secret", expiry_minutes=9999) +@pytest.fixture(name="config", scope="session", autouse=True) +def create_config( + session_mocker: MockerFixture, + jwt_config: JWTConfig, + db_config: DBConfig, + broker_config: BrokerConfig, +) -> Config: + config = Config( + jwt_config=jwt_config, + db_config=db_config, + broker_config=broker_config, + ) + + session_mocker.patch("src.infrastructure.config.config.create_config_obj", return_value=config) + session_mocker.patch("src.infrastructure.di.config.create_config_obj", return_value=config) + + return config + + @pytest.fixture(name="app", scope="session") async def create_app(jwt_config: JWTConfig) -> AsyncGenerator[Application, None]: app: Application = build_api() - setup_test_di(app, DIOverride(jwt_config)) - await app.start() yield app await app.stop() diff --git a/tests/integration/fixtures/__init__.py b/tests/integration/fixtures/__init__.py index e0fd778..90a260e 100644 --- a/tests/integration/fixtures/__init__.py +++ b/tests/integration/fixtures/__init__.py @@ -4,6 +4,7 @@ from .db.sqla import create_async_sa_session, create_sa_session_factory, create_test_sa_engine from .events import ( connect_nats, + create_broker_config, create_event_bus, create_nats_container, create_nats_message_broker, @@ -12,22 +13,23 @@ from .repositories import create_menu_repository, create_user_factory, create_user_repository __all__ = ( - "create_db_config", - "create_postgres_db", + "connect_nats", "create_alembic_config", - "run_db_migrations", "create_async_sa_session", - "create_sa_session_factory", - "create_test_sa_engine", "create_auth_token", - "di_overrides", - "test_client", - "create_menu_repository", - "create_user_repository", - "create_user_factory", + "create_broker_config", + "create_db_config", "create_event_bus", + "create_menu_repository", "create_nats_container", - "menu_create_handler", "create_nats_message_broker", - "connect_nats", + "create_postgres_db", + "create_sa_session_factory", + "create_test_sa_engine", + "create_user_factory", + "create_user_repository", + "di_overrides", + "menu_create_handler", + "run_db_migrations", + "test_client", ) diff --git a/tests/integration/fixtures/api.py b/tests/integration/fixtures/api.py index 459cc94..031f608 100644 --- a/tests/integration/fixtures/api.py +++ b/tests/integration/fixtures/api.py @@ -3,13 +3,20 @@ from blacksheep.testing import TestClient from src.application.persistence.menu_repo import MenuRepository from src.application.persistence.user_repo import UserRepository +from src.infrastructure.config.jwt import JWTConfig from tests.integration.di import DIOverride, setup_test_di @pytest.fixture -def di_overrides(user_repo: UserRepository, menu_repo: MenuRepository) -> list[DIOverride]: - return [DIOverride(user_repo, UserRepository), DIOverride(menu_repo, MenuRepository)] +def di_overrides( + jwt_config: JWTConfig, user_repo: UserRepository, menu_repo: MenuRepository, +) -> list[DIOverride]: + return [ + DIOverride(jwt_config), + DIOverride(user_repo, UserRepository), + DIOverride(menu_repo, MenuRepository), + ] @pytest.fixture diff --git a/tests/integration/fixtures/events.py b/tests/integration/fixtures/events.py index 81f2c90..0789456 100644 --- a/tests/integration/fixtures/events.py +++ b/tests/integration/fixtures/events.py @@ -4,8 +4,10 @@ import pytest from src.application.common.events.event_bus import EventBus from src.application.dinners.events.menu_create_handler import MenuCreateHandler +from src.infrastructure.config.broker import BrokerConfig from src.infrastructure.event_bus.event_bus import EventBusImpl from src.infrastructure.message_broker.interface import MessageBroker +from src.infrastructure.message_broker.main import make_broker_connection from src.infrastructure.message_broker.message_broker import MessageBrokerImpl from testcontainers.nats import NatsContainer @@ -16,12 +18,21 @@ async def create_nats_container() -> Generator[NatsContainer, None, None]: yield nats_container +@pytest.fixture(name="broker_config", scope="session") +def create_broker_config(nats_container: NatsContainer) -> BrokerConfig: + host, port = nats_container.nats_host_and_port() + return BrokerConfig(host=host, port=port) + + @pytest.fixture(name="nats_conn") async def connect_nats(nats_container: NatsContainer) -> AsyncGenerator[nats.NATS, None]: conn_url = nats_container.nats_uri() - nats_conn = await nats.connect(conn_url) - yield nats_conn - await nats_conn.close() + + conn = await make_broker_connection(conn_url=conn_url) + + yield conn + + await conn.close() @pytest.fixture(name="message_broker") diff --git a/tests/integration/menu/test_repository.py b/tests/integration/menu/test_repository.py index 674d7fe..e899285 100644 --- a/tests/integration/menu/test_repository.py +++ b/tests/integration/menu/test_repository.py @@ -1,4 +1,4 @@ -import pytest + from src.application.menu.dto.average_rating import AverageRatingDTO from src.application.persistence.menu_repo import MenuRepository from src.domain.host.vo.host_id import HostId @@ -12,7 +12,6 @@ async def test_get_all_menu(menu_repo: MenuRepository) -> None: assert resp == [] -@pytest.mark.skip("Error with IoC container. https://github.com/edpyt/buber_dinner/issues/14") async def test_add_menu(menu_repo: MenuRepository) -> None: menu = Menu.create( name="test",