mirror of
https://github.com/arcan1s/ahriman.git
synced 2025-06-28 06:41:43 +00:00
Add ability to trigger updates from the web (#31)
* add external process spawner and update test cases * pass no_report to handlers * provide service api endpoints * do not spawn process for single architecture run * pass no report to handlers * make _call method of handlers public and also simplify process spawn * move update under add * implement actions from web page * clear logging & improve l&f
This commit is contained in:
@ -1,5 +1,4 @@
|
||||
import argparse
|
||||
import aur
|
||||
import pytest
|
||||
|
||||
from pytest_mock import MockerFixture
|
||||
@ -8,7 +7,6 @@ from ahriman.application.ahriman import _parser
|
||||
from ahriman.application.application import Application
|
||||
from ahriman.application.lock import Lock
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.models.package import Package
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@ -20,7 +18,7 @@ def application(configuration: Configuration, mocker: MockerFixture) -> Applicat
|
||||
:return: application test instance
|
||||
"""
|
||||
mocker.patch("pathlib.Path.mkdir")
|
||||
return Application("x86_64", configuration)
|
||||
return Application("x86_64", configuration, no_report=True)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@ -32,31 +30,6 @@ def args() -> argparse.Namespace:
|
||||
return argparse.Namespace(lock=None, force=False, unsafe=False, no_report=True)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def aur_package_ahriman(package_ahriman: Package) -> aur.Package:
|
||||
"""
|
||||
fixture for AUR package
|
||||
:param package_ahriman: package fixture
|
||||
:return: AUR package test instance
|
||||
"""
|
||||
return aur.Package(
|
||||
num_votes=None,
|
||||
description=package_ahriman.packages[package_ahriman.base].description,
|
||||
url_path=package_ahriman.web_url,
|
||||
last_modified=None,
|
||||
name=package_ahriman.base,
|
||||
out_of_date=None,
|
||||
id=None,
|
||||
first_submitted=None,
|
||||
maintainer=None,
|
||||
version=package_ahriman.version,
|
||||
license=package_ahriman.packages[package_ahriman.base].licenses,
|
||||
url=None,
|
||||
package_base=package_ahriman.base,
|
||||
package_base_id=None,
|
||||
category_id=None)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def lock(args: argparse.Namespace, configuration: Configuration) -> Lock:
|
||||
"""
|
||||
|
@ -6,7 +6,7 @@ from pytest_mock import MockerFixture
|
||||
|
||||
from ahriman.application.handlers import Handler
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.exceptions import MissingArchitecture
|
||||
from ahriman.core.exceptions import MissingArchitecture, MultipleArchitecture
|
||||
|
||||
|
||||
def test_call(args: argparse.Namespace, mocker: MockerFixture) -> None:
|
||||
@ -20,7 +20,7 @@ def test_call(args: argparse.Namespace, mocker: MockerFixture) -> None:
|
||||
enter_mock = mocker.patch("ahriman.application.lock.Lock.__enter__")
|
||||
exit_mock = mocker.patch("ahriman.application.lock.Lock.__exit__")
|
||||
|
||||
assert Handler._call(args, "x86_64")
|
||||
assert Handler.call(args, "x86_64")
|
||||
enter_mock.assert_called_once()
|
||||
exit_mock.assert_called_once()
|
||||
|
||||
@ -30,7 +30,7 @@ def test_call_exception(args: argparse.Namespace, mocker: MockerFixture) -> None
|
||||
must process exception
|
||||
"""
|
||||
mocker.patch("ahriman.application.lock.Lock.__enter__", side_effect=Exception())
|
||||
assert not Handler._call(args, "x86_64")
|
||||
assert not Handler.call(args, "x86_64")
|
||||
|
||||
|
||||
def test_execute(args: argparse.Namespace, mocker: MockerFixture) -> None:
|
||||
@ -44,6 +44,29 @@ def test_execute(args: argparse.Namespace, mocker: MockerFixture) -> None:
|
||||
starmap_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_execute_multiple_not_supported(args: argparse.Namespace, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must raise an exception if multiple architectures are not supported by the handler
|
||||
"""
|
||||
args.architecture = ["i686", "x86_64"]
|
||||
args.command = "web"
|
||||
mocker.patch.object(Handler, "ALLOW_MULTI_ARCHITECTURE_RUN", False)
|
||||
|
||||
with pytest.raises(MultipleArchitecture):
|
||||
Handler.execute(args)
|
||||
|
||||
|
||||
def test_execute_single(args: argparse.Namespace, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must run execution in current process if only one architecture supplied
|
||||
"""
|
||||
args.architecture = ["x86_64"]
|
||||
starmap_mock = mocker.patch("multiprocessing.pool.Pool.starmap")
|
||||
|
||||
Handler.execute(args)
|
||||
starmap_mock.assert_not_called()
|
||||
|
||||
|
||||
def test_extract_architectures(args: argparse.Namespace, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must generate list of available architectures
|
||||
@ -94,4 +117,4 @@ def test_run(args: argparse.Namespace, configuration: Configuration) -> None:
|
||||
must raise NotImplemented for missing method
|
||||
"""
|
||||
with pytest.raises(NotImplementedError):
|
||||
Handler.run(args, "x86_64", configuration)
|
||||
Handler.run(args, "x86_64", configuration, True)
|
||||
|
@ -26,7 +26,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
|
||||
mocker.patch("pathlib.Path.mkdir")
|
||||
application_mock = mocker.patch("ahriman.application.application.Application.add")
|
||||
|
||||
Add.run(args, "x86_64", configuration)
|
||||
Add.run(args, "x86_64", configuration, True)
|
||||
application_mock.assert_called_once()
|
||||
|
||||
|
||||
@ -41,6 +41,6 @@ def test_run_with_updates(args: argparse.Namespace, configuration: Configuration
|
||||
application_mock = mocker.patch("ahriman.application.application.Application.update")
|
||||
updates_mock = mocker.patch("ahriman.application.application.Application.get_updates")
|
||||
|
||||
Add.run(args, "x86_64", configuration)
|
||||
Add.run(args, "x86_64", configuration, True)
|
||||
application_mock.assert_called_once()
|
||||
updates_mock.assert_called_once()
|
||||
|
@ -28,5 +28,5 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
|
||||
mocker.patch("pathlib.Path.mkdir")
|
||||
application_mock = mocker.patch("ahriman.application.application.Application.clean")
|
||||
|
||||
Clean.run(args, "x86_64", configuration)
|
||||
Clean.run(args, "x86_64", configuration, True)
|
||||
application_mock.assert_called_once()
|
||||
|
@ -19,7 +19,7 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
|
||||
"""
|
||||
args.username = "user"
|
||||
args.password = "pa55w0rd"
|
||||
args.role = UserAccess.Status
|
||||
args.role = UserAccess.Read
|
||||
args.as_service = False
|
||||
return args
|
||||
|
||||
@ -34,7 +34,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
|
||||
create_user = mocker.patch("ahriman.application.handlers.CreateUser.create_user")
|
||||
get_salt_mock = mocker.patch("ahriman.application.handlers.CreateUser.get_salt")
|
||||
|
||||
CreateUser.run(args, "x86_64", configuration)
|
||||
CreateUser.run(args, "x86_64", configuration, True)
|
||||
get_auth_configuration_mock.assert_called_once()
|
||||
create_configuration_mock.assert_called_once()
|
||||
create_user.assert_called_once()
|
||||
|
@ -15,6 +15,6 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
|
||||
application_mock = mocker.patch("ahriman.core.configuration.Configuration.dump",
|
||||
return_value=configuration.dump())
|
||||
|
||||
Dump.run(args, "x86_64", configuration)
|
||||
Dump.run(args, "x86_64", configuration, True)
|
||||
application_mock.assert_called_once()
|
||||
print_mock.assert_called()
|
||||
|
@ -13,6 +13,6 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
|
||||
create_tree_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.create_tree")
|
||||
init_mock = mocker.patch("ahriman.core.alpm.repo.Repo.init")
|
||||
|
||||
Init.run(args, "x86_64", configuration)
|
||||
Init.run(args, "x86_64", configuration, True)
|
||||
create_tree_mock.assert_called_once()
|
||||
init_mock.assert_called_once()
|
||||
|
@ -25,5 +25,5 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
|
||||
mocker.patch("pathlib.Path.mkdir")
|
||||
application_mock = mocker.patch("ahriman.core.sign.gpg.GPG.import_key")
|
||||
|
||||
KeyImport.run(args, "x86_64", configuration)
|
||||
KeyImport.run(args, "x86_64", configuration, True)
|
||||
application_mock.assert_called_once()
|
||||
|
@ -26,7 +26,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
|
||||
application_packages_mock = mocker.patch("ahriman.core.repository.repository.Repository.packages")
|
||||
application_mock = mocker.patch("ahriman.application.application.Application.update")
|
||||
|
||||
Rebuild.run(args, "x86_64", configuration)
|
||||
Rebuild.run(args, "x86_64", configuration, True)
|
||||
application_packages_mock.assert_called_once()
|
||||
application_mock.assert_called_once()
|
||||
|
||||
@ -44,7 +44,7 @@ def test_run_filter(args: argparse.Namespace, configuration: Configuration,
|
||||
return_value=[package_ahriman, package_python_schedule])
|
||||
application_mock = mocker.patch("ahriman.application.application.Application.update")
|
||||
|
||||
Rebuild.run(args, "x86_64", configuration)
|
||||
Rebuild.run(args, "x86_64", configuration, True)
|
||||
application_mock.assert_called_with([package_ahriman])
|
||||
|
||||
|
||||
@ -60,5 +60,5 @@ def test_run_without_filter(args: argparse.Namespace, configuration: Configurati
|
||||
return_value=[package_ahriman, package_python_schedule])
|
||||
application_mock = mocker.patch("ahriman.application.application.Application.update")
|
||||
|
||||
Rebuild.run(args, "x86_64", configuration)
|
||||
Rebuild.run(args, "x86_64", configuration, True)
|
||||
application_mock.assert_called_with([package_ahriman, package_python_schedule])
|
||||
|
@ -24,5 +24,5 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
|
||||
mocker.patch("pathlib.Path.mkdir")
|
||||
application_mock = mocker.patch("ahriman.application.application.Application.remove")
|
||||
|
||||
Remove.run(args, "x86_64", configuration)
|
||||
Remove.run(args, "x86_64", configuration, True)
|
||||
application_mock.assert_called_once()
|
||||
|
@ -26,7 +26,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
|
||||
application_mock = mocker.patch("ahriman.application.application.Application.unknown")
|
||||
remove_mock = mocker.patch("ahriman.application.application.Application.remove")
|
||||
|
||||
RemoveUnknown.run(args, "x86_64", configuration)
|
||||
RemoveUnknown.run(args, "x86_64", configuration, True)
|
||||
application_mock.assert_called_once()
|
||||
remove_mock.assert_called_once()
|
||||
|
||||
@ -44,7 +44,7 @@ def test_run_dry_run(args: argparse.Namespace, configuration: Configuration, pac
|
||||
remove_mock = mocker.patch("ahriman.application.application.Application.remove")
|
||||
log_fn_mock = mocker.patch("ahriman.application.handlers.remove_unknown.RemoveUnknown.log_fn")
|
||||
|
||||
RemoveUnknown.run(args, "x86_64", configuration)
|
||||
RemoveUnknown.run(args, "x86_64", configuration, True)
|
||||
application_mock.assert_called_once()
|
||||
remove_mock.assert_not_called()
|
||||
log_fn_mock.assert_called_with(package_ahriman)
|
||||
|
@ -24,5 +24,5 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
|
||||
mocker.patch("pathlib.Path.mkdir")
|
||||
application_mock = mocker.patch("ahriman.application.application.Application.report")
|
||||
|
||||
Report.run(args, "x86_64", configuration)
|
||||
Report.run(args, "x86_64", configuration, True)
|
||||
application_mock.assert_called_once()
|
||||
|
@ -26,7 +26,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, aur_package
|
||||
mocker.patch("aur.search", return_value=[aur_package_ahriman])
|
||||
log_mock = mocker.patch("ahriman.application.handlers.search.Search.log_fn")
|
||||
|
||||
Search.run(args, "x86_64", configuration)
|
||||
Search.run(args, "x86_64", configuration, True)
|
||||
log_mock.assert_called_once()
|
||||
|
||||
|
||||
@ -38,7 +38,7 @@ def test_run_multiple_search(args: argparse.Namespace, configuration: Configurat
|
||||
args.search = ["ahriman", "is", "cool"]
|
||||
search_mock = mocker.patch("aur.search")
|
||||
|
||||
Search.run(args, "x86_64", configuration)
|
||||
Search.run(args, "x86_64", configuration, True)
|
||||
search_mock.assert_called_with(" ".join(args.search))
|
||||
|
||||
|
||||
@ -51,5 +51,5 @@ def test_log_fn(args: argparse.Namespace, configuration: Configuration, aur_pack
|
||||
mocker.patch("aur.search", return_value=[aur_package_ahriman])
|
||||
print_mock = mocker.patch("builtins.print")
|
||||
|
||||
Search.run(args, "x86_64", configuration)
|
||||
Search.run(args, "x86_64", configuration, True)
|
||||
print_mock.assert_called() # we don't really care about call details tbh
|
||||
|
@ -39,7 +39,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
|
||||
sudo_configuration_mock = mocker.patch("ahriman.application.handlers.setup.Setup.create_sudo_configuration")
|
||||
executable_mock = mocker.patch("ahriman.application.handlers.setup.Setup.create_executable")
|
||||
|
||||
Setup.run(args, "x86_64", configuration)
|
||||
Setup.run(args, "x86_64", configuration, True)
|
||||
ahriman_configuration_mock.assert_called_once()
|
||||
devtools_configuration_mock.assert_called_once()
|
||||
makepkg_configuration_mock.assert_called_once()
|
||||
|
@ -24,5 +24,5 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
|
||||
mocker.patch("pathlib.Path.mkdir")
|
||||
application_mock = mocker.patch("ahriman.application.application.Application.sign")
|
||||
|
||||
Sign.run(args, "x86_64", configuration)
|
||||
Sign.run(args, "x86_64", configuration, True)
|
||||
application_mock.assert_called_once()
|
||||
|
@ -30,7 +30,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, package_ahr
|
||||
packages_mock = mocker.patch("ahriman.core.status.client.Client.get",
|
||||
return_value=[(package_ahriman, BuildStatus())])
|
||||
|
||||
Status.run(args, "x86_64", configuration)
|
||||
Status.run(args, "x86_64", configuration, True)
|
||||
application_mock.assert_called_once()
|
||||
packages_mock.assert_called_once()
|
||||
|
||||
@ -46,5 +46,17 @@ def test_run_with_package_filter(args: argparse.Namespace, configuration: Config
|
||||
packages_mock = mocker.patch("ahriman.core.status.client.Client.get",
|
||||
return_value=[(package_ahriman, BuildStatus())])
|
||||
|
||||
Status.run(args, "x86_64", configuration)
|
||||
Status.run(args, "x86_64", configuration, True)
|
||||
packages_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_imply_with_report(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must create application object with native reporting
|
||||
"""
|
||||
args = _default_args(args)
|
||||
mocker.patch("pathlib.Path.mkdir")
|
||||
load_mock = mocker.patch("ahriman.core.status.client.Client.load")
|
||||
|
||||
Status.run(args, "x86_64", configuration, True)
|
||||
load_mock.assert_called_once()
|
||||
|
@ -28,7 +28,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
|
||||
mocker.patch("pathlib.Path.mkdir")
|
||||
update_self_mock = mocker.patch("ahriman.core.status.client.Client.update_self")
|
||||
|
||||
StatusUpdate.run(args, "x86_64", configuration)
|
||||
StatusUpdate.run(args, "x86_64", configuration, True)
|
||||
update_self_mock.assert_called_once()
|
||||
|
||||
|
||||
@ -42,7 +42,7 @@ def test_run_packages(args: argparse.Namespace, configuration: Configuration, pa
|
||||
mocker.patch("pathlib.Path.mkdir")
|
||||
update_mock = mocker.patch("ahriman.core.status.client.Client.update")
|
||||
|
||||
StatusUpdate.run(args, "x86_64", configuration)
|
||||
StatusUpdate.run(args, "x86_64", configuration, True)
|
||||
update_mock.assert_called_once()
|
||||
|
||||
|
||||
@ -57,5 +57,17 @@ def test_run_remove(args: argparse.Namespace, configuration: Configuration, pack
|
||||
mocker.patch("pathlib.Path.mkdir")
|
||||
update_mock = mocker.patch("ahriman.core.status.client.Client.remove")
|
||||
|
||||
StatusUpdate.run(args, "x86_64", configuration)
|
||||
StatusUpdate.run(args, "x86_64", configuration, True)
|
||||
update_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_imply_with_report(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must create application object with native reporting
|
||||
"""
|
||||
args = _default_args(args)
|
||||
mocker.patch("pathlib.Path.mkdir")
|
||||
load_mock = mocker.patch("ahriman.core.status.client.Client.load")
|
||||
|
||||
StatusUpdate.run(args, "x86_64", configuration, True)
|
||||
load_mock.assert_called_once()
|
||||
|
@ -24,5 +24,5 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
|
||||
mocker.patch("pathlib.Path.mkdir")
|
||||
application_mock = mocker.patch("ahriman.application.application.Application.sync")
|
||||
|
||||
Sync.run(args, "x86_64", configuration)
|
||||
Sync.run(args, "x86_64", configuration, True)
|
||||
application_mock.assert_called_once()
|
||||
|
@ -30,7 +30,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
|
||||
application_mock = mocker.patch("ahriman.application.application.Application.update")
|
||||
updates_mock = mocker.patch("ahriman.application.application.Application.get_updates")
|
||||
|
||||
Update.run(args, "x86_64", configuration)
|
||||
Update.run(args, "x86_64", configuration, True)
|
||||
application_mock.assert_called_once()
|
||||
updates_mock.assert_called_once()
|
||||
|
||||
@ -44,7 +44,7 @@ def test_run_dry_run(args: argparse.Namespace, configuration: Configuration, moc
|
||||
mocker.patch("pathlib.Path.mkdir")
|
||||
updates_mock = mocker.patch("ahriman.application.application.Application.get_updates")
|
||||
|
||||
Update.run(args, "x86_64", configuration)
|
||||
Update.run(args, "x86_64", configuration, True)
|
||||
updates_mock.assert_called_once()
|
||||
|
||||
|
||||
|
@ -6,14 +6,33 @@ from ahriman.application.handlers import Web
|
||||
from ahriman.core.configuration import Configuration
|
||||
|
||||
|
||||
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
|
||||
"""
|
||||
default arguments for these test cases
|
||||
:param args: command line arguments fixture
|
||||
:return: generated arguments for these test cases
|
||||
"""
|
||||
args.parser = lambda: True
|
||||
return args
|
||||
|
||||
|
||||
def test_run(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must run command
|
||||
"""
|
||||
args = _default_args(args)
|
||||
mocker.patch("pathlib.Path.mkdir")
|
||||
mocker.patch("ahriman.core.spawn.Spawn.start")
|
||||
setup_mock = mocker.patch("ahriman.web.web.setup_service")
|
||||
run_mock = mocker.patch("ahriman.web.web.run_server")
|
||||
|
||||
Web.run(args, "x86_64", configuration)
|
||||
Web.run(args, "x86_64", configuration, True)
|
||||
setup_mock.assert_called_once()
|
||||
run_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_disallow_multi_architecture_run() -> None:
|
||||
"""
|
||||
must not allow multi architecture run
|
||||
"""
|
||||
assert not Web.ALLOW_MULTI_ARCHITECTURE_RUN
|
||||
|
@ -260,11 +260,12 @@ def test_subparsers_update(parser: argparse.ArgumentParser) -> None:
|
||||
|
||||
def test_subparsers_web(parser: argparse.ArgumentParser) -> None:
|
||||
"""
|
||||
web command must imply lock and no_report
|
||||
web command must imply lock, no_report and parser
|
||||
"""
|
||||
args = parser.parse_args(["-a", "x86_64", "web"])
|
||||
assert args.lock is None
|
||||
assert args.no_report
|
||||
assert args.parser is not None and args.parser()
|
||||
|
||||
|
||||
def test_run(args: argparse.Namespace, mocker: MockerFixture) -> None:
|
||||
|
@ -1,11 +1,14 @@
|
||||
import aur
|
||||
import pytest
|
||||
|
||||
from pathlib import Path
|
||||
from pytest_mock import MockerFixture
|
||||
from typing import Any, Type, TypeVar
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from ahriman.core.auth.auth import Auth
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.spawn import Spawn
|
||||
from ahriman.core.status.watcher import Watcher
|
||||
from ahriman.models.package import Package
|
||||
from ahriman.models.package_description import PackageDescription
|
||||
@ -13,6 +16,7 @@ from ahriman.models.repository_paths import RepositoryPaths
|
||||
from ahriman.models.user import User
|
||||
from ahriman.models.user_access import UserAccess
|
||||
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
@ -43,10 +47,36 @@ def anyvar(cls: Type[T], strict: bool = False) -> T:
|
||||
|
||||
|
||||
# generic fixtures
|
||||
@pytest.fixture
|
||||
def aur_package_ahriman(package_ahriman: Package) -> aur.Package:
|
||||
"""
|
||||
fixture for AUR package
|
||||
:param package_ahriman: package fixture
|
||||
:return: AUR package test instance
|
||||
"""
|
||||
return aur.Package(
|
||||
num_votes=None,
|
||||
description=package_ahriman.packages[package_ahriman.base].description,
|
||||
url_path=package_ahriman.web_url,
|
||||
last_modified=None,
|
||||
name=package_ahriman.base,
|
||||
out_of_date=None,
|
||||
id=None,
|
||||
first_submitted=None,
|
||||
maintainer=None,
|
||||
version=package_ahriman.version,
|
||||
license=package_ahriman.packages[package_ahriman.base].licenses,
|
||||
url=None,
|
||||
package_base=package_ahriman.base,
|
||||
package_base_id=None,
|
||||
category_id=None)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def auth(configuration: Configuration) -> Auth:
|
||||
"""
|
||||
auth provider fixture
|
||||
:param configuration: configuration fixture
|
||||
:return: auth service instance
|
||||
"""
|
||||
return Auth(configuration)
|
||||
@ -160,6 +190,7 @@ def package_description_python2_schedule() -> PackageDescription:
|
||||
def repository_paths(configuration: Configuration) -> RepositoryPaths:
|
||||
"""
|
||||
repository paths fixture
|
||||
:param configuration: configuration fixture
|
||||
:return: repository paths test instance
|
||||
"""
|
||||
return RepositoryPaths(
|
||||
@ -167,13 +198,23 @@ def repository_paths(configuration: Configuration) -> RepositoryPaths:
|
||||
root=configuration.getpath("repository", "root"))
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def spawner(configuration: Configuration) -> Spawn:
|
||||
"""
|
||||
spawner fixture
|
||||
:param configuration: configuration fixture
|
||||
:return: spawner fixture
|
||||
"""
|
||||
return Spawn(MagicMock(), "x86_64", configuration)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def user() -> User:
|
||||
"""
|
||||
fixture for user descriptor
|
||||
:return: user descriptor instance
|
||||
"""
|
||||
return User("user", "pa55w0rd", UserAccess.Status)
|
||||
return User("user", "pa55w0rd", UserAccess.Read)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
@ -46,8 +46,8 @@ def test_is_safe_request(auth: Auth) -> None:
|
||||
must validate safe request
|
||||
"""
|
||||
# login and logout are always safe
|
||||
assert auth.is_safe_request("/login", UserAccess.Write)
|
||||
assert auth.is_safe_request("/logout", UserAccess.Write)
|
||||
assert auth.is_safe_request("/user-api/v1/login", UserAccess.Write)
|
||||
assert auth.is_safe_request("/user-api/v1/logout", UserAccess.Write)
|
||||
|
||||
auth.allowed_paths.add("/safe")
|
||||
auth.allowed_paths_groups.add("/unsafe/safe")
|
||||
|
@ -19,7 +19,7 @@ def cleaner(configuration: Configuration, mocker: MockerFixture) -> Cleaner:
|
||||
:return: cleaner test instance
|
||||
"""
|
||||
mocker.patch("pathlib.Path.mkdir")
|
||||
return Cleaner("x86_64", configuration)
|
||||
return Cleaner("x86_64", configuration, no_report=True)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@ -36,7 +36,7 @@ def executor(configuration: Configuration, mocker: MockerFixture) -> Executor:
|
||||
mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_chroot")
|
||||
mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_manual")
|
||||
mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_packages")
|
||||
return Executor("x86_64", configuration)
|
||||
return Executor("x86_64", configuration, no_report=True)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@ -48,7 +48,7 @@ def repository(configuration: Configuration, mocker: MockerFixture) -> Repositor
|
||||
:return: repository test instance
|
||||
"""
|
||||
mocker.patch("pathlib.Path.mkdir")
|
||||
return Repository("x86_64", configuration)
|
||||
return Repository("x86_64", configuration, no_report=True)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@ -58,7 +58,7 @@ def properties(configuration: Configuration) -> Properties:
|
||||
:param configuration: configuration fixture
|
||||
:return: properties test instance
|
||||
"""
|
||||
return Properties("x86_64", configuration)
|
||||
return Properties("x86_64", configuration, no_report=True)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@ -75,4 +75,4 @@ def update_handler(configuration: Configuration, mocker: MockerFixture) -> Updat
|
||||
mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_chroot")
|
||||
mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_manual")
|
||||
mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_packages")
|
||||
return UpdateHandler("x86_64", configuration)
|
||||
return UpdateHandler("x86_64", configuration, no_report=True)
|
||||
|
@ -2,6 +2,7 @@ from pytest_mock import MockerFixture
|
||||
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.repository.properties import Properties
|
||||
from ahriman.core.status.web_client import WebClient
|
||||
|
||||
|
||||
def test_create_tree_on_load(configuration: Configuration, mocker: MockerFixture) -> None:
|
||||
@ -9,6 +10,29 @@ def test_create_tree_on_load(configuration: Configuration, mocker: MockerFixture
|
||||
must create tree on load
|
||||
"""
|
||||
create_tree_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.create_tree")
|
||||
Properties("x86_64", configuration)
|
||||
Properties("x86_64", configuration, True)
|
||||
|
||||
create_tree_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_create_dummy_report_client(configuration: Configuration, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must create dummy report client if report is disabled
|
||||
"""
|
||||
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.create_tree")
|
||||
load_mock = mocker.patch("ahriman.core.status.client.Client.load")
|
||||
properties = Properties("x86_64", configuration, True)
|
||||
|
||||
load_mock.assert_not_called()
|
||||
assert not isinstance(properties.reporter, WebClient)
|
||||
|
||||
|
||||
def test_create_full_report_client(configuration: Configuration, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must create load report client if report is enabled
|
||||
"""
|
||||
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.create_tree")
|
||||
load_mock = mocker.patch("ahriman.core.status.client.Client.load")
|
||||
Properties("x86_64", configuration, False)
|
||||
|
||||
load_mock.assert_called_once()
|
||||
|
@ -5,12 +5,28 @@ from pathlib import Path
|
||||
from pytest_mock import MockerFixture
|
||||
from unittest.mock import PropertyMock
|
||||
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.exceptions import UnknownPackage
|
||||
from ahriman.core.status.watcher import Watcher
|
||||
from ahriman.core.status.web_client import WebClient
|
||||
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
|
||||
from ahriman.models.package import Package
|
||||
|
||||
|
||||
def test_force_no_report(configuration: Configuration, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must force dummy report client
|
||||
"""
|
||||
configuration.set_option("web", "port", "8080")
|
||||
mocker.patch("pathlib.Path.mkdir")
|
||||
|
||||
load_mock = mocker.patch("ahriman.core.status.client.Client.load")
|
||||
watcher = Watcher("x86_64", configuration)
|
||||
|
||||
load_mock.assert_not_called()
|
||||
assert not isinstance(watcher.repository.reporter, WebClient)
|
||||
|
||||
|
||||
def test_cache_load(watcher: Watcher, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must load state from cache
|
||||
|
111
tests/ahriman/core/test_spawn.py
Normal file
111
tests/ahriman/core/test_spawn.py
Normal file
@ -0,0 +1,111 @@
|
||||
from pytest_mock import MockerFixture
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from ahriman.core.spawn import Spawn
|
||||
|
||||
|
||||
def test_process(spawner: Spawn) -> None:
|
||||
"""
|
||||
must process external process run correctly
|
||||
"""
|
||||
args = MagicMock()
|
||||
callback = MagicMock()
|
||||
callback.return_value = True
|
||||
|
||||
spawner.process(callback, args, spawner.architecture, "id", spawner.queue)
|
||||
|
||||
callback.assert_called_with(args, spawner.architecture)
|
||||
(uuid, status) = spawner.queue.get()
|
||||
assert uuid == "id"
|
||||
assert status
|
||||
assert spawner.queue.empty()
|
||||
|
||||
|
||||
def test_process_error(spawner: Spawn) -> None:
|
||||
"""
|
||||
must process external run with error correctly
|
||||
"""
|
||||
callback = MagicMock()
|
||||
callback.return_value = False
|
||||
|
||||
spawner.process(callback, MagicMock(), spawner.architecture, "id", spawner.queue)
|
||||
|
||||
(uuid, status) = spawner.queue.get()
|
||||
assert uuid == "id"
|
||||
assert not status
|
||||
assert spawner.queue.empty()
|
||||
|
||||
|
||||
def test_packages_add(spawner: Spawn, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must call package addition
|
||||
"""
|
||||
spawn_mock = mocker.patch("ahriman.core.spawn.Spawn.spawn_process")
|
||||
spawner.packages_add(["ahriman", "linux"], now=False)
|
||||
spawn_mock.assert_called_with("add", "ahriman", "linux")
|
||||
|
||||
|
||||
def test_packages_add_with_build(spawner: Spawn, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must call package addition with update
|
||||
"""
|
||||
spawn_mock = mocker.patch("ahriman.core.spawn.Spawn.spawn_process")
|
||||
spawner.packages_add(["ahriman", "linux"], now=True)
|
||||
spawn_mock.assert_called_with("add", "ahriman", "linux", now="")
|
||||
|
||||
|
||||
def test_packages_remove(spawner: Spawn, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must call package removal
|
||||
"""
|
||||
spawn_mock = mocker.patch("ahriman.core.spawn.Spawn.spawn_process")
|
||||
spawner.packages_remove(["ahriman", "linux"])
|
||||
spawn_mock.assert_called_with("remove", "ahriman", "linux")
|
||||
|
||||
|
||||
def test_spawn_process(spawner: Spawn, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must correctly spawn child process
|
||||
"""
|
||||
start_mock = mocker.patch("multiprocessing.Process.start")
|
||||
|
||||
spawner.spawn_process("add", "ahriman", now="", maybe="?")
|
||||
start_mock.assert_called_once()
|
||||
spawner.args_parser.parse_args.assert_called_with([
|
||||
"--architecture", spawner.architecture, "--configuration", str(spawner.configuration.path),
|
||||
"add", "ahriman", "--now", "--maybe", "?"
|
||||
])
|
||||
|
||||
|
||||
def test_run(spawner: Spawn, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must implement run method
|
||||
"""
|
||||
logging_mock = mocker.patch("logging.Logger.info")
|
||||
|
||||
spawner.queue.put(("1", False))
|
||||
spawner.queue.put(("2", True))
|
||||
spawner.queue.put(None) # terminate
|
||||
|
||||
spawner.run()
|
||||
logging_mock.assert_called()
|
||||
|
||||
|
||||
def test_run_pop(spawner: Spawn) -> None:
|
||||
"""
|
||||
must pop and terminate child process
|
||||
"""
|
||||
first = spawner.active["1"] = MagicMock()
|
||||
second = spawner.active["2"] = MagicMock()
|
||||
|
||||
spawner.queue.put(("1", False))
|
||||
spawner.queue.put(("2", True))
|
||||
spawner.queue.put(None) # terminate
|
||||
|
||||
spawner.run()
|
||||
|
||||
first.terminate.assert_called_once()
|
||||
first.join.assert_called_once()
|
||||
second.terminate.assert_called_once()
|
||||
second.join.assert_called_once()
|
||||
assert not spawner.active
|
@ -59,10 +59,14 @@ def test_get_local_files(s3: S3, resource_path_root: Path) -> None:
|
||||
Path("models/package_ahriman_srcinfo"),
|
||||
Path("models/package_tpacpi-bat-git_srcinfo"),
|
||||
Path("models/package_yay_srcinfo"),
|
||||
Path("web/templates/build-status/login-modal.jinja2"),
|
||||
Path("web/templates/build-status/package-actions-modals.jinja2"),
|
||||
Path("web/templates/build-status/package-actions-script.jinja2"),
|
||||
Path("web/templates/utils/bootstrap-scripts.jinja2"),
|
||||
Path("web/templates/utils/style.jinja2"),
|
||||
Path("web/templates/build-status.jinja2"),
|
||||
Path("web/templates/email-index.jinja2"),
|
||||
Path("web/templates/repo-index.jinja2"),
|
||||
Path("web/templates/style.jinja2"),
|
||||
])
|
||||
|
||||
local_files = list(sorted(s3.get_local_files(resource_path_root).keys()))
|
||||
|
@ -7,7 +7,7 @@ def test_from_option(user: User) -> None:
|
||||
must generate user from options
|
||||
"""
|
||||
assert User.from_option(user.username, user.password) == user
|
||||
# default is status access
|
||||
# default is read access
|
||||
user.access = UserAccess.Write
|
||||
assert User.from_option(user.username, user.password) != user
|
||||
|
||||
@ -52,17 +52,6 @@ def test_verify_access_read(user: User) -> None:
|
||||
user.access = UserAccess.Read
|
||||
assert user.verify_access(UserAccess.Read)
|
||||
assert not user.verify_access(UserAccess.Write)
|
||||
assert not user.verify_access(UserAccess.Status)
|
||||
|
||||
|
||||
def test_verify_access_status(user: User) -> None:
|
||||
"""
|
||||
user with status access must be able to only request status
|
||||
"""
|
||||
user.access = UserAccess.Status
|
||||
assert not user.verify_access(UserAccess.Read)
|
||||
assert not user.verify_access(UserAccess.Write)
|
||||
assert user.verify_access(UserAccess.Status)
|
||||
|
||||
|
||||
def test_verify_access_write(user: User) -> None:
|
||||
@ -72,4 +61,3 @@ def test_verify_access_write(user: User) -> None:
|
||||
user.access = UserAccess.Write
|
||||
assert user.verify_access(UserAccess.Read)
|
||||
assert user.verify_access(UserAccess.Write)
|
||||
assert user.verify_access(UserAccess.Status)
|
||||
|
@ -1,41 +1,64 @@
|
||||
import pytest
|
||||
|
||||
from aiohttp import web
|
||||
from collections import namedtuple
|
||||
from pytest_mock import MockerFixture
|
||||
from typing import Any
|
||||
|
||||
import ahriman.core.auth.helpers
|
||||
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.spawn import Spawn
|
||||
from ahriman.models.user import User
|
||||
from ahriman.web.web import setup_service
|
||||
|
||||
|
||||
_request = namedtuple("_request", ["app", "path", "method", "json", "post"])
|
||||
|
||||
|
||||
@pytest.helpers.register
|
||||
def request(app: web.Application, path: str, method: str, json: Any = None, data: Any = None) -> _request:
|
||||
"""
|
||||
request generator helper
|
||||
:param app: application fixture
|
||||
:param path: path for the request
|
||||
:param method: method for the request
|
||||
:param json: json payload of the request
|
||||
:param data: form data payload of the request
|
||||
:return: dummy request object
|
||||
"""
|
||||
return _request(app, path, method, json, data)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def application(configuration: Configuration, mocker: MockerFixture) -> web.Application:
|
||||
def application(configuration: Configuration, spawner: Spawn, mocker: MockerFixture) -> web.Application:
|
||||
"""
|
||||
application fixture
|
||||
:param configuration: configuration fixture
|
||||
:param spawner: spawner fixture
|
||||
:param mocker: mocker object
|
||||
:return: application test instance
|
||||
"""
|
||||
mocker.patch.object(ahriman.core.auth.helpers, "_has_aiohttp_security", False)
|
||||
mocker.patch("pathlib.Path.mkdir")
|
||||
return setup_service("x86_64", configuration)
|
||||
return setup_service("x86_64", configuration, spawner)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def application_with_auth(configuration: Configuration, user: User, mocker: MockerFixture) -> web.Application:
|
||||
def application_with_auth(configuration: Configuration, user: User, spawner: Spawn,
|
||||
mocker: MockerFixture) -> web.Application:
|
||||
"""
|
||||
application fixture with auth enabled
|
||||
:param configuration: configuration fixture
|
||||
:param user: user descriptor fixture
|
||||
:param spawner: spawner fixture
|
||||
:param mocker: mocker object
|
||||
:return: application test instance
|
||||
"""
|
||||
configuration.set_option("auth", "target", "configuration")
|
||||
mocker.patch.object(ahriman.core.auth.helpers, "_has_aiohttp_security", True)
|
||||
mocker.patch("pathlib.Path.mkdir")
|
||||
application = setup_service("x86_64", configuration)
|
||||
application = setup_service("x86_64", configuration, spawner)
|
||||
|
||||
generated = User(user.username, user.hash_password(application["validator"].salt), user.access)
|
||||
application["validator"]._users[generated.username] = generated
|
||||
|
@ -1,23 +1,10 @@
|
||||
import pytest
|
||||
|
||||
from collections import namedtuple
|
||||
|
||||
from ahriman.core.auth.auth import Auth
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.models.user import User
|
||||
from ahriman.web.middlewares.auth_handler import AuthorizationPolicy
|
||||
|
||||
_request = namedtuple("_request", ["path", "method"])
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def aiohttp_request() -> _request:
|
||||
"""
|
||||
fixture for aiohttp like object
|
||||
:return: aiohttp like request test instance
|
||||
"""
|
||||
return _request("path", "GET")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def authorization_policy(configuration: Configuration, user: User) -> AuthorizationPolicy:
|
||||
|
@ -1,6 +1,7 @@
|
||||
import pytest
|
||||
|
||||
from aiohttp import web
|
||||
from pytest_mock import MockerFixture
|
||||
from typing import Any
|
||||
from unittest.mock import AsyncMock, MagicMock
|
||||
|
||||
from ahriman.core.auth.auth import Auth
|
||||
@ -29,40 +30,40 @@ async def test_permits(authorization_policy: AuthorizationPolicy, user: User) ->
|
||||
authorization_policy.validator.verify_access.assert_called_with(user.username, user.access, "/endpoint")
|
||||
|
||||
|
||||
async def test_auth_handler_api(aiohttp_request: Any, auth: Auth, mocker: MockerFixture) -> None:
|
||||
async def test_auth_handler_api(auth: Auth, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must ask for status permission for api calls
|
||||
"""
|
||||
aiohttp_request = aiohttp_request._replace(path="/status-api")
|
||||
aiohttp_request = pytest.helpers.request("", "/status-api", "GET")
|
||||
request_handler = AsyncMock()
|
||||
mocker.patch("ahriman.core.auth.auth.Auth.is_safe_request", return_value=False)
|
||||
check_permission_mock = mocker.patch("aiohttp_security.check_permission")
|
||||
|
||||
handler = auth_handler(auth)
|
||||
await handler(aiohttp_request, request_handler)
|
||||
check_permission_mock.assert_called_with(aiohttp_request, UserAccess.Status, aiohttp_request.path)
|
||||
check_permission_mock.assert_called_with(aiohttp_request, UserAccess.Read, aiohttp_request.path)
|
||||
|
||||
|
||||
async def test_auth_handler_api_post(aiohttp_request: Any, auth: Auth, mocker: MockerFixture) -> None:
|
||||
async def test_auth_handler_api_post(auth: Auth, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must ask for status permission for api calls with POST
|
||||
"""
|
||||
aiohttp_request = aiohttp_request._replace(path="/status-api", method="POST")
|
||||
aiohttp_request = pytest.helpers.request("", "/status-api", "POST")
|
||||
request_handler = AsyncMock()
|
||||
mocker.patch("ahriman.core.auth.auth.Auth.is_safe_request", return_value=False)
|
||||
check_permission_mock = mocker.patch("aiohttp_security.check_permission")
|
||||
|
||||
handler = auth_handler(auth)
|
||||
await handler(aiohttp_request, request_handler)
|
||||
check_permission_mock.assert_called_with(aiohttp_request, UserAccess.Status, aiohttp_request.path)
|
||||
check_permission_mock.assert_called_with(aiohttp_request, UserAccess.Write, aiohttp_request.path)
|
||||
|
||||
|
||||
async def test_auth_handler_read(aiohttp_request: Any, auth: Auth, mocker: MockerFixture) -> None:
|
||||
async def test_auth_handler_read(auth: Auth, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must ask for read permission for api calls with GET
|
||||
"""
|
||||
for method in ("GET", "HEAD", "OPTIONS"):
|
||||
aiohttp_request = aiohttp_request._replace(method=method)
|
||||
aiohttp_request = pytest.helpers.request("", "", method)
|
||||
request_handler = AsyncMock()
|
||||
mocker.patch("ahriman.core.auth.auth.Auth.is_safe_request", return_value=False)
|
||||
check_permission_mock = mocker.patch("aiohttp_security.check_permission")
|
||||
@ -72,12 +73,12 @@ async def test_auth_handler_read(aiohttp_request: Any, auth: Auth, mocker: Mocke
|
||||
check_permission_mock.assert_called_with(aiohttp_request, UserAccess.Read, aiohttp_request.path)
|
||||
|
||||
|
||||
async def test_auth_handler_write(aiohttp_request: Any, auth: Auth, mocker: MockerFixture) -> None:
|
||||
async def test_auth_handler_write(auth: Auth, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must ask for read permission for api calls with POST
|
||||
"""
|
||||
for method in ("CONNECT", "DELETE", "PATCH", "POST", "PUT", "TRACE"):
|
||||
aiohttp_request = aiohttp_request._replace(method=method)
|
||||
aiohttp_request = pytest.helpers.request("", "", method)
|
||||
request_handler = AsyncMock()
|
||||
mocker.patch("ahriman.core.auth.auth.Auth.is_safe_request", return_value=False)
|
||||
check_permission_mock = mocker.patch("aiohttp_security.check_permission")
|
||||
|
@ -3,45 +3,47 @@ import pytest
|
||||
|
||||
from aiohttp.web_exceptions import HTTPBadRequest
|
||||
from pytest_mock import MockerFixture
|
||||
from typing import Any
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
from ahriman.web.middlewares.exception_handler import exception_handler
|
||||
|
||||
|
||||
async def test_exception_handler(aiohttp_request: Any, mocker: MockerFixture) -> None:
|
||||
async def test_exception_handler(mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must pass success response
|
||||
"""
|
||||
request = pytest.helpers.request("", "", "")
|
||||
request_handler = AsyncMock()
|
||||
logging_mock = mocker.patch("logging.Logger.exception")
|
||||
|
||||
handler = exception_handler(logging.getLogger())
|
||||
await handler(aiohttp_request, request_handler)
|
||||
await handler(request, request_handler)
|
||||
logging_mock.assert_not_called()
|
||||
|
||||
|
||||
async def test_exception_handler_client_error(aiohttp_request: Any, mocker: MockerFixture) -> None:
|
||||
async def test_exception_handler_client_error(mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must pass client exception
|
||||
"""
|
||||
request = pytest.helpers.request("", "", "")
|
||||
request_handler = AsyncMock(side_effect=HTTPBadRequest())
|
||||
logging_mock = mocker.patch("logging.Logger.exception")
|
||||
|
||||
handler = exception_handler(logging.getLogger())
|
||||
with pytest.raises(HTTPBadRequest):
|
||||
await handler(aiohttp_request, request_handler)
|
||||
await handler(request, request_handler)
|
||||
logging_mock.assert_not_called()
|
||||
|
||||
|
||||
async def test_exception_handler_server_error(aiohttp_request: Any, mocker: MockerFixture) -> None:
|
||||
async def test_exception_handler_server_error(mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must log server exception and re-raise it
|
||||
"""
|
||||
request = pytest.helpers.request("", "", "")
|
||||
request_handler = AsyncMock(side_effect=Exception())
|
||||
logging_mock = mocker.patch("logging.Logger.exception")
|
||||
|
||||
handler = exception_handler(logging.getLogger())
|
||||
with pytest.raises(Exception):
|
||||
await handler(aiohttp_request, request_handler)
|
||||
await handler(request, request_handler)
|
||||
logging_mock.assert_called_once()
|
||||
|
@ -6,6 +6,18 @@ from pytest_aiohttp import TestClient
|
||||
from pytest_mock import MockerFixture
|
||||
from typing import Any
|
||||
|
||||
from ahriman.web.views.base import BaseView
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def base(application: web.Application) -> BaseView:
|
||||
"""
|
||||
base view fixture
|
||||
:param application: application fixture
|
||||
:return: generated base view fixture
|
||||
"""
|
||||
return BaseView(pytest.helpers.request(application, "", ""))
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client(application: web.Application, loop: BaseEventLoop,
|
||||
|
46
tests/ahriman/web/views/service/test_views_service_add.py
Normal file
46
tests/ahriman/web/views/service/test_views_service_add.py
Normal file
@ -0,0 +1,46 @@
|
||||
from aiohttp.test_utils import TestClient
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
|
||||
async def test_post(client: TestClient, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must call post request correctly
|
||||
"""
|
||||
add_mock = mocker.patch("ahriman.core.spawn.Spawn.packages_add")
|
||||
response = await client.post("/service-api/v1/add", json={"packages": ["ahriman"]})
|
||||
|
||||
assert response.status == 200
|
||||
add_mock.assert_called_with(["ahriman"], True)
|
||||
|
||||
|
||||
async def test_post_now(client: TestClient, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must call post and run build
|
||||
"""
|
||||
add_mock = mocker.patch("ahriman.core.spawn.Spawn.packages_add")
|
||||
response = await client.post("/service-api/v1/add", json={"packages": ["ahriman"], "build_now": False})
|
||||
|
||||
assert response.status == 200
|
||||
add_mock.assert_called_with(["ahriman"], False)
|
||||
|
||||
|
||||
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 = await client.post("/service-api/v1/add")
|
||||
|
||||
assert response.status == 400
|
||||
add_mock.assert_not_called()
|
||||
|
||||
|
||||
async def test_post_update(client: TestClient, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must call post request correctly for alias
|
||||
"""
|
||||
add_mock = mocker.patch("ahriman.core.spawn.Spawn.packages_add")
|
||||
response = await client.post("/service-api/v1/update", json={"packages": ["ahriman"]})
|
||||
|
||||
assert response.status == 200
|
||||
add_mock.assert_called_with(["ahriman"], True)
|
24
tests/ahriman/web/views/service/test_views_service_remove.py
Normal file
24
tests/ahriman/web/views/service/test_views_service_remove.py
Normal file
@ -0,0 +1,24 @@
|
||||
from aiohttp.test_utils import TestClient
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
|
||||
async def test_post(client: TestClient, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must call post request correctly
|
||||
"""
|
||||
add_mock = mocker.patch("ahriman.core.spawn.Spawn.packages_remove")
|
||||
response = await client.post("/service-api/v1/remove", json={"packages": ["ahriman"]})
|
||||
|
||||
assert response.status == 200
|
||||
add_mock.assert_called_with(["ahriman"])
|
||||
|
||||
|
||||
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_remove")
|
||||
response = await client.post("/service-api/v1/remove")
|
||||
|
||||
assert response.status == 400
|
||||
add_mock.assert_not_called()
|
59
tests/ahriman/web/views/service/test_views_service_search.py
Normal file
59
tests/ahriman/web/views/service/test_views_service_search.py
Normal file
@ -0,0 +1,59 @@
|
||||
import aur
|
||||
|
||||
from aiohttp.test_utils import TestClient
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
|
||||
async def test_get(client: TestClient, aur_package_ahriman: aur.Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must call get request correctly
|
||||
"""
|
||||
mocker.patch("aur.search", return_value=[aur_package_ahriman])
|
||||
response = await client.get("/service-api/v1/search", params={"for": "ahriman"})
|
||||
|
||||
assert response.status == 200
|
||||
assert await response.json() == ["ahriman"]
|
||||
|
||||
|
||||
async def test_get_exception(client: TestClient, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must raise 400 on empty search string
|
||||
"""
|
||||
search_mock = mocker.patch("aur.search")
|
||||
response = await client.get("/service-api/v1/search")
|
||||
|
||||
assert response.status == 400
|
||||
search_mock.assert_not_called()
|
||||
|
||||
|
||||
async def test_get_join(client: TestClient, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must join search args with space
|
||||
"""
|
||||
search_mock = mocker.patch("aur.search")
|
||||
response = await client.get("/service-api/v1/search", params=[("for", "ahriman"), ("for", "maybe")])
|
||||
|
||||
assert response.status == 200
|
||||
search_mock.assert_called_with("ahriman maybe")
|
||||
|
||||
|
||||
async def test_get_join_filter(client: TestClient, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must filter search parameters with less than 3 symbols
|
||||
"""
|
||||
search_mock = mocker.patch("aur.search")
|
||||
response = await client.get("/service-api/v1/search", params=[("for", "ah"), ("for", "maybe")])
|
||||
|
||||
assert response.status == 200
|
||||
search_mock.assert_called_with("maybe")
|
||||
|
||||
|
||||
async def test_get_join_filter_empty(client: TestClient, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must filter search parameters with less than 3 symbols (empty result)
|
||||
"""
|
||||
search_mock = mocker.patch("aur.search")
|
||||
response = await client.get("/service-api/v1/search", params=[("for", "ah"), ("for", "ma")])
|
||||
|
||||
assert response.status == 400
|
||||
search_mock.assert_not_called()
|
88
tests/ahriman/web/views/test_views_base.py
Normal file
88
tests/ahriman/web/views/test_views_base.py
Normal file
@ -0,0 +1,88 @@
|
||||
import pytest
|
||||
|
||||
from multidict import MultiDict
|
||||
|
||||
from ahriman.web.views.base import BaseView
|
||||
|
||||
|
||||
def test_service(base: BaseView) -> None:
|
||||
"""
|
||||
must return service
|
||||
"""
|
||||
assert base.service
|
||||
|
||||
|
||||
def test_spawn(base: BaseView) -> None:
|
||||
"""
|
||||
must return spawn thread
|
||||
"""
|
||||
assert base.spawner
|
||||
|
||||
|
||||
def test_validator(base: BaseView) -> None:
|
||||
"""
|
||||
must return service
|
||||
"""
|
||||
assert base.validator
|
||||
|
||||
|
||||
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
|
||||
"""
|
||||
json = {"key1": "value1", "key2": ["value2", "value3"], "key3": ["value4", "value5", "value6"]}
|
||||
|
||||
async def get_data():
|
||||
result = MultiDict()
|
||||
for key, values in json.items():
|
||||
if isinstance(values, list):
|
||||
for value in values:
|
||||
result.add(key, value)
|
||||
else:
|
||||
result.add(key, values)
|
||||
return result
|
||||
|
||||
base._request = pytest.helpers.request(base.request.app, "", "", data=get_data)
|
||||
assert await base.data_as_json([]) == json
|
||||
|
||||
|
||||
async def test_data_as_json_with_list_keys(base: BaseView) -> None:
|
||||
"""
|
||||
must parse multi value form payload with forced list
|
||||
"""
|
||||
json = {"key1": "value1"}
|
||||
|
||||
async def get_data():
|
||||
return json
|
||||
|
||||
base._request = pytest.helpers.request(base.request.app, "", "", data=get_data)
|
||||
assert await base.data_as_json(["key1"]) == {"key1": ["value1"]}
|
@ -11,10 +11,10 @@ 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")
|
||||
|
||||
post_response = await client_with_auth.post("/login", json=payload)
|
||||
post_response = await client_with_auth.post("/user-api/v1/login", json=payload)
|
||||
assert post_response.status == 200
|
||||
|
||||
post_response = await client_with_auth.post("/login", data=payload)
|
||||
post_response = await client_with_auth.post("/user-api/v1/login", data=payload)
|
||||
assert post_response.status == 200
|
||||
|
||||
remember_mock.assert_called()
|
||||
@ -25,7 +25,7 @@ async def test_post_skip(client: TestClient, user: User) -> None:
|
||||
must process if no auth configured
|
||||
"""
|
||||
payload = {"username": user.username, "password": user.password}
|
||||
post_response = await client.post("/login", json=payload)
|
||||
post_response = await client.post("/user-api/v1/login", json=payload)
|
||||
assert post_response.status == 200
|
||||
|
||||
|
||||
@ -36,6 +36,6 @@ async def test_post_unauthorized(client_with_auth: TestClient, user: User, mocke
|
||||
payload = {"username": user.username, "password": ""}
|
||||
remember_mock = mocker.patch("aiohttp_security.remember")
|
||||
|
||||
post_response = await client_with_auth.post("/login", json=payload)
|
||||
post_response = await client_with_auth.post("/user-api/v1/login", json=payload)
|
||||
assert post_response.status == 401
|
||||
remember_mock.assert_not_called()
|
@ -10,7 +10,7 @@ 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("/logout")
|
||||
post_response = await client_with_auth.post("/user-api/v1/logout")
|
||||
assert post_response.status == 200
|
||||
forget_mock.assert_called_once()
|
||||
|
||||
@ -22,7 +22,7 @@ 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")
|
||||
|
||||
post_response = await client_with_auth.post("/logout")
|
||||
post_response = await client_with_auth.post("/user-api/v1/logout")
|
||||
assert post_response.status == 401
|
||||
forget_mock.assert_not_called()
|
||||
|
||||
@ -31,5 +31,5 @@ async def test_post_disabled(client: TestClient) -> None:
|
||||
"""
|
||||
must raise exception if auth is disabled
|
||||
"""
|
||||
post_response = await client.post("/logout")
|
||||
post_response = await client.post("/user-api/v1/logout")
|
||||
assert post_response.status == 200
|
Reference in New Issue
Block a user