feat: add retry policy

This commit is contained in:
2026-02-20 02:44:32 +02:00
parent dec025b45a
commit b0f1828ae7
13 changed files with 303 additions and 68 deletions

View File

@@ -33,6 +33,14 @@ def test_normalize_coerce_boolean(validator: Validator, mocker: MockerFixture) -
convert_mock.assert_called_once_with("1")
def test_normalize_coerce_float(validator: Validator) -> None:
"""
must convert string value to float by using configuration converters
"""
assert validator._normalize_coerce_float("1.5") == 1.5
assert validator._normalize_coerce_float("0.0") == 0.0
def test_normalize_coerce_integer(validator: Validator) -> None:
"""
must convert string value to integer by using configuration converters

View File

@@ -1,6 +1,5 @@
import pytest
import requests
import requests_unixsocket
from pytest_mock import MockerFixture
@@ -8,31 +7,32 @@ from ahriman.core.http import SyncAhrimanClient
from ahriman.models.user import User
def test_session(ahriman_client: SyncAhrimanClient, mocker: MockerFixture) -> None:
def test_adapters(ahriman_client: SyncAhrimanClient) -> None:
"""
must create normal requests session
must return native adapters
"""
login_mock = mocker.patch("ahriman.core.http.SyncAhrimanClient._login")
assert isinstance(ahriman_client.session, requests.Session)
assert not isinstance(ahriman_client.session, requests_unixsocket.Session)
login_mock.assert_called_once_with(pytest.helpers.anyvar(int))
assert "http+unix://" not in ahriman_client.adapters()
def test_session_unix_socket(ahriman_client: SyncAhrimanClient, mocker: MockerFixture) -> None:
def test_adapters_unix_socket(ahriman_client: SyncAhrimanClient) -> None:
"""
must create unix socket session
must register unix socket adapter
"""
login_mock = mocker.patch("ahriman.core.http.SyncAhrimanClient._login")
ahriman_client.address = "http+unix://path"
assert isinstance(ahriman_client.session, requests_unixsocket.Session)
login_mock.assert_not_called()
assert "http+unix://" in ahriman_client.adapters()
def test_login(ahriman_client: SyncAhrimanClient, user: User, mocker: MockerFixture) -> None:
def test_login_url(ahriman_client: SyncAhrimanClient) -> None:
"""
must login user
must generate login url correctly
"""
assert ahriman_client._login_url().startswith(ahriman_client.address)
assert ahriman_client._login_url().endswith("/api/v1/login")
def test_on_session_creation(ahriman_client: SyncAhrimanClient, user: User, mocker: MockerFixture) -> None:
"""
must log in user on start
"""
ahriman_client.auth = (user.username, user.password)
requests_mock = mocker.patch("ahriman.core.http.SyncAhrimanClient.make_request")
@@ -42,40 +42,32 @@ def test_login(ahriman_client: SyncAhrimanClient, user: User, mocker: MockerFixt
}
session = requests.Session()
ahriman_client._login(session)
ahriman_client.on_session_creation(session)
requests_mock.assert_called_once_with("POST", pytest.helpers.anyvar(str, True), json=payload, session=session)
def test_login_failed(ahriman_client: SyncAhrimanClient, user: User, mocker: MockerFixture) -> None:
def test_on_session_creation_failed(ahriman_client: SyncAhrimanClient, user: User, mocker: MockerFixture) -> None:
"""
must suppress any exception happened during login
must suppress any exception happened during session start
"""
ahriman_client.user = user
mocker.patch("requests.Session.request", side_effect=Exception)
ahriman_client._login(requests.Session())
ahriman_client.on_session_creation(requests.Session())
def test_login_failed_http_error(ahriman_client: SyncAhrimanClient, user: User, mocker: MockerFixture) -> None:
def test_start_failed_http_error(ahriman_client: SyncAhrimanClient, user: User, mocker: MockerFixture) -> None:
"""
must suppress HTTP exception happened during login
must suppress HTTP exception happened during session start
"""
ahriman_client.user = user
mocker.patch("requests.Session.request", side_effect=requests.HTTPError)
ahriman_client._login(requests.Session())
ahriman_client.on_session_creation(requests.Session())
def test_login_skip(ahriman_client: SyncAhrimanClient, mocker: MockerFixture) -> None:
def test_start_skip(ahriman_client: SyncAhrimanClient, mocker: MockerFixture) -> None:
"""
must skip login if no user set
"""
requests_mock = mocker.patch("requests.Session.request")
ahriman_client._login(requests.Session())
ahriman_client.on_session_creation(requests.Session())
requests_mock.assert_not_called()
def test_login_url(ahriman_client: SyncAhrimanClient) -> None:
"""
must generate login url correctly
"""
assert ahriman_client._login_url().startswith(ahriman_client.address)
assert ahriman_client._login_url().endswith("/api/v1/login")

View File

@@ -4,6 +4,7 @@ import requests
from pytest_mock import MockerFixture
from unittest.mock import MagicMock, call as MockCall
from ahriman.core.alpm.remote import AUR
from ahriman.core.configuration import Configuration
from ahriman.core.http import SyncHttpClient
@@ -33,12 +34,29 @@ def test_init_auth_empty() -> None:
assert SyncHttpClient().auth is None
def test_session() -> None:
def test_session(mocker: MockerFixture) -> None:
"""
must generate valid session
"""
on_session_creation_mock = mocker.patch("ahriman.core.http.sync_http_client.SyncHttpClient.on_session_creation")
session = SyncHttpClient().session
assert "User-Agent" in session.headers
on_session_creation_mock.assert_called_once_with(pytest.helpers.anyvar(int))
def test_retry_policy() -> None:
"""
must set retry policy
"""
SyncHttpClient.retry = SyncHttpClient.retry_policy(1, 2.0)
AUR.retry = AUR.retry_policy(3, 4.0)
assert SyncHttpClient.retry.connect == 1
assert SyncHttpClient.retry.backoff_factor == 2.0
assert AUR.retry.connect == 3
assert AUR.retry.backoff_factor == 4.0
def test_exception_response_text() -> None:
@@ -60,6 +78,18 @@ def test_exception_response_text_empty() -> None:
assert SyncHttpClient.exception_response_text(exception) == ""
def test_adapters() -> None:
"""
must create adapters with retry policy
"""
client = SyncHttpClient()
adapters = client.adapters()
assert "http://" in adapters
assert "https://" in adapters
assert all(adapter.max_retries == client.retry for adapter in adapters.values())
def test_make_request(mocker: MockerFixture) -> None:
"""
must make HTTP request
@@ -158,3 +188,11 @@ def test_make_request_session() -> None:
session_mock.request.assert_called_once_with(
"GET", "url", params=None, data=None, headers=None, files=None, json=None,
stream=None, auth=None, timeout=client.timeout)
def test_on_session_creation() -> None:
"""
must do nothing on start
"""
client = SyncHttpClient()
client.on_session_creation(client.session)

View File

@@ -2,6 +2,7 @@ from pytest_mock import MockerFixture
from unittest.mock import call as MockCall
from ahriman.core.alpm.pacman import Pacman
from ahriman.core.alpm.remote import AUR
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.core.repository import Repository
@@ -13,13 +14,27 @@ def test_load(configuration: Configuration, database: SQLite, mocker: MockerFixt
"""
must correctly load instance
"""
globals_mock = mocker.patch("ahriman.core.repository.Repository._set_globals")
context_mock = mocker.patch("ahriman.core.repository.Repository._set_context")
_, repository_id = configuration.check_loaded()
Repository.load(repository_id, configuration, database, report=False)
globals_mock.assert_called_once_with(configuration)
context_mock.assert_called_once_with()
def test_set_globals(configuration: Configuration) -> None:
"""
must correctly set globals
"""
configuration.set_option("aur", "timeout", "42")
configuration.set_option("aur", "max_retries", "10")
Repository._set_globals(configuration)
assert AUR.timeout == 42
assert AUR.retry.connect == 10
def test_set_context(configuration: Configuration, database: SQLite, mocker: MockerFixture) -> None:
"""
must set context variables