complete status tests

This commit is contained in:
2021-03-27 17:28:42 +03:00
parent 077b80d345
commit afbd956fe2
16 changed files with 599 additions and 41 deletions

View File

@ -28,7 +28,7 @@ from typing import Literal, Optional, Type
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import DuplicateRun, UnsafeRun from ahriman.core.exceptions import DuplicateRun, UnsafeRun
from ahriman.core.watcher.client import Client from ahriman.core.status.client import Client
from ahriman.models.build_status import BuildStatusEnum from ahriman.models.build_status import BuildStatusEnum
@ -105,7 +105,7 @@ class Lock:
if self.path is None: if self.path is None:
return return
try: try:
self.path.touch() self.path.touch(exist_ok=False)
except FileExistsError: except FileExistsError:
raise DuplicateRun() raise DuplicateRun()

View File

@ -107,6 +107,15 @@ class SyncFailed(Exception):
Exception.__init__(self, "Sync failed") Exception.__init__(self, "Sync failed")
class UnknownPackage(Exception):
"""
exception for status watcher which will be thrown on unknown package
"""
def __init__(self, base: str) -> None:
Exception.__init__(self, f"Package base {base} is unknown")
class UnsafeRun(Exception): class UnsafeRun(Exception):
""" """
exception which will be raised in case if user is not owner of repository exception which will be raised in case if user is not owner of repository

View File

@ -25,7 +25,7 @@ from ahriman.core.alpm.pacman import Pacman
from ahriman.core.alpm.repo import Repo from ahriman.core.alpm.repo import Repo
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.sign.gpg import GPG from ahriman.core.sign.gpg import GPG
from ahriman.core.watcher.client import Client from ahriman.core.status.client import Client
from ahriman.models.repository_paths import RepositoryPaths from ahriman.models.repository_paths import RepositoryPaths

View File

@ -124,5 +124,5 @@ class Client:
if host is None or port is None: if host is None or port is None:
return Client() return Client()
from ahriman.core.watcher.web_client import WebClient from ahriman.core.status.web_client import WebClient
return WebClient(host, port) return WebClient(host, port)

View File

@ -24,6 +24,7 @@ from pathlib import Path
from typing import Any, Dict, List, Optional, Tuple from typing import Any, Dict, List, Optional, Tuple
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import UnknownPackage
from ahriman.core.repository.repository import Repository from ahriman.core.repository.repository import Repository
from ahriman.models.build_status import BuildStatus, BuildStatusEnum from ahriman.models.build_status import BuildStatus, BuildStatusEnum
from ahriman.models.package import Package from ahriman.models.package import Package
@ -80,8 +81,12 @@ class Watcher:
if not self.cache_path.is_file(): if not self.cache_path.is_file():
return return
with self.cache_path.open() as cache: with self.cache_path.open() as cache:
dump = json.load(cache) try:
for item in dump["packages"]: dump = json.load(cache)
except Exception:
self.logger.exception("cannot parse json from file")
dump = {}
for item in dump.get("packages", []):
try: try:
parse_single(item) parse_single(item)
except Exception: except Exception:
@ -110,7 +115,10 @@ class Watcher:
get current package base build status get current package base build status
:return: package and its status :return: package and its status
""" """
return self.known[base] try:
return self.known[base]
except KeyError:
raise UnknownPackage(base)
def load(self) -> None: def load(self) -> None:
""" """
@ -142,7 +150,10 @@ class Watcher:
:param package: optional new package description. In case if not set current properties will be used :param package: optional new package description. In case if not set current properties will be used
""" """
if package is None: if package is None:
package, _ = self.known[base] try:
package, _ = self.known[base]
except KeyError:
raise UnknownPackage(base)
full_status = BuildStatus(status) full_status = BuildStatus(status)
self.known[base] = (package, full_status) self.known[base] = (package, full_status)
self._cache_save() self._cache_save()

View File

