feat: pagination support for logs request

This commit is contained in:
2023-09-07 18:16:39 +03:00
committed by Evgeniy Alekseev
parent 853185c659
commit c915d68c97
66 changed files with 650 additions and 238 deletions

View File

@ -13,8 +13,8 @@ def test_logs_insert_remove_process(database: SQLite, package_ahriman: Package,
database.logs_insert(LogRecordId(package_python_schedule.base, "1"), 42.0, "message 3")
database.logs_remove(package_ahriman.base, "1")
assert database.logs_get(package_ahriman.base) == "[1970-01-01 00:00:42] message 1"
assert database.logs_get(package_python_schedule.base) == "[1970-01-01 00:00:42] message 3"
assert database.logs_get(package_ahriman.base) == [(42.0, "message 1")]
assert database.logs_get(package_python_schedule.base) == [(42.0, "message 3")]
def test_logs_insert_remove_full(database: SQLite, package_ahriman: Package, package_python_schedule: Package) -> None:
@ -27,7 +27,7 @@ def test_logs_insert_remove_full(database: SQLite, package_ahriman: Package, pac
database.logs_remove(package_ahriman.base, None)
assert not database.logs_get(package_ahriman.base)
assert database.logs_get(package_python_schedule.base) == "[1970-01-01 00:00:42] message 3"
assert database.logs_get(package_python_schedule.base) == [(42.0, "message 3")]
def test_logs_insert_get(database: SQLite, package_ahriman: Package) -> None:
@ -36,4 +36,13 @@ def test_logs_insert_get(database: SQLite, package_ahriman: Package) -> None:
"""
database.logs_insert(LogRecordId(package_ahriman.base, "1"), 43.0, "message 2")
database.logs_insert(LogRecordId(package_ahriman.base, "1"), 42.0, "message 1")
assert database.logs_get(package_ahriman.base) == "[1970-01-01 00:00:42] message 1\n[1970-01-01 00:00:43] message 2"
assert database.logs_get(package_ahriman.base) == [(42.0, "message 1"), (43.0, "message 2")]
def test_logs_insert_get_pagination(database: SQLite, package_ahriman: Package) -> None:
"""
must insert and get package logs with pagination
"""
database.logs_insert(LogRecordId(package_ahriman.base, "1"), 42.0, "message 1")
database.logs_insert(LogRecordId(package_ahriman.base, "1"), 43.0, "message 2")
assert database.logs_get(package_ahriman.base, 1, 1) == [(43.0, "message 2")]

View File

@ -55,8 +55,8 @@ def test_logs_get(watcher: Watcher, package_ahriman: Package, mocker: MockerFixt
must return package logs
"""
logs_mock = mocker.patch("ahriman.core.database.SQLite.logs_get")
watcher.logs_get(package_ahriman.base)
logs_mock.assert_called_once_with(package_ahriman.base)
watcher.logs_get(package_ahriman.base, 1, 2)
logs_mock.assert_called_once_with(package_ahriman.base, 1, 2)
def test_logs_remove(watcher: Watcher, package_ahriman: Package, mocker: MockerFixture) -> None:

View File

@ -0,0 +1,27 @@
from pathlib import Path
from ahriman.core.util import walk
def test_test_coverage() -> None:
"""
must have test files for each source file
"""
root = Path()
for source_file in filter(lambda fn: fn.suffix == ".py" and fn.name != "__init__.py", walk(root / "src")):
# some workaround for well known files
if source_file.parts[2:4] == ("application", "handlers") and source_file.name != "handler.py":
filename = f"test_handler_{source_file.name}"
elif source_file.parts[2:4] == ("web", "views"):
if (api := source_file.parts[4]) == "api":
filename = f"test_view_{api}_{source_file.name}"
elif (version := source_file.parts[4]) in ("v1", "v2"):
api = source_file.parts[5]
filename = f"test_view_{version}_{api}_{source_file.name}"
else:
filename = f"test_view_{source_file.name}"
else:
filename = f"test_{source_file.name}"
test_file = Path("tests", *source_file.parts[1:-1], filename)
assert test_file.is_file(), test_file

View File

@ -0,0 +1 @@
# schema testing goes in view class tests

View File

@ -5,7 +5,7 @@ from pytest_mock import MockerFixture
from unittest.mock import AsyncMock
from ahriman.models.user_access import UserAccess
from ahriman.web.views.service.add import AddView
from ahriman.web.views.v1 import AddView
async def test_get_permission() -> None:

View File

@ -4,7 +4,7 @@ from aiohttp.test_utils import TestClient
from pytest_mock import MockerFixture
from ahriman.models.user_access import UserAccess
from ahriman.web.views.service.pgp import PGPView
from ahriman.web.views.v1 import PGPView
async def test_get_permission() -> None:

View File

@ -4,7 +4,7 @@ from aiohttp.test_utils import TestClient
from pytest_mock import MockerFixture
from ahriman.models.user_access import UserAccess
from ahriman.web.views.service.process import ProcessView
from ahriman.web.views.v1 import ProcessView
async def test_get_permission() -> None:

View File

@ -5,7 +5,7 @@ from pytest_mock import MockerFixture
from unittest.mock import AsyncMock
from ahriman.models.user_access import UserAccess
from ahriman.web.views.service.rebuild import RebuildView
from ahriman.web.views.v1 import RebuildView
async def test_get_permission() -> None:

View File

@ -4,7 +4,7 @@ from aiohttp.test_utils import TestClient
from pytest_mock import MockerFixture
from ahriman.models.user_access import UserAccess
from ahriman.web.views.service.remove import RemoveView
from ahriman.web.views.v1 import RemoveView
async def test_get_permission() -> None:

View File

@ -5,7 +5,7 @@ from pytest_mock import MockerFixture
from unittest.mock import AsyncMock
from ahriman.models.user_access import UserAccess
from ahriman.web.views.service.request import RequestView
from ahriman.web.views.v1 import RequestView
async def test_get_permission() -> None:

View File

@ -5,7 +5,7 @@ from pytest_mock import MockerFixture
from ahriman.models.aur_package import AURPackage
from ahriman.models.user_access import UserAccess
from ahriman.web.views.service.search import SearchView
from ahriman.web.views.v1 import SearchView
async def test_get_permission() -> None:

View File

@ -5,7 +5,7 @@ from pytest_mock import MockerFixture
from unittest.mock import AsyncMock
from ahriman.models.user_access import UserAccess
from ahriman.web.views.service.update import UpdateView
from ahriman.web.views.v1 import UpdateView
async def test_get_permission() -> None:

View File

@ -10,7 +10,7 @@ from unittest.mock import AsyncMock, MagicMock, call as MockCall
from ahriman.models.repository_paths import RepositoryPaths
from ahriman.models.user_access import UserAccess
from ahriman.web.views.service.upload import UploadView
from ahriman.web.views.v1 import UploadView
async def test_get_permission() -> None:
@ -30,7 +30,7 @@ async def test_save_file(mocker: MockerFixture) -> None:
part_mock.filename = "filename"
part_mock.read_chunk = AsyncMock(side_effect=[b"content", None])
tempfile_mock = mocker.patch("ahriman.web.views.service.upload.NamedTemporaryFile")
tempfile_mock = mocker.patch("ahriman.web.views.v1.service.upload.NamedTemporaryFile")
file_mock = MagicMock()
tempfile_mock.return_value.__enter__.return_value = file_mock
@ -84,7 +84,7 @@ async def test_post(client: TestClient, repository_paths: RepositoryPaths, mocke
must process file upload via http
"""
local = Path("local")
save_mock = mocker.patch("ahriman.web.views.service.upload.UploadView.save_file",
save_mock = mocker.patch("ahriman.web.views.v1.UploadView.save_file",
side_effect=AsyncMock(return_value=("filename", local / ".filename")))
rename_mock = mocker.patch("pathlib.Path.rename")
# no content validation here because it has invalid schema
@ -103,7 +103,7 @@ async def test_post_with_sig(client: TestClient, repository_paths: RepositoryPat
must process file upload with signature via http
"""
local = Path("local")
save_mock = mocker.patch("ahriman.web.views.service.upload.UploadView.save_file",
save_mock = mocker.patch("ahriman.web.views.v1.UploadView.save_file",
side_effect=AsyncMock(side_effect=[
("filename", local / ".filename"),
("filename.sig", local / ".filename.sig"),

View File

@ -5,7 +5,7 @@ from aiohttp.test_utils import TestClient
from ahriman.models.build_status import BuildStatusEnum
from ahriman.models.package import Package
from ahriman.models.user_access import UserAccess
from ahriman.web.views.status.logs import LogsView
from ahriman.web.views.v1 import LogsView
async def test_get_permission() -> None:

View File

@ -5,7 +5,7 @@ from aiohttp.test_utils import TestClient
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
from ahriman.models.package import Package
from ahriman.models.user_access import UserAccess
from ahriman.web.views.status.package import PackageView
from ahriman.web.views.v1 import PackageView
async def test_get_permission() -> None:

View File

@ -6,7 +6,7 @@ from pytest_mock import MockerFixture
from ahriman.models.build_status import BuildStatusEnum
from ahriman.models.package import Package
from ahriman.models.user_access import UserAccess
from ahriman.web.views.status.packages import PackagesView
from ahriman.web.views.v1 import PackagesView
async def test_get_permission() -> None:

View File

@ -8,7 +8,7 @@ from ahriman.models.build_status import BuildStatusEnum
from ahriman.models.internal_status import InternalStatus
from ahriman.models.package import Package
from ahriman.models.user_access import UserAccess
from ahriman.web.views.status.status import StatusView
from ahriman.web.views.v1 import StatusView
async def test_get_permission() -> None:

View File

@ -5,7 +5,7 @@ from pytest_mock import MockerFixture
from ahriman.models.user import User
from ahriman.models.user_access import UserAccess
from ahriman.web.views.user.login import LoginView
from ahriman.web.views.v1 import LoginView
async def test_get_permission() -> None:

View File

@ -5,7 +5,7 @@ from aiohttp.web import HTTPUnauthorized
from pytest_mock import MockerFixture
from ahriman.models.user_access import UserAccess
from ahriman.web.views.user.logout import LogoutView
from ahriman.web.views.v1 import LogoutView
async def test_get_permission() -> None:

View File

@ -0,0 +1,93 @@
import pytest
from aiohttp.test_utils import TestClient
from ahriman.models.build_status import BuildStatusEnum
from ahriman.models.package import Package
from ahriman.models.user_access import UserAccess
from ahriman.web.views.v2 import LogsView
async def test_get_permission() -> None:
"""
must return correct permission for the request
"""
for method in ("GET",):
request = pytest.helpers.request("", "", method)
assert await LogsView.get_permission(request) == UserAccess.Reporter
async def test_get(client: TestClient, package_ahriman: Package) -> None:
"""
must get logs for package
"""
await client.post(f"/api/v1/packages/{package_ahriman.base}",
json={"status": BuildStatusEnum.Success.value, "package": package_ahriman.view()})
await client.post(f"/api/v1/packages/{package_ahriman.base}/logs",
json={"created": 42.0, "message": "message 1", "version": "42"})
await client.post(f"/api/v1/packages/{package_ahriman.base}/logs",
json={"created": 43.0, "message": "message 2", "version": "42"})
request_schema = pytest.helpers.schema_request(LogsView.get, location="querystring")
response_schema = pytest.helpers.schema_response(LogsView.get)
payload = {}
assert not request_schema.validate(payload)
response = await client.get(f"/api/v2/packages/{package_ahriman.base}/logs", params=payload)
assert response.status == 200
logs = await response.json()
assert not response_schema.validate(logs)
assert logs["logs"] == [[42.0, "message 1"], [43.0, "message 2"]]
async def test_get_with_pagination(client: TestClient, package_ahriman: Package) -> None:
"""
must get logs with pagination
"""
await client.post(f"/api/v1/packages/{package_ahriman.base}",
json={"status": BuildStatusEnum.Success.value, "package": package_ahriman.view()})
await client.post(f"/api/v1/packages/{package_ahriman.base}/logs",
json={"created": 42.0, "message": "message 1", "version": "42"})
await client.post(f"/api/v1/packages/{package_ahriman.base}/logs",
json={"created": 43.0, "message": "message 2", "version": "42"})
request_schema = pytest.helpers.schema_request(LogsView.get, location="querystring")
response_schema = pytest.helpers.schema_response(LogsView.get)
payload = {"limit": 1, "offset": 1}
assert not request_schema.validate(payload)
response = await client.get(f"/api/v2/packages/{package_ahriman.base}/logs", params=payload)
assert response.status == 200
logs = await response.json()
assert not response_schema.validate(logs)
assert logs["logs"] == [[43.0, "message 2"]]
async def test_get_not_found(client: TestClient, package_ahriman: Package) -> None:
"""
must return not found for missing package
"""
response_schema = pytest.helpers.schema_response(LogsView.get, code=404)
response = await client.get(f"/api/v2/packages/{package_ahriman.base}/logs")
assert response.status == 404
assert not response_schema.validate(await response.json())
async def test_get_bad_request(client: TestClient, package_ahriman: Package) -> None:
"""
must return bad request for invalid query parameters
"""
await client.post(f"/api/v1/packages/{package_ahriman.base}",
json={"status": BuildStatusEnum.Success.value, "package": package_ahriman.view()})
await client.post(f"/api/v1/packages/{package_ahriman.base}/logs",
json={"created": 42.0, "message": "message", "version": "42"})
response_schema = pytest.helpers.schema_response(LogsView.get, code=400)
response = await client.get(f"/api/v2/packages/{package_ahriman.base}/logs", params={"limit": "limit"})
assert response.status == 400
assert not response_schema.validate(await response.json())
response = await client.get(f"/api/v2/packages/{package_ahriman.base}/logs", params={"offset": "offset"})
assert response.status == 400
assert not response_schema.validate(await response.json())