gendesign/tradein-mvp/backend/tests/skip_allowlist.txt
bot-backend a8503d3a58
All checks were successful
CI Trade-In / backend-tests (pull_request) Successful in 5m9s
CI Trade-In / changes (pull_request) Successful in 9s
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 12s
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
tradein: потолок на запрос и на ожидание блокировки для движка БД (#3463)
На боевой БД `statement_timeout`, `lock_timeout` и
`idle_in_transaction_session_timeout` равны 0, а у движка `app/core/db.py` не
было `connect_args` вовсе. После #3444/#3449 шаги БД на пути `/estimate` идут
через обёртку, которая при отмене по бюджету ДОЖИДАЕТСЯ своего потока (иначе он
остаётся сиротой в общей `Session`) — ожидание верное, но его верхняя граница
равна длительности самого запроса, а у запроса границы не было. Один
`ACCESS EXCLUSIVE` на таблице → четыре повисших запроса → `_ESTIMATE_CONCURRENCY`
исчерпан → `/estimate` отдаёт 429 всем остальным.

Потолок ставится на КОННЕКТЕ (libpq `options`), а не в обёртке: таймаут в
обёртке вернул бы ровно ту сироту, ради которой писался #3449.

statement_timeout = 30 с: выше самого длинного ОБЪЯВЛЕННОГО бюджета `/estimate`
(20 с, `estimate_avito_imv_timeout_s`) в 1.5 раза и в 7 раз выше самого долгого
ЗАМЕРЕННОГО запроса через этот движок (4.27 с, `pg_stat_statements` на проде за
16 суток), но конечен. lock_timeout = 5 с: та же величина, что у миграций
проекта, и больше `deadlock_timeout` (1 с на проде).

`idle_in_transaction_session_timeout` намеренно не трогаем: тем же движком живёт
планировщик, а его свипы держат транзакцию открытой всё время внешнего HTTP
(замер: живая сессия `idle in transaction` 29 с).

Задачи планировщика проверены, а не предположены: у `listing_source_snapshot`
свой `SET LOCAL statement_timeout = 900000`, и тест доказывает, что `SET LOCAL`
ПЕРЕКРЫВАЕТ сессионный потолок и не течёт за свою транзакцию. Самая долгая
чисто-БД задача по `scrape_runs` за 14 суток укладывается в 9.7 с целиком;
единственный запрос длиннее 20 с на всей БД (`REFRESH MATERIALIZED VIEW
CONCURRENTLY`, 30.85 с) идёт мимо движка — по своему сырому psycopg-соединению.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-12 19:46:57 +05:00

146 lines
14 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Объявленные пропуски сьюта tradein-mvp/backend.
#
# Пропуск, которого здесь нет, роняет прогон (хук в tests/conftest.py). Смысл: не
# запретить пропуски, а запретить НЕЗАМЕТНЫЕ. Проверка, которая тихо перестала
# исполняться, со временем расходится с кодом, и узнают об этом ровно тогда,
# когда на неё понадобилось опереться (#2722, #2729, #2740).
#
# Формат: один nodeid в строке, без «[параметров]»; `#` — комментарий.
# Прежде чем добавить строку, ответь: почему эту проверку нельзя выполнить ЗДЕСЬ
# и где она выполняется вместо этого. Нет ответа — это не пропуск, а дыра.
#
# Список — НАДмножество сред: в CI Postgres есть (ci-tradein.yml поднимает
# сервис-контейнер и собирает схему из backend/data/sql/), на ноутбуке без БД —
# нет. Запись, чей пропуск не случился, безвредна; пропуск без записи — нет.
# ── Реальный рендер PDF: нужны native-либы WeasyPrint (Pango/cairo/GObject) ────
# Где выполняется: внутри prod-образа — runner-stage tradein-mvp/backend/Dockerfile
# ставит libcairo2 + libpango-1.0-0 + libpangoft2-1.0-0:
# docker exec tradein-backend python -m pytest -q -m pdf_render tests/test_pdf_real_render.py
# На голом ubuntu-latest в ci-tradein.yml этих либ нет (apt-шага в job'е нет).
tests/test_pdf_real_render.py
# ── Тесты против живой Postgres ───────────────────────────────────────────────
# В CI они ИДУТ (postgres-сервис в ci-tradein.yml, #2745) — записи ниже нужны
# только для прогона на машине без БД. Именно эта группа разъехалась со схемой,
# пока не бежала нигде: #2740 (houses.url стал NOT NULL) и #2744
# (test_gar_flats_loader падал до первого утверждения). Поднять локально:
# docker run -d -p 5432:5432 -e POSTGRES_DB=tradein -e POSTGRES_USER=tradein \
# -e POSTGRES_PASSWORD=tradein postgis/postgis:16-3.4
# psql ... -f backend/data/sql/*.sql # порядок как в deploy-tradein.yml
tests/tasks/test_backfill_house_coords_from_listings.py::test_real_transfers_agreeing_coords_skips_scattered_and_is_idempotent
tests/tasks/test_cadastral_geo_match.py::test_real_knn_nearest_within_threshold_picked
tests/test_audit_api.py::test_real_accounts_and_analytics_aggregate_inserted_rows
tests/test_gar_flats_loader.py::test_upsert_and_canon_match_populates_gar_flat_count
tests/test_house_dedup_merge.py::test_real_canon_clusterkey_and_geo_guard_merge_semantics
tests/test_house_dedup_merge.py::test_real_fias_pass_cross_guard_and_identity_carryover
tests/test_house_dedup_merge.py::test_real_fias_pass_ignores_geo_guard
tests/test_house_dedup_merge.py::test_real_merge_is_reversible_via_journal
tests/test_house_dedup_merge.py::test_real_merge_repoints_dedups_deletes_and_is_idempotent
tests/test_user_events.py::test_real_record_event_inserts_row
# Приватность/ретеншн (#2547) — тот же `_live_session()`. Приехали в main
# параллельно с самим списком, поэтому первым же прогоном deploy-лэйна хук их и
# поймал: 4 проверки не исполнились и не были объявлены. Проверено против живой
# БД (схема из backend/data/sql/ по 233 включительно) — 29 passed, тесты живы.
tests/test_data_erasure.py::test_real_erase_by_phone_does_not_match_different_number
tests/test_data_erasure.py::test_real_erase_by_phone_finds_differently_formatted_number
tests/test_data_erasure.py::test_real_erase_by_phone_finds_ru_trunk_prefix_variant
tests/test_purge_expired_trade_in_data.py::test_real_purge_deletes_only_anonymous_expired_estimates
# Payments retention (PR #2754), deep-review MEDIUM finding 2026-08-06 — тот же
# `_live_session()`. Проверяют предполётную проверку purge против реального
# retain_until/payments состояния (здоровая оплаченная строка не поднимает
# тревогу и не блокирует джобу), не только SQL-текст.
tests/test_purge_expired_trade_in_data.py::test_real_preflight_ignores_healthy_paid_row_flags_only_anomaly
tests/test_purge_expired_trade_in_data.py::test_real_purge_not_wedged_by_healthy_paid_row
# Диагноз оборванного прогона (#2764) — тот же `_live_session()`. Проверяет, что
# дефолт mark_banned ('unknown') проходит CHECK-констрейнт из миграции 234:
# на мок-лэйне (deploy-tradein.yml, DSN-заглушка) констрейнта нет вовсе.
tests/test_2764_ban_kind_no_default.py::test_real_default_ban_kind_survives_the_check_constraint
# Гейт номеров миграций (#2683) сверяется с origin/main и точкой ветвления. Где
# git-эталона нет — прогон внутри prod-образа, экспорт исходников без .git —
# проверять не с чем, и тест это ГОВОРИТ вслух вместо тихого зелёного.
# В CI пропуска не бывает: при CI/GITHUB_ACTIONS та же ветка делает pytest.fail
# (отсутствие эталона в пайплайне — сломанный гейт, а не «нечего проверять»),
# а ci-tradein.yml/deploy-tradein.yml берут checkout с fetch-depth: 0 — при нём
# checkout сам приносит refs/remotes/origin/*, отдельный git fetch не нужен и
# из job-контейнера всё равно не проходит (run 6977, connection refused).
tests/test_migration_numbering.py::test_applied_migration_is_not_renamed_or_deleted
tests/test_migration_numbering.py::test_new_migration_takes_a_free_number
# Повтор застрявших transient_error (#2674, PR #2843) — тот же `_live_session()`.
# Проверяют ВЫБОРКУ очереди на живой схеме (кто попал в пакет прогона), а не текст
# SQL: на мок-лэйне deploy-tradein.yml БД нет вовсе. В ci-tradein.yml они бегут
# по-настоящему (Postgres + схема из data/sql) — там прогон и был красным на
# origin/main и зелёным на ветке. Статическая половина файла
# (test_retry_queue_is_not_bound_to_only_status) БД не требует и идёт в обоих лэйнах.
tests/test_house_imv_retry_stuck.py::test_explicit_only_status_still_takes_exhausted_houses
tests/test_house_imv_retry_stuck.py::test_stuck_transient_house_returns_to_the_queue_by_itself
tests/test_house_imv_retry_stuck.py::test_transient_attempts_counter_only_counts_transient
# MAJOR-1 fix, coverage probe (#2894, независимый ревью) — тот же `live_session` fixture
# (self-skip через `_live_db_available()`, живёт только при реальном Postgres DSN).
# Проверяет, что novostroyki-строка / geo_precision='city'-строка / price_rub=0-строка
# физически не попадают в когорту (не только SQL-текст, который проверяется отдельным
# статическим тестом test_cohort_sql_excludes_* в этом же файле, идущим на обоих лэйнах).
tests/test_coverage_probe_endpoint.py::test_major1_cohort_excludes_novostroyki_and_city_precision_live
# MAJOR-2 поведенческий пин (повторная проверка #2894) — та же `live_session` fixture.
# Ловит мутацию «убрать FILTER у percentile_cont, оставив у count(*)», которую
# текстовый тест test_max_age_outlier_days_passed_to_sql пропускал (подстрока
# `days_on_market <= :max_age_days` встречается в SQL дважды). На мок-лэйне
# (deploy-tradein.yml, DSN-заглушка) реальной БД нет — self-skip.
tests/test_coverage_probe_endpoint.py::test_max_age_outlier_excluded_from_median_live
# #2992 — гейт неизменной строки в апсертах listings / listing_sources. Шесть live-тестов
# идут через реальный save_listings / upsert_listing_source на живом Postgres и меряют,
# переписана ли строка, по ctid (save_listings коммитит — транзакционные счётчики
# бесполезны). Без БД — skip; в CI Trade-In идут на postgres-сервисе (#2745). Свои
# строки t2992-* тесты удаляют явно в finally.
tests/test_2992_upsert_unchanged_gate.py::test_unchanged_rescrape_same_day_does_not_update_the_row
tests/test_2992_upsert_unchanged_gate.py::test_changed_price_still_updates
tests/test_2992_upsert_unchanged_gate.py::test_coalesce_backfill_still_updates
tests/test_2992_upsert_unchanged_gate.py::test_next_day_rescrape_updates_even_if_unchanged
tests/test_2992_upsert_unchanged_gate.py::test_skipped_row_still_yields_listing_id_for_downstream
tests/test_2992_upsert_unchanged_gate.py::test_listing_sources_unchanged_rescrape_same_day_does_not_update
# #3036 — house-поля с детальной страницы Авито → houses (fill-only). Live-тест ходит в
# настоящую БД (в CI она есть, #2745), локально без TEST_DATABASE_URL пропускается. Строки
# t3036-* тест удаляет в finally.
tests/test_3036_detail_house_params_to_houses.py::test_live_fill_only_then_keep_then_unlinked_untouched
# PR-A LATERAL-поиск предшественника в поле переобхода (#2659 продолжение) — тот же
# `_live_session()`. Три live-теста вставляют синтетическую историю снимков (в т.ч.
# с дырами) в ОДНОЙ транзакции и проверяют результат JOIN LATERAL/equality-join
# прямым SELECT'ом в той же транзакции — cleanup через rollback (без commit), явный
# DELETE не нужен. Без БД — skip; в CI Trade-In идут на postgres-сервисе (#2745).
tests/test_revisit_floor_lateral_lookup.py::test_lateral_ignores_gaps_and_matches_dense_daily_history
tests/test_revisit_floor_lateral_lookup.py::test_equality_join_loses_gapped_pairs_and_gives_a_smaller_floor
tests/test_revisit_floor_lateral_lookup.py::test_missing_history_before_anchor_still_yields_no_floor_via_lateral
# Эрозия признаков продавца (#3063, PR #3067) — тот же `_live_session()`.
# Проверяют ПОВЕДЕНИЕ апсерта на живой схеме: что бедный re-scrape не обнуляет
# is_homeowner/is_pro_seller/phones/sale_type (COALESCE держит), и обратное — что
# настоящая смена признака всё ещё перезаписывает (COALESCE не сделал поля
# write-once). SQL-текст проверяется отдельно статической половиной того же файла
# (test_field_is_coalesce_protected_in_set + test_unchanged_gate_mirrors_the_set_clause),
# она идёт на ОБОИХ лэйнах. В ci-tradein.yml эти два бегут по-настоящему
# (postgres-сервис) — там прогон и был зелёным; в deploy-tradein.yml БД нет вовсе.
tests/test_3063_seller_fields_not_eroded.py::test_poor_rescrape_does_not_erase_seller_fields
tests/test_3063_seller_fields_not_eroded.py::test_real_change_still_overwrites
# Потолки БД на движке (#3463) — поведенческая половина: проверяют, что запрос
# длиннее потолка ОБРЫВАЕТСЯ (pg_sleep), что ожидание блокировки отваливается по
# lock_timeout, и что `SET LOCAL` задачи планировщика перекрывает сессионный
# потолок и не течёт за свою транзакцию. Всё это нельзя проверить без сервера:
# потолок применяет Postgres, а не питон. Статическая половина того же файла
# (test_engine_opens_connections_with_both_ceilings +
# test_ceilings_are_coherent_with_declared_estimate_budgets) идёт на ОБОИХ лэйнах
# и краснеет от снятия `connect_args` — проверено вручную 12.09. В ci-tradein.yml
# эти пять бегут по-настоящему (postgres-сервис, #2745).
tests/test_3463_db_timeouts.py::test_live_session_reports_both_ceilings
tests/test_3463_db_timeouts.py::test_statement_over_ceiling_is_cancelled_not_hung
tests/test_3463_db_timeouts.py::test_lock_wait_over_ceiling_is_aborted
tests/test_3463_db_timeouts.py::test_set_local_statement_timeout_overrides_session_ceiling
tests/test_3463_db_timeouts.py::test_set_local_is_scoped_to_its_transaction