mirror of
				https://github.com/arcan1s/ahriman.git
				synced 2025-10-31 22:03:42 +00:00 
			
		
		
		
	use api generated docs instead of comments
This commit is contained in:
		| @ -12,7 +12,7 @@ from unittest.mock import MagicMock | ||||
|  | ||||
| from ahriman.core.exceptions import BuildError, OptionError, UnsafeRunError | ||||
| from ahriman.core.util import check_output, check_user, enum_values, exception_response_text, filter_json, \ | ||||
|     full_version, package_like, pretty_datetime, pretty_size, safe_filename, trim_package, utcnow, walk | ||||
|     full_version, package_like, partition, pretty_datetime, pretty_size, safe_filename, trim_package, utcnow, walk | ||||
| from ahriman.models.package import Package | ||||
| from ahriman.models.package_source import PackageSource | ||||
| from ahriman.models.repository_paths import RepositoryPaths | ||||
| @ -228,6 +228,15 @@ def test_package_like_sig(package_ahriman: Package) -> None: | ||||
|     assert not package_like(sig_file) | ||||
|  | ||||
|  | ||||
| def test_partition() -> None: | ||||
|     """ | ||||
|     must partition list based on predicate | ||||
|     """ | ||||
|     even, odd = partition([1, 4, 2, 1, 3, 4], lambda i: i % 2 == 0) | ||||
|     assert even == [4, 2, 4] | ||||
|     assert odd == [1, 1, 3] | ||||
|  | ||||
|  | ||||
| def test_pretty_datetime() -> None: | ||||
|     """ | ||||
|     must generate string from timestamp value | ||||
| @ -371,6 +380,7 @@ def test_walk(resource_path_root: Path) -> None: | ||||
|         resource_path_root / "web" / "templates" / "static" / "favicon.ico", | ||||
|         resource_path_root / "web" / "templates" / "utils" / "bootstrap-scripts.jinja2", | ||||
|         resource_path_root / "web" / "templates" / "utils" / "style.jinja2", | ||||
|         resource_path_root / "web" / "templates" / "api.jinja2", | ||||
|         resource_path_root / "web" / "templates" / "build-status.jinja2", | ||||
|         resource_path_root / "web" / "templates" / "email-index.jinja2", | ||||
|         resource_path_root / "web" / "templates" / "error.jinja2", | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| import pytest | ||||
|  | ||||
| from asyncio import BaseEventLoop | ||||
| from aiohttp import web | ||||
| from aiohttp.web import Application, Resource, UrlMappingMatchInfo | ||||
| from aiohttp.test_utils import TestClient | ||||
| from pytest_mock import MockerFixture | ||||
| from typing import Any, Dict, Optional | ||||
| @ -19,29 +19,35 @@ from ahriman.web.web import setup_service | ||||
|  | ||||
|  | ||||
| @pytest.helpers.register | ||||
| def request(app: web.Application, path: str, method: str, json: Any = None, data: Any = None, | ||||
|             extra: Optional[Dict[str, Any]] = None) -> MagicMock: | ||||
| def request(application: Application, path: str, method: str, json: Any = None, data: Any = None, | ||||
|             extra: Optional[Dict[str, Any]] = None, resource: Optional[Resource] = None) -> MagicMock: | ||||
|     """ | ||||
|     request generator helper | ||||
|  | ||||
|     Args: | ||||
|         app(web.Application): application fixture | ||||
|         application(Application): application fixture | ||||
|         path(str): path for the request | ||||
|         method(str): method for the request | ||||
|         json(Any, optional): json payload of the request (Default value = None) | ||||
|         data(Any, optional): form data payload of the request (Default value = None) | ||||
|         extra(Optional[Dict[str, Any]], optional): extra info which will be injected for ``get_extra_info`` command | ||||
|         resource(Optional[Resource], optional): optional web resource for the request (Default value = None) | ||||
|  | ||||
|     Returns: | ||||
|         MagicMock: dummy request mock | ||||
|     """ | ||||
|     request_mock = MagicMock() | ||||
|     request_mock.app = app | ||||
|     request_mock.app = application | ||||
|     request_mock.path = path | ||||
|     request_mock.method = method | ||||
|     request_mock.json = json | ||||
|     request_mock.post = data | ||||
|  | ||||
|     if resource is not None: | ||||
|         route_mock = MagicMock() | ||||
|         route_mock.resource = resource | ||||
|         request_mock.match_info = UrlMappingMatchInfo({}, route_mock) | ||||
|  | ||||
|     extra = extra or {} | ||||
|     request_mock.get_extra_info.side_effect = lambda key: extra.get(key) | ||||
|  | ||||
| @ -50,7 +56,7 @@ def request(app: web.Application, path: str, method: str, json: Any = None, data | ||||
|  | ||||
| @pytest.fixture | ||||
| def application(configuration: Configuration, spawner: Spawn, database: SQLite, repository: Repository, | ||||
|                 mocker: MockerFixture) -> web.Application: | ||||
|                 mocker: MockerFixture) -> Application: | ||||
|     """ | ||||
|     application fixture | ||||
|  | ||||
| @ -62,17 +68,19 @@ def application(configuration: Configuration, spawner: Spawn, database: SQLite, | ||||
|         mocker(MockerFixture): mocker object | ||||
|  | ||||
|     Returns: | ||||
|         web.Application: application test instance | ||||
|         Application: application test instance | ||||
|     """ | ||||
|     configuration.set_option("web", "port", "8080") | ||||
|     mocker.patch("ahriman.core.database.SQLite.load", return_value=database) | ||||
|     mocker.patch("ahriman.core.repository.Repository.load", return_value=repository) | ||||
|     mocker.patch("aiohttp_apispec.setup_aiohttp_apispec") | ||||
|     mocker.patch.object(ahriman.core.auth.helpers, "_has_aiohttp_security", False) | ||||
|     return setup_service("x86_64", configuration, spawner) | ||||
|  | ||||
|  | ||||
| @pytest.fixture | ||||
| def application_with_auth(configuration: Configuration, user: User, spawner: Spawn, database: SQLite, | ||||
|                           repository: Repository, mocker: MockerFixture) -> web.Application: | ||||
|                           repository: Repository, mocker: MockerFixture) -> Application: | ||||
|     """ | ||||
|     application fixture with auth enabled | ||||
|  | ||||
| @ -85,11 +93,13 @@ def application_with_auth(configuration: Configuration, user: User, spawner: Spa | ||||
|         mocker(MockerFixture): mocker object | ||||
|  | ||||
|     Returns: | ||||
|         web.Application: application test instance | ||||
|         Application: application test instance | ||||
|     """ | ||||
|     configuration.set_option("auth", "target", "configuration") | ||||
|     configuration.set_option("web", "port", "8080") | ||||
|     mocker.patch("ahriman.core.database.SQLite.load", return_value=database) | ||||
|     mocker.patch("ahriman.core.repository.Repository.load", return_value=repository) | ||||
|     mocker.patch("aiohttp_apispec.setup_aiohttp_apispec") | ||||
|     mocker.patch.object(ahriman.core.auth.helpers, "_has_aiohttp_security", True) | ||||
|     application = setup_service("x86_64", configuration, spawner) | ||||
|  | ||||
| @ -101,7 +111,7 @@ def application_with_auth(configuration: Configuration, user: User, spawner: Spa | ||||
|  | ||||
| @pytest.fixture | ||||
| def application_with_debug(configuration: Configuration, user: User, spawner: Spawn, database: SQLite, | ||||
|                            repository: Repository, mocker: MockerFixture) -> web.Application: | ||||
|                            repository: Repository, mocker: MockerFixture) -> Application: | ||||
|     """ | ||||
|     application fixture with debug enabled | ||||
|  | ||||
| @ -114,23 +124,25 @@ def application_with_debug(configuration: Configuration, user: User, spawner: Sp | ||||
|         mocker(MockerFixture): mocker object | ||||
|  | ||||
|     Returns: | ||||
|         web.Application: application test instance | ||||
|         Application: application test instance | ||||
|     """ | ||||
|     configuration.set_option("web", "debug", "yes") | ||||
|     configuration.set_option("web", "port", "8080") | ||||
|     mocker.patch("ahriman.core.database.SQLite.load", return_value=database) | ||||
|     mocker.patch("ahriman.core.repository.Repository.load", return_value=repository) | ||||
|     mocker.patch("aiohttp_apispec.setup_aiohttp_apispec") | ||||
|     mocker.patch.object(ahriman.core.auth.helpers, "_has_aiohttp_security", False) | ||||
|     return setup_service("x86_64", configuration, spawner) | ||||
|  | ||||
|  | ||||
| @pytest.fixture | ||||
| def client(application: web.Application, event_loop: BaseEventLoop, | ||||
| def client(application: Application, event_loop: BaseEventLoop, | ||||
|            aiohttp_client: Any, mocker: MockerFixture) -> TestClient: | ||||
|     """ | ||||
|     web client fixture | ||||
|  | ||||
|     Args: | ||||
|         application(web.Application): application fixture | ||||
|         application(Application): application fixture | ||||
|         event_loop(BaseEventLoop): context event loop | ||||
|         aiohttp_client(Any): aiohttp client fixture | ||||
|         mocker(MockerFixture): mocker object | ||||
| @ -143,13 +155,13 @@ def client(application: web.Application, event_loop: BaseEventLoop, | ||||
|  | ||||
|  | ||||
| @pytest.fixture | ||||
| def client_with_auth(application_with_auth: web.Application, event_loop: BaseEventLoop, | ||||
| def client_with_auth(application_with_auth: Application, event_loop: BaseEventLoop, | ||||
|                      aiohttp_client: Any, mocker: MockerFixture) -> TestClient: | ||||
|     """ | ||||
|     web client fixture with full authorization functions | ||||
|  | ||||
|     Args: | ||||
|         application_with_auth(web.Application): application fixture | ||||
|         application_with_auth(Application): application fixture | ||||
|         event_loop(BaseEventLoop): context event loop | ||||
|         aiohttp_client(Any): aiohttp client fixture | ||||
|         mocker(MockerFixture): mocker object | ||||
| @ -162,13 +174,13 @@ def client_with_auth(application_with_auth: web.Application, event_loop: BaseEve | ||||
|  | ||||
|  | ||||
| @pytest.fixture | ||||
| def client_with_oauth_auth(application_with_auth: web.Application, event_loop: BaseEventLoop, | ||||
| def client_with_oauth_auth(application_with_auth: Application, event_loop: BaseEventLoop, | ||||
|                            aiohttp_client: Any, mocker: MockerFixture) -> TestClient: | ||||
|     """ | ||||
|     web client fixture with full authorization functions | ||||
|  | ||||
|     Args: | ||||
|         application_with_auth(web.Application): application fixture | ||||
|         application_with_auth(Application): application fixture | ||||
|         event_loop(BaseEventLoop): context event loop | ||||
|         aiohttp_client(Any): aiohttp client fixture | ||||
|         mocker(MockerFixture): mocker object | ||||
|  | ||||
| @ -4,11 +4,11 @@ from ahriman.core.auth import Auth | ||||
| from ahriman.core.configuration import Configuration | ||||
| from ahriman.core.database import SQLite | ||||
| from ahriman.models.user import User | ||||
| from ahriman.web.middlewares.auth_handler import AuthorizationPolicy | ||||
| from ahriman.web.middlewares.auth_handler import _AuthorizationPolicy | ||||
|  | ||||
|  | ||||
| @pytest.fixture | ||||
| def authorization_policy(configuration: Configuration, database: SQLite, user: User) -> AuthorizationPolicy: | ||||
| def authorization_policy(configuration: Configuration, database: SQLite, user: User) -> _AuthorizationPolicy: | ||||
|     """ | ||||
|     fixture for authorization policy | ||||
|  | ||||
| @ -22,5 +22,5 @@ def authorization_policy(configuration: Configuration, database: SQLite, user: U | ||||
|     """ | ||||
|     configuration.set_option("auth", "target", "configuration") | ||||
|     validator = Auth.load(configuration, database) | ||||
|     policy = AuthorizationPolicy(validator) | ||||
|     policy = _AuthorizationPolicy(validator) | ||||
|     return policy | ||||
|  | ||||
| @ -1,8 +1,8 @@ | ||||
| import pytest | ||||
| import socket | ||||
|  | ||||
| from aiohttp import web | ||||
| from aiohttp.test_utils import TestClient | ||||
| from aiohttp.web import Application | ||||
| from cryptography import fernet | ||||
| from pytest_mock import MockerFixture | ||||
| from unittest.mock import AsyncMock, call as MockCall | ||||
| @ -11,10 +11,10 @@ from ahriman.core.auth import Auth | ||||
| from ahriman.core.configuration import Configuration | ||||
| from ahriman.models.user import User | ||||
| from ahriman.models.user_access import UserAccess | ||||
| from ahriman.web.middlewares.auth_handler import AuthorizationPolicy, auth_handler, cookie_secret_key, setup_auth | ||||
| from ahriman.web.middlewares.auth_handler import _AuthorizationPolicy, _auth_handler, _cookie_secret_key, setup_auth | ||||
|  | ||||
|  | ||||
| async def test_authorized_userid(authorization_policy: AuthorizationPolicy, user: User, mocker: MockerFixture) -> None: | ||||
| async def test_authorized_userid(authorization_policy: _AuthorizationPolicy, user: User, mocker: MockerFixture) -> None: | ||||
|     """ | ||||
|     must return authorized user id | ||||
|     """ | ||||
| @ -22,7 +22,7 @@ async def test_authorized_userid(authorization_policy: AuthorizationPolicy, user | ||||
|     assert await authorization_policy.authorized_userid(user.username) == user.username | ||||
|  | ||||
|  | ||||
| async def test_authorized_userid_unknown(authorization_policy: AuthorizationPolicy, user: User) -> None: | ||||
| async def test_authorized_userid_unknown(authorization_policy: _AuthorizationPolicy, user: User) -> None: | ||||
|     """ | ||||
|     must not allow unknown user id for authorization | ||||
|     """ | ||||
| @ -30,7 +30,7 @@ async def test_authorized_userid_unknown(authorization_policy: AuthorizationPoli | ||||
|     assert await authorization_policy.authorized_userid("somerandomname") is None | ||||
|  | ||||
|  | ||||
| async def test_permits(authorization_policy: AuthorizationPolicy, user: User) -> None: | ||||
| async def test_permits(authorization_policy: _AuthorizationPolicy, user: User) -> None: | ||||
|     """ | ||||
|     must call validator check | ||||
|     """ | ||||
| @ -56,7 +56,7 @@ async def test_auth_handler_unix_socket(client_with_auth: TestClient, mocker: Mo | ||||
|     request_handler.get_permission.return_value = UserAccess.Full | ||||
|     check_permission_mock = mocker.patch("aiohttp_security.check_permission") | ||||
|  | ||||
|     handler = auth_handler(allow_read_only=False) | ||||
|     handler = _auth_handler(allow_read_only=False) | ||||
|     await handler(aiohttp_request, request_handler) | ||||
|     check_permission_mock.assert_not_called() | ||||
|  | ||||
| @ -70,7 +70,7 @@ async def test_auth_handler_api(mocker: MockerFixture) -> None: | ||||
|     request_handler.get_permission.return_value = UserAccess.Read | ||||
|     check_permission_mock = mocker.patch("aiohttp_security.check_permission") | ||||
|  | ||||
|     handler = auth_handler(allow_read_only=False) | ||||
|     handler = _auth_handler(allow_read_only=False) | ||||
|     await handler(aiohttp_request, request_handler) | ||||
|     check_permission_mock.assert_called_once_with(aiohttp_request, UserAccess.Read, aiohttp_request.path) | ||||
|  | ||||
| @ -102,7 +102,7 @@ async def test_auth_handler_allow_read_only(mocker: MockerFixture) -> None: | ||||
|     request_handler.get_permission.return_value = UserAccess.Read | ||||
|     check_permission_mock = mocker.patch("aiohttp_security.check_permission") | ||||
|  | ||||
|     handler = auth_handler(allow_read_only=True) | ||||
|     handler = _auth_handler(allow_read_only=True) | ||||
|     await handler(aiohttp_request, request_handler) | ||||
|     check_permission_mock.assert_not_called() | ||||
|  | ||||
| @ -116,7 +116,7 @@ async def test_auth_handler_api_no_method(mocker: MockerFixture) -> None: | ||||
|     request_handler.get_permission = None | ||||
|     check_permission_mock = mocker.patch("aiohttp_security.check_permission") | ||||
|  | ||||
|     handler = auth_handler(allow_read_only=False) | ||||
|     handler = _auth_handler(allow_read_only=False) | ||||
|     await handler(aiohttp_request, request_handler) | ||||
|     check_permission_mock.assert_called_once_with(aiohttp_request, UserAccess.Full, aiohttp_request.path) | ||||
|  | ||||
| @ -130,7 +130,7 @@ async def test_auth_handler_api_post(mocker: MockerFixture) -> None: | ||||
|     request_handler.get_permission.return_value = UserAccess.Full | ||||
|     check_permission_mock = mocker.patch("aiohttp_security.check_permission") | ||||
|  | ||||
|     handler = auth_handler(allow_read_only=False) | ||||
|     handler = _auth_handler(allow_read_only=False) | ||||
|     await handler(aiohttp_request, request_handler) | ||||
|     check_permission_mock.assert_called_once_with(aiohttp_request, UserAccess.Full, aiohttp_request.path) | ||||
|  | ||||
| @ -145,7 +145,7 @@ async def test_auth_handler_read(mocker: MockerFixture) -> None: | ||||
|         request_handler.get_permission.return_value = UserAccess.Read | ||||
|         check_permission_mock = mocker.patch("aiohttp_security.check_permission") | ||||
|  | ||||
|         handler = auth_handler(allow_read_only=False) | ||||
|         handler = _auth_handler(allow_read_only=False) | ||||
|         await handler(aiohttp_request, request_handler) | ||||
|         check_permission_mock.assert_called_once_with(aiohttp_request, UserAccess.Read, aiohttp_request.path) | ||||
|  | ||||
| @ -160,7 +160,7 @@ async def test_auth_handler_write(mocker: MockerFixture) -> None: | ||||
|         request_handler.get_permission.return_value = UserAccess.Full | ||||
|         check_permission_mock = mocker.patch("aiohttp_security.check_permission") | ||||
|  | ||||
|         handler = auth_handler(allow_read_only=False) | ||||
|         handler = _auth_handler(allow_read_only=False) | ||||
|         await handler(aiohttp_request, request_handler) | ||||
|         check_permission_mock.assert_called_once_with(aiohttp_request, UserAccess.Full, aiohttp_request.path) | ||||
|  | ||||
| @ -169,7 +169,7 @@ def test_cookie_secret_key(configuration: Configuration) -> None: | ||||
|     """ | ||||
|     must generate fernet key | ||||
|     """ | ||||
|     secret_key = cookie_secret_key(configuration) | ||||
|     secret_key = _cookie_secret_key(configuration) | ||||
|     assert isinstance(secret_key, fernet.Fernet) | ||||
|  | ||||
|  | ||||
| @ -178,10 +178,10 @@ def test_cookie_secret_key_cached(configuration: Configuration) -> None: | ||||
|     must use cookie key as set by configuration | ||||
|     """ | ||||
|     configuration.set_option("auth", "cookie_secret_key", fernet.Fernet.generate_key().decode("utf8")) | ||||
|     assert cookie_secret_key(configuration) is not None | ||||
|     assert _cookie_secret_key(configuration) is not None | ||||
|  | ||||
|  | ||||
| def test_setup_auth(application_with_auth: web.Application, configuration: Configuration, auth: Auth, | ||||
| def test_setup_auth(application_with_auth: Application, configuration: Configuration, auth: Auth, | ||||
|                     mocker: MockerFixture) -> None: | ||||
|     """ | ||||
|     must set up authorization | ||||
|  | ||||
| @ -2,12 +2,12 @@ import json | ||||
| import logging | ||||
| import pytest | ||||
|  | ||||
| from aiohttp.web import HTTPBadRequest, HTTPInternalServerError, HTTPNoContent, HTTPUnauthorized | ||||
| from aiohttp.web import HTTPBadRequest, HTTPInternalServerError, HTTPMethodNotAllowed, HTTPNoContent, HTTPUnauthorized | ||||
| from pytest_mock import MockerFixture | ||||
| from typing import Any | ||||
| from unittest.mock import AsyncMock, MagicMock | ||||
|  | ||||
| from ahriman.web.middlewares.exception_handler import exception_handler, is_templated_unauthorized | ||||
| from ahriman.web.middlewares.exception_handler import _is_templated_unauthorized, exception_handler | ||||
|  | ||||
|  | ||||
| def _extract_body(response: Any) -> Any: | ||||
| @ -31,27 +31,27 @@ def test_is_templated_unauthorized() -> None: | ||||
|  | ||||
|     response_mock.path = "/api/v1/login" | ||||
|     response_mock.headers.getall.return_value = ["*/*"] | ||||
|     assert is_templated_unauthorized(response_mock) | ||||
|     assert _is_templated_unauthorized(response_mock) | ||||
|  | ||||
|     response_mock.path = "/api/v1/login" | ||||
|     response_mock.headers.getall.return_value = ["application/json"] | ||||
|     assert not is_templated_unauthorized(response_mock) | ||||
|     assert not _is_templated_unauthorized(response_mock) | ||||
|  | ||||
|     response_mock.path = "/api/v1/logout" | ||||
|     response_mock.headers.getall.return_value = ["*/*"] | ||||
|     assert is_templated_unauthorized(response_mock) | ||||
|     assert _is_templated_unauthorized(response_mock) | ||||
|  | ||||
|     response_mock.path = "/api/v1/logout" | ||||
|     response_mock.headers.getall.return_value = ["application/json"] | ||||
|     assert not is_templated_unauthorized(response_mock) | ||||
|     assert not _is_templated_unauthorized(response_mock) | ||||
|  | ||||
|     response_mock.path = "/api/v1/status" | ||||
|     response_mock.headers.getall.return_value = ["*/*"] | ||||
|     assert not is_templated_unauthorized(response_mock) | ||||
|     assert not _is_templated_unauthorized(response_mock) | ||||
|  | ||||
|     response_mock.path = "/api/v1/status" | ||||
|     response_mock.headers.getall.return_value = ["application/json"] | ||||
|     assert not is_templated_unauthorized(response_mock) | ||||
|     assert not _is_templated_unauthorized(response_mock) | ||||
|  | ||||
|  | ||||
| async def test_exception_handler(mocker: MockerFixture) -> None: | ||||
| @ -87,7 +87,7 @@ async def test_exception_handler_unauthorized(mocker: MockerFixture) -> None: | ||||
|     """ | ||||
|     request = pytest.helpers.request("", "", "") | ||||
|     request_handler = AsyncMock(side_effect=HTTPUnauthorized()) | ||||
|     mocker.patch("ahriman.web.middlewares.exception_handler.is_templated_unauthorized", return_value=False) | ||||
|     mocker.patch("ahriman.web.middlewares.exception_handler._is_templated_unauthorized", return_value=False) | ||||
|     render_mock = mocker.patch("aiohttp_jinja2.render_template") | ||||
|  | ||||
|     handler = exception_handler(logging.getLogger()) | ||||
| @ -102,7 +102,7 @@ async def test_exception_handler_unauthorized_templated(mocker: MockerFixture) - | ||||
|     """ | ||||
|     request = pytest.helpers.request("", "", "") | ||||
|     request_handler = AsyncMock(side_effect=HTTPUnauthorized()) | ||||
|     mocker.patch("ahriman.web.middlewares.exception_handler.is_templated_unauthorized", return_value=True) | ||||
|     mocker.patch("ahriman.web.middlewares.exception_handler._is_templated_unauthorized", return_value=True) | ||||
|     render_mock = mocker.patch("aiohttp_jinja2.render_template") | ||||
|  | ||||
|     handler = exception_handler(logging.getLogger()) | ||||
| @ -111,6 +111,44 @@ async def test_exception_handler_unauthorized_templated(mocker: MockerFixture) - | ||||
|     render_mock.assert_called_once_with("error.jinja2", request, context, status=HTTPUnauthorized.status_code) | ||||
|  | ||||
|  | ||||
| async def test_exception_handler_options() -> None: | ||||
|     """ | ||||
|     must handle OPTIONS request | ||||
|     """ | ||||
|     request = pytest.helpers.request("", "", "OPTIONS") | ||||
|     request_handler = AsyncMock(side_effect=HTTPMethodNotAllowed("OPTIONS", ["GET"])) | ||||
|  | ||||
|     handler = exception_handler(logging.getLogger()) | ||||
|     with pytest.raises(HTTPNoContent) as response: | ||||
|         await handler(request, request_handler) | ||||
|         assert response.headers["Allow"] == "GET" | ||||
|  | ||||
|  | ||||
| async def test_exception_handler_head() -> None: | ||||
|     """ | ||||
|     must handle missing HEAD requests | ||||
|     """ | ||||
|     request = pytest.helpers.request("", "", "HEAD") | ||||
|     request_handler = AsyncMock(side_effect=HTTPMethodNotAllowed("HEAD", ["HEAD,GET"])) | ||||
|  | ||||
|     handler = exception_handler(logging.getLogger()) | ||||
|     with pytest.raises(HTTPMethodNotAllowed) as response: | ||||
|         await handler(request, request_handler) | ||||
|         assert response.headers["Allow"] == "GET" | ||||
|  | ||||
|  | ||||
| async def test_exception_handler_method_not_allowed() -> None: | ||||
|     """ | ||||
|     must handle not allowed methodss | ||||
|     """ | ||||
|     request = pytest.helpers.request("", "", "POST") | ||||
|     request_handler = AsyncMock(side_effect=HTTPMethodNotAllowed("POST", ["GET"])) | ||||
|  | ||||
|     handler = exception_handler(logging.getLogger()) | ||||
|     with pytest.raises(HTTPMethodNotAllowed): | ||||
|         await handler(request, request_handler) | ||||
|  | ||||
|  | ||||
| async def test_exception_handler_client_error(mocker: MockerFixture) -> None: | ||||
|     """ | ||||
|     must handle client exception | ||||
|  | ||||
							
								
								
									
										1
									
								
								tests/ahriman/web/schemas/test_aur_package_schema.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/ahriman/web/schemas/test_aur_package_schema.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| # schema testing goes in view class tests | ||||
							
								
								
									
										9
									
								
								tests/ahriman/web/schemas/test_auth_schema.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								tests/ahriman/web/schemas/test_auth_schema.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | ||||
| from ahriman.web.schemas.auth_schema import AuthSchema | ||||
|  | ||||
|  | ||||
| def test_schema() -> None: | ||||
|     """ | ||||
|     must return valid schema | ||||
|     """ | ||||
|     schema = AuthSchema() | ||||
|     assert not schema.validate({"API_SESSION": "key"}) | ||||
							
								
								
									
										1
									
								
								tests/ahriman/web/schemas/test_counters_schema.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/ahriman/web/schemas/test_counters_schema.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| # schema testing goes in view class tests | ||||
							
								
								
									
										1
									
								
								tests/ahriman/web/schemas/test_error_schema.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/ahriman/web/schemas/test_error_schema.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| # schema testing goes in view class tests | ||||
							
								
								
									
										1
									
								
								tests/ahriman/web/schemas/test_internal_status_schema.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/ahriman/web/schemas/test_internal_status_schema.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| # schema testing goes in view class tests | ||||
							
								
								
									
										1
									
								
								tests/ahriman/web/schemas/test_log_schema.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/ahriman/web/schemas/test_log_schema.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| # schema testing goes in view class tests | ||||
							
								
								
									
										1
									
								
								tests/ahriman/web/schemas/test_login_schema.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/ahriman/web/schemas/test_login_schema.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| # schema testing goes in view class tests | ||||
							
								
								
									
										1
									
								
								tests/ahriman/web/schemas/test_logs_schema.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/ahriman/web/schemas/test_logs_schema.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| # schema testing goes in view class tests | ||||
							
								
								
									
										1
									
								
								tests/ahriman/web/schemas/test_oauth2_schema.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/ahriman/web/schemas/test_oauth2_schema.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| # schema testing goes in view class tests | ||||
							
								
								
									
										10
									
								
								tests/ahriman/web/schemas/test_package_name_schema.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								tests/ahriman/web/schemas/test_package_name_schema.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| from ahriman.models.package import Package | ||||
| from ahriman.web.schemas.package_name_schema import PackageNameSchema | ||||
|  | ||||
|  | ||||
| def test_schema(package_ahriman: Package) -> None: | ||||
|     """ | ||||
|     must return valid schema | ||||
|     """ | ||||
|     schema = PackageNameSchema() | ||||
|     assert not schema.validate({"package": package_ahriman.base}) | ||||
							
								
								
									
										1
									
								
								tests/ahriman/web/schemas/test_package_names_schema.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/ahriman/web/schemas/test_package_names_schema.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| # schema testing goes in view class tests | ||||
| @ -0,0 +1 @@ | ||||
| # schema testing goes in view class tests | ||||
							
								
								
									
										1
									
								
								tests/ahriman/web/schemas/test_package_schema.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/ahriman/web/schemas/test_package_schema.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| # schema testing goes in view class tests | ||||
							
								
								
									
										1
									
								
								tests/ahriman/web/schemas/test_package_status_schema.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/ahriman/web/schemas/test_package_status_schema.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| # schema testing goes in view class tests | ||||
							
								
								
									
										1
									
								
								tests/ahriman/web/schemas/test_pgp_key_id_schema.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/ahriman/web/schemas/test_pgp_key_id_schema.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| # schema testing goes in view class tests | ||||
							
								
								
									
										1
									
								
								tests/ahriman/web/schemas/test_pgp_key_schema.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/ahriman/web/schemas/test_pgp_key_schema.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| # schema testing goes in view class tests | ||||
							
								
								
									
										1
									
								
								tests/ahriman/web/schemas/test_remote_schema.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/ahriman/web/schemas/test_remote_schema.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| # schema testing goes in view class tests | ||||
							
								
								
									
										1
									
								
								tests/ahriman/web/schemas/test_search_schema.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/ahriman/web/schemas/test_search_schema.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| # schema testing goes in view class tests | ||||
							
								
								
									
										1
									
								
								tests/ahriman/web/schemas/test_status_schema.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/ahriman/web/schemas/test_status_schema.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| # schema testing goes in view class tests | ||||
							
								
								
									
										57
									
								
								tests/ahriman/web/test_apispec.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								tests/ahriman/web/test_apispec.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,57 @@ | ||||
| import pytest | ||||
|  | ||||
| from aiohttp.web import Application | ||||
| from pytest_mock import MockerFixture | ||||
|  | ||||
| from ahriman import version | ||||
| from ahriman.web.apispec import _info, _security, _servers, setup_apispec | ||||
|  | ||||
|  | ||||
| def test_info() -> None: | ||||
|     """ | ||||
|     must generate info object for swagger | ||||
|     """ | ||||
|     info = _info() | ||||
|     assert info["title"] == "ahriman" | ||||
|     assert info["version"] == version.__version__ | ||||
|  | ||||
|  | ||||
| def test_security() -> None: | ||||
|     """ | ||||
|     must generate security definitions for swagger | ||||
|     """ | ||||
|     token = next(iter(_security()))["token"] | ||||
|     assert token == {"type": "apiKey", "name": "API_SESSION", "in": "cookie"} | ||||
|  | ||||
|  | ||||
| def test_servers(application: Application) -> None: | ||||
|     """ | ||||
|     must generate servers definitions | ||||
|     """ | ||||
|     servers = _servers(application) | ||||
|     assert servers == [{"url": "http://127.0.0.1:8080"}] | ||||
|  | ||||
|  | ||||
| def test_servers_address(application: Application) -> None: | ||||
|     """ | ||||
|     must generate servers definitions with address | ||||
|     """ | ||||
|     application["configuration"].set_option("web", "address", "https://example.com") | ||||
|     servers = _servers(application) | ||||
|     assert servers == [{"url": "https://example.com"}] | ||||
|  | ||||
|  | ||||
| def test_setup_apispec(application: Application, mocker: MockerFixture) -> None: | ||||
|     """ | ||||
|     must set api specification | ||||
|     """ | ||||
|     apispec_mock = mocker.patch("aiohttp_apispec.setup_aiohttp_apispec") | ||||
|     setup_apispec(application) | ||||
|     apispec_mock.assert_called_once_with( | ||||
|         application, | ||||
|         url="/api-docs/swagger.json", | ||||
|         openapi_version="3.0.2", | ||||
|         info=pytest.helpers.anyvar(int), | ||||
|         servers=pytest.helpers.anyvar(int), | ||||
|         security=pytest.helpers.anyvar(int), | ||||
|     ) | ||||
							
								
								
									
										20
									
								
								tests/ahriman/web/test_cors.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								tests/ahriman/web/test_cors.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,20 @@ | ||||
| import aiohttp_cors | ||||
| import pytest | ||||
|  | ||||
| from aiohttp.web import Application | ||||
|  | ||||
|  | ||||
| def test_setup_cors(application: Application) -> None: | ||||
|     """ | ||||
|     must setup CORS | ||||
|     """ | ||||
|     cors: aiohttp_cors.CorsConfig = application[aiohttp_cors.APP_CONFIG_KEY] | ||||
|     # let's test here that it is enabled for all requests | ||||
|     for route in application.router.routes(): | ||||
|         # we don't want to deal with match info here though | ||||
|         try: | ||||
|             url = route.url_for() | ||||
|         except (KeyError, TypeError): | ||||
|             continue | ||||
|         request = pytest.helpers.request(application, url, route.method, resource=route.resource) | ||||
|         assert cors._cors_impl._router_adapter.is_cors_enabled_on_request(request) | ||||
| @ -1,10 +1,10 @@ | ||||
| from aiohttp import web | ||||
| from aiohttp.web import Application | ||||
|  | ||||
| from ahriman.core.configuration import Configuration | ||||
| from ahriman.web.routes import setup_routes | ||||
|  | ||||
|  | ||||
| def test_setup_routes(application: web.Application, configuration: Configuration) -> None: | ||||
| def test_setup_routes(application: Application, configuration: Configuration) -> None: | ||||
|     """ | ||||
|     must generate non-empty list of routes | ||||
|     """ | ||||
|  | ||||
| @ -1,17 +1,17 @@ | ||||
| import pytest | ||||
| import socket | ||||
|  | ||||
| from aiohttp import web | ||||
| from aiohttp.web import Application | ||||
| from pytest_mock import MockerFixture | ||||
| from unittest.mock import call as MockCall | ||||
|  | ||||
| from ahriman.core.exceptions import InitializeError | ||||
| from ahriman.core.log.filtered_access_logger import FilteredAccessLogger | ||||
| from ahriman.core.status.watcher import Watcher | ||||
| from ahriman.web.web import create_socket, on_shutdown, on_startup, run_server | ||||
| from ahriman.web.web import _create_socket, _on_shutdown, _on_startup, run_server | ||||
|  | ||||
|  | ||||
| async def test_create_socket(application: web.Application, mocker: MockerFixture) -> None: | ||||
| async def test_create_socket(application: Application, mocker: MockerFixture) -> None: | ||||
|     """ | ||||
|     must create socket | ||||
|     """ | ||||
| @ -23,7 +23,7 @@ async def test_create_socket(application: web.Application, mocker: MockerFixture | ||||
|     chmod_mock = mocker.patch("pathlib.Path.chmod") | ||||
|     unlink_mock = mocker.patch("pathlib.Path.unlink") | ||||
|  | ||||
|     sock = create_socket(application["configuration"], application) | ||||
|     sock = _create_socket(application["configuration"], application) | ||||
|     assert sock.family == socket.AF_UNIX | ||||
|     assert sock.type == socket.SOCK_STREAM | ||||
|     bind_mock.assert_called_once_with(str(path)) | ||||
| @ -35,14 +35,14 @@ async def test_create_socket(application: web.Application, mocker: MockerFixture | ||||
|     unlink_mock.assert_has_calls([MockCall(missing_ok=True), MockCall(missing_ok=True)]) | ||||
|  | ||||
|  | ||||
| def test_create_socket_empty(application: web.Application) -> None: | ||||
| def test_create_socket_empty(application: Application) -> None: | ||||
|     """ | ||||
|     must skip socket creation if not set by configuration | ||||
|     """ | ||||
|     assert create_socket(application["configuration"], application) is None | ||||
|     assert _create_socket(application["configuration"], application) is None | ||||
|  | ||||
|  | ||||
| def test_create_socket_safe(application: web.Application, mocker: MockerFixture) -> None: | ||||
| def test_create_socket_safe(application: Application, mocker: MockerFixture) -> None: | ||||
|     """ | ||||
|     must create socket with default permission set | ||||
|     """ | ||||
| @ -54,32 +54,32 @@ def test_create_socket_safe(application: web.Application, mocker: MockerFixture) | ||||
|     mocker.patch("pathlib.Path.unlink") | ||||
|     chmod_mock = mocker.patch("pathlib.Path.chmod") | ||||
|  | ||||
|     sock = create_socket(application["configuration"], application) | ||||
|     sock = _create_socket(application["configuration"], application) | ||||
|     assert sock is not None | ||||
|     chmod_mock.assert_not_called() | ||||
|  | ||||
|  | ||||
| async def test_on_shutdown(application: web.Application, mocker: MockerFixture) -> None: | ||||
| async def test_on_shutdown(application: Application, mocker: MockerFixture) -> None: | ||||
|     """ | ||||
|     must write information to log | ||||
|     """ | ||||
|     logging_mock = mocker.patch("logging.Logger.warning") | ||||
|     await on_shutdown(application) | ||||
|     await _on_shutdown(application) | ||||
|     logging_mock.assert_called_once_with(pytest.helpers.anyvar(str, True)) | ||||
|  | ||||
|  | ||||
| async def test_on_startup(application: web.Application, watcher: Watcher, mocker: MockerFixture) -> None: | ||||
| async def test_on_startup(application: Application, watcher: Watcher, mocker: MockerFixture) -> None: | ||||
|     """ | ||||
|     must call load method | ||||
|     """ | ||||
|     mocker.patch("aiohttp.web.Application.__getitem__", return_value=watcher) | ||||
|     load_mock = mocker.patch("ahriman.core.status.watcher.Watcher.load") | ||||
|  | ||||
|     await on_startup(application) | ||||
|     await _on_startup(application) | ||||
|     load_mock.assert_called_once_with() | ||||
|  | ||||
|  | ||||
| async def test_on_startup_exception(application: web.Application, watcher: Watcher, mocker: MockerFixture) -> None: | ||||
| async def test_on_startup_exception(application: Application, watcher: Watcher, mocker: MockerFixture) -> None: | ||||
|     """ | ||||
|     must throw exception on load error | ||||
|     """ | ||||
| @ -87,16 +87,16 @@ async def test_on_startup_exception(application: web.Application, watcher: Watch | ||||
|     mocker.patch("ahriman.core.status.watcher.Watcher.load", side_effect=Exception()) | ||||
|  | ||||
|     with pytest.raises(InitializeError): | ||||
|         await on_startup(application) | ||||
|         await _on_startup(application) | ||||
|  | ||||
|  | ||||
| def test_run(application: web.Application, mocker: MockerFixture) -> None: | ||||
| def test_run(application: Application, mocker: MockerFixture) -> None: | ||||
|     """ | ||||
|     must run application | ||||
|     """ | ||||
|     port = 8080 | ||||
|     application["configuration"].set_option("web", "port", str(port)) | ||||
|     run_application_mock = mocker.patch("aiohttp.web.run_app") | ||||
|     run_application_mock = mocker.patch("ahriman.web.web.run_app") | ||||
|  | ||||
|     run_server(application) | ||||
|     run_application_mock.assert_called_once_with( | ||||
| @ -105,13 +105,13 @@ def test_run(application: web.Application, mocker: MockerFixture) -> None: | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def test_run_with_auth(application_with_auth: web.Application, mocker: MockerFixture) -> None: | ||||
| def test_run_with_auth(application_with_auth: Application, mocker: MockerFixture) -> None: | ||||
|     """ | ||||
|     must run application with enabled authorization | ||||
|     """ | ||||
|     port = 8080 | ||||
|     application_with_auth["configuration"].set_option("web", "port", str(port)) | ||||
|     run_application_mock = mocker.patch("aiohttp.web.run_app") | ||||
|     run_application_mock = mocker.patch("ahriman.web.web.run_app") | ||||
|  | ||||
|     run_server(application_with_auth) | ||||
|     run_application_mock.assert_called_once_with( | ||||
| @ -120,13 +120,13 @@ def test_run_with_auth(application_with_auth: web.Application, mocker: MockerFix | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def test_run_with_debug(application_with_debug: web.Application, mocker: MockerFixture) -> None: | ||||
| def test_run_with_debug(application_with_debug: Application, mocker: MockerFixture) -> None: | ||||
|     """ | ||||
|     must run application with enabled debug panel | ||||
|     """ | ||||
|     port = 8080 | ||||
|     application_with_debug["configuration"].set_option("web", "port", str(port)) | ||||
|     run_application_mock = mocker.patch("aiohttp.web.run_app") | ||||
|     run_application_mock = mocker.patch("ahriman.web.web.run_app") | ||||
|  | ||||
|     run_server(application_with_debug) | ||||
|     run_application_mock.assert_called_once_with( | ||||
| @ -135,14 +135,14 @@ def test_run_with_debug(application_with_debug: web.Application, mocker: MockerF | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def test_run_with_socket(application: web.Application, mocker: MockerFixture) -> None: | ||||
| def test_run_with_socket(application: Application, mocker: MockerFixture) -> None: | ||||
|     """ | ||||
|     must run application | ||||
|     """ | ||||
|     port = 8080 | ||||
|     application["configuration"].set_option("web", "port", str(port)) | ||||
|     socket_mock = mocker.patch("ahriman.web.web.create_socket", return_value=42) | ||||
|     run_application_mock = mocker.patch("aiohttp.web.run_app") | ||||
|     socket_mock = mocker.patch("ahriman.web.web._create_socket", return_value=42) | ||||
|     run_application_mock = mocker.patch("ahriman.web.web.run_app") | ||||
|  | ||||
|     run_server(application) | ||||
|     socket_mock.assert_called_once_with(application["configuration"], application) | ||||
|  | ||||
							
								
								
									
										24
									
								
								tests/ahriman/web/views/api/test_views_api_docs.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								tests/ahriman/web/views/api/test_views_api_docs.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,24 @@ | ||||
| import pytest | ||||
|  | ||||
| from aiohttp.test_utils import TestClient | ||||
|  | ||||
| from ahriman.models.user_access import UserAccess | ||||
| from ahriman.web.views.api.docs import DocsView | ||||
|  | ||||
|  | ||||
| async def test_get_permission() -> None: | ||||
|     """ | ||||
|     must return correct permission for the request | ||||
|     """ | ||||
|     for method in ("GET",): | ||||
|         request = pytest.helpers.request("", "", method) | ||||
|         assert await DocsView.get_permission(request) == UserAccess.Unauthorized | ||||
|  | ||||
|  | ||||
| async def test_get(client: TestClient) -> None: | ||||
|     """ | ||||
|     must generate api-docs correctly | ||||
|     """ | ||||
|     response = await client.get("/api-docs") | ||||
|     assert response.ok | ||||
|     assert await response.text() | ||||
							
								
								
									
										91
									
								
								tests/ahriman/web/views/api/test_views_api_swagger.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								tests/ahriman/web/views/api/test_views_api_swagger.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,91 @@ | ||||
| import pytest | ||||
|  | ||||
| from aiohttp.test_utils import TestClient | ||||
|  | ||||
| from ahriman.models.user_access import UserAccess | ||||
| from ahriman.web.views.api.swagger import SwaggerView | ||||
|  | ||||
|  | ||||
| def _client(client: TestClient) -> TestClient: | ||||
|     """ | ||||
|     generate test client with docs | ||||
|  | ||||
|     Args: | ||||
|         client(TestClient): test client fixture | ||||
|  | ||||
|     Returns: | ||||
|         TestClient: test client fixture with additional properties | ||||
|     """ | ||||
|     client.app["swagger_dict"] = { | ||||
|         "paths": { | ||||
|             "/api/v1/logout": { | ||||
|                 "get": { | ||||
|                     "parameters": [ | ||||
|                         { | ||||
|                             "in": "cookie", | ||||
|                             "name": "API_SESSION", | ||||
|                             "schema": { | ||||
|                                 "type": "string", | ||||
|                             }, | ||||
|                         }, | ||||
|                     ], | ||||
|                 }, | ||||
|                 "head": {}, | ||||
|                 "post": { | ||||
|                     "parameters": [ | ||||
|                         { | ||||
|                             "in": "cookie", | ||||
|                             "name": "API_SESSION", | ||||
|                             "schema": { | ||||
|                                 "type": "string", | ||||
|                             }, | ||||
|                         }, | ||||
|                         { | ||||
|                             "in": "body", | ||||
|                             "name": "schema", | ||||
|                             "schema": { | ||||
|                                 "type": "string", | ||||
|                             }, | ||||
|                         }, | ||||
|                     ], | ||||
|                 }, | ||||
|             }, | ||||
|         }, | ||||
|         "components": {}, | ||||
|         "security": [ | ||||
|             { | ||||
|                 "token": { | ||||
|                     "type": "apiKey", | ||||
|                     "name": "API_SESSION", | ||||
|                     "in": "cookie", | ||||
|                 }, | ||||
|             }, | ||||
|         ], | ||||
|     } | ||||
|  | ||||
|     return client | ||||
|  | ||||
|  | ||||
| async def test_get_permission() -> None: | ||||
|     """ | ||||
|     must return correct permission for the request | ||||
|     """ | ||||
|     for method in ("GET",): | ||||
|         request = pytest.helpers.request("", "", method) | ||||
|         assert await SwaggerView.get_permission(request) == UserAccess.Unauthorized | ||||
|  | ||||
|  | ||||
| async def test_get(client: TestClient) -> None: | ||||
|     """ | ||||
|     must generate api-docs correctly | ||||
|     """ | ||||
|     client = _client(client) | ||||
|     response = await client.get("/api-docs/swagger.json") | ||||
|     assert response.ok | ||||
|  | ||||
|     json = await response.json() | ||||
|     assert "securitySchemes" in json["components"] | ||||
|     assert not any(parameter["in"] == "body" for parameter in json["paths"]["/api/v1/logout"]["post"]["parameters"]) | ||||
|     assert "requestBody" in json["paths"]["/api/v1/logout"]["post"] | ||||
|     assert "requestBody" not in json["paths"]["/api/v1/logout"]["get"] | ||||
|     assert "requestBody" not in json["paths"]["/api/v1/logout"]["head"] | ||||
| @ -1,17 +1,17 @@ | ||||
| import pytest | ||||
|  | ||||
| from aiohttp import web | ||||
| from aiohttp.web import Application | ||||
|  | ||||
| from ahriman.web.views.base import BaseView | ||||
|  | ||||
|  | ||||
| @pytest.fixture | ||||
| def base(application: web.Application) -> BaseView: | ||||
| def base(application: Application) -> BaseView: | ||||
|     """ | ||||
|     base view fixture | ||||
|  | ||||
|     Args: | ||||
|         application(web.Application): application fixture | ||||
|         application(Application): application fixture | ||||
|  | ||||
|     Returns: | ||||
|         BaseView: generated base view fixture | ||||
|  | ||||
| @ -4,6 +4,8 @@ from aiohttp.test_utils import TestClient | ||||
| from pytest_mock import MockerFixture | ||||
|  | ||||
| from ahriman.models.user_access import UserAccess | ||||
| from ahriman.web.schemas.error_schema import ErrorSchema | ||||
| from ahriman.web.schemas.package_names_schema import PackageNamesSchema | ||||
| from ahriman.web.views.service.add import AddView | ||||
|  | ||||
|  | ||||
| @ -21,8 +23,11 @@ async def test_post(client: TestClient, mocker: MockerFixture) -> None: | ||||
|     must call post request correctly | ||||
|     """ | ||||
|     add_mock = mocker.patch("ahriman.core.spawn.Spawn.packages_add") | ||||
|     request_schema = PackageNamesSchema() | ||||
|  | ||||
|     response = await client.post("/api/v1/service/add", json={"packages": ["ahriman"]}) | ||||
|     payload = {"packages": ["ahriman"]} | ||||
|     assert not request_schema.validate(payload) | ||||
|     response = await client.post("/api/v1/service/add", json=payload) | ||||
|     assert response.ok | ||||
|     add_mock.assert_called_once_with(["ahriman"], now=True) | ||||
|  | ||||
| @ -32,15 +37,19 @@ async def test_post_empty(client: TestClient, mocker: MockerFixture) -> None: | ||||
|     must call raise 400 on empty request | ||||
|     """ | ||||
|     add_mock = mocker.patch("ahriman.core.spawn.Spawn.packages_add") | ||||
|     response_schema = ErrorSchema() | ||||
|  | ||||
|     response = await client.post("/api/v1/service/add", json={"packages": [""]}) | ||||
|     assert response.status == 400 | ||||
|     assert not response_schema.validate(await response.json()) | ||||
|     add_mock.assert_not_called() | ||||
|  | ||||
|     response = await client.post("/api/v1/service/add", json={"packages": []}) | ||||
|     assert response.status == 400 | ||||
|     assert not response_schema.validate(await response.json()) | ||||
|     add_mock.assert_not_called() | ||||
|  | ||||
|     response = await client.post("/api/v1/service/add", json={}) | ||||
|     assert response.status == 400 | ||||
|     assert not response_schema.validate(await response.json()) | ||||
|     add_mock.assert_not_called() | ||||
|  | ||||
| @ -4,6 +4,9 @@ from aiohttp.test_utils import TestClient | ||||
| from pytest_mock import MockerFixture | ||||
|  | ||||
| from ahriman.models.user_access import UserAccess | ||||
| from ahriman.web.schemas.error_schema import ErrorSchema | ||||
| from ahriman.web.schemas.pgp_key_id_schema import PGPKeyIdSchema | ||||
| from ahriman.web.schemas.pgp_key_schema import PGPKeySchema | ||||
| from ahriman.web.views.service.pgp import PGPView | ||||
|  | ||||
|  | ||||
| @ -11,7 +14,7 @@ async def test_get_permission() -> None: | ||||
|     """ | ||||
|     must return correct permission for the request | ||||
|     """ | ||||
|     for method in ("GET", "HEAD"): | ||||
|     for method in ("GET",): | ||||
|         request = pytest.helpers.request("", "", method) | ||||
|         assert await PGPView.get_permission(request) == UserAccess.Reporter | ||||
|     for method in ("POST",): | ||||
| @ -24,10 +27,15 @@ async def test_get(client: TestClient, mocker: MockerFixture) -> None: | ||||
|     must retrieve key from the keyserver | ||||
|     """ | ||||
|     import_mock = mocker.patch("ahriman.core.sign.gpg.GPG.key_download", return_value="imported") | ||||
|     request_schema = PGPKeyIdSchema() | ||||
|     response_schema = PGPKeySchema() | ||||
|  | ||||
|     response = await client.get("/api/v1/service/pgp", params={"key": "0xdeadbeaf", "server": "keyserver.ubuntu.com"}) | ||||
|     payload = {"key": "0xdeadbeaf", "server": "keyserver.ubuntu.com"} | ||||
|     assert not request_schema.validate(payload) | ||||
|     response = await client.get("/api/v1/service/pgp", params=payload) | ||||
|     assert response.ok | ||||
|     import_mock.assert_called_once_with("keyserver.ubuntu.com", "0xdeadbeaf") | ||||
|     assert not response_schema.validate(await response.json()) | ||||
|     assert await response.json() == {"key": "imported"} | ||||
|  | ||||
|  | ||||
| @ -36,9 +44,11 @@ async def test_get_empty(client: TestClient, mocker: MockerFixture) -> None: | ||||
|     must raise 400 on missing parameters | ||||
|     """ | ||||
|     import_mock = mocker.patch("ahriman.core.sign.gpg.GPG.key_download") | ||||
|     response_schema = ErrorSchema() | ||||
|  | ||||
|     response = await client.get("/api/v1/service/pgp") | ||||
|     assert response.status == 400 | ||||
|     assert not response_schema.validate(await response.json()) | ||||
|     import_mock.assert_not_called() | ||||
|  | ||||
|  | ||||
| @ -47,9 +57,11 @@ async def test_get_process_exception(client: TestClient, mocker: MockerFixture) | ||||
|     must raise 404 on invalid PGP server response | ||||
|     """ | ||||
|     import_mock = mocker.patch("ahriman.core.sign.gpg.GPG.key_download", side_effect=Exception()) | ||||
|     response_schema = ErrorSchema() | ||||
|  | ||||
|     response = await client.get("/api/v1/service/pgp", params={"key": "0xdeadbeaf", "server": "keyserver.ubuntu.com"}) | ||||
|     assert response.status == 404 | ||||
|     assert not response_schema.validate(await response.json()) | ||||
|     import_mock.assert_called_once_with("keyserver.ubuntu.com", "0xdeadbeaf") | ||||
|  | ||||
|  | ||||
| @ -58,8 +70,11 @@ async def test_post(client: TestClient, mocker: MockerFixture) -> None: | ||||
|     must call post request correctly | ||||
|     """ | ||||
|     import_mock = mocker.patch("ahriman.core.spawn.Spawn.key_import") | ||||
|     request_schema = PGPKeyIdSchema() | ||||
|  | ||||
|     response = await client.post("/api/v1/service/pgp", json={"key": "0xdeadbeaf", "server": "keyserver.ubuntu.com"}) | ||||
|     payload = {"key": "0xdeadbeaf", "server": "keyserver.ubuntu.com"} | ||||
|     assert not request_schema.validate(payload) | ||||
|     response = await client.post("/api/v1/service/pgp", json=payload) | ||||
|     assert response.ok | ||||
|     import_mock.assert_called_once_with("0xdeadbeaf", "keyserver.ubuntu.com") | ||||
|  | ||||
| @ -69,7 +84,9 @@ async def test_post_exception(client: TestClient, mocker: MockerFixture) -> None | ||||
|     must raise exception on missing key payload | ||||
|     """ | ||||
|     import_mock = mocker.patch("ahriman.core.spawn.Spawn.key_import") | ||||
|     response_schema = ErrorSchema() | ||||
|  | ||||
|     response = await client.post("/api/v1/service/pgp") | ||||
|     assert response.status == 400 | ||||
|     assert not response_schema.validate(await response.json()) | ||||
|     import_mock.assert_not_called() | ||||
|  | ||||
| @ -4,6 +4,8 @@ from aiohttp.test_utils import TestClient | ||||
| from pytest_mock import MockerFixture | ||||
|  | ||||
| from ahriman.models.user_access import UserAccess | ||||
| from ahriman.web.schemas.error_schema import ErrorSchema | ||||
| from ahriman.web.schemas.package_names_schema import PackageNamesSchema | ||||
| from ahriman.web.views.service.rebuild import RebuildView | ||||
|  | ||||
|  | ||||
| @ -21,8 +23,11 @@ async def test_post(client: TestClient, mocker: MockerFixture) -> None: | ||||
|     must call post request correctly | ||||
|     """ | ||||
|     rebuild_mock = mocker.patch("ahriman.core.spawn.Spawn.packages_rebuild") | ||||
|     request_schema = PackageNamesSchema() | ||||
|  | ||||
|     response = await client.post("/api/v1/service/rebuild", json={"packages": ["python", "ahriman"]}) | ||||
|     payload = {"packages": ["python", "ahriman"]} | ||||
|     assert not request_schema.validate(payload) | ||||
|     response = await client.post("/api/v1/service/rebuild", json=payload) | ||||
|     assert response.ok | ||||
|     rebuild_mock.assert_called_once_with("python") | ||||
|  | ||||
| @ -32,7 +37,9 @@ async def test_post_exception(client: TestClient, mocker: MockerFixture) -> None | ||||
|     must raise exception on missing packages payload | ||||
|     """ | ||||
|     rebuild_mock = mocker.patch("ahriman.core.spawn.Spawn.packages_rebuild") | ||||
|     response_schema = ErrorSchema() | ||||
|  | ||||
|     response = await client.post("/api/v1/service/rebuild") | ||||
|     assert response.status == 400 | ||||
|     assert not response_schema.validate(await response.json()) | ||||
|     rebuild_mock.assert_not_called() | ||||
|  | ||||
| @ -4,6 +4,8 @@ from aiohttp.test_utils import TestClient | ||||
| from pytest_mock import MockerFixture | ||||
|  | ||||
| from ahriman.models.user_access import UserAccess | ||||
| from ahriman.web.schemas.error_schema import ErrorSchema | ||||
| from ahriman.web.schemas.package_names_schema import PackageNamesSchema | ||||
| from ahriman.web.views.service.remove import RemoveView | ||||
|  | ||||
|  | ||||
| @ -21,8 +23,11 @@ async def test_post(client: TestClient, mocker: MockerFixture) -> None: | ||||
|     must call post request correctly | ||||
|     """ | ||||
|     remove_mock = mocker.patch("ahriman.core.spawn.Spawn.packages_remove") | ||||
|     request_schema = PackageNamesSchema() | ||||
|  | ||||
|     response = await client.post("/api/v1/service/remove", json={"packages": ["ahriman"]}) | ||||
|     payload = {"packages": ["ahriman"]} | ||||
|     assert not request_schema.validate(payload) | ||||
|     response = await client.post("/api/v1/service/remove", json=payload) | ||||
|     assert response.ok | ||||
|     remove_mock.assert_called_once_with(["ahriman"]) | ||||
|  | ||||
| @ -32,7 +37,9 @@ async def test_post_exception(client: TestClient, mocker: MockerFixture) -> None | ||||
|     must raise exception on missing packages payload | ||||
|     """ | ||||
|     remove_mock = mocker.patch("ahriman.core.spawn.Spawn.packages_remove") | ||||
|     response_schema = ErrorSchema() | ||||
|  | ||||
|     response = await client.post("/api/v1/service/remove") | ||||
|     assert response.status == 400 | ||||
|     assert not response_schema.validate(await response.json()) | ||||
|     remove_mock.assert_not_called() | ||||
|  | ||||
| @ -4,6 +4,8 @@ from aiohttp.test_utils import TestClient | ||||
| from pytest_mock import MockerFixture | ||||
|  | ||||
| from ahriman.models.user_access import UserAccess | ||||
| from ahriman.web.schemas.error_schema import ErrorSchema | ||||
| from ahriman.web.schemas.package_names_schema import PackageNamesSchema | ||||
| from ahriman.web.views.service.request import RequestView | ||||
|  | ||||
|  | ||||
| @ -21,8 +23,11 @@ async def test_post(client: TestClient, mocker: MockerFixture) -> None: | ||||
|     must call post request correctly | ||||
|     """ | ||||
|     add_mock = mocker.patch("ahriman.core.spawn.Spawn.packages_add") | ||||
|     request_schema = PackageNamesSchema() | ||||
|  | ||||
|     response = await client.post("/api/v1/service/request", json={"packages": ["ahriman"]}) | ||||
|     payload = {"packages": ["ahriman"]} | ||||
|     assert not request_schema.validate(payload) | ||||
|     response = await client.post("/api/v1/service/request", json=payload) | ||||
|     assert response.ok | ||||
|     add_mock.assert_called_once_with(["ahriman"], now=False) | ||||
|  | ||||
| @ -32,7 +37,9 @@ async def test_post_exception(client: TestClient, mocker: MockerFixture) -> None | ||||
|     must raise exception on missing packages payload | ||||
|     """ | ||||
|     add_mock = mocker.patch("ahriman.core.spawn.Spawn.packages_add") | ||||
|     response_schema = ErrorSchema() | ||||
|  | ||||
|     response = await client.post("/api/v1/service/request") | ||||
|     assert response.status == 400 | ||||
|     assert not response_schema.validate(await response.json()) | ||||
|     add_mock.assert_not_called() | ||||
|  | ||||
| @ -5,6 +5,9 @@ from pytest_mock import MockerFixture | ||||
|  | ||||
| from ahriman.models.aur_package import AURPackage | ||||
| from ahriman.models.user_access import UserAccess | ||||
| from ahriman.web.schemas.aur_package_schema import AURPackageSchema | ||||
| from ahriman.web.schemas.error_schema import ErrorSchema | ||||
| from ahriman.web.schemas.search_schema import SearchSchema | ||||
| from ahriman.web.views.service.search import SearchView | ||||
|  | ||||
|  | ||||
| @ -12,7 +15,7 @@ async def test_get_permission() -> None: | ||||
|     """ | ||||
|     must return correct permission for the request | ||||
|     """ | ||||
|     for method in ("GET", "HEAD"): | ||||
|     for method in ("GET",): | ||||
|         request = pytest.helpers.request("", "", method) | ||||
|         assert await SearchView.get_permission(request) == UserAccess.Reporter | ||||
|  | ||||
| @ -22,11 +25,16 @@ async def test_get(client: TestClient, aur_package_ahriman: AURPackage, mocker: | ||||
|     must call get request correctly | ||||
|     """ | ||||
|     mocker.patch("ahriman.core.alpm.remote.AUR.multisearch", return_value=[aur_package_ahriman]) | ||||
|     request_schema = SearchSchema() | ||||
|     response_schema = AURPackageSchema() | ||||
|  | ||||
|     response = await client.get("/api/v1/service/search", params={"for": "ahriman"}) | ||||
|     payload = {"for": ["ahriman"]} | ||||
|     assert not request_schema.validate(payload) | ||||
|     response = await client.get("/api/v1/service/search", params=payload) | ||||
|     assert response.ok | ||||
|     assert await response.json() == [{"package": aur_package_ahriman.package_base, | ||||
|                                       "description": aur_package_ahriman.description}] | ||||
|     assert not response_schema.validate(await response.json(), many=True) | ||||
|  | ||||
|  | ||||
| async def test_get_exception(client: TestClient, mocker: MockerFixture) -> None: | ||||
| @ -34,9 +42,11 @@ async def test_get_exception(client: TestClient, mocker: MockerFixture) -> None: | ||||
|     must raise 400 on empty search string | ||||
|     """ | ||||
|     search_mock = mocker.patch("ahriman.core.alpm.remote.AUR.multisearch") | ||||
|     response_schema = ErrorSchema() | ||||
|  | ||||
|     response = await client.get("/api/v1/service/search") | ||||
|     assert response.status == 400 | ||||
|     assert not response_schema.validate(await response.json()) | ||||
|     search_mock.assert_not_called() | ||||
|  | ||||
|  | ||||
| @ -45,8 +55,11 @@ async def test_get_empty(client: TestClient, mocker: MockerFixture) -> None: | ||||
|     must raise 404 on empty search result | ||||
|     """ | ||||
|     mocker.patch("ahriman.core.alpm.remote.AUR.multisearch", return_value=[]) | ||||
|     response = await client.get("/api/v1/service/search", params={"for": "ahriman"}) | ||||
|     response_schema = ErrorSchema() | ||||
|  | ||||
|     response = await client.get("/api/v1/service/search", params={"for": ["ahriman"]}) | ||||
|     assert response.status == 404 | ||||
|     assert not response_schema.validate(await response.json()) | ||||
|  | ||||
|  | ||||
| async def test_get_join(client: TestClient, mocker: MockerFixture) -> None: | ||||
| @ -54,7 +67,10 @@ async def test_get_join(client: TestClient, mocker: MockerFixture) -> None: | ||||
|     must join search args with space | ||||
|     """ | ||||
|     search_mock = mocker.patch("ahriman.core.alpm.remote.AUR.multisearch") | ||||
|     request_schema = SearchSchema() | ||||
|  | ||||
|     response = await client.get("/api/v1/service/search", params=[("for", "ahriman"), ("for", "maybe")]) | ||||
|     payload = {"for": ["ahriman", "maybe"]} | ||||
|     assert not request_schema.validate(payload) | ||||
|     response = await client.get("/api/v1/service/search", params=payload) | ||||
|     assert response.ok | ||||
|     search_mock.assert_called_once_with("ahriman", "maybe", pacman=pytest.helpers.anyvar(int)) | ||||
|  | ||||
| @ -5,6 +5,9 @@ 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.schemas.error_schema import ErrorSchema | ||||
| from ahriman.web.schemas.log_schema import LogSchema | ||||
| from ahriman.web.schemas.logs_schema import LogsSchema | ||||
| from ahriman.web.views.status.logs import LogsView | ||||
|  | ||||
|  | ||||
| @ -12,7 +15,7 @@ async def test_get_permission() -> None: | ||||
|     """ | ||||
|     must return correct permission for the request | ||||
|     """ | ||||
|     for method in ("GET", "HEAD"): | ||||
|     for method in ("GET",): | ||||
|         request = pytest.helpers.request("", "", method) | ||||
|         assert await LogsView.get_permission(request) == UserAccess.Reporter | ||||
|     for method in ("DELETE", "POST"): | ||||
| @ -54,11 +57,13 @@ async def test_get(client: TestClient, package_ahriman: Package) -> None: | ||||
|                       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", "process_id": 42}) | ||||
|     response_schema = LogsSchema() | ||||
|  | ||||
|     response = await client.get(f"/api/v1/packages/{package_ahriman.base}/logs") | ||||
|     assert response.status == 200 | ||||
|  | ||||
|     logs = await response.json() | ||||
|     assert not response_schema.validate(logs) | ||||
|     assert logs["logs"] == "[1970-01-01 00:00:42] message" | ||||
|  | ||||
|  | ||||
| @ -66,8 +71,11 @@ async def test_get_not_found(client: TestClient, package_ahriman: Package) -> No | ||||
|     """ | ||||
|     must return not found for missing package | ||||
|     """ | ||||
|     response_schema = ErrorSchema() | ||||
|  | ||||
|     response = await client.get(f"/api/v1/packages/{package_ahriman.base}/logs") | ||||
|     assert response.status == 404 | ||||
|     assert not response_schema.validate(await response.json()) | ||||
|  | ||||
|  | ||||
| async def test_post(client: TestClient, package_ahriman: Package) -> None: | ||||
| @ -76,10 +84,12 @@ async def test_post(client: TestClient, package_ahriman: Package) -> None: | ||||
|     """ | ||||
|     await client.post(f"/api/v1/packages/{package_ahriman.base}", | ||||
|                       json={"status": BuildStatusEnum.Success.value, "package": package_ahriman.view()}) | ||||
|     request_schema = LogSchema() | ||||
|  | ||||
|     post_response = await client.post(f"/api/v1/packages/{package_ahriman.base}/logs", | ||||
|                                       json={"created": 42.0, "message": "message", "process_id": 42}) | ||||
|     assert post_response.status == 204 | ||||
|     payload = {"created": 42.0, "message": "message", "process_id": 42} | ||||
|     assert not request_schema.validate(payload) | ||||
|     response = await client.post(f"/api/v1/packages/{package_ahriman.base}/logs", json=payload) | ||||
|     assert response.status == 204 | ||||
|  | ||||
|     response = await client.get(f"/api/v1/packages/{package_ahriman.base}/logs") | ||||
|     logs = await response.json() | ||||
| @ -90,5 +100,8 @@ async def test_post_exception(client: TestClient, package_ahriman: Package) -> N | ||||
|     """ | ||||
|     must raise exception on invalid payload | ||||
|     """ | ||||
|     post_response = await client.post(f"/api/v1/packages/{package_ahriman.base}/logs", json={}) | ||||
|     assert post_response.status == 400 | ||||
|     response_schema = ErrorSchema() | ||||
|  | ||||
|     response = await client.post(f"/api/v1/packages/{package_ahriman.base}/logs", json={}) | ||||
|     assert response.status == 400 | ||||
|     assert not response_schema.validate(await response.json()) | ||||
|  | ||||
| @ -5,6 +5,8 @@ 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.schemas.error_schema import ErrorSchema | ||||
| from ahriman.web.schemas.package_status_schema import PackageStatusSchema, PackageStatusSimplifiedSchema | ||||
| from ahriman.web.views.status.package import PackageView | ||||
|  | ||||
|  | ||||
| @ -12,7 +14,7 @@ async def test_get_permission() -> None: | ||||
|     """ | ||||
|     must return correct permission for the request | ||||
|     """ | ||||
|     for method in ("GET", "HEAD"): | ||||
|     for method in ("GET",): | ||||
|         request = pytest.helpers.request("", "", method) | ||||
|         assert await PackageView.get_permission(request) == UserAccess.Read | ||||
|     for method in ("DELETE", "POST"): | ||||
| @ -64,11 +66,14 @@ async def test_get(client: TestClient, package_ahriman: Package, package_python_ | ||||
|                       json={"status": BuildStatusEnum.Success.value, "package": package_ahriman.view()}) | ||||
|     await client.post(f"/api/v1/packages/{package_python_schedule.base}", | ||||
|                       json={"status": BuildStatusEnum.Success.value, "package": package_python_schedule.view()}) | ||||
|     response_schema = PackageStatusSchema() | ||||
|  | ||||
|     response = await client.get(f"/api/v1/packages/{package_ahriman.base}") | ||||
|     assert response.ok | ||||
|     json = await response.json() | ||||
|     assert not response_schema.validate(json, many=True) | ||||
|  | ||||
|     packages = [Package.from_json(item["package"]) for item in await response.json()] | ||||
|     packages = [Package.from_json(item["package"]) for item in json] | ||||
|     assert packages | ||||
|     assert {package.base for package in packages} == {package_ahriman.base} | ||||
|  | ||||
| @ -77,18 +82,23 @@ async def test_get_not_found(client: TestClient, package_ahriman: Package) -> No | ||||
|     """ | ||||
|     must return Not Found for unknown package | ||||
|     """ | ||||
|     response_schema = ErrorSchema() | ||||
|  | ||||
|     response = await client.get(f"/api/v1/packages/{package_ahriman.base}") | ||||
|     assert response.status == 404 | ||||
|     assert not response_schema.validate(await response.json()) | ||||
|  | ||||
|  | ||||
| async def test_post(client: TestClient, package_ahriman: Package) -> None: | ||||
|     """ | ||||
|     must update package status | ||||
|     """ | ||||
|     post_response = await client.post( | ||||
|         f"/api/v1/packages/{package_ahriman.base}", | ||||
|         json={"status": BuildStatusEnum.Success.value, "package": package_ahriman.view()}) | ||||
|     assert post_response.status == 204 | ||||
|     request_schema = PackageStatusSimplifiedSchema() | ||||
|  | ||||
|     payload = {"status": BuildStatusEnum.Success.value, "package": package_ahriman.view()} | ||||
|     assert not request_schema.validate(payload) | ||||
|     response = await client.post(f"/api/v1/packages/{package_ahriman.base}", json=payload) | ||||
|     assert response.status == 204 | ||||
|  | ||||
|     response = await client.get(f"/api/v1/packages/{package_ahriman.base}") | ||||
|     assert response.ok | ||||
| @ -98,22 +108,28 @@ async def test_post_exception(client: TestClient, package_ahriman: Package) -> N | ||||
|     """ | ||||
|     must raise exception on invalid payload | ||||
|     """ | ||||
|     post_response = await client.post(f"/api/v1/packages/{package_ahriman.base}", json={}) | ||||
|     assert post_response.status == 400 | ||||
|     response_schema = ErrorSchema() | ||||
|  | ||||
|     response = await client.post(f"/api/v1/packages/{package_ahriman.base}", json={}) | ||||
|     assert response.status == 400 | ||||
|     assert not response_schema.validate(await response.json()) | ||||
|  | ||||
|  | ||||
| async def test_post_light(client: TestClient, package_ahriman: Package) -> None: | ||||
|     """ | ||||
|     must update package status only | ||||
|     """ | ||||
|     post_response = await client.post( | ||||
|         f"/api/v1/packages/{package_ahriman.base}", | ||||
|         json={"status": BuildStatusEnum.Unknown.value, "package": package_ahriman.view()}) | ||||
|     assert post_response.status == 204 | ||||
|     request_schema = PackageStatusSimplifiedSchema() | ||||
|  | ||||
|     post_response = await client.post( | ||||
|         f"/api/v1/packages/{package_ahriman.base}", json={"status": BuildStatusEnum.Success.value}) | ||||
|     assert post_response.status == 204 | ||||
|     payload = {"status": BuildStatusEnum.Unknown.value, "package": package_ahriman.view()} | ||||
|     assert not request_schema.validate(payload) | ||||
|     response = await client.post(f"/api/v1/packages/{package_ahriman.base}", json=payload) | ||||
|     assert response.status == 204 | ||||
|  | ||||
|     payload = {"status": BuildStatusEnum.Success.value} | ||||
|     assert not request_schema.validate(payload) | ||||
|     response = await client.post(f"/api/v1/packages/{package_ahriman.base}", json=payload) | ||||
|     assert response.status == 204 | ||||
|  | ||||
|     response = await client.get(f"/api/v1/packages/{package_ahriman.base}") | ||||
|     assert response.ok | ||||
| @ -128,6 +144,11 @@ async def test_post_not_found(client: TestClient, package_ahriman: Package) -> N | ||||
|     """ | ||||
|     must raise exception on status update for unknown package | ||||
|     """ | ||||
|     post_response = await client.post( | ||||
|         f"/api/v1/packages/{package_ahriman.base}", json={"status": BuildStatusEnum.Success.value}) | ||||
|     assert post_response.status == 400 | ||||
|     request_schema = PackageStatusSimplifiedSchema() | ||||
|     response_schema = ErrorSchema() | ||||
|  | ||||
|     payload = {"status": BuildStatusEnum.Success.value} | ||||
|     assert not request_schema.validate(payload) | ||||
|     response = await client.post(f"/api/v1/packages/{package_ahriman.base}", json=payload) | ||||
|     assert response.status == 400 | ||||
|     assert not response_schema.validate(await response.json()) | ||||
|  | ||||
| @ -6,6 +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.schemas.package_status_schema import PackageStatusSchema | ||||
| from ahriman.web.views.status.packages import PackagesView | ||||
|  | ||||
|  | ||||
| @ -13,7 +14,7 @@ async def test_get_permission() -> None: | ||||
|     """ | ||||
|     must return correct permission for the request | ||||
|     """ | ||||
|     for method in ("GET", "HEAD"): | ||||
|     for method in ("GET",): | ||||
|         request = pytest.helpers.request("", "", method) | ||||
|         assert await PackagesView.get_permission(request) == UserAccess.Read | ||||
|     for method in ("POST",): | ||||
| @ -29,11 +30,14 @@ async def test_get(client: TestClient, package_ahriman: Package, package_python_ | ||||
|                       json={"status": BuildStatusEnum.Success.value, "package": package_ahriman.view()}) | ||||
|     await client.post(f"/api/v1/packages/{package_python_schedule.base}", | ||||
|                       json={"status": BuildStatusEnum.Success.value, "package": package_python_schedule.view()}) | ||||
|     response_schema = PackageStatusSchema() | ||||
|  | ||||
|     response = await client.get("/api/v1/packages") | ||||
|     assert response.ok | ||||
|     json = await response.json() | ||||
|     assert not response_schema.validate(json, many=True) | ||||
|  | ||||
|     packages = [Package.from_json(item["package"]) for item in await response.json()] | ||||
|     packages = [Package.from_json(item["package"]) for item in json] | ||||
|     assert packages | ||||
|     assert {package.base for package in packages} == {package_ahriman.base, package_python_schedule.base} | ||||
|  | ||||
| @ -43,6 +47,7 @@ async def test_post(client: TestClient, mocker: MockerFixture) -> None: | ||||
|     must be able to reload packages | ||||
|     """ | ||||
|     load_mock = mocker.patch("ahriman.core.status.watcher.Watcher.load") | ||||
|  | ||||
|     response = await client.post("/api/v1/packages") | ||||
|     assert response.status == 204 | ||||
|     load_mock.assert_called_once_with() | ||||
|  | ||||
| @ -9,6 +9,9 @@ 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.schemas.error_schema import ErrorSchema | ||||
| from ahriman.web.schemas.internal_status_schema import InternalStatusSchema | ||||
| from ahriman.web.schemas.status_schema import StatusSchema | ||||
| from ahriman.web.views.status.status import StatusView | ||||
|  | ||||
|  | ||||
| @ -16,7 +19,7 @@ async def test_get_permission() -> None: | ||||
|     """ | ||||
|     must return correct permission for the request | ||||
|     """ | ||||
|     for method in ("GET", "HEAD"): | ||||
|     for method in ("GET",): | ||||
|         request = pytest.helpers.request("", "", method) | ||||
|         assert await StatusView.get_permission(request) == UserAccess.Read | ||||
|     for method in ("POST",): | ||||
| @ -30,11 +33,13 @@ async def test_get(client: TestClient, package_ahriman: Package) -> None: | ||||
|     """ | ||||
|     await client.post(f"/api/v1/packages/{package_ahriman.base}", | ||||
|                       json={"status": BuildStatusEnum.Success.value, "package": package_ahriman.view()}) | ||||
|     response_schema = InternalStatusSchema() | ||||
|  | ||||
|     response = await client.get("/api/v1/status") | ||||
|     assert response.ok | ||||
|  | ||||
|     json = await response.json() | ||||
|     assert not response_schema.validate(json) | ||||
|  | ||||
|     assert json["version"] == version.__version__ | ||||
|     assert json["packages"] | ||||
|     assert json["packages"]["total"] == 1 | ||||
| @ -44,7 +49,10 @@ async def test_post(client: TestClient) -> None: | ||||
|     """ | ||||
|     must update service status correctly | ||||
|     """ | ||||
|     request_schema = StatusSchema() | ||||
|  | ||||
|     payload = {"status": BuildStatusEnum.Success.value} | ||||
|     assert not request_schema.validate(payload) | ||||
|     post_response = await client.post("/api/v1/status", json=payload) | ||||
|     assert post_response.status == 204 | ||||
|  | ||||
| @ -59,8 +67,11 @@ async def test_post_exception(client: TestClient) -> None: | ||||
|     """ | ||||
|     must raise exception on invalid payload | ||||
|     """ | ||||
|     post_response = await client.post("/api/v1/status", json={}) | ||||
|     assert post_response.status == 400 | ||||
|     response_schema = ErrorSchema() | ||||
|  | ||||
|     response = await client.post("/api/v1/status", json={}) | ||||
|     assert response.status == 400 | ||||
|     assert not response_schema.validate(await response.json()) | ||||
|  | ||||
|  | ||||
| async def test_post_exception_inside(client: TestClient, mocker: MockerFixture) -> None: | ||||
| @ -69,6 +80,8 @@ async def test_post_exception_inside(client: TestClient, mocker: MockerFixture) | ||||
|     """ | ||||
|     payload = {"status": BuildStatusEnum.Success.value} | ||||
|     mocker.patch("ahriman.core.status.watcher.Watcher.update_self", side_effect=Exception()) | ||||
|     response_schema = ErrorSchema() | ||||
|  | ||||
|     post_response = await client.post("/api/v1/status", json=payload) | ||||
|     assert post_response.status == 500 | ||||
|     response = await client.post("/api/v1/status", json=payload) | ||||
|     assert response.status == 500 | ||||
|     assert not response_schema.validate(await response.json()) | ||||
|  | ||||
| @ -1,7 +1,9 @@ | ||||
| import pytest | ||||
|  | ||||
| from multidict import MultiDict | ||||
| from aiohttp.test_utils import TestClient | ||||
|  | ||||
| from ahriman.models.user_access import UserAccess | ||||
| from ahriman.web.views.base import BaseView | ||||
|  | ||||
|  | ||||
| @ -37,11 +39,17 @@ async def test_get_permission(base: BaseView) -> None: | ||||
|     """ | ||||
|     must search for permission attribute in class | ||||
|     """ | ||||
|     for method in ("DELETE", "GET", "POST"): | ||||
|         setattr(BaseView, f"{method.upper()}_PERMISSION", "permission") | ||||
|  | ||||
|     for method in ("DELETE", "GET", "HEAD", "POST"): | ||||
|         request = pytest.helpers.request(base.request.app, "", method) | ||||
|         setattr(BaseView, f"{method.upper()}_PERMISSION", "permission") | ||||
|         assert await base.get_permission(request) == "permission" | ||||
|  | ||||
|     for method in ("OPTIONS",): | ||||
|         request = pytest.helpers.request(base.request.app, "", method) | ||||
|         assert await base.get_permission(request) == UserAccess.Unauthorized | ||||
|  | ||||
|  | ||||
| def test_get_non_empty() -> None: | ||||
|     """ | ||||
| @ -61,35 +69,6 @@ def test_get_non_empty() -> None: | ||||
|         BaseView.get_non_empty(lambda k: [], "key") | ||||
|  | ||||
|  | ||||
| async def test_extract_data_json(base: BaseView) -> None: | ||||
|     """ | ||||
|     must parse and return json | ||||
|     """ | ||||
|     json = {"key1": "value1", "key2": "value2"} | ||||
|  | ||||
|     async def get_json(): | ||||
|         return json | ||||
|  | ||||
|     base._request = pytest.helpers.request(base.request.app, "", "", json=get_json) | ||||
|     assert await base.extract_data() == json | ||||
|  | ||||
|  | ||||
| async def test_extract_data_post(base: BaseView) -> None: | ||||
|     """ | ||||
|     must parse and return form data | ||||
|     """ | ||||
|     json = {"key1": "value1", "key2": "value2"} | ||||
|  | ||||
|     async def get_json(): | ||||
|         raise ValueError() | ||||
|  | ||||
|     async def get_data(): | ||||
|         return json | ||||
|  | ||||
|     base._request = pytest.helpers.request(base.request.app, "", "", json=get_json, data=get_data) | ||||
|     assert await base.extract_data() == json | ||||
|  | ||||
|  | ||||
| async def test_data_as_json(base: BaseView) -> None: | ||||
|     """ | ||||
|     must parse multi value form payload | ||||
| @ -121,3 +100,49 @@ async def test_data_as_json_with_list_keys(base: BaseView) -> None: | ||||
|  | ||||
|     base._request = pytest.helpers.request(base.request.app, "", "", data=get_data) | ||||
|     assert await base.data_as_json(["key1"]) == {"key1": ["value1"]} | ||||
|  | ||||
|  | ||||
| async def test_extract_data_json(base: BaseView) -> None: | ||||
|     """ | ||||
|     must parse and return json | ||||
|     """ | ||||
|     json = {"key1": "value1", "key2": "value2"} | ||||
|  | ||||
|     async def get_json(): | ||||
|         return json | ||||
|  | ||||
|     base._request = pytest.helpers.request(base.request.app, "", "", json=get_json) | ||||
|     assert await base.extract_data() == json | ||||
|  | ||||
|  | ||||
| async def test_extract_data_post(base: BaseView) -> None: | ||||
|     """ | ||||
|     must parse and return form data | ||||
|     """ | ||||
|     json = {"key1": "value1", "key2": "value2"} | ||||
|  | ||||
|     async def get_json(): | ||||
|         raise ValueError() | ||||
|  | ||||
|     async def get_data(): | ||||
|         return json | ||||
|  | ||||
|     base._request = pytest.helpers.request(base.request.app, "", "", json=get_json, data=get_data) | ||||
|     assert await base.extract_data() == json | ||||
|  | ||||
|  | ||||
| async def test_head(client: TestClient) -> None: | ||||
|     """ | ||||
|     must implement head as get method | ||||
|     """ | ||||
|     response = await client.head("/") | ||||
|     assert response.ok | ||||
|     assert await response.text() == "" | ||||
|  | ||||
|  | ||||
| async def test_head_not_allowed(client: TestClient) -> None: | ||||
|     """ | ||||
|     must raise MethodNotAllowed in case if no get method was implemented | ||||
|     """ | ||||
|     response = await client.head("/api/v1/service/add") | ||||
|     assert response.status == 405 | ||||
|  | ||||
| @ -10,7 +10,7 @@ async def test_get_permission() -> None: | ||||
|     """ | ||||
|     must return correct permission for the request | ||||
|     """ | ||||
|     for method in ("GET", "HEAD"): | ||||
|     for method in ("GET",): | ||||
|         request = pytest.helpers.request("", "", method) | ||||
|         assert await IndexView.get_permission(request) == UserAccess.Unauthorized | ||||
|  | ||||
|  | ||||
| @ -5,6 +5,9 @@ from pytest_mock import MockerFixture | ||||
|  | ||||
| from ahriman.models.user import User | ||||
| from ahriman.models.user_access import UserAccess | ||||
| from ahriman.web.schemas.error_schema import ErrorSchema | ||||
| from ahriman.web.schemas.login_schema import LoginSchema | ||||
| from ahriman.web.schemas.oauth2_schema import OAuth2Schema | ||||
| from ahriman.web.views.user.login import LoginView | ||||
|  | ||||
|  | ||||
| @ -21,8 +24,8 @@ async def test_get_default_validator(client_with_auth: TestClient) -> None: | ||||
|     """ | ||||
|     must return 405 in case if no OAuth enabled | ||||
|     """ | ||||
|     get_response = await client_with_auth.get("/api/v1/login") | ||||
|     assert get_response.status == 405 | ||||
|     response = await client_with_auth.get("/api/v1/login") | ||||
|     assert response.status == 405 | ||||
|  | ||||
|  | ||||
| async def test_get_redirect_to_oauth(client_with_oauth_auth: TestClient) -> None: | ||||
| @ -31,9 +34,12 @@ async def test_get_redirect_to_oauth(client_with_oauth_auth: TestClient) -> None | ||||
|     """ | ||||
|     oauth = client_with_oauth_auth.app["validator"] | ||||
|     oauth.get_oauth_url.return_value = "https://httpbin.org" | ||||
|     request_schema = OAuth2Schema() | ||||
|  | ||||
|     get_response = await client_with_oauth_auth.get("/api/v1/login") | ||||
|     assert get_response.ok | ||||
|     payload = {} | ||||
|     assert not request_schema.validate(payload) | ||||
|     response = await client_with_oauth_auth.get("/api/v1/login", params=payload) | ||||
|     assert response.ok | ||||
|     oauth.get_oauth_url.assert_called_once_with() | ||||
|  | ||||
|  | ||||
| @ -43,9 +49,12 @@ async def test_get_redirect_to_oauth_empty_code(client_with_oauth_auth: TestClie | ||||
|     """ | ||||
|     oauth = client_with_oauth_auth.app["validator"] | ||||
|     oauth.get_oauth_url.return_value = "https://httpbin.org" | ||||
|     request_schema = OAuth2Schema() | ||||
|  | ||||
|     get_response = await client_with_oauth_auth.get("/api/v1/login", params={"code": ""}) | ||||
|     assert get_response.ok | ||||
|     payload = {"code": ""} | ||||
|     assert not request_schema.validate(payload) | ||||
|     response = await client_with_oauth_auth.get("/api/v1/login", params=payload) | ||||
|     assert response.ok | ||||
|     oauth.get_oauth_url.assert_called_once_with() | ||||
|  | ||||
|  | ||||
| @ -59,10 +68,13 @@ async def test_get(client_with_oauth_auth: TestClient, mocker: MockerFixture) -> | ||||
|     oauth.enabled = False  # lol | ||||
|     oauth.max_age = 60 | ||||
|     remember_mock = mocker.patch("aiohttp_security.remember") | ||||
|     request_schema = OAuth2Schema() | ||||
|  | ||||
|     get_response = await client_with_oauth_auth.get("/api/v1/login", params={"code": "code"}) | ||||
|     payload = {"code": "code"} | ||||
|     assert not request_schema.validate(payload) | ||||
|     response = await client_with_oauth_auth.get("/api/v1/login", params=payload) | ||||
|  | ||||
|     assert get_response.ok | ||||
|     assert response.ok | ||||
|     oauth.get_oauth_username.assert_called_once_with("code") | ||||
|     oauth.known_username.assert_called_once_with("user") | ||||
|     remember_mock.assert_called_once_with( | ||||
| @ -77,10 +89,13 @@ async def test_get_unauthorized(client_with_oauth_auth: TestClient, mocker: Mock | ||||
|     oauth.known_username.return_value = False | ||||
|     oauth.max_age = 60 | ||||
|     remember_mock = mocker.patch("aiohttp_security.remember") | ||||
|     response_schema = ErrorSchema() | ||||
|  | ||||
|     get_response = await client_with_oauth_auth.get("/api/v1/login", params={"code": "code"}) | ||||
|     response = await client_with_oauth_auth.get( | ||||
|         "/api/v1/login", params={"code": "code"}, headers={"accept": "application/json"}) | ||||
|  | ||||
|     assert get_response.status == 401 | ||||
|     assert response.status == 401 | ||||
|     assert not response_schema.validate(await response.json()) | ||||
|     remember_mock.assert_not_called() | ||||
|  | ||||
|  | ||||
| @ -90,12 +105,15 @@ async def test_post(client_with_auth: TestClient, user: User, mocker: MockerFixt | ||||
|     """ | ||||
|     payload = {"username": user.username, "password": user.password} | ||||
|     remember_mock = mocker.patch("aiohttp_security.remember") | ||||
|     request_schema = LoginSchema() | ||||
|  | ||||
|     post_response = await client_with_auth.post("/api/v1/login", json=payload) | ||||
|     assert post_response.ok | ||||
|     assert not request_schema.validate(payload) | ||||
|  | ||||
|     post_response = await client_with_auth.post("/api/v1/login", data=payload) | ||||
|     assert post_response.ok | ||||
|     response = await client_with_auth.post("/api/v1/login", json=payload) | ||||
|     assert response.ok | ||||
|  | ||||
|     response = await client_with_auth.post("/api/v1/login", data=payload) | ||||
|     assert response.ok | ||||
|  | ||||
|     remember_mock.assert_called() | ||||
|  | ||||
| @ -104,18 +122,24 @@ async def test_post_skip(client: TestClient, user: User) -> None: | ||||
|     """ | ||||
|     must process if no auth configured | ||||
|     """ | ||||
|     request_schema = LoginSchema() | ||||
|  | ||||
|     payload = {"username": user.username, "password": user.password} | ||||
|     post_response = await client.post("/api/v1/login", json=payload) | ||||
|     assert post_response.ok | ||||
|     assert not request_schema.validate(payload) | ||||
|     response = await client.post("/api/v1/login", json=payload) | ||||
|     assert response.ok | ||||
|  | ||||
|  | ||||
| async def test_post_unauthorized(client_with_auth: TestClient, user: User, mocker: MockerFixture) -> None: | ||||
|     """ | ||||
|     must return unauthorized on invalid auth | ||||
|     """ | ||||
|     response_schema = ErrorSchema() | ||||
|  | ||||
|     payload = {"username": user.username, "password": ""} | ||||
|     remember_mock = mocker.patch("aiohttp_security.remember") | ||||
|  | ||||
|     post_response = await client_with_auth.post("/api/v1/login", json=payload) | ||||
|     assert post_response.status == 401 | ||||
|     response = await client_with_auth.post("/api/v1/login", json=payload, headers={"accept": "application/json"}) | ||||
|     assert response.status == 401 | ||||
|     assert not response_schema.validate(await response.json()) | ||||
|     remember_mock.assert_not_called() | ||||
|  | ||||
| @ -5,6 +5,7 @@ from aiohttp.web import HTTPUnauthorized | ||||
| from pytest_mock import MockerFixture | ||||
|  | ||||
| from ahriman.models.user_access import UserAccess | ||||
| from ahriman.web.schemas.error_schema import ErrorSchema | ||||
| from ahriman.web.views.user.logout import LogoutView | ||||
|  | ||||
|  | ||||
| @ -24,8 +25,8 @@ async def test_post(client_with_auth: TestClient, mocker: MockerFixture) -> None | ||||
|     mocker.patch("aiohttp_security.check_authorized") | ||||
|     forget_mock = mocker.patch("aiohttp_security.forget") | ||||
|  | ||||
|     post_response = await client_with_auth.post("/api/v1/logout") | ||||
|     assert post_response.ok | ||||
|     response = await client_with_auth.post("/api/v1/logout") | ||||
|     assert response.ok | ||||
|     forget_mock.assert_called_once_with(pytest.helpers.anyvar(int), pytest.helpers.anyvar(int)) | ||||
|  | ||||
|  | ||||
| @ -35,9 +36,11 @@ async def test_post_unauthorized(client_with_auth: TestClient, mocker: MockerFix | ||||
|     """ | ||||
|     mocker.patch("aiohttp_security.check_authorized", side_effect=HTTPUnauthorized()) | ||||
|     forget_mock = mocker.patch("aiohttp_security.forget") | ||||
|     response_schema = ErrorSchema() | ||||
|  | ||||
|     post_response = await client_with_auth.post("/api/v1/logout") | ||||
|     assert post_response.status == 401 | ||||
|     response = await client_with_auth.post("/api/v1/logout", headers={"accept": "application/json"}) | ||||
|     assert response.status == 401 | ||||
|     assert not response_schema.validate(await response.json()) | ||||
|     forget_mock.assert_not_called() | ||||
|  | ||||
|  | ||||
| @ -45,5 +48,5 @@ async def test_post_disabled(client: TestClient) -> None: | ||||
|     """ | ||||
|     must raise exception if auth is disabled | ||||
|     """ | ||||
|     post_response = await client.post("/api/v1/logout") | ||||
|     assert post_response.ok | ||||
|     response = await client.post("/api/v1/logout") | ||||
|     assert response.ok | ||||
|  | ||||
		Reference in New Issue
	
	Block a user