Compare commits
No commits in common. "4fe0538ea0c96d0cbd34de2edd6e3178f216ad9f" and "add974db6577f1a8907454dc9ee78454ad384953" have entirely different histories.
4fe0538ea0
...
add974db65
2 changed files with 0 additions and 73 deletions
|
|
@ -1,64 +0,0 @@
|
||||||
"""Классификация HTTP-статусов IMV: временное не должно становиться приговором.
|
|
||||||
|
|
||||||
Замер на проде 01.09.2026: 93 дома заперты в терминальном imv_status='error',
|
|
||||||
из них 19 — с причиной «HTTP 429». Rate-limit — канонический ВРЕМЕННЫЙ отказ,
|
|
||||||
и механика повтора для таких существует (transient_error + retry-lane), но 429
|
|
||||||
проваливался в общий 4xx-fall-through `_raise_for_status_categorized` и
|
|
||||||
приезжал в house_imv_backfill голым RuntimeError. Тот типизирует только
|
|
||||||
IMV*-исключения → дом получал 'error' и выпадал из повторных пакетов навсегда.
|
|
||||||
|
|
||||||
Тест держит ГРАНИЦУ классификации с обеих сторон: 429/408 — transient,
|
|
||||||
400 — по-прежнему НЕ transient (тело безликое «Bad Request», оснований считать
|
|
||||||
его временным нет; сделать 400 ретраебельным значило бы вечно долбить дома с
|
|
||||||
реально кривыми параметрами).
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
from scraper_kit.providers.avito.imv import (
|
|
||||||
IMVAuthError,
|
|
||||||
IMVTransientError,
|
|
||||||
_raise_for_status_categorized,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class _Resp:
|
|
||||||
def __init__(self, status: int, body: str = "") -> None:
|
|
||||||
self.status_code = status
|
|
||||||
self.text = body
|
|
||||||
|
|
||||||
def raise_for_status(self) -> None:
|
|
||||||
if self.status_code >= 400:
|
|
||||||
raise RuntimeError(f"HTTP {self.status_code}")
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("status", [429, 408])
|
|
||||||
def test_rate_limit_and_timeout_are_transient(status: int) -> None:
|
|
||||||
with pytest.raises(IMVTransientError):
|
|
||||||
_raise_for_status_categorized(_Resp(status), "imv-evaluate")
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("status", [500, 502, 503])
|
|
||||||
def test_5xx_stays_transient(status: int) -> None:
|
|
||||||
with pytest.raises(IMVTransientError):
|
|
||||||
_raise_for_status_categorized(_Resp(status), "imv-evaluate")
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("status", [401, 403])
|
|
||||||
def test_auth_stays_auth(status: int) -> None:
|
|
||||||
with pytest.raises(IMVAuthError):
|
|
||||||
_raise_for_status_categorized(_Resp(status), "imv-evaluate")
|
|
||||||
|
|
||||||
|
|
||||||
def test_400_is_not_transient() -> None:
|
|
||||||
# Обратная сторона границы: безликий Bad Request не становится
|
|
||||||
# ретраебельным — иначе дома с кривыми параметрами долбились бы вечно.
|
|
||||||
with pytest.raises(RuntimeError) as ei:
|
|
||||||
_raise_for_status_categorized(_Resp(400, '{"code":400}'), "imv-evaluate")
|
|
||||||
assert not isinstance(ei.value, IMVTransientError)
|
|
||||||
assert "400" in str(ei.value)
|
|
||||||
|
|
||||||
|
|
||||||
def test_2xx_passes_silently() -> None:
|
|
||||||
_raise_for_status_categorized(_Resp(200), "imv-evaluate")
|
|
||||||
|
|
@ -594,15 +594,6 @@ def _raise_for_status_categorized(resp: Any, context: str) -> None:
|
||||||
status = resp.status_code
|
status = resp.status_code
|
||||||
if status in (401, 403):
|
if status in (401, 403):
|
||||||
raise IMVAuthError(f"Avito {context}: HTTP {status} — auth rejected / quota exceeded")
|
raise IMVAuthError(f"Avito {context}: HTTP {status} — auth rejected / quota exceeded")
|
||||||
if status in (408, 429):
|
|
||||||
# Rate-limit и таймаут — канонические ВРЕМЕННЫЕ отказы, но до 01.09.2026
|
|
||||||
# они проваливались в общий 4xx-fall-through ниже и приезжали в
|
|
||||||
# house_imv_backfill голым RuntimeError. Тот classифицирует только
|
|
||||||
# типизированные IMV*-исключения, поэтому дом получал ТЕРМИНАЛЬНЫЙ
|
|
||||||
# imv_status='error' и выпадал из повторных пакетов навсегда.
|
|
||||||
# Замер на проде: 19 домов заперты в error с причиной «HTTP 429» —
|
|
||||||
# ретраебельный отказ стал вечным приговором.
|
|
||||||
raise IMVTransientError(f"Avito {context}: HTTP {status} — rate limited / timeout")
|
|
||||||
if status >= 500:
|
if status >= 500:
|
||||||
raise IMVTransientError(f"Avito {context}: HTTP {status} — transient server error")
|
raise IMVTransientError(f"Avito {context}: HTTP {status} — transient server error")
|
||||||
if status >= 400:
|
if status >= 400:
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue