mirror of
https://github.com/arcan1s/ahriman.git
synced 2025-06-27 14:22:10 +00:00
Compare commits
9 Commits
Author | SHA1 | Date | |
---|---|---|---|
e441d93a56 | |||
664b6369bb | |||
4f6bd29ff4 | |||
f6d9ea480a | |||
08f62842ba | |||
1912790ccc | |||
cf3c48ffeb | |||
6633766cc3 | |||
f73d1eb424 |
@ -1 +1 @@
|
||||
skips: ['B101', 'B105', 'B404']
|
||||
skips: ['B101', 'B105', 'B106', 'B404']
|
2
.github/workflows/setup.sh
vendored
2
.github/workflows/setup.sh
vendored
@ -8,7 +8,7 @@ echo -e '[arcanisrepo]\nServer = http://repo.arcanis.me/$arch\nSigLevel = Never'
|
||||
# refresh the image
|
||||
pacman --noconfirm -Syu
|
||||
# main dependencies
|
||||
pacman --noconfirm -Sy base-devel devtools git pyalpm python-aur python-passlib python-srcinfo sudo
|
||||
pacman --noconfirm -Sy base-devel devtools git pyalpm python-aur python-passlib python-setuptools python-srcinfo sudo
|
||||
# make dependencies
|
||||
pacman --noconfirm -Sy python-build python-installer python-wheel
|
||||
# optional dependencies
|
||||
|
2
.github/workflows/tests.sh
vendored
2
.github/workflows/tests.sh
vendored
@ -4,7 +4,7 @@
|
||||
set -ex
|
||||
|
||||
# install dependencies
|
||||
pacman --noconfirm -Syu base-devel python-pip python-tox
|
||||
pacman --noconfirm -Syu base-devel python-pip python-setuptools python-tox
|
||||
|
||||
# run test and check targets
|
||||
make check tests
|
||||
|
30
Dockerfile
30
Dockerfile
@ -1,4 +1,4 @@
|
||||
FROM archlinux:base-devel
|
||||
FROM archlinux:base
|
||||
|
||||
# image configuration
|
||||
ENV AHRIMAN_ARCHITECTURE="x86_64"
|
||||
@ -13,24 +13,22 @@ ENV AHRIMAN_REPOSITORY_ROOT="/var/lib/ahriman/ahriman"
|
||||
ENV AHRIMAN_USER="ahriman"
|
||||
|
||||
# install environment
|
||||
## install git which is required for AUR interaction and go for yay
|
||||
RUN pacman --noconfirm -Syu git go
|
||||
## install minimal required packages
|
||||
RUN pacman --noconfirm -Syu binutils fakeroot git make sudo
|
||||
## create build user
|
||||
RUN useradd -m -d /home/build -s /usr/bin/nologin build && \
|
||||
echo "build ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/build
|
||||
## install AUR helper
|
||||
RUN YAY_DIR="$(runuser -u build -- mktemp -d)" && \
|
||||
git clone https://aur.archlinux.org/yay.git "$YAY_DIR" && \
|
||||
cd "$YAY_DIR" && \
|
||||
runuser -u build -- makepkg --noconfirm --install && \
|
||||
cd - && rm -r "$YAY_DIR"
|
||||
COPY "docker/install-aur-package.sh" "/usr/local/bin/install-aur-package"
|
||||
## install package dependencies
|
||||
RUN runuser -u build -- yay --noconfirm -Sy devtools git pyalpm python-inflection python-passlib python-requests python-srcinfo && \
|
||||
runuser -u build -- yay --noconfirm -Sy python-build python-installer python-wheel && \
|
||||
runuser -u build -- yay --noconfirm -Sy breezy darcs mercurial python-aioauth-client python-aiohttp \
|
||||
python-aiohttp-debugtoolbar python-aiohttp-jinja2 python-aiohttp-security \
|
||||
python-aiohttp-session python-boto3 python-cryptography python-jinja \
|
||||
rsync subversion
|
||||
## darcs is not installed by reasons, because it requires a lot haskell packages which dramatically increase image size
|
||||
RUN pacman --noconfirm -Sy devtools git pyalpm python-inflection python-passlib python-requests python-setuptools python-srcinfo && \
|
||||
pacman --noconfirm -Sy python-build python-installer python-wheel && \
|
||||
pacman --noconfirm -Sy breezy mercurial python-aiohttp python-boto3 python-cryptography python-jinja rsync subversion && \
|
||||
runuser -u build -- install-aur-package python-aioauth-client python-aiohttp-jinja2 python-aiohttp-debugtoolbar \
|
||||
python-aiohttp-session python-aiohttp-security
|
||||
|
||||
# cleanup unused
|
||||
RUN find "/var/cache/pacman/pkg" -type f -delete
|
||||
|
||||
# install ahriman
|
||||
## copy tree
|
||||
@ -41,7 +39,7 @@ RUN cd "/home/build/ahriman" && \
|
||||
cp ./*-src.tar.xz "package/archlinux" && \
|
||||
cd "package/archlinux" && \
|
||||
runuser -u build -- makepkg --noconfirm --install --skipchecksums && \
|
||||
cd - && rm -r "/home/build/ahriman"
|
||||
cd / && rm -r "/home/build/ahriman"
|
||||
|
||||
VOLUME ["/var/lib/ahriman"]
|
||||
|
||||
|
@ -30,6 +30,6 @@ For installation details kindly refer to the [documentation](https://ahriman.rea
|
||||
|
||||
Every available option is described in the [documentation](https://ahriman.readthedocs.io/en/latest/configuration.html).
|
||||
|
||||
The application provides reasonable defaults which allow to use it out-of-box, though additional steps (like configuring build toolchain and sudoers) is recommended and can be easily achieved by following install instructions.
|
||||
The application provides reasonable defaults which allow to use it out-of-box; however additional steps (like configuring build toolchain and sudoers) are recommended and can be easily achieved by following install instructions.
|
||||
|
||||
## [FAQ](https://ahriman.readthedocs.io/en/latest/faq.html)
|
||||
|
@ -33,7 +33,7 @@ fi
|
||||
ahriman "${AHRIMAN_DEFAULT_ARGS[@]}" repo-setup "${AHRIMAN_SETUP_ARGS[@]}"
|
||||
|
||||
# refresh database
|
||||
runuser -u build -- yay --noconfirm -Syy &> /dev/null
|
||||
pacman -Syy &> /dev/null
|
||||
# create machine-id which is required by build tools
|
||||
systemd-machine-id-setup &> /dev/null
|
||||
|
||||
|
12
docker/install-aur-package.sh
Executable file
12
docker/install-aur-package.sh
Executable file
@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
for PACKAGE in "$@"; do
|
||||
BUILD_DIR="$(mktemp -d)"
|
||||
git clone https://aur.archlinux.org/"$PACKAGE".git "$BUILD_DIR"
|
||||
cd "$BUILD_DIR"
|
||||
makepkg --noconfirm --install --rmdeps --syncdeps
|
||||
cd /
|
||||
rm -r "$BUILD_DIR"
|
||||
done
|
File diff suppressed because it is too large
Load Diff
Before Width: | Height: | Size: 537 KiB After Width: | Height: | Size: 537 KiB |
@ -38,6 +38,14 @@ ahriman.core.exceptions module
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.core.lazy\_logging module
|
||||
---------------------------------
|
||||
|
||||
.. automodule:: ahriman.core.lazy_logging
|
||||
:members:
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.core.spawn module
|
||||
-------------------------
|
||||
|
||||
|
@ -92,6 +92,14 @@ ahriman.models.package\_source module
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.models.pkgbuild\_patch module
|
||||
-------------------------------------
|
||||
|
||||
.. automodule:: ahriman.models.pkgbuild_patch
|
||||
:members:
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.models.property module
|
||||
------------------------------
|
||||
|
||||
|
@ -138,6 +138,7 @@ Section name must be either ``telegram`` (plus optional architecture name, e.g.
|
||||
* ``link_path`` - prefix for HTML links, string, required.
|
||||
* ``template_path`` - path to Jinja2 template, string, required.
|
||||
* ``template_type`` - ``parse_mode`` to be passed to telegram API, one of ``MarkdownV2``, ``HTML``, ``Markdown``, string, optional, default ``HTML``.
|
||||
* ``timeout`` - HTTP request timeout in seconds, int, optional, default is ``30``.
|
||||
|
||||
``upload`` group
|
||||
----------------
|
||||
@ -167,6 +168,7 @@ This feature requires Github key creation (see below). Section name must be eith
|
||||
#. Generate new token. Required scope is ``public_repo`` (or ``repo`` for private repository support).
|
||||
|
||||
* ``repository`` - Github repository name, string, required. Repository must be created before any action and must have active branch (e.g. with readme).
|
||||
* ``timeout`` - HTTP request timeout in seconds, int, optional, default is ``30``.
|
||||
* ``username`` - Github authorization user, string, required. Basically the same as ``owner``.
|
||||
|
||||
``rsync`` type
|
||||
|
@ -240,7 +240,7 @@ The default action (in case if no arguments provided) is ``repo-update``. Basica
|
||||
|
||||
docker run -v /path/to/local/repo:/var/lib/ahriman -v /etc/ahriman.ini:/etc/ahriman.ini.d/10-overrides.ini arcan1s/ahriman:latest
|
||||
|
||||
By default, it runs ``repo-update``, but it can be overwritten to any other command you would like to, e.g.:
|
||||
The action can be specified during run, e.g.:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
# Maintainer: Evgeniy Alekseev
|
||||
|
||||
pkgname='ahriman'
|
||||
pkgver=2.1.0
|
||||
pkgver=2.2.2
|
||||
pkgrel=1
|
||||
pkgdesc="ArcH linux ReposItory MANager"
|
||||
arch=('any')
|
||||
url="https://github.com/arcan1s/ahriman"
|
||||
license=('GPL3')
|
||||
depends=('devtools' 'git' 'pyalpm' 'python-inflection' 'python-passlib' 'python-requests' 'python-srcinfo')
|
||||
depends=('devtools' 'git' 'pyalpm' 'python-inflection' 'python-passlib' 'python-requests' 'python-setuptools' 'python-srcinfo')
|
||||
makedepends=('python-build' 'python-installer' 'python-wheel')
|
||||
optdepends=('breezy: -bzr packages support'
|
||||
'darcs: -darcs packages support'
|
||||
|
@ -20,7 +20,7 @@ archbuild_flags =
|
||||
build_command = extra-x86_64-build
|
||||
ignore_packages =
|
||||
makechrootpkg_flags =
|
||||
makepkg_flags = --nocolor
|
||||
makepkg_flags = --nocolor --ignorearch
|
||||
triggers = ahriman.core.report.ReportTrigger ahriman.core.upload.UploadTrigger
|
||||
|
||||
[repository]
|
||||
|
1
setup.py
1
setup.py
@ -32,6 +32,7 @@ setup(
|
||||
"inflection",
|
||||
"passlib",
|
||||
"requests",
|
||||
"setuptools",
|
||||
"srcinfo",
|
||||
],
|
||||
setup_requires=[
|
||||
|
@ -129,7 +129,7 @@ class ApplicationPackages(ApplicationProperties):
|
||||
source(str): remote URL of the package archive
|
||||
"""
|
||||
dst = self.repository.paths.packages / Path(source).name # URL is path, is not it?
|
||||
response = requests.get(source, stream=True)
|
||||
response = requests.get(source, stream=True, timeout=None) # timeout=None to suppress pylint warns
|
||||
response.raise_for_status()
|
||||
|
||||
with dst.open("wb") as local_file:
|
||||
|
@ -149,7 +149,7 @@ class Users(Handler):
|
||||
Returns:
|
||||
User: built user descriptor
|
||||
"""
|
||||
user = User(args.username, args.password, args.role)
|
||||
if user.password is None:
|
||||
user.password = getpass.getpass()
|
||||
return user
|
||||
password = args.password
|
||||
if password is None:
|
||||
password = getpass.getpass()
|
||||
return User(username=args.username, password=password, access=args.role)
|
||||
|
@ -36,11 +36,13 @@ class AUR(Remote):
|
||||
DEFAULT_AUR_URL(str): (class attribute) default AUR url
|
||||
DEFAULT_RPC_URL(str): (class attribute) default AUR RPC url
|
||||
DEFAULT_RPC_VERSION(str): (class attribute) default AUR RPC version
|
||||
DEFAULT_TIMEOUT(int): (class attribute) HTTP request timeout in seconds
|
||||
"""
|
||||
|
||||
DEFAULT_AUR_URL = "https://aur.archlinux.org"
|
||||
DEFAULT_RPC_URL = f"{DEFAULT_AUR_URL}/rpc"
|
||||
DEFAULT_RPC_VERSION = "5"
|
||||
DEFAULT_TIMEOUT = 30
|
||||
|
||||
@staticmethod
|
||||
def parse_response(response: Dict[str, Any]) -> List[AURPackage]:
|
||||
@ -113,7 +115,7 @@ class AUR(Remote):
|
||||
query[key] = value
|
||||
|
||||
try:
|
||||
response = requests.get(self.DEFAULT_RPC_URL, params=query)
|
||||
response = requests.get(self.DEFAULT_RPC_URL, params=query, timeout=self.DEFAULT_TIMEOUT)
|
||||
response.raise_for_status()
|
||||
return self.parse_response(response.json())
|
||||
except requests.HTTPError as e:
|
||||
|
@ -36,11 +36,13 @@ class Official(Remote):
|
||||
DEFAULT_ARCHLINUX_URL(str): (class attribute) default archlinux url
|
||||
DEFAULT_SEARCH_REPOSITORIES(List[str]): (class attribute) default list of repositories to search
|
||||
DEFAULT_RPC_URL(str): (class attribute) default archlinux repositories RPC url
|
||||
DEFAULT_TIMEOUT(int): (class attribute) HTTP request timeout in seconds
|
||||
"""
|
||||
|
||||
DEFAULT_ARCHLINUX_URL = "https://archlinux.org"
|
||||
DEFAULT_SEARCH_REPOSITORIES = ["Core", "Extra", "Multilib", "Community"]
|
||||
DEFAULT_RPC_URL = "https://archlinux.org/packages/search/json"
|
||||
DEFAULT_TIMEOUT = 30
|
||||
|
||||
@staticmethod
|
||||
def parse_response(response: Dict[str, Any]) -> List[AURPackage]:
|
||||
@ -101,7 +103,10 @@ class Official(Remote):
|
||||
List[AURPackage]: response parsed to package list
|
||||
"""
|
||||
try:
|
||||
response = requests.get(self.DEFAULT_RPC_URL, params={by: args, "repo": self.DEFAULT_SEARCH_REPOSITORIES})
|
||||
response = requests.get(
|
||||
self.DEFAULT_RPC_URL,
|
||||
params={by: args, "repo": self.DEFAULT_SEARCH_REPOSITORIES},
|
||||
timeout=self.DEFAULT_TIMEOUT)
|
||||
response.raise_for_status()
|
||||
return self.parse_response(response.json())
|
||||
except requests.HTTPError as e:
|
||||
|
@ -25,6 +25,7 @@ from typing import List, Optional
|
||||
from ahriman.core.lazy_logging import LazyLogging
|
||||
from ahriman.core.util import check_output, walk
|
||||
from ahriman.models.package import Package
|
||||
from ahriman.models.pkgbuild_patch import PkgbuildPatch
|
||||
from ahriman.models.remote_source import RemoteSource
|
||||
from ahriman.models.repository_paths import RepositoryPaths
|
||||
|
||||
@ -42,6 +43,26 @@ class Sources(LazyLogging):
|
||||
|
||||
_check_output = check_output
|
||||
|
||||
@staticmethod
|
||||
def extend_architectures(sources_dir: Path, architecture: str) -> None:
|
||||
"""
|
||||
extend existing PKGBUILD with repository architecture
|
||||
|
||||
Args:
|
||||
sources_dir(Path): local path to directory with source files
|
||||
architecture(str): repository architecture
|
||||
"""
|
||||
pkgbuild_path = sources_dir / "PKGBUILD"
|
||||
if not pkgbuild_path.is_file():
|
||||
return
|
||||
|
||||
architectures = Package.supported_architectures(sources_dir)
|
||||
if "any" in architectures: # makepkg does not like when there is any other arch except for any
|
||||
return
|
||||
architectures.add(architecture)
|
||||
patch = PkgbuildPatch("arch", list(architectures))
|
||||
patch.write(pkgbuild_path)
|
||||
|
||||
@staticmethod
|
||||
def fetch(sources_dir: Path, remote: Optional[RemoteSource]) -> None:
|
||||
"""
|
||||
@ -128,10 +149,9 @@ class Sources(LazyLogging):
|
||||
shutil.copytree(cache_dir, sources_dir, dirs_exist_ok=True)
|
||||
instance.fetch(sources_dir, package.remote)
|
||||
|
||||
if patch is None:
|
||||
instance.logger.info("no patches found")
|
||||
return
|
||||
instance.patch_apply(sources_dir, patch)
|
||||
if patch is not None:
|
||||
instance.patch_apply(sources_dir, patch)
|
||||
instance.extend_architectures(sources_dir, paths.architecture)
|
||||
|
||||
@staticmethod
|
||||
def patch_create(sources_dir: Path, *pattern: str) -> str:
|
||||
|
@ -83,7 +83,7 @@ class Migrations(LazyLogging):
|
||||
module = import_module(f"{__name__}.{module_name}")
|
||||
steps: List[str] = getattr(module, "steps", [])
|
||||
self.logger.debug("found migration %s at index %s with steps count %s", module_name, index, len(steps))
|
||||
migrations.append(Migration(index, module_name, steps))
|
||||
migrations.append(Migration(index=index, name=module_name, steps=steps))
|
||||
|
||||
return migrations
|
||||
|
||||
@ -97,7 +97,7 @@ class Migrations(LazyLogging):
|
||||
migrations = self.migrations()
|
||||
current_version = self.user_version()
|
||||
expected_version = len(migrations)
|
||||
result = MigrationResult(current_version, expected_version)
|
||||
result = MigrationResult(old_version=current_version, new_version=expected_version)
|
||||
|
||||
if not result.is_outdated:
|
||||
self.logger.info("no migrations required")
|
||||
|
@ -58,7 +58,7 @@ class AuthOperations(Operations):
|
||||
|
||||
def run(connection: Connection) -> List[User]:
|
||||
return [
|
||||
User(cursor["username"], cursor["password"], UserAccess(cursor["access"]))
|
||||
User(username=cursor["username"], password=cursor["password"], access=UserAccess(cursor["access"]))
|
||||
for cursor in connection.execute(
|
||||
"""
|
||||
select * from users
|
||||
|
@ -154,7 +154,11 @@ class PackageOperations(Operations):
|
||||
Dict[str, Package]: map of the package base to its descriptor (without packages themselves)
|
||||
"""
|
||||
return {
|
||||
row["package_base"]: Package(row["package_base"], row["version"], RemoteSource.from_json(row), {})
|
||||
row["package_base"]: Package(
|
||||
base=row["package_base"],
|
||||
version=row["version"],
|
||||
remote=RemoteSource.from_json(row),
|
||||
packages={})
|
||||
for row in connection.execute("""select * from package_bases""")
|
||||
}
|
||||
|
||||
|
@ -18,11 +18,4 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from ahriman.core.report.report import Report
|
||||
from ahriman.core.report.jinja_template import JinjaTemplate
|
||||
|
||||
from ahriman.core.report.console import Console
|
||||
from ahriman.core.report.email import Email
|
||||
from ahriman.core.report.html import HTML
|
||||
from ahriman.core.report.telegram import Telegram
|
||||
|
||||
from ahriman.core.report.report_trigger import ReportTrigger
|
||||
|
@ -25,7 +25,8 @@ from email.mime.text import MIMEText
|
||||
from typing import Dict, Iterable
|
||||
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.report import JinjaTemplate, Report
|
||||
from ahriman.core.report import Report
|
||||
from ahriman.core.report.jinja_template import JinjaTemplate
|
||||
from ahriman.core.util import pretty_datetime
|
||||
from ahriman.models.package import Package
|
||||
from ahriman.models.result import Result
|
||||
|
@ -20,7 +20,8 @@
|
||||
from typing import Iterable
|
||||
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.report import JinjaTemplate, Report
|
||||
from ahriman.core.report import Report
|
||||
from ahriman.core.report.jinja_template import JinjaTemplate
|
||||
from ahriman.models.package import Package
|
||||
from ahriman.models.result import Result
|
||||
|
||||
|
@ -84,16 +84,16 @@ class Report(LazyLogging):
|
||||
section, provider_name = configuration.gettype(target, architecture)
|
||||
provider = ReportSettings.from_option(provider_name)
|
||||
if provider == ReportSettings.HTML:
|
||||
from ahriman.core.report import HTML
|
||||
from ahriman.core.report.html import HTML
|
||||
return HTML(architecture, configuration, section)
|
||||
if provider == ReportSettings.Email:
|
||||
from ahriman.core.report import Email
|
||||
from ahriman.core.report.email import Email
|
||||
return Email(architecture, configuration, section)
|
||||
if provider == ReportSettings.Console:
|
||||
from ahriman.core.report import Console
|
||||
from ahriman.core.report.console import Console
|
||||
return Console(architecture, configuration, section)
|
||||
if provider == ReportSettings.Telegram:
|
||||
from ahriman.core.report import Telegram
|
||||
from ahriman.core.report.telegram import Telegram
|
||||
return Telegram(architecture, configuration, section)
|
||||
return cls(architecture, configuration) # should never happen
|
||||
|
||||
|
@ -23,7 +23,8 @@ import requests
|
||||
from typing import Iterable
|
||||
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.report import JinjaTemplate, Report
|
||||
from ahriman.core.report import Report
|
||||
from ahriman.core.report.jinja_template import JinjaTemplate
|
||||
from ahriman.core.util import exception_response_text
|
||||
from ahriman.models.package import Package
|
||||
from ahriman.models.result import Result
|
||||
@ -40,6 +41,7 @@ class Telegram(Report, JinjaTemplate):
|
||||
chat_id(str): chat id to post message, either string with @ or integer
|
||||
template_path(Path): path to template for built packages
|
||||
template_type(str): template message type to be used in parse mode, one of MarkdownV2, HTML, Markdown
|
||||
timeout(int): HTTP request timeout in seconds
|
||||
"""
|
||||
|
||||
TELEGRAM_API_URL = "https://api.telegram.org"
|
||||
@ -61,6 +63,7 @@ class Telegram(Report, JinjaTemplate):
|
||||
self.chat_id = configuration.get(section, "chat_id")
|
||||
self.template_path = configuration.getpath(section, "template_path")
|
||||
self.template_type = configuration.get(section, "template_type", fallback="HTML")
|
||||
self.timeout = configuration.getint(section, "timeout", fallback=30)
|
||||
|
||||
def _send(self, text: str) -> None:
|
||||
"""
|
||||
@ -72,7 +75,8 @@ class Telegram(Report, JinjaTemplate):
|
||||
try:
|
||||
response = requests.post(
|
||||
f"{self.TELEGRAM_API_URL}/bot{self.api_key}/sendMessage",
|
||||
data={"chat_id": self.chat_id, "text": text, "parse_mode": self.template_type})
|
||||
data={"chat_id": self.chat_id, "text": text, "parse_mode": self.template_type},
|
||||
timeout=self.timeout)
|
||||
response.raise_for_status()
|
||||
except requests.HTTPError as e:
|
||||
self.logger.exception("could not perform request: %s", exception_response_text(e))
|
||||
|
@ -34,6 +34,7 @@ class GPG(LazyLogging):
|
||||
gnupg wrapper
|
||||
|
||||
Attributes:
|
||||
DEFAULT_TIMEOUT(int): (class attribute) HTTP request timeout in seconds
|
||||
architecture(str): repository architecture
|
||||
configuration(Configuration): configuration instance
|
||||
default_key(Optional[str]): default PGP key ID to use
|
||||
@ -41,6 +42,7 @@ class GPG(LazyLogging):
|
||||
"""
|
||||
|
||||
_check_output = check_output
|
||||
DEFAULT_TIMEOUT = 30
|
||||
|
||||
def __init__(self, architecture: str, configuration: Configuration) -> None:
|
||||
"""
|
||||
@ -120,7 +122,7 @@ class GPG(LazyLogging):
|
||||
"op": "get",
|
||||
"options": "mr",
|
||||
"search": key
|
||||
})
|
||||
}, timeout=self.DEFAULT_TIMEOUT)
|
||||
response.raise_for_status()
|
||||
except requests.exceptions.HTTPError as e:
|
||||
self.logger.exception("could not download key %s from %s: %s", key, server, exception_response_text(e))
|
||||
|
@ -80,7 +80,7 @@ class Client:
|
||||
Returns:
|
||||
InternalStatus: current internal (web) service status
|
||||
"""
|
||||
return InternalStatus(BuildStatus())
|
||||
return InternalStatus(status=BuildStatus())
|
||||
|
||||
def remove(self, base: str) -> None:
|
||||
"""
|
||||
|
@ -189,7 +189,7 @@ class WebClient(Client, LazyLogging):
|
||||
self.logger.exception("could not get web service status: %s", exception_response_text(e))
|
||||
except Exception:
|
||||
self.logger.exception("could not get web service status")
|
||||
return InternalStatus(BuildStatus())
|
||||
return InternalStatus(status=BuildStatus())
|
||||
|
||||
def remove(self, base: str) -> None:
|
||||
"""
|
||||
|
@ -18,10 +18,4 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from ahriman.core.upload.upload import Upload
|
||||
from ahriman.core.upload.http_upload import HttpUpload
|
||||
|
||||
from ahriman.core.upload.github import Github
|
||||
from ahriman.core.upload.rsync import Rsync
|
||||
from ahriman.core.upload.s3 import S3
|
||||
|
||||
from ahriman.core.upload.upload_trigger import UploadTrigger
|
||||
|
@ -24,7 +24,7 @@ from pathlib import Path
|
||||
from typing import Any, Dict, Iterable, Optional
|
||||
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.upload import HttpUpload
|
||||
from ahriman.core.upload.http_upload import HttpUpload
|
||||
from ahriman.core.util import walk
|
||||
from ahriman.models.package import Package
|
||||
|
||||
|
@ -34,6 +34,7 @@ class HttpUpload(Upload):
|
||||
|
||||
Attributes:
|
||||
auth(Tuple[str, str]): HTTP auth object
|
||||
timeout(int): HTTP request timeout in seconds
|
||||
"""
|
||||
|
||||
def __init__(self, architecture: str, configuration: Configuration, section: str) -> None:
|
||||
@ -49,6 +50,7 @@ class HttpUpload(Upload):
|
||||
password = configuration.get(section, "password")
|
||||
username = configuration.get(section, "username")
|
||||
self.auth = (password, username)
|
||||
self.timeout = configuration.getint(section, "timeout", fallback=30)
|
||||
|
||||
@staticmethod
|
||||
def calculate_hash(path: Path) -> str:
|
||||
@ -108,7 +110,7 @@ class HttpUpload(Upload):
|
||||
requests.Response: request response object
|
||||
"""
|
||||
try:
|
||||
response = requests.request(method, url, auth=self.auth, **kwargs)
|
||||
response = requests.request(method, url, auth=self.auth, timeout=self.timeout, **kwargs)
|
||||
response.raise_for_status()
|
||||
except requests.HTTPError as e:
|
||||
self.logger.exception("could not perform %s request to %s: %s", method, url, exception_response_text(e))
|
||||
|
@ -83,13 +83,13 @@ class Upload(LazyLogging):
|
||||
section, provider_name = configuration.gettype(target, architecture)
|
||||
provider = UploadSettings.from_option(provider_name)
|
||||
if provider == UploadSettings.Rsync:
|
||||
from ahriman.core.upload import Rsync
|
||||
from ahriman.core.upload.rsync import Rsync
|
||||
return Rsync(architecture, configuration, section)
|
||||
if provider == UploadSettings.S3:
|
||||
from ahriman.core.upload import S3
|
||||
from ahriman.core.upload.s3 import S3
|
||||
return S3(architecture, configuration, section)
|
||||
if provider == UploadSettings.Github:
|
||||
from ahriman.core.upload import Github
|
||||
from ahriman.core.upload.github import Github
|
||||
return Github(architecture, configuration, section)
|
||||
return cls(architecture, configuration) # should never happen
|
||||
|
||||
|
@ -29,7 +29,7 @@ from typing import Any, Callable, Dict, List, Optional, Type
|
||||
from ahriman.core.util import filter_json, full_version
|
||||
|
||||
|
||||
@dataclass
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class AURPackage:
|
||||
"""
|
||||
AUR package descriptor
|
||||
|
@ -47,7 +47,7 @@ class BuildStatusEnum(str, Enum):
|
||||
Success = "success"
|
||||
|
||||
|
||||
@dataclass
|
||||
@dataclass(frozen=True)
|
||||
class BuildStatus:
|
||||
"""
|
||||
build status holder
|
||||
@ -64,7 +64,7 @@ class BuildStatus:
|
||||
"""
|
||||
convert status to enum type
|
||||
"""
|
||||
self.status = BuildStatusEnum(self.status)
|
||||
object.__setattr__(self, "status", BuildStatusEnum(self.status))
|
||||
|
||||
@classmethod
|
||||
def from_json(cls: Type[BuildStatus], dump: Dict[str, Any]) -> BuildStatus:
|
||||
|
@ -27,7 +27,7 @@ from ahriman.models.build_status import BuildStatus
|
||||
from ahriman.models.package import Package
|
||||
|
||||
|
||||
@dataclass
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class Counters:
|
||||
"""
|
||||
package counters
|
||||
|
@ -26,7 +26,7 @@ from ahriman.models.build_status import BuildStatus
|
||||
from ahriman.models.counters import Counters
|
||||
|
||||
|
||||
@dataclass
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class InternalStatus:
|
||||
"""
|
||||
internal server status
|
||||
|
@ -21,7 +21,7 @@ from dataclasses import dataclass
|
||||
from typing import List
|
||||
|
||||
|
||||
@dataclass
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class Migration:
|
||||
"""
|
||||
migration implementation
|
||||
|
@ -22,7 +22,7 @@ from dataclasses import dataclass
|
||||
from ahriman.core.exceptions import MigrationError
|
||||
|
||||
|
||||
@dataclass
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class MigrationResult:
|
||||
"""
|
||||
migration result implementation model
|
||||
|
@ -38,7 +38,7 @@ from ahriman.models.remote_source import RemoteSource
|
||||
from ahriman.models.repository_paths import RepositoryPaths
|
||||
|
||||
|
||||
@dataclass
|
||||
@dataclass(kw_only=True)
|
||||
class Package(LazyLogging):
|
||||
"""
|
||||
package properties representation
|
||||
@ -147,7 +147,7 @@ class Package(LazyLogging):
|
||||
"""
|
||||
package = pacman.handle.load_pkg(str(path))
|
||||
description = PackageDescription.from_package(package, path)
|
||||
return cls(package.base, package.version, remote, {package.name: description})
|
||||
return cls(base=package.base, version=package.version, remote=remote, packages={package.name: description})
|
||||
|
||||
@classmethod
|
||||
def from_aur(cls: Type[Package], name: str, pacman: Pacman) -> Package:
|
||||
@ -163,7 +163,11 @@ class Package(LazyLogging):
|
||||
"""
|
||||
package = AUR.info(name, pacman=pacman)
|
||||
remote = RemoteSource.from_source(PackageSource.AUR, package.package_base, package.repository)
|
||||
return cls(package.package_base, package.version, remote, {package.name: PackageDescription()})
|
||||
return cls(
|
||||
base=package.package_base,
|
||||
version=package.version,
|
||||
remote=remote,
|
||||
packages={package.name: PackageDescription()})
|
||||
|
||||
@classmethod
|
||||
def from_build(cls: Type[Package], path: Path) -> Package:
|
||||
@ -186,7 +190,7 @@ class Package(LazyLogging):
|
||||
packages = {key: PackageDescription() for key in srcinfo["packages"]}
|
||||
version = full_version(srcinfo.get("epoch"), srcinfo["pkgver"], srcinfo["pkgrel"])
|
||||
|
||||
return cls(srcinfo["pkgbase"], version, None, packages)
|
||||
return cls(base=srcinfo["pkgbase"], version=version, remote=None, packages=packages)
|
||||
|
||||
@classmethod
|
||||
def from_json(cls: Type[Package], dump: Dict[str, Any]) -> Package:
|
||||
@ -204,11 +208,7 @@ class Package(LazyLogging):
|
||||
for key, value in dump.get("packages", {}).items()
|
||||
}
|
||||
remote = dump.get("remote", {})
|
||||
return cls(
|
||||
base=dump["base"],
|
||||
version=dump["version"],
|
||||
remote=RemoteSource.from_json(remote),
|
||||
packages=packages)
|
||||
return cls(base=dump["base"], version=dump["version"], remote=RemoteSource.from_json(remote), packages=packages)
|
||||
|
||||
@classmethod
|
||||
def from_official(cls: Type[Package], name: str, pacman: Pacman, use_syncdb: bool = True) -> Package:
|
||||
@ -225,7 +225,11 @@ class Package(LazyLogging):
|
||||
"""
|
||||
package = OfficialSyncdb.info(name, pacman=pacman) if use_syncdb else Official.info(name, pacman=pacman)
|
||||
remote = RemoteSource.from_source(PackageSource.Repository, package.package_base, package.repository)
|
||||
return cls(package.package_base, package.version, remote, {package.name: PackageDescription()})
|
||||
return cls(
|
||||
base=package.package_base,
|
||||
version=package.version,
|
||||
remote=remote,
|
||||
packages={package.name: PackageDescription()})
|
||||
|
||||
@staticmethod
|
||||
def dependencies(path: Path) -> Set[str]:
|
||||
@ -263,6 +267,26 @@ class Package(LazyLogging):
|
||||
packages = set(srcinfo["packages"].keys())
|
||||
return (depends | makedepends) - packages
|
||||
|
||||
@staticmethod
|
||||
def supported_architectures(path: Path) -> Set[str]:
|
||||
"""
|
||||
load supported architectures from package sources
|
||||
|
||||
Args:
|
||||
path(Path): path to package sources directory
|
||||
|
||||
Returns:
|
||||
Set[str]: list of package supported architectures
|
||||
|
||||
Raises:
|
||||
InvalidPackageInfo: if there are parsing errors
|
||||
"""
|
||||
srcinfo_source = Package._check_output("makepkg", "--printsrcinfo", exception=None, cwd=path)
|
||||
srcinfo, errors = parse_srcinfo(srcinfo_source)
|
||||
if errors:
|
||||
raise InvalidPackageInfo(errors)
|
||||
return set(srcinfo.get("arch", []))
|
||||
|
||||
def actual_version(self, paths: RepositoryPaths) -> str:
|
||||
"""
|
||||
additional method to handle VCS package versions
|
||||
|
@ -27,7 +27,7 @@ from typing import Any, Dict, List, Optional, Type
|
||||
from ahriman.core.util import filter_json
|
||||
|
||||
|
||||
@dataclass
|
||||
@dataclass(kw_only=True)
|
||||
class PackageDescription:
|
||||
"""
|
||||
package specific properties
|
||||
|
92
src/ahriman/models/pkgbuild_patch.py
Normal file
92
src/ahriman/models/pkgbuild_patch.py
Normal file
@ -0,0 +1,92 @@
|
||||
#
|
||||
# Copyright (c) 2021-2022 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import shlex
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from pathlib import Path
|
||||
from typing import List, Union
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class PkgbuildPatch:
|
||||
"""
|
||||
wrapper for patching PKBGUILDs
|
||||
|
||||
Attributes:
|
||||
key(str): name of the property in PKGBUILD, e.g. version, url etc
|
||||
value(Union[str, List[str]]): value of the stored PKGBUILD property. It must be either string or list of string
|
||||
values
|
||||
unsafe(bool): if set, value will be not quoted, might break PKGBUILD
|
||||
"""
|
||||
|
||||
key: str
|
||||
value: Union[str, List[str]]
|
||||
unsafe: bool = field(default=False, kw_only=True)
|
||||
|
||||
@property
|
||||
def is_function(self) -> bool:
|
||||
"""
|
||||
parse key and define whether it function or not
|
||||
|
||||
Returns:
|
||||
bool: True in case if key ends with parentheses and False otherwise
|
||||
"""
|
||||
return self.key.endswith("()")
|
||||
|
||||
def quote(self, value: str) -> str:
|
||||
"""
|
||||
quote value according to the unsafe flag
|
||||
|
||||
Args:
|
||||
value(str): value to be quoted
|
||||
|
||||
Returns:
|
||||
str: quoted string in case if unsafe is False and as is otherwise
|
||||
"""
|
||||
return value if self.unsafe else shlex.quote(value)
|
||||
|
||||
def serialize(self) -> str:
|
||||
"""
|
||||
serialize key-value pair into PKBGBUILD string. List values will be put inside parentheses. All string
|
||||
values (including the ones inside list values) will be put inside quotes, no shell variables expanding supported
|
||||
at the moment
|
||||
|
||||
Returns:
|
||||
str: serialized key-value pair, print-friendly
|
||||
"""
|
||||
if isinstance(self.value, list): # list like
|
||||
value = " ".join(map(self.quote, self.value))
|
||||
return f"""{self.key}=({value})"""
|
||||
# we suppose that function values are only supported in string-like values
|
||||
if self.is_function:
|
||||
return f"{self.key} {self.value}" # no quoting enabled here
|
||||
return f"""{self.key}={self.quote(self.value)}"""
|
||||
|
||||
def write(self, pkgbuild_path: Path) -> None:
|
||||
"""
|
||||
write serialized value into PKGBUILD by specified path
|
||||
|
||||
Args:
|
||||
pkgbuild_path(Path): path to PKGBUILD file
|
||||
"""
|
||||
with pkgbuild_path.open("a") as pkgbuild:
|
||||
pkgbuild.write("\n") # in case if file ends without new line we are appending it at the end
|
||||
pkgbuild.write(self.serialize())
|
||||
pkgbuild.write("\n") # append new line after the values
|
@ -17,11 +17,11 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from dataclasses import dataclass
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Any
|
||||
|
||||
|
||||
@dataclass
|
||||
@dataclass(frozen=True)
|
||||
class Property:
|
||||
"""
|
||||
holder of object properties descriptor
|
||||
@ -34,4 +34,4 @@ class Property:
|
||||
|
||||
name: str
|
||||
value: Any
|
||||
is_required: bool = False
|
||||
is_required: bool = field(default=False, kw_only=True)
|
||||
|
@ -27,7 +27,7 @@ from ahriman.core.util import filter_json
|
||||
from ahriman.models.package_source import PackageSource
|
||||
|
||||
|
||||
@dataclass
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class RemoteSource:
|
||||
"""
|
||||
remote package source properties
|
||||
@ -50,7 +50,7 @@ class RemoteSource:
|
||||
"""
|
||||
convert source to enum type
|
||||
"""
|
||||
self.source = PackageSource(self.source)
|
||||
object.__setattr__(self, "source", PackageSource(self.source))
|
||||
|
||||
@property
|
||||
def pkgbuild_dir(self) -> Path:
|
||||
|
@ -29,7 +29,7 @@ from typing import Set, Tuple, Type
|
||||
from ahriman.core.exceptions import InvalidPath
|
||||
|
||||
|
||||
@dataclass
|
||||
@dataclass(frozen=True)
|
||||
class RepositoryPaths:
|
||||
"""
|
||||
repository paths holder. For the most operations with paths you want to use this object
|
||||
|
@ -19,7 +19,7 @@
|
||||
#
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from dataclasses import dataclass, replace
|
||||
from typing import Optional, Type
|
||||
from passlib.pwd import genword as generate_password # type: ignore
|
||||
from passlib.handlers.sha2_crypt import sha512_crypt # type: ignore
|
||||
@ -27,7 +27,7 @@ from passlib.handlers.sha2_crypt import sha512_crypt # type: ignore
|
||||
from ahriman.models.user_access import UserAccess
|
||||
|
||||
|
||||
@dataclass
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class User:
|
||||
"""
|
||||
authorized web user model
|
||||
@ -82,7 +82,7 @@ class User:
|
||||
"""
|
||||
if username is None or password is None:
|
||||
return None
|
||||
return cls(username, password, access)
|
||||
return cls(username=username, password=password, access=access)
|
||||
|
||||
@staticmethod
|
||||
def generate_password(length: int) -> str:
|
||||
@ -130,7 +130,7 @@ class User:
|
||||
# when we do not store any password here
|
||||
return self
|
||||
password_hash: str = self._HASHER.hash(self.password + salt)
|
||||
return User(self.username, password_hash, self.access)
|
||||
return replace(self, password=password_hash)
|
||||
|
||||
def verify_access(self, required: UserAccess) -> bool:
|
||||
"""
|
||||
|
@ -25,7 +25,7 @@ from dataclasses import dataclass
|
||||
from typing import Optional, Type
|
||||
|
||||
|
||||
@dataclass
|
||||
@dataclass(frozen=True)
|
||||
class UserIdentity:
|
||||
"""
|
||||
user identity used inside web service
|
||||
|
@ -17,4 +17,4 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
__version__ = "2.1.0"
|
||||
__version__ = "2.2.2"
|
||||
|
@ -111,7 +111,7 @@ def test_add_remote(application_packages: ApplicationPackages, package_descripti
|
||||
|
||||
application_packages._add_remote(url)
|
||||
open_mock.assert_called_once_with("wb")
|
||||
request_mock.assert_called_once_with(url, stream=True)
|
||||
request_mock.assert_called_once_with(url, stream=True, timeout=None)
|
||||
response_mock.raise_for_status.assert_called_once_with()
|
||||
|
||||
|
||||
|
@ -38,7 +38,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, database: S
|
||||
must run command
|
||||
"""
|
||||
args = _default_args(args)
|
||||
user = User(args.username, args.password, args.role)
|
||||
user = User(username=args.username, password=args.password, access=args.role)
|
||||
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
|
||||
mocker.patch("ahriman.models.user.User.hash_password", return_value=user)
|
||||
get_auth_configuration_mock = mocker.patch("ahriman.application.handlers.Users.configuration_get")
|
||||
|
@ -426,7 +426,7 @@ def user() -> User:
|
||||
Returns:
|
||||
User: user descriptor instance
|
||||
"""
|
||||
return User("user", "pa55w0rd", UserAccess.Reporter)
|
||||
return User(username="user", password="pa55w0rd", access=UserAccess.Reporter)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
@ -78,7 +78,9 @@ def test_make_request(aur: AUR, aur_package_ahriman: AURPackage,
|
||||
|
||||
assert aur.make_request("info", "ahriman") == [aur_package_ahriman]
|
||||
request_mock.assert_called_once_with(
|
||||
"https://aur.archlinux.org/rpc", params={"v": "5", "type": "info", "arg": ["ahriman"]})
|
||||
"https://aur.archlinux.org/rpc",
|
||||
params={"v": "5", "type": "info", "arg": ["ahriman"]},
|
||||
timeout=aur.DEFAULT_TIMEOUT)
|
||||
|
||||
|
||||
def test_make_request_multi_arg(aur: AUR, aur_package_ahriman: AURPackage,
|
||||
@ -92,7 +94,9 @@ def test_make_request_multi_arg(aur: AUR, aur_package_ahriman: AURPackage,
|
||||
|
||||
assert aur.make_request("search", "ahriman", "is", "cool") == [aur_package_ahriman]
|
||||
request_mock.assert_called_once_with(
|
||||
"https://aur.archlinux.org/rpc", params={"v": "5", "type": "search", "arg[]": ["ahriman", "is", "cool"]})
|
||||
"https://aur.archlinux.org/rpc",
|
||||
params={"v": "5", "type": "search", "arg[]": ["ahriman", "is", "cool"]},
|
||||
timeout=aur.DEFAULT_TIMEOUT)
|
||||
|
||||
|
||||
def test_make_request_with_kwargs(aur: AUR, aur_package_ahriman: AURPackage,
|
||||
@ -106,7 +110,9 @@ def test_make_request_with_kwargs(aur: AUR, aur_package_ahriman: AURPackage,
|
||||
|
||||
assert aur.make_request("search", "ahriman", by="name") == [aur_package_ahriman]
|
||||
request_mock.assert_called_once_with(
|
||||
"https://aur.archlinux.org/rpc", params={"v": "5", "type": "search", "arg": ["ahriman"], "by": "name"})
|
||||
"https://aur.archlinux.org/rpc",
|
||||
params={"v": "5", "type": "search", "arg": ["ahriman"], "by": "name"},
|
||||
timeout=aur.DEFAULT_TIMEOUT)
|
||||
|
||||
|
||||
def test_make_request_failed(aur: AUR, mocker: MockerFixture) -> None:
|
||||
|
@ -84,8 +84,10 @@ def test_make_request(official: Official, aur_package_akonadi: AURPackage,
|
||||
request_mock = mocker.patch("requests.get", return_value=response_mock)
|
||||
|
||||
assert official.make_request("akonadi", by="q") == [aur_package_akonadi]
|
||||
request_mock.assert_called_once_with("https://archlinux.org/packages/search/json",
|
||||
params={"q": ("akonadi",), "repo": Official.DEFAULT_SEARCH_REPOSITORIES})
|
||||
request_mock.assert_called_once_with(
|
||||
"https://archlinux.org/packages/search/json",
|
||||
params={"q": ("akonadi",), "repo": Official.DEFAULT_SEARCH_REPOSITORIES},
|
||||
timeout=official.DEFAULT_TIMEOUT)
|
||||
|
||||
|
||||
def test_make_request_failed(official: Official, mocker: MockerFixture) -> None:
|
||||
|
@ -10,6 +10,42 @@ from ahriman.models.remote_source import RemoteSource
|
||||
from ahriman.models.repository_paths import RepositoryPaths
|
||||
|
||||
|
||||
def test_extend_architectures(mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must update available architecture list
|
||||
"""
|
||||
mocker.patch("pathlib.Path.is_file", return_value=True)
|
||||
archs_mock = mocker.patch("ahriman.models.package.Package.supported_architectures", return_value={"x86_64"})
|
||||
write_mock = mocker.patch("ahriman.models.pkgbuild_patch.PkgbuildPatch.write")
|
||||
|
||||
Sources.extend_architectures(Path("local"), "i686")
|
||||
archs_mock.assert_called_once_with(Path("local"))
|
||||
write_mock.assert_called_once_with(Path("local") / "PKGBUILD")
|
||||
|
||||
|
||||
def test_extend_architectures_any(mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must skip architecture patching in case if there is any architecture
|
||||
"""
|
||||
mocker.patch("pathlib.Path.is_file", return_value=True)
|
||||
mocker.patch("ahriman.models.package.Package.supported_architectures", return_value={"any"})
|
||||
write_mock = mocker.patch("ahriman.models.pkgbuild_patch.PkgbuildPatch.write")
|
||||
|
||||
Sources.extend_architectures(Path("local"), "i686")
|
||||
write_mock.assert_not_called()
|
||||
|
||||
|
||||
def test_extend_architectures_skip(mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must skip extending list of the architectures in case if no PKGBUILD file found
|
||||
"""
|
||||
mocker.patch("pathlib.Path.is_file", return_value=False)
|
||||
write_mock = mocker.patch("ahriman.models.pkgbuild_patch.PkgbuildPatch.write")
|
||||
|
||||
Sources.extend_architectures(Path("local"), "i686")
|
||||
write_mock.assert_not_called()
|
||||
|
||||
|
||||
def test_fetch_empty(remote_source: RemoteSource, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must do nothing in case if no branches available
|
||||
@ -134,10 +170,12 @@ def test_load(package_ahriman: Package, repository_paths: RepositoryPaths, mocke
|
||||
mocker.patch("pathlib.Path.is_dir", return_value=False)
|
||||
fetch_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.fetch")
|
||||
patch_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.patch_apply")
|
||||
architectures_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.extend_architectures")
|
||||
|
||||
Sources.load(Path("local"), package_ahriman, "patch", repository_paths)
|
||||
fetch_mock.assert_called_once_with(Path("local"), package_ahriman.remote)
|
||||
patch_mock.assert_called_once_with(Path("local"), "patch")
|
||||
architectures_mock.assert_called_once_with(Path("local"), repository_paths.architecture)
|
||||
|
||||
|
||||
def test_load_no_patch(package_ahriman: Package, repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
|
||||
|
@ -44,7 +44,7 @@ def test_run(migrations: Migrations, mocker: MockerFixture) -> None:
|
||||
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"])])
|
||||
return_value=[Migration(index=0, name="test", steps=["select 1"])])
|
||||
migrations.connection.cursor.return_value = cursor
|
||||
validate_mock = mocker.patch("ahriman.models.migration_result.MigrationResult.validate")
|
||||
migrate_data_mock = mocker.patch("ahriman.core.database.migrations.migrate_data")
|
||||
@ -58,7 +58,8 @@ def test_run(migrations: Migrations, mocker: MockerFixture) -> None:
|
||||
mock.call("commit"),
|
||||
])
|
||||
cursor.close.assert_called_once_with()
|
||||
migrate_data_mock.assert_called_once_with(MigrationResult(0, 1), migrations.connection, migrations.configuration)
|
||||
migrate_data_mock.assert_called_once_with(
|
||||
MigrationResult(old_version=0, new_version=1), migrations.connection, migrations.configuration)
|
||||
|
||||
|
||||
def test_run_migration_exception(migrations: Migrations, mocker: MockerFixture) -> None:
|
||||
@ -69,7 +70,7 @@ def test_run_migration_exception(migrations: Migrations, mocker: MockerFixture)
|
||||
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"])])
|
||||
return_value=[Migration(index=0, name="test", steps=["select 1"])])
|
||||
mocker.patch("ahriman.models.migration_result.MigrationResult.validate")
|
||||
migrations.connection.cursor.return_value = cursor
|
||||
|
||||
@ -90,7 +91,7 @@ def test_run_sql_exception(migrations: Migrations, mocker: MockerFixture) -> Non
|
||||
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"])])
|
||||
return_value=[Migration(index=0, name="test", steps=["select 1"])])
|
||||
mocker.patch("ahriman.models.migration_result.MigrationResult.validate")
|
||||
migrations.connection.cursor.return_value = cursor
|
||||
|
||||
|
@ -16,21 +16,21 @@ 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))
|
||||
database.user_update(User(username=user.password, password=user.username, access=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
|
||||
assert User(username=user.password, password=user.username, access=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.Full)
|
||||
third = User("3", "", UserAccess.Read)
|
||||
first = User(username="1", password="", access=UserAccess.Read)
|
||||
second = User(username="2", password="", access=UserAccess.Full)
|
||||
third = User(username="3", password="", access=UserAccess.Read)
|
||||
|
||||
database.user_update(first)
|
||||
database.user_update(second)
|
||||
@ -45,9 +45,9 @@ 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.Full)
|
||||
third = User("3", "", UserAccess.Read)
|
||||
first = User(username="1", password="", access=UserAccess.Read)
|
||||
second = User(username="2", password="", access=UserAccess.Full)
|
||||
third = User(username="3", password="", access=UserAccess.Read)
|
||||
|
||||
database.user_update(first)
|
||||
database.user_update(second)
|
||||
@ -63,9 +63,9 @@ 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.Full)
|
||||
third = User("3", "", UserAccess.Read)
|
||||
first = User(username="1", password="", access=UserAccess.Read)
|
||||
second = User(username="2", password="", access=UserAccess.Full)
|
||||
third = User(username="3", password="", access=UserAccess.Read)
|
||||
|
||||
database.user_update(first)
|
||||
database.user_update(second)
|
||||
@ -91,7 +91,6 @@ def test_user_update(database: SQLite, user: User) -> None:
|
||||
database.user_update(user)
|
||||
assert database.user_get(user.username) == user
|
||||
|
||||
new_user = user.hash_password("salt")
|
||||
new_user.access = UserAccess.Full
|
||||
new_user = User(username=user.username, password=user.hash_password("salt").password, access=UserAccess.Full)
|
||||
database.user_update(new_user)
|
||||
assert database.user_get(new_user.username) == new_user
|
||||
|
@ -2,7 +2,7 @@ from pytest_mock import MockerFixture
|
||||
from unittest import mock
|
||||
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.report import Console
|
||||
from ahriman.core.report.console import Console
|
||||
from ahriman.models.package import Package
|
||||
from ahriman.models.result import Result
|
||||
|
||||
|
@ -3,7 +3,7 @@ import pytest
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.report import Email
|
||||
from ahriman.core.report.email import Email
|
||||
from ahriman.models.package import Package
|
||||
from ahriman.models.result import Result
|
||||
|
||||
@ -90,7 +90,7 @@ def test_generate(configuration: Configuration, package_ahriman: Package, mocker
|
||||
"""
|
||||
must generate report
|
||||
"""
|
||||
send_mock = mocker.patch("ahriman.core.report.Email._send")
|
||||
send_mock = mocker.patch("ahriman.core.report.email.Email._send")
|
||||
|
||||
report = Email("x86_64", configuration, "email")
|
||||
report.generate([package_ahriman], Result())
|
||||
@ -102,7 +102,7 @@ def test_generate_with_built(configuration: Configuration, package_ahriman: Pack
|
||||
"""
|
||||
must generate report with built packages
|
||||
"""
|
||||
send_mock = mocker.patch("ahriman.core.report.Email._send")
|
||||
send_mock = mocker.patch("ahriman.core.report.email.Email._send")
|
||||
|
||||
report = Email("x86_64", configuration, "email")
|
||||
report.generate([package_ahriman], result)
|
||||
@ -117,7 +117,7 @@ def test_generate_with_built_and_full_path(
|
||||
"""
|
||||
must generate report with built packages and full packages lists
|
||||
"""
|
||||
send_mock = mocker.patch("ahriman.core.report.Email._send")
|
||||
send_mock = mocker.patch("ahriman.core.report.email.Email._send")
|
||||
|
||||
report = Email("x86_64", configuration, "email")
|
||||
report.full_template_path = report.template_path
|
||||
@ -130,7 +130,7 @@ def test_generate_no_empty(configuration: Configuration, package_ahriman: Packag
|
||||
must not generate report with built packages if no_empty_report is set
|
||||
"""
|
||||
configuration.set_option("email", "no_empty_report", "yes")
|
||||
send_mock = mocker.patch("ahriman.core.report.Email._send")
|
||||
send_mock = mocker.patch("ahriman.core.report.email.Email._send")
|
||||
|
||||
report = Email("x86_64", configuration, "email")
|
||||
report.generate([package_ahriman], Result())
|
||||
@ -143,7 +143,7 @@ def test_generate_no_empty_with_built(configuration: Configuration, package_ahri
|
||||
must generate report with built packages if no_empty_report is set
|
||||
"""
|
||||
configuration.set_option("email", "no_empty_report", "yes")
|
||||
send_mock = mocker.patch("ahriman.core.report.Email._send")
|
||||
send_mock = mocker.patch("ahriman.core.report.email.Email._send")
|
||||
|
||||
report = Email("x86_64", configuration, "email")
|
||||
report.generate([package_ahriman], result)
|
||||
|
@ -3,7 +3,7 @@ import pytest
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.report import HTML
|
||||
from ahriman.core.report.html import HTML
|
||||
from ahriman.models.package import Package
|
||||
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.report import JinjaTemplate
|
||||
from ahriman.core.report.jinja_template import JinjaTemplate
|
||||
from ahriman.models.package import Package
|
||||
from ahriman.models.result import Result
|
||||
|
||||
|
@ -13,7 +13,7 @@ def test_report_failure(configuration: Configuration, mocker: MockerFixture) ->
|
||||
"""
|
||||
must raise ReportFailed on errors
|
||||
"""
|
||||
mocker.patch("ahriman.core.report.HTML.generate", side_effect=Exception())
|
||||
mocker.patch("ahriman.core.report.html.HTML.generate", side_effect=Exception())
|
||||
with pytest.raises(ReportFailed):
|
||||
Report.load("x86_64", configuration, "html").run([], Result())
|
||||
|
||||
@ -32,7 +32,7 @@ def test_report_console(configuration: Configuration, result: Result, mocker: Mo
|
||||
"""
|
||||
must generate console report
|
||||
"""
|
||||
report_mock = mocker.patch("ahriman.core.report.Console.generate")
|
||||
report_mock = mocker.patch("ahriman.core.report.console.Console.generate")
|
||||
Report.load("x86_64", configuration, "console").run(result, [])
|
||||
report_mock.assert_called_once_with([], result)
|
||||
|
||||
@ -41,7 +41,7 @@ def test_report_email(configuration: Configuration, result: Result, mocker: Mock
|
||||
"""
|
||||
must generate email report
|
||||
"""
|
||||
report_mock = mocker.patch("ahriman.core.report.Email.generate")
|
||||
report_mock = mocker.patch("ahriman.core.report.email.Email.generate")
|
||||
Report.load("x86_64", configuration, "email").run(result, [])
|
||||
report_mock.assert_called_once_with([], result)
|
||||
|
||||
@ -50,7 +50,7 @@ def test_report_html(configuration: Configuration, result: Result, mocker: Mocke
|
||||
"""
|
||||
must generate html report
|
||||
"""
|
||||
report_mock = mocker.patch("ahriman.core.report.HTML.generate")
|
||||
report_mock = mocker.patch("ahriman.core.report.html.HTML.generate")
|
||||
Report.load("x86_64", configuration, "html").run(result, [])
|
||||
report_mock.assert_called_once_with([], result)
|
||||
|
||||
@ -59,6 +59,6 @@ def test_report_telegram(configuration: Configuration, result: Result, mocker: M
|
||||
"""
|
||||
must generate telegram report
|
||||
"""
|
||||
report_mock = mocker.patch("ahriman.core.report.Telegram.generate")
|
||||
report_mock = mocker.patch("ahriman.core.report.telegram.Telegram.generate")
|
||||
Report.load("x86_64", configuration, "telegram").run(result, [])
|
||||
report_mock.assert_called_once_with([], result)
|
||||
|
@ -5,7 +5,7 @@ from pytest_mock import MockerFixture
|
||||
from unittest import mock
|
||||
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.report import Telegram
|
||||
from ahriman.core.report.telegram import Telegram
|
||||
from ahriman.models.package import Package
|
||||
from ahriman.models.result import Result
|
||||
|
||||
@ -20,7 +20,8 @@ def test_send(configuration: Configuration, mocker: MockerFixture) -> None:
|
||||
report._send("a text")
|
||||
request_mock.assert_called_once_with(
|
||||
pytest.helpers.anyvar(str, strict=True),
|
||||
data={"chat_id": pytest.helpers.anyvar(str, strict=True), "text": "a text", "parse_mode": "HTML"})
|
||||
data={"chat_id": pytest.helpers.anyvar(str, strict=True), "text": "a text", "parse_mode": "HTML"},
|
||||
timeout=report.timeout)
|
||||
|
||||
|
||||
def test_send_failed(configuration: Configuration, mocker: MockerFixture) -> None:
|
||||
@ -50,7 +51,7 @@ def test_generate(configuration: Configuration, package_ahriman: Package, result
|
||||
"""
|
||||
must generate report
|
||||
"""
|
||||
send_mock = mocker.patch("ahriman.core.report.Telegram._send")
|
||||
send_mock = mocker.patch("ahriman.core.report.telegram.Telegram._send")
|
||||
|
||||
report = Telegram("x86_64", configuration, "telegram")
|
||||
report.generate([package_ahriman], result)
|
||||
@ -62,7 +63,7 @@ def test_generate_big_text_without_spaces(configuration: Configuration, package_
|
||||
"""
|
||||
must raise ValueError in case if there are no new lines in text
|
||||
"""
|
||||
mocker.patch("ahriman.core.report.JinjaTemplate.make_html", return_value="ab" * 4096)
|
||||
mocker.patch("ahriman.core.report.jinja_template.JinjaTemplate.make_html", return_value="ab" * 4096)
|
||||
report = Telegram("x86_64", configuration, "telegram")
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
@ -74,8 +75,8 @@ def test_generate_big_text(configuration: Configuration, package_ahriman: Packag
|
||||
"""
|
||||
must generate report with big text
|
||||
"""
|
||||
mocker.patch("ahriman.core.report.JinjaTemplate.make_html", return_value="a\n" * 4096)
|
||||
send_mock = mocker.patch("ahriman.core.report.Telegram._send")
|
||||
mocker.patch("ahriman.core.report.jinja_template.JinjaTemplate.make_html", return_value="a\n" * 4096)
|
||||
send_mock = mocker.patch("ahriman.core.report.telegram.Telegram._send")
|
||||
|
||||
report = Telegram("x86_64", configuration, "telegram")
|
||||
report.generate([package_ahriman], result)
|
||||
@ -89,8 +90,8 @@ def test_generate_very_big_text(configuration: Configuration, package_ahriman: P
|
||||
"""
|
||||
must generate report with very big text
|
||||
"""
|
||||
mocker.patch("ahriman.core.report.JinjaTemplate.make_html", return_value="ab\n" * 4096)
|
||||
send_mock = mocker.patch("ahriman.core.report.Telegram._send")
|
||||
mocker.patch("ahriman.core.report.jinja_template.JinjaTemplate.make_html", return_value="ab\n" * 4096)
|
||||
send_mock = mocker.patch("ahriman.core.report.telegram.Telegram._send")
|
||||
|
||||
report = Telegram("x86_64", configuration, "telegram")
|
||||
report.generate([package_ahriman], result)
|
||||
@ -105,7 +106,7 @@ def test_generate_no_empty(configuration: Configuration, package_ahriman: Packag
|
||||
"""
|
||||
must generate report
|
||||
"""
|
||||
send_mock = mocker.patch("ahriman.core.report.Telegram._send")
|
||||
send_mock = mocker.patch("ahriman.core.report.telegram.Telegram._send")
|
||||
|
||||
report = Telegram("x86_64", configuration, "telegram")
|
||||
report.generate([package_ahriman], Result())
|
||||
|
@ -83,7 +83,9 @@ def test_key_download(gpg: GPG, mocker: MockerFixture) -> None:
|
||||
requests_mock = mocker.patch("requests.get")
|
||||
gpg.key_download("pgp.mit.edu", "0xE989490C")
|
||||
requests_mock.assert_called_once_with(
|
||||
"http://pgp.mit.edu/pks/lookup", params={"op": "get", "options": "mr", "search": "0xE989490C"})
|
||||
"http://pgp.mit.edu/pks/lookup",
|
||||
params={"op": "get", "options": "mr", "search": "0xE989490C"},
|
||||
timeout=gpg.DEFAULT_TIMEOUT)
|
||||
|
||||
|
||||
def test_key_download_failure(gpg: GPG, mocker: MockerFixture) -> None:
|
||||
|
@ -51,9 +51,8 @@ def test_get_internal(client: Client) -> None:
|
||||
"""
|
||||
must return dummy status for web service
|
||||
"""
|
||||
expected = InternalStatus(BuildStatus())
|
||||
actual = client.get_internal()
|
||||
actual.status.timestamp = expected.status.timestamp
|
||||
expected = InternalStatus(status=BuildStatus(timestamp=actual.status.timestamp))
|
||||
|
||||
assert actual == expected
|
||||
|
||||
|
@ -164,8 +164,9 @@ def test_get_internal(web_client: WebClient, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must return web service status
|
||||
"""
|
||||
status = InternalStatus(status=BuildStatus(), architecture="x86_64")
|
||||
response_obj = Response()
|
||||
response_obj._content = json.dumps(InternalStatus(BuildStatus(), architecture="x86_64").view()).encode("utf8")
|
||||
response_obj._content = json.dumps(status.view()).encode("utf8")
|
||||
response_obj.status_code = 200
|
||||
|
||||
requests_mock = mocker.patch("requests.Session.get", return_value=response_obj)
|
||||
|
@ -5,7 +5,9 @@ from typing import Any, Dict, List
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.upload import Github, Rsync, S3
|
||||
from ahriman.core.upload.github import Github
|
||||
from ahriman.core.upload.rsync import Rsync
|
||||
from ahriman.core.upload.s3 import S3
|
||||
|
||||
|
||||
_s3_object = namedtuple("s3_object", ["key", "e_tag", "delete"])
|
||||
|
@ -6,14 +6,14 @@ from pytest_mock import MockerFixture
|
||||
from typing import Any, Dict
|
||||
from unittest import mock
|
||||
|
||||
from ahriman.core.upload import Github
|
||||
from ahriman.core.upload.github import Github
|
||||
|
||||
|
||||
def test_asset_remove(github: Github, github_release: Dict[str, Any], mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must remove asset from the release
|
||||
"""
|
||||
request_mock = mocker.patch("ahriman.core.upload.Github._request")
|
||||
request_mock = mocker.patch("ahriman.core.upload.github.Github._request")
|
||||
github.asset_remove(github_release, "asset_name")
|
||||
request_mock.assert_called_once_with("DELETE", "asset_url")
|
||||
|
||||
@ -22,7 +22,7 @@ def test_asset_remove_unknown(github: Github, github_release: Dict[str, Any], mo
|
||||
"""
|
||||
must not fail if no asset found
|
||||
"""
|
||||
request_mock = mocker.patch("ahriman.core.upload.Github._request")
|
||||
request_mock = mocker.patch("ahriman.core.upload.github.Github._request")
|
||||
github.asset_remove(github_release, "unknown_asset_name")
|
||||
request_mock.assert_not_called()
|
||||
|
||||
@ -32,8 +32,8 @@ def test_asset_upload(github: Github, github_release: Dict[str, Any], mocker: Mo
|
||||
must upload asset to the repository
|
||||
"""
|
||||
mocker.patch("pathlib.Path.open", return_value=b"")
|
||||
request_mock = mocker.patch("ahriman.core.upload.Github._request")
|
||||
remove_mock = mocker.patch("ahriman.core.upload.Github.asset_remove")
|
||||
request_mock = mocker.patch("ahriman.core.upload.github.Github._request")
|
||||
remove_mock = mocker.patch("ahriman.core.upload.github.Github.asset_remove")
|
||||
|
||||
github.asset_upload(github_release, Path("/root/new.tar.xz"))
|
||||
request_mock.assert_called_once_with("POST", "upload_url", params={"name": "new.tar.xz"},
|
||||
@ -46,8 +46,8 @@ def test_asset_upload_with_removal(github: Github, github_release: Dict[str, Any
|
||||
must remove existing file before upload
|
||||
"""
|
||||
mocker.patch("pathlib.Path.open", return_value=b"")
|
||||
mocker.patch("ahriman.core.upload.Github._request")
|
||||
remove_mock = mocker.patch("ahriman.core.upload.Github.asset_remove")
|
||||
mocker.patch("ahriman.core.upload.github.Github._request")
|
||||
remove_mock = mocker.patch("ahriman.core.upload.github.Github.asset_remove")
|
||||
|
||||
github.asset_upload(github_release, Path("asset_name"))
|
||||
github.asset_upload(github_release, Path("/root/asset_name"))
|
||||
@ -62,9 +62,9 @@ def test_asset_upload_empty_mimetype(github: Github, github_release: Dict[str, A
|
||||
must upload asset to the repository with empty mime type if cannot guess it
|
||||
"""
|
||||
mocker.patch("pathlib.Path.open", return_value=b"")
|
||||
mocker.patch("ahriman.core.upload.Github.asset_remove")
|
||||
mocker.patch("ahriman.core.upload.github.Github.asset_remove")
|
||||
mocker.patch("mimetypes.guess_type", return_value=(None, None))
|
||||
request_mock = mocker.patch("ahriman.core.upload.Github._request")
|
||||
request_mock = mocker.patch("ahriman.core.upload.github.Github._request")
|
||||
|
||||
github.asset_upload(github_release, Path("/root/new.tar.xz"))
|
||||
request_mock.assert_called_once_with("POST", "upload_url", params={"name": "new.tar.xz"},
|
||||
@ -84,7 +84,7 @@ def test_files_remove(github: Github, github_release: Dict[str, Any], mocker: Mo
|
||||
"""
|
||||
must remove files from the remote
|
||||
"""
|
||||
remove_mock = mocker.patch("ahriman.core.upload.Github.asset_remove")
|
||||
remove_mock = mocker.patch("ahriman.core.upload.github.Github.asset_remove")
|
||||
github.files_remove(github_release, {Path("a"): "a"}, {"a": "a", "b": "b"})
|
||||
remove_mock.assert_called_once_with(github_release, "b")
|
||||
|
||||
@ -93,7 +93,7 @@ def test_files_remove_empty(github: Github, github_release: Dict[str, Any], mock
|
||||
"""
|
||||
must remove nothing if nothing changed
|
||||
"""
|
||||
remove_mock = mocker.patch("ahriman.core.upload.Github.asset_remove")
|
||||
remove_mock = mocker.patch("ahriman.core.upload.github.Github.asset_remove")
|
||||
github.files_remove(github_release, {Path("a"): "a"}, {"a": "a"})
|
||||
remove_mock.assert_not_called()
|
||||
|
||||
@ -102,7 +102,7 @@ def test_files_upload(github: Github, github_release: Dict[str, Any], mocker: Mo
|
||||
"""
|
||||
must upload files to the remote
|
||||
"""
|
||||
upload_mock = mocker.patch("ahriman.core.upload.Github.asset_upload")
|
||||
upload_mock = mocker.patch("ahriman.core.upload.github.Github.asset_upload")
|
||||
github.files_upload(github_release, {Path("a"): "a", Path("b"): "c", Path("c"): "c"}, {"a": "a", "b": "b"})
|
||||
upload_mock.assert_has_calls([
|
||||
mock.call(github_release, Path("b")),
|
||||
@ -114,7 +114,7 @@ def test_files_upload_empty(github: Github, github_release: Dict[str, Any], mock
|
||||
"""
|
||||
must upload nothing if nothing changed
|
||||
"""
|
||||
upload_mock = mocker.patch("ahriman.core.upload.Github.asset_upload")
|
||||
upload_mock = mocker.patch("ahriman.core.upload.github.Github.asset_upload")
|
||||
github.files_upload(github_release, {Path("a"): "a"}, {"a": "a"})
|
||||
upload_mock.assert_not_called()
|
||||
|
||||
@ -123,7 +123,7 @@ def test_release_create(github: Github, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must create release
|
||||
"""
|
||||
request_mock = mocker.patch("ahriman.core.upload.Github._request")
|
||||
request_mock = mocker.patch("ahriman.core.upload.github.Github._request")
|
||||
github.release_create()
|
||||
request_mock.assert_called_once_with("POST", pytest.helpers.anyvar(str, True),
|
||||
json={"tag_name": github.architecture, "name": github.architecture})
|
||||
@ -133,7 +133,7 @@ def test_release_get(github: Github, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must get release
|
||||
"""
|
||||
request_mock = mocker.patch("ahriman.core.upload.Github._request")
|
||||
request_mock = mocker.patch("ahriman.core.upload.github.Github._request")
|
||||
github.release_get()
|
||||
request_mock.assert_called_once_with("GET", pytest.helpers.anyvar(str, True))
|
||||
|
||||
@ -144,7 +144,7 @@ def test_release_get_empty(github: Github, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
response = requests.Response()
|
||||
response.status_code = 404
|
||||
mocker.patch("ahriman.core.upload.Github._request", side_effect=requests.HTTPError(response=response))
|
||||
mocker.patch("ahriman.core.upload.github.Github._request", side_effect=requests.HTTPError(response=response))
|
||||
assert github.release_get() is None
|
||||
|
||||
|
||||
@ -152,7 +152,7 @@ def test_release_get_exception(github: Github, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must re-raise non HTTPError exception
|
||||
"""
|
||||
mocker.patch("ahriman.core.upload.Github._request", side_effect=Exception())
|
||||
mocker.patch("ahriman.core.upload.github.Github._request", side_effect=Exception())
|
||||
with pytest.raises(Exception):
|
||||
github.release_get()
|
||||
|
||||
@ -162,7 +162,7 @@ def test_release_get_exception_http_error(github: Github, mocker: MockerFixture)
|
||||
must re-raise HTTPError exception with code differs from 404
|
||||
"""
|
||||
exception = requests.HTTPError(response=requests.Response())
|
||||
mocker.patch("ahriman.core.upload.Github._request", side_effect=exception)
|
||||
mocker.patch("ahriman.core.upload.github.Github._request", side_effect=exception)
|
||||
with pytest.raises(requests.HTTPError):
|
||||
github.release_get()
|
||||
|
||||
@ -171,7 +171,7 @@ def test_release_update(github: Github, github_release: Dict[str, Any], mocker:
|
||||
"""
|
||||
must update release
|
||||
"""
|
||||
request_mock = mocker.patch("ahriman.core.upload.Github._request")
|
||||
request_mock = mocker.patch("ahriman.core.upload.github.Github._request")
|
||||
github.release_update(github_release, "body")
|
||||
request_mock.assert_called_once_with("POST", "release_url", json={"body": "body"})
|
||||
|
||||
@ -180,12 +180,12 @@ def test_release_sync(github: Github, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must run sync command
|
||||
"""
|
||||
release_get_mock = mocker.patch("ahriman.core.upload.Github.release_get", return_value={})
|
||||
get_hashes_mock = mocker.patch("ahriman.core.upload.Github.get_hashes", return_value={})
|
||||
get_local_files_mock = mocker.patch("ahriman.core.upload.Github.get_local_files", return_value={})
|
||||
files_upload_mock = mocker.patch("ahriman.core.upload.Github.files_upload")
|
||||
files_remove_mock = mocker.patch("ahriman.core.upload.Github.files_remove")
|
||||
release_update_mock = mocker.patch("ahriman.core.upload.Github.release_update")
|
||||
release_get_mock = mocker.patch("ahriman.core.upload.github.Github.release_get", return_value={})
|
||||
get_hashes_mock = mocker.patch("ahriman.core.upload.github.Github.get_hashes", return_value={})
|
||||
get_local_files_mock = mocker.patch("ahriman.core.upload.github.Github.get_local_files", return_value={})
|
||||
files_upload_mock = mocker.patch("ahriman.core.upload.github.Github.files_upload")
|
||||
files_remove_mock = mocker.patch("ahriman.core.upload.github.Github.files_remove")
|
||||
release_update_mock = mocker.patch("ahriman.core.upload.github.Github.release_update")
|
||||
|
||||
github.sync(Path("local"), [])
|
||||
release_get_mock.assert_called_once_with()
|
||||
@ -200,13 +200,13 @@ def test_release_sync_create_release(github: Github, mocker: MockerFixture) -> N
|
||||
"""
|
||||
must create release in case if it does not exist
|
||||
"""
|
||||
mocker.patch("ahriman.core.upload.Github.release_get", return_value=None)
|
||||
mocker.patch("ahriman.core.upload.Github.get_hashes")
|
||||
mocker.patch("ahriman.core.upload.Github.get_local_files")
|
||||
mocker.patch("ahriman.core.upload.Github.files_upload")
|
||||
mocker.patch("ahriman.core.upload.Github.files_remove")
|
||||
mocker.patch("ahriman.core.upload.Github.release_update")
|
||||
release_create_mock = mocker.patch("ahriman.core.upload.Github.release_create")
|
||||
mocker.patch("ahriman.core.upload.github.Github.release_get", return_value=None)
|
||||
mocker.patch("ahriman.core.upload.github.Github.get_hashes")
|
||||
mocker.patch("ahriman.core.upload.github.Github.get_local_files")
|
||||
mocker.patch("ahriman.core.upload.github.Github.files_upload")
|
||||
mocker.patch("ahriman.core.upload.github.Github.files_remove")
|
||||
mocker.patch("ahriman.core.upload.github.Github.release_update")
|
||||
release_create_mock = mocker.patch("ahriman.core.upload.github.Github.release_create")
|
||||
|
||||
github.sync(Path("local"), [])
|
||||
release_create_mock.assert_called_once_with()
|
||||
|
@ -5,7 +5,8 @@ from pathlib import Path
|
||||
from pytest_mock import MockerFixture
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from ahriman.core.upload import Github, HttpUpload
|
||||
from ahriman.core.upload.github import Github
|
||||
from ahriman.core.upload.http_upload import HttpUpload
|
||||
|
||||
|
||||
def test_calculate_hash_empty(resource_path_root: Path) -> None:
|
||||
@ -49,7 +50,7 @@ def test_request(github: Github, mocker: MockerFixture) -> None:
|
||||
request_mock = mocker.patch("requests.request", return_value=response_mock)
|
||||
|
||||
github._request("GET", "url", arg="arg")
|
||||
request_mock.assert_called_once_with("GET", "url", auth=github.auth, arg="arg")
|
||||
request_mock.assert_called_once_with("GET", "url", auth=github.auth, timeout=github.timeout, arg="arg")
|
||||
response_mock.raise_for_status.assert_called_once_with()
|
||||
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
from pathlib import Path
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from ahriman.core.upload import Rsync
|
||||
from ahriman.core.upload.rsync import Rsync
|
||||
|
||||
|
||||
def test_sync(rsync: Rsync, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must run sync command
|
||||
"""
|
||||
check_output_mock = mocker.patch("ahriman.core.upload.Rsync._check_output")
|
||||
check_output_mock = mocker.patch("ahriman.core.upload.rsync.Rsync._check_output")
|
||||
rsync.sync(Path("path"), [])
|
||||
check_output_mock.assert_called_once_with(*rsync.command, "path", rsync.remote, exception=None, logger=rsync.logger)
|
||||
|
@ -4,7 +4,7 @@ from typing import Any, List, Optional, Tuple
|
||||
from unittest import mock
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from ahriman.core.upload import S3
|
||||
from ahriman.core.upload.s3 import S3
|
||||
|
||||
|
||||
_chunk_size = 8 * 1024 * 1024
|
||||
@ -104,10 +104,10 @@ def test_sync(s3: S3, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must run sync command
|
||||
"""
|
||||
local_files_mock = mocker.patch("ahriman.core.upload.S3.get_local_files", return_value=["a"])
|
||||
remote_objects_mock = mocker.patch("ahriman.core.upload.S3.get_remote_objects", return_value=["b"])
|
||||
remove_files_mock = mocker.patch("ahriman.core.upload.S3.files_remove")
|
||||
upload_files_mock = mocker.patch("ahriman.core.upload.S3.files_upload")
|
||||
local_files_mock = mocker.patch("ahriman.core.upload.s3.S3.get_local_files", return_value=["a"])
|
||||
remote_objects_mock = mocker.patch("ahriman.core.upload.s3.S3.get_remote_objects", return_value=["b"])
|
||||
remove_files_mock = mocker.patch("ahriman.core.upload.s3.S3.files_remove")
|
||||
upload_files_mock = mocker.patch("ahriman.core.upload.s3.S3.files_upload")
|
||||
|
||||
s3.sync(Path("root"), [])
|
||||
remote_objects_mock.assert_called_once_with()
|
||||
|
@ -13,7 +13,7 @@ def test_upload_failure(configuration: Configuration, mocker: MockerFixture) ->
|
||||
"""
|
||||
must raise SyncFailed on errors
|
||||
"""
|
||||
mocker.patch("ahriman.core.upload.Rsync.sync", side_effect=Exception())
|
||||
mocker.patch("ahriman.core.upload.rsync.Rsync.sync", side_effect=Exception())
|
||||
with pytest.raises(SyncFailed):
|
||||
Upload.load("x86_64", configuration, "rsync").run(Path("path"), [])
|
||||
|
||||
@ -32,7 +32,7 @@ def test_upload_rsync(configuration: Configuration, mocker: MockerFixture) -> No
|
||||
"""
|
||||
must upload via rsync
|
||||
"""
|
||||
upload_mock = mocker.patch("ahriman.core.upload.Rsync.sync")
|
||||
upload_mock = mocker.patch("ahriman.core.upload.rsync.Rsync.sync")
|
||||
Upload.load("x86_64", configuration, "rsync").run(Path("path"), [])
|
||||
upload_mock.assert_called_once_with(Path("path"), [])
|
||||
|
||||
@ -41,7 +41,7 @@ def test_upload_s3(configuration: Configuration, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must upload via s3
|
||||
"""
|
||||
upload_mock = mocker.patch("ahriman.core.upload.S3.sync")
|
||||
upload_mock = mocker.patch("ahriman.core.upload.s3.S3.sync")
|
||||
Upload.load("x86_64", configuration, "customs3").run(Path("path"), [])
|
||||
upload_mock.assert_called_once_with(Path("path"), [])
|
||||
|
||||
@ -50,6 +50,6 @@ def test_upload_github(configuration: Configuration, mocker: MockerFixture) -> N
|
||||
"""
|
||||
must upload via github
|
||||
"""
|
||||
upload_mock = mocker.patch("ahriman.core.upload.Github.sync")
|
||||
upload_mock = mocker.patch("ahriman.core.upload.github.Github.sync")
|
||||
Upload.load("x86_64", configuration, "github").run(Path("path"), [])
|
||||
upload_mock.assert_called_once_with(Path("path"), [])
|
||||
|
@ -61,11 +61,11 @@ def test_from_pacman(pyalpm_package_ahriman: pyalpm.Package, aur_package_ahriman
|
||||
"""
|
||||
model = AURPackage.from_pacman(pyalpm_package_ahriman)
|
||||
# some fields are missing so we are changing them
|
||||
model.id = aur_package_ahriman.id
|
||||
model.package_base_id = aur_package_ahriman.package_base_id
|
||||
model.first_submitted = aur_package_ahriman.first_submitted
|
||||
model.url_path = aur_package_ahriman.url_path
|
||||
model.maintainer = aur_package_ahriman.maintainer
|
||||
object.__setattr__(model, "id", aur_package_ahriman.id)
|
||||
object.__setattr__(model, "package_base_id", aur_package_ahriman.package_base_id)
|
||||
object.__setattr__(model, "first_submitted", aur_package_ahriman.first_submitted)
|
||||
object.__setattr__(model, "url_path", aur_package_ahriman.url_path)
|
||||
object.__setattr__(model, "maintainer", aur_package_ahriman.maintainer)
|
||||
|
||||
assert model == aur_package_ahriman
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
import datetime
|
||||
import time
|
||||
|
||||
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
|
||||
@ -53,43 +52,3 @@ def test_build_status_eq(build_status_failed: BuildStatus) -> None:
|
||||
"""
|
||||
other = BuildStatus.from_json(build_status_failed.view())
|
||||
assert other == build_status_failed
|
||||
|
||||
|
||||
def test_build_status_eq_self(build_status_failed: BuildStatus) -> None:
|
||||
"""
|
||||
must be equal itself
|
||||
"""
|
||||
assert build_status_failed == build_status_failed
|
||||
|
||||
|
||||
def test_build_status_ne_by_status(build_status_failed: BuildStatus) -> None:
|
||||
"""
|
||||
must be not equal by status
|
||||
"""
|
||||
other = BuildStatus.from_json(build_status_failed.view())
|
||||
other.status = BuildStatusEnum.Success
|
||||
assert build_status_failed != other
|
||||
|
||||
|
||||
def test_build_status_ne_by_timestamp(build_status_failed: BuildStatus) -> None:
|
||||
"""
|
||||
must be not equal by timestamp
|
||||
"""
|
||||
other = BuildStatus.from_json(build_status_failed.view())
|
||||
other.timestamp = datetime.datetime.utcnow().timestamp()
|
||||
assert build_status_failed != other
|
||||
|
||||
|
||||
def test_build_status_ne_other(build_status_failed: BuildStatus) -> None:
|
||||
"""
|
||||
must be not equal to random object
|
||||
"""
|
||||
assert build_status_failed != object()
|
||||
|
||||
|
||||
def test_build_status_repr(build_status_failed: BuildStatus) -> None:
|
||||
"""
|
||||
must return string in __repr__ function
|
||||
"""
|
||||
assert build_status_failed.__repr__()
|
||||
assert isinstance(build_status_failed.__repr__(), str)
|
||||
|
@ -154,7 +154,7 @@ def test_from_official(package_ahriman: Package, aur_package_ahriman: AURPackage
|
||||
|
||||
def test_dependencies_failed(mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must raise exception if there are errors during srcinfo load
|
||||
must raise exception if there are errors during srcinfo load for dependencies
|
||||
"""
|
||||
mocker.patch("ahriman.models.package.Package._check_output", return_value="")
|
||||
mocker.patch("ahriman.models.package.parse_srcinfo", return_value=({"packages": {}}, ["an error"]))
|
||||
@ -183,6 +183,27 @@ def test_dependencies_with_version_and_overlap(mocker: MockerFixture, resource_p
|
||||
assert Package.dependencies(Path("path")) == {"glibc", "doxygen", "binutils", "git", "libmpc", "python", "zstd"}
|
||||
|
||||
|
||||
def test_supported_architectures(mocker: MockerFixture, resource_path_root: Path) -> None:
|
||||
"""
|
||||
must generate list of available architectures
|
||||
"""
|
||||
srcinfo = (resource_path_root / "models" / "package_yay_srcinfo").read_text()
|
||||
mocker.patch("ahriman.models.package.Package._check_output", return_value=srcinfo)
|
||||
assert Package.supported_architectures(Path("path")) == \
|
||||
{"i686", "pentium4", "x86_64", "arm", "armv7h", "armv6h", "aarch64"}
|
||||
|
||||
|
||||
def test_supported_architectures_failed(mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must raise exception if there are errors during srcinfo load for architectures
|
||||
"""
|
||||
mocker.patch("ahriman.models.package.Package._check_output", return_value="")
|
||||
mocker.patch("ahriman.models.package.parse_srcinfo", return_value=({"packages": {}}, ["an error"]))
|
||||
|
||||
with pytest.raises(InvalidPackageInfo):
|
||||
Package.supported_architectures(Path("path"))
|
||||
|
||||
|
||||
def test_actual_version(package_ahriman: Package, repository_paths: RepositoryPaths) -> None:
|
||||
"""
|
||||
must return same actual_version as version is
|
||||
|
61
tests/ahriman/models/test_pkgbuild_patch.py
Normal file
61
tests/ahriman/models/test_pkgbuild_patch.py
Normal file
@ -0,0 +1,61 @@
|
||||
from pathlib import Path
|
||||
from pytest_mock import MockerFixture
|
||||
from unittest.mock import MagicMock, call
|
||||
|
||||
from ahriman.models.pkgbuild_patch import PkgbuildPatch
|
||||
|
||||
|
||||
def test_is_function() -> None:
|
||||
"""
|
||||
must correctly define key as function
|
||||
"""
|
||||
assert not PkgbuildPatch("key", "value").is_function
|
||||
assert PkgbuildPatch("key()", "value").is_function
|
||||
|
||||
|
||||
def test_quote() -> None:
|
||||
"""
|
||||
must quote strings if unsafe flag is not set
|
||||
"""
|
||||
assert PkgbuildPatch("key", "value").quote("value") == """value"""
|
||||
assert PkgbuildPatch("key", "va'lue").quote("va'lue") == """'va'"'"'lue'"""
|
||||
assert PkgbuildPatch("key", "va'lue", unsafe=True).quote("va'lue") == """va'lue"""
|
||||
|
||||
|
||||
def test_serialize() -> None:
|
||||
"""
|
||||
must correctly serialize string values
|
||||
"""
|
||||
assert PkgbuildPatch("key", "value").serialize() == "key=value"
|
||||
assert PkgbuildPatch("key", "42").serialize() == "key=42"
|
||||
assert PkgbuildPatch("key", "4'2").serialize() == """key='4'"'"'2'"""
|
||||
assert PkgbuildPatch("key", "4'2", unsafe=True).serialize() == "key=4'2"
|
||||
|
||||
|
||||
def test_serialize_function() -> None:
|
||||
"""
|
||||
must correctly serialize function values
|
||||
"""
|
||||
assert PkgbuildPatch("key()", "{ value }", unsafe=True).serialize() == "key() { value }"
|
||||
|
||||
|
||||
def test_serialize_list() -> None:
|
||||
"""
|
||||
must correctly serialize list values
|
||||
"""
|
||||
assert PkgbuildPatch("arch", ["i686", "x86_64"]).serialize() == """arch=(i686 x86_64)"""
|
||||
assert PkgbuildPatch("key", ["val'ue", "val\"ue2"]).serialize() == """key=('val'"'"'ue' 'val"ue2')"""
|
||||
assert PkgbuildPatch("key", ["val'ue", "val\"ue2"], unsafe=True).serialize() == """key=(val'ue val"ue2)"""
|
||||
|
||||
|
||||
def test_write(mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must write serialized value to the file
|
||||
"""
|
||||
file_mock = MagicMock()
|
||||
open_mock = mocker.patch("pathlib.Path.open")
|
||||
open_mock.return_value.__enter__.return_value = file_mock
|
||||
|
||||
PkgbuildPatch("key", "value").write(Path("PKGBUILD"))
|
||||
open_mock.assert_called_once_with("a")
|
||||
file_mock.write.assert_has_calls([call("\n"), call("""key=value"""), call("\n")])
|
@ -69,7 +69,7 @@ def test_chown(repository_paths: RepositoryPaths, mocker: MockerFixture) -> None
|
||||
"""
|
||||
must correctly set owner for the directory
|
||||
"""
|
||||
repository_paths.owner = _get_owner(repository_paths.root, same=False)
|
||||
object.__setattr__(repository_paths, "owner", _get_owner(repository_paths.root, same=False))
|
||||
mocker.patch.object(RepositoryPaths, "root_owner", (42, 42))
|
||||
chown_mock = mocker.patch("os.chown")
|
||||
|
||||
@ -82,7 +82,7 @@ def test_chown_parent(repository_paths: RepositoryPaths, mocker: MockerFixture)
|
||||
"""
|
||||
must correctly set owner for the directory including parents
|
||||
"""
|
||||
repository_paths.owner = _get_owner(repository_paths.root, same=False)
|
||||
object.__setattr__(repository_paths, "owner", _get_owner(repository_paths.root, same=False))
|
||||
mocker.patch.object(RepositoryPaths, "root_owner", (42, 42))
|
||||
chown_mock = mocker.patch("os.chown")
|
||||
|
||||
@ -98,7 +98,7 @@ def test_chown_skip(repository_paths: RepositoryPaths, mocker: MockerFixture) ->
|
||||
"""
|
||||
must skip ownership set in case if it is same as root
|
||||
"""
|
||||
repository_paths.owner = _get_owner(repository_paths.root, same=True)
|
||||
object.__setattr__(repository_paths, "owner", _get_owner(repository_paths.root, same=True))
|
||||
mocker.patch.object(RepositoryPaths, "root_owner", (42, 42))
|
||||
chown_mock = mocker.patch("os.chown")
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
from dataclasses import replace
|
||||
|
||||
from ahriman.models.user import User
|
||||
from ahriman.models.user_access import UserAccess
|
||||
|
||||
@ -6,10 +8,10 @@ def test_from_option(user: User) -> None:
|
||||
"""
|
||||
must generate user from options
|
||||
"""
|
||||
user.access = UserAccess.Read
|
||||
user = replace(user, access=UserAccess.Read)
|
||||
assert User.from_option(user.username, user.password) == user
|
||||
# default is read access
|
||||
user.access = UserAccess.Full
|
||||
user = replace(user, access=UserAccess.Full)
|
||||
assert User.from_option(user.username, user.password) != user
|
||||
assert User.from_option(user.username, user.password, user.access) == user
|
||||
|
||||
@ -40,7 +42,7 @@ def test_check_credentials_empty_hash(user: User) -> None:
|
||||
"""
|
||||
current_password = user.password
|
||||
assert not user.check_credentials(current_password, "salt")
|
||||
user.password = ""
|
||||
user = replace(user, password="")
|
||||
assert not user.check_credentials(current_password, "salt")
|
||||
|
||||
|
||||
@ -48,9 +50,9 @@ def test_hash_password_empty_hash(user: User) -> None:
|
||||
"""
|
||||
must return empty string after hash in case if password not set
|
||||
"""
|
||||
user.password = ""
|
||||
user = replace(user, password="")
|
||||
assert user.hash_password("salt") == user
|
||||
user.password = None
|
||||
user = replace(user, password=None)
|
||||
assert user.hash_password("salt") == user
|
||||
|
||||
|
||||
@ -71,7 +73,7 @@ def test_verify_access_read(user: User) -> None:
|
||||
"""
|
||||
user with read access must be able to only request read
|
||||
"""
|
||||
user.access = UserAccess.Read
|
||||
user = replace(user, access=UserAccess.Read)
|
||||
assert user.verify_access(UserAccess.Read)
|
||||
assert not user.verify_access(UserAccess.Full)
|
||||
|
||||
@ -80,7 +82,7 @@ def test_verify_access_write(user: User) -> None:
|
||||
"""
|
||||
user with write access must be able to do anything
|
||||
"""
|
||||
user.access = UserAccess.Full
|
||||
user = replace(user, access=UserAccess.Full)
|
||||
assert user.verify_access(UserAccess.Read)
|
||||
assert user.verify_access(UserAccess.Full)
|
||||
|
||||
|
@ -13,7 +13,7 @@ def test_from_identity_expired(user_identity: UserIdentity) -> None:
|
||||
"""
|
||||
must construct None from expired identity
|
||||
"""
|
||||
user_identity.expire_at -= 60
|
||||
user_identity = UserIdentity(username=user_identity.username, expire_at=user_identity.expire_at - 60)
|
||||
assert UserIdentity.from_identity(f"{user_identity.username} {user_identity.expire_at}") is None
|
||||
|
||||
|
||||
@ -53,7 +53,7 @@ def test_is_expired(user_identity: UserIdentity) -> None:
|
||||
"""
|
||||
assert not user_identity.is_expired()
|
||||
|
||||
user_identity.expire_at -= 60
|
||||
user_identity = UserIdentity(username=user_identity.username, expire_at=user_identity.expire_at - 60)
|
||||
assert user_identity.is_expired()
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user