diff --git a/app/api/api_v1/routers/config.py b/app/api/api_v1/routers/config.py index b7719c65..da588354 100644 --- a/app/api/api_v1/routers/config.py +++ b/app/api/api_v1/routers/config.py @@ -1,6 +1,7 @@ import logging from fastapi import APIRouter, HTTPException, Request, status +from app.model.config import ConfigReadDTO import app.service.config as config_service from app.errors import RepositoryError @@ -9,8 +10,8 @@ _LOGGER = logging.getLogger(__file__) -@r.get("/config") -async def get_config(request: Request): +@r.get("/config", response_model=ConfigReadDTO) +async def get_config(request: Request) -> ConfigReadDTO: user = request.state.user _LOGGER.info(f"User {user.email} is getting config") try: @@ -19,10 +20,4 @@ async def get_config(request: Request): raise HTTPException( status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail=e.message ) - - if config is None: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="Config not found" - ) - return config diff --git a/app/repository/__init__.py b/app/repository/__init__.py index d2ac929f..4959c14b 100644 --- a/app/repository/__init__.py +++ b/app/repository/__init__.py @@ -5,3 +5,4 @@ import app.repository.collection as collection_repo import app.repository.app_user as app_user_repo import app.clients.aws.s3bucket as s3bucket_repo +import app.repository.config as config_repo diff --git a/app/service/config.py b/app/service/config.py index 2ce596dc..9f84216a 100644 --- a/app/service/config.py +++ b/app/service/config.py @@ -1,5 +1,6 @@ import logging from app.errors import RepositoryError +from app.model.config import ConfigReadDTO import app.repository.config as config_repo from sqlalchemy import exc @@ -9,7 +10,13 @@ _LOGGER = logging.getLogger(__name__) -def get(): +def get() -> ConfigReadDTO: + """ + Gets the config + + :raises RepositoryError: If there is an issue getting the config + :return ConfigReadDTO: The config for the application + """ try: with db_session.get_db() as db: return config_repo.get(db) diff --git a/unit_tests/conftest.py b/unit_tests/conftest.py index ff8ce3aa..15c42288 100644 --- a/unit_tests/conftest.py +++ b/unit_tests/conftest.py @@ -13,6 +13,7 @@ import app.service.family as family_service import app.service.collection as collection_service +import app.service.config as config_service import app.service.token as token_service from app.repository import ( family_repo, @@ -21,6 +22,7 @@ organisation_repo, collection_repo, app_user_repo, + config_repo, ) from unit_tests.mocks.repos.app_user_repo import mock_app_user_repo @@ -29,9 +31,11 @@ from unit_tests.mocks.repos.geography_repo import mock_geography_repo from unit_tests.mocks.repos.metadata_repo import mock_metadata_repo from unit_tests.mocks.repos.organisation_repo import mock_organisation_repo +from unit_tests.mocks.repos.config_repo import mock_config_repo from unit_tests.mocks.services.family_service import mock_family_service from unit_tests.mocks.services.collection_service import mock_collection_service +from unit_tests.mocks.services.config_service import mock_config_service @pytest.fixture @@ -86,6 +90,13 @@ def collection_repo_mock(monkeypatch, mocker): yield collection_repo +@pytest.fixture +def config_repo_mock(monkeypatch, mocker): + """Mocks the repository for a single test.""" + mock_config_repo(config_repo, monkeypatch, mocker) + yield config_repo + + # ----- Mock services @@ -103,6 +114,13 @@ def collection_service_mock(monkeypatch, mocker): yield collection_service +@pytest.fixture +def config_service_mock(monkeypatch, mocker): + """Mocks the service for a single test.""" + mock_config_service(config_service, monkeypatch, mocker) + yield config_service + + # ----- User tokens diff --git a/unit_tests/mocks/repos/config_repo.py b/unit_tests/mocks/repos/config_repo.py new file mode 100644 index 00000000..d7226764 --- /dev/null +++ b/unit_tests/mocks/repos/config_repo.py @@ -0,0 +1,22 @@ +from typing import Optional +from pytest import MonkeyPatch + +from sqlalchemy import exc + +from app.model.config import ConfigReadDTO + + +def mock_config_repo(config_repo, monkeypatch: MonkeyPatch, mocker): + config_repo.return_empty = False + config_repo.throw_repository_error = False + + def maybe_throw(): + if config_repo.throw_repository_error: + raise exc.SQLAlchemyError("") + + def mock_get(_) -> Optional[ConfigReadDTO]: + maybe_throw() + return ConfigReadDTO(geographies=[], taxonomies={}, languages={}) + + monkeypatch.setattr(config_repo, "get", mock_get) + mocker.spy(config_repo, "get") diff --git a/unit_tests/mocks/services/config_service.py b/unit_tests/mocks/services/config_service.py new file mode 100644 index 00000000..bba2dcf2 --- /dev/null +++ b/unit_tests/mocks/services/config_service.py @@ -0,0 +1,19 @@ +from pytest import MonkeyPatch +from app.errors import RepositoryError + +from app.model.config import ConfigReadDTO + + +def mock_config_service(config_service, monkeypatch: MonkeyPatch, mocker): + config_service.throw_repository_error = False + + def maybe_throw(): + if config_service.throw_repository_error: + raise RepositoryError("bad repo") + + def mock_get_config() -> ConfigReadDTO: + maybe_throw() + return ConfigReadDTO(geographies=[], taxonomies={}, languages={}) + + monkeypatch.setattr(config_service, "get", mock_get_config) + mocker.spy(config_service, "get") diff --git a/unit_tests/routers/README.md b/unit_tests/routers/README.md new file mode 100644 index 00000000..0fb52d4f --- /dev/null +++ b/unit_tests/routers/README.md @@ -0,0 +1,7 @@ +# Test Approach + +In the routers we mock the service to generate the different behaviours. + +We then test that these behaviours (such as a db error) result in the correct HTTP response (in the case of a db error it should be a 503). + +This ensures we are just unit testing the behaviour of the routers. \ No newline at end of file diff --git a/unit_tests/routers/test_config.py b/unit_tests/routers/test_config.py index d72459ac..6b172ba3 100644 --- a/unit_tests/routers/test_config.py +++ b/unit_tests/routers/test_config.py @@ -1,6 +1,21 @@ +from fastapi import status from fastapi.testclient import TestClient -def test_get_config(client: TestClient, user_header_token): - # TODO: Finish the config tests by adding mock for repo - pass +def test_get_config(client: TestClient, user_header_token, config_service_mock): + response = client.get("/api/v1/config", headers=user_header_token) + assert response.status_code == status.HTTP_200_OK + data = response.json() + keys = data.keys() + assert "geographies" in keys + assert "taxonomies" in keys + assert "languages" in keys + assert config_service_mock.get.call_count == 1 + + +def test_get_config_when_db_error( + client: TestClient, user_header_token, config_service_mock +): + config_service_mock.throw_repository_error = True + response = client.get("/api/v1/config", headers=user_header_token) + assert response.status_code == status.HTTP_503_SERVICE_UNAVAILABLE