Тесты разбора площадок: дом Авито, выдачи Циана и Яндекса снова проверяются на сохранённых страницах #3580
No reviewers
Labels
No labels
Fable 5 ревью
GG-форсайт
admin
analytics
auth
automation
bug
business
chore
ci
compliance
data
data-moat
docs
duplicate
dx
enhancement
feedback/max
generative
needs-discussion
needs-human
observability
pause-bots
performance
priority/p0
priority/p1
priority/p2
priority/p3
scope/backend
scope/db
scope/devops
scope/frontend
scope/qa
scrapers
security
site-finder
stage/1
stage/2
status/blocked
status/done
status/needs-analysis
status/needs-fix
status/qa
status/ready
status/review
status/wip
tech-debt
tradein
ux
week ревью 1
wontfix
ИРД
вторичка
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: lekss361/gendesign#3580
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "fix/kit-parser-unit-tests"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
#2422: тесты kit-парсеров на сохранённых страницах (поглотил закрытый дубль #2408)
Что было
Golden-parity тесты сверяли kit-парсеры с legacy-копией и были удалены вместе с ней (#2419). Состояние на origin/main
85d455e3:parse_houses_state,_parse_house_page,_parse_reviews,_parse_development_pageне вызывал ни один тест. Проверялись только_normalize_house_typeи реэкспорт_unix_to_date._parse_serp_htmlвызывался только на заглушках вида<html>irrelevant</html>с подменённым state (test_cian_serp_offers_total_mismatch.py,test_captcha_vs_empty_detect.py). Поля лота не проверялись._entity_to_lotи_parse_gate_jsonне встречались ни в одном тесте._extract_rooms/area/floor_from_titleне упоминались в тестах.test_kit_avito_has_no_app_imports.avito_house_572538.html,avito_houses_sample.html,avito_novostroyka_12879380.html.cian_flat_330982715.htmlиdomclick_bff_offers_sample.jsonк этому моменту уже использовались.test_avito_detail_fields_3047.py,test_cian_views_parse.py,test_cian_bti_house_persist.py,test_domclick_sweep.py,scrapers/test_domclick_detail.py,scrapers/test_base_clamp_date.py. Их я не дублировал.Чем это опасно: правка извлечения поля в kit (рейтинг, цена, адрес, канонический url) проходила зелёной.
Что сделано
Изменения только в тестах. Код kit не менялся.
tests/scrapers/test_2422_kit_parsers_on_fixtures.py(19 тестов). Ожидания записаны значениями из самих страниц, второй реализации в роли эталона нет. Значения для Циана и Яндекса я сверил с сырыми полями фикстур отдельным скриптом, мимо парсера.reviews.filterEntries(#1789). В обеих карточках бейдж housePage приходит без оценки, так что без запасного пути рейтинг был бы None;reviews_next_page_url;removed_date=None;developmentPage. Проверены класс, тип дома, лифты, застройщик, пустойfullAddress, превращённый в None, и отсутствие рейтинга и года постройки.avito_serp_sample.html): цена, комнаты, площадь, этаж и этажность, дата изsortTimeStamp.cian_serp_ekb_2room_trimmed.html. Это живая выдача от 2026-09-17, урезанная до 4 offers из 28 и до ключей, которые читает парсер.userиuserIdубраны.deadline),decoration=fineс балконом, новостройка без улицы. На последней номер дома не выдумывается (#1773).totalOffers=13895и счётчики извлечения state.yandex_gate_ekb_{vtorichka,novostroyki}_trimmed.json. Это живой gate-API от 2026-09-17: 3+1 entity, у author убранagentName.roomsTotal, лимит в 5 фото, новостройка без оценки и потолка.tests/scrapers/test_2422_kit_no_app_imports.py. Тест обходит весьscraper_kit, а не только providers, и через AST ищетimport app/from app(включая ленивые импорты внутри функций). Чтобы тест не был зелёным по построению, он сначала проверяет, что обход дошёл доproviders/avito/houses.pyи нашёл больше 40 файлов.Почему тесты лежат не в
packages/scraper-kit/tests: CI (ci-tradein.yml) запускает pytest только изtradein-mvp/backend. Каталог внутри пакета никто бы не собирал. Для проверки:cd tradein-mvp/packages/scraper-kit && uv run python -m pytest -qвыдаёт «no tests ran», rc=5. Это тот же класс дефекта, что и каталог вне пайплайна в #2722.Судьба осиротевших фикстур: все три используются как вход новых тестов, удалять нечего.
Тесты
20 passed, rc=0.85d455e3:DATABASE_URL=… uv run python -m pytest tests/ -q -p no:cacheproviderдал6394 passed, 44 skipped, rc=0.uv run ruff check app tests: All checks passed.ruff format --checkпо новым файлам чистый.Фальсификация
Мутационный прогон: копия исходника в scratchpad, правка одной строки в kit, pytest новых файлов (rc берётся у самого pytest), восстановление, затем
diff -qпротив копии (все файлы идентичны). Каждая из 9 порч роняет хотя бы один тест:Замечено по ходу (не чинил, в тестах не закрепил)
_parse_ru_dateтребует год и возвращает None. Прод, чтение 2026-09-17: вhouse_reviewsу 69 из 188 строкrated_date IS NULL, и у всех 69 вraw_payload->>'rated'нет года. Для починки нужно выбрать правило вывода года и сделать backfill строк: правка разбора сама старые строки не перезапишет.locationпосле района. Offer 333611840:geo.addressсодержит «Юго-Западный жилрайон» и «ФорумПарк ЖК» сtype=location._format_addressвыкидывает всеlocation(задумано для области и города), и получается «р-н Верх-Исетский, 3».balconiesCount=0иloggiasCount=1получаетсяhas_balcony=False: лоджия учитывается только приbalconiesCount=None(offer 331661795). Неясно, задумано ли так.Приёмка на проде
Изменения только в тестах:
backend/tests/в.dockerignore, поведение на проде не меняется. Приёмка на проде: после мержа, не позже 2026-09-18, pre-merge гейтci-tradeinи post-merge test-job вdeploy-tradeinдолжны быть зелёными на коммите мержа, а в логе pytest должны быть оба файлаtests/scrapers/test_2422_*.Деплой: правка под
tradein-mvp/backend/**включает фильтрbackend, пересобирает backend-образ и пересоздаёт tradein-backend и tradein-scraper с drain до 5 минут. Перед мержем нужно проверитьSELECT count(*) FROM scrape_runs WHERE status='running'(задача «деплой убивает работающее»). Миграций нет.Closes #2422
🤖 Generated with Claude Code