fix(ptica): «объекта нет в БД» считается пропуском, а не сбоем (#2464) #2974

Merged
bot-backend merged 1 commit from fix/2464-catalog-skipped-counter into main 2026-08-20 11:59:40 +00:00
Collaborator

Пункт эпика #2464: domrf_catalog_object.py:426.

Дефект

stats["skipped"] объявлен в контракте, возвращается и печатается в лог — и никогда не увеличивается. Все исходы сваливаются в failed: WAF-блок, битый разбор, «UPDATE затронул 0 строк».

По такому счётчику нельзя отличить временную помеху от настоящей регрессии разбора, а сам он всегда показывает ноль.

Источник пропуска существовал

obj_id берутся из БД, но снимок мог смениться между выборкой и UPDATE'ом — тогда строки (obj_id, snapshot_date) уже нет. Ветка с логом «not in DB?» была на месте и возвращала False, попадая в failed.

Почему None, а не новый Literal

Красивее была бы сигнатура Literal["ok","skipped","failed"]. Но она ломает два существующих теста, чьи заглушки возвращают True — включая test_domrf_catalog_object_browsersession_throttle и тесты предохранителя из #2971.

Менять чужой тест ради красоты своей сигнатуры — плохая цена. None как третье состояние сохраняет смысл True/False, и это подтверждено прогоном: 531 passed, ни один существующий тест не тронут.

Это свойство закреплено отдельным контролем, чтобы следующая «починка» не сломала его молча.

Тест

Против origin/main:

skipped=0, failed=3 вместо skipped=2, failed=1   → падает
сумма счётчиков сходится с processed  → контроль, зелёный с обеих сторон
True/False сохраняют смысл            → контроль, зелёный с обеих сторон

Первый контроль ловит «починку», при которой пропуск считался бы дважды или терялся.

Прогоны

tests/services/scrapers + tests/workers   531 passed   rc=0
Пункт эпика #2464: `domrf_catalog_object.py:426`. ## Дефект `stats["skipped"]` объявлен в контракте, возвращается и печатается в лог — и **никогда не увеличивается**. Все исходы сваливаются в `failed`: WAF-блок, битый разбор, «UPDATE затронул 0 строк». По такому счётчику нельзя отличить временную помеху от настоящей регрессии разбора, а сам он всегда показывает ноль. ## Источник пропуска существовал `obj_id` берутся из БД, но снимок мог смениться между выборкой и `UPDATE`'ом — тогда строки `(obj_id, snapshot_date)` уже нет. Ветка с логом «not in DB?» была на месте и возвращала `False`, попадая в `failed`. ## Почему `None`, а не новый `Literal` Красивее была бы сигнатура `Literal["ok","skipped","failed"]`. Но она ломает два существующих теста, чьи заглушки возвращают `True` — включая `test_domrf_catalog_object_browsersession_throttle` и тесты предохранителя из #2971. Менять чужой тест ради красоты своей сигнатуры — плохая цена. `None` как третье состояние сохраняет смысл `True`/`False`, и это подтверждено прогоном: **531 passed**, ни один существующий тест не тронут. Это свойство закреплено отдельным контролем, чтобы следующая «починка» не сломала его молча. ## Тест Против `origin/main`: ``` skipped=0, failed=3 вместо skipped=2, failed=1 → падает сумма счётчиков сходится с processed → контроль, зелёный с обеих сторон True/False сохраняют смысл → контроль, зелёный с обеих сторон ``` Первый контроль ловит «починку», при которой пропуск считался бы дважды или терялся. ## Прогоны ``` tests/services/scrapers + tests/workers 531 passed rc=0 ```
bot-backend added 1 commit 2026-08-20 11:40:32 +00:00
fix(ptica): «объекта нет в БД» считается пропуском, а не сбоем (#2464)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / changes (pull_request) Successful in 9s
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 2m5s
CI / backend-tests (pull_request) Successful in 17m17s
80ca7a8a35
stats["skipped"] был объявлен в контракте, возвращался и печатался в лог — и
никогда не увеличивался. Все исходы сваливались в failed: и WAF-блок, и битый
разбор, и «UPDATE затронул 0 строк». По такому счётчику нельзя отличить временную
помеху от настоящей регрессии разбора, а сам он всегда показывал ноль.

Законный источник пропуска в коде БЫЛ: obj_id берутся из БД, но снимок мог
смениться между выборкой и UPDATE'ом — тогда строки (obj_id, snapshot_date) уже
нет. Ветка с логом «not in DB?» существовала и возвращала False, попадая в failed.

Третье состояние сделано через None, а не новым Literal, намеренно: прежние
True/False сохраняют смысл, поэтому существующие вызывающие и тесты не
переписываются. Проверено прогоном — 531 passed, включая
test_domrf_catalog_object_browsersession_throttle (fake возвращает True) и тесты
предохранителя из #2971, ни один не тронут. Это и был довод против Literal:
менять чужой тест ради своей правки — плохая цена за красоту сигнатуры.

Против origin/main:

  skipped=0, failed=3 вместо skipped=2, failed=1   → падает
  сумма счётчиков сходится с processed  — контроль, зелёный с обеих сторон
  True/False сохраняют смысл            — контроль, зелёный с обеих сторон

Первый контроль ловит «починку», при которой пропуск считался бы дважды или
терялся; второй фиксирует ровно то свойство, ради которого выбран None.

Прогоны: tests/services/scrapers + tests/workers — 531 passed rc=0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
bot-backend merged commit f7d4b5bccf into main 2026-08-20 11:59:40 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#2974
No description provided.