@ -18,11 +18,11 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
import logging import logging
from typing import List, Optional, Tuple
import requests import requests
from ahriman.core.watcher.client import Client from typing import List, Optional, Tuple
from ahriman.core.status.client import Client
from ahriman.models.build_status import BuildStatusEnum, BuildStatus from ahriman.models.build_status import BuildStatusEnum, BuildStatus
from ahriman.models.package import Package from ahriman.models.package import Package

View File

@ -19,7 +19,7 @@
# #
from aiohttp.web import View from aiohttp.web import View
from ahriman.core.watcher.watcher import Watcher from ahriman.core.status.watcher import Watcher
class BaseView(View): class BaseView(View):

View File

@ -19,6 +19,7 @@
# #
from aiohttp.web import HTTPBadRequest, HTTPNoContent, HTTPNotFound, Response, json_response from aiohttp.web import HTTPBadRequest, HTTPNoContent, HTTPNotFound, Response, json_response
from ahriman.core.exceptions import UnknownPackage
from ahriman.models.build_status import BuildStatusEnum from ahriman.models.build_status import BuildStatusEnum
from ahriman.models.package import Package from ahriman.models.package import Package
from ahriman.web.views.base import BaseView from ahriman.web.views.base import BaseView
@ -38,7 +39,7 @@ class PackageView(BaseView):
try: try:
package, status = self.service.get(base) package, status = self.service.get(base)
except KeyError: except UnknownPackage:
raise HTTPNotFound() raise HTTPNotFound()
response = [ response = [
@ -83,7 +84,7 @@ class PackageView(BaseView):
try: try:
self.service.update(base, status, package) self.service.update(base, status, package)
except KeyError: except UnknownPackage:
raise HTTPBadRequest(text=f"Package {base} is unknown, but no package body set") raise HTTPBadRequest(text=f"Package {base} is unknown, but no package body set")
return HTTPNoContent() return HTTPNoContent()

View File

@ -25,7 +25,7 @@ from aiohttp import web
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import InitializeException from ahriman.core.exceptions import InitializeException
from ahriman.core.watcher.watcher import Watcher from ahriman.core.status.watcher import Watcher
from ahriman.web.middlewares.exception_handler import exception_handler from ahriman.web.middlewares.exception_handler import exception_handler
from ahriman.web.routes import setup_routes from ahriman.web.routes import setup_routes

View File

@ -14,14 +14,14 @@ def test_process_build(executor: Executor, package_ahriman: Package, mocker: Moc
mocker.patch("ahriman.core.build_tools.task.Task.build", return_value=[Path(package_ahriman.base)]) 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("ahriman.core.build_tools.task.Task.init")
move_mock = mocker.patch("shutil.move") move_mock = mocker.patch("shutil.move")
watcher_client_mock = mocker.patch("ahriman.core.watcher.client.Client.set_building") status_client_mock = mocker.patch("ahriman.core.status.client.Client.set_building")
# must return list of built packages # must return list of built packages
assert executor.process_build([package_ahriman]) == [package_ahriman] assert executor.process_build([package_ahriman]) == [package_ahriman]
# must move files (once) # must move files (once)
move_mock.assert_called_once() move_mock.assert_called_once()
# must update status # must update status
watcher_client_mock.assert_called_once() status_client_mock.assert_called_once()
# must clear directory # must clear directory
from ahriman.core.repository.cleaner import Cleaner from ahriman.core.repository.cleaner import Cleaner
Cleaner.clear_build.assert_called_once() Cleaner.clear_build.assert_called_once()
@ -35,10 +35,10 @@ def test_process_build_failure(executor: Executor, package_ahriman: Package, moc
mocker.patch("ahriman.core.build_tools.task.Task.build", return_value=[Path(package_ahriman.base)]) 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("ahriman.core.build_tools.task.Task.init")
mocker.patch("shutil.move", side_effect=Exception()) mocker.patch("shutil.move", side_effect=Exception())
watcher_client_mock = mocker.patch("ahriman.core.watcher.client.Client.set_failed") status_client_mock = mocker.patch("ahriman.core.status.client.Client.set_failed")
executor.process_build([package_ahriman]) executor.process_build([package_ahriman])
watcher_client_mock.assert_called_once() status_client_mock.assert_called_once()
def test_process_remove_base(executor: Executor, package_ahriman: Package, mocker: MockerFixture) -> None: def test_process_remove_base(executor: Executor, package_ahriman: Package, mocker: MockerFixture) -> None:
@ -47,13 +47,13 @@ def test_process_remove_base(executor: Executor, package_ahriman: Package, mocke
""" """
mocker.patch("ahriman.core.repository.executor.Executor.packages", return_value=[package_ahriman]) mocker.patch("ahriman.core.repository.executor.Executor.packages", return_value=[package_ahriman])
repo_remove_mock = mocker.patch("ahriman.core.alpm.repo.Repo.remove") repo_remove_mock = mocker.patch("ahriman.core.alpm.repo.Repo.remove")
watcher_client_mock = mocker.patch("ahriman.core.watcher.client.Client.remove") status_client_mock = mocker.patch("ahriman.core.status.client.Client.remove")
executor.process_remove([package_ahriman.base]) executor.process_remove([package_ahriman.base])
# must remove via alpm wrapper # must remove via alpm wrapper
repo_remove_mock.assert_called_once() repo_remove_mock.assert_called_once()
# must update status # must update status
watcher_client_mock.assert_called_once() status_client_mock.assert_called_once()
def test_process_remove_base_multiple(executor: Executor, package_python_schedule: Package, def test_process_remove_base_multiple(executor: Executor, package_python_schedule: Package,
@ -63,7 +63,7 @@ def test_process_remove_base_multiple(executor: Executor, package_python_schedul
""" """
mocker.patch("ahriman.core.repository.executor.Executor.packages", return_value=[package_python_schedule]) mocker.patch("ahriman.core.repository.executor.Executor.packages", return_value=[package_python_schedule])
repo_remove_mock = mocker.patch("ahriman.core.alpm.repo.Repo.remove") repo_remove_mock = mocker.patch("ahriman.core.alpm.repo.Repo.remove")
watcher_client_mock = mocker.patch("ahriman.core.watcher.client.Client.remove") status_client_mock = mocker.patch("ahriman.core.status.client.Client.remove")
executor.process_remove([package_python_schedule.base]) executor.process_remove([package_python_schedule.base])
# must remove via alpm wrapper # must remove via alpm wrapper
@ -72,7 +72,7 @@ def test_process_remove_base_multiple(executor: Executor, package_python_schedul
for package, props in package_python_schedule.packages.items() for package, props in package_python_schedule.packages.items()
], any_order=True) ], any_order=True)
# must update status # must update status
watcher_client_mock.assert_called_once() status_client_mock.assert_called_once()
def test_process_remove_base_single(executor: Executor, package_python_schedule: Package, def test_process_remove_base_single(executor: Executor, package_python_schedule: Package,
@ -82,13 +82,13 @@ def test_process_remove_base_single(executor: Executor, package_python_schedule:
""" """
mocker.patch("ahriman.core.repository.executor.Executor.packages", return_value=[package_python_schedule]) mocker.patch("ahriman.core.repository.executor.Executor.packages", return_value=[package_python_schedule])
repo_remove_mock = mocker.patch("ahriman.core.alpm.repo.Repo.remove") repo_remove_mock = mocker.patch("ahriman.core.alpm.repo.Repo.remove")
watcher_client_mock = mocker.patch("ahriman.core.watcher.client.Client.remove") status_client_mock = mocker.patch("ahriman.core.status.client.Client.remove")
executor.process_remove(["python2-schedule"]) executor.process_remove(["python2-schedule"])
# must remove via alpm wrapper # must remove via alpm wrapper
repo_remove_mock.assert_called_once() repo_remove_mock.assert_called_once()
# must not update status # must not update status
watcher_client_mock.assert_not_called() status_client_mock.assert_not_called()
def test_process_remove_nothing(executor: Executor, package_ahriman: Package, package_python_schedule: Package, def test_process_remove_nothing(executor: Executor, package_ahriman: Package, package_python_schedule: Package,
@ -131,7 +131,7 @@ def test_process_update(executor: Executor, package_ahriman: Package, mocker: Mo
move_mock = mocker.patch("shutil.move") move_mock = mocker.patch("shutil.move")
repo_add_mock = mocker.patch("ahriman.core.alpm.repo.Repo.add") 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]) sign_package_mock = mocker.patch("ahriman.core.sign.gpg.GPG.sign_package", side_effect=lambda fn, _: [fn])
watcher_client_mock = mocker.patch("ahriman.core.watcher.client.Client.set_success") status_client_mock = mocker.patch("ahriman.core.status.client.Client.set_success")
# must return complete # must return complete
assert executor.process_update([Path(package.filename) for package in package_ahriman.packages.values()]) assert executor.process_update([Path(package.filename) for package in package_ahriman.packages.values()])
@ -142,7 +142,7 @@ def test_process_update(executor: Executor, package_ahriman: Package, mocker: Mo
# must add package # must add package
repo_add_mock.assert_called_once() repo_add_mock.assert_called_once()
# must update status # must update status
watcher_client_mock.assert_called_once() status_client_mock.assert_called_once()
# must clear directory # must clear directory
from ahriman.core.repository.cleaner import Cleaner from ahriman.core.repository.cleaner import Cleaner
Cleaner.clear_packages.assert_called_once() Cleaner.clear_packages.assert_called_once()
@ -156,14 +156,14 @@ def test_process_update_group(executor: Executor, package_python_schedule: Packa
mocker.patch("shutil.move") mocker.patch("shutil.move")
mocker.patch("ahriman.models.package.Package.load", return_value=package_python_schedule) mocker.patch("ahriman.models.package.Package.load", return_value=package_python_schedule)
repo_add_mock = mocker.patch("ahriman.core.alpm.repo.Repo.add") repo_add_mock = mocker.patch("ahriman.core.alpm.repo.Repo.add")
watcher_client_mock = mocker.patch("ahriman.core.watcher.client.Client.set_success") 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()]) executor.process_update([Path(package.filename) for package in package_python_schedule.packages.values()])
repo_add_mock.assert_has_calls([ repo_add_mock.assert_has_calls([
mock.call(executor.paths.repository / package.filename) mock.call(executor.paths.repository / package.filename)
for package in package_python_schedule.packages.values() for package in package_python_schedule.packages.values()
], any_order=True) ], any_order=True)
watcher_client_mock.assert_called_with(package_python_schedule) status_client_mock.assert_called_with(package_python_schedule)
def test_process_update_failed(executor: Executor, package_ahriman: Package, mocker: MockerFixture) -> None: def test_process_update_failed(executor: Executor, package_ahriman: Package, mocker: MockerFixture) -> None:
@ -172,10 +172,10 @@ def test_process_update_failed(executor: Executor, package_ahriman: Package, moc
""" """
mocker.patch("shutil.move", side_effect=Exception()) mocker.patch("shutil.move", side_effect=Exception())
mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman) mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman)
watcher_client_mock = mocker.patch("ahriman.core.watcher.client.Client.set_failed") 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()]) executor.process_update([Path(package.filename) for package in package_ahriman.packages.values()])
watcher_client_mock.assert_called_once() status_client_mock.assert_called_once()
def test_process_update_failed_on_load(executor: Executor, package_ahriman: Package, mocker: MockerFixture) -> None: def test_process_update_failed_on_load(executor: Executor, package_ahriman: Package, mocker: MockerFixture) -> None:

View File

@ -7,28 +7,28 @@ from ahriman.models.package import Package
def test_updates_aur(update_handler: UpdateHandler, package_ahriman: Package, def test_updates_aur(update_handler: UpdateHandler, package_ahriman: Package,
mocker: MockerFixture) -> None: mocker: MockerFixture) -> None:
""" """
must provide updates with status watcher updates must provide updates with status updates
""" """
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[package_ahriman]) 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.is_outdated", return_value=True)
mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman) mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman)
watcher_client_mock = mocker.patch("ahriman.core.watcher.client.Client.set_pending") status_client_mock = mocker.patch("ahriman.core.status.client.Client.set_pending")
assert update_handler.updates_aur([], False) == [package_ahriman] assert update_handler.updates_aur([], False) == [package_ahriman]
watcher_client_mock.assert_called_once() status_client_mock.assert_called_once()
def test_updates_aur_failed(update_handler: UpdateHandler, package_ahriman: Package, def test_updates_aur_failed(update_handler: UpdateHandler, package_ahriman: Package,
mocker: MockerFixture) -> None: mocker: MockerFixture) -> None:
""" """
must update status watcher via client for failed load must update status via client for failed load
""" """
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[package_ahriman]) mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[package_ahriman])
mocker.patch("ahriman.models.package.Package.load", side_effect=Exception()) mocker.patch("ahriman.models.package.Package.load", side_effect=Exception())
watcher_client_mock = mocker.patch("ahriman.core.watcher.client.Client.set_failed") status_client_mock = mocker.patch("ahriman.core.status.client.Client.set_failed")
update_handler.updates_aur([], False) update_handler.updates_aur([], False)
watcher_client_mock.assert_called_once() status_client_mock.assert_called_once()
def test_updates_aur_filter(update_handler: UpdateHandler, package_ahriman: Package, package_python_schedule: Package, def test_updates_aur_filter(update_handler: UpdateHandler, package_ahriman: Package, package_python_schedule: Package,
@ -92,10 +92,10 @@ def test_updates_manual_status_known(update_handler: UpdateHandler, package_ahri
mocker.patch("pathlib.Path.iterdir", return_value=[package_ahriman.base]) 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.core.repository.update_handler.UpdateHandler.packages", return_value=[package_ahriman])
mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman) mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman)
watcher_client_mock = mocker.patch("ahriman.core.watcher.client.Client.set_pending") status_client_mock = mocker.patch("ahriman.core.status.client.Client.set_pending")
update_handler.updates_manual() update_handler.updates_manual()
watcher_client_mock.assert_called_once() status_client_mock.assert_called_once()
def test_updates_manual_status_unknown(update_handler: UpdateHandler, package_ahriman: Package, def test_updates_manual_status_unknown(update_handler: UpdateHandler, package_ahriman: Package,
@ -106,10 +106,10 @@ def test_updates_manual_status_unknown(update_handler: UpdateHandler, package_ah
mocker.patch("pathlib.Path.iterdir", return_value=[package_ahriman.base]) mocker.patch("pathlib.Path.iterdir", return_value=[package_ahriman.base])
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[]) mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[])
mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman) mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman)
watcher_client_mock = mocker.patch("ahriman.core.watcher.client.Client.set_unknown") status_client_mock = mocker.patch("ahriman.core.status.client.Client.set_unknown")
update_handler.updates_manual() update_handler.updates_manual()
watcher_client_mock.assert_called_once() status_client_mock.assert_called_once()
def test_updates_manual_with_failures(update_handler: UpdateHandler, package_ahriman: Package, def test_updates_manual_with_failures(update_handler: UpdateHandler, package_ahriman: Package,

View File

@ -0,0 +1,39 @@
import pytest
from pytest_mock import MockerFixture
from typing import Any, Dict
from ahriman.core.configuration import Configuration
from ahriman.core.status.client import Client
from ahriman.core.status.watcher import Watcher
from ahriman.core.status.web_client import WebClient
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
from ahriman.models.package import Package
# 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 watcher(configuration: Configuration, mocker: MockerFixture) -> Watcher:
mocker.patch("pathlib.Path.mkdir")
return Watcher("x86_64", configuration)
@pytest.fixture
def web_client() -> WebClient:
return WebClient("localhost", 8080)

View 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)

View 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

View 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)