arden scrape safety telemetry and polish run state UX
This commit is contained in:
parent
110e246a1c
commit
d7404abf18
33 changed files with 2074 additions and 96 deletions
107
tests/unit/test_run_safety.py
Normal file
107
tests/unit/test_run_safety.py
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
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
|
||||
Loading…
Add table
Add a link
Reference in a new issue