feat: allow to use one application for multiple repositories (#111)

* allow to use one application for multiple repositories

* update tests

* handle None append argument everywhere

* rewrite repository definition logic

* drop optional flags from docs

* support of new schema in systemd units

* add migration docs and ability to migrate tree automatically

* use repostory id instead

* verbose multiarchitectureerror

* object path support for s3 sync

* fix tests after rebase
This commit is contained in:
2023-09-08 03:42:28 +03:00
parent c915d68c97
commit efde0b2e86
191 changed files with 3441 additions and 1319 deletions

View File

@ -19,12 +19,13 @@ def test_init_with_local_cache(configuration: Configuration, mocker: MockerFixtu
mocker.patch("ahriman.core.alpm.pacman.Pacman.database_copy")
sync_mock = mocker.patch("ahriman.core.alpm.pacman.Pacman.database_sync")
configuration.set_option("alpm", "use_ahriman_cache", "yes")
_, repository_id = configuration.check_loaded()
# pyalpm.Handle is trying to reach the directory we've asked, thus we need to patch it a bit
with TemporaryDirectory(ignore_cleanup_errors=True) as pacman_root:
mocker.patch.object(RepositoryPaths, "pacman", Path(pacman_root))
# during the creation pyalpm.Handle will create also version file which we would like to remove later
pacman = Pacman("x86_64", configuration, refresh_database=PacmanSynchronization.Enabled)
pacman = Pacman(repository_id, configuration, refresh_database=PacmanSynchronization.Enabled)
assert pacman.handle
sync_mock.assert_called_once_with(pytest.helpers.anyvar(int), force=False)
@ -36,12 +37,13 @@ def test_init_with_local_cache_forced(configuration: Configuration, mocker: Mock
mocker.patch("ahriman.core.alpm.pacman.Pacman.database_copy")
sync_mock = mocker.patch("ahriman.core.alpm.pacman.Pacman.database_sync")
configuration.set_option("alpm", "use_ahriman_cache", "yes")
_, repository_id = configuration.check_loaded()
# pyalpm.Handle is trying to reach the directory we've asked, thus we need to patch it a bit
with TemporaryDirectory(ignore_cleanup_errors=True) as pacman_root:
mocker.patch.object(RepositoryPaths, "pacman", Path(pacman_root))
# during the creation pyalpm.Handle will create also version file which we would like to remove later
pacman = Pacman("x86_64", configuration, refresh_database=PacmanSynchronization.Force)
pacman = Pacman(repository_id, configuration, refresh_database=PacmanSynchronization.Force)
assert pacman.handle
sync_mock.assert_called_once_with(pytest.helpers.anyvar(int), force=True)
@ -56,10 +58,12 @@ def test_database_copy(pacman: Pacman, repository_paths: RepositoryPaths, mocker
mocker.patch("pathlib.Path.is_dir", return_value=True)
# root database exists, local database does not
mocker.patch("pathlib.Path.is_file", autospec=True, side_effect=lambda p: p.is_relative_to(path))
mkdir_mock = mocker.patch("pathlib.Path.mkdir")
copy_mock = mocker.patch("shutil.copy")
chown_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.chown")
pacman.database_copy(pacman.handle, database, path, repository_paths, use_ahriman_cache=True)
mkdir_mock.assert_called_once_with(mode=0o755, exist_ok=True)
copy_mock.assert_called_once_with(path / "sync" / "core.db", dst_path)
chown_mock.assert_called_once_with(dst_path)
@ -128,8 +132,8 @@ def test_database_init(pacman: Pacman, configuration: Configuration) -> None:
must init database with settings
"""
mirror = configuration.get("alpm", "mirror")
database = pacman.database_init(pacman.handle, "test", mirror, "x86_64")
assert len(database.servers) == 1
database = pacman.database_init(pacman.handle, "testing", mirror, "x86_64")
assert database.servers == ["https://geo.mirror.pkgbuild.com/testing/os/x86_64"]
def test_database_sync(pacman: Pacman) -> None:

View File

@ -177,7 +177,7 @@ def test_load(package_ahriman: Package, repository_paths: RepositoryPaths, mocke
Sources.load(path, package_ahriman, [patch], repository_paths)
fetch_mock.assert_called_once_with(path, package_ahriman.remote)
patch_mock.assert_called_once_with(path, patch)
architectures_mock.assert_called_once_with(path, repository_paths.architecture)
architectures_mock.assert_called_once_with(path, repository_paths.repository_id.architecture)
def test_load_no_patch(package_ahriman: Package, repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:

View File

@ -7,9 +7,17 @@ from unittest.mock import call as MockCall
from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import InitializeError
from ahriman.models.repository_id import RepositoryId
from ahriman.models.repository_paths import RepositoryPaths
def test_architecture(configuration: Configuration) -> None:
"""
must return valid repository architecture
"""
assert configuration.architecture == "x86_64"
def test_repository_name(configuration: Configuration) -> None:
"""
must return valid repository name
@ -24,7 +32,7 @@ def test_repository_paths(configuration: Configuration, repository_paths: Reposi
assert configuration.repository_paths == repository_paths
def test_from_path(mocker: MockerFixture) -> None:
def test_from_path(repository_id: RepositoryId, mocker: MockerFixture) -> None:
"""
must load configuration
"""
@ -33,13 +41,13 @@ def test_from_path(mocker: MockerFixture) -> None:
load_includes_mock = mocker.patch("ahriman.core.configuration.Configuration.load_includes")
path = Path("path")
configuration = Configuration.from_path(path, "x86_64")
configuration = Configuration.from_path(path, repository_id)
assert configuration.path == path
read_mock.assert_called_once_with(path)
load_includes_mock.assert_called_once_with()
def test_from_path_file_missing(mocker: MockerFixture) -> None:
def test_from_path_file_missing(repository_id: RepositoryId, mocker: MockerFixture) -> None:
"""
must load configuration based on package files
"""
@ -47,17 +55,40 @@ def test_from_path_file_missing(mocker: MockerFixture) -> None:
mocker.patch("ahriman.core.configuration.Configuration.load_includes")
read_mock = mocker.patch("ahriman.core.configuration.Configuration.read")
configuration = Configuration.from_path(Path("path"), "x86_64")
configuration = Configuration.from_path(Path("path"), repository_id)
read_mock.assert_called_once_with(configuration.SYSTEM_CONFIGURATION_PATH)
def test_override_sections(repository_id: RepositoryId) -> None:
"""
must correctly generate override section names
"""
assert Configuration.override_sections("build", repository_id) == [
"build:x86_64",
"build:aur-clone",
"build:aur-clone:x86_64",
]
def test_section_name(configuration: Configuration) -> None:
"""
must return architecture specific group
"""
assert configuration.section_name("build") == "build"
assert configuration.section_name("build", None) == "build"
assert configuration.section_name("build", "x86_64") == "build:x86_64"
assert configuration.section_name("build", "aur-clone", "x86_64") == "build:aur-clone:x86_64"
assert configuration.section_name("build", "aur-clone", None) == "build:aur-clone"
assert configuration.section_name("build", None, "x86_64") == "build:x86_64"
def test_check_loaded(configuration: Configuration) -> None:
"""
must return valid path and architecture
"""
path, architecture = configuration.check_loaded()
path, repository_id = configuration.check_loaded()
assert path == configuration.path
assert architecture == configuration.architecture
assert repository_id == configuration.repository_id
def test_check_loaded_path(configuration: Configuration) -> None:
@ -73,7 +104,7 @@ def test_check_loaded_architecture(configuration: Configuration) -> None:
"""
must raise exception if architecture is none
"""
configuration.architecture = None
configuration.repository_id = None
with pytest.raises(InitializeError):
configuration.check_loaded()
@ -89,9 +120,9 @@ def test_dump_architecture_specific(configuration: Configuration) -> None:
"""
dump must contain architecture specific settings
"""
section = configuration.section_name("build", "x86_64")
section = configuration.section_name("build", configuration.architecture)
configuration.set_option(section, "archbuild_flags", "hello flag")
configuration.merge_sections("x86_64")
configuration.merge_sections(configuration.repository_id)
dump = configuration.dump()
assert dump
@ -100,13 +131,6 @@ def test_dump_architecture_specific(configuration: Configuration) -> None:
assert dump["build"]["archbuild_flags"] == "hello flag"
def test_section_name(configuration: Configuration) -> None:
"""
must return architecture specific group
"""
assert configuration.section_name("build", "x86_64") == "build:x86_64"
def test_getlist(configuration: Configuration) -> None:
"""
must return list of string correctly
@ -209,7 +233,7 @@ def test_gettype(configuration: Configuration) -> None:
"""
must extract type from variable
"""
section, provider = configuration.gettype("customs3", "x86_64")
section, provider = configuration.gettype("customs3", configuration.repository_id)
assert section == "customs3"
assert provider == "s3"
@ -218,7 +242,7 @@ def test_gettype_with_fallback(configuration: Configuration) -> None:
"""
must return same provider name as in fallback
"""
section, provider = configuration.gettype("rsync", "x86_64", fallback="abracadabra")
section, provider = configuration.gettype("rsync", configuration.repository_id, fallback="abracadabra")
assert section == "rsync"
assert provider == "abracadabra"
@ -227,7 +251,7 @@ def test_gettype_from_section(configuration: Configuration) -> None:
"""
must extract type from section name
"""
section, provider = configuration.gettype("rsync", "x86_64")
section, provider = configuration.gettype("rsync", configuration.repository_id)
assert section == "rsync"
assert provider == "rsync"
@ -236,7 +260,7 @@ def test_gettype_from_section_with_architecture(configuration: Configuration) ->
"""
must extract type from section name with architecture
"""
section, provider = configuration.gettype("github", "x86_64")
section, provider = configuration.gettype("github", configuration.repository_id)
assert section == "github:x86_64"
assert provider == "github"
@ -248,7 +272,7 @@ def test_gettype_from_section_no_section(configuration: Configuration) -> None:
# technically rsync:x86_64 is valid section
# but in current configuration it must be considered as missing section
with pytest.raises(configparser.NoSectionError):
configuration.gettype("rsync:x86_64", "x86_64")
configuration.gettype("rsync:x86_64", configuration.repository_id)
def test_load_includes_missing(configuration: Configuration) -> None:
@ -279,14 +303,44 @@ def test_merge_sections_missing(configuration: Configuration) -> None:
"""
must merge create section if not exists
"""
section = configuration.section_name("build", "x86_64")
section = configuration.section_name("build", configuration.architecture)
configuration.remove_section("build")
configuration.set_option(section, "key", "value")
configuration.merge_sections("x86_64")
configuration.merge_sections(configuration.repository_id)
assert configuration.get("build", "key") == "value"
def test_merge_sections_priority(configuration: Configuration) -> None:
"""
must merge sections in valid order
"""
empty = "build"
arch = configuration.section_name(empty, configuration.architecture)
repo = configuration.section_name(empty, configuration.repository_name)
repo_arch = configuration.section_name(empty, configuration.repository_name, configuration.architecture)
configuration.set_option(empty, "key1", "key1_value1")
configuration.set_option(arch, "key1", "key1_value2")
configuration.set_option(repo, "key1", "key1_value3")
configuration.set_option(repo_arch, "key1", "key1_value4")
configuration.set_option(empty, "key2", "key2_value1")
configuration.set_option(arch, "key2", "key2_value2")
configuration.set_option(repo, "key2", "key2_value3")
configuration.set_option(empty, "key3", "key3_value1")
configuration.set_option(arch, "key3", "key3_value2")
configuration.set_option(empty, "key4", "key4_value1")
configuration.merge_sections(configuration.repository_id)
assert configuration.get("build", "key1") == "key1_value4"
assert configuration.get("build", "key2") == "key2_value3"
assert configuration.get("build", "key3") == "key3_value2"
assert configuration.get("build", "key4") == "key4_value1"
def test_reload(configuration: Configuration, mocker: MockerFixture) -> None:
"""
must reload configuration
@ -296,7 +350,7 @@ def test_reload(configuration: Configuration, mocker: MockerFixture) -> None:
configuration.reload()
load_mock.assert_called_once_with(configuration.path)
merge_mock.assert_called_once_with(configuration.architecture)
merge_mock.assert_called_once_with(configuration.repository_id)
def test_reload_clear(configuration: Configuration, mocker: MockerFixture) -> None:
@ -314,7 +368,7 @@ def test_reload_no_architecture(configuration: Configuration) -> None:
"""
must raise exception on reload if no architecture set
"""
configuration.architecture = None
configuration.repository_id = None
with pytest.raises(InitializeError):
configuration.reload()

View File

@ -2,10 +2,12 @@ import pytest
from sqlite3 import Connection
from pytest_mock import MockerFixture
from unittest.mock import call as MockCall
from ahriman.core.configuration import Configuration
from ahriman.core.database.migrations.m001_package_source import migrate_data, migrate_package_remotes, steps
from ahriman.models.package import Package
from ahriman.models.package_source import PackageSource
from ahriman.models.repository_paths import RepositoryPaths
@ -30,13 +32,18 @@ def test_migrate_package_remotes(package_ahriman: Package, connection: Connectio
"""
must put package remotes to database
"""
mocker.patch(
"ahriman.core.database.operations.PackageOperations._packages_get_select_package_bases",
return_value={package_ahriman.base: package_ahriman})
connection.execute.return_value = [{
"package_base": package_ahriman.base,
"version": package_ahriman.version,
"source": PackageSource.AUR,
}]
mocker.patch("pathlib.Path.exists", return_value=False)
migrate_package_remotes(connection, repository_paths)
connection.execute.assert_called_once_with(pytest.helpers.anyvar(str, strict=True), pytest.helpers.anyvar(int))
connection.execute.assert_has_calls([
MockCall(pytest.helpers.anyvar(str, strict=True)),
MockCall(pytest.helpers.anyvar(str, strict=True), pytest.helpers.anyvar(int)),
])
def test_migrate_package_remotes_has_local(package_ahriman: Package, connection: Connection,
@ -44,13 +51,15 @@ def test_migrate_package_remotes_has_local(package_ahriman: Package, connection:
"""
must skip processing for packages which have local cache
"""
mocker.patch(
"ahriman.core.database.operations.PackageOperations._packages_get_select_package_bases",
return_value={package_ahriman.base: package_ahriman})
connection.execute.return_value = [{
"package_base": package_ahriman.base,
"version": package_ahriman.version,
"source": PackageSource.AUR,
}]
mocker.patch("pathlib.Path.exists", return_value=True)
migrate_package_remotes(connection, repository_paths)
connection.execute.assert_not_called()
connection.execute.assert_called_once_with(pytest.helpers.anyvar(str, strict=True))
def test_migrate_package_remotes_vcs(package_ahriman: Package, connection: Connection,
@ -58,11 +67,16 @@ def test_migrate_package_remotes_vcs(package_ahriman: Package, connection: Conne
"""
must process VCS packages with local cache
"""
mocker.patch(
"ahriman.core.database.operations.PackageOperations._packages_get_select_package_bases",
return_value={package_ahriman.base: package_ahriman})
connection.execute.return_value = [{
"package_base": package_ahriman.base,
"version": package_ahriman.version,
"source": PackageSource.AUR,
}]
mocker.patch("pathlib.Path.exists", return_value=True)
mocker.patch.object(Package, "is_vcs", True)
migrate_package_remotes(connection, repository_paths)
connection.execute.assert_called_once_with(pytest.helpers.anyvar(str, strict=True), pytest.helpers.anyvar(int))
connection.execute.assert_has_calls([
MockCall(pytest.helpers.anyvar(str, strict=True)),
MockCall(pytest.helpers.anyvar(str, strict=True), pytest.helpers.anyvar(int)),
])

View File

@ -0,0 +1,39 @@
import pytest
from pytest_mock import MockerFixture
from sqlite3 import Connection
from unittest.mock import call as MockCall
from ahriman.core.configuration import Configuration
from ahriman.core.database.migrations.m011_repository_name import migrate_data, migrate_package_repository, steps
def test_migration_check_depends() -> None:
"""
migration must not be empty
"""
assert steps
def test_migrate_data(connection: Connection, configuration: Configuration, mocker: MockerFixture) -> None:
"""
must perform data migration
"""
repository_mock = mocker.patch("ahriman.core.database.migrations.m011_repository_name.migrate_package_repository")
migrate_data(connection, configuration)
repository_mock.assert_called_once_with(connection, configuration)
def test_migrate_package_repository(connection: Connection, configuration: Configuration) -> None:
"""
must correctly set repository and architecture
"""
migrate_package_repository(connection, configuration)
connection.execute.assert_has_calls([
MockCall(pytest.helpers.anyvar(str, strict=True), {"repository": configuration.repository_id.id}),
MockCall(pytest.helpers.anyvar(str, strict=True), {"repository": configuration.repository_id.id}),
MockCall(pytest.helpers.anyvar(str, strict=True), {"repository": configuration.repository_id.id}),
MockCall(pytest.helpers.anyvar(str, strict=True), {"repository": configuration.repository_id.id}),
MockCall(pytest.helpers.anyvar(str, strict=True), {"repository": configuration.repository_id.id}),
])

View File

@ -1,5 +1,6 @@
from ahriman.core.database import SQLite
from ahriman.models.package import Package
from ahriman.models.repository_id import RepositoryId
def test_build_queue_insert_clear(database: SQLite, package_ahriman: Package, package_python_schedule: Package) -> None:
@ -13,6 +14,19 @@ def test_build_queue_insert_clear(database: SQLite, package_ahriman: Package, pa
assert not database.build_queue_get()
def test_build_queue_insert_clear_multi(database: SQLite, package_ahriman: Package) -> None:
"""
must clear all packages from queue for specific repository
"""
database.build_queue_insert(package_ahriman)
database.repository_id = RepositoryId("i686", database.repository_id.name)
database.build_queue_insert(package_ahriman)
database.build_queue_clear(None)
database.repository_id = RepositoryId("x86_64", database.repository_id.name)
assert database.build_queue_get() == [package_ahriman]
def test_build_queue_insert_clear_specific(database: SQLite, package_ahriman: Package,
package_python_schedule: Package) -> None:
"""
@ -43,3 +57,30 @@ def test_build_queue_insert(database: SQLite, package_ahriman: Package) -> None:
package_ahriman.version = "42"
database.build_queue_insert(package_ahriman)
assert database.build_queue_get() == [package_ahriman]
def test_build_queue_insert_multi(database: SQLite, package_ahriman: Package) -> None:
"""
must update build queue in the database for multiple architectures and repositories
"""
package_ahriman.version = "1"
database.build_queue_insert(package_ahriman)
assert database.build_queue_get() == [package_ahriman]
package_ahriman.version = "2"
database.repository_id = RepositoryId("i686", database.repository_id.name)
database.build_queue_insert(package_ahriman)
assert database.build_queue_get() == [package_ahriman]
package_ahriman.version = "1"
database.repository_id = RepositoryId("x86_64", database.repository_id.name)
assert database.build_queue_get() == [package_ahriman]
package_ahriman.version = "3"
database.repository_id = RepositoryId(database.repository_id.architecture, "repo")
database.build_queue_insert(package_ahriman)
assert database.build_queue_get() == [package_ahriman]
package_ahriman.version = "1"
database.repository_id = RepositoryId(database.repository_id.architecture, "aur-clone")
assert database.build_queue_get() == [package_ahriman]

View File

@ -1,12 +1,13 @@
from ahriman.core.database import SQLite
from ahriman.models.log_record_id import LogRecordId
from ahriman.models.package import Package
from ahriman.models.repository_id import RepositoryId
def test_logs_insert_remove_process(database: SQLite, package_ahriman: Package,
def test_logs_insert_remove_version(database: SQLite, package_ahriman: Package,
package_python_schedule: Package) -> None:
"""
must clear process specific package logs
must clear version specific package logs
"""
database.logs_insert(LogRecordId(package_ahriman.base, "1"), 42.0, "message 1")
database.logs_insert(LogRecordId(package_ahriman.base, "2"), 43.0, "message 2")
@ -17,6 +18,20 @@ def test_logs_insert_remove_process(database: SQLite, package_ahriman: Package,
assert database.logs_get(package_python_schedule.base) == [(42.0, "message 3")]
def test_logs_insert_remove_multi(database: SQLite, package_ahriman: Package) -> None:
"""
must clear logs for specified repository
"""
database.logs_insert(LogRecordId(package_ahriman.base, "1"), 42.0, "message 1")
database.repository_id = RepositoryId("i686", database.repository_id.name)
database.logs_insert(LogRecordId(package_ahriman.base, "1"), 43.0, "message 2")
database.logs_remove(package_ahriman.base, None)
assert not database.logs_get(package_ahriman.base)
database.repository_id = RepositoryId("x86_64", database.repository_id.name)
assert database.logs_get(package_ahriman.base) == [(42.0, "message 1")]
def test_logs_insert_remove_full(database: SQLite, package_ahriman: Package, package_python_schedule: Package) -> None:
"""
must clear full package logs
@ -46,3 +61,16 @@ def test_logs_insert_get_pagination(database: SQLite, package_ahriman: Package)
database.logs_insert(LogRecordId(package_ahriman.base, "1"), 42.0, "message 1")
database.logs_insert(LogRecordId(package_ahriman.base, "1"), 43.0, "message 2")
assert database.logs_get(package_ahriman.base, 1, 1) == [(43.0, "message 2")]
def test_logs_insert_get_multi(database: SQLite, package_ahriman: Package) -> None:
"""
must insert and get package logs for multiple repositories
"""
database.logs_insert(LogRecordId(package_ahriman.base, "1"), 42.0, "message 1")
database.repository_id = RepositoryId("i686", database.repository_id.name)
database.logs_insert(LogRecordId(package_ahriman.base, "1"), 43.0, "message 2")
assert database.logs_get(package_ahriman.base) == [(43.0, "message 2")]
database.repository_id = RepositoryId("x86_64", database.repository_id.name)
assert database.logs_get(package_ahriman.base) == [(42.0, "message 1")]

View File

@ -16,9 +16,13 @@ def test_package_remove_package_base(database: SQLite, connection: Connection) -
must remove package base
"""
database._package_remove_package_base(connection, "package")
args = {
"package_base": "package",
"repository": database.repository_id.id,
}
connection.execute.assert_has_calls([
MockCall(pytest.helpers.anyvar(str, strict=True), {"package_base": "package"}),
MockCall(pytest.helpers.anyvar(str, strict=True), {"package_base": "package"}),
MockCall(pytest.helpers.anyvar(str, strict=True), args),
MockCall(pytest.helpers.anyvar(str, strict=True), args),
])
@ -28,7 +32,10 @@ def test_package_remove_packages(database: SQLite, connection: Connection, packa
"""
database._package_remove_packages(connection, package_ahriman.base, package_ahriman.packages.keys())
connection.execute.assert_called_once_with(
pytest.helpers.anyvar(str, strict=True), {"package_base": package_ahriman.base})
pytest.helpers.anyvar(str, strict=True), {
"package_base": package_ahriman.base,
"repository": database.repository_id.id,
})
connection.executemany.assert_called_once_with(pytest.helpers.anyvar(str, strict=True), [])

View File

@ -22,7 +22,7 @@ def test_patches_list(database: SQLite, package_ahriman: Package, package_python
database.patches_insert(package_ahriman.base, PkgbuildPatch(None, "patch1"))
database.patches_insert(package_ahriman.base, PkgbuildPatch("key", "patch3"))
database.patches_insert(package_python_schedule.base, PkgbuildPatch(None, "patch2"))
assert database.patches_list(None, []) == {
assert database.patches_list(None, None) == {
package_ahriman.base: [PkgbuildPatch(None, "patch1"), PkgbuildPatch("key", "patch3")],
package_python_schedule.base: [PkgbuildPatch(None, "patch2")],
}
@ -35,8 +35,8 @@ def test_patches_list_filter(database: SQLite, package_ahriman: Package, package
database.patches_insert(package_ahriman.base, PkgbuildPatch(None, "patch1"))
database.patches_insert(package_python_schedule.base, PkgbuildPatch(None, "patch2"))
assert database.patches_list(package_ahriman.base, []) == {package_ahriman.base: [PkgbuildPatch(None, "patch1")]}
assert database.patches_list(package_python_schedule.base, []) == {
assert database.patches_list(package_ahriman.base, None) == {package_ahriman.base: [PkgbuildPatch(None, "patch1")]}
assert database.patches_list(package_python_schedule.base, None) == {
package_python_schedule.base: [PkgbuildPatch(None, "patch2")],
}
@ -50,7 +50,7 @@ def test_patches_list_filter_by_variable(database: SQLite, package_ahriman: Pack
database.patches_insert(package_ahriman.base, PkgbuildPatch("key", "patch2"))
database.patches_insert(package_python_schedule.base, PkgbuildPatch(None, "patch3"))
assert database.patches_list(None, []) == {
assert database.patches_list(None, None) == {
package_ahriman.base: [PkgbuildPatch(None, "patch1"), PkgbuildPatch("key", "patch2")],
package_python_schedule.base: [PkgbuildPatch(None, "patch3")],
}
@ -65,7 +65,7 @@ def test_patches_insert_remove(database: SQLite, package_ahriman: Package, packa
"""
database.patches_insert(package_ahriman.base, PkgbuildPatch(None, "patch1"))
database.patches_insert(package_python_schedule.base, PkgbuildPatch(None, "patch2"))
database.patches_remove(package_ahriman.base, [])
database.patches_remove(package_ahriman.base, None)
assert database.patches_get(package_ahriman.base) == []
assert database.patches_get(package_python_schedule.base) == [PkgbuildPatch(None, "patch2")]

View File

@ -16,7 +16,8 @@ def test_repo_clone(configuration: Configuration, mocker: MockerFixture) -> None
"""
fetch_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.fetch")
copy_mock = mocker.patch("ahriman.core.gitremote.remote_pull.RemotePull.repo_copy")
runner = RemotePull(configuration, "x86_64", "gitremote")
_, repository_id = configuration.check_loaded()
runner = RemotePull(repository_id, configuration, "gitremote")
runner.repo_clone()
fetch_mock.assert_called_once_with(pytest.helpers.anyvar(int), runner.remote_source)
@ -32,7 +33,8 @@ def test_package_copy(configuration: Configuration, package_ahriman: Package, mo
ignore_patterns_mock = mocker.patch("shutil.ignore_patterns", return_value=patterns)
copytree_mock = mocker.patch("shutil.copytree")
init_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.init")
runner = RemotePull(configuration, "x86_64", "gitremote")
_, repository_id = configuration.check_loaded()
runner = RemotePull(repository_id, configuration, "gitremote")
local = Path("local")
runner.package_copy(local / "PKGBUILD")
@ -57,7 +59,8 @@ def test_repo_copy(configuration: Configuration, mocker: MockerFixture) -> None:
local / "package3" / ".SRCINFO",
])
copy_mock = mocker.patch("ahriman.core.gitremote.remote_pull.RemotePull.package_copy")
runner = RemotePull(configuration, "x86_64", "gitremote")
_, repository_id = configuration.check_loaded()
runner = RemotePull(repository_id, configuration, "gitremote")
runner.repo_copy(local)
copy_mock.assert_has_calls([
@ -71,7 +74,8 @@ def test_run(configuration: Configuration, mocker: MockerFixture) -> None:
must clone repo on run
"""
clone_mock = mocker.patch("ahriman.core.gitremote.remote_pull.RemotePull.repo_clone")
runner = RemotePull(configuration, "x86_64", "gitremote")
_, repository_id = configuration.check_loaded()
runner = RemotePull(repository_id, configuration, "gitremote")
runner.run()
clone_mock.assert_called_once_with()
@ -82,7 +86,8 @@ def test_run_failed(configuration: Configuration, mocker: MockerFixture) -> None
must reraise exception on error occurred
"""
mocker.patch("ahriman.core.gitremote.remote_pull.RemotePull.repo_clone", side_effect=Exception())
runner = RemotePull(configuration, "x86_64", "gitremote")
_, repository_id = configuration.check_loaded()
runner = RemotePull(repository_id, configuration, "gitremote")
with pytest.raises(GitRemoteError):
runner.run()

View File

@ -20,7 +20,8 @@ def test_on_start(configuration: Configuration, mocker: MockerFixture) -> None:
must clone repo on start
"""
run_mock = mocker.patch("ahriman.core.gitremote.remote_pull.RemotePull.run")
trigger = RemotePullTrigger("x86_64", configuration)
_, repository_id = configuration.check_loaded()
trigger = RemotePullTrigger(repository_id, configuration)
trigger.on_start()
run_mock.assert_called_once_with()

View File

@ -26,7 +26,8 @@ def test_on_result(configuration: Configuration, result: Result, package_ahriman
"""
database_mock = mocker.patch("ahriman.core._Context.get", return_value=database)
run_mock = mocker.patch("ahriman.core.gitremote.remote_push.RemotePush.run")
trigger = RemotePushTrigger("x86_64", configuration)
_, repository_id = configuration.check_loaded()
trigger = RemotePushTrigger(repository_id, configuration)
trigger.on_result(result, [package_ahriman])
database_mock.assert_called_once_with(ContextKey("database", SQLite))

View File

@ -22,7 +22,7 @@ def test_init_auth(configuration: Configuration) -> None:
configuration.set_option("web", "username", "username")
configuration.set_option("web", "password", "password")
assert SyncHttpClient("web", configuration).auth == ("username", "password")
assert SyncHttpClient(configuration, "web").auth == ("username", "password")
assert SyncHttpClient(configuration=configuration).auth is None

View File

@ -7,7 +7,7 @@ from pytest_mock import MockerFixture
from systemd.journal import JournalHandler
from ahriman.core.configuration import Configuration
from ahriman.core.log import Log
from ahriman.core.log.log_loader import LogLoader
from ahriman.models.log_handler import LogHandler
@ -15,14 +15,14 @@ def test_handler() -> None:
"""
must extract journald handler if available
"""
assert Log.handler(None) == LogHandler.Journald
assert LogLoader.handler(None) == LogHandler.Journald
def test_handler_selected() -> None:
"""
must return selected log handler
"""
assert Log.handler(LogHandler.Console) == LogHandler.Console
assert LogLoader.handler(LogHandler.Console) == LogHandler.Console
def test_handler_syslog(mocker: MockerFixture) -> None:
@ -31,7 +31,7 @@ def test_handler_syslog(mocker: MockerFixture) -> None:
"""
mocker.patch("pathlib.Path.exists", return_value=True)
mocker.patch.dict(sys.modules, {"systemd.journal": None})
assert Log.handler(None) == LogHandler.Syslog
assert LogLoader.handler(None) == LogHandler.Syslog
def test_handler_console(mocker: MockerFixture) -> None:
@ -40,17 +40,17 @@ def test_handler_console(mocker: MockerFixture) -> None:
"""
mocker.patch("pathlib.Path.exists", return_value=False)
mocker.patch.dict(sys.modules, {"systemd.journal": None})
assert Log.handler(None) == LogHandler.Console
assert LogLoader.handler(None) == LogHandler.Console
def test_load(configuration: Configuration, mocker: MockerFixture) -> None:
"""
must load logging
"""
logging_mock = mocker.patch("ahriman.core.log.log.fileConfig", side_effect=fileConfig)
logging_mock = mocker.patch("ahriman.core.log.log_loader.fileConfig", side_effect=fileConfig)
http_log_mock = mocker.patch("ahriman.core.log.http_log_handler.HttpLogHandler.load")
Log.load(configuration, LogHandler.Journald, quiet=False, report=False)
LogLoader.load(configuration, LogHandler.Journald, quiet=False, report=False)
logging_mock.assert_called_once_with(pytest.helpers.anyvar(int), disable_existing_loggers=True)
http_log_mock.assert_called_once_with(configuration, report=False)
assert all(isinstance(handler, JournalHandler) for handler in logging.getLogger().handlers)
@ -60,8 +60,8 @@ def test_load_fallback(configuration: Configuration, mocker: MockerFixture) -> N
"""
must fall back to stderr without errors
"""
mocker.patch("ahriman.core.log.log.fileConfig", side_effect=PermissionError())
Log.load(configuration, LogHandler.Journald, quiet=False, report=False)
mocker.patch("ahriman.core.log.log_loader.fileConfig", side_effect=PermissionError())
LogLoader.load(configuration, LogHandler.Journald, quiet=False, report=False)
def test_load_quiet(configuration: Configuration, mocker: MockerFixture) -> None:
@ -69,5 +69,5 @@ def test_load_quiet(configuration: Configuration, mocker: MockerFixture) -> None
must disable logging in case if quiet flag set
"""
disable_mock = mocker.patch("logging.disable")
Log.load(configuration, LogHandler.Journald, quiet=True, report=False)
LogLoader.load(configuration, LogHandler.Journald, quiet=True, report=False)
disable_mock.assert_called_once_with(logging.WARNING)

View File

@ -1,7 +1,24 @@
import pytest
from ahriman.core.configuration import Configuration
from ahriman.core.report.email import Email
from ahriman.core.report.remote_call import RemoteCall
from ahriman.core.report.telegram import Telegram
@pytest.fixture
def email(configuration: Configuration) -> Email:
"""
fixture for email trigger
Args:
configuration(Configuration): configuration fixture
Returns:
RemoteCall: email trigger test instance
"""
_, repository_id = configuration.check_loaded()
return Email(repository_id, configuration, "email")
@pytest.fixture
@ -17,4 +34,20 @@ def remote_call(configuration: Configuration) -> RemoteCall:
"""
configuration.set_option("web", "host", "localhost")
configuration.set_option("web", "port", "8080")
return RemoteCall("x86_64", configuration, "remote-call")
_, repository_id = configuration.check_loaded()
return RemoteCall(repository_id, configuration, "remote-call")
@pytest.fixture
def telegram(configuration: Configuration) -> Telegram:
"""
fixture for telegram trigger
Args:
configuration(Configuration): configuration fixture
Returns:
RemoteCall: telegram trigger test instance
"""
_, repository_id = configuration.check_loaded()
return Telegram(repository_id, configuration, "telegram")

View File

@ -14,7 +14,8 @@ def test_generate(configuration: Configuration, result: Result, package_python_s
"""
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
result.add_failed(package_python_schedule)
report = Console("x86_64", configuration, "console")
_, repository_id = configuration.check_loaded()
report = Console(repository_id, configuration, "console")
report.generate([], result)
print_mock.assert_has_calls([MockCall(verbose=True), MockCall(verbose=True)])

View File

@ -8,17 +8,16 @@ from ahriman.models.package import Package
from ahriman.models.result import Result
def test_send(configuration: Configuration, mocker: MockerFixture) -> None:
def test_send(email: Email, mocker: MockerFixture) -> None:
"""
must send an email with attachment
"""
smtp_mock = mocker.patch("smtplib.SMTP")
report = Email("x86_64", configuration, "email")
report._send("a text", {"attachment.html": "an attachment"})
email._send("a text", {"attachment.html": "an attachment"})
smtp_mock.return_value.starttls.assert_not_called()
smtp_mock.return_value.login.assert_not_called()
smtp_mock.return_value.sendmail.assert_called_once_with(report.sender, report.receivers, pytest.helpers.anyvar(int))
smtp_mock.return_value.sendmail.assert_called_once_with(email.sender, email.receivers, pytest.helpers.anyvar(int))
smtp_mock.return_value.quit.assert_called_once_with()
@ -29,10 +28,11 @@ def test_send_auth(configuration: Configuration, mocker: MockerFixture) -> None:
configuration.set_option("email", "user", "username")
configuration.set_option("email", "password", "password")
smtp_mock = mocker.patch("smtplib.SMTP")
_, repository_id = configuration.check_loaded()
report = Email("x86_64", configuration, "email")
report._send("a text", {"attachment.html": "an attachment"})
smtp_mock.return_value.login.assert_called_once_with(report.user, report.password)
email = Email(repository_id, configuration, "email")
email._send("a text", {"attachment.html": "an attachment"})
smtp_mock.return_value.login.assert_called_once_with(email.user, email.password)
def test_send_auth_no_password(configuration: Configuration, mocker: MockerFixture) -> None:
@ -41,9 +41,10 @@ def test_send_auth_no_password(configuration: Configuration, mocker: MockerFixtu
"""
configuration.set_option("email", "user", "username")
smtp_mock = mocker.patch("smtplib.SMTP")
_, repository_id = configuration.check_loaded()
report = Email("x86_64", configuration, "email")
report._send("a text", {"attachment.html": "an attachment"})
email = Email(repository_id, configuration, "email")
email._send("a text", {"attachment.html": "an attachment"})
smtp_mock.return_value.login.assert_not_called()
@ -53,9 +54,10 @@ def test_send_auth_no_user(configuration: Configuration, mocker: MockerFixture)
"""
configuration.set_option("email", "password", "password")
smtp_mock = mocker.patch("smtplib.SMTP")
_, repository_id = configuration.check_loaded()
report = Email("x86_64", configuration, "email")
report._send("a text", {"attachment.html": "an attachment"})
email = Email(repository_id, configuration, "email")
email._send("a text", {"attachment.html": "an attachment"})
smtp_mock.return_value.login.assert_not_called()
@ -65,12 +67,13 @@ def test_send_ssl_tls(configuration: Configuration, mocker: MockerFixture) -> No
"""
configuration.set_option("email", "ssl", "ssl")
smtp_mock = mocker.patch("smtplib.SMTP_SSL")
_, repository_id = configuration.check_loaded()
report = Email("x86_64", configuration, "email")
report._send("a text", {"attachment.html": "an attachment"})
email = Email(repository_id, configuration, "email")
email._send("a text", {"attachment.html": "an attachment"})
smtp_mock.return_value.starttls.assert_not_called()
smtp_mock.return_value.login.assert_not_called()
smtp_mock.return_value.sendmail.assert_called_once_with(report.sender, report.receivers, pytest.helpers.anyvar(int))
smtp_mock.return_value.sendmail.assert_called_once_with(email.sender, email.receivers, pytest.helpers.anyvar(int))
smtp_mock.return_value.quit.assert_called_once_with()
@ -80,48 +83,40 @@ def test_send_starttls(configuration: Configuration, mocker: MockerFixture) -> N
"""
configuration.set_option("email", "ssl", "starttls")
smtp_mock = mocker.patch("smtplib.SMTP")
_, repository_id = configuration.check_loaded()
report = Email("x86_64", configuration, "email")
report._send("a text", {"attachment.html": "an attachment"})
email = Email(repository_id, configuration, "email")
email._send("a text", {"attachment.html": "an attachment"})
smtp_mock.return_value.starttls.assert_called_once_with()
def test_generate(configuration: Configuration, package_ahriman: Package, mocker: MockerFixture) -> None:
def test_generate(email: Email, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must generate report
"""
send_mock = mocker.patch("ahriman.core.report.email.Email._send")
report = Email("x86_64", configuration, "email")
report.generate([package_ahriman], Result())
email.generate([package_ahriman], Result())
send_mock.assert_called_once_with(pytest.helpers.anyvar(int), {})
def test_generate_with_built(configuration: Configuration, package_ahriman: Package, result: Result,
mocker: MockerFixture) -> None:
def test_generate_with_built(email: Email, package_ahriman: Package, result: Result, mocker: MockerFixture) -> None:
"""
must generate report with built packages
"""
send_mock = mocker.patch("ahriman.core.report.email.Email._send")
report = Email("x86_64", configuration, "email")
report.generate([package_ahriman], result)
email.generate([package_ahriman], result)
send_mock.assert_called_once_with(pytest.helpers.anyvar(int), {})
def test_generate_with_built_and_full_path(
configuration: Configuration,
package_ahriman: Package,
result: Result,
mocker: MockerFixture) -> None:
def test_generate_with_built_and_full_path(email: Email, package_ahriman: Package, result: Result,
mocker: MockerFixture) -> None:
"""
must generate report with built packages and full packages lists
"""
send_mock = mocker.patch("ahriman.core.report.email.Email._send")
report = Email("x86_64", configuration, "email")
report.full_template_path = report.template_path
report.generate([package_ahriman], result)
email.full_template_path = email.template_path
email.generate([package_ahriman], result)
send_mock.assert_called_once_with(pytest.helpers.anyvar(int), pytest.helpers.anyvar(int))
@ -131,9 +126,10 @@ def test_generate_no_empty(configuration: Configuration, package_ahriman: Packag
"""
configuration.set_option("email", "no_empty_report", "yes")
send_mock = mocker.patch("ahriman.core.report.email.Email._send")
_, repository_id = configuration.check_loaded()
report = Email("x86_64", configuration, "email")
report.generate([package_ahriman], Result())
email = Email(repository_id, configuration, "email")
email.generate([package_ahriman], Result())
send_mock.assert_not_called()
@ -144,7 +140,8 @@ def test_generate_no_empty_with_built(configuration: Configuration, package_ahri
"""
configuration.set_option("email", "no_empty_report", "yes")
send_mock = mocker.patch("ahriman.core.report.email.Email._send")
_, repository_id = configuration.check_loaded()
report = Email("x86_64", configuration, "email")
report.generate([package_ahriman], result)
email = Email(repository_id, configuration, "email")
email.generate([package_ahriman], result)
send_mock.assert_called_once_with(pytest.helpers.anyvar(int), {})

View File

@ -13,7 +13,8 @@ def test_generate(configuration: Configuration, package_ahriman: Package, mocker
must generate report
"""
write_mock = mocker.patch("pathlib.Path.write_text")
_, repository_id = configuration.check_loaded()
report = HTML("x86_64", configuration, "html")
report = HTML(repository_id, configuration, "html")
report.generate([package_ahriman], Result())
write_mock.assert_called_once_with(pytest.helpers.anyvar(int), encoding="utf8")

View File

@ -9,5 +9,6 @@ def test_generate(configuration: Configuration, package_ahriman: Package) -> Non
must generate html report
"""
path = configuration.getpath("html", "template_path")
report = JinjaTemplate("html", configuration)
_, repository_id = configuration.check_loaded()
report = JinjaTemplate(repository_id, configuration, "html")
assert report.make_html(Result(success=[package_ahriman]), path)

View File

@ -14,8 +14,10 @@ def test_report_failure(configuration: Configuration, mocker: MockerFixture) ->
must raise ReportFailed on errors
"""
mocker.patch("ahriman.core.report.html.HTML.generate", side_effect=Exception())
_, repository_id = configuration.check_loaded()
with pytest.raises(ReportError):
Report.load("x86_64", configuration, "html").run(Result(), [])
Report.load(repository_id, configuration, "html").run(Result(), [])
def test_report_dummy(configuration: Configuration, result: Result, mocker: MockerFixture) -> None:
@ -24,8 +26,9 @@ def test_report_dummy(configuration: Configuration, result: Result, mocker: Mock
"""
mocker.patch("ahriman.models.report_settings.ReportSettings.from_option", return_value=ReportSettings.Disabled)
report_mock = mocker.patch("ahriman.core.report.report.Report.generate")
_, repository_id = configuration.check_loaded()
Report.load("x86_64", configuration, "disabled").run(result, [])
Report.load(repository_id, configuration, "disabled").run(result, [])
report_mock.assert_called_once_with([], result)
@ -34,7 +37,9 @@ def test_report_console(configuration: Configuration, result: Result, mocker: Mo
must generate console report
"""
report_mock = mocker.patch("ahriman.core.report.console.Console.generate")
Report.load("x86_64", configuration, "console").run(result, [])
_, repository_id = configuration.check_loaded()
Report.load(repository_id, configuration, "console").run(result, [])
report_mock.assert_called_once_with([], result)
@ -43,7 +48,9 @@ def test_report_email(configuration: Configuration, result: Result, mocker: Mock
must generate email report
"""
report_mock = mocker.patch("ahriman.core.report.email.Email.generate")
Report.load("x86_64", configuration, "email").run(result, [])
_, repository_id = configuration.check_loaded()
Report.load(repository_id, configuration, "email").run(result, [])
report_mock.assert_called_once_with([], result)
@ -52,7 +59,9 @@ def test_report_html(configuration: Configuration, result: Result, mocker: Mocke
must generate html report
"""
report_mock = mocker.patch("ahriman.core.report.html.HTML.generate")
Report.load("x86_64", configuration, "html").run(result, [])
_, repository_id = configuration.check_loaded()
Report.load(repository_id, configuration, "html").run(result, [])
report_mock.assert_called_once_with([], result)
@ -63,8 +72,9 @@ def test_report_remote_call(configuration: Configuration, result: Result, mocker
configuration.set_option("web", "host", "localhost")
configuration.set_option("web", "port", "8080")
report_mock = mocker.patch("ahriman.core.report.remote_call.RemoteCall.generate")
_, repository_id = configuration.check_loaded()
Report.load("x86_64", configuration, "remote-call").run(result, [])
Report.load(repository_id, configuration, "remote-call").run(result, [])
report_mock.assert_called_once_with([], result)
@ -73,5 +83,7 @@ def test_report_telegram(configuration: Configuration, result: Result, mocker: M
must generate telegram report
"""
report_mock = mocker.patch("ahriman.core.report.telegram.Telegram.generate")
Report.load("x86_64", configuration, "telegram").run(result, [])
_, repository_id = configuration.check_loaded()
Report.load(repository_id, configuration, "telegram").run(result, [])
report_mock.assert_called_once_with([], result)

View File

@ -22,7 +22,8 @@ def test_on_result(configuration: Configuration, mocker: MockerFixture) -> None:
"""
configuration.set_option("report", "target", "email")
run_mock = mocker.patch("ahriman.core.report.report.Report.run")
_, repository_id = configuration.check_loaded()
trigger = ReportTrigger("x86_64", configuration)
trigger = ReportTrigger(repository_id, configuration)
trigger.on_result(Result(), [])
run_mock.assert_called_once_with(Result(), [])

View File

@ -10,67 +10,58 @@ from ahriman.models.package import Package
from ahriman.models.result import Result
def test_send(configuration: Configuration, mocker: MockerFixture) -> None:
def test_send(telegram: Telegram, mocker: MockerFixture) -> None:
"""
must send a message
"""
request_mock = mocker.patch("ahriman.core.report.telegram.Telegram.make_request")
report = Telegram("x86_64", configuration, "telegram")
report._send("a text")
telegram._send("a text")
request_mock.assert_called_once_with(
"POST",
pytest.helpers.anyvar(str, strict=True),
data={"chat_id": pytest.helpers.anyvar(str, strict=True), "text": "a text", "parse_mode": "HTML"})
def test_send_failed(configuration: Configuration, mocker: MockerFixture) -> None:
def test_send_failed(telegram: Telegram, mocker: MockerFixture) -> None:
"""
must reraise generic exception
"""
mocker.patch("requests.Session.request", side_effect=Exception())
report = Telegram("x86_64", configuration, "telegram")
with pytest.raises(Exception):
report._send("a text")
telegram._send("a text")
def test_send_failed_http_error(configuration: Configuration, mocker: MockerFixture) -> None:
def test_send_failed_http_error(telegram: Telegram, mocker: MockerFixture) -> None:
"""
must reraise http exception
"""
mocker.patch("requests.Session.request", side_effect=requests.HTTPError())
report = Telegram("x86_64", configuration, "telegram")
with pytest.raises(requests.HTTPError):
report._send("a text")
telegram._send("a text")
def test_generate(configuration: Configuration, package_ahriman: Package, result: Result,
def test_generate(telegram: Telegram, package_ahriman: Package, result: Result,
mocker: MockerFixture) -> None:
"""
must generate report
"""
send_mock = mocker.patch("ahriman.core.report.telegram.Telegram._send")
report = Telegram("x86_64", configuration, "telegram")
report.generate([package_ahriman], result)
telegram.generate([package_ahriman], result)
send_mock.assert_called_once_with(pytest.helpers.anyvar(int))
def test_generate_big_text_without_spaces(configuration: Configuration, package_ahriman: Package, result: Result,
def test_generate_big_text_without_spaces(telegram: Telegram, package_ahriman: Package, result: Result,
mocker: MockerFixture) -> None:
"""
must raise ValueError in case if there are no new lines in text
"""
mocker.patch("ahriman.core.report.jinja_template.JinjaTemplate.make_html", return_value="ab" * 4096)
report = Telegram("x86_64", configuration, "telegram")
with pytest.raises(ValueError):
report.generate([package_ahriman], result)
telegram.generate([package_ahriman], result)
def test_generate_big_text(configuration: Configuration, package_ahriman: Package, result: Result,
def test_generate_big_text(telegram: Telegram, package_ahriman: Package, result: Result,
mocker: MockerFixture) -> None:
"""
must generate report with big text
@ -78,14 +69,13 @@ def test_generate_big_text(configuration: Configuration, package_ahriman: Packag
mocker.patch("ahriman.core.report.jinja_template.JinjaTemplate.make_html", return_value="a\n" * 4096)
send_mock = mocker.patch("ahriman.core.report.telegram.Telegram._send")
report = Telegram("x86_64", configuration, "telegram")
report.generate([package_ahriman], result)
telegram.generate([package_ahriman], result)
send_mock.assert_has_calls([
MockCall(pytest.helpers.anyvar(str, strict=True)), MockCall(pytest.helpers.anyvar(str, strict=True))
])
def test_generate_very_big_text(configuration: Configuration, package_ahriman: Package, result: Result,
def test_generate_very_big_text(telegram: Telegram, package_ahriman: Package, result: Result,
mocker: MockerFixture) -> None:
"""
must generate report with very big text
@ -93,8 +83,7 @@ def test_generate_very_big_text(configuration: Configuration, package_ahriman: P
mocker.patch("ahriman.core.report.jinja_template.JinjaTemplate.make_html", return_value="ab\n" * 4096)
send_mock = mocker.patch("ahriman.core.report.telegram.Telegram._send")
report = Telegram("x86_64", configuration, "telegram")
report.generate([package_ahriman], result)
telegram.generate([package_ahriman], result)
send_mock.assert_has_calls([
MockCall(pytest.helpers.anyvar(str, strict=True)),
MockCall(pytest.helpers.anyvar(str, strict=True)),
@ -102,12 +91,10 @@ def test_generate_very_big_text(configuration: Configuration, package_ahriman: P
])
def test_generate_no_empty(configuration: Configuration, package_ahriman: Package, mocker: MockerFixture) -> None:
def test_generate_no_empty(telegram: Telegram, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must generate report
"""
send_mock = mocker.patch("ahriman.core.report.telegram.Telegram._send")
report = Telegram("x86_64", configuration, "telegram")
report.generate([package_ahriman], Result())
telegram.generate([package_ahriman], Result())
send_mock.assert_not_called()

View File

@ -22,7 +22,8 @@ def cleaner(configuration: Configuration, database: SQLite) -> Cleaner:
Returns:
Cleaner: cleaner test instance
"""
return Cleaner("x86_64", configuration, database, report=False,
_, repository_id = configuration.check_loaded()
return Cleaner(repository_id, configuration, database, report=False,
refresh_pacman_database=PacmanSynchronization.Disabled)
@ -43,7 +44,8 @@ def executor(configuration: Configuration, database: SQLite, mocker: MockerFixtu
mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_chroot")
mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_packages")
mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_queue")
return Executor("x86_64", configuration, database, report=False,
_, repository_id = configuration.check_loaded()
return Executor(repository_id, configuration, database, report=False,
refresh_pacman_database=PacmanSynchronization.Disabled)
@ -64,5 +66,6 @@ def update_handler(configuration: Configuration, database: SQLite, mocker: Mocke
mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_chroot")
mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_packages")
mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_queue")
return UpdateHandler("x86_64", configuration, database, report=False,
_, repository_id = configuration.check_loaded()
return UpdateHandler(repository_id, configuration, database, report=False,
refresh_pacman_database=PacmanSynchronization.Disabled)

View File

@ -20,7 +20,9 @@ def test_load(configuration: Configuration, database: SQLite, mocker: MockerFixt
must correctly load instance
"""
context_mock = mocker.patch("ahriman.core.repository.Repository._set_context")
Repository.load("x86_64", configuration, database, report=False)
_, repository_id = configuration.check_loaded()
Repository.load(repository_id, configuration, database, report=False)
context_mock.assert_called_once_with()
@ -29,8 +31,9 @@ def test_set_context(configuration: Configuration, database: SQLite, mocker: Moc
must set context variables
"""
set_mock = mocker.patch("ahriman.core._Context.set")
_, repository_id = configuration.check_loaded()
instance = Repository.load("x86_64", configuration, database, report=False)
instance = Repository.load(repository_id, configuration, database, report=False)
set_mock.assert_has_calls([
MockCall(ContextKey("database", SQLite), instance.database),
MockCall(ContextKey("configuration", Configuration), instance.configuration),

View File

@ -6,6 +6,20 @@ from ahriman.models.user import User
from ahriman.models.user_access import UserAccess
def test_architecture(repository: RepositoryProperties) -> None:
"""
must provide repository architecture for backward compatibility
"""
assert repository.architecture == repository.repository_id.architecture
def test_name(repository: RepositoryProperties) -> None:
"""
must provide repository name for backward compatibility
"""
assert repository.name == repository.repository_id.name
def test_packager(repository: RepositoryProperties, mocker: MockerFixture) -> None:
"""
must extract packager

View File

@ -17,9 +17,10 @@ def test_force_no_report(configuration: Configuration, database: SQLite, mocker:
"""
configuration.set_option("web", "port", "8080")
load_mock = mocker.patch("ahriman.core.repository.Repository.load")
_, repository_id = configuration.check_loaded()
Watcher("x86_64", configuration, database)
load_mock.assert_called_once_with("x86_64", configuration, database, report=False)
Watcher(repository_id, configuration, database)
load_mock.assert_called_once_with(repository_id, configuration, database, report=False)
def test_load(watcher: Watcher, package_ahriman: Package, mocker: MockerFixture) -> None:

View File

@ -16,7 +16,8 @@ def mirrorlist_generator(configuration: Configuration) -> MirrorlistGenerator:
Returns:
MirrorlistGenerator: mirrorlist pkgbuild generator test instance
"""
return MirrorlistGenerator(configuration, "mirrorlist")
_, repository_id = configuration.check_loaded()
return MirrorlistGenerator(repository_id, configuration, "mirrorlist")
@pytest.fixture

View File

@ -20,7 +20,8 @@ def keyring_generator(database: SQLite, gpg: GPG, configuration: Configuration)
Returns:
KeyringGenerator: keyring generator test instance
"""
return KeyringGenerator(database, gpg, configuration, "keyring")
_, repository_id = configuration.check_loaded()
return KeyringGenerator(database, gpg, repository_id, configuration, "keyring")
@pytest.fixture

View File

@ -18,74 +18,87 @@ def test_init_packagers(database: SQLite, gpg: GPG, configuration: Configuration
must extract packagers keys
"""
mocker.patch("ahriman.core.database.SQLite.user_list", return_value=[user])
_, repository_id = configuration.check_loaded()
assert KeyringGenerator(database, gpg, configuration, "keyring").packagers == ["key"]
assert KeyringGenerator(database, gpg, repository_id, configuration, "keyring").packagers == ["key"]
configuration.set_option("keyring", "packagers", "key1")
assert KeyringGenerator(database, gpg, configuration, "keyring").packagers == ["key1"]
assert KeyringGenerator(database, gpg, repository_id, configuration, "keyring").packagers == ["key1"]
def test_init_revoked(database: SQLite, gpg: GPG, configuration: Configuration) -> None:
"""
must extract revoked keys
"""
assert KeyringGenerator(database, gpg, configuration, "keyring").revoked == []
_, repository_id = configuration.check_loaded()
assert KeyringGenerator(database, gpg, repository_id, configuration, "keyring").revoked == []
configuration.set_option("keyring", "revoked", "key1")
assert KeyringGenerator(database, gpg, configuration, "keyring").revoked == ["key1"]
assert KeyringGenerator(database, gpg, repository_id, configuration, "keyring").revoked == ["key1"]
def test_init_trusted(database: SQLite, gpg: GPG, configuration: Configuration) -> None:
"""
must extract trusted keys
"""
assert KeyringGenerator(database, gpg, configuration, "keyring").trusted == []
_, repository_id = configuration.check_loaded()
assert KeyringGenerator(database, gpg, repository_id, configuration, "keyring").trusted == []
gpg.default_key = "key"
assert KeyringGenerator(database, gpg, configuration, "keyring").trusted == ["key"]
assert KeyringGenerator(database, gpg, repository_id, configuration, "keyring").trusted == ["key"]
configuration.set_option("keyring", "trusted", "key1")
assert KeyringGenerator(database, gpg, configuration, "keyring").trusted == ["key1"]
assert KeyringGenerator(database, gpg, repository_id, configuration, "keyring").trusted == ["key1"]
def test_license(database: SQLite, gpg: GPG, configuration: Configuration) -> None:
"""
must generate correct licenses list
"""
assert KeyringGenerator(database, gpg, configuration, "keyring").license == ["Unlicense"]
_, repository_id = configuration.check_loaded()
assert KeyringGenerator(database, gpg, repository_id, configuration, "keyring").license == ["Unlicense"]
configuration.set_option("keyring", "license", "GPL MPL")
assert KeyringGenerator(database, gpg, configuration, "keyring").license == ["GPL", "MPL"]
assert KeyringGenerator(database, gpg, repository_id, configuration, "keyring").license == ["GPL", "MPL"]
def test_pkgdesc(database: SQLite, gpg: GPG, configuration: Configuration) -> None:
"""
must generate correct pkgdesc property
"""
assert KeyringGenerator(database, gpg, configuration, "keyring").pkgdesc == "aur-clone PGP keyring"
_, repository_id = configuration.check_loaded()
assert KeyringGenerator(database, gpg, repository_id, configuration, "keyring").pkgdesc == "aur-clone PGP keyring"
configuration.set_option("keyring", "description", "description")
assert KeyringGenerator(database, gpg, configuration, "keyring").pkgdesc == "description"
assert KeyringGenerator(database, gpg, repository_id, configuration, "keyring").pkgdesc == "description"
def test_pkgname(database: SQLite, gpg: GPG, configuration: Configuration) -> None:
"""
must generate correct pkgname property
"""
assert KeyringGenerator(database, gpg, configuration, "keyring").pkgname == "aur-clone-keyring"
_, repository_id = configuration.check_loaded()
assert KeyringGenerator(database, gpg, repository_id, configuration, "keyring").pkgname == "aur-clone-keyring"
configuration.set_option("keyring", "package", "keyring")
assert KeyringGenerator(database, gpg, configuration, "keyring").pkgname == "keyring"
assert KeyringGenerator(database, gpg, repository_id, configuration, "keyring").pkgname == "keyring"
def test_url(database: SQLite, gpg: GPG, configuration: Configuration) -> None:
"""
must generate correct url property
"""
assert KeyringGenerator(database, gpg, configuration, "keyring").url == ""
_, repository_id = configuration.check_loaded()
assert KeyringGenerator(database, gpg, repository_id, configuration, "keyring").url == ""
configuration.set_option("keyring", "homepage", "homepage")
assert KeyringGenerator(database, gpg, configuration, "keyring").url == "homepage"
assert KeyringGenerator(database, gpg, repository_id, configuration, "keyring").url == "homepage"
def test_generate_gpg(keyring_generator: KeyringGenerator, mocker: MockerFixture) -> None:

View File

@ -9,50 +9,62 @@ def test_init_path(configuration: Configuration) -> None:
"""
must set relative path to mirrorlist
"""
assert MirrorlistGenerator(configuration, "mirrorlist").path == Path("etc") / "pacman.d" / "aur-clone-mirrorlist"
_, repository_id = configuration.check_loaded()
assert MirrorlistGenerator(repository_id, configuration, "mirrorlist").path == \
Path("etc") / "pacman.d" / "aur-clone-mirrorlist"
configuration.set_option("mirrorlist", "path", "/etc")
assert MirrorlistGenerator(configuration, "mirrorlist").path == Path("etc")
assert MirrorlistGenerator(repository_id, configuration, "mirrorlist").path == Path("etc")
def test_license(configuration: Configuration) -> None:
"""
must generate correct licenses list
"""
assert MirrorlistGenerator(configuration, "mirrorlist").license == ["Unlicense"]
_, repository_id = configuration.check_loaded()
assert MirrorlistGenerator(repository_id, configuration, "mirrorlist").license == ["Unlicense"]
configuration.set_option("mirrorlist", "license", "GPL MPL")
assert MirrorlistGenerator(configuration, "mirrorlist").license == ["GPL", "MPL"]
assert MirrorlistGenerator(repository_id, configuration, "mirrorlist").license == ["GPL", "MPL"]
def test_pkgdesc(configuration: Configuration) -> None:
"""
must generate correct pkgdesc property
"""
assert MirrorlistGenerator(configuration, "mirrorlist").pkgdesc == "aur-clone mirror list for use by pacman"
_, repository_id = configuration.check_loaded()
assert MirrorlistGenerator(repository_id, configuration, "mirrorlist").pkgdesc == \
"aur-clone mirror list for use by pacman"
configuration.set_option("mirrorlist", "description", "description")
assert MirrorlistGenerator(configuration, "mirrorlist").pkgdesc == "description"
assert MirrorlistGenerator(repository_id, configuration, "mirrorlist").pkgdesc == "description"
def test_pkgname(configuration: Configuration) -> None:
"""
must generate correct pkgname property
"""
assert MirrorlistGenerator(configuration, "mirrorlist").pkgname == "aur-clone-mirrorlist"
_, repository_id = configuration.check_loaded()
assert MirrorlistGenerator(repository_id, configuration, "mirrorlist").pkgname == "aur-clone-mirrorlist"
configuration.set_option("mirrorlist", "package", "mirrorlist")
assert MirrorlistGenerator(configuration, "mirrorlist").pkgname == "mirrorlist"
assert MirrorlistGenerator(repository_id, configuration, "mirrorlist").pkgname == "mirrorlist"
def test_url(configuration: Configuration) -> None:
"""
must generate correct url property
"""
assert MirrorlistGenerator(configuration, "mirrorlist").url == ""
_, repository_id = configuration.check_loaded()
assert MirrorlistGenerator(repository_id, configuration, "mirrorlist").url == ""
configuration.set_option("mirrorlist", "homepage", "homepage")
assert MirrorlistGenerator(configuration, "mirrorlist").url == "homepage"
assert MirrorlistGenerator(repository_id, configuration, "mirrorlist").url == "homepage"
def test_generate_mirrorlist(mirrorlist_generator: MirrorlistGenerator, mocker: MockerFixture) -> None:

View File

@ -25,8 +25,9 @@ def test_on_start(configuration: Configuration, mocker: MockerFixture) -> None:
"""
context_mock = mocker.patch("ahriman.core._Context.get")
run_mock = mocker.patch("ahriman.core.support.package_creator.PackageCreator.run")
_, repository_id = configuration.check_loaded()
trigger = KeyringTrigger("x86_64", configuration)
trigger = KeyringTrigger(repository_id, configuration)
trigger.on_start()
context_mock.assert_has_calls([MockCall(ContextKey("sign", GPG)), MockCall(ContextKey("database", SQLite))])
run_mock.assert_called_once_with()

View File

@ -20,7 +20,8 @@ def test_on_start(configuration: Configuration, mocker: MockerFixture) -> None:
must run report for specified targets
"""
run_mock = mocker.patch("ahriman.core.support.package_creator.PackageCreator.run")
_, repository_id = configuration.check_loaded()
trigger = MirrorlistTrigger("x86_64", configuration)
trigger = MirrorlistTrigger(repository_id, configuration)
trigger.on_start()
run_mock.assert_called_once_with()

View File

@ -7,6 +7,8 @@ from ahriman.core.support.package_creator import PackageCreator
from ahriman.models.context_key import ContextKey
from ahriman.models.package import Package
from ahriman.models.package_description import PackageDescription
from ahriman.models.package_source import PackageSource
from ahriman.models.remote_source import RemoteSource
def test_run(package_creator: PackageCreator, database: SQLite, mocker: MockerFixture) -> None:
@ -16,7 +18,7 @@ def test_run(package_creator: PackageCreator, database: SQLite, mocker: MockerFi
package = Package(
base=package_creator.generator.pkgname,
version=package_creator.generator.pkgver,
remote=None,
remote=RemoteSource(source=PackageSource.Local),
packages={package_creator.generator.pkgname: PackageDescription()},
)
local_path = package_creator.configuration.repository_paths.cache_for(package_creator.generator.pkgname)

View File

@ -20,9 +20,9 @@ def test_process(spawner: Spawn) -> None:
callback = MagicMock()
callback.return_value = True
spawner.process(callback, args, spawner.architecture, "id", spawner.queue)
spawner.process(callback, args, spawner.repository_id, "id", spawner.queue)
callback.assert_called_once_with(args, spawner.architecture)
callback.assert_called_once_with(args, spawner.repository_id)
(uuid, status, time) = spawner.queue.get()
assert uuid == "id"
assert status
@ -37,7 +37,7 @@ def test_process_error(spawner: Spawn) -> None:
callback = MagicMock()
callback.return_value = False
spawner.process(callback, MagicMock(), spawner.architecture, "id", spawner.queue)
spawner.process(callback, MagicMock(), spawner.repository_id, "id", spawner.queue)
(uuid, status, time) = spawner.queue.get()
assert uuid == "id"

View File

@ -15,7 +15,8 @@ def trigger(configuration: Configuration) -> Trigger:
Returns:
Trigger: trigger test instance
"""
return Trigger("x86_64", configuration)
_, repository_id = configuration.check_loaded()
return Trigger(repository_id, configuration)
@pytest.fixture
@ -28,4 +29,5 @@ def trigger_loader(configuration: Configuration) -> TriggerLoader:
Returns:
TriggerLoader: trigger loader test instance
"""
return TriggerLoader.load("x86_64", configuration)
_, repository_id = configuration.check_loaded()
return TriggerLoader.load(repository_id, configuration)

View File

@ -6,15 +6,23 @@ from ahriman.core.triggers import Trigger
from ahriman.models.result import Result
def test_architecture(trigger: Trigger) -> None:
"""
must return repository architecture for backward compatibility
"""
assert trigger.architecture == trigger.repository_id.architecture
def test_configuration_schema(configuration: Configuration) -> None:
"""
must return used configuration schema
"""
section = "console"
configuration.set_option("report", "target", section)
_, repository_id = configuration.check_loaded()
expected = {section: ReportTrigger.CONFIGURATION_SCHEMA[section]}
assert ReportTrigger.configuration_schema("x86_64", configuration) == expected
assert ReportTrigger.configuration_schema(repository_id, configuration) == expected
def test_configuration_schema_no_section(configuration: Configuration) -> None:
@ -23,7 +31,9 @@ def test_configuration_schema_no_section(configuration: Configuration) -> None:
"""
section = "abracadabra"
configuration.set_option("report", "target", section)
assert ReportTrigger.configuration_schema("x86_64", configuration) == {}
_, repository_id = configuration.check_loaded()
assert ReportTrigger.configuration_schema(repository_id, configuration) == {}
def test_configuration_schema_no_schema(configuration: Configuration) -> None:
@ -33,15 +43,17 @@ def test_configuration_schema_no_schema(configuration: Configuration) -> None:
section = "abracadabra"
configuration.set_option("report", "target", section)
configuration.set_option(section, "key", "value")
_, repository_id = configuration.check_loaded()
assert ReportTrigger.configuration_schema("x86_64", configuration) == {}
assert ReportTrigger.configuration_schema(repository_id, configuration) == {}
def test_configuration_schema_empty() -> None:
def test_configuration_schema_empty(configuration: Configuration) -> None:
"""
must return default schema if no configuration set
"""
assert ReportTrigger.configuration_schema("x86_64", None) == ReportTrigger.CONFIGURATION_SCHEMA
_, repository_id = configuration.check_loaded()
assert ReportTrigger.configuration_schema(repository_id, None) == ReportTrigger.CONFIGURATION_SCHEMA
def test_configuration_schema_variables(configuration: Configuration) -> None:

View File

@ -48,8 +48,10 @@ def test_load_trigger_package_error_on_creation(trigger_loader: TriggerLoader, c
must raise InvalidException on trigger initialization if any exception is thrown
"""
mocker.patch("ahriman.core.triggers.trigger.Trigger.__init__", side_effect=Exception())
_, repository_id = configuration.check_loaded()
with pytest.raises(ExtensionError):
trigger_loader.load_trigger("ahriman.core.report.ReportTrigger", "x86_64", configuration)
trigger_loader.load_trigger("ahriman.core.report.ReportTrigger", repository_id, configuration)
def test_load_trigger_class_package(trigger_loader: TriggerLoader) -> None:
@ -155,8 +157,9 @@ def test_on_stop_with_on_start(configuration: Configuration, mocker: MockerFixtu
mocker.patch("ahriman.core.upload.UploadTrigger.on_start")
mocker.patch("ahriman.core.report.ReportTrigger.on_start")
on_stop_mock = mocker.patch("ahriman.core.triggers.trigger_loader.TriggerLoader.on_stop")
_, repository_id = configuration.check_loaded()
trigger_loader = TriggerLoader.load("x86_64", configuration)
trigger_loader = TriggerLoader.load(repository_id, configuration)
trigger_loader.on_start()
del trigger_loader
on_stop_mock.assert_called_once_with()
@ -167,8 +170,9 @@ def test_on_stop_without_on_start(configuration: Configuration, mocker: MockerFi
must call not on_stop on exit if on_start wasn't called
"""
on_stop_mock = mocker.patch("ahriman.core.triggers.trigger_loader.TriggerLoader.on_stop")
_, repository_id = configuration.check_loaded()
trigger_loader = TriggerLoader.load("x86_64", configuration)
trigger_loader = TriggerLoader.load(repository_id, configuration)
del trigger_loader
on_stop_mock.assert_not_called()

View File

@ -4,33 +4,34 @@ from typing import Any
from unittest.mock import MagicMock
from ahriman.core.configuration import Configuration
from ahriman.core.upload.github import Github
from ahriman.core.upload.github import GitHub
from ahriman.core.upload.remote_service import RemoteService
from ahriman.core.upload.rsync import Rsync
from ahriman.core.upload.s3 import S3
@pytest.fixture
def github(configuration: Configuration) -> Github:
def github(configuration: Configuration) -> GitHub:
"""
fixture for github synchronization
fixture for GitHub synchronization
Args:
configuration(Configuration): configuration fixture
Returns:
Github: github test instance
GitHub: GitHub test instance
"""
return Github("x86_64", configuration, "github:x86_64")
_, repository_id = configuration.check_loaded()
return GitHub(repository_id, configuration, "github:x86_64")
@pytest.fixture
def github_release() -> dict[str, Any]:
"""
fixture for the github release object
fixture for the GitHub release object
Returns:
dict[str, Any]: github test release object
dict[str, Any]: GitHub test release object
"""
return {
"url": "release_url",
@ -59,7 +60,8 @@ def remote_service(configuration: Configuration) -> RemoteService:
"""
configuration.set_option("web", "host", "localhost")
configuration.set_option("web", "port", "8080")
return RemoteService("x86_64", configuration, "remote-service")
_, repository_id = configuration.check_loaded()
return RemoteService(repository_id, configuration, "remote-service")
@pytest.fixture
@ -73,7 +75,8 @@ def rsync(configuration: Configuration) -> Rsync:
Returns:
Rsync: rsync test instance
"""
return Rsync("x86_64", configuration, "rsync")
_, repository_id = configuration.check_loaded()
return Rsync(repository_id, configuration, "rsync")
@pytest.fixture
@ -87,23 +90,25 @@ def s3(configuration: Configuration) -> S3:
Returns:
S3: S3 test instance
"""
return S3("x86_64", configuration, "customs3")
_, repository_id = configuration.check_loaded()
return S3(repository_id, configuration, "customs3")
@pytest.fixture
def s3_remote_objects() -> list[MagicMock]:
def s3_remote_objects(configuration: Configuration) -> list[MagicMock]:
"""
fixture for boto3 like S3 objects
Returns:
list[MagicMock]: boto3 like S3 objects test instance
"""
_, repository_id = configuration.check_loaded()
delete_mock = MagicMock()
result = []
for item in ["a", "b", "c"]:
s3_object = MagicMock()
s3_object.key = f"x86_64/{item}"
s3_object.key = f"{repository_id.name}/{repository_id.architecture}/{item}"
s3_object.e_tag = f"\"{item}\""
s3_object.delete = delete_mock

View File

@ -6,34 +6,50 @@ from pytest_mock import MockerFixture
from typing import Any
from unittest.mock import call as MockCall
from ahriman.core.upload.github import Github
from ahriman.core.configuration import Configuration
from ahriman.core.upload.github import GitHub
def test_asset_remove(github: Github, github_release: dict[str, Any], mocker: MockerFixture) -> None:
def test_github_release_tag(configuration: Configuration) -> None:
"""
must correctly define GitHub release tag
"""
_, repository_id = configuration.check_loaded()
instance = GitHub(repository_id, configuration, "github:x86_64")
assert instance.github_release_tag == instance.github_release_tag_name == repository_id.architecture
configuration.set_option("github:x86_64", "use_full_release_name", "yes")
instance = GitHub(repository_id, configuration, "github:x86_64")
assert instance.github_release_tag == f"{repository_id.name}-{repository_id.architecture}"
assert instance.github_release_tag_name == f"{repository_id.name} {repository_id.architecture}"
def test_asset_remove(github: GitHub, github_release: dict[str, Any], mocker: MockerFixture) -> None:
"""
must remove asset from the release
"""
request_mock = mocker.patch("ahriman.core.upload.github.Github.make_request")
request_mock = mocker.patch("ahriman.core.upload.github.GitHub.make_request")
github.asset_remove(github_release, "asset_name")
request_mock.assert_called_once_with("DELETE", "asset_url")
def test_asset_remove_unknown(github: Github, github_release: dict[str, Any], mocker: MockerFixture) -> None:
def test_asset_remove_unknown(github: GitHub, github_release: dict[str, Any], mocker: MockerFixture) -> None:
"""
must not fail if no asset found
"""
request_mock = mocker.patch("ahriman.core.upload.github.Github.make_request")
request_mock = mocker.patch("ahriman.core.upload.github.GitHub.make_request")
github.asset_remove(github_release, "unknown_asset_name")
request_mock.assert_not_called()
def test_asset_upload(github: Github, github_release: dict[str, Any], mocker: MockerFixture) -> None:
def test_asset_upload(github: GitHub, github_release: dict[str, Any], mocker: MockerFixture) -> None:
"""
must upload asset to the repository
"""
mocker.patch("pathlib.Path.open")
request_mock = mocker.patch("ahriman.core.upload.github.Github.make_request")
remove_mock = mocker.patch("ahriman.core.upload.github.Github.asset_remove")
request_mock = mocker.patch("ahriman.core.upload.github.GitHub.make_request")
remove_mock = mocker.patch("ahriman.core.upload.github.GitHub.asset_remove")
github.asset_upload(github_release, Path("/root/new.tar.xz"))
request_mock.assert_called_once_with("POST", "upload_url", params=[("name", "new.tar.xz")],
@ -42,13 +58,13 @@ def test_asset_upload(github: Github, github_release: dict[str, Any], mocker: Mo
remove_mock.assert_not_called()
def test_asset_upload_with_removal(github: Github, github_release: dict[str, Any], mocker: MockerFixture) -> None:
def test_asset_upload_with_removal(github: GitHub, github_release: dict[str, Any], mocker: MockerFixture) -> None:
"""
must remove existing file before upload
"""
mocker.patch("pathlib.Path.open")
mocker.patch("ahriman.core.upload.github.Github.make_request")
remove_mock = mocker.patch("ahriman.core.upload.github.Github.asset_remove")
mocker.patch("ahriman.core.upload.github.GitHub.make_request")
remove_mock = mocker.patch("ahriman.core.upload.github.GitHub.asset_remove")
github.asset_upload(github_release, Path("asset_name"))
github.asset_upload(github_release, Path("/root/asset_name"))
@ -58,14 +74,14 @@ def test_asset_upload_with_removal(github: Github, github_release: dict[str, Any
])
def test_asset_upload_empty_mimetype(github: Github, github_release: dict[str, Any], mocker: MockerFixture) -> None:
def test_asset_upload_empty_mimetype(github: GitHub, github_release: dict[str, Any], mocker: MockerFixture) -> None:
"""
must upload asset to the repository with empty mime type if the library cannot guess it
"""
mocker.patch("pathlib.Path.open")
mocker.patch("ahriman.core.upload.github.Github.asset_remove")
mocker.patch("ahriman.core.upload.github.GitHub.asset_remove")
mocker.patch("mimetypes.guess_type", return_value=(None, None))
request_mock = mocker.patch("ahriman.core.upload.github.Github.make_request")
request_mock = mocker.patch("ahriman.core.upload.github.GitHub.make_request")
github.asset_upload(github_release, Path("/root/new.tar.xz"))
request_mock.assert_called_once_with("POST", "upload_url", params=[("name", "new.tar.xz")],
@ -73,7 +89,7 @@ def test_asset_upload_empty_mimetype(github: Github, github_release: dict[str, A
headers={"Content-Type": "application/octet-stream"})
def test_get_local_files(github: Github, resource_path_root: Path, mocker: MockerFixture) -> None:
def test_get_local_files(github: GitHub, resource_path_root: Path, mocker: MockerFixture) -> None:
"""
must get all local files recursively
"""
@ -82,29 +98,29 @@ def test_get_local_files(github: Github, resource_path_root: Path, mocker: Mocke
walk_mock.assert_called()
def test_files_remove(github: Github, github_release: dict[str, Any], mocker: MockerFixture) -> None:
def test_files_remove(github: GitHub, github_release: dict[str, Any], mocker: MockerFixture) -> None:
"""
must remove files from the remote
"""
remove_mock = mocker.patch("ahriman.core.upload.github.Github.asset_remove")
remove_mock = mocker.patch("ahriman.core.upload.github.GitHub.asset_remove")
github.files_remove(github_release, {Path("a"): "a"}, {"a": "a", "b": "b"})
remove_mock.assert_called_once_with(github_release, "b")
def test_files_remove_empty(github: Github, github_release: dict[str, Any], mocker: MockerFixture) -> None:
def test_files_remove_empty(github: GitHub, github_release: dict[str, Any], mocker: MockerFixture) -> None:
"""
must remove nothing if nothing changed
"""
remove_mock = mocker.patch("ahriman.core.upload.github.Github.asset_remove")
remove_mock = mocker.patch("ahriman.core.upload.github.GitHub.asset_remove")
github.files_remove(github_release, {Path("a"): "a"}, {"a": "a"})
remove_mock.assert_not_called()
def test_files_upload(github: Github, github_release: dict[str, Any], mocker: MockerFixture) -> None:
def test_files_upload(github: GitHub, github_release: dict[str, Any], mocker: MockerFixture) -> None:
"""
must upload files to the remote
"""
upload_mock = mocker.patch("ahriman.core.upload.github.Github.asset_upload")
upload_mock = mocker.patch("ahriman.core.upload.github.GitHub.asset_upload")
github.files_upload(github_release, {Path("a"): "a", Path("b"): "c", Path("c"): "c"}, {"a": "a", "b": "b"})
upload_mock.assert_has_calls([
MockCall(github_release, Path("b")),
@ -112,83 +128,84 @@ def test_files_upload(github: Github, github_release: dict[str, Any], mocker: Mo
])
def test_files_upload_empty(github: Github, github_release: dict[str, Any], mocker: MockerFixture) -> None:
def test_files_upload_empty(github: GitHub, github_release: dict[str, Any], mocker: MockerFixture) -> None:
"""
must upload nothing if nothing changed
"""
upload_mock = mocker.patch("ahriman.core.upload.github.Github.asset_upload")
upload_mock = mocker.patch("ahriman.core.upload.github.GitHub.asset_upload")
github.files_upload(github_release, {Path("a"): "a"}, {"a": "a"})
upload_mock.assert_not_called()
def test_release_create(github: Github, mocker: MockerFixture) -> None:
def test_release_create(github: GitHub, mocker: MockerFixture) -> None:
"""
must create release
"""
request_mock = mocker.patch("ahriman.core.upload.github.Github.make_request")
request_mock = mocker.patch("ahriman.core.upload.github.GitHub.make_request")
github.release_create()
request_mock.assert_called_once_with("POST", pytest.helpers.anyvar(str, True),
json={"tag_name": github.architecture, "name": github.architecture})
request_mock.assert_called_once_with(
"POST", pytest.helpers.anyvar(str, True),
json={"tag_name": github.github_release_tag, "name": github.github_release_tag_name})
def test_release_get(github: Github, mocker: MockerFixture) -> None:
def test_release_get(github: GitHub, mocker: MockerFixture) -> None:
"""
must get release
"""
request_mock = mocker.patch("ahriman.core.upload.github.Github.make_request")
request_mock = mocker.patch("ahriman.core.upload.github.GitHub.make_request")
github.release_get()
request_mock.assert_called_once_with("GET", pytest.helpers.anyvar(str, True))
def test_release_get_empty(github: Github, mocker: MockerFixture) -> None:
def test_release_get_empty(github: GitHub, mocker: MockerFixture) -> None:
"""
must return nothing in case of 404 status code
"""
response = requests.Response()
response.status_code = 404
mocker.patch("ahriman.core.upload.github.Github.make_request",
mocker.patch("ahriman.core.upload.github.GitHub.make_request",
side_effect=requests.HTTPError(response=response))
assert github.release_get() is None
def test_release_get_exception(github: Github, mocker: MockerFixture) -> None:
def test_release_get_exception(github: GitHub, mocker: MockerFixture) -> None:
"""
must re-raise non HTTPError exception
"""
mocker.patch("ahriman.core.upload.github.Github.make_request", side_effect=Exception())
mocker.patch("ahriman.core.upload.github.GitHub.make_request", side_effect=Exception())
with pytest.raises(Exception):
github.release_get()
def test_release_get_exception_http_error(github: Github, mocker: MockerFixture) -> None:
def test_release_get_exception_http_error(github: GitHub, mocker: MockerFixture) -> None:
"""
must re-raise HTTPError exception with code differs from 404
"""
exception = requests.HTTPError(response=requests.Response())
mocker.patch("ahriman.core.upload.github.Github.make_request", side_effect=exception)
mocker.patch("ahriman.core.upload.github.GitHub.make_request", side_effect=exception)
with pytest.raises(requests.HTTPError):
github.release_get()
def test_release_update(github: Github, github_release: dict[str, Any], mocker: MockerFixture) -> None:
def test_release_update(github: GitHub, github_release: dict[str, Any], mocker: MockerFixture) -> None:
"""
must update release
"""
request_mock = mocker.patch("ahriman.core.upload.github.Github.make_request")
request_mock = mocker.patch("ahriman.core.upload.github.GitHub.make_request")
github.release_update(github_release, "body")
request_mock.assert_called_once_with("POST", "release_url", json={"body": "body"})
def test_release_sync(github: Github, mocker: MockerFixture) -> None:
def test_release_sync(github: GitHub, mocker: MockerFixture) -> None:
"""
must run sync command
"""
release_get_mock = mocker.patch("ahriman.core.upload.github.Github.release_get", return_value={})
get_hashes_mock = mocker.patch("ahriman.core.upload.github.Github.get_hashes", return_value={})
get_local_files_mock = mocker.patch("ahriman.core.upload.github.Github.get_local_files", return_value={})
files_upload_mock = mocker.patch("ahriman.core.upload.github.Github.files_upload")
files_remove_mock = mocker.patch("ahriman.core.upload.github.Github.files_remove")
release_update_mock = mocker.patch("ahriman.core.upload.github.Github.release_update")
release_get_mock = mocker.patch("ahriman.core.upload.github.GitHub.release_get", return_value={})
get_hashes_mock = mocker.patch("ahriman.core.upload.github.GitHub.get_hashes", return_value={})
get_local_files_mock = mocker.patch("ahriman.core.upload.github.GitHub.get_local_files", return_value={})
files_upload_mock = mocker.patch("ahriman.core.upload.github.GitHub.files_upload")
files_remove_mock = mocker.patch("ahriman.core.upload.github.GitHub.files_remove")
release_update_mock = mocker.patch("ahriman.core.upload.github.GitHub.release_update")
github.sync(Path("local"), [])
release_get_mock.assert_called_once_with()
@ -199,17 +216,17 @@ def test_release_sync(github: Github, mocker: MockerFixture) -> None:
release_update_mock.assert_called_once_with({}, pytest.helpers.anyvar(int))
def test_release_sync_create_release(github: Github, mocker: MockerFixture) -> None:
def test_release_sync_create_release(github: GitHub, mocker: MockerFixture) -> None:
"""
must create release in case if it does not exist
"""
mocker.patch("ahriman.core.upload.github.Github.release_get", return_value=None)
mocker.patch("ahriman.core.upload.github.Github.get_hashes")
mocker.patch("ahriman.core.upload.github.Github.get_local_files")
mocker.patch("ahriman.core.upload.github.Github.files_upload")
mocker.patch("ahriman.core.upload.github.Github.files_remove")
mocker.patch("ahriman.core.upload.github.Github.release_update")
release_create_mock = mocker.patch("ahriman.core.upload.github.Github.release_create")
mocker.patch("ahriman.core.upload.github.GitHub.release_get", return_value=None)
mocker.patch("ahriman.core.upload.github.GitHub.get_hashes")
mocker.patch("ahriman.core.upload.github.GitHub.get_local_files")
mocker.patch("ahriman.core.upload.github.GitHub.files_upload")
mocker.patch("ahriman.core.upload.github.GitHub.files_remove")
mocker.patch("ahriman.core.upload.github.GitHub.release_update")
release_create_mock = mocker.patch("ahriman.core.upload.github.GitHub.release_create")
github.sync(Path("local"), [])
release_create_mock.assert_called_once_with()

View File

@ -3,12 +3,32 @@ from pytest_mock import MockerFixture
from typing import Any
from unittest.mock import MagicMock, call as MockCall
from ahriman.core.configuration import Configuration
from ahriman.core.upload.s3 import S3
from ahriman.models.repository_paths import RepositoryPaths
_chunk_size = 8 * 1024 * 1024
def test_object_path(configuration: Configuration, mocker: MockerFixture) -> None:
"""
must correctly read object path
"""
_, repository_id = configuration.check_loaded()
# new-style tree
assert S3(repository_id, configuration, "customs3").object_path == Path("aur-clone/x86_64")
# legacy tree
mocker.patch.object(RepositoryPaths, "_suffix", Path("x86_64"))
assert S3(repository_id, configuration, "customs3").object_path == Path("x86_64")
# user defined prefix
configuration.set_option("customs3", "object_path", "local")
assert S3(repository_id, configuration, "customs3").object_path == Path("local")
def test_calculate_etag_big(resource_path_root: Path) -> None:
"""
must calculate checksum for path which is more than one chunk
@ -38,12 +58,12 @@ def test_files_remove(s3_remote_objects: list[Any]) -> None:
must remove remote objects
"""
local_files = {
Path(item.key): item.e_tag for item in s3_remote_objects if item.key != "x86_64/a"
Path(item.key): item.e_tag for item in s3_remote_objects if item.key != "aur-clone/x86_64/a"
}
remote_objects = {Path(item.key): item for item in s3_remote_objects}
S3.files_remove(local_files, remote_objects)
remote_objects[Path("x86_64/a")].delete.assert_called_once_with()
remote_objects[Path("aur-clone/x86_64/a")].delete.assert_called_once_with()
def test_files_upload(s3: S3, s3_remote_objects: list[Any], mocker: MockerFixture) -> None:
@ -55,9 +75,10 @@ def test_files_upload(s3: S3, s3_remote_objects: list[Any], mocker: MockerFixtur
root = Path("path")
local_files = {
Path(item.key.replace("a", "d")): item.e_tag.replace("b", "d").replace("\"", "")
Path(item.key[:-1] + item.key[-1].replace("a", "d")): item.e_tag.replace("b", "d").replace("\"", "")
for item in s3_remote_objects
}
print(local_files)
remote_objects = {Path(item.key): item for item in s3_remote_objects}
mocker.patch("mimetypes.guess_type", side_effect=mimetype)
@ -67,12 +88,12 @@ def test_files_upload(s3: S3, s3_remote_objects: list[Any], mocker: MockerFixtur
upload_mock.upload_file.assert_has_calls(
[
MockCall(
Filename=str(root / s3.architecture / "b"),
Key=f"{s3.architecture}/{s3.architecture}/b",
Filename=str(root / s3.object_path / "b"),
Key=f"{s3.object_path}/b",
ExtraArgs={"ContentType": "text/html"}),
MockCall(
Filename=str(root / s3.architecture / "d"),
Key=f"{s3.architecture}/{s3.architecture}/d",
Filename=str(root / s3.object_path / "d"),
Key=f"{s3.object_path}/d",
ExtraArgs=None),
],
any_order=True)
@ -91,7 +112,7 @@ def test_get_remote_objects(s3: S3, s3_remote_objects: list[Any]) -> None:
"""
must generate list of remote objects by calling boto3 function
"""
expected = {Path(item.key).relative_to(s3.architecture): item for item in s3_remote_objects}
expected = {Path(item.key).relative_to(s3.object_path): item for item in s3_remote_objects}
s3.bucket = MagicMock()
s3.bucket.objects.filter.return_value = s3_remote_objects

View File

@ -14,8 +14,10 @@ def test_upload_failure(configuration: Configuration, mocker: MockerFixture) ->
must raise SyncFailed on errors
"""
mocker.patch("ahriman.core.upload.rsync.Rsync.sync", side_effect=Exception())
_, repository_id = configuration.check_loaded()
with pytest.raises(SynchronizationError):
Upload.load("x86_64", configuration, "rsync").run(Path("path"), [])
Upload.load(repository_id, configuration, "rsync").run(Path("path"), [])
def test_report_dummy(configuration: Configuration, mocker: MockerFixture) -> None:
@ -24,7 +26,9 @@ def test_report_dummy(configuration: Configuration, mocker: MockerFixture) -> No
"""
mocker.patch("ahriman.models.upload_settings.UploadSettings.from_option", return_value=UploadSettings.Disabled)
upload_mock = mocker.patch("ahriman.core.upload.upload.Upload.sync")
Upload.load("x86_64", configuration, "disabled").run(Path("path"), [])
_, repository_id = configuration.check_loaded()
Upload.load(repository_id, configuration, "disabled").run(Path("path"), [])
upload_mock.assert_called_once_with(Path("path"), [])
@ -33,7 +37,9 @@ def test_upload_rsync(configuration: Configuration, mocker: MockerFixture) -> No
must upload via rsync
"""
upload_mock = mocker.patch("ahriman.core.upload.rsync.Rsync.sync")
Upload.load("x86_64", configuration, "rsync").run(Path("path"), [])
_, repository_id = configuration.check_loaded()
Upload.load(repository_id, configuration, "rsync").run(Path("path"), [])
upload_mock.assert_called_once_with(Path("path"), [])
@ -42,7 +48,9 @@ def test_upload_s3(configuration: Configuration, mocker: MockerFixture) -> None:
must upload via s3
"""
upload_mock = mocker.patch("ahriman.core.upload.s3.S3.sync")
Upload.load("x86_64", configuration, "customs3").run(Path("path"), [])
_, repository_id = configuration.check_loaded()
Upload.load(repository_id, configuration, "customs3").run(Path("path"), [])
upload_mock.assert_called_once_with(Path("path"), [])
@ -50,8 +58,10 @@ def test_upload_github(configuration: Configuration, mocker: MockerFixture) -> N
"""
must upload via github
"""
upload_mock = mocker.patch("ahriman.core.upload.github.Github.sync")
Upload.load("x86_64", configuration, "github").run(Path("path"), [])
upload_mock = mocker.patch("ahriman.core.upload.github.GitHub.sync")
_, repository_id = configuration.check_loaded()
Upload.load(repository_id, configuration, "github").run(Path("path"), [])
upload_mock.assert_called_once_with(Path("path"), [])
@ -62,6 +72,7 @@ def test_upload_ahriman(configuration: Configuration, mocker: MockerFixture) ->
upload_mock = mocker.patch("ahriman.core.upload.remote_service.RemoteService.sync")
configuration.set_option("web", "host", "localhost")
configuration.set_option("web", "port", "8080")
_, repository_id = configuration.check_loaded()
Upload.load("x86_64", configuration, "remote-service").run(Path("path"), [])
Upload.load(repository_id, configuration, "remote-service").run(Path("path"), [])
upload_mock.assert_called_once_with(Path("path"), [])

View File

@ -22,7 +22,8 @@ def test_on_result(configuration: Configuration, mocker: MockerFixture) -> None:
"""
configuration.set_option("upload", "target", "rsync")
run_mock = mocker.patch("ahriman.core.upload.upload.Upload.run")
_, repository_id = configuration.check_loaded()
trigger = UploadTrigger("x86_64", configuration)
trigger = UploadTrigger(repository_id, configuration)
trigger.on_result(Result(), [])
run_mock.assert_called_once_with(configuration.repository_paths.repository, [])