107 lines
4.3 KiB
Python
107 lines
4.3 KiB
Python
from __future__ import annotations
|
|
|
|
from datetime import datetime, timedelta, timezone
|
|
|
|
from app.db.models import UserSetting
|
|
from app.services import run_safety
|
|
|
|
|
|
def test_apply_run_safety_outcome_triggers_blocked_cooldown() -> None:
|
|
settings = UserSetting(user_id=1, scrape_safety_state={})
|
|
now = datetime(2026, 2, 19, 20, 0, tzinfo=timezone.utc)
|
|
|
|
safety_state, reason = run_safety.apply_run_safety_outcome(
|
|
settings,
|
|
run_id=12,
|
|
blocked_failure_count=2,
|
|
network_failure_count=0,
|
|
blocked_failure_threshold=1,
|
|
network_failure_threshold=2,
|
|
blocked_cooldown_seconds=600,
|
|
network_cooldown_seconds=300,
|
|
now_utc=now,
|
|
)
|
|
|
|
assert reason == run_safety.COOLDOWN_REASON_BLOCKED_FAILURE_THRESHOLD
|
|
assert safety_state["cooldown_active"] is True
|
|
assert safety_state["cooldown_reason"] == run_safety.COOLDOWN_REASON_BLOCKED_FAILURE_THRESHOLD
|
|
assert safety_state["cooldown_remaining_seconds"] == 600
|
|
assert safety_state["counters"]["cooldown_entry_count"] == 1
|
|
assert safety_state["counters"]["last_blocked_failure_count"] == 2
|
|
assert safety_state["counters"]["last_evaluated_run_id"] == 12
|
|
|
|
|
|
def test_clear_expired_cooldown() -> None:
|
|
now = datetime(2026, 2, 19, 20, 0, tzinfo=timezone.utc)
|
|
settings = UserSetting(
|
|
user_id=1,
|
|
scrape_safety_state={"blocked_start_count": 3},
|
|
scrape_cooldown_until=now - timedelta(seconds=5),
|
|
scrape_cooldown_reason=run_safety.COOLDOWN_REASON_NETWORK_FAILURE_THRESHOLD,
|
|
)
|
|
|
|
changed = run_safety.clear_expired_cooldown(settings, now_utc=now)
|
|
safety_state = run_safety.get_safety_state_payload(settings, now_utc=now)
|
|
|
|
assert changed is True
|
|
assert safety_state["cooldown_active"] is False
|
|
assert safety_state["cooldown_reason"] is None
|
|
assert safety_state["counters"]["blocked_start_count"] == 3
|
|
|
|
|
|
def test_apply_run_safety_outcome_triggers_network_cooldown() -> None:
|
|
settings = UserSetting(user_id=1, scrape_safety_state={})
|
|
now = datetime(2026, 2, 19, 20, 0, tzinfo=timezone.utc)
|
|
|
|
safety_state, reason = run_safety.apply_run_safety_outcome(
|
|
settings,
|
|
run_id=21,
|
|
blocked_failure_count=0,
|
|
network_failure_count=3,
|
|
blocked_failure_threshold=2,
|
|
network_failure_threshold=1,
|
|
blocked_cooldown_seconds=900,
|
|
network_cooldown_seconds=300,
|
|
now_utc=now,
|
|
)
|
|
|
|
assert reason == run_safety.COOLDOWN_REASON_NETWORK_FAILURE_THRESHOLD
|
|
assert safety_state["cooldown_active"] is True
|
|
assert safety_state["cooldown_reason"] == run_safety.COOLDOWN_REASON_NETWORK_FAILURE_THRESHOLD
|
|
assert safety_state["cooldown_remaining_seconds"] == 300
|
|
assert safety_state["counters"]["cooldown_entry_count"] == 1
|
|
assert safety_state["counters"]["last_network_failure_count"] == 3
|
|
assert safety_state["counters"]["last_evaluated_run_id"] == 21
|
|
|
|
|
|
def test_register_cooldown_blocked_start_increments_counter() -> None:
|
|
now = datetime(2026, 2, 19, 20, 0, tzinfo=timezone.utc)
|
|
settings = UserSetting(
|
|
user_id=1,
|
|
scrape_safety_state={"blocked_start_count": 1},
|
|
scrape_cooldown_until=now + timedelta(minutes=5),
|
|
scrape_cooldown_reason=run_safety.COOLDOWN_REASON_BLOCKED_FAILURE_THRESHOLD,
|
|
)
|
|
|
|
safety_state = run_safety.register_cooldown_blocked_start(settings, now_utc=now)
|
|
|
|
assert safety_state["cooldown_active"] is True
|
|
assert safety_state["cooldown_reason"] == run_safety.COOLDOWN_REASON_BLOCKED_FAILURE_THRESHOLD
|
|
assert safety_state["counters"]["blocked_start_count"] == 2
|
|
|
|
|
|
def test_get_safety_event_context_contains_structured_fields() -> None:
|
|
now = datetime(2026, 2, 19, 20, 0, tzinfo=timezone.utc)
|
|
settings = UserSetting(
|
|
user_id=1,
|
|
scrape_safety_state={"cooldown_entry_count": 4},
|
|
scrape_cooldown_until=now + timedelta(minutes=10),
|
|
scrape_cooldown_reason=run_safety.COOLDOWN_REASON_BLOCKED_FAILURE_THRESHOLD,
|
|
)
|
|
|
|
event_context = run_safety.get_safety_event_context(settings, now_utc=now)
|
|
|
|
assert event_context["cooldown_active"] is True
|
|
assert event_context["cooldown_reason"] == run_safety.COOLDOWN_REASON_BLOCKED_FAILURE_THRESHOLD
|
|
assert int(event_context["cooldown_remaining_seconds"]) == 600
|
|
assert event_context["safety_counters"]["cooldown_entry_count"] == 4
|