mirror of
https://github.com/arcan1s/ahriman.git
synced 2025-06-28 06:41:43 +00:00
* add models tests (#1) also replace single quote to double one to confort PEP docstring + move _check_output to class properties to make it available for mocking * alpm tests implementation * try to replace os with pathlib * update tests for pathlib * fix includes glob and trim version from dependencies * build_tools package tests * repository component tests * add sign tests * complete status tests * handle exceptions in actual_version calls * complete core tests * move configuration to root conftest * application tests * complete application tests * change copyright to more generic one * base web tests * complete web tests * complete testkit also add argument parsers test
This commit is contained in:
30
tests/ahriman/application/conftest.py
Normal file
30
tests/ahriman/application/conftest.py
Normal file
@ -0,0 +1,30 @@
|
||||
import argparse
|
||||
import pytest
|
||||
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from ahriman.application.ahriman import _parser
|
||||
from ahriman.application.application import Application
|
||||
from ahriman.application.lock import Lock
|
||||
from ahriman.core.configuration import Configuration
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def application(configuration: Configuration, mocker: MockerFixture) -> Application:
|
||||
mocker.patch("pathlib.Path.mkdir")
|
||||
return Application("x86_64", configuration)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def args() -> argparse.Namespace:
|
||||
return argparse.Namespace(lock=None, force=False, unsafe=False, no_report=True)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def lock(args: argparse.Namespace, configuration: Configuration) -> Lock:
|
||||
return Lock(args, "x86_64", configuration)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def parser() -> argparse.ArgumentParser:
|
||||
return _parser()
|
27
tests/ahriman/application/handlers/test_handler.py
Normal file
27
tests/ahriman/application/handlers/test_handler.py
Normal file
@ -0,0 +1,27 @@
|
||||
import argparse
|
||||
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from ahriman.application.handlers import Handler
|
||||
from ahriman.core.configuration import Configuration
|
||||
|
||||
|
||||
def test_call(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must call inside lock
|
||||
"""
|
||||
mocker.patch("ahriman.application.handlers.Handler.run")
|
||||
enter_mock = mocker.patch("ahriman.application.lock.Lock.__enter__")
|
||||
exit_mock = mocker.patch("ahriman.application.lock.Lock.__exit__")
|
||||
|
||||
assert Handler._call(args, "x86_64", configuration)
|
||||
enter_mock.assert_called_once()
|
||||
exit_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_call_exception(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must process exception
|
||||
"""
|
||||
mocker.patch("ahriman.application.lock.Lock.__enter__", side_effect=Exception())
|
||||
assert not Handler._call(args, "x86_64", configuration)
|
19
tests/ahriman/application/handlers/test_handler_add.py
Normal file
19
tests/ahriman/application/handlers/test_handler_add.py
Normal file
@ -0,0 +1,19 @@
|
||||
import argparse
|
||||
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from ahriman.application.handlers import Add
|
||||
from ahriman.core.configuration import Configuration
|
||||
|
||||
|
||||
def test_run(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must run command
|
||||
"""
|
||||
args.package = []
|
||||
args.without_dependencies = False
|
||||
mocker.patch("pathlib.Path.mkdir")
|
||||
application_mock = mocker.patch("ahriman.application.application.Application.add")
|
||||
|
||||
Add.run(args, "x86_64", configuration)
|
||||
application_mock.assert_called_once()
|
22
tests/ahriman/application/handlers/test_handler_clean.py
Normal file
22
tests/ahriman/application/handlers/test_handler_clean.py
Normal file
@ -0,0 +1,22 @@
|
||||
import argparse
|
||||
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from ahriman.application.handlers import Clean
|
||||
from ahriman.core.configuration import Configuration
|
||||
|
||||
|
||||
def test_run(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must run command
|
||||
"""
|
||||
args.no_build = False
|
||||
args.no_cache = False
|
||||
args.no_chroot = False
|
||||
args.no_manual = False
|
||||
args.no_packages = False
|
||||
mocker.patch("pathlib.Path.mkdir")
|
||||
application_mock = mocker.patch("ahriman.application.application.Application.clean")
|
||||
|
||||
Clean.run(args, "x86_64", configuration)
|
||||
application_mock.assert_called_once()
|
17
tests/ahriman/application/handlers/test_handler_dump.py
Normal file
17
tests/ahriman/application/handlers/test_handler_dump.py
Normal file
@ -0,0 +1,17 @@
|
||||
import argparse
|
||||
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from ahriman.application.handlers import Dump
|
||||
from ahriman.core.configuration import Configuration
|
||||
|
||||
|
||||
def test_run(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must run command
|
||||
"""
|
||||
mocker.patch("pathlib.Path.mkdir")
|
||||
application_mock = mocker.patch("ahriman.core.configuration.Configuration.dump")
|
||||
|
||||
Dump.run(args, "x86_64", configuration)
|
||||
application_mock.assert_called_once()
|
19
tests/ahriman/application/handlers/test_handler_rebuild.py
Normal file
19
tests/ahriman/application/handlers/test_handler_rebuild.py
Normal file
@ -0,0 +1,19 @@
|
||||
import argparse
|
||||
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from ahriman.application.handlers import Rebuild
|
||||
from ahriman.core.configuration import Configuration
|
||||
|
||||
|
||||
def test_run(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must run command
|
||||
"""
|
||||
mocker.patch("pathlib.Path.mkdir")
|
||||
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)
|
||||
application_packages_mock.assert_called_once()
|
||||
application_mock.assert_called_once()
|
18
tests/ahriman/application/handlers/test_handler_remove.py
Normal file
18
tests/ahriman/application/handlers/test_handler_remove.py
Normal file
@ -0,0 +1,18 @@
|
||||
import argparse
|
||||
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from ahriman.application.handlers import Remove
|
||||
from ahriman.core.configuration import Configuration
|
||||
|
||||
|
||||
def test_run(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must run command
|
||||
"""
|
||||
args.package = []
|
||||
mocker.patch("pathlib.Path.mkdir")
|
||||
application_mock = mocker.patch("ahriman.application.application.Application.remove")
|
||||
|
||||
Remove.run(args, "x86_64", configuration)
|
||||
application_mock.assert_called_once()
|
18
tests/ahriman/application/handlers/test_handler_report.py
Normal file
18
tests/ahriman/application/handlers/test_handler_report.py
Normal file
@ -0,0 +1,18 @@
|
||||
import argparse
|
||||
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from ahriman.application.handlers import Report
|
||||
from ahriman.core.configuration import Configuration
|
||||
|
||||
|
||||
def test_run(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must run command
|
||||
"""
|
||||
args.target = []
|
||||
mocker.patch("pathlib.Path.mkdir")
|
||||
application_mock = mocker.patch("ahriman.application.application.Application.report")
|
||||
|
||||
Report.run(args, "x86_64", configuration)
|
||||
application_mock.assert_called_once()
|
22
tests/ahriman/application/handlers/test_handler_status.py
Normal file
22
tests/ahriman/application/handlers/test_handler_status.py
Normal file
@ -0,0 +1,22 @@
|
||||
import argparse
|
||||
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from ahriman.application.handlers import Status
|
||||
from ahriman.core.configuration import Configuration
|
||||
|
||||
|
||||
def test_run(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must run command
|
||||
"""
|
||||
args.ahriman = True
|
||||
args.package = []
|
||||
args.without_dependencies = False
|
||||
mocker.patch("pathlib.Path.mkdir")
|
||||
application_mock = mocker.patch("ahriman.core.status.client.Client.get_self")
|
||||
packages_mock = mocker.patch("ahriman.core.status.client.Client.get")
|
||||
|
||||
Status.run(args, "x86_64", configuration)
|
||||
application_mock.assert_called_once()
|
||||
packages_mock.assert_called_once()
|
18
tests/ahriman/application/handlers/test_handler_sync.py
Normal file
18
tests/ahriman/application/handlers/test_handler_sync.py
Normal file
@ -0,0 +1,18 @@
|
||||
import argparse
|
||||
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from ahriman.application.handlers import Sync
|
||||
from ahriman.core.configuration import Configuration
|
||||
|
||||
|
||||
def test_run(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must run command
|
||||
"""
|
||||
args.target = []
|
||||
mocker.patch("pathlib.Path.mkdir")
|
||||
application_mock = mocker.patch("ahriman.application.application.Application.sync")
|
||||
|
||||
Sync.run(args, "x86_64", configuration)
|
||||
application_mock.assert_called_once()
|
40
tests/ahriman/application/handlers/test_handler_update.py
Normal file
40
tests/ahriman/application/handlers/test_handler_update.py
Normal file
@ -0,0 +1,40 @@
|
||||
import argparse
|
||||
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from ahriman.application.handlers import Update
|
||||
from ahriman.core.configuration import Configuration
|
||||
|
||||
|
||||
def test_run(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must run command
|
||||
"""
|
||||
args.package = []
|
||||
args.dry_run = False
|
||||
args.no_aur = False
|
||||
args.no_manual = False
|
||||
args.no_vcs = False
|
||||
mocker.patch("pathlib.Path.mkdir")
|
||||
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)
|
||||
application_mock.assert_called_once()
|
||||
updates_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_run_dry_run(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must run simplified command
|
||||
"""
|
||||
args.package = []
|
||||
args.dry_run = True
|
||||
args.no_aur = False
|
||||
args.no_manual = False
|
||||
args.no_vcs = False
|
||||
mocker.patch("pathlib.Path.mkdir")
|
||||
updates_mock = mocker.patch("ahriman.application.application.Application.get_updates")
|
||||
|
||||
Update.run(args, "x86_64", configuration)
|
||||
updates_mock.assert_called_once()
|
19
tests/ahriman/application/handlers/test_handler_web.py
Normal file
19
tests/ahriman/application/handlers/test_handler_web.py
Normal file
@ -0,0 +1,19 @@
|
||||
import argparse
|
||||
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from ahriman.application.handlers import Web
|
||||
from ahriman.core.configuration import Configuration
|
||||
|
||||
|
||||
def test_run(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must run command
|
||||
"""
|
||||
mocker.patch("pathlib.Path.mkdir")
|
||||
setup_mock = mocker.patch("ahriman.web.web.setup_service")
|
||||
run_mock = mocker.patch("ahriman.web.web.run_server")
|
||||
|
||||
Web.run(args, "x86_64", configuration)
|
||||
setup_mock.assert_called_once()
|
||||
run_mock.assert_called_once()
|
55
tests/ahriman/application/test_ahriman.py
Normal file
55
tests/ahriman/application/test_ahriman.py
Normal file
@ -0,0 +1,55 @@
|
||||
import argparse
|
||||
|
||||
|
||||
def test_parser(parser: argparse.ArgumentParser) -> None:
|
||||
"""
|
||||
must parse valid command line
|
||||
"""
|
||||
parser.parse_args(["-a", "x86_64", "config"])
|
||||
|
||||
|
||||
def test_multiple_architectures(parser: argparse.ArgumentParser) -> None:
|
||||
"""
|
||||
must accept multiple architectures
|
||||
"""
|
||||
args = parser.parse_args(["-a", "x86_64", "-a", "i686", "config"])
|
||||
assert len(args.architecture) == 2
|
||||
|
||||
|
||||
def test_subparsers_check(parser: argparse.ArgumentParser) -> None:
|
||||
"""
|
||||
check command must imply no_aur, no_manual and dry_run
|
||||
"""
|
||||
args = parser.parse_args(["-a", "x86_64", "check"])
|
||||
assert not args.no_aur
|
||||
assert args.no_manual
|
||||
assert args.dry_run
|
||||
|
||||
|
||||
def test_subparsers_config(parser: argparse.ArgumentParser) -> None:
|
||||
"""
|
||||
config command must imply lock, no_report and unsafe
|
||||
"""
|
||||
args = parser.parse_args(["-a", "x86_64", "config"])
|
||||
assert args.lock is None
|
||||
assert args.no_report
|
||||
assert args.unsafe
|
||||
|
||||
|
||||
def test_subparsers_status(parser: argparse.ArgumentParser) -> None:
|
||||
"""
|
||||
status command must imply lock, no_report and unsafe
|
||||
"""
|
||||
args = parser.parse_args(["-a", "x86_64", "status"])
|
||||
assert args.lock is None
|
||||
assert args.no_report
|
||||
assert args.unsafe
|
||||
|
||||
|
||||
def test_subparsers_web(parser: argparse.ArgumentParser) -> None:
|
||||
"""
|
||||
web command must imply lock and no_report
|
||||
"""
|
||||
args = parser.parse_args(["-a", "x86_64", "web"])
|
||||
assert args.lock is None
|
||||
assert args.no_report
|
237
tests/ahriman/application/test_application.py
Normal file
237
tests/ahriman/application/test_application.py
Normal file
@ -0,0 +1,237 @@
|
||||
from pytest_mock import MockerFixture
|
||||
from unittest import mock
|
||||
|
||||
from ahriman.application.application import Application
|
||||
from ahriman.core.tree import Leaf, Tree
|
||||
from ahriman.models.package import Package
|
||||
|
||||
|
||||
def test_finalize(application: Application, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must report and sync at the last
|
||||
"""
|
||||
report_mock = mocker.patch("ahriman.application.application.Application.report")
|
||||
sync_mock = mocker.patch("ahriman.application.application.Application.sync")
|
||||
|
||||
application._finalize()
|
||||
report_mock.assert_called_once()
|
||||
sync_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_get_updates_all(application: Application, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must get updates for all
|
||||
"""
|
||||
updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur")
|
||||
updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
|
||||
|
||||
application.get_updates([], no_aur=False, no_manual=False, no_vcs=False, log_fn=print)
|
||||
updates_aur_mock.assert_called_with([], False)
|
||||
updates_manual_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_get_updates_disabled(application: Application, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must get updates without anything
|
||||
"""
|
||||
updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur")
|
||||
updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
|
||||
|
||||
application.get_updates([], no_aur=True, no_manual=True, no_vcs=False, log_fn=print)
|
||||
updates_aur_mock.assert_not_called()
|
||||
updates_manual_mock.assert_not_called()
|
||||
|
||||
|
||||
def test_get_updates_no_aur(application: Application, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must get updates without aur
|
||||
"""
|
||||
updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur")
|
||||
updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
|
||||
|
||||
application.get_updates([], no_aur=True, no_manual=False, no_vcs=False, log_fn=print)
|
||||
updates_aur_mock.assert_not_called()
|
||||
updates_manual_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_get_updates_no_manual(application: Application, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must get updates without manual
|
||||
"""
|
||||
updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur")
|
||||
updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
|
||||
|
||||
application.get_updates([], no_aur=False, no_manual=True, no_vcs=False, log_fn=print)
|
||||
updates_aur_mock.assert_called_with([], False)
|
||||
updates_manual_mock.assert_not_called()
|
||||
|
||||
|
||||
def test_get_updates_no_vcs(application: Application, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must get updates without VCS
|
||||
"""
|
||||
updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur")
|
||||
updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
|
||||
|
||||
application.get_updates([], no_aur=False, no_manual=False, no_vcs=True, log_fn=print)
|
||||
updates_aur_mock.assert_called_with([], True)
|
||||
updates_manual_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_get_updates_with_filter(application: Application, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must get updates without VCS
|
||||
"""
|
||||
updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur")
|
||||
updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
|
||||
|
||||
application.get_updates(["filter"], no_aur=False, no_manual=False, no_vcs=False, log_fn=print)
|
||||
updates_aur_mock.assert_called_with(["filter"], False)
|
||||
updates_manual_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_add_directory(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must add packages from directory
|
||||
"""
|
||||
mocker.patch("ahriman.application.application.Application._known_packages", return_value=set())
|
||||
mocker.patch("pathlib.Path.is_dir", return_value=True)
|
||||
iterdir_mock = mocker.patch("pathlib.Path.iterdir",
|
||||
return_value=[package.filepath for package in package_ahriman.packages.values()])
|
||||
move_mock = mocker.patch("shutil.move")
|
||||
|
||||
application.add([package_ahriman.base], False)
|
||||
iterdir_mock.assert_called_once()
|
||||
move_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_add_manual(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must add package from AUR
|
||||
"""
|
||||
mocker.patch("ahriman.application.application.Application._known_packages", return_value=set())
|
||||
mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman)
|
||||
fetch_mock = mocker.patch("ahriman.core.build_tools.task.Task.fetch")
|
||||
|
||||
application.add([package_ahriman.base], True)
|
||||
fetch_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_add_manual_with_dependencies(application: Application, package_ahriman: Package,
|
||||
mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must add package from AUR with dependencies
|
||||
"""
|
||||
mocker.patch("ahriman.application.application.Application._known_packages", return_value=set())
|
||||
mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman)
|
||||
mocker.patch("ahriman.core.build_tools.task.Task.fetch")
|
||||
dependencies_mock = mocker.patch("ahriman.models.package.Package.dependencies")
|
||||
|
||||
application.add([package_ahriman.base], False)
|
||||
dependencies_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_add_package(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must add package from archive
|
||||
"""
|
||||
mocker.patch("ahriman.application.application.Application._known_packages", return_value=set())
|
||||
mocker.patch("pathlib.Path.is_file", return_value=True)
|
||||
move_mock = mocker.patch("shutil.move")
|
||||
|
||||
application.add([package_ahriman.base], False)
|
||||
move_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_clean_build(application: Application, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must clean build directory
|
||||
"""
|
||||
clear_mock = mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_build")
|
||||
application.clean(False, True, True, True, True)
|
||||
clear_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_clean_cache(application: Application, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must clean cache directory
|
||||
"""
|
||||
clear_mock = mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_cache")
|
||||
application.clean(True, False, True, True, True)
|
||||
clear_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_clean_chroot(application: Application, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must clean chroot directory
|
||||
"""
|
||||
clear_mock = mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_chroot")
|
||||
application.clean(True, True, False, True, True)
|
||||
clear_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_clean_manual(application: Application, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must clean manual directory
|
||||
"""
|
||||
clear_mock = mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_manual")
|
||||
application.clean(True, True, True, False, True)
|
||||
clear_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_clean_packages(application: Application, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must clean packages directory
|
||||
"""
|
||||
clear_mock = mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_packages")
|
||||
application.clean(True, True, True, True, False)
|
||||
clear_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_remove(application: Application, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must remove package
|
||||
"""
|
||||
executor_mock = mocker.patch("ahriman.core.repository.executor.Executor.process_remove")
|
||||
finalize_mock = mocker.patch("ahriman.application.application.Application._finalize")
|
||||
|
||||
application.remove([])
|
||||
executor_mock.assert_called_once()
|
||||
finalize_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_report(application: Application, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must generate report
|
||||
"""
|
||||
executor_mock = mocker.patch("ahriman.core.repository.executor.Executor.process_report")
|
||||
application.report(None)
|
||||
executor_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_sync(application: Application, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must sync to remote
|
||||
"""
|
||||
executor_mock = mocker.patch("ahriman.core.repository.executor.Executor.process_sync")
|
||||
application.sync(None)
|
||||
executor_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_update(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must process package updates
|
||||
"""
|
||||
paths = [package.filepath for package in package_ahriman.packages.values()]
|
||||
tree = Tree([Leaf(package_ahriman, set())])
|
||||
|
||||
mocker.patch("ahriman.core.tree.Tree.load", return_value=tree)
|
||||
mocker.patch("ahriman.core.repository.repository.Repository.packages_built", return_value=[])
|
||||
build_mock = mocker.patch("ahriman.core.repository.executor.Executor.process_build", return_value=paths)
|
||||
update_mock = mocker.patch("ahriman.core.repository.executor.Executor.process_update")
|
||||
finalize_mock = mocker.patch("ahriman.application.application.Application._finalize")
|
||||
|
||||
application.update([package_ahriman])
|
||||
build_mock.assert_called_once()
|
||||
update_mock.assert_has_calls([mock.call([]), mock.call(paths)])
|
||||
finalize_mock.assert_has_calls([mock.call(), mock.call()])
|
151
tests/ahriman/application/test_lock.py
Normal file
151
tests/ahriman/application/test_lock.py
Normal file
@ -0,0 +1,151 @@
|
||||
import pytest
|
||||
import tempfile
|
||||
|
||||
from pathlib import Path
|
||||
from pytest_mock import MockerFixture
|
||||
from unittest import mock
|
||||
|
||||
from ahriman.application.lock import Lock
|
||||
from ahriman.core.exceptions import DuplicateRun, UnsafeRun
|
||||
from ahriman.models.build_status import BuildStatusEnum
|
||||
|
||||
|
||||
def test_enter(lock: Lock, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must process with context manager
|
||||
"""
|
||||
check_user_mock = mocker.patch("ahriman.application.lock.Lock.check_user")
|
||||
remove_mock = mocker.patch("ahriman.application.lock.Lock.remove")
|
||||
create_mock = mocker.patch("ahriman.application.lock.Lock.create")
|
||||
update_status_mock = mocker.patch("ahriman.core.status.client.Client.update_self")
|
||||
|
||||
with lock:
|
||||
pass
|
||||
check_user_mock.assert_called_once()
|
||||
remove_mock.assert_called_once()
|
||||
create_mock.assert_called_once()
|
||||
update_status_mock.assert_has_calls([
|
||||
mock.call(BuildStatusEnum.Building),
|
||||
mock.call(BuildStatusEnum.Success)
|
||||
])
|
||||
|
||||
|
||||
def test_exit_with_exception(lock: Lock, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must process with context manager in case if exception raised
|
||||
"""
|
||||
mocker.patch("ahriman.application.lock.Lock.check_user")
|
||||
mocker.patch("ahriman.application.lock.Lock.remove")
|
||||
mocker.patch("ahriman.application.lock.Lock.create")
|
||||
update_status_mock = mocker.patch("ahriman.core.status.client.Client.update_self")
|
||||
|
||||
with pytest.raises(Exception):
|
||||
with lock:
|
||||
raise Exception()
|
||||
update_status_mock.assert_has_calls([
|
||||
mock.call(BuildStatusEnum.Building),
|
||||
mock.call(BuildStatusEnum.Failed)
|
||||
])
|
||||
|
||||
|
||||
def test_check_user(lock: Lock, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must check user correctly
|
||||
"""
|
||||
stat = Path.cwd().stat()
|
||||
mocker.patch("pathlib.Path.stat", return_value=stat)
|
||||
mocker.patch("os.getuid", return_value=stat.st_uid)
|
||||
|
||||
lock.check_user()
|
||||
|
||||
|
||||
def test_check_user_exception(lock: Lock, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must raise exception if user differs
|
||||
"""
|
||||
stat = Path.cwd().stat()
|
||||
mocker.patch("pathlib.Path.stat")
|
||||
mocker.patch("os.getuid", return_value=stat.st_uid + 1)
|
||||
|
||||
with pytest.raises(UnsafeRun):
|
||||
lock.check_user()
|
||||
|
||||
|
||||
def test_check_user_unsafe(lock: Lock) -> None:
|
||||
"""
|
||||
must skip user check if unsafe flag set
|
||||
"""
|
||||
lock.unsafe = True
|
||||
lock.check_user()
|
||||
|
||||
|
||||
def test_create(lock: Lock) -> None:
|
||||
"""
|
||||
must create lock
|
||||
"""
|
||||
lock.path = Path(tempfile.mktemp())
|
||||
|
||||
lock.create()
|
||||
assert lock.path.is_file()
|
||||
lock.path.unlink()
|
||||
|
||||
|
||||
def test_create_exception(lock: Lock) -> None:
|
||||
"""
|
||||
must raise exception if file already exists
|
||||
"""
|
||||
lock.path = Path(tempfile.mktemp())
|
||||
lock.path.touch()
|
||||
|
||||
with pytest.raises(DuplicateRun):
|
||||
lock.create()
|
||||
lock.path.unlink()
|
||||
|
||||
|
||||
def test_create_skip(lock: Lock, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must skip creating if no file set
|
||||
"""
|
||||
touch_mock = mocker.patch("pathlib.Path.touch")
|
||||
lock.create()
|
||||
touch_mock.assert_not_called()
|
||||
|
||||
|
||||
def test_create_unsafe(lock: Lock) -> None:
|
||||
"""
|
||||
must not raise exception if force flag set
|
||||
"""
|
||||
lock.force = True
|
||||
lock.path = Path(tempfile.mktemp())
|
||||
lock.path.touch()
|
||||
|
||||
lock.create()
|
||||
lock.path.unlink()
|
||||
|
||||
|
||||
def test_remove(lock: Lock) -> None:
|
||||
"""
|
||||
must remove lock file
|
||||
"""
|
||||
lock.path = Path(tempfile.mktemp())
|
||||
lock.path.touch()
|
||||
|
||||
lock.remove()
|
||||
assert not lock.path.is_file()
|
||||
|
||||
|
||||
def test_remove_missing(lock: Lock) -> None:
|
||||
"""
|
||||
must not fail on lock removal if file is missing
|
||||
"""
|
||||
lock.path = Path(tempfile.mktemp())
|
||||
lock.remove()
|
||||
|
||||
|
||||
def test_remove_skip(lock: Lock, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must skip removal if no file set
|
||||
"""
|
||||
unlink_mock = mocker.patch("pathlib.Path.unlink")
|
||||
lock.remove()
|
||||
unlink_mock.assert_not_called()
|
95
tests/ahriman/conftest.py
Normal file
95
tests/ahriman/conftest.py
Normal file
@ -0,0 +1,95 @@
|
||||
import pytest
|
||||
|
||||
from pathlib import Path
|
||||
from pytest_mock import MockerFixture
|
||||
from typing import Any, Type, TypeVar
|
||||
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.status.watcher import Watcher
|
||||
from ahriman.models.package import Package
|
||||
from ahriman.models.package_desciption import PackageDescription
|
||||
from ahriman.models.repository_paths import RepositoryPaths
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
# helpers
|
||||
# https://stackoverflow.com/a/21611963
|
||||
@pytest.helpers.register
|
||||
def anyvar(cls: Type[T], strict: bool = False) -> T:
|
||||
class AnyVar(cls):
|
||||
def __eq__(self, other: Any) -> bool:
|
||||
return not strict or isinstance(other, cls)
|
||||
return AnyVar()
|
||||
|
||||
|
||||
# generic fixtures
|
||||
@pytest.fixture
|
||||
def configuration(resource_path_root: Path) -> Configuration:
|
||||
path = resource_path_root / "core" / "ahriman.ini"
|
||||
return Configuration.from_path(path=path, logfile=False)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def package_ahriman(package_description_ahriman: PackageDescription) -> Package:
|
||||
packages = {"ahriman": package_description_ahriman}
|
||||
return Package(
|
||||
base="ahriman",
|
||||
version="0.12.1-1",
|
||||
aur_url="https://aur.archlinux.org",
|
||||
packages=packages)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def package_python_schedule(
|
||||
package_description_python_schedule: PackageDescription,
|
||||
package_description_python2_schedule: PackageDescription) -> Package:
|
||||
packages = {
|
||||
"python-schedule": package_description_python_schedule,
|
||||
"python2-schedule": package_description_python2_schedule
|
||||
}
|
||||
return Package(
|
||||
base="python-schedule",
|
||||
version="1.0.0-2",
|
||||
aur_url="https://aur.archlinux.org",
|
||||
packages=packages)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def package_description_ahriman() -> PackageDescription:
|
||||
return PackageDescription(
|
||||
archive_size=4200,
|
||||
build_date=42,
|
||||
filename="ahriman-0.12.1-1-any.pkg.tar.zst",
|
||||
installed_size=4200000)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def package_description_python_schedule() -> PackageDescription:
|
||||
return PackageDescription(
|
||||
archive_size=4201,
|
||||
build_date=421,
|
||||
filename="python-schedule-1.0.0-2-any.pkg.tar.zst",
|
||||
installed_size=4200001)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def package_description_python2_schedule() -> PackageDescription:
|
||||
return PackageDescription(
|
||||
archive_size=4202,
|
||||
build_date=422,
|
||||
filename="python2-schedule-1.0.0-2-any.pkg.tar.zst",
|
||||
installed_size=4200002)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def repository_paths(configuration: Configuration) -> RepositoryPaths:
|
||||
return RepositoryPaths(
|
||||
architecture="x86_64",
|
||||
root=configuration.getpath("repository", "root"))
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def watcher(configuration: Configuration, mocker: MockerFixture) -> Watcher:
|
||||
mocker.patch("pathlib.Path.mkdir")
|
||||
return Watcher("x86_64", configuration)
|
10
tests/ahriman/core/alpm/test_pacman.py
Normal file
10
tests/ahriman/core/alpm/test_pacman.py
Normal file
@ -0,0 +1,10 @@
|
||||
from ahriman.core.alpm.pacman import Pacman
|
||||
|
||||
|
||||
def test_all_packages(pacman: Pacman) -> None:
|
||||
"""
|
||||
package list must not be empty
|
||||
"""
|
||||
packages = pacman.all_packages()
|
||||
assert packages
|
||||
assert "pacman" in packages
|
47
tests/ahriman/core/alpm/test_repo.py
Normal file
47
tests/ahriman/core/alpm/test_repo.py
Normal file
@ -0,0 +1,47 @@
|
||||
import pytest
|
||||
|
||||
from pathlib import Path
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from ahriman.core.alpm.repo import Repo
|
||||
|
||||
|
||||
def test_repo_path(repo: Repo) -> None:
|
||||
"""
|
||||
name must be something like archive name
|
||||
"""
|
||||
assert repo.repo_path.name.endswith("db.tar.gz")
|
||||
|
||||
|
||||
def test_repo_add(repo: Repo, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must call repo-add on package addition
|
||||
"""
|
||||
check_output_mock = mocker.patch("ahriman.core.alpm.repo.Repo._check_output")
|
||||
|
||||
repo.add(Path("path"))
|
||||
check_output_mock.assert_called_once()
|
||||
assert check_output_mock.call_args[0][0] == "repo-add"
|
||||
|
||||
|
||||
def test_repo_remove(repo: Repo, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must call repo-remove on package addition
|
||||
"""
|
||||
mocker.patch("pathlib.Path.glob", return_value=[])
|
||||
check_output_mock = mocker.patch("ahriman.core.alpm.repo.Repo._check_output")
|
||||
|
||||
repo.remove("package", Path("package.pkg.tar.xz"))
|
||||
check_output_mock.assert_called_once()
|
||||
assert check_output_mock.call_args[0][0] == "repo-remove"
|
||||
|
||||
|
||||
def test_repo_remove_fail_no_file(repo: Repo, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must fail on missing file
|
||||
"""
|
||||
mocker.patch("pathlib.Path.glob", return_value=[Path("package.pkg.tar.xz")])
|
||||
mocker.patch("pathlib.Path.unlink", side_effect=FileNotFoundError())
|
||||
|
||||
with pytest.raises(FileNotFoundError):
|
||||
repo.remove("package", Path("package.pkg.tar.xz"))
|
64
tests/ahriman/core/build_tools/test_task.py
Normal file
64
tests/ahriman/core/build_tools/test_task.py
Normal file
@ -0,0 +1,64 @@
|
||||
import pytest
|
||||
import shutil
|
||||
|
||||
from pathlib import Path
|
||||
from pytest_mock import MockerFixture
|
||||
from unittest import mock
|
||||
|
||||
from ahriman.core.build_tools.task import Task
|
||||
|
||||
|
||||
def test_fetch_existing(mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must fetch new package via clone command
|
||||
"""
|
||||
mocker.patch("pathlib.Path.is_dir", return_value=True)
|
||||
check_output_mock = mocker.patch("ahriman.core.build_tools.task.Task._check_output")
|
||||
|
||||
local = Path("local")
|
||||
Task.fetch(local, "remote", "master")
|
||||
check_output_mock.assert_has_calls([
|
||||
mock.call("git", "fetch", "origin", "master",
|
||||
exception=pytest.helpers.anyvar(int),
|
||||
cwd=local, logger=pytest.helpers.anyvar(int)),
|
||||
mock.call("git", "checkout", "--force", "master",
|
||||
exception=pytest.helpers.anyvar(int),
|
||||
cwd=local, logger=pytest.helpers.anyvar(int)),
|
||||
mock.call("git", "reset", "--hard", "origin/master",
|
||||
exception=pytest.helpers.anyvar(int),
|
||||
cwd=local, logger=pytest.helpers.anyvar(int))
|
||||
])
|
||||
|
||||
|
||||
def test_fetch_new(mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must fetch new package via clone command
|
||||
"""
|
||||
mocker.patch("pathlib.Path.is_dir", return_value=False)
|
||||
check_output_mock = mocker.patch("ahriman.core.build_tools.task.Task._check_output")
|
||||
|
||||
local = Path("local")
|
||||
Task.fetch(local, "remote", "master")
|
||||
check_output_mock.assert_has_calls([
|
||||
mock.call("git", "clone", "remote", str(local),
|
||||
exception=pytest.helpers.anyvar(int),
|
||||
logger=pytest.helpers.anyvar(int)),
|
||||
mock.call("git", "checkout", "--force", "master",
|
||||
exception=pytest.helpers.anyvar(int),
|
||||
cwd=local, logger=pytest.helpers.anyvar(int)),
|
||||
mock.call("git", "reset", "--hard", "origin/master",
|
||||
exception=pytest.helpers.anyvar(int),
|
||||
cwd=local, logger=pytest.helpers.anyvar(int))
|
||||
])
|
||||
|
||||
|
||||
def test_init_with_cache(task_ahriman: Task, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must copy tree instead of fetch
|
||||
"""
|
||||
mocker.patch("pathlib.Path.is_dir", return_value=True)
|
||||
mocker.patch("ahriman.core.build_tools.task.Task.fetch")
|
||||
copytree_mock = mocker.patch("shutil.copytree")
|
||||
|
||||
task_ahriman.init(None)
|
||||
copytree_mock.assert_called_once()
|
34
tests/ahriman/core/conftest.py
Normal file
34
tests/ahriman/core/conftest.py
Normal file
@ -0,0 +1,34 @@
|
||||
import pytest
|
||||
|
||||
from ahriman.core.alpm.pacman import Pacman
|
||||
from ahriman.core.alpm.repo import Repo
|
||||
from ahriman.core.build_tools.task import Task
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.tree import Leaf
|
||||
from ahriman.models.package import Package
|
||||
from ahriman.models.repository_paths import RepositoryPaths
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def leaf_ahriman(package_ahriman: Package) -> Leaf:
|
||||
return Leaf(package_ahriman, set())
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def leaf_python_schedule(package_python_schedule: Package) -> Leaf:
|
||||
return Leaf(package_python_schedule, set())
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def pacman(configuration: Configuration) -> Pacman:
|
||||
return Pacman(configuration)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def repo(configuration: Configuration, repository_paths: RepositoryPaths) -> Repo:
|
||||
return Repo(configuration.get("repository", "name"), repository_paths, [])
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def task_ahriman(package_ahriman: Package, configuration: Configuration, repository_paths: RepositoryPaths) -> Task:
|
||||
return Task(package_ahriman, "x86_64", configuration, repository_paths)
|
0
tests/ahriman/core/report/test_html.py
Normal file
0
tests/ahriman/core/report/test_html.py
Normal file
0
tests/ahriman/core/report/test_report.py
Normal file
0
tests/ahriman/core/report/test_report.py
Normal file
49
tests/ahriman/core/repository/conftest.py
Normal file
49
tests/ahriman/core/repository/conftest.py
Normal file
@ -0,0 +1,49 @@
|
||||
import pytest
|
||||
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.repository.cleaner import Cleaner
|
||||
from ahriman.core.repository.executor import Executor
|
||||
from ahriman.core.repository.properties import Properties
|
||||
from ahriman.core.repository.repository import Repository
|
||||
from ahriman.core.repository.update_handler import UpdateHandler
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def cleaner(configuration: Configuration, mocker: MockerFixture) -> Cleaner:
|
||||
mocker.patch("pathlib.Path.mkdir")
|
||||
return Cleaner("x86_64", configuration)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def executor(configuration: Configuration, mocker: MockerFixture) -> Executor:
|
||||
mocker.patch("pathlib.Path.mkdir")
|
||||
mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_build")
|
||||
mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_cache")
|
||||
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)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def repository(configuration: Configuration, mocker: MockerFixture) -> Repository:
|
||||
mocker.patch("pathlib.Path.mkdir")
|
||||
return Repository("x86_64", configuration)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def properties(configuration: Configuration) -> Properties:
|
||||
return Properties("x86_64", configuration)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def update_handler(configuration: Configuration, mocker: MockerFixture) -> UpdateHandler:
|
||||
mocker.patch("pathlib.Path.mkdir")
|
||||
mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_build")
|
||||
mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_cache")
|
||||
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)
|
68
tests/ahriman/core/repository/test_cleaner.py
Normal file
68
tests/ahriman/core/repository/test_cleaner.py
Normal file
@ -0,0 +1,68 @@
|
||||
import shutil
|
||||
|
||||
from pathlib import Path
|
||||
from pytest_mock import MockerFixture
|
||||
from unittest import mock
|
||||
|
||||
from ahriman.core.repository.cleaner import Cleaner
|
||||
|
||||
|
||||
def _mock_clear(mocker: MockerFixture) -> None:
|
||||
mocker.patch("pathlib.Path.iterdir", return_value=[Path("a"), Path("b"), Path("c")])
|
||||
mocker.patch("shutil.rmtree")
|
||||
|
||||
|
||||
def _mock_clear_check() -> None:
|
||||
shutil.rmtree.assert_has_calls([
|
||||
mock.call(Path("a")),
|
||||
mock.call(Path("b")),
|
||||
mock.call(Path("c"))
|
||||
])
|
||||
|
||||
|
||||
def test_clear_build(cleaner: Cleaner, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must remove directories with sources
|
||||
"""
|
||||
_mock_clear(mocker)
|
||||
cleaner.clear_build()
|
||||
_mock_clear_check()
|
||||
|
||||
|
||||
def test_clear_cache(cleaner: Cleaner, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must remove every cached sources
|
||||
"""
|
||||
_mock_clear(mocker)
|
||||
cleaner.clear_cache()
|
||||
_mock_clear_check()
|
||||
|
||||
|
||||
def test_clear_chroot(cleaner: Cleaner, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must clear chroot
|
||||
"""
|
||||
_mock_clear(mocker)
|
||||
cleaner.clear_chroot()
|
||||
_mock_clear_check()
|
||||
|
||||
|
||||
def test_clear_manual(cleaner: Cleaner, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must clear directory with manual packages
|
||||
"""
|
||||
_mock_clear(mocker)
|
||||
cleaner.clear_manual()
|
||||
_mock_clear_check()
|
||||
|
||||
|
||||
def test_clear_packages(cleaner: Cleaner, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must delete built packages
|
||||
"""
|
||||
mocker.patch("ahriman.core.repository.cleaner.Cleaner.packages_built",
|
||||
return_value=[Path("a"), Path("b"), Path("c")])
|
||||
mocker.patch("pathlib.Path.unlink")
|
||||
|
||||
cleaner.clear_packages()
|
||||
Path.unlink.assert_has_calls([mock.call(), mock.call(), mock.call()])
|
188
tests/ahriman/core/repository/test_executor.py
Normal file
188
tests/ahriman/core/repository/test_executor.py
Normal file
@ -0,0 +1,188 @@
|
||||
from pathlib import Path
|
||||
from pytest_mock import MockerFixture
|
||||
from unittest import mock
|
||||
|
||||
from ahriman.core.repository.executor import Executor
|
||||
from ahriman.models.package import Package
|
||||
|
||||
|
||||
def test_process_build(executor: Executor, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must run build process
|
||||
"""
|
||||
mocker.patch("ahriman.core.repository.executor.Executor.packages_built", return_value=[package_ahriman])
|
||||
mocker.patch("ahriman.core.build_tools.task.Task.build", return_value=[Path(package_ahriman.base)])
|
||||
mocker.patch("ahriman.core.build_tools.task.Task.init")
|
||||
move_mock = mocker.patch("shutil.move")
|
||||
status_client_mock = mocker.patch("ahriman.core.status.client.Client.set_building")
|
||||
|
||||
# must return list of built packages
|
||||
assert executor.process_build([package_ahriman]) == [package_ahriman]
|
||||
# must move files (once)
|
||||
move_mock.assert_called_once()
|
||||
# must update status
|
||||
status_client_mock.assert_called_once()
|
||||
# must clear directory
|
||||
from ahriman.core.repository.cleaner import Cleaner
|
||||
Cleaner.clear_build.assert_called_once()
|
||||
|
||||
|
||||
def test_process_build_failure(executor: Executor, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must run correct process failed builds
|
||||
"""
|
||||
mocker.patch("ahriman.core.repository.executor.Executor.packages_built")
|
||||
mocker.patch("ahriman.core.build_tools.task.Task.build", return_value=[Path(package_ahriman.base)])
|
||||
mocker.patch("ahriman.core.build_tools.task.Task.init")
|
||||
mocker.patch("shutil.move", side_effect=Exception())
|
||||
status_client_mock = mocker.patch("ahriman.core.status.client.Client.set_failed")
|
||||
|
||||
executor.process_build([package_ahriman])
|
||||
status_client_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_process_remove_base(executor: Executor, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must run remove process for whole base
|
||||
"""
|
||||
mocker.patch("ahriman.core.repository.executor.Executor.packages", return_value=[package_ahriman])
|
||||
repo_remove_mock = mocker.patch("ahriman.core.alpm.repo.Repo.remove")
|
||||
status_client_mock = mocker.patch("ahriman.core.status.client.Client.remove")
|
||||
|
||||
executor.process_remove([package_ahriman.base])
|
||||
# must remove via alpm wrapper
|
||||
repo_remove_mock.assert_called_once()
|
||||
# must update status
|
||||
status_client_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_process_remove_base_multiple(executor: Executor, package_python_schedule: Package,
|
||||
mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must run remove process for whole base with multiple packages
|
||||
"""
|
||||
mocker.patch("ahriman.core.repository.executor.Executor.packages", return_value=[package_python_schedule])
|
||||
repo_remove_mock = mocker.patch("ahriman.core.alpm.repo.Repo.remove")
|
||||
status_client_mock = mocker.patch("ahriman.core.status.client.Client.remove")
|
||||
|
||||
executor.process_remove([package_python_schedule.base])
|
||||
# must remove via alpm wrapper
|
||||
repo_remove_mock.assert_has_calls([
|
||||
mock.call(package, Path(props.filename))
|
||||
for package, props in package_python_schedule.packages.items()
|
||||
], any_order=True)
|
||||
# must update status
|
||||
status_client_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_process_remove_base_single(executor: Executor, package_python_schedule: Package,
|
||||
mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must run remove process for single package in base
|
||||
"""
|
||||
mocker.patch("ahriman.core.repository.executor.Executor.packages", return_value=[package_python_schedule])
|
||||
repo_remove_mock = mocker.patch("ahriman.core.alpm.repo.Repo.remove")
|
||||
status_client_mock = mocker.patch("ahriman.core.status.client.Client.remove")
|
||||
|
||||
executor.process_remove(["python2-schedule"])
|
||||
# must remove via alpm wrapper
|
||||
repo_remove_mock.assert_called_once()
|
||||
# must not update status
|
||||
status_client_mock.assert_not_called()
|
||||
|
||||
|
||||
def test_process_remove_nothing(executor: Executor, package_ahriman: Package, package_python_schedule: Package,
|
||||
mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must not remove anything if it was not requested
|
||||
"""
|
||||
mocker.patch("ahriman.core.repository.executor.Executor.packages", return_value=[package_ahriman])
|
||||
repo_remove_mock = mocker.patch("ahriman.core.alpm.repo.Repo.remove")
|
||||
|
||||
executor.process_remove([package_python_schedule.base])
|
||||
repo_remove_mock.assert_not_called()
|
||||
|
||||
|
||||
def test_process_report_auto(executor: Executor, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must process report in auto mode if no targets supplied
|
||||
"""
|
||||
config_getlist_mock = mocker.patch("ahriman.core.configuration.Configuration.getlist")
|
||||
|
||||
executor.process_report(None)
|
||||
config_getlist_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_process_sync_auto(executor: Executor, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must process sync in auto mode if no targets supplied
|
||||
"""
|
||||
config_getlist_mock = mocker.patch("ahriman.core.configuration.Configuration.getlist")
|
||||
|
||||
executor.process_sync(None)
|
||||
config_getlist_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_process_update(executor: Executor, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must run update process
|
||||
"""
|
||||
mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman)
|
||||
move_mock = mocker.patch("shutil.move")
|
||||
repo_add_mock = mocker.patch("ahriman.core.alpm.repo.Repo.add")
|
||||
sign_package_mock = mocker.patch("ahriman.core.sign.gpg.GPG.sign_package", side_effect=lambda fn, _: [fn])
|
||||
status_client_mock = mocker.patch("ahriman.core.status.client.Client.set_success")
|
||||
|
||||
# must return complete
|
||||
assert executor.process_update([Path(package.filename) for package in package_ahriman.packages.values()])
|
||||
# must move files (once)
|
||||
move_mock.assert_called_once()
|
||||
# must sign package
|
||||
sign_package_mock.assert_called_once()
|
||||
# must add package
|
||||
repo_add_mock.assert_called_once()
|
||||
# must update status
|
||||
status_client_mock.assert_called_once()
|
||||
# must clear directory
|
||||
from ahriman.core.repository.cleaner import Cleaner
|
||||
Cleaner.clear_packages.assert_called_once()
|
||||
|
||||
|
||||
def test_process_update_group(executor: Executor, package_python_schedule: Package,
|
||||
mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must group single packages under one base
|
||||
"""
|
||||
mocker.patch("shutil.move")
|
||||
mocker.patch("ahriman.models.package.Package.load", return_value=package_python_schedule)
|
||||
repo_add_mock = mocker.patch("ahriman.core.alpm.repo.Repo.add")
|
||||
status_client_mock = mocker.patch("ahriman.core.status.client.Client.set_success")
|
||||
|
||||
executor.process_update([Path(package.filename) for package in package_python_schedule.packages.values()])
|
||||
repo_add_mock.assert_has_calls([
|
||||
mock.call(executor.paths.repository / package.filename)
|
||||
for package in package_python_schedule.packages.values()
|
||||
], any_order=True)
|
||||
status_client_mock.assert_called_with(package_python_schedule)
|
||||
|
||||
|
||||
def test_process_update_failed(executor: Executor, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must process update for failed package
|
||||
"""
|
||||
mocker.patch("shutil.move", side_effect=Exception())
|
||||
mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman)
|
||||
status_client_mock = mocker.patch("ahriman.core.status.client.Client.set_failed")
|
||||
|
||||
executor.process_update([Path(package.filename) for package in package_ahriman.packages.values()])
|
||||
status_client_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_process_update_failed_on_load(executor: Executor, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must process update even with failed package load
|
||||
"""
|
||||
mocker.patch("shutil.move")
|
||||
mocker.patch("ahriman.models.package.Package.load", side_effect=Exception())
|
||||
|
||||
assert executor.process_update([Path(package.filename) for package in package_ahriman.packages.values()])
|
14
tests/ahriman/core/repository/test_properties.py
Normal file
14
tests/ahriman/core/repository/test_properties.py
Normal file
@ -0,0 +1,14 @@
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.repository.properties import Properties
|
||||
|
||||
|
||||
def test_create_tree_on_load(configuration: Configuration, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must create tree on load
|
||||
"""
|
||||
create_tree_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.create_tree")
|
||||
Properties("x86_64", configuration)
|
||||
|
||||
create_tree_mock.assert_called_once()
|
33
tests/ahriman/core/repository/test_repository.py
Normal file
33
tests/ahriman/core/repository/test_repository.py
Normal file
@ -0,0 +1,33 @@
|
||||
from pathlib import Path
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from ahriman.core.repository.repository import Repository
|
||||
from ahriman.models.package import Package
|
||||
|
||||
|
||||
def test_packages(package_ahriman: Package, package_python_schedule: Package,
|
||||
repository: Repository, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must return all packages grouped by package base
|
||||
"""
|
||||
single_packages = [
|
||||
Package(base=package_python_schedule.base,
|
||||
version=package_python_schedule.version,
|
||||
aur_url=package_python_schedule.aur_url,
|
||||
packages={package: props})
|
||||
for package, props in package_python_schedule.packages.items()
|
||||
] + [package_ahriman]
|
||||
|
||||
mocker.patch("pathlib.Path.iterdir",
|
||||
return_value=[Path("a.pkg.tar.xz"), Path("b.pkg.tar.xz"), Path("c.pkg.tar.xz")])
|
||||
mocker.patch("ahriman.models.package.Package.load", side_effect=single_packages)
|
||||
|
||||
packages = repository.packages()
|
||||
assert len(packages) == 2
|
||||
assert {package.base for package in packages} == {package_ahriman.base, package_python_schedule.base}
|
||||
|
||||
archives = sum([list(package.packages.keys()) for package in packages], start=[])
|
||||
assert len(archives) == 3
|
||||
expected = set(package_ahriman.packages.keys())
|
||||
expected.update(package_python_schedule.packages.keys())
|
||||
assert set(archives) == expected
|
124
tests/ahriman/core/repository/test_update_handler.py
Normal file
124
tests/ahriman/core/repository/test_update_handler.py
Normal file
@ -0,0 +1,124 @@
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from ahriman.core.repository.update_handler import UpdateHandler
|
||||
from ahriman.models.package import Package
|
||||
|
||||
|
||||
def test_updates_aur(update_handler: UpdateHandler, package_ahriman: Package,
|
||||
mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must provide updates with status updates
|
||||
"""
|
||||
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[package_ahriman])
|
||||
mocker.patch("ahriman.models.package.Package.is_outdated", return_value=True)
|
||||
mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman)
|
||||
status_client_mock = mocker.patch("ahriman.core.status.client.Client.set_pending")
|
||||
|
||||
assert update_handler.updates_aur([], False) == [package_ahriman]
|
||||
status_client_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_updates_aur_failed(update_handler: UpdateHandler, package_ahriman: Package,
|
||||
mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must update status via client for failed load
|
||||
"""
|
||||
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[package_ahriman])
|
||||
mocker.patch("ahriman.models.package.Package.load", side_effect=Exception())
|
||||
status_client_mock = mocker.patch("ahriman.core.status.client.Client.set_failed")
|
||||
|
||||
update_handler.updates_aur([], False)
|
||||
status_client_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_updates_aur_filter(update_handler: UpdateHandler, package_ahriman: Package, package_python_schedule: Package,
|
||||
mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must provide updates only for filtered packages
|
||||
"""
|
||||
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages",
|
||||
return_value=[package_ahriman, package_python_schedule])
|
||||
mocker.patch("ahriman.models.package.Package.is_outdated", return_value=True)
|
||||
package_load_mock = mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman)
|
||||
|
||||
assert update_handler.updates_aur([package_ahriman.base], False) == [package_ahriman]
|
||||
package_load_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_updates_aur_ignore(update_handler: UpdateHandler, package_ahriman: Package,
|
||||
mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must skip ignore packages
|
||||
"""
|
||||
mocker.patch("ahriman.core.configuration.Configuration.getlist", return_value=[package_ahriman.base])
|
||||
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[package_ahriman])
|
||||
package_load_mock = mocker.patch("ahriman.models.package.Package.load")
|
||||
|
||||
update_handler.updates_aur([], False)
|
||||
package_load_mock.assert_not_called()
|
||||
|
||||
|
||||
def test_updates_aur_ignore_vcs(update_handler: UpdateHandler, package_ahriman: Package,
|
||||
mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must skip VCS packages check if requested
|
||||
"""
|
||||
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[package_ahriman])
|
||||
mocker.patch("ahriman.models.package.Package.is_vcs", return_value=True)
|
||||
package_is_outdated_mock = mocker.patch("ahriman.models.package.Package.is_outdated")
|
||||
|
||||
update_handler.updates_aur([], True)
|
||||
package_is_outdated_mock.assert_not_called()
|
||||
|
||||
|
||||
def test_updates_manual_clear(update_handler: UpdateHandler, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
requesting manual updates must clear packages directory
|
||||
"""
|
||||
mocker.patch("pathlib.Path.iterdir", return_value=[])
|
||||
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages")
|
||||
|
||||
update_handler.updates_manual()
|
||||
|
||||
from ahriman.core.repository.cleaner import Cleaner
|
||||
Cleaner.clear_manual.assert_called_once()
|
||||
|
||||
|
||||
def test_updates_manual_status_known(update_handler: UpdateHandler, package_ahriman: Package,
|
||||
mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must create record for known package via reporter
|
||||
"""
|
||||
mocker.patch("pathlib.Path.iterdir", return_value=[package_ahriman.base])
|
||||
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[package_ahriman])
|
||||
mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman)
|
||||
status_client_mock = mocker.patch("ahriman.core.status.client.Client.set_pending")
|
||||
|
||||
update_handler.updates_manual()
|
||||
status_client_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_updates_manual_status_unknown(update_handler: UpdateHandler, package_ahriman: Package,
|
||||
mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must create record for unknown package via reporter
|
||||
"""
|
||||
mocker.patch("pathlib.Path.iterdir", return_value=[package_ahriman.base])
|
||||
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[])
|
||||
mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman)
|
||||
status_client_mock = mocker.patch("ahriman.core.status.client.Client.set_unknown")
|
||||
|
||||
update_handler.updates_manual()
|
||||
status_client_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_updates_manual_with_failures(update_handler: UpdateHandler, package_ahriman: Package,
|
||||
mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must process through the packages with failure
|
||||
"""
|
||||
mocker.patch("pathlib.Path.iterdir", return_value=[package_ahriman.base])
|
||||
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[])
|
||||
mocker.patch("ahriman.models.package.Package.load", side_effect=Exception())
|
||||
|
||||
assert update_handler.updates_manual() == []
|
9
tests/ahriman/core/sign/conftest.py
Normal file
9
tests/ahriman/core/sign/conftest.py
Normal file
@ -0,0 +1,9 @@
|
||||
import pytest
|
||||
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.sign.gpg import GPG
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def gpg(configuration: Configuration) -> GPG:
|
||||
return GPG("x86_64", configuration)
|
61
tests/ahriman/core/sign/gpg.py
Normal file
61
tests/ahriman/core/sign/gpg.py
Normal file
@ -0,0 +1,61 @@
|
||||
from pathlib import Path
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from ahriman.core.sign.gpg import GPG
|
||||
from ahriman.models.sign_settings import SignSettings
|
||||
|
||||
|
||||
def test_repository_sign_args(gpg: GPG) -> None:
|
||||
"""
|
||||
must generate correct sign args
|
||||
"""
|
||||
gpg.target = {SignSettings.SignRepository}
|
||||
assert gpg.repository_sign_args
|
||||
|
||||
|
||||
def test_sign_package(gpg: GPG, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must sign package
|
||||
"""
|
||||
result = [Path("a"), Path("a.sig")]
|
||||
process_mock = mocker.patch("ahriman.core.sign.gpg.process", return_value=result)
|
||||
|
||||
for target in ({SignSettings.SignPackages}, {SignSettings.SignPackages, SignSettings.SignRepository}):
|
||||
gpg.target = target
|
||||
assert gpg.sign_package(Path("a"), "a") == result
|
||||
process_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_sign_package_skip(gpg: GPG, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must not sign package if it is not set
|
||||
"""
|
||||
process_mock = mocker.patch("ahriman.core.sign.gpg.process")
|
||||
|
||||
for target in ({}, {SignSettings.SignRepository}):
|
||||
gpg.target = target
|
||||
process_mock.assert_not_called()
|
||||
|
||||
|
||||
def test_sign_repository(gpg: GPG, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must sign repository
|
||||
"""
|
||||
result = [Path("a"), Path("a.sig")]
|
||||
process_mock = mocker.patch("ahriman.core.sign.gpg.process", return_value=result)
|
||||
|
||||
for target in ({SignSettings.SignRepository}, {SignSettings.SignPackages, SignSettings.SignRepository}):
|
||||
gpg.target = target
|
||||
assert gpg.sign_repository(Path("a")) == result
|
||||
process_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_sign_repository_skip(gpg: GPG, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must not sign repository if it is not set
|
||||
"""
|
||||
process_mock = mocker.patch("ahriman.core.sign.gpg.process")
|
||||
|
||||
for target in ({}, {SignSettings.SignPackages}):
|
||||
gpg.target = target
|
||||
process_mock.assert_not_called()
|
30
tests/ahriman/core/status/conftest.py
Normal file
30
tests/ahriman/core/status/conftest.py
Normal file
@ -0,0 +1,30 @@
|
||||
import pytest
|
||||
|
||||
from typing import Any, Dict
|
||||
|
||||
from ahriman.core.status.client import Client
|
||||
from ahriman.core.status.web_client import WebClient
|
||||
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
|
||||
from ahriman.models.package import Package
|
||||
|
||||
|
||||
# helpers
|
||||
@pytest.helpers.register
|
||||
def get_package_status(package: Package) -> Dict[str, Any]:
|
||||
return {"status": BuildStatusEnum.Unknown.value, "package": package.view()}
|
||||
|
||||
|
||||
@pytest.helpers.register
|
||||
def get_package_status_extended(package: Package) -> Dict[str, Any]:
|
||||
return {"status": BuildStatus().view(), "package": package.view()}
|
||||
|
||||
|
||||
# fixtures
|
||||
@pytest.fixture
|
||||
def client() -> Client:
|
||||
return Client()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def web_client() -> WebClient:
|
||||
return WebClient("localhost", 8080)
|
116
tests/ahriman/core/status/test_client.py
Normal file
116
tests/ahriman/core/status/test_client.py
Normal file
@ -0,0 +1,116 @@
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.status.client import Client
|
||||
from ahriman.core.status.web_client import WebClient
|
||||
from ahriman.models.build_status import BuildStatusEnum
|
||||
from ahriman.models.package import Package
|
||||
|
||||
|
||||
def test_add(client: Client, package_ahriman: Package) -> None:
|
||||
"""
|
||||
must process package addition without errors
|
||||
"""
|
||||
client.add(package_ahriman, BuildStatusEnum.Unknown)
|
||||
|
||||
|
||||
def test_get(client: Client, package_ahriman: Package) -> None:
|
||||
"""
|
||||
must return empty package list
|
||||
"""
|
||||
assert client.get(package_ahriman.base) == []
|
||||
assert client.get(None) == []
|
||||
|
||||
|
||||
def test_get_self(client: Client) -> None:
|
||||
"""
|
||||
must return unknown status for service
|
||||
"""
|
||||
assert client.get_self().status == BuildStatusEnum.Unknown
|
||||
|
||||
|
||||
def test_remove(client: Client, package_ahriman: Package) -> None:
|
||||
"""
|
||||
must process remove without errors
|
||||
"""
|
||||
client.remove(package_ahriman.base)
|
||||
|
||||
|
||||
def test_update(client: Client, package_ahriman: Package) -> None:
|
||||
"""
|
||||
must update package status without errors
|
||||
"""
|
||||
client.update(package_ahriman.base, BuildStatusEnum.Unknown)
|
||||
|
||||
|
||||
def test_update_self(client: Client) -> None:
|
||||
"""
|
||||
must update self status without errors
|
||||
"""
|
||||
client.update_self(BuildStatusEnum.Unknown)
|
||||
|
||||
|
||||
def test_set_building(client: Client, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must set building status to the package
|
||||
"""
|
||||
update_mock = mocker.patch("ahriman.core.status.client.Client.update")
|
||||
client.set_building(package_ahriman.base)
|
||||
|
||||
update_mock.assert_called_with(package_ahriman.base, BuildStatusEnum.Building)
|
||||
|
||||
|
||||
def test_set_failed(client: Client, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must set failed status to the package
|
||||
"""
|
||||
update_mock = mocker.patch("ahriman.core.status.client.Client.update")
|
||||
client.set_failed(package_ahriman.base)
|
||||
|
||||
update_mock.assert_called_with(package_ahriman.base, BuildStatusEnum.Failed)
|
||||
|
||||
|
||||
def test_set_pending(client: Client, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must set building status to the package
|
||||
"""
|
||||
update_mock = mocker.patch("ahriman.core.status.client.Client.update")
|
||||
client.set_pending(package_ahriman.base)
|
||||
|
||||
update_mock.assert_called_with(package_ahriman.base, BuildStatusEnum.Pending)
|
||||
|
||||
|
||||
def test_set_success(client: Client, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must set success status to the package
|
||||
"""
|
||||
add_mock = mocker.patch("ahriman.core.status.client.Client.add")
|
||||
client.set_success(package_ahriman)
|
||||
|
||||
add_mock.assert_called_with(package_ahriman, BuildStatusEnum.Success)
|
||||
|
||||
|
||||
def test_set_unknown(client: Client, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must add new package with unknown status
|
||||
"""
|
||||
add_mock = mocker.patch("ahriman.core.status.client.Client.add")
|
||||
client.set_unknown(package_ahriman)
|
||||
|
||||
add_mock.assert_called_with(package_ahriman, BuildStatusEnum.Unknown)
|
||||
|
||||
|
||||
def test_load_dummy_client(configuration: Configuration) -> None:
|
||||
"""
|
||||
must load dummy client if no settings set
|
||||
"""
|
||||
assert isinstance(Client.load("x86_64", configuration), Client)
|
||||
|
||||
|
||||
def test_load_full_client(configuration: Configuration) -> None:
|
||||
"""
|
||||
must load full client if no settings set
|
||||
"""
|
||||
configuration.set("web", "host", "localhost")
|
||||
configuration.set("web", "port", "8080")
|
||||
assert isinstance(Client.load("x86_64", configuration), WebClient)
|
219
tests/ahriman/core/status/test_watcher.py
Normal file
219
tests/ahriman/core/status/test_watcher.py
Normal file
@ -0,0 +1,219 @@
|
||||
import pytest
|
||||
import tempfile
|
||||
|
||||
from pathlib import Path
|
||||
from pytest_mock import MockerFixture
|
||||
from unittest.mock import PropertyMock
|
||||
|
||||
from ahriman.core.exceptions import UnknownPackage
|
||||
from ahriman.core.status.watcher import Watcher
|
||||
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
|
||||
from ahriman.models.package import Package
|
||||
|
||||
|
||||
def test_cache_load(watcher: Watcher, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must load state from cache
|
||||
"""
|
||||
response = {"packages": [pytest.helpers.get_package_status_extended(package_ahriman)]}
|
||||
|
||||
mocker.patch("pathlib.Path.is_file", return_value=True)
|
||||
mocker.patch("pathlib.Path.open")
|
||||
mocker.patch("json.load", return_value=response)
|
||||
|
||||
watcher.known = {package_ahriman.base: (None, None)}
|
||||
watcher._cache_load()
|
||||
|
||||
package, status = watcher.known[package_ahriman.base]
|
||||
assert package == package_ahriman
|
||||
assert status.status == BuildStatusEnum.Unknown
|
||||
|
||||
|
||||
def test_cache_load_json_error(watcher: Watcher, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must not fail on json errors
|
||||
"""
|
||||
mocker.patch("pathlib.Path.is_file", return_value=True)
|
||||
mocker.patch("pathlib.Path.open")
|
||||
mocker.patch("json.load", side_effect=Exception())
|
||||
|
||||
watcher._cache_load()
|
||||
assert not watcher.known
|
||||
|
||||
|
||||
def test_cache_load_no_file(watcher: Watcher, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must not fail on missing file
|
||||
"""
|
||||
mocker.patch("pathlib.Path.is_file", return_value=False)
|
||||
|
||||
watcher._cache_load()
|
||||
assert not watcher.known
|
||||
|
||||
|
||||
def test_cache_load_unknown(watcher: Watcher, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must not load unknown package
|
||||
"""
|
||||
response = {"packages": [pytest.helpers.get_package_status_extended(package_ahriman)]}
|
||||
|
||||
mocker.patch("pathlib.Path.is_file", return_value=True)
|
||||
mocker.patch("pathlib.Path.open")
|
||||
mocker.patch("json.load", return_value=response)
|
||||
|
||||
watcher._cache_load()
|
||||
assert not watcher.known
|
||||
|
||||
|
||||
def test_cache_save(watcher: Watcher, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must save state to cache
|
||||
"""
|
||||
mocker.patch("pathlib.Path.open")
|
||||
json_mock = mocker.patch("json.dump")
|
||||
|
||||
watcher.known = {package_ahriman.base: (package_ahriman, BuildStatus())}
|
||||
watcher._cache_save()
|
||||
json_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_cache_save_failed(watcher: Watcher, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must not fail on dumping packages
|
||||
"""
|
||||
mocker.patch("pathlib.Path.open")
|
||||
mocker.patch("json.dump", side_effect=Exception())
|
||||
|
||||
watcher._cache_save()
|
||||
|
||||
|
||||
def test_cache_save_load(watcher: Watcher, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must save state to cache which can be loaded later
|
||||
"""
|
||||
dump_file = Path(tempfile.mktemp())
|
||||
mocker.patch("ahriman.core.status.watcher.Watcher.cache_path",
|
||||
new_callable=PropertyMock, return_value=dump_file)
|
||||
known_current = {package_ahriman.base: (package_ahriman, BuildStatus())}
|
||||
|
||||
watcher.known = known_current
|
||||
watcher._cache_save()
|
||||
|
||||
watcher.known = {package_ahriman.base: (None, None)}
|
||||
watcher._cache_load()
|
||||
assert watcher.known == known_current
|
||||
|
||||
dump_file.unlink()
|
||||
|
||||
|
||||
def test_get(watcher: Watcher, package_ahriman: Package) -> None:
|
||||
"""
|
||||
must return package status
|
||||
"""
|
||||
watcher.known = {package_ahriman.base: (package_ahriman, BuildStatus())}
|
||||
package, status = watcher.get(package_ahriman.base)
|
||||
assert package == package_ahriman
|
||||
assert status.status == BuildStatusEnum.Unknown
|
||||
|
||||
|
||||
def test_get_failed(watcher: Watcher, package_ahriman: Package) -> None:
|
||||
"""
|
||||
must fail on unknown package
|
||||
"""
|
||||
with pytest.raises(UnknownPackage):
|
||||
watcher.get(package_ahriman.base)
|
||||
|
||||
|
||||
def test_load(watcher: Watcher, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must correctly load packages
|
||||
"""
|
||||
mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[package_ahriman])
|
||||
cache_mock = mocker.patch("ahriman.core.status.watcher.Watcher._cache_load")
|
||||
|
||||
watcher.load()
|
||||
cache_mock.assert_called_once()
|
||||
package, status = watcher.known[package_ahriman.base]
|
||||
assert package == package_ahriman
|
||||
assert status.status == BuildStatusEnum.Unknown
|
||||
|
||||
|
||||
def test_load_known(watcher: Watcher, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must correctly load packages with known statuses
|
||||
"""
|
||||
mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[package_ahriman])
|
||||
mocker.patch("ahriman.core.status.watcher.Watcher._cache_load")
|
||||
watcher.known = {package_ahriman.base: (package_ahriman, BuildStatus(BuildStatusEnum.Success))}
|
||||
|
||||
watcher.load()
|
||||
_, status = watcher.known[package_ahriman.base]
|
||||
assert status.status == BuildStatusEnum.Success
|
||||
|
||||
|
||||
def test_remove(watcher: Watcher, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must remove package base
|
||||
"""
|
||||
cache_mock = mocker.patch("ahriman.core.status.watcher.Watcher._cache_save")
|
||||
watcher.known = {package_ahriman.base: (package_ahriman, BuildStatus())}
|
||||
|
||||
watcher.remove(package_ahriman.base)
|
||||
assert not watcher.known
|
||||
cache_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_remove_unknown(watcher: Watcher, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must not fail on unknown base removal
|
||||
"""
|
||||
cache_mock = mocker.patch("ahriman.core.status.watcher.Watcher._cache_save")
|
||||
|
||||
watcher.remove(package_ahriman.base)
|
||||
cache_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_update(watcher: Watcher, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must update package status
|
||||
"""
|
||||
cache_mock = mocker.patch("ahriman.core.status.watcher.Watcher._cache_save")
|
||||
|
||||
watcher.update(package_ahriman.base, BuildStatusEnum.Unknown, package_ahriman)
|
||||
cache_mock.assert_called_once()
|
||||
package, status = watcher.known[package_ahriman.base]
|
||||
assert package == package_ahriman
|
||||
assert status.status == BuildStatusEnum.Unknown
|
||||
|
||||
|
||||
def test_update_ping(watcher: Watcher, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must update package status only for known package
|
||||
"""
|
||||
cache_mock = mocker.patch("ahriman.core.status.watcher.Watcher._cache_save")
|
||||
watcher.known = {package_ahriman.base: (package_ahriman, BuildStatus())}
|
||||
|
||||
watcher.update(package_ahriman.base, BuildStatusEnum.Success, None)
|
||||
cache_mock.assert_called_once()
|
||||
package, status = watcher.known[package_ahriman.base]
|
||||
assert package == package_ahriman
|
||||
assert status.status == BuildStatusEnum.Success
|
||||
|
||||
|
||||
def test_update_unknown(watcher: Watcher, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must fail on unknown package status update only
|
||||
"""
|
||||
cache_mock = mocker.patch("ahriman.core.status.watcher.Watcher._cache_save")
|
||||
|
||||
with pytest.raises(UnknownPackage):
|
||||
watcher.update(package_ahriman.base, BuildStatusEnum.Unknown, None)
|
||||
cache_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_update_self(watcher: Watcher) -> None:
|
||||
"""
|
||||
must update service status
|
||||
"""
|
||||
watcher.update_self(BuildStatusEnum.Success)
|
||||
assert watcher.status.status == BuildStatusEnum.Success
|
163
tests/ahriman/core/status/test_web_client.py
Normal file
163
tests/ahriman/core/status/test_web_client.py
Normal file
@ -0,0 +1,163 @@
|
||||
import json
|
||||
import pytest
|
||||
|
||||
from pytest_mock import MockerFixture
|
||||
from requests import Response
|
||||
|
||||
from ahriman.core.status.web_client import WebClient
|
||||
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
|
||||
from ahriman.models.package import Package
|
||||
|
||||
|
||||
def test_ahriman_url(web_client: WebClient) -> None:
|
||||
"""
|
||||
must generate service status url correctly
|
||||
"""
|
||||
assert web_client._ahriman_url().startswith(f"http://{web_client.host}:{web_client.port}")
|
||||
assert web_client._ahriman_url().endswith("/api/v1/ahriman")
|
||||
|
||||
|
||||
def test_package_url(web_client: WebClient, package_ahriman: Package) -> None:
|
||||
"""
|
||||
must generate package status correctly
|
||||
"""
|
||||
assert web_client._package_url(package_ahriman.base).startswith(f"http://{web_client.host}:{web_client.port}")
|
||||
assert web_client._package_url(package_ahriman.base).endswith(f"/api/v1/packages/{package_ahriman.base}")
|
||||
|
||||
|
||||
def test_add(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must process package addition
|
||||
"""
|
||||
requests_mock = mocker.patch("requests.post")
|
||||
payload = pytest.helpers.get_package_status(package_ahriman)
|
||||
|
||||
web_client.add(package_ahriman, BuildStatusEnum.Unknown)
|
||||
requests_mock.assert_called_with(pytest.helpers.anyvar(str, True), json=payload)
|
||||
|
||||
|
||||
def test_add_failed(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must suppress any exception happened during addition
|
||||
"""
|
||||
mocker.patch("requests.post", side_effect=Exception())
|
||||
web_client.add(package_ahriman, BuildStatusEnum.Unknown)
|
||||
|
||||
|
||||
def test_get_all(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must return all packages status
|
||||
"""
|
||||
response = [pytest.helpers.get_package_status_extended(package_ahriman)]
|
||||
response_obj = Response()
|
||||
response_obj._content = json.dumps(response).encode("utf8")
|
||||
response_obj.status_code = 200
|
||||
|
||||
requests_mock = mocker.patch("requests.get", return_value=response_obj)
|
||||
|
||||
result = web_client.get(None)
|
||||
requests_mock.assert_called_once()
|
||||
assert len(result) == len(response)
|
||||
assert (package_ahriman, BuildStatusEnum.Unknown) in [(package, status.status) for package, status in result]
|
||||
|
||||
|
||||
def test_get_failed(web_client: WebClient, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must suppress any exception happened during status getting
|
||||
"""
|
||||
mocker.patch("requests.get", side_effect=Exception())
|
||||
assert web_client.get(None) == []
|
||||
|
||||
|
||||
def test_get_single(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must return single package status
|
||||
"""
|
||||
response = [pytest.helpers.get_package_status_extended(package_ahriman)]
|
||||
response_obj = Response()
|
||||
response_obj._content = json.dumps(response).encode("utf8")
|
||||
response_obj.status_code = 200
|
||||
|
||||
requests_mock = mocker.patch("requests.get", return_value=response_obj)
|
||||
|
||||
result = web_client.get(package_ahriman.base)
|
||||
requests_mock.assert_called_once()
|
||||
assert len(result) == len(response)
|
||||
assert (package_ahriman, BuildStatusEnum.Unknown) in [(package, status.status) for package, status in result]
|
||||
|
||||
|
||||
def test_get_self(web_client: WebClient, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must return service status
|
||||
"""
|
||||
response_obj = Response()
|
||||
response_obj._content = json.dumps(BuildStatus().view()).encode("utf8")
|
||||
response_obj.status_code = 200
|
||||
|
||||
requests_mock = mocker.patch("requests.get", return_value=response_obj)
|
||||
|
||||
result = web_client.get_self()
|
||||
requests_mock.assert_called_once()
|
||||
assert result.status == BuildStatusEnum.Unknown
|
||||
|
||||
|
||||
def test_get_self_failed(web_client: WebClient, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must suppress any exception happened during service status getting
|
||||
"""
|
||||
mocker.patch("requests.get", side_effect=Exception())
|
||||
assert web_client.get_self().status == BuildStatusEnum.Unknown
|
||||
|
||||
|
||||
def test_remove(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must process package removal
|
||||
"""
|
||||
requests_mock = mocker.patch("requests.delete")
|
||||
|
||||
web_client.remove(package_ahriman.base)
|
||||
requests_mock.assert_called_with(pytest.helpers.anyvar(str, True))
|
||||
|
||||
|
||||
def test_remove_failed(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must suppress any exception happened during removal
|
||||
"""
|
||||
mocker.patch("requests.delete", side_effect=Exception())
|
||||
web_client.remove(package_ahriman.base)
|
||||
|
||||
|
||||
def test_update(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must process package update
|
||||
"""
|
||||
requests_mock = mocker.patch("requests.post")
|
||||
|
||||
web_client.update(package_ahriman.base, BuildStatusEnum.Unknown)
|
||||
requests_mock.assert_called_with(pytest.helpers.anyvar(str, True), json={"status": BuildStatusEnum.Unknown.value})
|
||||
|
||||
|
||||
def test_update_failed(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must suppress any exception happened during update
|
||||
"""
|
||||
mocker.patch("requests.post", side_effect=Exception())
|
||||
web_client.update(package_ahriman.base, BuildStatusEnum.Unknown)
|
||||
|
||||
|
||||
def test_update_self(web_client: WebClient, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must process service update
|
||||
"""
|
||||
requests_mock = mocker.patch("requests.post")
|
||||
|
||||
web_client.update_self(BuildStatusEnum.Unknown)
|
||||
requests_mock.assert_called_with(pytest.helpers.anyvar(str, True), json={"status": BuildStatusEnum.Unknown.value})
|
||||
|
||||
|
||||
def test_update_self_failed(web_client: WebClient, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must suppress any exception happened during service update
|
||||
"""
|
||||
mocker.patch("requests.post", side_effect=Exception())
|
||||
web_client.update_self(BuildStatusEnum.Unknown)
|
129
tests/ahriman/core/test_configuration.py
Normal file
129
tests/ahriman/core/test_configuration.py
Normal file
@ -0,0 +1,129 @@
|
||||
from pathlib import Path
|
||||
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from ahriman.core.configuration import Configuration
|
||||
|
||||
|
||||
def test_from_path(mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must load configuration
|
||||
"""
|
||||
read_mock = mocker.patch("configparser.RawConfigParser.read")
|
||||
load_includes_mock = mocker.patch("ahriman.core.configuration.Configuration.load_includes")
|
||||
load_logging_mock = mocker.patch("ahriman.core.configuration.Configuration.load_logging")
|
||||
path = Path("path")
|
||||
|
||||
config = Configuration.from_path(path, True)
|
||||
assert config.path == path
|
||||
read_mock.assert_called_with(path)
|
||||
load_includes_mock.assert_called_once()
|
||||
load_logging_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_absolute_path_for_absolute(configuration: Configuration) -> None:
|
||||
"""
|
||||
must not change path for absolute path in settings
|
||||
"""
|
||||
path = Path("/a/b/c")
|
||||
configuration.set("build", "path", str(path))
|
||||
assert configuration.getpath("build", "path") == path
|
||||
|
||||
|
||||
def test_absolute_path_for_relative(configuration: Configuration) -> None:
|
||||
"""
|
||||
must prepend root path to relative path
|
||||
"""
|
||||
path = Path("a")
|
||||
configuration.set("build", "path", str(path))
|
||||
result = configuration.getpath("build", "path")
|
||||
assert result.is_absolute()
|
||||
assert result.parent == configuration.path.parent
|
||||
assert result.name == path.name
|
||||
|
||||
|
||||
def test_dump(configuration: Configuration) -> None:
|
||||
"""
|
||||
dump must not be empty
|
||||
"""
|
||||
assert configuration.dump("x86_64")
|
||||
|
||||
|
||||
def test_dump_architecture_specific(configuration: Configuration) -> None:
|
||||
"""
|
||||
dump must contain architecture specific settings
|
||||
"""
|
||||
configuration.add_section("build_x86_64")
|
||||
configuration.set("build_x86_64", "archbuild_flags", "")
|
||||
|
||||
dump = configuration.dump("x86_64")
|
||||
assert dump
|
||||
assert "build" not in dump
|
||||
assert "build_x86_64" in dump
|
||||
|
||||
|
||||
def test_getlist(configuration: Configuration) -> None:
|
||||
"""
|
||||
must return list of string correctly
|
||||
"""
|
||||
configuration.set("build", "test_list", "a b c")
|
||||
assert configuration.getlist("build", "test_list") == ["a", "b", "c"]
|
||||
|
||||
|
||||
def test_getlist_empty(configuration: Configuration) -> None:
|
||||
"""
|
||||
must return list of string correctly for non-existing option
|
||||
"""
|
||||
assert configuration.getlist("build", "test_list") == []
|
||||
configuration.set("build", "test_list", "")
|
||||
assert configuration.getlist("build", "test_list") == []
|
||||
|
||||
|
||||
def test_getlist_single(configuration: Configuration) -> None:
|
||||
"""
|
||||
must return list of strings for single string
|
||||
"""
|
||||
configuration.set("build", "test_list", "a")
|
||||
assert configuration.getlist("build", "test_list") == ["a"]
|
||||
|
||||
|
||||
def test_get_section_name(configuration: Configuration) -> None:
|
||||
"""
|
||||
must return architecture specific group
|
||||
"""
|
||||
configuration.add_section("build_x86_64")
|
||||
configuration.set("build_x86_64", "archbuild_flags", "")
|
||||
assert configuration.get_section_name("build", "x86_64") == "build_x86_64"
|
||||
|
||||
|
||||
def test_get_section_name_missing(configuration: Configuration) -> None:
|
||||
"""
|
||||
must return default group if architecture depending group does not exist
|
||||
"""
|
||||
assert configuration.get_section_name("prefix", "suffix") == "prefix"
|
||||
assert configuration.get_section_name("build", "x86_64") == "build"
|
||||
|
||||
|
||||
def test_load_includes_missing(configuration: Configuration) -> None:
|
||||
"""
|
||||
must not fail if not include directory found
|
||||
"""
|
||||
configuration.set("settings", "include", "path")
|
||||
configuration.load_includes()
|
||||
|
||||
|
||||
def test_load_logging_fallback(configuration: Configuration, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must fallback to stderr without errors
|
||||
"""
|
||||
mocker.patch("logging.config.fileConfig", side_effect=PermissionError())
|
||||
configuration.load_logging(True)
|
||||
|
||||
|
||||
def test_load_logging_stderr(configuration: Configuration, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must use stderr if flag set
|
||||
"""
|
||||
logging_mock = mocker.patch("logging.config.fileConfig")
|
||||
configuration.load_logging(False)
|
||||
logging_mock.assert_not_called()
|
78
tests/ahriman/core/test_tree.py
Normal file
78
tests/ahriman/core/test_tree.py
Normal file
@ -0,0 +1,78 @@
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from ahriman.core.tree import Leaf, Tree
|
||||
from ahriman.models.package import Package
|
||||
|
||||
|
||||
def test_leaf_is_root_empty(leaf_ahriman: Leaf) -> None:
|
||||
"""
|
||||
must be root for empty packages list
|
||||
"""
|
||||
assert leaf_ahriman.is_root([])
|
||||
|
||||
|
||||
def test_leaf_is_root_false(leaf_ahriman: Leaf, leaf_python_schedule: Leaf) -> None:
|
||||
"""
|
||||
must be root for empty dependencies list or if does not depend on packages
|
||||
"""
|
||||
assert leaf_ahriman.is_root([leaf_python_schedule])
|
||||
leaf_ahriman.dependencies = {"ahriman-dependency"}
|
||||
assert leaf_ahriman.is_root([leaf_python_schedule])
|
||||
|
||||
|
||||
def test_leaf_is_root_true(leaf_ahriman: Leaf, leaf_python_schedule: Leaf) -> None:
|
||||
"""
|
||||
must not be root if depends on packages
|
||||
"""
|
||||
leaf_ahriman.dependencies = {"python-schedule"}
|
||||
assert not leaf_ahriman.is_root([leaf_python_schedule])
|
||||
|
||||
leaf_ahriman.dependencies = {"python2-schedule"}
|
||||
assert not leaf_ahriman.is_root([leaf_python_schedule])
|
||||
|
||||
leaf_ahriman.dependencies = set(leaf_python_schedule.package.packages.keys())
|
||||
assert not leaf_ahriman.is_root([leaf_python_schedule])
|
||||
|
||||
|
||||
def test_leaf_load(package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must load with dependencies
|
||||
"""
|
||||
tempdir_mock = mocker.patch("tempfile.mkdtemp")
|
||||
fetch_mock = mocker.patch("ahriman.core.build_tools.task.Task.fetch")
|
||||
dependencies_mock = mocker.patch("ahriman.models.package.Package.dependencies", return_value={"ahriman-dependency"})
|
||||
rmtree_mock = mocker.patch("shutil.rmtree")
|
||||
|
||||
leaf = Leaf.load(package_ahriman)
|
||||
assert leaf.package == package_ahriman
|
||||
assert leaf.dependencies == {"ahriman-dependency"}
|
||||
tempdir_mock.assert_called_once()
|
||||
fetch_mock.assert_called_once()
|
||||
dependencies_mock.assert_called_once()
|
||||
rmtree_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_tree_levels(leaf_ahriman: Leaf, leaf_python_schedule: Leaf, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must generate correct levels in the simples case
|
||||
"""
|
||||
leaf_ahriman.dependencies = set(leaf_python_schedule.package.packages.keys())
|
||||
|
||||
tree = Tree([leaf_ahriman, leaf_python_schedule])
|
||||
assert len(tree.levels()) == 2
|
||||
first, second = tree.levels()
|
||||
assert first == [leaf_python_schedule.package]
|
||||
assert second == [leaf_ahriman.package]
|
||||
|
||||
|
||||
def test_tree_load(package_ahriman: Package, package_python_schedule: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must package list
|
||||
"""
|
||||
mocker.patch("tempfile.mkdtemp")
|
||||
mocker.patch("ahriman.core.build_tools.task.Task.fetch")
|
||||
mocker.patch("ahriman.models.package.Package.dependencies")
|
||||
mocker.patch("shutil.rmtree")
|
||||
|
||||
tree = Tree.load([package_ahriman, package_python_schedule])
|
||||
assert len(tree.leaves) == 2
|
131
tests/ahriman/core/test_util.py
Normal file
131
tests/ahriman/core/test_util.py
Normal file
@ -0,0 +1,131 @@
|
||||
import logging
|
||||
import pytest
|
||||
import subprocess
|
||||
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from ahriman.core.util import check_output, package_like, pretty_datetime, pretty_size
|
||||
from ahriman.models.package import Package
|
||||
|
||||
|
||||
def test_check_output(mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must run command and log result
|
||||
"""
|
||||
logger_mock = mocker.patch("logging.Logger.debug")
|
||||
|
||||
assert check_output("echo", "hello", exception=None) == "hello"
|
||||
logger_mock.assert_not_called()
|
||||
|
||||
assert check_output("echo", "hello", exception=None, logger=logging.getLogger("")) == "hello"
|
||||
logger_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_check_output_failure(mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must process exception correctly
|
||||
"""
|
||||
logger_mock = mocker.patch("logging.Logger.debug")
|
||||
mocker.patch("subprocess.check_output", side_effect=subprocess.CalledProcessError(1, "echo"))
|
||||
|
||||
with pytest.raises(subprocess.CalledProcessError):
|
||||
check_output("echo", "hello", exception=None)
|
||||
logger_mock.assert_not_called()
|
||||
|
||||
with pytest.raises(subprocess.CalledProcessError):
|
||||
check_output("echo", "hello", exception=None, logger=logging.getLogger(""))
|
||||
logger_mock.assert_not_called()
|
||||
|
||||
|
||||
def test_check_output_failure_log(mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must process exception correctly and log it
|
||||
"""
|
||||
logger_mock = mocker.patch("logging.Logger.debug")
|
||||
mocker.patch("subprocess.check_output", side_effect=subprocess.CalledProcessError(1, "echo", output=b"result"))
|
||||
|
||||
with pytest.raises(subprocess.CalledProcessError):
|
||||
check_output("echo", "hello", exception=None, logger=logging.getLogger(""))
|
||||
logger_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_package_like(package_ahriman: Package) -> None:
|
||||
"""
|
||||
package_like must return true for archives
|
||||
"""
|
||||
assert package_like(package_ahriman.packages[package_ahriman.base].filepath)
|
||||
|
||||
|
||||
def test_package_like_sig(package_ahriman: Package) -> None:
|
||||
"""
|
||||
package_like must return false for signature files
|
||||
"""
|
||||
package_file = package_ahriman.packages[package_ahriman.base].filepath
|
||||
sig_file = package_file.parent / f"{package_file.name}.sig"
|
||||
assert not package_like(sig_file)
|
||||
|
||||
|
||||
def test_pretty_datetime() -> None:
|
||||
"""
|
||||
must generate string from timestamp value
|
||||
"""
|
||||
assert pretty_datetime(0) == "1970-01-01 00:00:00"
|
||||
|
||||
|
||||
def test_pretty_datetime_empty() -> None:
|
||||
"""
|
||||
must generate empty string from None timestamp
|
||||
"""
|
||||
assert pretty_datetime(None) == ""
|
||||
|
||||
|
||||
def test_pretty_size_bytes() -> None:
|
||||
"""
|
||||
must generate bytes string for bytes value
|
||||
"""
|
||||
value, abbrev = pretty_size(42).split()
|
||||
assert value == "42.0"
|
||||
assert abbrev == "B"
|
||||
|
||||
|
||||
def test_pretty_size_kbytes() -> None:
|
||||
"""
|
||||
must generate kibibytes string for kibibytes value
|
||||
"""
|
||||
value, abbrev = pretty_size(42 * 1024).split()
|
||||
assert value == "42.0"
|
||||
assert abbrev == "KiB"
|
||||
|
||||
|
||||
def test_pretty_size_mbytes() -> None:
|
||||
"""
|
||||
must generate mebibytes string for mebibytes value
|
||||
"""
|
||||
value, abbrev = pretty_size(42 * 1024 * 1024).split()
|
||||
assert value == "42.0"
|
||||
assert abbrev == "MiB"
|
||||
|
||||
|
||||
def test_pretty_size_gbytes() -> None:
|
||||
"""
|
||||
must generate gibibytes string for gibibytes value
|
||||
"""
|
||||
value, abbrev = pretty_size(42 * 1024 * 1024 * 1024).split()
|
||||
assert value == "42.0"
|
||||
assert abbrev == "GiB"
|
||||
|
||||
|
||||
def test_pretty_size_pbytes() -> None:
|
||||
"""
|
||||
must generate pebibytes string for pebibytes value
|
||||
"""
|
||||
value, abbrev = pretty_size(42 * 1024 * 1024 * 1024 * 1024).split()
|
||||
assert value == "43008.0"
|
||||
assert abbrev == "GiB"
|
||||
|
||||
|
||||
def test_pretty_size_empty() -> None:
|
||||
"""
|
||||
must generate empty string for None value
|
||||
"""
|
||||
assert pretty_size(None) == ""
|
0
tests/ahriman/core/upload/test_rsync.py
Normal file
0
tests/ahriman/core/upload/test_rsync.py
Normal file
0
tests/ahriman/core/upload/test_s3.py
Normal file
0
tests/ahriman/core/upload/test_s3.py
Normal file
0
tests/ahriman/core/upload/test_uploader.py
Normal file
0
tests/ahriman/core/upload/test_uploader.py
Normal file
19
tests/ahriman/models/conftest.py
Normal file
19
tests/ahriman/models/conftest.py
Normal file
@ -0,0 +1,19 @@
|
||||
import pytest
|
||||
|
||||
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
|
||||
from ahriman.models.package import Package
|
||||
from ahriman.models.package_desciption import PackageDescription
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def build_status_failed() -> BuildStatus:
|
||||
return BuildStatus(BuildStatusEnum.Failed, 42)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def package_tpacpi_bat_git() -> Package:
|
||||
return Package(
|
||||
base="tpacpi-bat-git",
|
||||
version="3.1.r12.g4959b52-1",
|
||||
aur_url="https://aur.archlinux.org",
|
||||
packages={"tpacpi-bat-git": PackageDescription()})
|
38
tests/ahriman/models/test_build_status.py
Normal file
38
tests/ahriman/models/test_build_status.py
Normal file
@ -0,0 +1,38 @@
|
||||
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
|
||||
|
||||
|
||||
def test_build_status_enum_badges_color() -> None:
|
||||
"""
|
||||
status color must be one of shields.io supported
|
||||
"""
|
||||
SUPPORTED_COLORS = [
|
||||
"brightgreen", "green", "yellowgreen", "yellow", "orange", "red", "blue", "lightgrey",
|
||||
"success", "important", "critical", "informational", "inactive", "blueviolet"
|
||||
]
|
||||
|
||||
for status in BuildStatusEnum:
|
||||
assert status.badges_color() in SUPPORTED_COLORS
|
||||
|
||||
|
||||
def test_build_status_init_1() -> None:
|
||||
"""
|
||||
must construct status object from None
|
||||
"""
|
||||
status = BuildStatus()
|
||||
assert status.status == BuildStatusEnum.Unknown
|
||||
assert status.timestamp > 0
|
||||
|
||||
|
||||
def test_build_status_init_2(build_status_failed: BuildStatus) -> None:
|
||||
"""
|
||||
must construct status object from objects
|
||||
"""
|
||||
status = BuildStatus(BuildStatusEnum.Failed, 42)
|
||||
assert status == build_status_failed
|
||||
|
||||
|
||||
def test_build_status_from_json_view(build_status_failed: BuildStatus) -> None:
|
||||
"""
|
||||
must construct same object from json
|
||||
"""
|
||||
assert BuildStatus.from_json(build_status_failed.view()) == build_status_failed
|
130
tests/ahriman/models/test_package.py
Normal file
130
tests/ahriman/models/test_package.py
Normal file
@ -0,0 +1,130 @@
|
||||
from pathlib import Path
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from ahriman.models.package import Package
|
||||
from ahriman.models.repository_paths import RepositoryPaths
|
||||
|
||||
|
||||
def test_git_url(package_ahriman: Package) -> None:
|
||||
"""
|
||||
must generate valid git url
|
||||
"""
|
||||
assert package_ahriman.git_url.endswith(".git")
|
||||
assert package_ahriman.git_url.startswith(package_ahriman.aur_url)
|
||||
assert package_ahriman.base in package_ahriman.git_url
|
||||
|
||||
|
||||
def test_is_single_package_false(package_python_schedule: Package) -> None:
|
||||
"""
|
||||
python-schedule must not be single package
|
||||
"""
|
||||
assert not package_python_schedule.is_single_package
|
||||
|
||||
|
||||
def test_is_single_package_true(package_ahriman: Package) -> None:
|
||||
"""
|
||||
ahriman must be single package
|
||||
"""
|
||||
assert package_ahriman.is_single_package
|
||||
|
||||
|
||||
def test_is_vcs_false(package_ahriman: Package) -> None:
|
||||
"""
|
||||
ahriman must not be VCS package
|
||||
"""
|
||||
assert not package_ahriman.is_vcs
|
||||
|
||||
|
||||
def test_is_vcs_true(package_tpacpi_bat_git: Package) -> None:
|
||||
"""
|
||||
tpacpi-bat-git must be VCS package
|
||||
"""
|
||||
assert package_tpacpi_bat_git.is_vcs
|
||||
|
||||
|
||||
def test_web_url(package_ahriman: Package) -> None:
|
||||
"""
|
||||
must generate valid web url
|
||||
"""
|
||||
assert package_ahriman.web_url.startswith(package_ahriman.aur_url)
|
||||
assert package_ahriman.base in package_ahriman.web_url
|
||||
|
||||
|
||||
def test_from_json_view_1(package_ahriman: Package) -> None:
|
||||
"""
|
||||
must construct same object from json
|
||||
"""
|
||||
assert Package.from_json(package_ahriman.view()) == package_ahriman
|
||||
|
||||
|
||||
def test_from_json_view_2(package_python_schedule: Package) -> None:
|
||||
"""
|
||||
must construct same object from json
|
||||
"""
|
||||
assert Package.from_json(package_python_schedule.view()) == package_python_schedule
|
||||
|
||||
|
||||
def test_from_json_view_3(package_tpacpi_bat_git: Package) -> None:
|
||||
"""
|
||||
must construct same object from json
|
||||
"""
|
||||
assert Package.from_json(package_tpacpi_bat_git.view()) == package_tpacpi_bat_git
|
||||
|
||||
|
||||
def test_dependencies_with_version(mocker: MockerFixture, resource_path_root: Path) -> None:
|
||||
"""
|
||||
must load correct list of dependencies with version
|
||||
"""
|
||||
srcinfo = (resource_path_root / "models" / "package_yay_srcinfo").read_text()
|
||||
|
||||
mocker.patch("pathlib.Path.read_text", return_value=srcinfo)
|
||||
|
||||
assert Package.dependencies(Path("path")) == {"git", "go", "pacman"}
|
||||
|
||||
|
||||
def test_actual_version(package_ahriman: Package, repository_paths: RepositoryPaths) -> None:
|
||||
"""
|
||||
must return same actual_version as version is
|
||||
"""
|
||||
assert package_ahriman.actual_version(repository_paths) == package_ahriman.version
|
||||
|
||||
|
||||
def test_actual_version_vcs(package_tpacpi_bat_git: Package, repository_paths: RepositoryPaths,
|
||||
mocker: MockerFixture, resource_path_root: Path) -> None:
|
||||
"""
|
||||
must return valid actual_version for VCS package
|
||||
"""
|
||||
srcinfo = (resource_path_root / "models" / "package_tpacpi-bat-git_srcinfo").read_text()
|
||||
|
||||
mocker.patch("ahriman.models.package.Package._check_output", return_value=srcinfo)
|
||||
mocker.patch("ahriman.core.build_tools.task.Task.fetch")
|
||||
|
||||
assert package_tpacpi_bat_git.actual_version(repository_paths) == "3.1.r13.g4959b52-1"
|
||||
|
||||
|
||||
def test_actual_version_vcs_failed(package_tpacpi_bat_git: Package, repository_paths: RepositoryPaths,
|
||||
mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must return same version in case if exception occurred
|
||||
"""
|
||||
mocker.patch("ahriman.models.package.Package._check_output", side_effect=Exception())
|
||||
mocker.patch("ahriman.core.build_tools.task.Task.fetch")
|
||||
|
||||
assert package_tpacpi_bat_git.actual_version(repository_paths) == package_tpacpi_bat_git.version
|
||||
|
||||
|
||||
def test_is_outdated_false(package_ahriman: Package, repository_paths: RepositoryPaths) -> None:
|
||||
"""
|
||||
must be not outdated for the same package
|
||||
"""
|
||||
assert not package_ahriman.is_outdated(package_ahriman, repository_paths)
|
||||
|
||||
|
||||
def test_is_outdated_true(package_ahriman: Package, repository_paths: RepositoryPaths) -> None:
|
||||
"""
|
||||
must be outdated for the new version
|
||||
"""
|
||||
other = Package.from_json(package_ahriman.view())
|
||||
other.version = other.version.replace("-1", "-2")
|
||||
|
||||
assert package_ahriman.is_outdated(other, repository_paths)
|
17
tests/ahriman/models/test_package_desciption.py
Normal file
17
tests/ahriman/models/test_package_desciption.py
Normal file
@ -0,0 +1,17 @@
|
||||
from ahriman.models.package_desciption import PackageDescription
|
||||
|
||||
|
||||
def test_filepath(package_description_ahriman: PackageDescription) -> None:
|
||||
"""
|
||||
must generate correct filepath if set
|
||||
"""
|
||||
assert package_description_ahriman.filepath is not None
|
||||
assert package_description_ahriman.filepath.name == package_description_ahriman.filename
|
||||
|
||||
|
||||
def test_filepath_empty(package_description_ahriman: PackageDescription) -> None:
|
||||
"""
|
||||
must return None for missing filename
|
||||
"""
|
||||
package_description_ahriman.filename = None
|
||||
assert package_description_ahriman.filepath is None
|
20
tests/ahriman/models/test_report_settings.py
Normal file
20
tests/ahriman/models/test_report_settings.py
Normal file
@ -0,0 +1,20 @@
|
||||
import pytest
|
||||
|
||||
from ahriman.core.exceptions import InvalidOption
|
||||
from ahriman.models.report_settings import ReportSettings
|
||||
|
||||
|
||||
def test_from_option_invalid() -> None:
|
||||
"""
|
||||
must raise exception on invalid option
|
||||
"""
|
||||
with pytest.raises(InvalidOption, match=".* `invalid`$"):
|
||||
ReportSettings.from_option("invalid")
|
||||
|
||||
|
||||
def test_from_option_valid() -> None:
|
||||
"""
|
||||
must return value from valid options
|
||||
"""
|
||||
assert ReportSettings.from_option("html") == ReportSettings.HTML
|
||||
assert ReportSettings.from_option("HTML") == ReportSettings.HTML
|
23
tests/ahriman/models/test_repository_paths.py
Normal file
23
tests/ahriman/models/test_repository_paths.py
Normal file
@ -0,0 +1,23 @@
|
||||
from pytest_mock import MockerFixture
|
||||
from unittest import mock
|
||||
|
||||
from ahriman.models.repository_paths import RepositoryPaths
|
||||
|
||||
|
||||
def test_create_tree(repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must create whole tree
|
||||
"""
|
||||
paths = {
|
||||
prop
|
||||
for prop in dir(repository_paths)
|
||||
if not prop.startswith("_") and prop not in ("architecture", "create_tree", "root")
|
||||
}
|
||||
mkdir_mock = mocker.patch("pathlib.Path.mkdir")
|
||||
|
||||
repository_paths.create_tree()
|
||||
mkdir_mock.assert_has_calls(
|
||||
[
|
||||
mock.call(mode=0o755, parents=True, exist_ok=True)
|
||||
for _ in paths
|
||||
])
|
26
tests/ahriman/models/test_sign_settings.py
Normal file
26
tests/ahriman/models/test_sign_settings.py
Normal file
@ -0,0 +1,26 @@
|
||||
import pytest
|
||||
|
||||
from ahriman.core.exceptions import InvalidOption
|
||||
from ahriman.models.sign_settings import SignSettings
|
||||
|
||||
|
||||
def test_from_option_invalid() -> None:
|
||||
"""
|
||||
must raise exception on invalid option
|
||||
"""
|
||||
with pytest.raises(InvalidOption, match=".* `invalid`$"):
|
||||
SignSettings.from_option("invalid")
|
||||
|
||||
|
||||
def test_from_option_valid() -> None:
|
||||
"""
|
||||
must return value from valid options
|
||||
"""
|
||||
assert SignSettings.from_option("package") == SignSettings.SignPackages
|
||||
assert SignSettings.from_option("PACKAGE") == SignSettings.SignPackages
|
||||
assert SignSettings.from_option("packages") == SignSettings.SignPackages
|
||||
assert SignSettings.from_option("sign-package") == SignSettings.SignPackages
|
||||
|
||||
assert SignSettings.from_option("repository") == SignSettings.SignRepository
|
||||
assert SignSettings.from_option("REPOSITORY") == SignSettings.SignRepository
|
||||
assert SignSettings.from_option("sign-repository") == SignSettings.SignRepository
|
23
tests/ahriman/models/test_upload_settings.py
Normal file
23
tests/ahriman/models/test_upload_settings.py
Normal file
@ -0,0 +1,23 @@
|
||||
import pytest
|
||||
|
||||
from ahriman.core.exceptions import InvalidOption
|
||||
from ahriman.models.upload_settings import UploadSettings
|
||||
|
||||
|
||||
def test_from_option_invalid() -> None:
|
||||
"""
|
||||
must raise exception on invalid option
|
||||
"""
|
||||
with pytest.raises(InvalidOption, match=".* `invalid`$"):
|
||||
UploadSettings.from_option("invalid")
|
||||
|
||||
|
||||
def test_from_option_valid() -> None:
|
||||
"""
|
||||
must return value from valid options
|
||||
"""
|
||||
assert UploadSettings.from_option("rsync") == UploadSettings.Rsync
|
||||
assert UploadSettings.from_option("RSYNC") == UploadSettings.Rsync
|
||||
|
||||
assert UploadSettings.from_option("s3") == UploadSettings.S3
|
||||
assert UploadSettings.from_option("S3") == UploadSettings.S3
|
13
tests/ahriman/web/conftest.py
Normal file
13
tests/ahriman/web/conftest.py
Normal file
@ -0,0 +1,13 @@
|
||||
import pytest
|
||||
|
||||
from aiohttp import web
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.web.web import setup_service
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def application(configuration: Configuration, mocker: MockerFixture) -> web.Application:
|
||||
mocker.patch("pathlib.Path.mkdir")
|
||||
return setup_service("x86_64", configuration)
|
0
tests/ahriman/web/test_routes.py
Normal file
0
tests/ahriman/web/test_routes.py
Normal file
45
tests/ahriman/web/test_web.py
Normal file
45
tests/ahriman/web/test_web.py
Normal file
@ -0,0 +1,45 @@
|
||||
import pytest
|
||||
|
||||
from aiohttp import web
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from ahriman.core.exceptions import InitializeException
|
||||
from ahriman.core.status.watcher import Watcher
|
||||
from ahriman.web.web import on_startup, run_server
|
||||
|
||||
|
||||
async def test_on_startup(application: web.Application, watcher: Watcher, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must call load method
|
||||
"""
|
||||
mocker.patch("aiohttp.web.Application.__getitem__", return_value=watcher)
|
||||
load_mock = mocker.patch("ahriman.core.status.watcher.Watcher.load")
|
||||
|
||||
await on_startup(application)
|
||||
load_mock.assert_called_once()
|
||||
|
||||
|
||||
async def test_on_startup_exception(application: web.Application, watcher: Watcher, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must throw exception on load error
|
||||
"""
|
||||
mocker.patch("aiohttp.web.Application.__getitem__", return_value=watcher)
|
||||
mocker.patch("ahriman.core.status.watcher.Watcher.load", side_effect=Exception())
|
||||
|
||||
with pytest.raises(InitializeException):
|
||||
await on_startup(application)
|
||||
|
||||
|
||||
def test_run(application: web.Application, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must run application
|
||||
"""
|
||||
host = "localhost"
|
||||
port = 8080
|
||||
application["config"].set("web", "host", host)
|
||||
application["config"].set("web", "port", str(port))
|
||||
run_app_mock = mocker.patch("aiohttp.web.run_app")
|
||||
|
||||
run_server(application)
|
||||
run_app_mock.assert_called_with(application, host=host, port=port,
|
||||
handle_signals=False, access_log=pytest.helpers.anyvar(int))
|
14
tests/ahriman/web/views/conftest.py
Normal file
14
tests/ahriman/web/views/conftest.py
Normal file
@ -0,0 +1,14 @@
|
||||
import pytest
|
||||
|
||||
from aiohttp import web
|
||||
from asyncio import BaseEventLoop
|
||||
from pytest_aiohttp import TestClient
|
||||
from pytest_mock import MockerFixture
|
||||
from typing import Any
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client(application: web.Application, loop: BaseEventLoop,
|
||||
aiohttp_client: Any, mocker: MockerFixture) -> TestClient:
|
||||
mocker.patch("pathlib.Path.iterdir", return_value=[])
|
||||
return loop.run_until_complete(aiohttp_client(application))
|
37
tests/ahriman/web/views/test_view_ahriman.py
Normal file
37
tests/ahriman/web/views/test_view_ahriman.py
Normal file
@ -0,0 +1,37 @@
|
||||
from aiohttp.test_utils import TestClient
|
||||
|
||||
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
|
||||
|
||||
|
||||
async def test_get(client: TestClient) -> None:
|
||||
"""
|
||||
must return valid service status
|
||||
"""
|
||||
response = await client.get("/api/v1/ahriman")
|
||||
status = BuildStatus.from_json(await response.json())
|
||||
|
||||
assert response.status == 200
|
||||
assert status.status == BuildStatusEnum.Unknown
|
||||
|
||||
|
||||
async def test_post(client: TestClient) -> None:
|
||||
"""
|
||||
must update service status correctly
|
||||
"""
|
||||
payload = {"status": BuildStatusEnum.Success.value}
|
||||
post_response = await client.post("/api/v1/ahriman", json=payload)
|
||||
assert post_response.status == 204
|
||||
|
||||
response = await client.get("/api/v1/ahriman")
|
||||
status = BuildStatus.from_json(await response.json())
|
||||
|
||||
assert response.status == 200
|
||||
assert status.status == BuildStatusEnum.Success
|
||||
|
||||
|
||||
async def test_post_exception(client: TestClient) -> None:
|
||||
"""
|
||||
must raise exception on invalid payload
|
||||
"""
|
||||
post_response = await client.post("/api/v1/ahriman", json={})
|
||||
assert post_response.status == 400
|
19
tests/ahriman/web/views/test_view_index.py
Normal file
19
tests/ahriman/web/views/test_view_index.py
Normal file
@ -0,0 +1,19 @@
|
||||
from pytest_aiohttp import TestClient
|
||||
|
||||
|
||||
async def test_get(client: TestClient) -> None:
|
||||
"""
|
||||
must generate status page correctly (/)
|
||||
"""
|
||||
response = await client.get("/")
|
||||
assert response.status == 200
|
||||
assert await response.text()
|
||||
|
||||
|
||||
async def test_get_index(client: TestClient) -> None:
|
||||
"""
|
||||
must generate status page correctly (/index.html)
|
||||
"""
|
||||
response = await client.get("/index.html")
|
||||
assert response.status == 200
|
||||
assert await response.text()
|
117
tests/ahriman/web/views/test_view_package.py
Normal file
117
tests/ahriman/web/views/test_view_package.py
Normal file
@ -0,0 +1,117 @@
|
||||
from pytest_aiohttp import TestClient
|
||||
|
||||
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
|
||||
from ahriman.models.package import Package
|
||||
|
||||
|
||||
async def test_get(client: TestClient, package_ahriman: Package, package_python_schedule: Package) -> None:
|
||||
"""
|
||||
must return status for specific package
|
||||
"""
|
||||
await client.post(f"/api/v1/packages/{package_ahriman.base}",
|
||||
json={"status": BuildStatusEnum.Success.value, "package": package_ahriman.view()})
|
||||
await client.post(f"/api/v1/packages/{package_python_schedule.base}",
|
||||
json={"status": BuildStatusEnum.Success.value, "package": package_python_schedule.view()})
|
||||
|
||||
response = await client.get(f"/api/v1/packages/{package_ahriman.base}")
|
||||
assert response.status == 200
|
||||
|
||||
packages = [Package.from_json(item["package"]) for item in await response.json()]
|
||||
assert packages
|
||||
assert {package.base for package in packages} == {package_ahriman.base}
|
||||
|
||||
|
||||
async def test_get_not_found(client: TestClient, package_ahriman: Package) -> None:
|
||||
"""
|
||||
must return Not Found for unknown package
|
||||
"""
|
||||
response = await client.get(f"/api/v1/packages/{package_ahriman.base}")
|
||||
assert response.status == 404
|
||||
|
||||
|
||||
async def test_delete(client: TestClient, package_ahriman: Package, package_python_schedule: Package) -> None:
|
||||
"""
|
||||
must delete single base
|
||||
"""
|
||||
await client.post(f"/api/v1/packages/{package_ahriman.base}",
|
||||
json={"status": BuildStatusEnum.Success.value, "package": package_ahriman.view()})
|
||||
await client.post(f"/api/v1/packages/{package_python_schedule.base}",
|
||||
json={"status": BuildStatusEnum.Success.value, "package": package_python_schedule.view()})
|
||||
|
||||
response = await client.delete(f"/api/v1/packages/{package_ahriman.base}")
|
||||
assert response.status == 204
|
||||
|
||||
response = await client.get(f"/api/v1/packages/{package_ahriman.base}")
|
||||
assert response.status == 404
|
||||
|
||||
response = await client.get(f"/api/v1/packages/{package_python_schedule.base}")
|
||||
assert response.status == 200
|
||||
|
||||
|
||||
async def test_delete_unknown(client: TestClient, package_ahriman: Package, package_python_schedule: Package) -> None:
|
||||
"""
|
||||
must suppress errors on unknown package deletion
|
||||
"""
|
||||
await client.post(f"/api/v1/packages/{package_python_schedule.base}",
|
||||
json={"status": BuildStatusEnum.Success.value, "package": package_python_schedule.view()})
|
||||
|
||||
response = await client.delete(f"/api/v1/packages/{package_ahriman.base}")
|
||||
assert response.status == 204
|
||||
|
||||
response = await client.get(f"/api/v1/packages/{package_ahriman.base}")
|
||||
assert response.status == 404
|
||||
|
||||
response = await client.get(f"/api/v1/packages/{package_python_schedule.base}")
|
||||
assert response.status == 200
|
||||
|
||||
|
||||
async def test_post(client: TestClient, package_ahriman: Package) -> None:
|
||||
"""
|
||||
must update package status
|
||||
"""
|
||||
post_response = await client.post(
|
||||
f"/api/v1/packages/{package_ahriman.base}",
|
||||
json={"status": BuildStatusEnum.Success.value, "package": package_ahriman.view()})
|
||||
assert post_response.status == 204
|
||||
|
||||
response = await client.get(f"/api/v1/packages/{package_ahriman.base}")
|
||||
assert response.status == 200
|
||||
|
||||
|
||||
async def test_post_exception(client: TestClient, package_ahriman: Package) -> None:
|
||||
"""
|
||||
must raise exception on invalid payload
|
||||
"""
|
||||
post_response = await client.post(f"/api/v1/packages/{package_ahriman.base}", json={})
|
||||
assert post_response.status == 400
|
||||
|
||||
|
||||
async def test_post_light(client: TestClient, package_ahriman: Package) -> None:
|
||||
"""
|
||||
must update package status only
|
||||
"""
|
||||
post_response = await client.post(
|
||||
f"/api/v1/packages/{package_ahriman.base}",
|
||||
json={"status": BuildStatusEnum.Unknown.value, "package": package_ahriman.view()})
|
||||
assert post_response.status == 204
|
||||
|
||||
post_response = await client.post(
|
||||
f"/api/v1/packages/{package_ahriman.base}", json={"status": BuildStatusEnum.Success.value})
|
||||
assert post_response.status == 204
|
||||
|
||||
response = await client.get(f"/api/v1/packages/{package_ahriman.base}")
|
||||
assert response.status == 200
|
||||
statuses = {
|
||||
Package.from_json(item["package"]).base: BuildStatus.from_json(item["status"])
|
||||
for item in await response.json()
|
||||
}
|
||||
assert statuses[package_ahriman.base].status == BuildStatusEnum.Success
|
||||
|
||||
|
||||
async def test_post_not_found(client: TestClient, package_ahriman: Package) -> None:
|
||||
"""
|
||||
must raise exception on status update for unknown package
|
||||
"""
|
||||
post_response = await client.post(
|
||||
f"/api/v1/packages/{package_ahriman.base}", json={"status": BuildStatusEnum.Success.value})
|
||||
assert post_response.status == 400
|
32
tests/ahriman/web/views/test_view_packages.py
Normal file
32
tests/ahriman/web/views/test_view_packages.py
Normal file
@ -0,0 +1,32 @@
|
||||
from pytest_aiohttp import TestClient
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from ahriman.models.build_status import BuildStatusEnum
|
||||
from ahriman.models.package import Package
|
||||
|
||||
|
||||
async def test_get(client: TestClient, package_ahriman: Package, package_python_schedule: Package) -> None:
|
||||
"""
|
||||
must return status for all packages
|
||||
"""
|
||||
await client.post(f"/api/v1/packages/{package_ahriman.base}",
|
||||
json={"status": BuildStatusEnum.Success.value, "package": package_ahriman.view()})
|
||||
await client.post(f"/api/v1/packages/{package_python_schedule.base}",
|
||||
json={"status": BuildStatusEnum.Success.value, "package": package_python_schedule.view()})
|
||||
|
||||
response = await client.get("/api/v1/packages")
|
||||
assert response.status == 200
|
||||
|
||||
packages = [Package.from_json(item["package"]) for item in await response.json()]
|
||||
assert packages
|
||||
assert {package.base for package in packages} == {package_ahriman.base, package_python_schedule.base}
|
||||
|
||||
|
||||
async def test_post(client: TestClient, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must be able to reload packages
|
||||
"""
|
||||
load_mock = mocker.patch("ahriman.core.status.watcher.Watcher.load")
|
||||
response = await client.post("/api/v1/packages")
|
||||
assert response.status == 204
|
||||
load_mock.assert_called_once()
|
44
tests/testresources/core/ahriman.ini
Normal file
44
tests/testresources/core/ahriman.ini
Normal file
@ -0,0 +1,44 @@
|
||||
[settings]
|
||||
logging = logging.ini
|
||||
|
||||
[alpm]
|
||||
aur_url = https://aur.archlinux.org
|
||||
database = /var/lib/pacman
|
||||
repositories = core extra community multilib
|
||||
root = /
|
||||
|
||||
[build]
|
||||
archbuild_flags =
|
||||
build_command = extra-x86_64-build
|
||||
ignore_packages =
|
||||
makechrootpkg_flags =
|
||||
makepkg_flags = --skippgpcheck
|
||||
|
||||
[repository]
|
||||
name = aur-clone
|
||||
root = /var/lib/ahriman
|
||||
|
||||
[sign]
|
||||
target =
|
||||
key =
|
||||
|
||||
[report]
|
||||
target =
|
||||
|
||||
[html]
|
||||
path =
|
||||
homepage =
|
||||
link_path =
|
||||
template_path = ../web/templates/repo-index.jinja2
|
||||
|
||||
[upload]
|
||||
target =
|
||||
|
||||
[rsync]
|
||||
remote =
|
||||
|
||||
[s3]
|
||||
bucket =
|
||||
|
||||
[web]
|
||||
templates = ../web/templates
|
59
tests/testresources/core/logging.ini
Normal file
59
tests/testresources/core/logging.ini
Normal file
@ -0,0 +1,59 @@
|
||||
[loggers]
|
||||
keys = root,builder,build_details,http
|
||||
|
||||
[handlers]
|
||||
keys = console_handler,build_file_handler,file_handler,http_handler
|
||||
|
||||
[formatters]
|
||||
keys = generic_format
|
||||
|
||||
[handler_console_handler]
|
||||
class = StreamHandler
|
||||
level = DEBUG
|
||||
formatter = generic_format
|
||||
args = (sys.stderr,)
|
||||
|
||||
[handler_file_handler]
|
||||
class = logging.handlers.RotatingFileHandler
|
||||
level = DEBUG
|
||||
formatter = generic_format
|
||||
args = ("/var/log/ahriman/ahriman.log", "a", 20971520, 20)
|
||||
|
||||
[handler_build_file_handler]
|
||||
class = logging.handlers.RotatingFileHandler
|
||||
level = DEBUG
|
||||
formatter = generic_format
|
||||
args = ("/var/log/ahriman/build.log", "a", 20971520, 20)
|
||||
|
||||
[handler_http_handler]
|
||||
class = logging.handlers.RotatingFileHandler
|
||||
level = DEBUG
|
||||
formatter = generic_format
|
||||
args = ("/var/log/ahriman/http.log", "a", 20971520, 20)
|
||||
|
||||
[formatter_generic_format]
|
||||
format = [%(levelname)s %(asctime)s] [%(filename)s:%(lineno)d] [%(funcName)s]: %(message)s
|
||||
datefmt =
|
||||
|
||||
[logger_root]
|
||||
level = DEBUG
|
||||
handlers = file_handler
|
||||
qualname = root
|
||||
|
||||
[logger_builder]
|
||||
level = DEBUG
|
||||
handlers = file_handler
|
||||
qualname = builder
|
||||
propagate = 0
|
||||
|
||||
[logger_build_details]
|
||||
level = DEBUG
|
||||
handlers = build_file_handler
|
||||
qualname = build_details
|
||||
propagate = 0
|
||||
|
||||
[logger_http]
|
||||
level = DEBUG
|
||||
handlers = http_handler
|
||||
qualname = http
|
||||
propagate = 0
|
17
tests/testresources/models/package_tpacpi-bat-git_srcinfo
Normal file
17
tests/testresources/models/package_tpacpi-bat-git_srcinfo
Normal file
@ -0,0 +1,17 @@
|
||||
pkgbase = tpacpi-bat-git
|
||||
pkgdesc = A Perl script with ACPI calls for recent ThinkPads which are not supported by tp_smapi
|
||||
pkgver = 3.1.r13.g4959b52
|
||||
pkgrel = 1
|
||||
url = https://github.com/teleshoes/tpacpi-bat
|
||||
arch = any
|
||||
license = GPL3
|
||||
makedepends = git
|
||||
depends = perl
|
||||
depends = acpi_call
|
||||
provides = tpacpi-bat
|
||||
conflicts = tpacpi-bat
|
||||
backup = etc/conf.d/tpacpi
|
||||
source = git+https://github.com/teleshoes/tpacpi-bat.git
|
||||
b2sums = SKIP
|
||||
|
||||
pkgname = tpacpi-bat-git
|
21
tests/testresources/models/package_yay_srcinfo
Normal file
21
tests/testresources/models/package_yay_srcinfo
Normal file
@ -0,0 +1,21 @@
|
||||
pkgbase = yay
|
||||
pkgdesc = Yet another yogurt. Pacman wrapper and AUR helper written in go.
|
||||
pkgver = 10.2.0
|
||||
pkgrel = 1
|
||||
url = https://github.com/Jguer/yay
|
||||
arch = i686
|
||||
arch = pentium4
|
||||
arch = x86_64
|
||||
arch = arm
|
||||
arch = armv7h
|
||||
arch = armv6h
|
||||
arch = aarch64
|
||||
license = GPL3
|
||||
makedepends = go
|
||||
depends = pacman>5
|
||||
depends = git
|
||||
optdepends = sudo
|
||||
source = yay-10.2.0.tar.gz::https://github.com/Jguer/yay/archive/v10.2.0.tar.gz
|
||||
sha256sums = 755d049ec09cc20bdcbb004b12ab4e35ba3bb94a7dce9dfa544d24f87deda8aa
|
||||
|
||||
pkgname = yay
|
54
tests/testresources/web/templates/build-status.jinja2
Normal file
54
tests/testresources/web/templates/build-status.jinja2
Normal file
@ -0,0 +1,54 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>{{ repository|e }}</title>
|
||||
|
||||
{% include "style.jinja2" %}
|
||||
|
||||
{% include "sorttable.jinja2" %}
|
||||
{% include "search.jinja2" %}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="root">
|
||||
<h1>ahriman
|
||||
<img src="https://img.shields.io/badge/version-{{ version|e }}-informational" alt="{{ version|e }}">
|
||||
<img src="https://img.shields.io/badge/architecture-{{ architecture|e }}-informational" alt="{{ architecture|e }}">
|
||||
<img src="https://img.shields.io/badge/service%20status-{{ service.status|e }}-{{ service.status_color|e }}" alt="{{ service.status|e }}" title="{{ service.timestamp|e }}">
|
||||
</h1>
|
||||
|
||||
{% include "search-line.jinja2" %}
|
||||
|
||||
<section class="element">
|
||||
<table class="sortable search-table">
|
||||
<tr class="header">
|
||||
<th>package base</th>
|
||||
<th>packages</th>
|
||||
<th>version</th>
|
||||
<th>last update</th>
|
||||
<th>status</th>
|
||||
</tr>
|
||||
|
||||
{% for package in packages %}
|
||||
<tr class="package">
|
||||
<td class="include-search"><a href="{{ package.web_url|e }}" title="{{ package.base|e }}">{{ package.base|e }}</a></td>
|
||||
<td class="include-search">{{ package.packages|join("<br>"|safe) }}</td>
|
||||
<td>{{ package.version|e }}</td>
|
||||
<td>{{ package.timestamp|e }}</td>
|
||||
<td class="status package-{{ package.status|e }}">{{ package.status|e }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<footer>
|
||||
<ul class="navigation">
|
||||
<li><a href="https://github.com/arcan1s/ahriman" title="sources">ahriman</a></li>
|
||||
<li><a href="https://github.com/arcan1s/ahriman/releases" title="releases list">releases</a></li>
|
||||
<li><a href="https://github.com/arcan1s/ahriman/issues" title="issues tracker">report a bug</a></li>
|
||||
</ul>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
62
tests/testresources/web/templates/repo-index.jinja2
Normal file
62
tests/testresources/web/templates/repo-index.jinja2
Normal file
@ -0,0 +1,62 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>{{ repository|e }}</title>
|
||||
|
||||
{% include "style.jinja2" %}
|
||||
|
||||
{% include "sorttable.jinja2" %}
|
||||
{% include "search.jinja2" %}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="root">
|
||||
<h1>Archlinux user repository</h1>
|
||||
|
||||
<section class="element">
|
||||
{% if pgp_key is not none %}
|
||||
<p>This repository is signed with <a href="http://keys.gnupg.net/pks/lookup?search=0x{{ pgp_key|e }}&fingerprint=on&op=index" title="key search">{{ pgp_key|e }}</a> by default.</p>
|
||||
{% endif %}
|
||||
|
||||
<code>
|
||||
$ cat /etc/pacman.conf<br>
|
||||
[{{ repository|e }}]<br>
|
||||
Server = {{ link_path|e }}<br>
|
||||
SigLevel = Database{% if has_repo_signed %}Required{% else %}Never{% endif %} Package{% if has_package_signed %}Required{% else %}Never{% endif %} TrustedOnly
|
||||
</code>
|
||||
</section>
|
||||
|
||||
{% include "search-line.jinja2" %}
|
||||
|
||||
<section class="element">
|
||||
<table class="sortable search-table">
|
||||
<tr class="header">
|
||||
<th>package</th>
|
||||
<th>version</th>
|
||||
<th>archive size</th>
|
||||
<th>installed size</th>
|
||||
<th>build date</th>
|
||||
</tr>
|
||||
|
||||
{% for package in packages %}
|
||||
<tr class="package">
|
||||
<td class="include-search"><a href="{{ link_path|e }}/{{ package.filename|e }}" title="{{ package.name|e }}">{{ package.name|e }}</a></td>
|
||||
<td>{{ package.version|e }}</td>
|
||||
<td>{{ package.archive_size|e }}</td>
|
||||
<td>{{ package.installed_size|e }}</td>
|
||||
<td>{{ package.build_date|e }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<footer>
|
||||
<ul class="navigation">
|
||||
{% if homepage is not none %}
|
||||
<li><a href="{{ homepage|e }}" title="homepage">Homepage</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
3
tests/testresources/web/templates/search-line.jinja2
Normal file
3
tests/testresources/web/templates/search-line.jinja2
Normal file
@ -0,0 +1,3 @@
|
||||
<section class="element">
|
||||
<input type="search" id="search" onkeyup="searchInTable()" placeholder="search for package" title="search for package"/>
|
||||
</section>
|
25
tests/testresources/web/templates/search.jinja2
Normal file
25
tests/testresources/web/templates/search.jinja2
Normal file
@ -0,0 +1,25 @@
|
||||
<script type="text/javascript">
|
||||
function searchInTable() {
|
||||
const input = document.getElementById("search");
|
||||
const filter = input.value.toLowerCase();
|
||||
const tables = document.getElementsByClassName("search-table");
|
||||
|
||||
for (let i = 0; i < tables.length; i++) {
|
||||
const tr = tables[i].getElementsByTagName("tr");
|
||||
// from 1 coz of header
|
||||
for (let i = 1; i < tr.length; i++) {
|
||||
let td = tr[i].getElementsByClassName("include-search");
|
||||
let display = "none";
|
||||
for (let j = 0; j < td.length; j++) {
|
||||
if (td[j].tagName.toLowerCase() === "td") {
|
||||
if (td[j].innerHTML.toLowerCase().indexOf(filter) > -1) {
|
||||
display = "";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
tr[i].style.display = display;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
1
tests/testresources/web/templates/sorttable.jinja2
Normal file
1
tests/testresources/web/templates/sorttable.jinja2
Normal file
@ -0,0 +1 @@
|
||||
<script src="https://www.kryogenix.org/code/browser/sorttable/sorttable.js"></script>
|
136
tests/testresources/web/templates/style.jinja2
Normal file
136
tests/testresources/web/templates/style.jinja2
Normal file
@ -0,0 +1,136 @@
|
||||
<style>
|
||||
:root {
|
||||
--color-building: 255, 255, 146;
|
||||
--color-failed: 255, 94, 94;
|
||||
--color-pending: 255, 255, 146;
|
||||
--color-success: 94, 255, 94;
|
||||
--color-unknown: 225, 225, 225;
|
||||
|
||||
--color-header: 200, 200, 255;
|
||||
--color-hover: 255, 255, 225;
|
||||
--color-line-blue: 235, 235, 255;
|
||||
--color-line-white: 255, 255, 255;
|
||||
}
|
||||
|
||||
@keyframes blink-building {
|
||||
0% { background-color: rgba(var(--color-building), 1.0); }
|
||||
10% { background-color: rgba(var(--color-building), 0.9); }
|
||||
20% { background-color: rgba(var(--color-building), 0.8); }
|
||||
30% { background-color: rgba(var(--color-building), 0.7); }
|
||||
40% { background-color: rgba(var(--color-building), 0.6); }
|
||||
50% { background-color: rgba(var(--color-building), 0.5); }
|
||||
60% { background-color: rgba(var(--color-building), 0.4); }
|
||||
70% { background-color: rgba(var(--color-building), 0.3); }
|
||||
80% { background-color: rgba(var(--color-building), 0.2); }
|
||||
90% { background-color: rgba(var(--color-building), 0.1); }
|
||||
100% { background-color: rgba(var(--color-building), 0.0); }
|
||||
}
|
||||
|
||||
div.root {
|
||||
width: 70%;
|
||||
padding: 15px 15% 0;
|
||||
}
|
||||
|
||||
section.element, footer {
|
||||
width: 100%;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
code, input, table {
|
||||
width: inherit;
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
tr.package:nth-child(odd) {
|
||||
background-color: rgba(var(--color-line-white), 1.0);
|
||||
}
|
||||
|
||||
tr.package:nth-child(even) {
|
||||
background-color: rgba(var(--color-line-blue), 1.0);
|
||||
}
|
||||
|
||||
tr.package:hover {
|
||||
background-color: rgba(var(--color-hover), 1.0);
|
||||
}
|
||||
|
||||
tr.header{
|
||||
background-color: rgba(var(--color-header), 1.0);
|
||||
}
|
||||
|
||||
td.status {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
td.package-unknown {
|
||||
background-color: rgba(var(--color-unknown), 1.0);
|
||||
}
|
||||
td.package-pending {
|
||||
background-color: rgba(var(--color-pending), 1.0);
|
||||
}
|
||||
td.package-building {
|
||||
background-color: rgba(var(--color-building), 1.0);
|
||||
animation-name: blink-building;
|
||||
animation-duration: 1s;
|
||||
animation-timing-function: linear;
|
||||
animation-iteration-count: infinite;
|
||||
animation-direction: alternate;
|
||||
}
|
||||
td.package-failed {
|
||||
background-color: rgba(var(--color-failed), 1.0);
|
||||
}
|
||||
td.package-success {
|
||||
background-color: rgba(var(--color-success), 1.0);
|
||||
}
|
||||
|
||||
li.service-unknown {
|
||||
background-color: rgba(var(--color-unknown), 1.0);
|
||||
}
|
||||
li.service-building {
|
||||
background-color: rgba(var(--color-building), 1.0);
|
||||
animation-name: blink-building;
|
||||
animation-duration: 1s;
|
||||
animation-timing-function: linear;
|
||||
animation-iteration-count: infinite;
|
||||
animation-direction: alternate;
|
||||
}
|
||||
li.service-failed {
|
||||
background-color: rgba(var(--color-failed), 1.0);
|
||||
}
|
||||
li.service-success {
|
||||
background-color: rgba(var(--color-success), 1.0);
|
||||
}
|
||||
|
||||
ul.navigation {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
background-color: rgba(var(--color-header), 1.0);
|
||||
}
|
||||
|
||||
ul.navigation li {
|
||||
float: left;
|
||||
}
|
||||
|
||||
ul.navigation li.status {
|
||||
display: block;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
padding: 14px 16px;
|
||||
}
|
||||
|
||||
ul.navigation li a {
|
||||
display: block;
|
||||
color: black;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
padding: 14px 16px;
|
||||
}
|
||||
|
||||
ul.navigation li a:hover {
|
||||
background-color: rgba(var(--color-hover), 1.0);
|
||||
}
|
||||
</style>
|
Reference in New Issue
Block a user