test(scrapers): поднять caplog-фильтры под error->warning штатных исходов
All checks were successful
CI Trade-In / backend-tests (pull_request) Successful in 8m2s
CI Trade-In / changes (pull_request) Successful in 10s
CI / changes (pull_request) Successful in 12s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
All checks were successful
CI Trade-In / backend-tests (pull_request) Successful in 8m2s
CI Trade-In / changes (pull_request) Successful in 10s
CI / changes (pull_request) Successful in 12s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
Ветка fix/3471-scraper-log-levels понизила error на warning для штатных исходов скрапинга (пустой/исчерпанный пул прокси, серия подтверждённых блоков площадки) -- 7 тестов фильтровали caplog по ERROR и падали на пустом списке. Поправлен только уровень фильтра/set_level, содержательные assert'ы (streak vs ratio, отсутствие qrator/ip_rate_limited литералов, различимость текстов "исчерпан" и "пуст") не менялись. В test_exhausted_and_empty_pool_log_texts_are_distinct оба сценария (пустой пул и fail-closed) теперь на одном уровне (warning) -- тест адаптирован проверять различимость по тексту, а не по уровню. Refs #3471 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JY6iWDnGDthdvsMWgK1BMG
This commit is contained in:
parent
70b1419cde
commit
6608fd5c70
4 changed files with 27 additions and 14 deletions
|
|
@ -420,8 +420,10 @@ def test_all_candidates_banned_raises_pool_exhausted_not_env_fallback(
|
|||
assert exc_info.value.pool_total == 2
|
||||
assert exc_info.value.banned_for_source == 2
|
||||
assert exc_info.value.unhealthy_or_disabled == 0
|
||||
errors = [rec for rec in caplog.records if rec.levelname == "ERROR"]
|
||||
assert any("fail-closed" in rec.message.lower() for rec in errors)
|
||||
# fail-closed без здорового узла — штатный исход скрапинга, а не инцидент;
|
||||
# понижено до warning, чтобы не шуметь в GlitchTip (было logger.error).
|
||||
warnings = [rec for rec in caplog.records if rec.levelname == "WARNING"]
|
||||
assert any("fail-closed" in rec.message.lower() for rec in warnings)
|
||||
# НЕ должно быть "обход пула" / "static-fallback" в логах — env не тронут.
|
||||
assert not any("static-fallback" in rec.message for rec in caplog.records)
|
||||
|
||||
|
|
@ -429,8 +431,10 @@ def test_all_candidates_banned_raises_pool_exhausted_not_env_fallback(
|
|||
def test_exhausted_and_empty_pool_log_texts_are_distinct(
|
||||
monkeypatch: pytest.MonkeyPatch, caplog: pytest.LogCaptureFixture
|
||||
) -> None:
|
||||
"""Регрессия на замечание ревью: "пуст" и "все отсеяны" — РАЗНЫЕ формулировки И
|
||||
разные уровни (WARNING vs ERROR), иначе их нельзя различить в логах/алертах."""
|
||||
"""Регрессия на замечание ревью: "пуст" и "все отсеяны" — РАЗНЫЕ формулировки,
|
||||
иначе их нельзя различить в логах/алертах. Оба сценария — штатный исход
|
||||
скрапинга, поэтому оба теперь warning (было WARNING vs ERROR), различимость
|
||||
держится на тексте, не на уровне."""
|
||||
now = datetime.now(UTC)
|
||||
|
||||
with caplog.at_level("WARNING"):
|
||||
|
|
@ -452,8 +456,8 @@ def test_exhausted_and_empty_pool_log_texts_are_distinct(
|
|||
exhausted_messages = {rec.levelname: rec.message for rec in caplog.records}
|
||||
|
||||
assert "ERROR" not in empty_pool_messages
|
||||
assert "ERROR" in exhausted_messages
|
||||
assert empty_pool_messages.get("WARNING") != exhausted_messages.get("ERROR")
|
||||
assert "ERROR" not in exhausted_messages
|
||||
assert empty_pool_messages.get("WARNING") != exhausted_messages.get("WARNING")
|
||||
|
||||
|
||||
def test_disabled_proxy_raises_pool_exhausted() -> None:
|
||||
|
|
|
|||
|
|
@ -348,7 +348,9 @@ async def test_backfill_abort_log_has_no_ip_rate_limited_literal(caplog: Any) ->
|
|||
patch(_RESOLVE_PROXY_URL, MagicMock(return_value="http://test-proxy.local:8080")),
|
||||
patch(_FETCH, mock_fetch),
|
||||
patch(_SLEEP, new_callable=AsyncMock),
|
||||
caplog.at_level("ERROR"),
|
||||
# серия подтверждённых блоков площадки -- штатный исход скрапинга, не
|
||||
# инцидент; понижено до warning, чтобы не шуметь в GlitchTip.
|
||||
caplog.at_level("WARNING"),
|
||||
):
|
||||
await run_avito_detail_backfill(
|
||||
db, run_id=3, params={"batch_size": 10, "budget_sec": 3600, "max_consecutive_blocks": 5}
|
||||
|
|
@ -1354,7 +1356,9 @@ async def test_backfill_ratio_abort_log_names_the_ratio_not_the_streak(caplog: A
|
|||
patch(_FETCH, mock_fetch),
|
||||
patch(_SAVE, return_value=True),
|
||||
patch(_SLEEP, new_callable=AsyncMock),
|
||||
caplog.at_level("ERROR"),
|
||||
# ABORT по доле блоков -- штатный исход скрапинга, не инцидент; понижено
|
||||
# до warning, чтобы не шуметь в GlitchTip.
|
||||
caplog.at_level("WARNING"),
|
||||
):
|
||||
result = await run_avito_detail_backfill(
|
||||
db, run_id=110, params={"batch_size": total, "budget_sec": 3600}
|
||||
|
|
@ -1396,7 +1400,9 @@ async def test_backfill_safety_net_abort_log_names_the_streak(caplog: Any) -> No
|
|||
patch(_FETCH, mock_fetch),
|
||||
patch(_SAVE, return_value=True),
|
||||
patch(_SLEEP, new_callable=AsyncMock),
|
||||
caplog.at_level("ERROR"),
|
||||
# ABORT по safety-net серии -- штатный исход скрапинга, не инцидент;
|
||||
# понижено до warning, чтобы не шуметь в GlitchTip.
|
||||
caplog.at_level("WARNING"),
|
||||
):
|
||||
result = await run_avito_detail_backfill(
|
||||
db, run_id=111, params={"batch_size": total, "budget_sec": 3600}
|
||||
|
|
|
|||
|
|
@ -287,7 +287,9 @@ async def test_backfill_abort_log_has_no_qrator_literal(caplog: pytest.LogCaptur
|
|||
patch(_BROWSER_FETCHER, mock_bf_cls),
|
||||
patch(_FETCH, mock_fetch),
|
||||
patch(_SLEEP, new_callable=AsyncMock),
|
||||
caplog.at_level("ERROR"),
|
||||
# серия подтверждённых блоков площадки -- штатный исход скрапинга, не
|
||||
# инцидент; понижено до warning, чтобы не шуметь в GlitchTip.
|
||||
caplog.at_level("WARNING"),
|
||||
):
|
||||
await run_domclick_detail_backfill(
|
||||
db,
|
||||
|
|
|
|||
|
|
@ -446,8 +446,9 @@ async def test_verify_session_pool_exhausted_returns_source_unavailable_with_err
|
|||
) -> None:
|
||||
"""#2825 fail-closed (#2616): пул scrape_proxies исчерпан для cian (все узлы
|
||||
забанены/нездоровы) — session.get НЕ вызывается (никуда не ходим без egress),
|
||||
возвращается VERIFY_SOURCE_UNAVAILABLE_SENTINEL, но с ERROR-логом (не warning,
|
||||
отдельным от обычного network-error пути) — явная деградация, а не проглатывание."""
|
||||
возвращается VERIFY_SOURCE_UNAVAILABLE_SENTINEL, с явным логом (не тихое
|
||||
проглатывание). Штатный исход скрапинга -- warning, не error (было error;
|
||||
понижено, чтобы не шуметь в GlitchTip)."""
|
||||
from app.services.proxy_egress import ProxyPoolExhaustedError
|
||||
|
||||
def _raise(source: str) -> str | None:
|
||||
|
|
@ -470,8 +471,8 @@ async def test_verify_session_pool_exhausted_returns_source_unavailable_with_err
|
|||
|
||||
assert result is VERIFY_SOURCE_UNAVAILABLE_SENTINEL
|
||||
mock_session.get.assert_not_called()
|
||||
errors = [rec for rec in caplog.records if rec.levelname == "ERROR"]
|
||||
assert any("пул прокси исчерпан" in rec.message.lower() for rec in errors)
|
||||
warnings = [rec for rec in caplog.records if rec.levelname == "WARNING"]
|
||||
assert any("пул прокси исчерпан" in rec.message.lower() for rec in warnings)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue