Skip to content

Commit

Permalink
refactor(tests): ♻️ add way to disable plugins in test for api units
Browse files Browse the repository at this point in the history
  • Loading branch information
miragecentury committed Dec 31, 2024
1 parent 01ba679 commit 7b52da7
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 14 deletions.
37 changes: 28 additions & 9 deletions src/python_factory/core/app/base/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@

from .config_abstract import AppConfigAbstract, AppConfigBuilder
from .fastapi_application_abstract import FastAPIAbstract
from .plugins_manager_abstract import ApplicationPluginManagerAbstract
from .plugins_manager_abstract import (
ApplicationPluginManagerAbstract,
PluginsActivationList,
)


class BaseApplication(FastAPIAbstract, ApplicationPluginManagerAbstract):
Expand All @@ -25,11 +28,12 @@ class BaseApplication(FastAPIAbstract, ApplicationPluginManagerAbstract):

ODM_DOCUMENT_MODELS: ClassVar[list[type[Document]]] = []

def __init__(self, config: AppConfigAbstract) -> None:
def __init__(self, config: AppConfigAbstract, plugin_activation_list: PluginsActivationList | None = None) -> None:
"""Instantiate the application.
Args:
config (AppConfigAbstract): The application configuration.
plugin_activation_list (PluginsActivationList | None, optional): The plugins activation list.
Returns:
None
Expand All @@ -47,7 +51,9 @@ def __init__(self, config: AppConfigAbstract) -> None:
api_router=api,
lifespan=cast(starlette.types.StatelessLifespan[starlette.types.ASGIApp], self.fastapi_lifespan),
)
ApplicationPluginManagerAbstract.__init__(self=cast(ApplicationPluginManagerAbstract, self))
ApplicationPluginManagerAbstract.__init__(
self=cast(ApplicationPluginManagerAbstract, self), plugin_activation_list=plugin_activation_list
)
self._on_load()

@classmethod
Expand All @@ -70,19 +76,32 @@ def main(cls) -> None:
pass

@classmethod
def build(cls, config: AppConfigAbstract | None = None) -> Self:
def build_config(cls) -> AppConfigAbstract:
"""Build the application configuration.
Returns:
AppConfigAbstract: The application configuration.
"""
config_builder: AppConfigBuilder = AppConfigBuilder(
package_name=cls.PACKAGE_NAME, config_class=cls.CONFIG_CLASS
)
return config_builder.build()

@classmethod
def build(
cls, config: AppConfigAbstract | None = None, plugin_activation_list: PluginsActivationList | None = None
) -> Self:
"""Build the application.
Args:
config (AppConfigAbstract | None, optional): The application configuration. Defaults to None.
plugin_activation_list (PluginsActivationList | None, optional): The plugins activation list.
Defaults to None.
"""
if config is None:
config_builder: AppConfigBuilder = AppConfigBuilder(
package_name=cls.PACKAGE_NAME, config_class=cls.CONFIG_CLASS
)
config = config_builder.build()
config = cls.build_config()

return cls(config=config)
return cls(config=config, plugin_activation_list=plugin_activation_list)

@asynccontextmanager
async def fastapi_lifespan(self, fastapi_application: FastAPI) -> AsyncGenerator[None, None]:
Expand Down
3 changes: 2 additions & 1 deletion src/python_factory/core/app/base/config_abstract.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from pydantic import Field

from python_factory.core.app.base.exceptions import ApplicationConfigFactoryException
from python_factory.core.app.base.plugins_manager_abstract import PluginsActivationList
from python_factory.core.utils.configs import (
UnableToReadConfigFileError,
ValueErrorConfigError,
Expand All @@ -14,7 +15,7 @@
from .fastapi_application_abstract import FastAPIConfigAbstract


class AppConfigAbstract(FastAPIConfigAbstract):
class AppConfigAbstract(FastAPIConfigAbstract, PluginsActivationList):
"""Application configuration abstract class."""

environment: EnvironmentEnum
Expand Down
7 changes: 5 additions & 2 deletions src/python_factory/core/app/base/plugins_manager_abstract.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,17 @@ class ApplicationPluginManagerAbstract(ABC):

PLUGIN_PACKAGE_NAME: str = "python_factory.core.plugins"

def __init__(self) -> None:
def __init__(self, plugin_activation_list: PluginsActivationList | None = None) -> None:
"""Instanciate the application plugin manager."""
if self.PACKAGE_NAME == "":
raise ValueError("The package name must be set in the concrete plugin manager class.")

self._plugins: list[PluginProtocol] = []

self._plugins_activation_list: PluginsActivationList = self._build_plugins_activation_list()
if plugin_activation_list is not None:
self._plugins_activation_list: PluginsActivationList = plugin_activation_list
else:
self._plugins_activation_list: PluginsActivationList = self._build_plugins_activation_list()

self._check_pre_conditions()

Expand Down
6 changes: 4 additions & 2 deletions src/python_factory/example/app/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from beanie import Document

from python_factory.core.app import BaseApplication
from python_factory.core.app.base.plugins_manager_abstract import PluginsActivationList
from python_factory.example.models.books.document import BookDocument

from .config import AppConfig
Expand All @@ -19,13 +20,14 @@ class App(BaseApplication):

ODM_DOCUMENT_MODELS: ClassVar[list[type[Document]]] = [BookDocument]

def __init__(self, config: AppConfig) -> None:
def __init__(self, config: AppConfig, plugin_activation_list: PluginsActivationList | None = None) -> None:
"""Instantiate the application with the configuration and the API router.
Args:
config (AppConfig): The application configuration.
plugin_activation_list (PluginsActivationList | None, optional): The plugins activation list.
"""
super().__init__(config=config)
super().__init__(config=config, plugin_activation_list=plugin_activation_list)

# Prevent circular imports
from ..api import api_router # pylint: disable=import-outside-toplevel
Expand Down
29 changes: 29 additions & 0 deletions tests/units/python_factory/example/api/v1/books/test_books.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"""Tests for the books API."""

from http import HTTPStatus
from unittest.mock import MagicMock

from fastapi.testclient import TestClient

from python_factory.core.app.base.plugins_manager_abstract import PluginsActivationList
from python_factory.example.api.books.routes import get_book_service
from python_factory.example.app.app import App
from python_factory.example.services.books.services import BookService


class TestBookApi:
"""Tests for the books API."""

def test_get_books(self) -> None:
"""Test get_books."""
application: App = App.build(plugin_activation_list=PluginsActivationList(activate=[]))

application.get_asgi_app().dependency_overrides[get_book_service] = lambda: MagicMock(
spec=BookService, return_value=[]
)

with TestClient(application) as client:
response = client.get("/api/v1/books")

assert response.status_code == HTTPStatus.OK
assert response.json()["books"] == []

0 comments on commit 7b52da7

Please sign in to comment.