port part of settings to database (#54)

This commit is contained in:
2022-03-31 01:48:06 +03:00
committed by GitHub
parent fb02e676af
commit 28cc38aaa5
117 changed files with 2768 additions and 1044 deletions

View File

@ -3,24 +3,27 @@ import pytest
from ahriman.core.auth.mapping import Mapping
from ahriman.core.auth.oauth import OAuth
from ahriman.core.configuration import Configuration
from ahriman.core.database.sqlite import SQLite
@pytest.fixture
def mapping(configuration: Configuration) -> Mapping:
def mapping(configuration: Configuration, database: SQLite) -> Mapping:
"""
auth provider fixture
:param configuration: configuration fixture
:param database: database fixture
:return: auth service instance
"""
return Mapping(configuration)
return Mapping(configuration, database)
@pytest.fixture
def oauth(configuration: Configuration) -> OAuth:
def oauth(configuration: Configuration, database: SQLite) -> OAuth:
"""
OAuth provider fixture
:param configuration: configuration fixture
:param database: database fixture
:return: OAuth2 service instance
"""
configuration.set("web", "address", "https://example.com")
return OAuth(configuration)
return OAuth(configuration, database)

View File

@ -1,10 +1,8 @@
import pytest
from ahriman.core.auth.auth import Auth
from ahriman.core.auth.mapping import Mapping
from ahriman.core.auth.oauth import OAuth
from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import DuplicateUser
from ahriman.core.database.sqlite import SQLite
from ahriman.models.user import User
from ahriman.models.user_access import UserAccess
@ -17,88 +15,42 @@ def test_auth_control(auth: Auth) -> None:
assert "button" in auth.auth_control # I think it should be button
def test_load_dummy(configuration: Configuration) -> None:
def test_load_dummy(configuration: Configuration, database: SQLite) -> None:
"""
must load dummy validator if authorization is not enabled
"""
configuration.set_option("auth", "target", "disabled")
auth = Auth.load(configuration)
auth = Auth.load(configuration, database)
assert isinstance(auth, Auth)
def test_load_dummy_empty(configuration: Configuration) -> None:
def test_load_dummy_empty(configuration: Configuration, database: SQLite) -> None:
"""
must load dummy validator if no option set
"""
auth = Auth.load(configuration)
auth = Auth.load(configuration, database)
assert isinstance(auth, Auth)
def test_load_mapping(configuration: Configuration) -> None:
def test_load_mapping(configuration: Configuration, database: SQLite) -> None:
"""
must load mapping validator if option set
"""
configuration.set_option("auth", "target", "configuration")
auth = Auth.load(configuration)
auth = Auth.load(configuration, database)
assert isinstance(auth, Mapping)
def test_load_oauth(configuration: Configuration) -> None:
def test_load_oauth(configuration: Configuration, database: SQLite) -> None:
"""
must load OAuth2 validator if option set
"""
configuration.set_option("auth", "target", "oauth")
configuration.set_option("web", "address", "https://example.com")
auth = Auth.load(configuration)
auth = Auth.load(configuration, database)
assert isinstance(auth, OAuth)
def test_get_users(mapping: Auth, configuration: Configuration) -> None:
"""
must return valid user list
"""
user_write = User("user_write", "pwd_write", UserAccess.Write)
write_section = Configuration.section_name("auth", user_write.access.value)
configuration.set_option(write_section, user_write.username, user_write.password)
user_read = User("user_read", "pwd_read", UserAccess.Read)
read_section = Configuration.section_name("auth", user_read.access.value)
configuration.set_option(read_section, user_read.username, user_read.password)
user_read = User("user_read", "pwd_read", UserAccess.Read)
read_section = Configuration.section_name("auth", user_read.access.value)
configuration.set_option(read_section, user_read.username, user_read.password)
users = mapping.get_users(configuration)
expected = {user_write.username: user_write, user_read.username: user_read}
assert users == expected
def test_get_users_normalized(mapping: Auth, configuration: Configuration) -> None:
"""
must return user list with normalized usernames in keys
"""
user = User("UsEr", "pwd_read", UserAccess.Read)
read_section = Configuration.section_name("auth", user.access.value)
configuration.set_option(read_section, user.username, user.password)
users = mapping.get_users(configuration)
expected = user.username.lower()
assert expected in users
assert users[expected].username == expected
def test_get_users_duplicate(mapping: Auth, configuration: Configuration, user: User) -> None:
"""
must raise exception on duplicate username
"""
write_section = Configuration.section_name("auth", UserAccess.Write.value)
configuration.set_option(write_section, user.username, user.password)
read_section = Configuration.section_name("auth", UserAccess.Read.value)
configuration.set_option(read_section, user.username, user.password)
with pytest.raises(DuplicateUser):
mapping.get_users(configuration)
async def test_check_credentials(auth: Auth, user: User) -> None:
"""
must pass any credentials

View File

@ -1,15 +1,17 @@
from pytest_mock import MockerFixture
from ahriman.core.auth.mapping import Mapping
from ahriman.models.user import User
from ahriman.models.user_access import UserAccess
async def test_check_credentials(mapping: Mapping, user: User) -> None:
async def test_check_credentials(mapping: Mapping, user: User, mocker: MockerFixture) -> None:
"""
must return true for valid credentials
"""
current_password = user.password
user.password = user.hash_password(mapping.salt)
mapping._users[user.username] = user
user = user.hash_password(mapping.salt)
mocker.patch("ahriman.core.database.sqlite.SQLite.user_get", return_value=user)
assert await mapping.check_credentials(user.username, current_password)
# here password is hashed so it is invalid
assert not await mapping.check_credentials(user.username, user.password)
@ -31,19 +33,19 @@ async def test_check_credentials_unknown(mapping: Mapping, user: User) -> None:
assert not await mapping.check_credentials(user.username, user.password)
def test_get_user(mapping: Mapping, user: User) -> None:
def test_get_user(mapping: Mapping, user: User, mocker: MockerFixture) -> None:
"""
must return user from storage by username
"""
mapping._users[user.username] = user
mocker.patch("ahriman.core.database.sqlite.SQLite.user_get", return_value=user)
assert mapping.get_user(user.username) == user
def test_get_user_normalized(mapping: Mapping, user: User) -> None:
def test_get_user_normalized(mapping: Mapping, user: User, mocker: MockerFixture) -> None:
"""
must return user from storage by username case-insensitive
"""
mapping._users[user.username] = user
mocker.patch("ahriman.core.database.sqlite.SQLite.user_get", return_value=user)
assert mapping.get_user(user.username.upper()) == user
@ -54,20 +56,27 @@ def test_get_user_unknown(mapping: Mapping, user: User) -> None:
assert mapping.get_user(user.username) is None
async def test_known_username(mapping: Mapping, user: User) -> None:
async def test_known_username(mapping: Mapping, user: User, mocker: MockerFixture) -> None:
"""
must allow only known users
"""
mapping._users[user.username] = user
mocker.patch("ahriman.core.database.sqlite.SQLite.user_get", return_value=user)
assert await mapping.known_username(user.username)
async def test_known_username_unknown(mapping: Mapping, user: User, mocker: MockerFixture) -> None:
"""
must not allow only known users
"""
assert not await mapping.known_username(None)
mocker.patch("ahriman.core.database.sqlite.SQLite.user_get", return_value=None)
assert not await mapping.known_username(user.password)
async def test_verify_access(mapping: Mapping, user: User) -> None:
async def test_verify_access(mapping: Mapping, user: User, mocker: MockerFixture) -> None:
"""
must verify user access
"""
mapping._users[user.username] = user
mocker.patch("ahriman.core.database.sqlite.SQLite.user_get", return_value=user)
assert await mapping.verify_access(user.username, user.access, None)
assert not await mapping.verify_access(user.username, UserAccess.Write, None)

View File

@ -22,16 +22,25 @@ def test_add(mocker: MockerFixture) -> None:
exception=None, cwd=local, logger=pytest.helpers.anyvar(int))
def test_add_skip(mocker: MockerFixture) -> None:
"""
must skip addition of files to index if no fiels found
"""
mocker.patch("pathlib.Path.glob", return_value=[])
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.Sources._check_output")
Sources.add(Path("local"), "pattern1")
check_output_mock.assert_not_called()
def test_diff(mocker: MockerFixture) -> None:
"""
must calculate diff
"""
write_mock = mocker.patch("pathlib.Path.write_text")
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.Sources._check_output")
local = Path("local")
Sources.diff(local, Path("patch"))
write_mock.assert_called_once_with(pytest.helpers.anyvar(int))
assert Sources.diff(local)
check_output_mock.assert_called_once_with("git", "diff",
exception=None, cwd=local, logger=pytest.helpers.anyvar(int))
@ -142,51 +151,34 @@ def test_load(mocker: MockerFixture) -> None:
fetch_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.fetch")
patch_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.patch_apply")
Sources.load(Path("local"), "remote", Path("patches"))
Sources.load(Path("local"), "remote", "patch")
fetch_mock.assert_called_once_with(Path("local"), "remote")
patch_mock.assert_called_once_with(Path("local"), Path("patches"))
patch_mock.assert_called_once_with(Path("local"), "patch")
def test_load_no_patch(mocker: MockerFixture) -> None:
"""
must load packages sources correctly without patches
"""
mocker.patch("ahriman.core.build_tools.sources.Sources.fetch")
patch_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.patch_apply")
Sources.load(Path("local"), "remote", None)
patch_mock.assert_not_called()
def test_patch_apply(mocker: MockerFixture) -> None:
"""
must apply patches if any
"""
mocker.patch("pathlib.Path.is_dir", return_value=True)
glob_mock = mocker.patch("pathlib.Path.glob", return_value=[Path("01.patch"), Path("02.patch")])
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.Sources._check_output")
local = Path("local")
Sources.patch_apply(local, Path("patches"))
glob_mock.assert_called_once_with("*.patch")
check_output_mock.assert_has_calls([
mock.call("git", "apply", "--ignore-space-change", "--ignore-whitespace", "01.patch",
exception=None, cwd=local, logger=pytest.helpers.anyvar(int)),
mock.call("git", "apply", "--ignore-space-change", "--ignore-whitespace", "02.patch",
exception=None, cwd=local, logger=pytest.helpers.anyvar(int)),
])
def test_patch_apply_no_dir(mocker: MockerFixture) -> None:
"""
must not fail if no patches directory exists
"""
mocker.patch("pathlib.Path.is_dir", return_value=False)
glob_mock = mocker.patch("pathlib.Path.glob")
Sources.patch_apply(Path("local"), Path("patches"))
glob_mock.assert_not_called()
def test_patch_apply_no_patches(mocker: MockerFixture) -> None:
"""
must not fail if no patches exist
"""
mocker.patch("pathlib.Path.is_dir", return_value=True)
mocker.patch("pathlib.Path.glob", return_value=[])
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.Sources._check_output")
Sources.patch_apply(Path("local"), Path("patches"))
check_output_mock.assert_not_called()
Sources.patch_apply(local, "patches")
check_output_mock.assert_called_once_with(
"git", "apply", "--ignore-space-change", "--ignore-whitespace",
exception=None, cwd=local, input_data="patches", logger=pytest.helpers.anyvar(int)
)
def test_patch_create(mocker: MockerFixture) -> None:
@ -196,6 +188,15 @@ def test_patch_create(mocker: MockerFixture) -> None:
add_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.add")
diff_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.diff")
Sources.patch_create(Path("local"), Path("patch"), "glob")
Sources.patch_create(Path("local"), "glob")
add_mock.assert_called_once_with(Path("local"), "glob")
diff_mock.assert_called_once_with(Path("local"), Path("patch"))
diff_mock.assert_called_once_with(Path("local"))
def test_patch_create_with_newline(mocker: MockerFixture) -> None:
"""
created patch must have new line at the end
"""
mocker.patch("ahriman.core.build_tools.sources.Sources.add")
mocker.patch("ahriman.core.build_tools.sources.Sources.diff", return_value="diff")
assert Sources.patch_create(Path("local"), "glob").endswith("\n")

View File

@ -1,6 +1,8 @@
from pathlib import Path
from pytest_mock import MockerFixture
from ahriman.core.build_tools.task import Task
from ahriman.core.database.sqlite import SQLite
def test_build(task_ahriman: Task, mocker: MockerFixture) -> None:
@ -8,11 +10,11 @@ def test_build(task_ahriman: Task, mocker: MockerFixture) -> None:
must build package
"""
check_output_mock = mocker.patch("ahriman.core.build_tools.task.Task._check_output")
task_ahriman.build()
task_ahriman.build(Path("ahriman"))
check_output_mock.assert_called()
def test_init_with_cache(task_ahriman: Task, mocker: MockerFixture) -> None:
def test_init_with_cache(task_ahriman: Task, database: SQLite, mocker: MockerFixture) -> None:
"""
must copy tree instead of fetch
"""
@ -20,5 +22,5 @@ def test_init_with_cache(task_ahriman: Task, mocker: MockerFixture) -> None:
mocker.patch("ahriman.core.build_tools.sources.Sources.load")
copytree_mock = mocker.patch("shutil.copytree")
task_ahriman.init(None)
task_ahriman.init(Path("ahriman"), database)
copytree_mock.assert_called_once() # we do not check full command here, sorry

View File

@ -0,0 +1,13 @@
import pytest
from sqlite3 import Connection
from unittest.mock import MagicMock
@pytest.fixture
def connection() -> Connection:
"""
mock object for sqlite3 connection
:return: sqlite3 connection test instance
"""
return MagicMock()

View File

@ -0,0 +1,33 @@
from pytest_mock import MockerFixture
from sqlite3 import Connection
from ahriman.core.configuration import Configuration
from ahriman.core.database.data import migrate_data
from ahriman.models.migration_result import MigrationResult
from ahriman.models.repository_paths import RepositoryPaths
def test_migrate_data_initial(connection: Connection, configuration: Configuration,
repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
"""
must perform initial migration
"""
packages = mocker.patch("ahriman.core.database.data.migrate_package_statuses")
users = mocker.patch("ahriman.core.database.data.migrate_users_data")
migrate_data(MigrationResult(old_version=0, new_version=900), connection, configuration, repository_paths)
packages.assert_called_once_with(connection, repository_paths)
users.assert_called_once_with(connection, configuration)
def test_migrate_data_skip(connection: Connection, configuration: Configuration,
repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
"""
must not migrate data if version is up-to-date
"""
packages = mocker.patch("ahriman.core.database.data.migrate_package_statuses")
users = mocker.patch("ahriman.core.database.data.migrate_users_data")
migrate_data(MigrationResult(old_version=900, new_version=900), connection, configuration, repository_paths)
packages.assert_not_called()
users.assert_not_called()

View File

@ -0,0 +1,43 @@
import pytest
from pytest_mock import MockerFixture
from sqlite3 import Connection
from unittest import mock
from ahriman.core.database.data import migrate_package_statuses
from ahriman.models.package import Package
from ahriman.models.repository_paths import RepositoryPaths
def test_migrate_package_statuses(connection: Connection, package_ahriman: Package, repository_paths: RepositoryPaths,
mocker: MockerFixture) -> None:
"""
must migrate packages to database
"""
response = {"packages": [pytest.helpers.get_package_status_extended(package_ahriman)]}
mocker.patch("pathlib.Path.is_file", return_value=True)
mocker.patch("pathlib.Path.open")
mocker.patch("json.load", return_value=response)
unlink_mock = mocker.patch("pathlib.Path.unlink")
migrate_package_statuses(connection, repository_paths)
unlink_mock.assert_called_once_with()
connection.execute.assert_has_calls([
mock.call(pytest.helpers.anyvar(str, strict=True), pytest.helpers.anyvar(int)),
mock.call(pytest.helpers.anyvar(str, strict=True), pytest.helpers.anyvar(int)),
])
connection.executemany.assert_has_calls([
mock.call(pytest.helpers.anyvar(str, strict=True), pytest.helpers.anyvar(int)),
])
connection.commit.assert_called_once_with()
def test_migrate_package_statuses_skip(connection: Connection, repository_paths: RepositoryPaths,
mocker: MockerFixture) -> None:
"""
must skip packages migration if no cache file found
"""
mocker.patch("pathlib.Path.is_file", return_value=False)
migrate_package_statuses(connection, repository_paths)
connection.commit.assert_not_called()

View File

@ -0,0 +1,53 @@
import pytest
from pathlib import Path
from pytest_mock import MockerFixture
from sqlite3 import Connection
from ahriman.core.database.data import migrate_patches
from ahriman.models.package import Package
from ahriman.models.repository_paths import RepositoryPaths
def test_migrate_patches(connection: Connection, repository_paths: RepositoryPaths,
package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must perform migration for patches
"""
mocker.patch("pathlib.Path.is_dir", return_value=True)
mocker.patch("pathlib.Path.is_file", return_value=True)
iterdir_mock = mocker.patch("pathlib.Path.iterdir", return_value=[Path(package_ahriman.base)])
read_mock = mocker.patch("pathlib.Path.read_text", return_value="patch")
migrate_patches(connection, repository_paths)
iterdir_mock.assert_called_once_with()
read_mock.assert_called_once_with(encoding="utf8")
connection.execute.assert_called_once_with(pytest.helpers.anyvar(str, strict=True), pytest.helpers.anyvar(int))
connection.commit.assert_called_once_with()
def test_migrate_patches_skip(connection: Connection, repository_paths: RepositoryPaths,
mocker: MockerFixture) -> None:
"""
must skip patches migration in case if no root found
"""
mocker.patch("pathlib.Path.is_dir", return_value=False)
iterdir_mock = mocker.patch("pathlib.Path.iterdir")
migrate_patches(connection, repository_paths)
iterdir_mock.assert_not_called()
def test_migrate_patches_no_patch(connection: Connection, repository_paths: RepositoryPaths,
package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must skip package if no match found
"""
mocker.patch("pathlib.Path.is_dir", return_value=True)
mocker.patch("pathlib.Path.is_file", return_value=False)
iterdir_mock = mocker.patch("pathlib.Path.iterdir", return_value=[Path(package_ahriman.base)])
read_mock = mocker.patch("pathlib.Path.read_text")
migrate_patches(connection, repository_paths)
iterdir_mock.assert_called_once_with()
read_mock.assert_not_called()

View File

@ -0,0 +1,22 @@
import pytest
from sqlite3 import Connection
from unittest import mock
from ahriman.core.configuration import Configuration
from ahriman.core.database.data import migrate_users_data
def test_migrate_users_data(connection: Connection, configuration: Configuration) -> None:
"""
must users to database
"""
configuration.set_option("auth:read", "user1", "password1")
configuration.set_option("auth:write", "user2", "password2")
migrate_users_data(connection, configuration)
connection.execute.assert_has_calls([
mock.call(pytest.helpers.anyvar(str, strict=True), pytest.helpers.anyvar(int)),
mock.call(pytest.helpers.anyvar(str, strict=True), pytest.helpers.anyvar(int)),
])
connection.commit.assert_called_once_with()

View File

@ -0,0 +1,15 @@
import pytest
from sqlite3 import Connection
from ahriman.core.database.migrations import Migrations
@pytest.fixture
def migrations(connection: Connection) -> Migrations:
"""
fixture for migrations object
:param connection: sqlite connection fixture
:return: migrations test instance
"""
return Migrations(connection)

View File

@ -0,0 +1,8 @@
from ahriman.core.database.migrations.m000_initial import steps
def test_migration_initial() -> None:
"""
migration must not be empty
"""
assert steps

View File

@ -0,0 +1,109 @@
import pytest
from pytest_mock import MockerFixture
from sqlite3 import Connection
from unittest import mock
from unittest.mock import MagicMock
from ahriman.core.database.migrations import Migrations
from ahriman.models.migration import Migration
from ahriman.models.migration_result import MigrationResult
def test_migrate(connection: Connection, mocker: MockerFixture) -> None:
"""
must perform migrations
"""
run_mock = mocker.patch("ahriman.core.database.migrations.Migrations.run")
Migrations.migrate(connection)
run_mock.assert_called_once_with()
def test_migrations(migrations: Migrations) -> None:
"""
must retrieve migrations
"""
assert migrations.migrations()
def test_run_skip(migrations: Migrations, mocker: MockerFixture) -> None:
"""
must skip migration if version is the same
"""
mocker.patch.object(MigrationResult, "is_outdated", False)
migrations.run()
migrations.connection.cursor.assert_not_called()
def test_run(migrations: Migrations, mocker: MockerFixture) -> None:
"""
must run migration
"""
cursor = MagicMock()
mocker.patch("ahriman.core.database.migrations.Migrations.user_version", return_value=0)
mocker.patch("ahriman.core.database.migrations.Migrations.migrations",
return_value=[Migration(0, "test", ["select 1"])])
migrations.connection.cursor.return_value = cursor
validate_mock = mocker.patch("ahriman.models.migration_result.MigrationResult.validate")
migrations.run()
validate_mock.assert_called_once_with()
cursor.execute.assert_has_calls([
mock.call("begin exclusive"),
mock.call("select 1"),
mock.call("pragma user_version = 1"),
mock.call("commit"),
])
cursor.close.assert_called_once_with()
def test_run_migration_exception(migrations: Migrations, mocker: MockerFixture) -> None:
"""
must rollback and close cursor on exception during migration
"""
cursor = MagicMock()
mocker.patch("logging.Logger.info", side_effect=Exception())
mocker.patch("ahriman.core.database.migrations.Migrations.user_version", return_value=0)
mocker.patch("ahriman.core.database.migrations.Migrations.migrations",
return_value=[Migration(0, "test", ["select 1"])])
mocker.patch("ahriman.models.migration_result.MigrationResult.validate")
migrations.connection.cursor.return_value = cursor
with pytest.raises(Exception):
migrations.run()
cursor.execute.assert_has_calls([
mock.call("begin exclusive"),
mock.call("rollback"),
])
cursor.close.assert_called_once_with()
def test_run_sql_exception(migrations: Migrations, mocker: MockerFixture) -> None:
"""
must close cursor on general migration error
"""
cursor = MagicMock()
cursor.execute.side_effect = Exception()
mocker.patch("ahriman.core.database.migrations.Migrations.user_version", return_value=0)
mocker.patch("ahriman.core.database.migrations.Migrations.migrations",
return_value=[Migration(0, "test", ["select 1"])])
mocker.patch("ahriman.models.migration_result.MigrationResult.validate")
migrations.connection.cursor.return_value = cursor
with pytest.raises(Exception):
migrations.run()
cursor.close.assert_called_once_with()
def test_user_version(migrations: Migrations) -> None:
"""
must correctly extract current migration version
"""
cursor = MagicMock()
cursor.fetchone.return_value = {"user_version": 42}
migrations.connection.execute.return_value = cursor
version = migrations.user_version()
migrations.connection.execute.assert_called_once_with("pragma user_version")
assert version == 42

View File

@ -0,0 +1,97 @@
from ahriman.core.database.sqlite import SQLite
from ahriman.models.user import User
from ahriman.models.user_access import UserAccess
def test_user_get_update(database: SQLite, user: User) -> None:
"""
must retrieve user from the database
"""
database.user_update(user)
assert database.user_get(user.username) == user
def test_user_list(database: SQLite, user: User) -> None:
"""
must return all users
"""
database.user_update(user)
database.user_update(User(user.password, user.username, user.access))
users = database.user_list(None, None)
assert len(users) == 2
assert user in users
assert User(user.password, user.username, user.access) in users
def test_user_list_filter_by_username(database: SQLite) -> None:
"""
must return users filtered by its id
"""
first = User("1", "", UserAccess.Read)
second = User("2", "", UserAccess.Write)
third = User("3", "", UserAccess.Read)
database.user_update(first)
database.user_update(second)
database.user_update(third)
assert database.user_list("1", None) == [first]
assert database.user_list("2", None) == [second]
assert database.user_list("3", None) == [third]
def test_user_list_filter_by_access(database: SQLite) -> None:
"""
must return users filtered by its access
"""
first = User("1", "", UserAccess.Read)
second = User("2", "", UserAccess.Write)
third = User("3", "", UserAccess.Read)
database.user_update(first)
database.user_update(second)
database.user_update(third)
users = database.user_list(None, UserAccess.Read)
assert len(users) == 2
assert first in users
assert third in users
def test_user_list_filter_by_username_access(database: SQLite) -> None:
"""
must return users filtered by its access and username
"""
first = User("1", "", UserAccess.Read)
second = User("2", "", UserAccess.Write)
third = User("3", "", UserAccess.Read)
database.user_update(first)
database.user_update(second)
database.user_update(third)
assert database.user_list("1", UserAccess.Read) == [first]
assert not database.user_list("1", UserAccess.Write)
def test_user_remove_update(database: SQLite, user: User) -> None:
"""
must remove user from the database
"""
database.user_update(user)
database.user_remove(user.username)
assert database.user_get(user.username) is None
def test_user_update(database: SQLite, user: User) -> None:
"""
must update user in the database
"""
database.user_update(user)
assert database.user_get(user.username) == user
new_user = user.hash_password("salt")
new_user.access = UserAccess.Write
database.user_update(new_user)
assert database.user_get(new_user.username) == new_user

View File

@ -0,0 +1,45 @@
from ahriman.core.database.sqlite import SQLite
from ahriman.models.package import Package
def test_build_queue_insert_clear(database: SQLite, package_ahriman: Package, package_python_schedule: Package) -> None:
"""
must clear all packages from queue
"""
database.build_queue_insert(package_ahriman)
database.build_queue_insert(package_python_schedule)
database.build_queue_clear(None)
assert not database.build_queue_get()
def test_build_queue_insert_clear_specific(database: SQLite, package_ahriman: Package,
package_python_schedule: Package) -> None:
"""
must remove only specified package from the queue
"""
database.build_queue_insert(package_ahriman)
database.build_queue_insert(package_python_schedule)
database.build_queue_clear(package_python_schedule.base)
assert database.build_queue_get() == [package_ahriman]
def test_build_queue_insert_get(database: SQLite, package_ahriman: Package) -> None:
"""
must insert and get package from the database
"""
database.build_queue_insert(package_ahriman)
assert database.build_queue_get() == [package_ahriman]
def test_build_queue_insert(database: SQLite, package_ahriman: Package) -> None:
"""
must update user in the database
"""
database.build_queue_insert(package_ahriman)
assert database.build_queue_get() == [package_ahriman]
package_ahriman.version = "42"
database.build_queue_insert(package_ahriman)
assert database.build_queue_get() == [package_ahriman]

View File

@ -0,0 +1,39 @@
import sqlite3
from pytest_mock import MockerFixture
from unittest.mock import MagicMock
from ahriman.core.database.sqlite import SQLite
def test_factory(database: SQLite) -> None:
"""
must convert response to dictionary
"""
result = database.with_connection(lambda conn: conn.execute("select 1 as result").fetchone())
assert isinstance(result, dict)
assert result["result"] == 1
def test_with_connection(database: SQLite, mocker: MockerFixture) -> None:
"""
must run query inside connection
"""
connection_mock = MagicMock()
connect_mock = mocker.patch("sqlite3.connect", return_value=connection_mock)
database.with_connection(lambda conn: conn.execute("select 1"))
connect_mock.assert_called_once_with(database.path, detect_types=sqlite3.PARSE_DECLTYPES)
connection_mock.__enter__().commit.assert_not_called()
def test_with_connection_with_commit(database: SQLite, mocker: MockerFixture) -> None:
"""
must run query inside connection and commit after
"""
connection_mock = MagicMock()
connection_mock.commit.return_value = 42
mocker.patch("sqlite3.connect", return_value=connection_mock)
database.with_connection(lambda conn: conn.execute("select 1"), commit=True)
connection_mock.__enter__().commit.assert_called_once_with()

View File

@ -0,0 +1,168 @@
import pytest
from pytest_mock import MockerFixture
from sqlite3 import Connection
from unittest import mock
from ahriman.core.database.sqlite import SQLite
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
from ahriman.models.package import Package
def test_package_remove_package_base(database: SQLite, connection: Connection) -> None:
"""
must remove package base
"""
database._package_remove_package_base(connection, "package")
connection.execute.assert_has_calls([
mock.call(pytest.helpers.anyvar(str, strict=True), {"package_base": "package"}),
mock.call(pytest.helpers.anyvar(str, strict=True), {"package_base": "package"}),
])
def test_package_remove_packages(database: SQLite, connection: Connection, package_ahriman: Package) -> None:
"""
must remove packages belong to base
"""
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})
connection.executemany.assert_called_once_with(pytest.helpers.anyvar(str, strict=True), [])
def test_package_update_insert_base(database: SQLite, connection: Connection, package_ahriman: Package) -> None:
"""
must insert base package
"""
database._package_update_insert_base(connection, package_ahriman)
connection.execute.assert_called_once_with(pytest.helpers.anyvar(str, strict=True), pytest.helpers.anyvar(int))
def test_package_update_insert_packages(database: SQLite, connection: Connection, package_ahriman: Package) -> None:
"""
must insert single packages
"""
database._package_update_insert_packages(connection, package_ahriman)
connection.executemany(pytest.helpers.anyvar(str, strict=True), pytest.helpers.anyvar(int))
def test_package_update_insert_status(database: SQLite, connection: Connection, package_ahriman: Package) -> None:
"""
must insert single package status
"""
database._package_update_insert_status(connection, package_ahriman.base, BuildStatus())
connection.execute(pytest.helpers.anyvar(str, strict=True), pytest.helpers.anyvar(int))
def test_packages_get_select_package_bases(database: SQLite, connection: Connection) -> None:
"""
must select all bases
"""
database._packages_get_select_package_bases(connection)
connection.execute(pytest.helpers.anyvar(str, strict=True))
def test_packages_get_select_packages(database: SQLite, connection: Connection, package_ahriman: Package) -> None:
"""
must select all packages
"""
database._packages_get_select_packages(connection, {package_ahriman.base: package_ahriman})
connection.execute(pytest.helpers.anyvar(str, strict=True))
def test_packages_get_select_packages_skip(database: SQLite, connection: Connection, package_ahriman: Package) -> None:
"""
must skip unknown packages
"""
view = {"package_base": package_ahriman.base}
for package, properties in package_ahriman.packages.items():
view.update({"package": package})
view.update(properties.view())
connection.execute.return_value = [{"package_base": "random name"}, view]
database._packages_get_select_packages(connection, {package_ahriman.base: package_ahriman})
def test_packages_get_select_statuses(database: SQLite, connection: Connection) -> None:
"""
must select all statuses
"""
database._packages_get_select_statuses(connection)
connection.execute(pytest.helpers.anyvar(str, strict=True))
def test_package_remove(database: SQLite, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must totally remove package from the database
"""
remove_package_mock = mocker.patch("ahriman.core.database.sqlite.SQLite._package_remove_package_base")
remove_packages_mock = mocker.patch("ahriman.core.database.sqlite.SQLite._package_remove_packages")
database.package_remove(package_ahriman.base)
remove_package_mock.assert_called_once_with(pytest.helpers.anyvar(int), package_ahriman.base)
remove_packages_mock.assert_called_once_with(pytest.helpers.anyvar(int), package_ahriman.base, [])
def test_package_update(database: SQLite, package_ahriman: Package, mocker: MockerFixture):
"""
must update package status
"""
status = BuildStatus()
insert_base_mock = mocker.patch("ahriman.core.database.sqlite.SQLite._package_update_insert_base")
insert_status_mock = mocker.patch("ahriman.core.database.sqlite.SQLite._package_update_insert_status")
insert_packages_mock = mocker.patch("ahriman.core.database.sqlite.SQLite._package_update_insert_packages")
remove_packages_mock = mocker.patch("ahriman.core.database.sqlite.SQLite._package_remove_packages")
database.package_update(package_ahriman, status)
insert_base_mock.assert_called_once_with(pytest.helpers.anyvar(int), package_ahriman)
insert_status_mock.assert_called_once_with(pytest.helpers.anyvar(int), package_ahriman.base, status)
insert_packages_mock.assert_called_once_with(pytest.helpers.anyvar(int), package_ahriman)
remove_packages_mock.assert_called_once_with(
pytest.helpers.anyvar(int), package_ahriman.base, package_ahriman.packages.keys())
def test_packages_get(database: SQLite, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must return all packages
"""
select_bases_mock = mocker.patch("ahriman.core.database.sqlite.SQLite._packages_get_select_package_bases",
return_value={package_ahriman.base: package_ahriman})
select_packages_mock = mocker.patch("ahriman.core.database.sqlite.SQLite._packages_get_select_packages")
select_statuses_mock = mocker.patch("ahriman.core.database.sqlite.SQLite._packages_get_select_statuses")
database.packages_get()
select_bases_mock.assert_called_once_with(pytest.helpers.anyvar(int))
select_statuses_mock.assert_called_once_with(pytest.helpers.anyvar(int))
select_packages_mock.assert_called_once_with(pytest.helpers.anyvar(int), {package_ahriman.base: package_ahriman})
def test_package_update_get(database: SQLite, package_ahriman: Package) -> None:
"""
must insert and retrieve package
"""
status = BuildStatus()
database.package_update(package_ahriman, status)
assert next((db_package, db_status)
for db_package, db_status in database.packages_get()
if db_package.base == package_ahriman.base) == (package_ahriman, status)
def test_package_update_remove_get(database: SQLite, package_ahriman: Package) -> None:
"""
must insert, remove and retrieve package
"""
status = BuildStatus()
database.package_update(package_ahriman, status)
database.package_remove(package_ahriman.base)
assert not database.packages_get()
def test_package_update_update(database: SQLite, package_ahriman: Package) -> None:
"""
must perform update for existing package
"""
database.package_update(package_ahriman, BuildStatus())
database.package_update(package_ahriman, BuildStatus(BuildStatusEnum.Failed))
assert next(db_status.status
for db_package, db_status in database.packages_get()
if db_package.base == package_ahriman.base) == BuildStatusEnum.Failed

View File

@ -0,0 +1,55 @@
from ahriman.core.database.sqlite import SQLite
from ahriman.models.package import Package
def test_patches_get_insert(database: SQLite, package_ahriman: Package, package_python_schedule: Package) -> None:
"""
must insert patch to database
"""
database.patches_insert(package_ahriman.base, "patch_1")
database.patches_insert(package_python_schedule.base, "patch_2")
assert database.patches_get(package_ahriman.base) == "patch_1"
assert not database.build_queue_get()
def test_patches_list(database: SQLite, package_ahriman: Package, package_python_schedule: Package) -> None:
"""
must list all patches
"""
database.patches_insert(package_ahriman.base, "patch1")
database.patches_insert(package_python_schedule.base, "patch2")
assert database.patches_list(None) == {package_ahriman.base: "patch1", package_python_schedule.base: "patch2"}
def test_patches_list_filter(database: SQLite, package_ahriman: Package, package_python_schedule: Package) -> None:
"""
must list all patches filtered by package name (same as get)
"""
database.patches_insert(package_ahriman.base, "patch1")
database.patches_insert(package_python_schedule.base, "patch2")
assert database.patches_list(package_ahriman.base) == {package_ahriman.base: "patch1"}
assert database.patches_list(package_python_schedule.base) == {package_python_schedule.base: "patch2"}
def test_patches_insert_remove(database: SQLite, package_ahriman: Package, package_python_schedule: Package) -> None:
"""
must remove patch from database
"""
database.patches_insert(package_ahriman.base, "patch_1")
database.patches_insert(package_python_schedule.base, "patch_2")
database.patches_remove(package_ahriman.base)
assert database.patches_get(package_ahriman.base) is None
database.patches_insert(package_python_schedule.base, "patch_2")
def test_patches_insert_insert(database: SQLite, package_ahriman: Package) -> None:
"""
must update patch in database
"""
database.patches_insert(package_ahriman.base, "patch_1")
assert database.patches_get(package_ahriman.base) == "patch_1"
database.patches_insert(package_ahriman.base, "patch_2")
assert database.patches_get(package_ahriman.base) == "patch_2"

View File

@ -0,0 +1,28 @@
import pytest
from pytest_mock import MockerFixture
from ahriman.core.configuration import Configuration
from ahriman.core.database.sqlite import SQLite
def test_load(configuration: Configuration, mocker: MockerFixture) -> None:
"""
must correctly load instance
"""
init_mock = mocker.patch("ahriman.core.database.sqlite.SQLite.init")
SQLite.load(configuration)
init_mock.assert_called_once_with(configuration)
def test_init(database: SQLite, configuration: Configuration, mocker: MockerFixture) -> None:
"""
must run migrations on init
"""
migrate_schema_mock = mocker.patch("ahriman.core.database.migrations.Migrations.migrate")
migrate_data_mock = mocker.patch("ahriman.core.database.sqlite.migrate_data")
database.init(configuration)
migrate_schema_mock.assert_called_once_with(pytest.helpers.anyvar(int))
migrate_data_mock.assert_called_once_with(
pytest.helpers.anyvar(int), pytest.helpers.anyvar(int), configuration, configuration.repository_paths)

View File

@ -6,9 +6,11 @@ from ahriman.core.formatters.package_printer import PackagePrinter
from ahriman.core.formatters.status_printer import StatusPrinter
from ahriman.core.formatters.string_printer import StringPrinter
from ahriman.core.formatters.update_printer import UpdatePrinter
from ahriman.core.formatters.user_printer import UserPrinter
from ahriman.models.aur_package import AURPackage
from ahriman.models.build_status import BuildStatus
from ahriman.models.package import Package
from ahriman.models.user import User
@pytest.fixture
@ -65,3 +67,13 @@ def update_printer(package_ahriman: Package) -> UpdatePrinter:
:return: build status printer test instance
"""
return UpdatePrinter(package_ahriman, None)
@pytest.fixture
def user_printer(user: User) -> UserPrinter:
"""
fixture for user printer
:param user: user fixture
:return: user printer test instance
"""
return UserPrinter(user)

View File

@ -0,0 +1,15 @@
from ahriman.core.formatters.user_printer import UserPrinter
def test_properties(user_printer: UserPrinter) -> None:
"""
must return non empty properties list
"""
assert user_printer.properties()
def test_title(user_printer: UserPrinter) -> None:
"""
must return non empty title
"""
assert user_printer.title() is not None

View File

@ -3,6 +3,7 @@ import pytest
from pytest_mock import MockerFixture
from ahriman.core.configuration import Configuration
from ahriman.core.database.sqlite import SQLite
from ahriman.core.repository import Repository
from ahriman.core.repository.cleaner import Cleaner
from ahriman.core.repository.executor import Executor
@ -11,68 +12,71 @@ from ahriman.core.repository.update_handler import UpdateHandler
@pytest.fixture
def cleaner(configuration: Configuration, mocker: MockerFixture) -> Cleaner:
def cleaner(configuration: Configuration, database: SQLite, mocker: MockerFixture) -> Cleaner:
"""
fixture for cleaner
:param configuration: configuration fixture
:param database: database fixture
:param mocker: mocker object
:return: cleaner test instance
"""
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
return Cleaner("x86_64", configuration, no_report=True, unsafe=False)
return Cleaner("x86_64", configuration, database, no_report=True, unsafe=False)
@pytest.fixture
def executor(configuration: Configuration, mocker: MockerFixture) -> Executor:
def executor(configuration: Configuration, database: SQLite, mocker: MockerFixture) -> Executor:
"""
fixture for executor
:param configuration: configuration fixture
:param database: database fixture
:param mocker: mocker object
:return: executor test instance
"""
mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_build")
mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_cache")
mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_chroot")
mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_manual")
mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_packages")
mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_queue")
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
return Executor("x86_64", configuration, no_report=True, unsafe=False)
return Executor("x86_64", configuration, database, no_report=True, unsafe=False)
@pytest.fixture
def repository(configuration: Configuration, mocker: MockerFixture) -> Repository:
def repository(configuration: Configuration, database: SQLite, mocker: MockerFixture) -> Repository:
"""
fixture for repository
:param configuration: configuration fixture
:param database: database fixture
:param mocker: mocker object
:return: repository test instance
"""
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
return Repository("x86_64", configuration, no_report=True, unsafe=False)
return Repository("x86_64", configuration, database, no_report=True, unsafe=False)
@pytest.fixture
def properties(configuration: Configuration) -> Properties:
def properties(configuration: Configuration, database: SQLite) -> Properties:
"""
fixture for properties
:param configuration: configuration fixture
:param database: database fixture
:return: properties test instance
"""
return Properties("x86_64", configuration, no_report=True, unsafe=False)
return Properties("x86_64", configuration, database, no_report=True, unsafe=False)
@pytest.fixture
def update_handler(configuration: Configuration, mocker: MockerFixture) -> UpdateHandler:
def update_handler(configuration: Configuration, database: SQLite, mocker: MockerFixture) -> UpdateHandler:
"""
fixture for update handler
:param configuration: configuration fixture
:param database: database fixture
:param mocker: mocker object
:return: update handler test instance
"""
mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_build")
mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_cache")
mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_chroot")
mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_manual")
mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_packages")
mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_queue")
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
return UpdateHandler("x86_64", configuration, no_report=True, unsafe=False)
return UpdateHandler("x86_64", configuration, database, no_report=True, unsafe=False)

View File

@ -36,15 +36,6 @@ def test_packages_built(cleaner: Cleaner) -> None:
cleaner.packages_built()
def test_clear_build(cleaner: Cleaner, mocker: MockerFixture) -> None:
"""
must remove directories with sources
"""
_mock_clear(mocker)
cleaner.clear_build()
_mock_clear_check()
def test_clear_cache(cleaner: Cleaner, mocker: MockerFixture) -> None:
"""
must remove every cached sources
@ -63,15 +54,6 @@ def test_clear_chroot(cleaner: Cleaner, mocker: MockerFixture) -> None:
_mock_clear_check()
def test_clear_manual(cleaner: Cleaner, mocker: MockerFixture) -> None:
"""
must clear directory with manual packages
"""
_mock_clear(mocker)
cleaner.clear_manual()
_mock_clear_check()
def test_clear_packages(cleaner: Cleaner, mocker: MockerFixture) -> None:
"""
must delete built packages
@ -84,10 +66,10 @@ def test_clear_packages(cleaner: Cleaner, mocker: MockerFixture) -> None:
Path.unlink.assert_has_calls([mock.call(), mock.call(), mock.call()])
def test_clear_patches(cleaner: Cleaner, mocker: MockerFixture) -> None:
def test_clear_queue(cleaner: Cleaner, mocker: MockerFixture) -> None:
"""
must clear directory with patches
must clear queued packages from the database
"""
_mock_clear(mocker)
cleaner.clear_patches()
_mock_clear_check()
clear_mock = mocker.patch("ahriman.core.database.sqlite.SQLite.build_queue_clear")
cleaner.clear_queue()
clear_mock.assert_called_once_with(None)

View File

@ -41,9 +41,6 @@ def test_process_build(executor: Executor, package_ahriman: Package, mocker: Moc
move_mock.assert_called_once_with(Path(package_ahriman.base), executor.paths.packages / package_ahriman.base)
# must update status
status_client_mock.assert_called_once_with(package_ahriman.base)
# must clear directory
from ahriman.core.repository.cleaner import Cleaner
Cleaner.clear_build.assert_called_once_with()
def test_process_build_failure(executor: Executor, package_ahriman: Package, mocker: MockerFixture) -> None:

View File

@ -1,51 +1,52 @@
from pytest_mock import MockerFixture
from ahriman.core.configuration import Configuration
from ahriman.core.database.sqlite import SQLite
from ahriman.core.exceptions import UnsafeRun
from ahriman.core.repository.properties import Properties
from ahriman.core.status.web_client import WebClient
def test_create_tree_on_load(configuration: Configuration, mocker: MockerFixture) -> None:
def test_create_tree_on_load(configuration: Configuration, database: SQLite, mocker: MockerFixture) -> None:
"""
must create tree on load
"""
mocker.patch("ahriman.core.repository.properties.check_user")
tree_create_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
Properties("x86_64", configuration, True, False)
Properties("x86_64", configuration, database, True, False)
tree_create_mock.assert_called_once_with()
def test_create_tree_on_load_unsafe(configuration: Configuration, mocker: MockerFixture) -> None:
def test_create_tree_on_load_unsafe(configuration: Configuration, database: SQLite, mocker: MockerFixture) -> None:
"""
must not create tree on load in case if user differs from the root owner
"""
mocker.patch("ahriman.core.repository.properties.check_user", side_effect=UnsafeRun(0, 1))
tree_create_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
Properties("x86_64", configuration, True, False)
Properties("x86_64", configuration, database, True, False)
tree_create_mock.assert_not_called()
def test_create_dummy_report_client(configuration: Configuration, mocker: MockerFixture) -> None:
def test_create_dummy_report_client(configuration: Configuration, database: SQLite, mocker: MockerFixture) -> None:
"""
must create dummy report client if report is disabled
"""
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
load_mock = mocker.patch("ahriman.core.status.client.Client.load")
properties = Properties("x86_64", configuration, True, False)
properties = Properties("x86_64", configuration, database, True, False)
load_mock.assert_not_called()
assert not isinstance(properties.reporter, WebClient)
def test_create_full_report_client(configuration: Configuration, mocker: MockerFixture) -> None:
def test_create_full_report_client(configuration: Configuration, database: SQLite, mocker: MockerFixture) -> None:
"""
must create load report client if report is enabled
"""
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
load_mock = mocker.patch("ahriman.core.status.client.Client.load")
Properties("x86_64", configuration, False, False)
Properties("x86_64", configuration, database, False, False)
load_mock.assert_called_once_with(configuration)

View File

@ -168,7 +168,7 @@ def test_updates_manual_clear(update_handler: UpdateHandler, mocker: MockerFixtu
update_handler.updates_manual()
from ahriman.core.repository.cleaner import Cleaner
Cleaner.clear_manual.assert_called_once_with()
Cleaner.clear_queue.assert_called_once_with()
def test_updates_manual_status_known(update_handler: UpdateHandler, package_ahriman: Package,
@ -176,9 +176,8 @@ def test_updates_manual_status_known(update_handler: UpdateHandler, package_ahri
"""
must create record for known package via reporter
"""
mocker.patch("pathlib.Path.iterdir", return_value=[package_ahriman.base])
mocker.patch("ahriman.core.database.sqlite.SQLite.build_queue_get", return_value=[package_ahriman])
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[package_ahriman])
mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman)
status_client_mock = mocker.patch("ahriman.core.status.client.Client.set_pending")
update_handler.updates_manual()
@ -190,9 +189,8 @@ def test_updates_manual_status_unknown(update_handler: UpdateHandler, package_ah
"""
must create record for unknown package via reporter
"""
mocker.patch("pathlib.Path.iterdir", return_value=[package_ahriman.base])
mocker.patch("ahriman.core.database.sqlite.SQLite.build_queue_get", return_value=[package_ahriman])
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[])
mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman)
status_client_mock = mocker.patch("ahriman.core.status.client.Client.set_unknown")
update_handler.updates_manual()
@ -204,8 +202,6 @@ def test_updates_manual_with_failures(update_handler: UpdateHandler, package_ahr
"""
must process manual through the packages with failure
"""
mocker.patch("pathlib.Path.iterdir", return_value=[package_ahriman.base])
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[])
mocker.patch("ahriman.models.package.Package.load", side_effect=Exception())
mocker.patch("ahriman.core.database.sqlite.SQLite.build_queue_get", side_effect=Exception())
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[package_ahriman])
assert update_handler.updates_manual() == []

View File

@ -1,33 +1,8 @@
import pytest
from typing import Any, Dict
from ahriman.core.configuration import Configuration
from ahriman.core.status.client import Client
from ahriman.core.status.web_client import WebClient
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
from ahriman.models.package import Package
# helpers
@pytest.helpers.register
def get_package_status(package: Package) -> Dict[str, Any]:
"""
helper to extract package status from package
:param package: package object
:return: simplified package status map (with only status and view)
"""
return {"status": BuildStatusEnum.Unknown.value, "package": package.view()}
@pytest.helpers.register
def get_package_status_extended(package: Package) -> Dict[str, Any]:
"""
helper to extract package status from package
:param package: package object
:return: full package status map (with timestamped build status and view)
"""
return {"status": BuildStatus().view(), "package": package.view()}
# fixtures
@ -47,5 +22,5 @@ def web_client(configuration: Configuration) -> WebClient:
:param configuration: configuration fixture
:return: web client test instance
"""
configuration.set("web", "port", 8080)
configuration.set("web", "port", "8080")
return WebClient(configuration)

View File

@ -61,13 +61,6 @@ def test_get_self(client: Client) -> None:
assert client.get_self().status == BuildStatusEnum.Unknown
def test_reload_auth(client: Client) -> None:
"""
must process auth reload without errors
"""
client.reload_auth()
def test_remove(client: Client, package_ahriman: Package) -> None:
"""
must process remove without errors

View File

@ -6,6 +6,7 @@ from pytest_mock import MockerFixture
from unittest.mock import PropertyMock
from ahriman.core.configuration import Configuration
from ahriman.core.database.sqlite import SQLite
from ahriman.core.exceptions import UnknownPackage
from ahriman.core.status.watcher import Watcher
from ahriman.core.status.web_client import WebClient
@ -13,7 +14,7 @@ from ahriman.models.build_status import BuildStatus, BuildStatusEnum
from ahriman.models.package import Package
def test_force_no_report(configuration: Configuration, mocker: MockerFixture) -> None:
def test_force_no_report(configuration: Configuration, database: SQLite, mocker: MockerFixture) -> None:
"""
must force dummy report client
"""
@ -21,122 +22,12 @@ def test_force_no_report(configuration: Configuration, mocker: MockerFixture) ->
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
load_mock = mocker.patch("ahriman.core.status.client.Client.load")
watcher = Watcher("x86_64", configuration)
watcher = Watcher("x86_64", configuration, database)
load_mock.assert_not_called()
assert not isinstance(watcher.repository.reporter, WebClient)
def test_cache_load(watcher: Watcher, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must load state from cache
"""
response = {"packages": [pytest.helpers.get_package_status_extended(package_ahriman)]}
mocker.patch("pathlib.Path.is_file", return_value=True)
mocker.patch("pathlib.Path.open")
mocker.patch("json.load", return_value=response)
watcher.known = {package_ahriman.base: (None, None)}
watcher._cache_load()
package, status = watcher.known[package_ahriman.base]
assert package == package_ahriman
assert status.status == BuildStatusEnum.Unknown
def test_cache_load_json_error(watcher: Watcher, mocker: MockerFixture) -> None:
"""
must not fail on json errors
"""
mocker.patch("pathlib.Path.is_file", return_value=True)
mocker.patch("pathlib.Path.open")
mocker.patch("json.load", side_effect=Exception())
watcher._cache_load()
assert not watcher.known
def test_cache_load_no_file(watcher: Watcher, mocker: MockerFixture) -> None:
"""
must not fail on missing file
"""
mocker.patch("pathlib.Path.is_file", return_value=False)
watcher._cache_load()
assert not watcher.known
def test_cache_load_package_load_error(watcher: Watcher, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must not fail on json errors
"""
response = {"packages": [pytest.helpers.get_package_status_extended(package_ahriman)]}
mocker.patch("pathlib.Path.is_file", return_value=True)
mocker.patch("pathlib.Path.open")
mocker.patch("ahriman.models.package.Package.from_json", side_effect=Exception())
mocker.patch("json.load", return_value=response)
watcher._cache_load()
assert not watcher.known
def test_cache_load_unknown(watcher: Watcher, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must not load unknown package
"""
response = {"packages": [pytest.helpers.get_package_status_extended(package_ahriman)]}
mocker.patch("pathlib.Path.is_file", return_value=True)
mocker.patch("pathlib.Path.open")
mocker.patch("json.load", return_value=response)
watcher._cache_load()
assert not watcher.known
def test_cache_save(watcher: Watcher, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must save state to cache
"""
mocker.patch("pathlib.Path.open")
json_mock = mocker.patch("json.dump")
watcher.known = {package_ahriman.base: (package_ahriman, BuildStatus())}
watcher._cache_save()
json_mock.assert_called_once_with(pytest.helpers.anyvar(int), pytest.helpers.anyvar(int))
def test_cache_save_failed(watcher: Watcher, mocker: MockerFixture) -> None:
"""
must not fail on dumping packages
"""
mocker.patch("pathlib.Path.open")
mocker.patch("json.dump", side_effect=Exception())
watcher._cache_save()
def test_cache_save_load(watcher: Watcher, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must save state to cache which can be loaded later
"""
dump_file = Path(tempfile.mktemp()) # nosec
mocker.patch("ahriman.core.status.watcher.Watcher.cache_path",
new_callable=PropertyMock, return_value=dump_file)
known_current = {package_ahriman.base: (package_ahriman, BuildStatus())}
watcher.known = known_current
watcher._cache_save()
watcher.known = {package_ahriman.base: (None, None)}
watcher._cache_load()
assert watcher.known == known_current
dump_file.unlink()
def test_get(watcher: Watcher, package_ahriman: Package) -> None:
"""
must return package status
@ -160,7 +51,7 @@ def test_load(watcher: Watcher, package_ahriman: Package, mocker: MockerFixture)
must correctly load packages
"""
mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[package_ahriman])
cache_mock = mocker.patch("ahriman.core.status.watcher.Watcher._cache_load")
cache_mock = mocker.patch("ahriman.core.database.sqlite.SQLite.packages_get")
watcher.load()
cache_mock.assert_called_once_with()
@ -173,9 +64,10 @@ def test_load_known(watcher: Watcher, package_ahriman: Package, mocker: MockerFi
"""
must correctly load packages with known statuses
"""
status = BuildStatus(BuildStatusEnum.Success)
mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[package_ahriman])
mocker.patch("ahriman.core.status.watcher.Watcher._cache_load")
watcher.known = {package_ahriman.base: (package_ahriman, BuildStatus(BuildStatusEnum.Success))}
mocker.patch("ahriman.core.database.sqlite.SQLite.packages_get", return_value=[(package_ahriman, status)])
watcher.known = {package_ahriman.base: (package_ahriman, status)}
watcher.load()
_, status = watcher.known[package_ahriman.base]
@ -186,32 +78,32 @@ def test_remove(watcher: Watcher, package_ahriman: Package, mocker: MockerFixtur
"""
must remove package base
"""
cache_mock = mocker.patch("ahriman.core.status.watcher.Watcher._cache_save")
cache_mock = mocker.patch("ahriman.core.database.sqlite.SQLite.package_remove")
watcher.known = {package_ahriman.base: (package_ahriman, BuildStatus())}
watcher.remove(package_ahriman.base)
assert not watcher.known
cache_mock.assert_called_once_with()
cache_mock.assert_called_once_with(package_ahriman.base)
def test_remove_unknown(watcher: Watcher, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must not fail on unknown base removal
"""
cache_mock = mocker.patch("ahriman.core.status.watcher.Watcher._cache_save")
cache_mock = mocker.patch("ahriman.core.database.sqlite.SQLite.package_remove")
watcher.remove(package_ahriman.base)
cache_mock.assert_called_once_with()
cache_mock.assert_called_once_with(package_ahriman.base)
def test_update(watcher: Watcher, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must update package status
"""
cache_mock = mocker.patch("ahriman.core.status.watcher.Watcher._cache_save")
cache_mock = mocker.patch("ahriman.core.database.sqlite.SQLite.package_update")
watcher.update(package_ahriman.base, BuildStatusEnum.Unknown, package_ahriman)
cache_mock.assert_called_once_with()
cache_mock.assert_called_once_with(package_ahriman, pytest.helpers.anyvar(int))
package, status = watcher.known[package_ahriman.base]
assert package == package_ahriman
assert status.status == BuildStatusEnum.Unknown
@ -221,25 +113,22 @@ def test_update_ping(watcher: Watcher, package_ahriman: Package, mocker: MockerF
"""
must update package status only for known package
"""
cache_mock = mocker.patch("ahriman.core.status.watcher.Watcher._cache_save")
cache_mock = mocker.patch("ahriman.core.database.sqlite.SQLite.package_update")
watcher.known = {package_ahriman.base: (package_ahriman, BuildStatus())}
watcher.update(package_ahriman.base, BuildStatusEnum.Success, None)
cache_mock.assert_called_once_with()
cache_mock.assert_called_once_with(package_ahriman, pytest.helpers.anyvar(int))
package, status = watcher.known[package_ahriman.base]
assert package == package_ahriman
assert status.status == BuildStatusEnum.Success
def test_update_unknown(watcher: Watcher, package_ahriman: Package, mocker: MockerFixture) -> None:
def test_update_unknown(watcher: Watcher, package_ahriman: Package) -> None:
"""
must fail on unknown package status update only
"""
cache_mock = mocker.patch("ahriman.core.status.watcher.Watcher._cache_save")
with pytest.raises(UnknownPackage):
watcher.update(package_ahriman.base, BuildStatusEnum.Unknown, None)
cache_mock.assert_called_once_with()
def test_update_self(watcher: Watcher) -> None:

View File

@ -230,32 +230,6 @@ def test_get_self_failed_http_error(web_client: WebClient, mocker: MockerFixture
assert web_client.get_self().status == BuildStatusEnum.Unknown
def test_reload_auth(web_client: WebClient, mocker: MockerFixture) -> None:
"""
must process auth reload
"""
requests_mock = mocker.patch("requests.Session.post")
web_client.reload_auth()
requests_mock.assert_called_once_with(pytest.helpers.anyvar(str, True))
def test_reload_auth_failed(web_client: WebClient, mocker: MockerFixture) -> None:
"""
must suppress any exception happened during auth reload
"""
mocker.patch("requests.Session.post", side_effect=Exception())
web_client.reload_auth()
def test_reload_auth_failed_http_error(web_client: WebClient, mocker: MockerFixture) -> None:
"""
must suppress any exception happened during removal
"""
mocker.patch("requests.Session.post", side_effect=requests.exceptions.HTTPError())
web_client.reload_auth()
def test_remove(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must process package removal

View File

@ -8,6 +8,14 @@ from unittest import mock
from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import InitializeException
from ahriman.models.repository_paths import RepositoryPaths
def test_repository_paths(configuration: Configuration, repository_paths: RepositoryPaths) -> None:
"""
must return repository paths
"""
assert configuration.repository_paths == repository_paths
def test_from_path(mocker: MockerFixture) -> None:
@ -40,6 +48,33 @@ def test_from_path_file_missing(mocker: MockerFixture) -> None:
read_mock.assert_called_once_with(configuration.SYSTEM_CONFIGURATION_PATH)
def test_check_loaded(configuration: Configuration) -> None:
"""
must return valid path and architecture
"""
path, architecture = configuration.check_loaded()
assert path == configuration.path
assert architecture == configuration.architecture
def test_check_loaded_path(configuration: Configuration) -> None:
"""
must raise exception if path is none
"""
configuration.path = None
with pytest.raises(InitializeException):
configuration.check_loaded()
def test_check_loaded_architecture(configuration: Configuration) -> None:
"""
must raise exception if architecture is none
"""
configuration.architecture = None
with pytest.raises(InitializeException):
configuration.check_loaded()
def test_dump(configuration: Configuration) -> None:
"""
dump must not be empty

View File

@ -2,9 +2,9 @@ import pytest
from pytest_mock import MockerFixture
from ahriman.core.database.sqlite import SQLite
from ahriman.core.tree import Leaf, Tree
from ahriman.models.package import Package
from ahriman.models.repository_paths import RepositoryPaths
def test_leaf_is_root_empty(leaf_ahriman: Leaf) -> None:
@ -37,7 +37,7 @@ def test_leaf_is_root_true(leaf_ahriman: Leaf, leaf_python_schedule: Leaf) -> No
assert not leaf_ahriman.is_root([leaf_python_schedule])
def test_leaf_load(package_ahriman: Package, repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
def test_leaf_load(package_ahriman: Package, database: SQLite, mocker: MockerFixture) -> None:
"""
must load with dependencies
"""
@ -46,12 +46,12 @@ def test_leaf_load(package_ahriman: Package, repository_paths: RepositoryPaths,
dependencies_mock = mocker.patch("ahriman.models.package.Package.dependencies", return_value={"ahriman-dependency"})
rmtree_mock = mocker.patch("shutil.rmtree")
leaf = Leaf.load(package_ahriman, repository_paths)
leaf = Leaf.load(package_ahriman, database)
assert leaf.package == package_ahriman
assert leaf.dependencies == {"ahriman-dependency"}
tempdir_mock.assert_called_once_with()
load_mock.assert_called_once_with(
pytest.helpers.anyvar(int), package_ahriman.git_url, repository_paths.patches_for(package_ahriman.base))
pytest.helpers.anyvar(int), package_ahriman.git_url, database.patches_get(package_ahriman.base))
dependencies_mock.assert_called_once_with(pytest.helpers.anyvar(int))
rmtree_mock.assert_called_once_with(pytest.helpers.anyvar(int), ignore_errors=True)
@ -69,8 +69,8 @@ def test_tree_levels(leaf_ahriman: Leaf, leaf_python_schedule: Leaf) -> None:
assert second == [leaf_ahriman.package]
def test_tree_load(package_ahriman: Package, package_python_schedule: Package,
repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
def test_tree_load(package_ahriman: Package, package_python_schedule: Package, database: SQLite,
mocker: MockerFixture) -> None:
"""
must package list
"""
@ -79,5 +79,5 @@ def test_tree_load(package_ahriman: Package, package_python_schedule: Package,
mocker.patch("ahriman.models.package.Package.dependencies")
mocker.patch("shutil.rmtree")
tree = Tree.load([package_ahriman, package_python_schedule], repository_paths)
tree = Tree.load([package_ahriman, package_python_schedule], database)
assert len(tree.leaves) == 2

View File

@ -7,7 +7,7 @@ from pathlib import Path
from pytest_mock import MockerFixture
from ahriman.core.exceptions import InvalidOption, UnsafeRun
from ahriman.core.util import check_output, check_user, filter_json, package_like, pretty_datetime, pretty_size, walk
from ahriman.core.util import check_output, check_user, filter_json, package_like, pretty_datetime, pretty_size, tmpdir, walk
from ahriman.models.package import Package
from ahriman.models.repository_paths import RepositoryPaths
@ -207,6 +207,25 @@ def test_pretty_size_empty() -> None:
assert pretty_size(None) == ""
def test_tmpdir() -> None:
"""
must create temporary directory and remove it after
"""
with tmpdir() as directory:
assert directory.is_dir()
assert not directory.exists()
def test_tmpdir_failure() -> None:
"""
must create temporary directory and remove it even after exception
"""
with pytest.raises(Exception):
with tmpdir() as directory:
raise Exception()
assert not directory.exists()
def test_walk(resource_path_root: Path) -> None:
"""
must traverse directory recursively