mirror of
https://github.com/arcan1s/ahriman.git
synced 2025-07-08 19:45:46 +00:00
Compare commits
1 Commits
master
...
feature/we
Author | SHA1 | Date | |
---|---|---|---|
c9e52f1b8a |
@ -5,6 +5,7 @@ After=network.target
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/ahriman web
|
||||
ExecReload=/usr/bin/ahriman web-reload
|
||||
User=ahriman
|
||||
Group=ahriman
|
||||
|
||||
|
67
src/ahriman/application/handlers/reload.py
Normal file
67
src/ahriman/application/handlers/reload.py
Normal file
@ -0,0 +1,67 @@
|
||||
#
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import argparse
|
||||
|
||||
from ahriman.application.application import Application
|
||||
from ahriman.application.handlers.handler import Handler, SubParserAction
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.models.repository_id import RepositoryId
|
||||
|
||||
|
||||
class Reload(Handler):
|
||||
"""
|
||||
web server handler
|
||||
"""
|
||||
|
||||
ALLOW_MULTI_ARCHITECTURE_RUN = False # system-wide action
|
||||
|
||||
@classmethod
|
||||
def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *,
|
||||
report: bool) -> None:
|
||||
"""
|
||||
callback for command line
|
||||
|
||||
Args:
|
||||
args(argparse.Namespace): command line args
|
||||
repository_id(RepositoryId): repository unique identifier
|
||||
configuration(Configuration): configuration instance
|
||||
report(bool): force enable or disable reporting
|
||||
"""
|
||||
application = Application(repository_id, configuration, report=True)
|
||||
client = application.repository.reporter
|
||||
client.configuration_reload()
|
||||
|
||||
@staticmethod
|
||||
def _set_reload_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
"""
|
||||
add parser for web reload subcommand
|
||||
|
||||
Args:
|
||||
root(SubParserAction): subparsers for the commands
|
||||
|
||||
Returns:
|
||||
argparse.ArgumentParser: created argument parser
|
||||
"""
|
||||
parser = root.add_parser("web-reload", help="reload configuration",
|
||||
description="reload web server configuration")
|
||||
parser.set_defaults(architecture="", lock=None, quiet=True, report=False, repository="", unsafe=True)
|
||||
return parser
|
||||
|
||||
arguments = [_set_reload_parser]
|
@ -267,8 +267,7 @@ class Pacman(LazyLogging):
|
||||
Package: list of packages which were returned by the query
|
||||
"""
|
||||
def is_package_provided(package: Package) -> bool:
|
||||
provides = [trim_package(name) for name in package.provides]
|
||||
return package_name in provides
|
||||
return package_name in package.provides
|
||||
|
||||
for database in self.handle.get_syncdbs():
|
||||
yield from filter(is_package_provided, database.search(package_name))
|
||||
|
@ -146,7 +146,7 @@ class AUR(Remote):
|
||||
# search api provides reduced models
|
||||
for stub in self.package_search(package_name, pacman=pacman, search_by="provides")
|
||||
# verity that found package actually provides it
|
||||
if package_name in (package := self.package_info(stub.name, pacman=pacman)).provides
|
||||
if package_name in (package := self.package_info(stub.package_base, pacman=pacman)).provides
|
||||
]
|
||||
|
||||
def package_search(self, *keywords: str, pacman: Pacman | None, search_by: str | None) -> list[AURPackage]:
|
||||
|
@ -203,8 +203,6 @@ def migrate_package_repository(connection: Connection, configuration: Configurat
|
||||
configuration(Configuration): configuration instance
|
||||
"""
|
||||
_, repository_id = configuration.check_loaded()
|
||||
if repository_id.is_empty:
|
||||
return # no repository available yet
|
||||
|
||||
connection.execute("""update build_queue set repository = :repository""", {"repository": repository_id.id})
|
||||
connection.execute("""update package_bases set repository = :repository""", {"repository": repository_id.id})
|
||||
|
@ -81,6 +81,11 @@ class Client:
|
||||
|
||||
return make_local_client()
|
||||
|
||||
def configuration_reload(self) -> None:
|
||||
"""
|
||||
reload configuration
|
||||
"""
|
||||
|
||||
def event_add(self, event: Event) -> None:
|
||||
"""
|
||||
create new event
|
||||
|
@ -17,6 +17,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# pylint: disable=too-many-public-methods
|
||||
import contextlib
|
||||
|
||||
from urllib.parse import quote_plus as url_encode
|
||||
@ -165,6 +166,13 @@ class WebClient(Client, SyncAhrimanClient):
|
||||
"""
|
||||
return f"{self.address}/api/v1/status"
|
||||
|
||||
def configuration_reload(self) -> None:
|
||||
"""
|
||||
reload configuration
|
||||
"""
|
||||
with contextlib.suppress(Exception):
|
||||
self.make_request("POST", f"{self.address}/api/v1/service/config")
|
||||
|
||||
def event_add(self, event: Event) -> None:
|
||||
"""
|
||||
create new event
|
||||
|
@ -25,7 +25,7 @@ from dataclasses import dataclass, field, fields
|
||||
from pyalpm import Package # type: ignore[import-not-found]
|
||||
from typing import Any, Self
|
||||
|
||||
from ahriman.core.utils import filter_json, full_version, trim_package
|
||||
from ahriman.core.utils import filter_json, full_version
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
@ -103,17 +103,6 @@ class AURPackage:
|
||||
keywords: list[str] = field(default_factory=list)
|
||||
groups: list[str] = field(default_factory=list)
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
"""
|
||||
update packages lists accordingly
|
||||
"""
|
||||
object.__setattr__(self, "depends", [trim_package(package) for package in self.depends])
|
||||
object.__setattr__(self, "make_depends", [trim_package(package) for package in self.make_depends])
|
||||
object.__setattr__(self, "opt_depends", [trim_package(package) for package in self.opt_depends])
|
||||
object.__setattr__(self, "check_depends", [trim_package(package) for package in self.check_depends])
|
||||
object.__setattr__(self, "conflicts", [trim_package(package) for package in self.conflicts])
|
||||
object.__setattr__(self, "provides", [trim_package(package) for package in self.provides])
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, dump: dict[str, Any]) -> Self:
|
||||
"""
|
||||
|
@ -83,13 +83,12 @@ class PackageDescription:
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
"""
|
||||
update packages lists accordingly
|
||||
update dependencies list accordingly
|
||||
"""
|
||||
self.depends = [trim_package(package) for package in self.depends]
|
||||
self.make_depends = [trim_package(package) for package in self.make_depends]
|
||||
self.opt_depends = [trim_package(package) for package in self.opt_depends]
|
||||
self.make_depends = [trim_package(package) for package in self.make_depends]
|
||||
self.check_depends = [trim_package(package) for package in self.check_depends]
|
||||
self.provides = [trim_package(package) for package in self.provides]
|
||||
|
||||
@property
|
||||
def filepath(self) -> Path | None:
|
||||
|
@ -22,6 +22,7 @@ from ahriman.web.schemas.aur_package_schema import AURPackageSchema
|
||||
from ahriman.web.schemas.auth_schema import AuthSchema
|
||||
from ahriman.web.schemas.build_options_schema import BuildOptionsSchema
|
||||
from ahriman.web.schemas.changes_schema import ChangesSchema
|
||||
from ahriman.web.schemas.configuration_schema import ConfigurationSchema
|
||||
from ahriman.web.schemas.counters_schema import CountersSchema
|
||||
from ahriman.web.schemas.dependencies_schema import DependenciesSchema
|
||||
from ahriman.web.schemas.error_schema import ErrorSchema
|
||||
|
39
src/ahriman/web/schemas/configuration_schema.py
Normal file
39
src/ahriman/web/schemas/configuration_schema.py
Normal file
@ -0,0 +1,39 @@
|
||||
#
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from ahriman.web.apispec import Schema, fields
|
||||
|
||||
|
||||
class ConfigurationSchema(Schema):
|
||||
"""
|
||||
response configuration schema
|
||||
"""
|
||||
|
||||
key = fields.String(required=True, metadata={
|
||||
"description": "Configuration key",
|
||||
"example": "host",
|
||||
})
|
||||
section = fields.String(required=True, metadata={
|
||||
"description": "Configuration section",
|
||||
"example": "web",
|
||||
})
|
||||
value = fields.String(required=True, metadata={
|
||||
"description": "Configuration value",
|
||||
"example": "127.0.0.1",
|
||||
})
|
84
src/ahriman/web/views/v1/service/config.py
Normal file
84
src/ahriman/web/views/v1/service/config.py
Normal file
@ -0,0 +1,84 @@
|
||||
#
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from aiohttp.web import HTTPNoContent, Response, json_response
|
||||
from typing import ClassVar
|
||||
|
||||
from ahriman.core.formatters import ConfigurationPrinter
|
||||
from ahriman.models.user_access import UserAccess
|
||||
from ahriman.web.apispec.decorators import apidocs
|
||||
from ahriman.web.schemas import ConfigurationSchema
|
||||
from ahriman.web.views.base import BaseView
|
||||
|
||||
|
||||
class ConfigView(BaseView):
|
||||
"""
|
||||
configuration control view
|
||||
|
||||
Attributes:
|
||||
GET_PERMISSION(UserAccess): (class attribute) get permissions of self
|
||||
POST_PERMISSION(UserAccess): (class attribute) post permissions of self
|
||||
"""
|
||||
|
||||
GET_PERMISSION = POST_PERMISSION = UserAccess.Full # type: ClassVar[UserAccess]
|
||||
ROUTES = ["/api/v1/service/config"]
|
||||
|
||||
@apidocs(
|
||||
tags=["Actions"],
|
||||
summary="Get configuration",
|
||||
description="Get current web service configuration as nested dictionary",
|
||||
permission=GET_PERMISSION,
|
||||
schema=ConfigurationSchema(many=True),
|
||||
)
|
||||
async def get(self) -> Response:
|
||||
"""
|
||||
get current web service configuration
|
||||
|
||||
Returns:
|
||||
Response: current web service configuration as nested dictionary
|
||||
"""
|
||||
dump = self.configuration.dump()
|
||||
|
||||
response = [
|
||||
{
|
||||
"section": section,
|
||||
"key": key,
|
||||
"value": value,
|
||||
} for section, values in dump.items()
|
||||
for key, value in values.items()
|
||||
if key not in ConfigurationPrinter.HIDE_KEYS
|
||||
]
|
||||
return json_response(response)
|
||||
|
||||
@apidocs(
|
||||
tags=["Actions"],
|
||||
summary="Reload configuration",
|
||||
description="Reload configuration from current files",
|
||||
permission=POST_PERMISSION,
|
||||
)
|
||||
async def post(self) -> None:
|
||||
"""
|
||||
reload web service configuration
|
||||
|
||||
Raises:
|
||||
HTTPNoContent: on success response
|
||||
"""
|
||||
self.configuration.reload()
|
||||
|
||||
raise HTTPNoContent
|
27
tests/ahriman/application/handlers/test_handler_reload.py
Normal file
27
tests/ahriman/application/handlers/test_handler_reload.py
Normal file
@ -0,0 +1,27 @@
|
||||
import argparse
|
||||
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from ahriman.application.handlers.reload import Reload
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.repository import Repository
|
||||
|
||||
|
||||
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
|
||||
mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must run command
|
||||
"""
|
||||
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
|
||||
run_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.configuration_reload")
|
||||
|
||||
_, repository_id = configuration.check_loaded()
|
||||
Reload.run(args, repository_id, configuration, report=False)
|
||||
run_mock.assert_called_once_with()
|
||||
|
||||
|
||||
def test_disallow_multi_architecture_run() -> None:
|
||||
"""
|
||||
must not allow multi architecture run
|
||||
"""
|
||||
assert not Reload.ALLOW_MULTI_ARCHITECTURE_RUN
|
@ -1563,6 +1563,35 @@ def test_subparsers_web_option_repository(parser: argparse.ArgumentParser) -> No
|
||||
assert args.repository == ""
|
||||
|
||||
|
||||
def test_subparsers_web_reload(parser: argparse.ArgumentParser) -> None:
|
||||
"""
|
||||
web-reload command must imply architecture, lock, quiet, report, repository and unsafe
|
||||
"""
|
||||
args = parser.parse_args(["web-reload"])
|
||||
assert args.architecture == ""
|
||||
assert args.lock is None
|
||||
assert args.quiet
|
||||
assert not args.report
|
||||
assert args.repository == ""
|
||||
assert args.unsafe
|
||||
|
||||
|
||||
def test_subparsers_web_reload_option_architecture(parser: argparse.ArgumentParser) -> None:
|
||||
"""
|
||||
web-reload command must correctly parse architecture list
|
||||
"""
|
||||
args = parser.parse_args(["-a", "x86_64", "web-reload"])
|
||||
assert args.architecture == ""
|
||||
|
||||
|
||||
def test_subparsers_web_reload_option_repository(parser: argparse.ArgumentParser) -> None:
|
||||
"""
|
||||
web-reload command must correctly parse repository list
|
||||
"""
|
||||
args = parser.parse_args(["-r", "repo", "web-reload"])
|
||||
assert args.repository == ""
|
||||
|
||||
|
||||
def test_run(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
application must be run
|
||||
|
@ -289,4 +289,3 @@ def test_package_provided_by(pacman: Pacman) -> None:
|
||||
must search through the provides lists
|
||||
"""
|
||||
assert list(pacman.provided_by("sh"))
|
||||
assert list(pacman.provided_by("libacl.so")) # case with exact version
|
||||
|
@ -6,7 +6,6 @@ from unittest.mock import call as MockCall
|
||||
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.database.migrations.m011_repository_name import migrate_data, migrate_package_repository, steps
|
||||
from ahriman.models.repository_id import RepositoryId
|
||||
|
||||
|
||||
def test_migration_repository_name() -> None:
|
||||
@ -38,13 +37,3 @@ def test_migrate_package_repository(connection: Connection, configuration: Confi
|
||||
MockCall(pytest.helpers.anyvar(str, strict=True), {"repository": configuration.repository_id.id}),
|
||||
MockCall(pytest.helpers.anyvar(str, strict=True), {"repository": configuration.repository_id.id}),
|
||||
])
|
||||
|
||||
|
||||
def test_migrate_package_repository_empty_id(connection: Connection, configuration: Configuration,
|
||||
mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must do nothing on empty repository id
|
||||
"""
|
||||
mocker.patch("ahriman.core.configuration.Configuration.check_loaded", return_value=("", RepositoryId("", "")))
|
||||
migrate_package_repository(connection, configuration)
|
||||
connection.execute.assert_not_called()
|
||||
|
@ -97,6 +97,13 @@ def test_load_web_client_from_legacy_unix_socket(configuration: Configuration, d
|
||||
assert isinstance(Client.load(repository_id, configuration, database, report=True), WebClient)
|
||||
|
||||
|
||||
def test_configuration_reload(client: Client) -> None:
|
||||
"""
|
||||
must do nothing on configuration reload
|
||||
"""
|
||||
client.configuration_reload()
|
||||
|
||||
|
||||
def test_event_add(client: Client) -> None:
|
||||
"""
|
||||
must raise not implemented on event insertion
|
||||
|
@ -107,6 +107,55 @@ def test_status_url(web_client: WebClient) -> None:
|
||||
assert web_client._status_url().endswith("/api/v1/status")
|
||||
|
||||
|
||||
def test_configuration_reload(web_client: WebClient, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must reload configuration
|
||||
"""
|
||||
requests_mock = mocker.patch("ahriman.core.status.web_client.WebClient.make_request")
|
||||
web_client.configuration_reload()
|
||||
requests_mock.assert_called_once_with("POST", pytest.helpers.anyvar(str, True))
|
||||
|
||||
|
||||
def test_configuration_reload_failed(web_client: WebClient, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must suppress any exception happened during configuration reload
|
||||
"""
|
||||
mocker.patch("requests.Session.request", side_effect=Exception())
|
||||
web_client.configuration_reload()
|
||||
|
||||
|
||||
def test_configuration_reload_failed_http_error(web_client: WebClient, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must suppress HTTP exception happened during configuration reload
|
||||
"""
|
||||
mocker.patch("requests.Session.request", side_effect=requests.HTTPError())
|
||||
web_client.configuration_reload()
|
||||
|
||||
|
||||
def test_configuration_reload_failed_suppress(web_client: WebClient, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must suppress any exception happened during configuration reload and don't log
|
||||
"""
|
||||
web_client.suppress_errors = True
|
||||
mocker.patch("requests.Session.request", side_effect=Exception())
|
||||
logging_mock = mocker.patch("logging.exception")
|
||||
|
||||
web_client.configuration_reload()
|
||||
logging_mock.assert_not_called()
|
||||
|
||||
|
||||
def test_configuration_reload_failed_http_error_suppress(web_client: WebClient, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must suppress HTTP exception happened during configuration reload and don't log
|
||||
"""
|
||||
web_client.suppress_errors = True
|
||||
mocker.patch("requests.Session.request", side_effect=requests.HTTPError())
|
||||
logging_mock = mocker.patch("logging.exception")
|
||||
|
||||
web_client.configuration_reload()
|
||||
logging_mock.assert_not_called()
|
||||
|
||||
|
||||
def test_event_add(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must create event
|
||||
|
@ -2,7 +2,7 @@ import datetime
|
||||
import json
|
||||
import pyalpm # typing: ignore
|
||||
|
||||
from dataclasses import asdict, fields, replace
|
||||
from dataclasses import asdict, fields
|
||||
from pathlib import Path
|
||||
from pytest_mock import MockerFixture
|
||||
from typing import Any
|
||||
@ -38,25 +38,6 @@ def _get_official_data(resource_path_root: Path) -> dict[str, Any]:
|
||||
return json.loads(response)["results"][0]
|
||||
|
||||
|
||||
def test_post_init(aur_package_ahriman: AURPackage) -> None:
|
||||
"""
|
||||
must trim versions and descriptions from packages list
|
||||
"""
|
||||
package = replace(
|
||||
aur_package_ahriman,
|
||||
depends=["a=1"],
|
||||
make_depends=["b>=3"],
|
||||
opt_depends=["c: a description"],
|
||||
check_depends=["d=4"],
|
||||
provides=["e=5"],
|
||||
)
|
||||
assert package.depends == ["a"]
|
||||
assert package.make_depends == ["b"]
|
||||
assert package.opt_depends == ["c"]
|
||||
assert package.check_depends == ["d"]
|
||||
assert package.provides == ["e"]
|
||||
|
||||
|
||||
def test_from_json(aur_package_ahriman: AURPackage, resource_path_root: Path) -> None:
|
||||
"""
|
||||
must load package from json
|
||||
|
@ -6,15 +6,10 @@ from ahriman.models.package_description import PackageDescription
|
||||
|
||||
def test_post_init() -> None:
|
||||
"""
|
||||
must trim versions and descriptions from packages list
|
||||
must trim versions and descriptions from dependencies list
|
||||
"""
|
||||
assert PackageDescription(
|
||||
depends=["a=1"],
|
||||
make_depends=["b>=3"],
|
||||
opt_depends=["c: a description"],
|
||||
check_depends=["d=4"],
|
||||
provides=["e=5"]
|
||||
) == PackageDescription(depends=["a"], make_depends=["b"], opt_depends=["c"], check_depends=["d"], provides=["e"])
|
||||
assert PackageDescription(depends=["a=1"], make_depends=["b>=3"], opt_depends=["c: a description"]) == \
|
||||
PackageDescription(depends=["a"], make_depends=["b"], opt_depends=["c"])
|
||||
|
||||
|
||||
def test_filepath(package_description_ahriman: PackageDescription) -> None:
|
||||
|
1
tests/ahriman/web/schemas/test_configuration_schema.py
Normal file
1
tests/ahriman/web/schemas/test_configuration_schema.py
Normal file
@ -0,0 +1 @@
|
||||
# schema testing goes in view class tests
|
@ -0,0 +1,54 @@
|
||||
import pytest
|
||||
|
||||
from aiohttp.test_utils import TestClient
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from ahriman.core.formatters.configuration_printer import ConfigurationPrinter
|
||||
from ahriman.models.user_access import UserAccess
|
||||
from ahriman.web.views.v1.service.config import ConfigView
|
||||
|
||||
|
||||
async def test_get_permission() -> None:
|
||||
"""
|
||||
must return correct permission for the request
|
||||
"""
|
||||
for method in ("GET",):
|
||||
request = pytest.helpers.request("", "", method)
|
||||
assert await ConfigView.get_permission(request) == UserAccess.Full
|
||||
for method in ("POST",):
|
||||
request = pytest.helpers.request("", "", method)
|
||||
assert await ConfigView.get_permission(request) == UserAccess.Full
|
||||
|
||||
|
||||
def test_routes() -> None:
|
||||
"""
|
||||
must return correct routes
|
||||
"""
|
||||
assert ConfigView.ROUTES == ["/api/v1/service/config"]
|
||||
|
||||
|
||||
async def test_get(client: TestClient) -> None:
|
||||
"""
|
||||
must get web configuration
|
||||
"""
|
||||
response_schema = pytest.helpers.schema_response(ConfigView.get)
|
||||
|
||||
response = await client.get("/api/v1/service/config")
|
||||
assert response.status == 200
|
||||
json = await response.json()
|
||||
assert json # check that it is not empty
|
||||
assert not response_schema.validate(json)
|
||||
|
||||
# check that there are no keys which have to be hidden
|
||||
assert not any(value["key"] in ConfigurationPrinter.HIDE_KEYS for value in json)
|
||||
|
||||
|
||||
async def test_post(client: TestClient, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must update package changes
|
||||
"""
|
||||
reload_mock = mocker.patch("ahriman.core.configuration.Configuration.reload")
|
||||
|
||||
response = await client.post("/api/v1/service/config")
|
||||
assert response.status == 204
|
||||
reload_mock.assert_called_once_with()
|
Reference in New Issue
Block a user