Skip to content

Commit

Permalink
Bring code coverage back up and change pre-commit (#350)
Browse files Browse the repository at this point in the history
  • Loading branch information
uittenbroekrobbert authored Nov 8, 2024
2 parents 9056704 + 2623554 commit c72825a
Show file tree
Hide file tree
Showing 17 changed files with 460 additions and 142 deletions.
12 changes: 0 additions & 12 deletions amt/utils/path.py

This file was deleted.

38 changes: 0 additions & 38 deletions amt/utils/storage.py

This file was deleted.

4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,10 @@ relative_files = true # needed for sonarcloud code coverage
omit = [
"tests/*"
]

concurrency = ["greenlet", "thread"]

[tool.coverage.report]
fail_under = 75
fail_under = 95

[tool.coverage.html]
directory = "htmlcov"
Expand Down
11 changes: 11 additions & 0 deletions tests/api/routes/test_auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import pytest
from httpx import AsyncClient
from pytest_mock import MockerFixture


@pytest.mark.asyncio
async def test_auth_profile(client: AsyncClient, mocker: MockerFixture) -> None:
mocker.patch("amt.api.routes.auth.get_user", return_value="test_user_for_auth_profile")
response = await client.get("/auth/profile")

assert b"https://gravatar.com/" in response.content
192 changes: 191 additions & 1 deletion tests/api/routes/test_project.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
from typing import Any

import pytest
from amt.api.routes.project import set_path
from amt.api.routes.project import (
MeasureUpdate,
find_measure_task,
find_requirement_task,
find_requirement_tasks_by_measure_urn,
get_project_context,
get_project_or_error,
set_path,
)
from amt.core.exceptions import AMTNotFound, AMTRepositoryError
from amt.models import Project
from amt.schema.task import MovedTask
from httpx import AsyncClient
from pytest_httpx import HTTPXMock
from pytest_mock import MockFixture

from tests.api.routes.test_projects import MockRequest
from tests.constants import (
TASK_REGISTRY_AIIA_CONTENT_PAYLOAD,
TASK_REGISTRY_CONTENT_PAYLOAD,
Expand Down Expand Up @@ -43,6 +54,96 @@ async def test_get_project_tasks(client: AsyncClient, db: DatabaseTestUtils) ->
assert b"Default Task" in response.content


@pytest.mark.asyncio
async def test_get_project_inference(client: AsyncClient, db: DatabaseTestUtils) -> None:
# given
await db.given([default_project("testproject1"), default_task(project_id=1, status_id=1)])

# when
response = await client.get("/algorithm-system/1/details/model/inference")

# then
assert response.status_code == 200
assert response.headers["content-type"] == "text/html; charset=utf-8"
assert b'<button id="runInference"' in response.content


@pytest.mark.asyncio
async def test_move_task(client: AsyncClient, db: DatabaseTestUtils, mocker: MockFixture) -> None:
# given
await db.given(
[
default_project("testproject1"),
default_task(project_id=1, status_id=1),
default_task(project_id=1, status_id=1),
]
)
mocker.patch("fastapi_csrf_protect.CsrfProtect.validate_csrf", new_callable=mocker.AsyncMock)
client.cookies["fastapi-csrf-token"] = "1"

# All -1 flow
move_task_json = MovedTask(taskId=1, statusId=1, previousSiblingId=-1, nextSiblingId=-1).model_dump(by_alias=True)
response = await client.patch("/algorithm-system/move_task", json=move_task_json, headers={"X-CSRF-Token": "1"})

# then
assert response.status_code == 200
assert response.headers["content-type"] == "text/html; charset=utf-8"
assert b"Default Task" in response.content

# All 1 flow
move_task_json = MovedTask(taskId=1, statusId=1, previousSiblingId=1, nextSiblingId=1).model_dump(by_alias=True)
response = await client.patch("/algorithm-system/move_task", json=move_task_json, headers={"X-CSRF-Token": "1"})

# then
assert response.status_code == 200
assert response.headers["content-type"] == "text/html; charset=utf-8"
assert b"Default Task" in response.content


@pytest.mark.asyncio
async def test_get_project_context(client: AsyncClient, db: DatabaseTestUtils, mocker: MockFixture) -> None:
# given
test_project = default_project_with_system_card("testproject1")
project_service = mocker.AsyncMock()
project_service.get.return_value = test_project

project, project_context = await get_project_context(
project_id=1, projects_service=project_service, request=MockRequest("nl", url="/")
)
assert project_context["last_edited"] is None
assert project == test_project


@pytest.mark.asyncio
async def test_get_project_non_existing_project(client: AsyncClient, db: DatabaseTestUtils) -> None:
# given
await db.given([default_project("testproject1"), default_task(project_id=1, status_id=1)])

# when
response = await client.get("/algorithm-system/99/details/tasks")

# then
assert response.status_code == 404
assert b"The requested page or resource could not be found." in response.content


@pytest.mark.asyncio
async def test_get_project_or_error(client: AsyncClient, db: DatabaseTestUtils, mocker: MockFixture) -> None:
# given
test_project = default_project("testproject1")
project_service = mocker.AsyncMock()
project_service.get.return_value = test_project

# happy flow
project = await get_project_or_error(project_id=1, projects_service=project_service, request=mocker.AsyncMock())
assert project == test_project

# unhappy flow
project_service.get.side_effect = AMTRepositoryError
with pytest.raises(AMTNotFound):
_ = await get_project_or_error(project_id=99, projects_service=project_service, request=mocker.AsyncMock())


# TODO: Test are now have hard coded URL paths because the system card
# is fixed for now. Tests need to be refactored and made proper once
# the actual stored system card in a project is being rendered.
Expand Down Expand Up @@ -364,3 +465,92 @@ def test_set_path(): # type: ignore
project_dict: dict[str, Any] = {}
set_path(project_dict, "/root", "value5")
assert project_dict == {"root": "value5"}


@pytest.mark.asyncio
async def test_find_measure_task() -> None:
test_project = default_project_with_system_card("testproject1")

# no matched measure
measure = find_measure_task(test_project.system_card, "")
assert measure is None

# matches measure
measure = find_measure_task(test_project.system_card, "urn:nl:ak:mtr:bnd-01")
assert measure.urn == "urn:nl:ak:mtr:bnd-01" # pyright: ignore [reportOptionalMemberAccess]
assert measure.value is not None # pyright: ignore [reportOptionalMemberAccess]

# no measures in system_card
test_project.system_card.measures = []
measure = find_measure_task(test_project.system_card, "")
assert measure is None


@pytest.mark.asyncio
async def test_find_requirement_task() -> None:
test_project = default_project_with_system_card("testproject1")

# no matched requirement
requirement = find_requirement_task(test_project.system_card, "")
assert requirement is None

# matches measure
requirement = find_requirement_task(test_project.system_card, "urn:nl:ak:ver:aia-05")
assert requirement.urn == "urn:nl:ak:ver:aia-05" # pyright: ignore [reportOptionalMemberAccess]
assert requirement.state is not None # pyright: ignore [reportOptionalMemberAccess]

# no measures in system_card
test_project.system_card.requirements = []
requirement = find_measure_task(test_project.system_card, "")
assert requirement is None


@pytest.mark.asyncio
async def test_find_requirement_tasks_by_measure_urn() -> None:
test_project = default_project_with_system_card("testproject1")

# no matched requirement
with pytest.raises(IndexError):
# TODO: this is because it is not coded well change later
requirement_tasks = find_requirement_tasks_by_measure_urn(test_project.system_card, "")

# matches measure
requirement_tasks = find_requirement_tasks_by_measure_urn(test_project.system_card, "urn:nl:ak:mtr:bnd-01")
assert len(requirement_tasks) == 3

# empty requirements
test_project.system_card.requirements = []
with pytest.raises(KeyError):
# TODO: this is because it is not coded well change later
requirement_tasks = find_requirement_tasks_by_measure_urn(test_project.system_card, "urn:nl:ak:mtr:bnd-01")


@pytest.mark.asyncio
async def test_get_measure(client: AsyncClient, db: DatabaseTestUtils) -> None:
# given
await db.given([default_project_with_system_card("testproject1")])

# when
response = await client.get("/algorithm-system/1/measure/urn:nl:ak:mtr:bnd-01")

# then
assert response.status_code == 200
assert response.headers["content-type"] == "text/html; charset=utf-8"
assert b"Gebruik aselecte steekproeven" in response.content


@pytest.mark.asyncio
async def test_update_measure_value(client: AsyncClient, mocker: MockFixture, db: DatabaseTestUtils) -> None:
# given
await db.given([default_project_with_system_card("testproject1")])
client.cookies["fastapi-csrf-token"] = "1"
mocker.patch("fastapi_csrf_protect.CsrfProtect.validate_csrf", new_callable=mocker.AsyncMock)

# happy flow
response = await client.post(
"/algorithm-system/1/measure/urn:nl:ak:mtr:bnd-01",
json={"measure_update": MeasureUpdate(measure_state="done", measure_value="something").model_dump()},
headers={"X-CSRF-Token": "1"},
)
assert response.status_code == 200
assert response.headers["content-type"] == "text/html; charset=utf-8"
11 changes: 9 additions & 2 deletions tests/api/routes/test_projects.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from collections.abc import MutableMapping
from datetime import UTC, datetime
from typing import cast
from typing import Any, cast

import pytest
from amt.api.routes.projects import get_localized_value
Expand All @@ -12,6 +13,7 @@
from fastapi.requests import Request
from httpx import AsyncClient
from pytest_mock import MockFixture
from starlette.datastructures import URL

from tests.constants import default_instrument, default_project
from tests.database_test_utils import DatabaseTestUtils
Expand Down Expand Up @@ -198,8 +200,13 @@ async def test_post_new_projects_write_system_card(


class MockRequest(Request):
def __init__(self, lang: str) -> None:
def __init__(self, lang: str, scope: MutableMapping[str, Any] | None = None, url: str | None = None) -> None:
if scope is None:
scope = {}
if url:
self._url = URL(url=url)
self.lang = lang
self.scope = scope

@property
def headers(self): # type: ignore
Expand Down
13 changes: 13 additions & 0 deletions tests/api/test_ai_act_profile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from amt.api.ai_act_profile import (
AiActProfileItem,
get_translation,
)
from babel.support import Translations


def test_get_translation():
translation = Translations.load("amt/locale", locales="en")
for item in AiActProfileItem:
assert get_translation(item, translation) is not None
# empty match
assert get_translation(None, translation) is None # pyright: ignore
12 changes: 12 additions & 0 deletions tests/api/test_deps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from amt.api.deps import custom_context_processor

from tests.constants import default_fastapi_request


def test_custom_context_processor():
result = custom_context_processor(default_fastapi_request())
assert result is not None
assert result["version"] == "0.1.0"
assert result["available_translations"] == ["en", "nl"]
assert result["language"] == "en"
assert result["translations"] is None
13 changes: 13 additions & 0 deletions tests/core/test_authorization.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import pytest
from amt.core.authorization import get_user
from pytest_mock import MockerFixture


@pytest.mark.asyncio
def test_get_user(mocker: MockerFixture) -> None:
mock_request = mocker.Mock(scope=["session"])
mock_request.session = {"user": {"name": "user"}}
mock_get_requested_language = mocker.patch("amt.core.authorization.get_requested_language", return_value="nl")
user = get_user(mock_request)
assert user == {"name": "user", "locale": "nl"}
mock_get_requested_language.assert_called_once_with(mock_request)
4 changes: 3 additions & 1 deletion tests/core/test_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import pytest
from amt.core.db import (
check_db,
init_db,
)
from pytest_mock import MockFixture
from sqlalchemy import select
Expand All @@ -13,12 +14,13 @@


@pytest.mark.asyncio
async def test_check_database(monkeypatch: pytest.MonkeyPatch, tmp_path: Path, mocker: MockFixture):
async def test_check_and_init_database(monkeypatch: pytest.MonkeyPatch, tmp_path: Path, mocker: MockFixture):
database_file = tmp_path / "database.sqlite3"
monkeypatch.setenv("APP_DATABASE_FILE", str(database_file))
org_exec = AsyncSession.execute
AsyncSession.execute = mocker.AsyncMock()
await check_db()
await init_db()

assert AsyncSession.execute.call_args is not None
assert str(select(1)) == str(AsyncSession.execute.call_args.args[0])
Expand Down
Loading

0 comments on commit c72825a

Please sign in to comment.