From 066d1b1dde3340690045d6c89e8acf89b6b58f5f Mon Sep 17 00:00:00 2001 From: Evgenii Alekseev Date: Wed, 18 Jun 2025 17:57:52 +0300 Subject: [PATCH] refactor: rework few tests and build system This commit includes the following changes * Bump github actions * Update tests github action to check documentation and streamline process * Update test cases to use temporary directories as roots * Simplify tox.ini --- .bandit-test.yml | 6 --- .github/workflows/docker.yml | 12 +++--- .github/workflows/regress.yml | 2 - .github/workflows/release.yml | 7 ++-- .github/workflows/setup.yml | 4 +- .github/workflows/tests.sh | 10 ----- .github/workflows/tests.yml | 15 +++++-- docs/architecture.rst | 3 +- docs/requirements.txt | 32 +++++++-------- .../core/configuration/configuration.py | 12 ++++++ src/ahriman/web/routes.py | 4 +- .../application/handlers/test_handler.py | 8 ++++ .../application/handlers/test_handler_copy.py | 11 +++-- .../handlers/test_handler_setup.py | 7 +++- .../handlers/test_handler_users.py | 4 +- tests/ahriman/application/test_ahriman.py | 1 + tests/ahriman/conftest.py | 14 ++++--- .../core/configuration/test_configuration.py | 9 +++++ .../core/configuration/test_validator.py | 4 +- tests/testresources/core/ahriman.ini | 2 - tox.ini | 40 ++++++++++--------- 21 files changed, 122 insertions(+), 85 deletions(-) delete mode 100644 .bandit-test.yml delete mode 100755 .github/workflows/tests.sh diff --git a/.bandit-test.yml b/.bandit-test.yml deleted file mode 100644 index 659fca58..00000000 --- a/.bandit-test.yml +++ /dev/null @@ -1,6 +0,0 @@ -skips: - - B101 - - B104 - - B105 - - B106 - - B404 diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 09e109f7..789945d8 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -21,18 +21,18 @@ jobs: packages: write steps: - - uses: docker/setup-qemu-action@v2 + - uses: docker/setup-qemu-action@v3 - - uses: docker/setup-buildx-action@v2 + - uses: docker/setup-buildx-action@v3 - name: Login to docker hub - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Login to github container registry - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.repository_owner }} @@ -40,7 +40,7 @@ jobs: - name: Extract docker metadata id: meta - uses: docker/metadata-action@v3 + uses: docker/metadata-action@v5 with: images: | arcan1s/ahriman @@ -50,7 +50,7 @@ jobs: type=edge - name: Build an image and push - uses: docker/build-push-action@v4 + uses: docker/build-push-action@v6 with: file: docker/Dockerfile push: true diff --git a/.github/workflows/regress.yml b/.github/workflows/regress.yml index 6b114c74..c0ebb3a5 100644 --- a/.github/workflows/regress.yml +++ b/.github/workflows/regress.yml @@ -37,8 +37,6 @@ jobs: - repo:/var/lib/ahriman steps: - - uses: actions/checkout@v3 - - run: pacman -Sy - name: Init repository diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 702b61bf..8e438294 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Extract version id: version @@ -27,8 +27,7 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} filter: 'Release \d+\.\d+\.\d+' - - name: Install dependencies - uses: ConorMacBride/install-package@v1.1.0 + - uses: ConorMacBride/install-package@v1.1.0 with: apt: tox @@ -38,7 +37,7 @@ jobs: VERSION: ${{ steps.version.outputs.VERSION }} - name: Publish release - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: body: | ${{ steps.changelog.outputs.compareurl }} diff --git a/.github/workflows/setup.yml b/.github/workflows/setup.yml index c3f7479c..b0fd9868 100644 --- a/.github/workflows/setup.yml +++ b/.github/workflows/setup.yml @@ -24,7 +24,7 @@ jobs: - ${{ github.workspace }}:/build steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup the minimal service in arch linux container run: .github/workflows/setup.sh minimal @@ -40,7 +40,7 @@ jobs: options: --privileged -w /build steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup the service in arch linux container run: .github/workflows/setup.sh diff --git a/.github/workflows/tests.sh b/.github/workflows/tests.sh deleted file mode 100755 index 55c4e1df..00000000 --- a/.github/workflows/tests.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -# Install dependencies and run test in container - -set -ex - -# install dependencies -pacman --noconfirm -Syyu base-devel python-tox - -# run test and check targets -tox diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e08af959..57781a0c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -26,7 +26,16 @@ jobs: - ${{ github.workspace }}:/build steps: - - uses: actions/checkout@v3 + - run: pacman --noconfirm -Syu base-devel git python-tox - - name: Run check and tests in arch linux container - run: .github/workflows/tests.sh + - run: git config --global --add safe.directory * + + - uses: actions/checkout@v4 + + - name: Run check and tests + run: tox + + - name: Generate documentation and check if there are untracked changes + run: | + tox -e docs + [ -z "$(git status --porcelain docs/*.rst)" ] diff --git a/docs/architecture.rst b/docs/architecture.rst index 97d25c7c..c7b51faa 100644 --- a/docs/architecture.rst +++ b/docs/architecture.rst @@ -413,10 +413,11 @@ Web application Web application requires the following python packages to be installed: * Core part requires ``aiohttp`` (application itself), ``aiohttp_jinja2`` and ``Jinja2`` (HTML generation from templates). -* Additional web features also require ``aiohttp-apispec`` (autogenerated documentation), ``aiohttp_cors`` (CORS support, required by documentation). +* Additional web features also require ``aiohttp-apispec`` (autogenerated documentation, optional), ``aiohttp_cors`` (CORS support, required by documentation). * In addition, authorization feature requires ``aiohttp_security``, ``aiohttp_session`` and ``cryptography``. * In addition to base authorization dependencies, OAuth2 also requires ``aioauth-client`` library. * In addition if you would like to disable authorization for local access (recommended way in order to run the application itself with reporting support), the ``requests-unixsocket2`` library is required. +* Application metrics will be automatically enabled after installing ``aiohttp-openmetrics`` package. Middlewares ^^^^^^^^^^^ diff --git a/docs/requirements.txt b/docs/requirements.txt index 9fc2635b..e5a585f1 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,36 +1,36 @@ # This file was autogenerated by uv via the following command: -# uv pip compile --group ../pyproject.toml:docs --extra s3 --extra validator --extra web --output-file ../docs/requirements.txt ../pyproject.toml +# uv pip compile --group pyproject.toml:docs --extra s3 --extra validator --extra web --output-file docs/requirements.txt pyproject.toml aiohappyeyeballs==2.6.1 # via aiohttp aiohttp==3.11.18 # via - # ahriman (../pyproject.toml) + # ahriman (pyproject.toml) # aiohttp-cors # aiohttp-jinja2 aiohttp-cors==0.8.1 - # via ahriman (../pyproject.toml) + # via ahriman (pyproject.toml) aiohttp-jinja2==1.6 - # via ahriman (../pyproject.toml) + # via ahriman (pyproject.toml) aiosignal==1.3.2 # via aiohttp alabaster==1.0.0 # via sphinx argparse-manpage==4.6 - # via ahriman (../pyproject.toml:docs) + # via ahriman (pyproject.toml:docs) attrs==25.3.0 # via aiohttp babel==2.17.0 # via sphinx bcrypt==4.3.0 - # via ahriman (../pyproject.toml) + # via ahriman (pyproject.toml) boto3==1.38.11 - # via ahriman (../pyproject.toml) + # via ahriman (pyproject.toml) botocore==1.38.11 # via # boto3 # s3transfer cerberus==1.3.7 - # via ahriman (../pyproject.toml) + # via ahriman (pyproject.toml) certifi==2025.4.26 # via requests charset-normalizer==3.4.2 @@ -51,7 +51,7 @@ idna==3.10 imagesize==1.4.1 # via sphinx inflection==0.5.1 - # via ahriman (../pyproject.toml) + # via ahriman (pyproject.toml) jinja2==3.1.6 # via # aiohttp-jinja2 @@ -73,37 +73,37 @@ propcache==0.3.1 # aiohttp # yarl pydeps==3.0.1 - # via ahriman (../pyproject.toml:docs) + # via ahriman (pyproject.toml:docs) pyelftools==0.32 - # via ahriman (../pyproject.toml) + # via ahriman (pyproject.toml) pygments==2.19.1 # via sphinx python-dateutil==2.9.0.post0 # via botocore requests==2.32.3 # via - # ahriman (../pyproject.toml) + # ahriman (pyproject.toml) # sphinx roman-numerals-py==3.1.0 # via sphinx s3transfer==0.12.0 # via boto3 shtab==1.7.2 - # via ahriman (../pyproject.toml:docs) + # via ahriman (pyproject.toml:docs) six==1.17.0 # via python-dateutil snowballstemmer==3.0.0.1 # via sphinx sphinx==8.2.3 # via - # ahriman (../pyproject.toml:docs) + # ahriman (pyproject.toml:docs) # sphinx-argparse # sphinx-rtd-theme # sphinxcontrib-jquery sphinx-argparse==0.5.2 - # via ahriman (../pyproject.toml:docs) + # via ahriman (pyproject.toml:docs) sphinx-rtd-theme==3.0.2 - # via ahriman (../pyproject.toml:docs) + # via ahriman (pyproject.toml:docs) sphinxcontrib-applehelp==2.0.0 # via sphinx sphinxcontrib-devhelp==2.0.0 diff --git a/src/ahriman/core/configuration/configuration.py b/src/ahriman/core/configuration/configuration.py index 257461f6..4b89ab1a 100644 --- a/src/ahriman/core/configuration/configuration.py +++ b/src/ahriman/core/configuration/configuration.py @@ -210,6 +210,17 @@ class Configuration(configparser.RawConfigParser): raise InitializeError("Configuration path and/or repository id are not set") return self.path, self.repository_id + def copy_from(self, configuration: Self) -> None: + """ + copy values from another instance overriding existing + + Args: + configuration(Self): configuration instance to merge from + """ + for section in configuration.sections(): + for key, value in configuration.items(section): + self.set_option(section, key, value) + def dump(self) -> dict[str, dict[str, str]]: """ dump configuration to dictionary @@ -220,6 +231,7 @@ class Configuration(configparser.RawConfigParser): return { section: dict(self.items(section)) for section in self.sections() + if self[section] } # pylint and mypy are too stupid to find these methods diff --git a/src/ahriman/web/routes.py b/src/ahriman/web/routes.py index f2faa601..b731a32c 100644 --- a/src/ahriman/web/routes.py +++ b/src/ahriman/web/routes.py @@ -72,8 +72,8 @@ def setup_routes(application: Application, configuration: Configuration) -> None application(Application): web application instance configuration(Configuration): configuration instance """ - application.router.add_static("/static", configuration.getpath("web", "static_path"), name="_static", - follow_symlinks=True) + application.router.add_static("/static", configuration.getpath("web", "static_path"), + name="_static", follow_symlinks=True) for route, view in _dynamic_routes(configuration): application.router.add_view(route, view, name=_identifier(route)) diff --git a/tests/ahriman/application/handlers/test_handler.py b/tests/ahriman/application/handlers/test_handler.py index 92af8dda..90258437 100644 --- a/tests/ahriman/application/handlers/test_handler.py +++ b/tests/ahriman/application/handlers/test_handler.py @@ -144,6 +144,7 @@ def test_repositories_extract(args: argparse.Namespace, configuration: Configura args.architecture = "arch" args.configuration = configuration.path args.repository = "repo" + mocker.patch("ahriman.core.configuration.Configuration.load", new=lambda self, _: self.copy_from(configuration)) known_architectures_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures") known_repositories_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_repositories") @@ -159,6 +160,7 @@ def test_repositories_extract_repository(args: argparse.Namespace, configuration """ args.architecture = "arch" args.configuration = configuration.path + mocker.patch("ahriman.core.configuration.Configuration.load", new=lambda self, _: self.copy_from(configuration)) known_architectures_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures") known_repositories_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_repositories", return_value={"repo"}) @@ -175,6 +177,7 @@ def test_repositories_extract_repository_legacy(args: argparse.Namespace, config """ args.architecture = "arch" args.configuration = configuration.path + mocker.patch("ahriman.core.configuration.Configuration.load", new=lambda self, _: self.copy_from(configuration)) known_architectures_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures") known_repositories_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_repositories", return_value=set()) @@ -191,6 +194,7 @@ def test_repositories_extract_architecture(args: argparse.Namespace, configurati """ args.configuration = configuration.path args.repository = "repo" + mocker.patch("ahriman.core.configuration.Configuration.load", new=lambda self, _: self.copy_from(configuration)) known_architectures_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures", return_value={"arch"}) known_repositories_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_repositories") @@ -207,6 +211,7 @@ def test_repositories_extract_empty(args: argparse.Namespace, configuration: Con """ args.command = "config" args.configuration = configuration.path + mocker.patch("ahriman.core.configuration.Configuration.load", new=lambda self, _: self.copy_from(configuration)) mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures", return_value=set()) mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_repositories", return_value=set()) @@ -221,6 +226,7 @@ def test_repositories_extract_systemd(args: argparse.Namespace, configuration: C """ args.configuration = configuration.path args.repository_id = "i686/some/repo/name" + mocker.patch("ahriman.core.configuration.Configuration.load", new=lambda self, _: self.copy_from(configuration)) known_architectures_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures") known_repositories_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_repositories") @@ -236,6 +242,7 @@ def test_repositories_extract_systemd_with_dash(args: argparse.Namespace, config """ args.configuration = configuration.path args.repository_id = "i686-some-repo-name" + mocker.patch("ahriman.core.configuration.Configuration.load", new=lambda self, _: self.copy_from(configuration)) known_architectures_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures") known_repositories_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_repositories") @@ -251,6 +258,7 @@ def test_repositories_extract_systemd_legacy(args: argparse.Namespace, configura """ args.configuration = configuration.path args.repository_id = "i686" + mocker.patch("ahriman.core.configuration.Configuration.load", new=lambda self, _: self.copy_from(configuration)) known_architectures_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures") known_repositories_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_repositories", return_value=set()) diff --git a/tests/ahriman/application/handlers/test_handler_copy.py b/tests/ahriman/application/handlers/test_handler_copy.py index 728171b3..c0796db9 100644 --- a/tests/ahriman/application/handlers/test_handler_copy.py +++ b/tests/ahriman/application/handlers/test_handler_copy.py @@ -6,6 +6,7 @@ from pytest_mock import MockerFixture from ahriman.application.application import Application from ahriman.application.handlers.copy import Copy from ahriman.core.configuration import Configuration +from ahriman.core.database import SQLite from ahriman.core.repository import Repository from ahriman.models.build_status import BuildStatusEnum from ahriman.models.package import Package @@ -30,11 +31,12 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace: def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository, - package_ahriman: Package, mocker: MockerFixture) -> None: + database: SQLite, package_ahriman: Package, mocker: MockerFixture) -> None: """ must run command """ args = _default_args(args) + mocker.patch("ahriman.core.database.SQLite.load", return_value=database) mocker.patch("ahriman.core.repository.Repository.load", return_value=repository) mocker.patch("ahriman.core.repository.Repository.packages", return_value=[package_ahriman]) application_mock = mocker.patch("ahriman.application.handlers.copy.Copy.copy_package") @@ -51,12 +53,13 @@ def test_run(args: argparse.Namespace, configuration: Configuration, repository: def test_run_remove(args: argparse.Namespace, configuration: Configuration, repository: Repository, - package_ahriman: Package, mocker: MockerFixture) -> None: + database: SQLite, package_ahriman: Package, mocker: MockerFixture) -> None: """ must run command and remove packages afterward """ args = _default_args(args) args.remove = True + mocker.patch("ahriman.core.database.SQLite.load", return_value=database) mocker.patch("ahriman.core.repository.Repository.load", return_value=repository) mocker.patch("ahriman.core.repository.Repository.packages", return_value=[package_ahriman]) mocker.patch("ahriman.application.handlers.copy.Copy.copy_package") @@ -69,12 +72,14 @@ def test_run_remove(args: argparse.Namespace, configuration: Configuration, repo def test_run_empty_exception(args: argparse.Namespace, configuration: Configuration, repository: Repository, - mocker: MockerFixture) -> None: + database: SQLite, mocker: MockerFixture) -> None: """ must raise ExitCode exception on empty result """ args = _default_args(args) args.exit_code = True + mocker.patch("ahriman.core.database.SQLite.load", return_value=database) + mocker.patch("ahriman.core.repository.Repository.load", return_value=repository) mocker.patch("ahriman.core.repository.Repository.packages", return_value=[]) mocker.patch("ahriman.application.application.Application.update") check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status") diff --git a/tests/ahriman/application/handlers/test_handler_setup.py b/tests/ahriman/application/handlers/test_handler_setup.py index c0418750..ec4aedd7 100644 --- a/tests/ahriman/application/handlers/test_handler_setup.py +++ b/tests/ahriman/application/handlers/test_handler_setup.py @@ -9,6 +9,7 @@ from urllib.parse import quote_plus as url_encode from ahriman.application.handlers.setup import Setup from ahriman.core.configuration import Configuration +from ahriman.core.database import SQLite from ahriman.core.exceptions import MissingArchitectureError from ahriman.core.repository import Repository from ahriman.models.repository_id import RepositoryId @@ -44,11 +45,12 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace: def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository, - repository_paths: RepositoryPaths, mocker: MockerFixture) -> None: + database: SQLite, repository_paths: RepositoryPaths, mocker: MockerFixture) -> None: """ must run command """ args = _default_args(args) + mocker.patch("ahriman.core.database.SQLite.load", return_value=database) mocker.patch("ahriman.core.repository.Repository.load", return_value=repository) ahriman_configuration_mock = mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_ahriman") devtools_configuration_mock = mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_devtools") @@ -88,12 +90,13 @@ def test_run_no_architecture_or_repository(configuration: Configuration) -> None def test_run_with_server(args: argparse.Namespace, configuration: Configuration, repository: Repository, - mocker: MockerFixture) -> None: + database: SQLite, mocker: MockerFixture) -> None: """ must run command with server specified """ args = _default_args(args) args.server = "server" + mocker.patch("ahriman.core.database.SQLite.load", return_value=database) mocker.patch("ahriman.core.repository.Repository.load", return_value=repository) mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_ahriman") mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_makepkg") diff --git a/tests/ahriman/application/handlers/test_handler_users.py b/tests/ahriman/application/handlers/test_handler_users.py index 8e60ce59..e964e13f 100644 --- a/tests/ahriman/application/handlers/test_handler_users.py +++ b/tests/ahriman/application/handlers/test_handler_users.py @@ -51,7 +51,8 @@ def test_run(args: argparse.Namespace, configuration: Configuration, database: S update_mock.assert_called_once_with(user) -def test_run_empty_salt(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None: +def test_run_empty_salt(args: argparse.Namespace, configuration: Configuration, database: SQLite, + mocker: MockerFixture) -> None: """ must process users with empty password salt """ @@ -59,6 +60,7 @@ def test_run_empty_salt(args: argparse.Namespace, configuration: Configuration, args = _default_args(args) user = User(username=args.username, password=args.password, access=args.role, packager_id=args.packager, key=args.key) + mocker.patch("ahriman.core.database.SQLite.load", return_value=database) mocker.patch("ahriman.models.user.User.hash_password", return_value=user) create_user_mock = mocker.patch("ahriman.application.handlers.users.Users.user_create", return_value=user) update_mock = mocker.patch("ahriman.core.database.SQLite.user_update") diff --git a/tests/ahriman/application/test_ahriman.py b/tests/ahriman/application/test_ahriman.py index e55a8c2d..99634595 100644 --- a/tests/ahriman/application/test_ahriman.py +++ b/tests/ahriman/application/test_ahriman.py @@ -1575,6 +1575,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc args.command = "" args.handler = Handler + mocker.patch("ahriman.core.configuration.Configuration.load", new=lambda self, _: self.copy_from(configuration)) mocker.patch("argparse.ArgumentParser.parse_args", return_value=args) assert ahriman.run() == 1 diff --git a/tests/ahriman/conftest.py b/tests/ahriman/conftest.py index d449d2a4..706c6354 100644 --- a/tests/ahriman/conftest.py +++ b/tests/ahriman/conftest.py @@ -249,19 +249,25 @@ def auth(configuration: Configuration) -> Auth: @pytest.fixture -def configuration(repository_id: RepositoryId, resource_path_root: Path) -> Configuration: +def configuration(repository_id: RepositoryId, tmp_path: Path, resource_path_root: Path) -> Configuration: """ configuration fixture Args: repository_id(RepositoryId): repository identifier fixture + tmp_path(Path): temporary path used by the fixture as root resource_path_root(Path): resource path root directory Returns: Configuration: configuration test instance """ path = resource_path_root / "core" / "ahriman.ini" - return Configuration.from_path(path, repository_id) + + instance = Configuration.from_path(path, repository_id) + instance.set_option("repository", "root", str(tmp_path)) + instance.set_option("settings", "database", str(tmp_path / "ahriman.db")) + + return instance @pytest.fixture @@ -275,9 +281,7 @@ def database(configuration: Configuration) -> SQLite: Returns: SQLite: database test instance """ - database = SQLite.load(configuration) - yield database - database.path.unlink() + return SQLite.load(configuration) @pytest.fixture diff --git a/tests/ahriman/core/configuration/test_configuration.py b/tests/ahriman/core/configuration/test_configuration.py index a9ac2e1d..ab70de2c 100644 --- a/tests/ahriman/core/configuration/test_configuration.py +++ b/tests/ahriman/core/configuration/test_configuration.py @@ -102,6 +102,15 @@ def test_check_loaded_architecture(configuration: Configuration) -> None: configuration.check_loaded() +def test_copy_from(configuration: Configuration) -> None: + """ + must copy values from another instance + """ + instance = Configuration() + instance.copy_from(configuration) + assert instance.dump() == configuration.dump() + + def test_dump(configuration: Configuration) -> None: """ dump must not be empty diff --git a/tests/ahriman/core/configuration/test_validator.py b/tests/ahriman/core/configuration/test_validator.py index 42b3795b..1abc4b75 100644 --- a/tests/ahriman/core/configuration/test_validator.py +++ b/tests/ahriman/core/configuration/test_validator.py @@ -62,8 +62,8 @@ def test_validate_is_ip_address(validator: Validator, mocker: MockerFixture) -> validator._validate_is_ip_address([], "field", "localhost") validator._validate_is_ip_address([], "field", "127.0.0.1") - validator._validate_is_ip_address([], "field", "::") - validator._validate_is_ip_address([], "field", "0.0.0.0") + validator._validate_is_ip_address([], "field", "::") # nosec + validator._validate_is_ip_address([], "field", "0.0.0.0") # nosec validator._validate_is_ip_address([], "field", "random string") diff --git a/tests/testresources/core/ahriman.ini b/tests/testresources/core/ahriman.ini index f3446e6d..be6edda3 100644 --- a/tests/testresources/core/ahriman.ini +++ b/tests/testresources/core/ahriman.ini @@ -1,7 +1,6 @@ [settings] include = . logging = logging.ini -database = ../../../ahriman-test.db [alpm] database = /var/lib/pacman @@ -31,7 +30,6 @@ triggers_known = ahriman.core.distributed.WorkerLoaderTrigger ahriman.core.distr [repository] name = aur -root = ../../../ [sign] target = diff --git a/tox.ini b/tox.ini index 568797f2..886807a4 100644 --- a/tox.ini +++ b/tox.ini @@ -6,8 +6,14 @@ labels = dependencies = -e .[journald,pacman,reports,s3,shell,stats,unixsocket,validator,web,web_api-docs,web_auth,web_oauth2,web_metrics] project_name = ahriman -[mypy] -flags = --implicit-reexport --strict --allow-untyped-decorators --allow-subclassing-any +[flags] +autopep8 = --max-line-length 120 -aa --in-place +bandit = --configfile .bandit.yml +manpage = --author "ahriman team" --author-email "" --description "ArcH linux ReposItory MANager" --manual-title "ArcH linux ReposItory MANager" --project-name ahriman --url https://github.com/arcan1s/ahriman +mypy = --implicit-reexport --strict --allow-untyped-decorators --allow-subclassing-any +pydeps = --no-config --cluster +pylint = --rcfile .pylint.toml +shtab = --prefix ahriman --prog ahriman ahriman.application.ahriman._parser [pytest] addopts = --cov=ahriman --cov-report=term-missing:skip-covered --no-cov-on-fail --cov-fail-under=100 --spec @@ -33,19 +39,17 @@ setenv = CFLAGS="-Wno-unterminated-string-initialization" MYPYPATH=src commands = - autopep8 --exit-code --max-line-length 120 -aa -i -j 0 -r "src/{[tox]project_name}" "tests/{[tox]project_name}" - pylint --rcfile=.pylint.toml "src/{[tox]project_name}" - bandit -c .bandit.yml -r "src/{[tox]project_name}" - bandit -c .bandit-test.yml -r "tests/{[tox]project_name}" - mypy {[mypy]flags} -p "{[tox]project_name}" --install-types --non-interactive + autopep8 {[flags]autopep8} --exit-code --jobs 0 --recursive "src/{[tox]project_name}" "tests/{[tox]project_name}" + pylint {[flags]pylint} "src/{[tox]project_name}" + bandit {[flags]bandit} --recursive "src/{[tox]project_name}" + bandit {[flags]bandit} --skip B101,B105,B106 --recursive "tests/{[tox]project_name}" + mypy {[flags]mypy} --install-types --non-interactive --package "{[tox]project_name}" [testenv:docs] description = Generate source files for documentation allowlist_externals = bash find - mv -changedir = src dependency_groups = docs depends = @@ -55,18 +59,18 @@ deps = uv pip_pre = true setenv = + PYTHONPATH=src SPHINX_APIDOC_OPTIONS=members,no-undoc-members,show-inheritance commands = - bash -c 'shtab --shell bash --prefix ahriman --prog ahriman ahriman.application.ahriman._parser > ../package/share/bash-completion/completions/_ahriman' - bash -c 'shtab --shell zsh --prefix ahriman --prog ahriman ahriman.application.ahriman._parser > ../package/share/zsh/site-functions/_ahriman' - argparse-manpage --module ahriman.application.ahriman --function _parser --author "ahriman team" --project-name ahriman --author-email "" --url https://github.com/arcan1s/ahriman --output ../package/share/man/man1/ahriman.1 - pydeps ahriman --no-output --show-dot --dot-output architecture.dot --no-config --cluster - mv architecture.dot ../docs/_static/architecture.dot + bash -c 'shtab {[flags]shtab} --shell bash > package/share/bash-completion/completions/_ahriman' + bash -c 'shtab {[flags]shtab} --shell zsh > package/share/zsh/site-functions/_ahriman' + argparse-manpage {[flags]manpage} --module ahriman.application.ahriman --function _parser --output ../package/share/man/man1/ahriman.1 + pydeps {[flags]pydeps} --no-output --show-dot --dot-output {tox_root}{/}docs/_static/architecture.dot src/ahriman # remove autogenerated modules rst files - find ../docs -type f -name "{[tox]project_name}*.rst" -delete - sphinx-apidoc -o ../docs . + find docs -type f -name "{[tox]project_name}*.rst" -delete + sphinx-apidoc --output-dir docs src # compile list of dependencies for rtd.io - uv pip compile --group ../pyproject.toml:docs --extra s3 --extra validator --extra web --output-file ../docs/requirements.txt --quiet ../pyproject.toml + uv pip compile --group pyproject.toml:docs --extra s3 --extra validator --extra web --output-file docs/requirements.txt --quiet pyproject.toml [testenv:html] description = Generate html documentation @@ -77,7 +81,7 @@ deps = pip_pre = true recreate = true commands = - sphinx-build -b html -a -j auto -W docs {envtmpdir}{/}html + sphinx-build --builder html --write-all --jobs auto --fail-on-warning docs {envtmpdir}{/}html [testenv:publish] description = Create and publish release to GitHub