"""Unit tests for retry backoff math.""" from datetime import UTC, datetime, timedelta import pytest from hypothesis import given from hypothesis import strategies as st from svcforge_core.domain.backoff import next_attempt_at T = datetime(2026, 7, 17, 12, 0, 0, tzinfo=UTC) def test_attempt_zero_full_jitter_is_base() -> None: assert next_attempt_at(0, now=T, rand=lambda: 1.0) == T + timedelta(seconds=2) def test_zero_jitter_returns_now() -> None: assert next_attempt_at(0, now=T, rand=lambda: 0.0) == T def test_negative_attempt_raises_value_error() -> None: with pytest.raises(ValueError, match="attempt"): next_attempt_at(-1, now=T) @given(attempt=st.integers(min_value=0, max_value=64), r=st.floats(min_value=0.0, max_value=1.0)) def test_delay_is_always_within_zero_and_cap(attempt: int, r: float) -> None: cap_s = 300.0 got = next_attempt_at(attempt, now=T, cap_s=cap_s, rand=lambda: r) delay = (got - T).total_seconds() assert 0.0 <= delay <= cap_s @given(attempt=st.integers(min_value=0, max_value=63)) def test_delay_is_non_decreasing_in_attempt(attempt: int) -> None: def ceiling_of(a: int) -> float: return (next_attempt_at(a, now=T, rand=lambda: 1.0) - T).total_seconds() assert ceiling_of(attempt) <= ceiling_of(attempt + 1)