diff --git a/package/archlinux/PKGBUILD b/package/archlinux/PKGBUILD
index fbebac2a..ac5ac857 100644
--- a/package/archlinux/PKGBUILD
+++ b/package/archlinux/PKGBUILD
@@ -7,7 +7,7 @@ pkgdesc="ArcH Linux ReposItory MANager"
 arch=('any')
 url="https://github.com/arcan1s/ahriman"
 license=('GPL3')
-depends=('devtools' 'git' 'pyalpm' 'python-aur' 'python-passlib' 'python-srcinfo')
+depends=('devtools' 'git' 'pyalpm' 'python-inflection' 'python-passlib' 'python-srcinfo')
 makedepends=('python-pip')
 optdepends=('breezy: -bzr packages support'
             'darcs: -darcs packages support'
diff --git a/setup.py b/setup.py
index 3c81a41b..137cc605 100644
--- a/setup.py
+++ b/setup.py
@@ -29,7 +29,7 @@ setup(
     dependency_links=[
     ],
     install_requires=[
-        "aur",
+        "inflection",
         "passlib",
         "pyalpm",
         "requests",
diff --git a/src/ahriman/application/formatters/aur_printer.py b/src/ahriman/application/formatters/aur_printer.py
index dddc077c..0ec2ddc6 100644
--- a/src/ahriman/application/formatters/aur_printer.py
+++ b/src/ahriman/application/formatters/aur_printer.py
@@ -17,12 +17,11 @@
 # You should have received a copy of the GNU General Public License
 # along with this program. If not, see .
 #
-import aur  # type: ignore
-
 from typing import List, Optional
 
 from ahriman.application.formatters.printer import Printer
 from ahriman.core.util import pretty_datetime
+from ahriman.models.aur_package import AURPackage
 from ahriman.models.property import Property
 
 
@@ -31,7 +30,7 @@ class AurPrinter(Printer):
     print content of the AUR package
     """
 
-    def __init__(self, package: aur.Package) -> None:
+    def __init__(self, package: AURPackage) -> None:
         """
         default constructor
         :param package: AUR package description
@@ -46,12 +45,12 @@ class AurPrinter(Printer):
         return [
             Property("Package base", self.content.package_base),
             Property("Description", self.content.description, is_required=True),
-            Property("Upstream URL", self.content.url),
-            Property("Licenses", self.content.license),  # it should be actually a list
-            Property("Maintainer", self.content.maintainer or ""),  # I think it is optional
+            Property("Upstream URL", self.content.url or ""),
+            Property("Licenses", ",".join(self.content.license)),
+            Property("Maintainer", self.content.maintainer or ""),
             Property("First submitted", pretty_datetime(self.content.first_submitted)),
             Property("Last updated", pretty_datetime(self.content.last_modified)),
-            # more fields coming https://github.com/cdown/aur/pull/29
+            Property("Keywords", ",".join(self.content.keywords)),
         ]
 
     def title(self) -> Optional[str]:
diff --git a/src/ahriman/application/handlers/search.py b/src/ahriman/application/handlers/search.py
index 96b1c3f4..ee99d2d1 100644
--- a/src/ahriman/application/handlers/search.py
+++ b/src/ahriman/application/handlers/search.py
@@ -18,24 +18,27 @@
 # along with this program. If not, see .
 #
 import argparse
-import aur  # type: ignore
 
+from dataclasses import fields
 from typing import Callable, Iterable, List, Tuple, Type
 
 from ahriman.application.formatters.aur_printer import AurPrinter
 from ahriman.application.handlers.handler import Handler
+from ahriman.core.alpm.aur import AUR
 from ahriman.core.configuration import Configuration
 from ahriman.core.exceptions import InvalidOption
-from ahriman.core.util import aur_search
+from ahriman.models.aur_package import AURPackage
 
 
 class Search(Handler):
     """
     packages search handler
+    :cvar SORT_FIELDS: allowed fields to sort the package list
     """
 
     ALLOW_AUTO_ARCHITECTURE_RUN = False  # it should be called only as "no-architecture"
-    SORT_FIELDS = set(aur.Package._fields)  # later we will have to remove some fields from here (lists)
+    # later we will have to remove some fields from here (lists)
+    SORT_FIELDS = {pair.name for pair in fields(AURPackage)}
 
     @classmethod
     def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
@@ -47,12 +50,12 @@ class Search(Handler):
         :param configuration: configuration instance
         :param no_report: force disable reporting
         """
-        packages_list = aur_search(*args.search)
+        packages_list = AUR.multisearch(*args.search)
         for package in Search.sort(packages_list, args.sort_by):
             AurPrinter(package).print(args.info)
 
     @staticmethod
-    def sort(packages: Iterable[aur.Package], sort_by: str) -> List[aur.Package]:
+    def sort(packages: Iterable[AURPackage], sort_by: str) -> List[AURPackage]:
         """
         sort package list by specified field
         :param packages: packages list to sort
@@ -63,6 +66,6 @@ class Search(Handler):
             raise InvalidOption(sort_by)
         # always sort by package name at the last
         # well technically it is not a string, but we can deal with it
-        comparator: Callable[[aur.Package], Tuple[str, str]] =\
+        comparator: Callable[[AURPackage], Tuple[str, str]] =\
             lambda package: (getattr(package, sort_by), package.name)
         return sorted(packages, key=comparator)
diff --git a/src/ahriman/core/alpm/aur.py b/src/ahriman/core/alpm/aur.py
new file mode 100644
index 00000000..8d2eefec
--- /dev/null
+++ b/src/ahriman/core/alpm/aur.py
@@ -0,0 +1,152 @@
+#
+# 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 .
+#
+from __future__ import annotations
+
+import logging
+import requests
+
+from typing import Any, Dict, List, Optional, Type
+
+from ahriman.core.exceptions import InvalidPackageInfo
+from ahriman.core.util import exception_response_text
+from ahriman.models.aur_package import AURPackage
+
+
+class AUR:
+    """
+    AUR RPC wrapper
+    :cvar DEFAULT_RPC_URL: default AUR RPC url
+    :cvar DEFAULT_RPC_VERSION: default AUR RPC version
+    :ivar logger: class logger
+    :ivar rpc_url: AUR RPC url
+    :ivar rpc_version: AUR RPC version
+    """
+
+    DEFAULT_RPC_URL = "https://aur.archlinux.org/rpc"
+    DEFAULT_RPC_VERSION = "5"
+
+    def __init__(self, rpc_url: Optional[str] = None, rpc_version: Optional[str] = None) -> None:
+        """
+        default constructor
+        :param rpc_url: AUR RPC url
+        :param rpc_version: AUR RPC version
+        """
+        self.rpc_url = rpc_url or self.DEFAULT_RPC_URL
+        self.rpc_version = rpc_version or self.DEFAULT_RPC_VERSION
+        self.logger = logging.getLogger("build_details")
+
+    @classmethod
+    def info(cls: Type[AUR], package_name: str) -> AURPackage:
+        """
+        get package info by its name
+        :param package_name: package name to search
+        :return: package which match the package name
+        """
+        return cls().package_info(package_name)
+
+    @classmethod
+    def multisearch(cls: Type[AUR], *keywords: str) -> List[AURPackage]:
+        """
+        search in AUR by using API with multiple words. This method is required in order to handle
+        https://bugs.archlinux.org/task/49133. In addition short words will be dropped
+        :param keywords: search terms, e.g. "ahriman", "is", "cool"
+        :return: list of packages each of them matches all search terms
+        """
+        instance = cls()
+        packages: Dict[str, AURPackage] = {}
+        for term in filter(lambda word: len(word) > 3, keywords):
+            portion = instance.search(term)
+            packages = {
+                package.package_base: package
+                for package in portion
+                if package.package_base in packages or not packages
+            }
+        return list(packages.values())
+
+    @classmethod
+    def search(cls: Type[AUR], *keywords: str) -> List[AURPackage]:
+        """
+        search package in AUR web
+        :param keywords: keywords to search
+        :return: list of packages which match the criteria
+        """
+        return cls().package_search(*keywords)
+
+    @staticmethod
+    def parse_response(response: Dict[str, Any]) -> List[AURPackage]:
+        """
+        parse RPC response to package list
+        :param response: RPC response json
+        :return: list of parsed packages
+        """
+        response_type = response["type"]
+        if response_type == "error":
+            error_details = response.get("error", "Unknown API error")
+            raise InvalidPackageInfo(error_details)
+        return [AURPackage.from_json(package) for package in response["results"]]
+
+    def make_request(self, request_type: str, *args: str, **kwargs: str) -> List[AURPackage]:
+        """
+        perform request to AUR RPC
+        :param request_type: AUR request type, e.g. search, info
+        :param args: list of arguments to be passed as args query parameter
+        :param kwargs: list of additional named parameters like by
+        :return: response parsed to package list
+        """
+        query: Dict[str, Any] = {
+            "type": request_type,
+            "v": self.rpc_version
+        }
+
+        arg_query = "arg[]" if len(args) > 1 else "arg"
+        query[arg_query] = list(args)
+
+        for key, value in kwargs.items():
+            query[key] = value
+
+        try:
+            response = requests.get(self.rpc_url, params=query)
+            response.raise_for_status()
+            return self.parse_response(response.json())
+        except requests.HTTPError as e:
+            self.logger.exception(
+                "could not perform request by using type %s: %s",
+                request_type,
+                exception_response_text(e))
+            raise
+        except Exception:
+            self.logger.exception("could not perform request by using type %s", request_type)
+            raise
+
+    def package_info(self, package_name: str) -> AURPackage:
+        """
+        get package info by its name
+        :param package_name: package name to search
+        :return: package which match the package name
+        """
+        packages = self.make_request("info", package_name)
+        return next(package for package in packages if package.name == package_name)
+
+    def package_search(self, *keywords: str, by: str = "name-desc") -> List[AURPackage]:
+        """
+        search package in AUR web
+        :param keywords: keywords to search
+        :param by: search by the field
+        :return: list of packages which match the criteria
+        """
+        return self.make_request("search", *keywords, by=by)
diff --git a/src/ahriman/core/util.py b/src/ahriman/core/util.py
index 0c5f928b..bed5aa65 100644
--- a/src/ahriman/core/util.py
+++ b/src/ahriman/core/util.py
@@ -17,7 +17,6 @@
 # You should have received a copy of the GNU General Public License
 # along with this program. If not, see .
 #
-import aur  # type: ignore
 import datetime
 import os
 import subprocess
@@ -25,29 +24,11 @@ import requests
 
 from logging import Logger
 from pathlib import Path
-from typing import Any, Dict, Generator, Iterable, List, Optional, Union
+from typing import Any, Dict, Generator, Iterable, Optional, Union
 
 from ahriman.core.exceptions import InvalidOption, UnsafeRun
 
 
-def aur_search(*terms: str) -> List[aur.Package]:
-    """
-    search in AUR by using API with multiple words. This method is required in order to handle
-    https://bugs.archlinux.org/task/49133. In addition short words will be dropped
-    :param terms: search terms, e.g. "ahriman", "is", "cool"
-    :return: list of packages each of them matches all search terms
-    """
-    packages: Dict[str, aur.Package] = {}
-    for term in filter(lambda word: len(word) > 3, terms):
-        portion = aur.search(term)
-        packages = {
-            package.package_base: package
-            for package in portion
-            if package.package_base in packages or not packages
-        }
-    return list(packages.values())
-
-
 def check_output(*args: str, exception: Optional[Exception], cwd: Optional[Path] = None,
                  input_data: Optional[str] = None, logger: Optional[Logger] = None) -> str:
     """
diff --git a/src/ahriman/models/aur_package.py b/src/ahriman/models/aur_package.py
new file mode 100644
index 00000000..ee678fa3
--- /dev/null
+++ b/src/ahriman/models/aur_package.py
@@ -0,0 +1,109 @@
+#
+# 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 .
+#
+from __future__ import annotations
+
+import datetime
+import inflection
+
+from dataclasses import dataclass, field, fields
+from typing import Any, Callable, Dict, List, Optional, Type
+
+from ahriman.core.util import filter_json
+
+
+@dataclass
+class AURPackage:
+    """
+    AUR package descriptor
+    :ivar id: package ID
+    :ivar name: package name
+    :ivar package_base_id: package base ID
+    :ivar version: package base version
+    :ivar description: package base description
+    :ivar url: package upstream URL
+    :ivar num_votes: number of votes for the package
+    :ivar polularity: package popularity
+    :ivar out_of_date: package out of date timestamp if any
+    :ivar maintainer: package maintainer
+    :ivar first_submitted: timestamp of the first package submission
+    :ivar last_modified: timestamp of the last package submission
+    :ivar url_path: AUR package path
+    :ivar depends: list of package dependencies
+    :ivar make_depends: list of package make dependencies
+    :ivar opt_depends: list of package optional dependencies
+    :ivar conflicts: conflicts list for the package
+    :ivar provides: list of packages which this package provides
+    :ivar license: list of package licenses
+    :ivar keywords: list of package keywords
+    """
+
+    id: int
+    name: str
+    package_base_id: int
+    package_base: str
+    version: str
+    description: str
+    num_votes: int
+    popularity: float
+    first_submitted: datetime.datetime
+    last_modified: datetime.datetime
+    url_path: str
+    url: Optional[str] = None
+    out_of_date: Optional[datetime.datetime] = None
+    maintainer: Optional[str] = None
+    depends: List[str] = field(default_factory=list)
+    make_depends: List[str] = field(default_factory=list)
+    opt_depends: List[str] = field(default_factory=list)
+    conflicts: List[str] = field(default_factory=list)
+    provides: List[str] = field(default_factory=list)
+    license: List[str] = field(default_factory=list)
+    keywords: List[str] = field(default_factory=list)
+
+    @classmethod
+    def from_json(cls: Type[AURPackage], dump: Dict[str, Any]) -> AURPackage:
+        """
+        construct package descriptor from RPC properties
+        :param dump: json dump body
+        :return: AUR package descriptor
+        """
+        # filter to only known fields
+        known_fields = [pair.name for pair in fields(cls)]
+        properties = cls.convert(dump)
+        return cls(**filter_json(properties, known_fields))
+
+    @staticmethod
+    def convert(descriptor: Dict[str, Any]) -> Dict[str, Any]:
+        """
+        covert AUR RPC key names to package keys
+        :param descriptor: RPC package descriptor
+        :return: package descriptor with names converted to snake case
+        """
+        identity_mapper: Callable[[Any], Any] = lambda value: value
+        value_mapper: Dict[str, Callable[[Any], Any]] = {
+            "out_of_date": lambda value: datetime.datetime.utcfromtimestamp(value) if value is not None else None,
+            "first_submitted": datetime.datetime.utcfromtimestamp,
+            "last_modified": datetime.datetime.utcfromtimestamp,
+        }
+
+        result: Dict[str, Any] = {}
+        for api_key, api_value in descriptor.items():
+            property_key = inflection.underscore(api_key)
+            mapper = value_mapper.get(property_key, identity_mapper)
+            result[property_key] = mapper(api_value)
+
+        return result
diff --git a/src/ahriman/models/package.py b/src/ahriman/models/package.py
index 6a38c9be..63db4b66 100644
--- a/src/ahriman/models/package.py
+++ b/src/ahriman/models/package.py
@@ -19,7 +19,6 @@
 #
 from __future__ import annotations
 
-import aur  # type: ignore
 import copy
 import logging
 
@@ -29,6 +28,7 @@ from pyalpm import vercmp  # type: ignore
 from srcinfo.parse import parse_srcinfo  # type: ignore
 from typing import Any, Dict, Iterable, List, Optional, Set, Type
 
+from ahriman.core.alpm.aur import AUR
 from ahriman.core.alpm.pacman import Pacman
 from ahriman.core.exceptions import InvalidPackageInfo
 from ahriman.core.util import check_output
@@ -129,7 +129,7 @@ class Package:
         :param aur_url: AUR root url
         :return: package properties
         """
-        package = aur.info(name)
+        package = AUR.info(name)
         return cls(package.package_base, package.version, aur_url, {package.name: PackageDescription()})
 
     @classmethod
diff --git a/src/ahriman/web/middlewares/auth_handler.py b/src/ahriman/web/middlewares/auth_handler.py
index f2cbdc2c..481d0647 100644
--- a/src/ahriman/web/middlewares/auth_handler.py
+++ b/src/ahriman/web/middlewares/auth_handler.py
@@ -25,8 +25,8 @@ from aiohttp import web
 from aiohttp.web import middleware, Request
 from aiohttp.web_response import StreamResponse
 from aiohttp.web_urldispatcher import StaticResource
-from aiohttp_session import setup as setup_session  # type: ignore
-from aiohttp_session.cookie_storage import EncryptedCookieStorage  # type: ignore
+from aiohttp_session import setup as setup_session
+from aiohttp_session.cookie_storage import EncryptedCookieStorage
 from cryptography import fernet
 from typing import Optional
 
diff --git a/src/ahriman/web/views/service/search.py b/src/ahriman/web/views/service/search.py
index 1df3b9b8..f1fb7fe7 100644
--- a/src/ahriman/web/views/service/search.py
+++ b/src/ahriman/web/views/service/search.py
@@ -17,12 +17,11 @@
 # You should have received a copy of the GNU General Public License
 # along with this program. If not, see .
 #
-import aur  # type: ignore
-
 from aiohttp.web import HTTPNotFound, Response, json_response
 from typing import Callable, List
 
-from ahriman.core.util import aur_search
+from ahriman.core.alpm.aur import AUR
+from ahriman.models.aur_package import AURPackage
 from ahriman.models.user_access import UserAccess
 from ahriman.web.views.base import BaseView
 
@@ -45,11 +44,11 @@ class SearchView(BaseView):
         :return: 200 with found package bases and descriptions sorted by base
         """
         search: List[str] = self.request.query.getall("for", default=[])
-        packages = aur_search(*search)
+        packages = AUR.multisearch(*search)
         if not packages:
             raise HTTPNotFound(reason=f"No packages found for terms: {search}")
 
-        comparator: Callable[[aur.Package], str] = lambda item: str(item.package_base)
+        comparator: Callable[[AURPackage], str] = lambda item: str(item.package_base)
         response = [
             {
                 "package": package.package_base,
diff --git a/tests/ahriman/application/formatters/conftest.py b/tests/ahriman/application/formatters/conftest.py
index 0394e05f..6a38c710 100644
--- a/tests/ahriman/application/formatters/conftest.py
+++ b/tests/ahriman/application/formatters/conftest.py
@@ -1,4 +1,3 @@
-import aur
 import pytest
 
 from ahriman.application.formatters.aur_printer import AurPrinter
@@ -7,12 +6,13 @@ from ahriman.application.formatters.package_printer import PackagePrinter
 from ahriman.application.formatters.status_printer import StatusPrinter
 from ahriman.application.formatters.string_printer import StringPrinter
 from ahriman.application.formatters.update_printer import UpdatePrinter
+from ahriman.models.aur_package import AURPackage
 from ahriman.models.build_status import BuildStatus
 from ahriman.models.package import Package
 
 
 @pytest.fixture
-def aur_package_ahriman_printer(aur_package_ahriman: aur.Package) -> AurPrinter:
+def aur_package_ahriman_printer(aur_package_ahriman: AURPackage) -> AurPrinter:
     """
     fixture for AUR package printer
     :param aur_package_ahriman: AUR package fixture
diff --git a/tests/ahriman/application/handlers/test_handler_search.py b/tests/ahriman/application/handlers/test_handler_search.py
index 8300e0b9..b8e5bb81 100644
--- a/tests/ahriman/application/handlers/test_handler_search.py
+++ b/tests/ahriman/application/handlers/test_handler_search.py
@@ -1,5 +1,5 @@
 import argparse
-import aur
+import dataclasses
 import pytest
 
 from pytest_mock import MockerFixture
@@ -7,6 +7,7 @@ from pytest_mock import MockerFixture
 from ahriman.application.handlers import Search
 from ahriman.core.configuration import Configuration
 from ahriman.core.exceptions import InvalidOption
+from ahriman.models.aur_package import AURPackage
 
 
 def _default_args(args: argparse.Namespace) -> argparse.Namespace:
@@ -21,13 +22,13 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
     return args
 
 
-def test_run(args: argparse.Namespace, configuration: Configuration, aur_package_ahriman: aur.Package,
+def test_run(args: argparse.Namespace, configuration: Configuration, aur_package_ahriman: AURPackage,
              mocker: MockerFixture) -> None:
     """
     must run command
     """
     args = _default_args(args)
-    search_mock = mocker.patch("ahriman.application.handlers.search.aur_search", return_value=[aur_package_ahriman])
+    search_mock = mocker.patch("ahriman.core.alpm.aur.AUR.multisearch", return_value=[aur_package_ahriman])
     print_mock = mocker.patch("ahriman.application.formatters.printer.Printer.print")
 
     Search.run(args, "x86_64", configuration, True)
@@ -35,38 +36,38 @@ def test_run(args: argparse.Namespace, configuration: Configuration, aur_package
     print_mock.assert_called_once()
 
 
-def test_run_sort(args: argparse.Namespace, configuration: Configuration, aur_package_ahriman: aur.Package,
+def test_run_sort(args: argparse.Namespace, configuration: Configuration, aur_package_ahriman: AURPackage,
                   mocker: MockerFixture) -> None:
     """
     must run command with sorting
     """
     args = _default_args(args)
-    mocker.patch("ahriman.application.handlers.search.aur_search", return_value=[aur_package_ahriman])
+    mocker.patch("ahriman.core.alpm.aur.AUR.multisearch", return_value=[aur_package_ahriman])
     sort_mock = mocker.patch("ahriman.application.handlers.search.Search.sort")
 
     Search.run(args, "x86_64", configuration, True)
     sort_mock.assert_called_once_with([aur_package_ahriman], "name")
 
 
-def test_run_sort_by(args: argparse.Namespace, configuration: Configuration, aur_package_ahriman: aur.Package,
+def test_run_sort_by(args: argparse.Namespace, configuration: Configuration, aur_package_ahriman: AURPackage,
                      mocker: MockerFixture) -> None:
     """
     must run command with sorting by specified field
     """
     args = _default_args(args)
     args.sort_by = "field"
-    mocker.patch("ahriman.application.handlers.search.aur_search", return_value=[aur_package_ahriman])
+    mocker.patch("ahriman.core.alpm.aur.AUR.multisearch", return_value=[aur_package_ahriman])
     sort_mock = mocker.patch("ahriman.application.handlers.search.Search.sort")
 
     Search.run(args, "x86_64", configuration, True)
     sort_mock.assert_called_once_with([aur_package_ahriman], "field")
 
 
-def test_sort(aur_package_ahriman: aur.Package) -> None:
+def test_sort(aur_package_ahriman: AURPackage) -> None:
     """
     must sort package list
     """
-    another = aur_package_ahriman._replace(name="1", package_base="base")
+    another = dataclasses.replace(aur_package_ahriman, name="1", package_base="base")
     # sort by name
     assert Search.sort([aur_package_ahriman, another], "name") == [another, aur_package_ahriman]
     # sort by another field
@@ -75,7 +76,7 @@ def test_sort(aur_package_ahriman: aur.Package) -> None:
     assert Search.sort([aur_package_ahriman, another], "version") == [another, aur_package_ahriman]
 
 
-def test_sort_exception(aur_package_ahriman: aur.Package) -> None:
+def test_sort_exception(aur_package_ahriman: AURPackage) -> None:
     """
     must raise an exception on unknown sorting field
     """
@@ -94,4 +95,5 @@ def test_sort_fields() -> None:
     """
     must store valid field list which are allowed to be used for sorting
     """
-    assert all(field in aur.Package._fields for field in Search.SORT_FIELDS)
+    expected = {pair.name for pair in dataclasses.fields(AURPackage)}
+    assert all(field in expected for field in Search.SORT_FIELDS)
diff --git a/tests/ahriman/conftest.py b/tests/ahriman/conftest.py
index 23fb8e69..96afe586 100644
--- a/tests/ahriman/conftest.py
+++ b/tests/ahriman/conftest.py
@@ -1,4 +1,4 @@
-import aur
+import datetime
 import pytest
 
 from pathlib import Path
@@ -10,6 +10,7 @@ from ahriman.core.auth.auth import Auth
 from ahriman.core.configuration import Configuration
 from ahriman.core.spawn import Spawn
 from ahriman.core.status.watcher import Watcher
+from ahriman.models.aur_package import AURPackage
 from ahriman.models.package import Package
 from ahriman.models.package_description import PackageDescription
 from ahriman.models.repository_paths import RepositoryPaths
@@ -48,28 +49,56 @@ def anyvar(cls: Type[T], strict: bool = False) -> T:
 
 # generic fixtures
 @pytest.fixture
-def aur_package_ahriman(package_ahriman: Package) -> aur.Package:
+def aur_package_ahriman() -> AURPackage:
     """
     fixture for AUR package
-    :param package_ahriman: package fixture
     :return: AUR package test instance
     """
-    return aur.Package(
-        num_votes=None,
-        description=package_ahriman.packages[package_ahriman.base].description,
-        url_path=package_ahriman.web_url,
-        last_modified=None,
-        name=package_ahriman.base,
+    return AURPackage(
+        id=1009791,
+        name="ahriman",
+        package_base_id=165427,
+        package_base="ahriman",
+        version="1.7.0-1",
+        description="ArcH Linux ReposItory MANager",
+        num_votes=0,
+        popularity=0,
+        first_submitted=datetime.datetime(2021, 4, 9, 22, 44, 45),
+        last_modified=datetime.datetime(2021, 12, 25, 23, 11, 11),
+        url_path="/cgit/aur.git/snapshot/ahriman.tar.gz",
+        url="https://github.com/arcan1s/ahriman",
         out_of_date=None,
-        id=None,
-        first_submitted=None,
-        maintainer=None,
-        version=package_ahriman.version,
-        license=package_ahriman.packages[package_ahriman.base].licenses,
-        url=None,
-        package_base=package_ahriman.base,
-        package_base_id=None,
-        category_id=None)
+        maintainer="arcanis",
+        depends=[
+            "devtools",
+            "git",
+            "pyalpm",
+            "python-aur",
+            "python-passlib",
+            "python-srcinfo",
+        ],
+        make_depends=["python-pip"],
+        opt_depends=[
+            "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",
+        ],
+        conflicts=[],
+        provides=[],
+        license=["GPL3"],
+        keywords=[],
+    )
 
 
 @pytest.fixture
@@ -103,7 +132,7 @@ def package_ahriman(package_description_ahriman: PackageDescription) -> Package:
     packages = {"ahriman": package_description_ahriman}
     return Package(
         base="ahriman",
-        version="0.12.1-1",
+        version="1.7.0-1",
         aur_url="https://aur.archlinux.org",
         packages=packages)
 
@@ -139,9 +168,16 @@ def package_description_ahriman() -> PackageDescription:
         architecture="x86_64",
         archive_size=4200,
         build_date=42,
-        depends=["devtools", "git", "pyalpm", "python-aur", "python-srcinfo"],
+        depends=[
+            "devtools",
+            "git",
+            "pyalpm",
+            "python-aur",
+            "python-passlib",
+            "python-srcinfo",
+        ],
         description="ArcH Linux ReposItory MANager",
-        filename="ahriman-0.12.1-1-any.pkg.tar.zst",
+        filename="ahriman-1.7.0-1-any.pkg.tar.zst",
         groups=[],
         installed_size=4200000,
         licenses=["GPL3"],
diff --git a/tests/ahriman/core/alpm/conftest.py b/tests/ahriman/core/alpm/conftest.py
new file mode 100644
index 00000000..6f0a021a
--- /dev/null
+++ b/tests/ahriman/core/alpm/conftest.py
@@ -0,0 +1,12 @@
+import pytest
+
+from ahriman.core.alpm.aur import AUR
+
+
+@pytest.fixture
+def aur() -> AUR:
+    """
+    aur helper fixture
+    :return: aur helper instance
+    """
+    return AUR()
diff --git a/tests/ahriman/core/alpm/test_aur.py b/tests/ahriman/core/alpm/test_aur.py
new file mode 100644
index 00000000..bf9f9541
--- /dev/null
+++ b/tests/ahriman/core/alpm/test_aur.py
@@ -0,0 +1,173 @@
+import json
+import pytest
+import requests
+
+from pathlib import Path
+from pytest_mock import MockerFixture
+from unittest import mock
+from unittest.mock import MagicMock
+
+from ahriman.core.alpm.aur import AUR
+from ahriman.core.exceptions import InvalidPackageInfo
+from ahriman.models.aur_package import AURPackage
+
+
+def _get_response(resource_path_root: Path) -> str:
+    """
+    load response from resource file
+    :param resource_path_root: path to resource root
+    :return: response text
+    """
+    return (resource_path_root / "models" / "package_ahriman_aur").read_text()
+
+
+def test_info(mocker: MockerFixture) -> None:
+    """
+    must call info method
+    """
+    info_mock = mocker.patch("ahriman.core.alpm.aur.AUR.package_info")
+    AUR.info("ahriman")
+    info_mock.assert_called_once_with("ahriman")
+
+
+def test_multisearch(aur_package_ahriman: AURPackage, mocker: MockerFixture) -> None:
+    """
+    must search in AUR with multiple words
+    """
+    terms = ["ahriman", "is", "cool"]
+    search_mock = mocker.patch("ahriman.core.alpm.aur.AUR.search", return_value=[aur_package_ahriman])
+
+    assert AUR.multisearch(*terms) == [aur_package_ahriman]
+    search_mock.assert_has_calls([mock.call("ahriman"), mock.call("cool")])
+
+
+def test_multisearch_empty(mocker: MockerFixture) -> None:
+    """
+    must return empty list if no long terms supplied
+    """
+    terms = ["it", "is"]
+    search_mock = mocker.patch("ahriman.core.alpm.aur.AUR.search")
+
+    assert AUR.multisearch(*terms) == []
+    search_mock.assert_not_called()
+
+
+def test_multisearch_single(aur_package_ahriman: AURPackage, mocker: MockerFixture) -> None:
+    """
+    must search in AUR with one word
+    """
+    search_mock = mocker.patch("ahriman.core.alpm.aur.AUR.search", return_value=[aur_package_ahriman])
+    assert AUR.multisearch("ahriman") == [aur_package_ahriman]
+    search_mock.assert_called_once_with("ahriman")
+
+
+def test_search(mocker: MockerFixture) -> None:
+    """
+    must call search method
+    """
+    search_mock = mocker.patch("ahriman.core.alpm.aur.AUR.package_search")
+    AUR.search("ahriman")
+    search_mock.assert_called_once_with("ahriman")
+
+
+def test_parse_response(aur_package_ahriman: AURPackage, resource_path_root: Path) -> None:
+    """
+    must parse success response
+    """
+    response = _get_response(resource_path_root)
+    assert AUR.parse_response(json.loads(response)) == [aur_package_ahriman]
+
+
+def test_parse_response_error(resource_path_root: Path) -> None:
+    """
+    must raise exception on invalid response
+    """
+    response = (resource_path_root / "models" / "aur_error").read_text()
+    with pytest.raises(InvalidPackageInfo, match="Incorrect request type specified."):
+        AUR.parse_response(json.loads(response))
+
+
+def test_parse_response_unknown_error(resource_path_root: Path) -> None:
+    """
+    must raise exception on invalid response with empty error message
+    """
+    with pytest.raises(InvalidPackageInfo, match="Unknown API error"):
+        AUR.parse_response({"type": "error"})
+
+
+def test_make_request(aur: AUR, aur_package_ahriman: AURPackage,
+                      mocker: MockerFixture, resource_path_root: Path) -> None:
+    """
+    must perform request to AUR
+    """
+    response_mock = MagicMock()
+    response_mock.json.return_value = json.loads(_get_response(resource_path_root))
+    request_mock = mocker.patch("requests.get", return_value=response_mock)
+
+    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"]})
+
+
+def test_make_request_multi_arg(aur: AUR, aur_package_ahriman: AURPackage,
+                                mocker: MockerFixture, resource_path_root: Path) -> None:
+    """
+    must perform request to AUR with multiple args
+    """
+    response_mock = MagicMock()
+    response_mock.json.return_value = json.loads(_get_response(resource_path_root))
+    request_mock = mocker.patch("requests.get", return_value=response_mock)
+
+    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"]})
+
+
+def test_make_request_with_kwargs(aur: AUR, aur_package_ahriman: AURPackage,
+                                  mocker: MockerFixture, resource_path_root: Path) -> None:
+    """
+    must perform request to AUR with named parameters
+    """
+    response_mock = MagicMock()
+    response_mock.json.return_value = json.loads(_get_response(resource_path_root))
+    request_mock = mocker.patch("requests.get", return_value=response_mock)
+
+    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"})
+
+
+def test_make_request_failed(aur: AUR, mocker: MockerFixture) -> None:
+    """
+    must reraise generic exception
+    """
+    mocker.patch("requests.get", side_effect=Exception())
+    with pytest.raises(Exception):
+        aur.make_request("info", "ahriman")
+
+
+def test_make_request_failed_http_error(aur: AUR, mocker: MockerFixture) -> None:
+    """
+    must reraise http exception
+    """
+    mocker.patch("requests.get", side_effect=requests.exceptions.HTTPError())
+    with pytest.raises(requests.exceptions.HTTPError):
+        aur.make_request("info", "ahriman")
+
+
+def test_package_info(aur: AUR, aur_package_ahriman: AURPackage, mocker: MockerFixture) -> None:
+    """
+    must make request for info
+    """
+    request_mock = mocker.patch("ahriman.core.alpm.aur.AUR.make_request", return_value=[aur_package_ahriman])
+    assert aur.package_info(aur_package_ahriman.name) == aur_package_ahriman
+    request_mock.assert_called_once_with("info", aur_package_ahriman.name)
+
+
+def test_package_search(aur: AUR, aur_package_ahriman: AURPackage, mocker: MockerFixture) -> None:
+    """
+    must make request for search
+    """
+    request_mock = mocker.patch("ahriman.core.alpm.aur.AUR.make_request", return_value=[aur_package_ahriman])
+    assert aur.package_search(aur_package_ahriman.name, by="name") == [aur_package_ahriman]
+    request_mock.assert_called_once_with("search", aur_package_ahriman.name, by="name")
diff --git a/tests/ahriman/core/test_util.py b/tests/ahriman/core/test_util.py
index 98b48d2f..b8df4a12 100644
--- a/tests/ahriman/core/test_util.py
+++ b/tests/ahriman/core/test_util.py
@@ -1,4 +1,3 @@
-import aur
 import datetime
 import logging
 import pytest
@@ -6,45 +5,12 @@ import subprocess
 
 from pathlib import Path
 from pytest_mock import MockerFixture
-from unittest import mock
 
 from ahriman.core.exceptions import InvalidOption, UnsafeRun
-from ahriman.core.util import aur_search, check_output, check_user, filter_json, package_like, pretty_datetime, \
-    pretty_size, walk
+from ahriman.core.util import check_output, check_user, filter_json, package_like, pretty_datetime, pretty_size, walk
 from ahriman.models.package import Package
 
 
-def test_aur_search(aur_package_ahriman: aur.Package, mocker: MockerFixture) -> None:
-    """
-    must search in AUR with multiple words
-    """
-    terms = ["ahriman", "is", "cool"]
-    search_mock = mocker.patch("aur.search", return_value=[aur_package_ahriman])
-
-    assert aur_search(*terms) == [aur_package_ahriman]
-    search_mock.assert_has_calls([mock.call("ahriman"), mock.call("cool")])
-
-
-def test_aur_search_empty(mocker: MockerFixture) -> None:
-    """
-    must return empty list if no long terms supplied
-    """
-    terms = ["it", "is"]
-    search_mock = mocker.patch("aur.search")
-
-    assert aur_search(*terms) == []
-    search_mock.assert_not_called()
-
-
-def test_aur_search_single(aur_package_ahriman: aur.Package, mocker: MockerFixture) -> None:
-    """
-    must search in AUR with one word
-    """
-    search_mock = mocker.patch("aur.search", return_value=[aur_package_ahriman])
-    assert aur_search("ahriman") == [aur_package_ahriman]
-    search_mock.assert_called_once_with("ahriman")
-
-
 def test_check_output(mocker: MockerFixture) -> None:
     """
     must run command and log result
@@ -127,7 +93,7 @@ def test_filter_json(package_ahriman: Package) -> None:
 
 def test_filter_json_empty_value(package_ahriman: Package) -> None:
     """
-    must return empty values from object
+    must filter empty values from object
     """
     probe = package_ahriman.view()
     probe["base"] = None
@@ -238,8 +204,10 @@ def test_walk(resource_path_root: Path) -> None:
     expected = sorted([
         resource_path_root / "core/ahriman.ini",
         resource_path_root / "core/logging.ini",
+        resource_path_root / "models/aur_error",
         resource_path_root / "models/big_file_checksum",
         resource_path_root / "models/empty_file_checksum",
+        resource_path_root / "models/package_ahriman_aur",
         resource_path_root / "models/package_ahriman_srcinfo",
         resource_path_root / "models/package_tpacpi-bat-git_srcinfo",
         resource_path_root / "models/package_yay_srcinfo",
diff --git a/tests/ahriman/core/upload/test_http_upload.py b/tests/ahriman/core/upload/test_http_upload.py
index 97846527..9e70fd9f 100644
--- a/tests/ahriman/core/upload/test_http_upload.py
+++ b/tests/ahriman/core/upload/test_http_upload.py
@@ -22,7 +22,7 @@ def test_calculate_hash_small(resource_path_root: Path) -> None:
     must calculate checksum for path which is single chunk
     """
     path = resource_path_root / "models" / "package_ahriman_srcinfo"
-    assert HttpUpload.calculate_hash(path) == "a55f82198e56061295d405aeb58f4062"
+    assert HttpUpload.calculate_hash(path) == "c0aaf6ebf95ca9206dc8ba1d8ff10af3"
 
 
 def test_get_body_get_hashes() -> None:
diff --git a/tests/ahriman/core/upload/test_s3.py b/tests/ahriman/core/upload/test_s3.py
index ca6a24a7..a0256c47 100644
--- a/tests/ahriman/core/upload/test_s3.py
+++ b/tests/ahriman/core/upload/test_s3.py
@@ -31,7 +31,7 @@ def test_calculate_etag_small(resource_path_root: Path) -> None:
     must calculate checksum for path which is single chunk
     """
     path = resource_path_root / "models" / "package_ahriman_srcinfo"
-    assert S3.calculate_etag(path, _chunk_size) == "a55f82198e56061295d405aeb58f4062"
+    assert S3.calculate_etag(path, _chunk_size) == "c0aaf6ebf95ca9206dc8ba1d8ff10af3"
 
 
 def test_files_remove(s3_remote_objects: List[Any]) -> None:
diff --git a/tests/ahriman/models/test_aur_package.py b/tests/ahriman/models/test_aur_package.py
new file mode 100644
index 00000000..96029b42
--- /dev/null
+++ b/tests/ahriman/models/test_aur_package.py
@@ -0,0 +1,47 @@
+import datetime
+import json
+
+from dataclasses import asdict, fields
+from pathlib import Path
+from pytest_mock import MockerFixture
+from typing import Any, Dict
+
+from ahriman.models.aur_package import AURPackage
+
+
+def _get_data(resource_path_root: Path) -> Dict[str, Any]:
+    """
+    load package description from resource file
+    :param resource_path_root: path to resource root
+    :return: json descriptor
+    """
+    response = (resource_path_root / "models" / "package_ahriman_aur").read_text()
+    return json.loads(response)["results"][0]
+
+
+def test_from_json(aur_package_ahriman: AURPackage, resource_path_root: Path) -> None:
+    """
+    must load package from json
+    """
+    model = _get_data(resource_path_root)
+    assert AURPackage.from_json(model) == aur_package_ahriman
+
+
+def test_from_json_2(aur_package_ahriman: AURPackage, mocker: MockerFixture) -> None:
+    """
+    must load the same package from json
+    """
+    mocker.patch("ahriman.models.aur_package.AURPackage.convert", side_effect=lambda v: v)
+    assert AURPackage.from_json(asdict(aur_package_ahriman)) == aur_package_ahriman
+
+
+def test_convert(aur_package_ahriman: AURPackage, resource_path_root: Path) -> None:
+    """
+    must convert fields to snakecase and also apply converters
+    """
+    model = _get_data(resource_path_root)
+    converted = AURPackage.convert(model)
+    known_fields = [pair.name for pair in fields(AURPackage)]
+    assert all(field in known_fields for field in converted)
+    assert isinstance(converted.get("first_submitted"), datetime.datetime)
+    assert isinstance(converted.get("last_modified"), datetime.datetime)
diff --git a/tests/ahriman/models/test_package.py b/tests/ahriman/models/test_package.py
index 110c8cc8..20e54a88 100644
--- a/tests/ahriman/models/test_package.py
+++ b/tests/ahriman/models/test_package.py
@@ -2,9 +2,10 @@ import pytest
 
 from pathlib import Path
 from pytest_mock import MockerFixture
-from unittest.mock import MagicMock, PropertyMock
+from unittest.mock import MagicMock
 
 from ahriman.core.exceptions import InvalidPackageInfo
+from ahriman.models.aur_package import AURPackage
 from ahriman.models.package import Package
 from ahriman.models.package_source import PackageSource
 from ahriman.models.repository_paths import RepositoryPaths
@@ -96,15 +97,11 @@ def test_from_archive(package_ahriman: Package, pyalpm_handle: MagicMock, mocker
     assert Package.from_archive(Path("path"), pyalpm_handle, package_ahriman.aur_url) == package_ahriman
 
 
-def test_from_aur(package_ahriman: Package, mocker: MockerFixture) -> None:
+def test_from_aur(package_ahriman: Package, aur_package_ahriman: AURPackage, mocker: MockerFixture) -> None:
     """
     must construct package from aur
     """
-    mock = MagicMock()
-    type(mock).name = PropertyMock(return_value=package_ahriman.base)
-    type(mock).package_base = PropertyMock(return_value=package_ahriman.base)
-    type(mock).version = PropertyMock(return_value=package_ahriman.version)
-    mocker.patch("aur.info", return_value=mock)
+    mocker.patch("ahriman.core.alpm.aur.AUR.info", return_value=aur_package_ahriman)
 
     package = Package.from_aur(package_ahriman.base, package_ahriman.aur_url)
     assert package_ahriman.base == package.base
diff --git a/tests/ahriman/web/views/service/test_views_service_search.py b/tests/ahriman/web/views/service/test_views_service_search.py
index b2c37964..7ba037e6 100644
--- a/tests/ahriman/web/views/service/test_views_service_search.py
+++ b/tests/ahriman/web/views/service/test_views_service_search.py
@@ -1,9 +1,9 @@
-import aur
 import pytest
 
 from aiohttp.test_utils import TestClient
 from pytest_mock import MockerFixture
 
+from ahriman.models.aur_package import AURPackage
 from ahriman.models.user_access import UserAccess
 from ahriman.web.views.service.search import SearchView
 
@@ -17,11 +17,11 @@ async def test_get_permission() -> None:
         assert await SearchView.get_permission(request) == UserAccess.Read
 
 
-async def test_get(client: TestClient, aur_package_ahriman: aur.Package, mocker: MockerFixture) -> None:
+async def test_get(client: TestClient, aur_package_ahriman: AURPackage, mocker: MockerFixture) -> None:
     """
     must call get request correctly
     """
-    mocker.patch("ahriman.web.views.service.search.aur_search", return_value=[aur_package_ahriman])
+    mocker.patch("ahriman.core.alpm.aur.AUR.multisearch", return_value=[aur_package_ahriman])
     response = await client.get("/service-api/v1/search", params={"for": "ahriman"})
 
     assert response.ok
@@ -33,7 +33,7 @@ async def test_get_exception(client: TestClient, mocker: MockerFixture) -> None:
     """
     must raise 400 on empty search string
     """
-    search_mock = mocker.patch("ahriman.web.views.service.search.aur_search", return_value=[])
+    search_mock = mocker.patch("ahriman.core.alpm.aur.AUR.multisearch", return_value=[])
     response = await client.get("/service-api/v1/search")
 
     assert response.status == 404
@@ -44,7 +44,7 @@ async def test_get_join(client: TestClient, mocker: MockerFixture) -> None:
     """
     must join search args with space
     """
-    search_mock = mocker.patch("ahriman.web.views.service.search.aur_search")
+    search_mock = mocker.patch("ahriman.core.alpm.aur.AUR.multisearch")
     response = await client.get("/service-api/v1/search", params=[("for", "ahriman"), ("for", "maybe")])
 
     assert response.ok
diff --git a/tests/ahriman/web/views/user/test_views_user_login.py b/tests/ahriman/web/views/user/test_views_user_login.py
index de980e36..572a199e 100644
--- a/tests/ahriman/web/views/user/test_views_user_login.py
+++ b/tests/ahriman/web/views/user/test_views_user_login.py
@@ -1,4 +1,5 @@
 import pytest
+
 from aiohttp.test_utils import TestClient
 from pytest_mock import MockerFixture
 from unittest.mock import MagicMock
@@ -31,7 +32,7 @@ async def test_get_redirect_to_oauth(client_with_auth: TestClient) -> None:
     must redirect to OAuth service provider in case if no code is supplied
     """
     oauth = client_with_auth.app["validator"] = MagicMock(spec=OAuth)
-    oauth.get_oauth_url.return_value = "https://example.com"
+    oauth.get_oauth_url.return_value = "https://httpbin.org"
 
     get_response = await client_with_auth.get("/user-api/v1/login")
     assert get_response.ok
@@ -43,7 +44,7 @@ async def test_get_redirect_to_oauth_empty_code(client_with_auth: TestClient) ->
     must redirect to OAuth service provider in case if empty code is supplied
     """
     oauth = client_with_auth.app["validator"] = MagicMock(spec=OAuth)
-    oauth.get_oauth_url.return_value = "https://example.com"
+    oauth.get_oauth_url.return_value = "https://httpbin.org"
 
     get_response = await client_with_auth.get("/user-api/v1/login", params={"code": ""})
     assert get_response.ok
diff --git a/tests/testresources/models/aur_error b/tests/testresources/models/aur_error
new file mode 100644
index 00000000..04655520
--- /dev/null
+++ b/tests/testresources/models/aur_error
@@ -0,0 +1,7 @@
+{
+  "error": "Incorrect request type specified.",
+  "resultcount": 0,
+  "results": [],
+  "type": "error",
+  "version": 5
+}
diff --git a/tests/testresources/models/package_ahriman_aur b/tests/testresources/models/package_ahriman_aur
new file mode 100644
index 00000000..585df47a
--- /dev/null
+++ b/tests/testresources/models/package_ahriman_aur
@@ -0,0 +1,54 @@
+{
+  "resultcount": 1,
+  "results": [
+    {
+      "Depends": [
+        "devtools",
+        "git",
+        "pyalpm",
+        "python-aur",
+        "python-passlib",
+        "python-srcinfo"
+      ],
+      "Description": "ArcH Linux ReposItory MANager",
+      "FirstSubmitted": 1618008285,
+      "ID": 1009791,
+      "Keywords": [],
+      "LastModified": 1640473871,
+      "License": [
+        "GPL3"
+      ],
+      "Maintainer": "arcanis",
+      "MakeDepends": [
+        "python-pip"
+      ],
+      "Name": "ahriman",
+      "NumVotes": 0,
+      "OptDepends": [
+        "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"
+      ],
+      "OutOfDate": null,
+      "PackageBase": "ahriman",
+      "PackageBaseID": 165427,
+      "Popularity": 0,
+      "URL": "https://github.com/arcan1s/ahriman",
+      "URLPath": "/cgit/aur.git/snapshot/ahriman.tar.gz",
+      "Version": "1.7.0-1"
+    }
+  ],
+  "type": "multiinfo",
+  "version": 5
+}
diff --git a/tests/testresources/models/package_ahriman_srcinfo b/tests/testresources/models/package_ahriman_srcinfo
index 57e15e39..2529d2ef 100644
--- a/tests/testresources/models/package_ahriman_srcinfo
+++ b/tests/testresources/models/package_ahriman_srcinfo
@@ -1,6 +1,6 @@
 pkgbase = ahriman
         pkgdesc = ArcH Linux ReposItory MANager
-        pkgver = 0.12.1
+        pkgver = 1.7.0
         pkgrel = 1
         url = https://github.com/arcan1s/ahriman
         arch = any
@@ -10,6 +10,7 @@ pkgbase = ahriman
         depends = git
         depends = pyalpm
         depends = python-aur
+        depends = python-passlib
         depends = python-srcinfo
         optdepends = aws-cli: sync to s3
         optdepends = breezy: -bzr packages support
@@ -24,7 +25,7 @@ pkgbase = ahriman
         optdepends = subversion: -svn packages support
         backup = etc/ahriman.ini
         backup = etc/ahriman.ini.d/logging.ini
-        source = https://github.com/arcan1s/ahriman/releases/download/0.12.1/ahriman-0.12.1-src.tar.xz
+        source = https://github.com/arcan1s/ahriman/releases/download/1.7.0/ahriman-1.7.0-src.tar.xz
         source = ahriman.sysusers
         source = ahriman.tmpfiles
         sha512sums = 8acc57f937d587ca665c29092cadddbaf3ba0b80e870b80d1551e283aba8f21306f9030a26fec8c71ab5863316f5f5f061b7ddc63cdff9e6d5a885f28ef1893d