mirror of
https://github.com/arcan1s/ahriman.git
synced 2025-07-10 04:25:47 +00:00
Compare commits
2 Commits
feature/we
...
master
Author | SHA1 | Date | |
---|---|---|---|
435375721d | |||
4c5caba6b7 |
@ -5,7 +5,6 @@ After=network.target
|
|||||||
[Service]
|
[Service]
|
||||||
Type=simple
|
Type=simple
|
||||||
ExecStart=/usr/bin/ahriman web
|
ExecStart=/usr/bin/ahriman web
|
||||||
ExecReload=/usr/bin/ahriman web-reload
|
|
||||||
User=ahriman
|
User=ahriman
|
||||||
Group=ahriman
|
Group=ahriman
|
||||||
|
|
||||||
|
@ -1,67 +0,0 @@
|
|||||||
#
|
|
||||||
# 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,7 +267,8 @@ class Pacman(LazyLogging):
|
|||||||
Package: list of packages which were returned by the query
|
Package: list of packages which were returned by the query
|
||||||
"""
|
"""
|
||||||
def is_package_provided(package: Package) -> bool:
|
def is_package_provided(package: Package) -> bool:
|
||||||
return package_name in package.provides
|
provides = [trim_package(name) for name in package.provides]
|
||||||
|
return package_name in provides
|
||||||
|
|
||||||
for database in self.handle.get_syncdbs():
|
for database in self.handle.get_syncdbs():
|
||||||
yield from filter(is_package_provided, database.search(package_name))
|
yield from filter(is_package_provided, database.search(package_name))
|
||||||
|
@ -146,7 +146,7 @@ class AUR(Remote):
|
|||||||
# search api provides reduced models
|
# search api provides reduced models
|
||||||
for stub in self.package_search(package_name, pacman=pacman, search_by="provides")
|
for stub in self.package_search(package_name, pacman=pacman, search_by="provides")
|
||||||
# verity that found package actually provides it
|
# verity that found package actually provides it
|
||||||
if package_name in (package := self.package_info(stub.package_base, pacman=pacman)).provides
|
if package_name in (package := self.package_info(stub.name, pacman=pacman)).provides
|
||||||
]
|
]
|
||||||
|
|
||||||
def package_search(self, *keywords: str, pacman: Pacman | None, search_by: str | None) -> list[AURPackage]:
|
def package_search(self, *keywords: str, pacman: Pacman | None, search_by: str | None) -> list[AURPackage]:
|
||||||
|
@ -203,6 +203,8 @@ def migrate_package_repository(connection: Connection, configuration: Configurat
|
|||||||
configuration(Configuration): configuration instance
|
configuration(Configuration): configuration instance
|
||||||
"""
|
"""
|
||||||
_, repository_id = configuration.check_loaded()
|
_, 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 build_queue set repository = :repository""", {"repository": repository_id.id})
|
||||||
connection.execute("""update package_bases set repository = :repository""", {"repository": repository_id.id})
|
connection.execute("""update package_bases set repository = :repository""", {"repository": repository_id.id})
|
||||||
|
@ -81,11 +81,6 @@ class Client:
|
|||||||
|
|
||||||
return make_local_client()
|
return make_local_client()
|
||||||
|
|
||||||
def configuration_reload(self) -> None:
|
|
||||||
"""
|
|
||||||
reload configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
def event_add(self, event: Event) -> None:
|
def event_add(self, event: Event) -> None:
|
||||||
"""
|
"""
|
||||||
create new event
|
create new event
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
# pylint: disable=too-many-public-methods
|
|
||||||
import contextlib
|
import contextlib
|
||||||
|
|
||||||
from urllib.parse import quote_plus as url_encode
|
from urllib.parse import quote_plus as url_encode
|
||||||
@ -166,13 +165,6 @@ class WebClient(Client, SyncAhrimanClient):
|
|||||||
"""
|
"""
|
||||||
return f"{self.address}/api/v1/status"
|
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:
|
def event_add(self, event: Event) -> None:
|
||||||
"""
|
"""
|
||||||
create new event
|
create new event
|
||||||
|
@ -25,7 +25,7 @@ from dataclasses import dataclass, field, fields
|
|||||||
from pyalpm import Package # type: ignore[import-not-found]
|
from pyalpm import Package # type: ignore[import-not-found]
|
||||||
from typing import Any, Self
|
from typing import Any, Self
|
||||||
|
|
||||||
from ahriman.core.utils import filter_json, full_version
|
from ahriman.core.utils import filter_json, full_version, trim_package
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True, kw_only=True)
|
@dataclass(frozen=True, kw_only=True)
|
||||||
@ -103,6 +103,17 @@ class AURPackage:
|
|||||||
keywords: list[str] = field(default_factory=list)
|
keywords: list[str] = field(default_factory=list)
|
||||||
groups: 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
|
@classmethod
|
||||||
def from_json(cls, dump: dict[str, Any]) -> Self:
|
def from_json(cls, dump: dict[str, Any]) -> Self:
|
||||||
"""
|
"""
|
||||||
|
@ -83,12 +83,13 @@ class PackageDescription:
|
|||||||
|
|
||||||
def __post_init__(self) -> None:
|
def __post_init__(self) -> None:
|
||||||
"""
|
"""
|
||||||
update dependencies list accordingly
|
update packages lists accordingly
|
||||||
"""
|
"""
|
||||||
self.depends = [trim_package(package) for package in self.depends]
|
self.depends = [trim_package(package) for package in self.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.make_depends = [trim_package(package) for package in self.make_depends]
|
||||||
|
self.opt_depends = [trim_package(package) for package in self.opt_depends]
|
||||||
self.check_depends = [trim_package(package) for package in self.check_depends]
|
self.check_depends = [trim_package(package) for package in self.check_depends]
|
||||||
|
self.provides = [trim_package(package) for package in self.provides]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def filepath(self) -> Path | None:
|
def filepath(self) -> Path | None:
|
||||||
|
@ -22,7 +22,6 @@ from ahriman.web.schemas.aur_package_schema import AURPackageSchema
|
|||||||
from ahriman.web.schemas.auth_schema import AuthSchema
|
from ahriman.web.schemas.auth_schema import AuthSchema
|
||||||
from ahriman.web.schemas.build_options_schema import BuildOptionsSchema
|
from ahriman.web.schemas.build_options_schema import BuildOptionsSchema
|
||||||
from ahriman.web.schemas.changes_schema import ChangesSchema
|
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.counters_schema import CountersSchema
|
||||||
from ahriman.web.schemas.dependencies_schema import DependenciesSchema
|
from ahriman.web.schemas.dependencies_schema import DependenciesSchema
|
||||||
from ahriman.web.schemas.error_schema import ErrorSchema
|
from ahriman.web.schemas.error_schema import ErrorSchema
|
||||||
|
@ -1,39 +0,0 @@
|
|||||||
#
|
|
||||||
# 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",
|
|
||||||
})
|
|
@ -1,84 +0,0 @@
|
|||||||
#
|
|
||||||
# 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
|
|
@ -1,27 +0,0 @@
|
|||||||
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,35 +1563,6 @@ def test_subparsers_web_option_repository(parser: argparse.ArgumentParser) -> No
|
|||||||
assert args.repository == ""
|
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:
|
def test_run(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
application must be run
|
application must be run
|
||||||
|
@ -289,3 +289,4 @@ def test_package_provided_by(pacman: Pacman) -> None:
|
|||||||
must search through the provides lists
|
must search through the provides lists
|
||||||
"""
|
"""
|
||||||
assert list(pacman.provided_by("sh"))
|
assert list(pacman.provided_by("sh"))
|
||||||
|
assert list(pacman.provided_by("libacl.so")) # case with exact version
|
||||||
|
@ -6,6 +6,7 @@ from unittest.mock import call as MockCall
|
|||||||
|
|
||||||
from ahriman.core.configuration import Configuration
|
from ahriman.core.configuration import Configuration
|
||||||
from ahriman.core.database.migrations.m011_repository_name import migrate_data, migrate_package_repository, steps
|
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:
|
def test_migration_repository_name() -> None:
|
||||||
@ -37,3 +38,13 @@ 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}),
|
||||||
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,13 +97,6 @@ def test_load_web_client_from_legacy_unix_socket(configuration: Configuration, d
|
|||||||
assert isinstance(Client.load(repository_id, configuration, database, report=True), WebClient)
|
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:
|
def test_event_add(client: Client) -> None:
|
||||||
"""
|
"""
|
||||||
must raise not implemented on event insertion
|
must raise not implemented on event insertion
|
||||||
|
@ -107,55 +107,6 @@ def test_status_url(web_client: WebClient) -> None:
|
|||||||
assert web_client._status_url().endswith("/api/v1/status")
|
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:
|
def test_event_add(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must create event
|
must create event
|
||||||
|
@ -2,7 +2,7 @@ import datetime
|
|||||||
import json
|
import json
|
||||||
import pyalpm # typing: ignore
|
import pyalpm # typing: ignore
|
||||||
|
|
||||||
from dataclasses import asdict, fields
|
from dataclasses import asdict, fields, replace
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from pytest_mock import MockerFixture
|
from pytest_mock import MockerFixture
|
||||||
from typing import Any
|
from typing import Any
|
||||||
@ -38,6 +38,25 @@ def _get_official_data(resource_path_root: Path) -> dict[str, Any]:
|
|||||||
return json.loads(response)["results"][0]
|
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:
|
def test_from_json(aur_package_ahriman: AURPackage, resource_path_root: Path) -> None:
|
||||||
"""
|
"""
|
||||||
must load package from json
|
must load package from json
|
||||||
|
@ -6,10 +6,15 @@ from ahriman.models.package_description import PackageDescription
|
|||||||
|
|
||||||
def test_post_init() -> None:
|
def test_post_init() -> None:
|
||||||
"""
|
"""
|
||||||
must trim versions and descriptions from dependencies list
|
must trim versions and descriptions from packages list
|
||||||
"""
|
"""
|
||||||
assert PackageDescription(depends=["a=1"], make_depends=["b>=3"], opt_depends=["c: a description"]) == \
|
assert PackageDescription(
|
||||||
PackageDescription(depends=["a"], make_depends=["b"], opt_depends=["c"])
|
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"])
|
||||||
|
|
||||||
|
|
||||||
def test_filepath(package_description_ahriman: PackageDescription) -> None:
|
def test_filepath(package_description_ahriman: PackageDescription) -> None:
|
||||||
|
@ -1 +0,0 @@
|
|||||||
# schema testing goes in view class tests
|
|
@ -1,54 +0,0 @@
|
|||||||
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