gendesign/tradein-mvp/backend/tests/services
bot-backend 5eadae1e95 fix(tradein/support): address deep-review H1/M1/M2/M3/M5/L1-L5 (web chat backend)
H1 (critical): reorder send_support_message — get_or_create_thread now runs
AFTER a successful Telegram send, not before. Prod runs a single uvicorn
process with no --workers on a sync SQLAlchemy engine sharing one event
loop; holding a row-lock across the Telegram call (which can legally take
minutes on 429/5xx worker-grade retries) risked freezing the entire API on
a second concurrent request from the same user. send_message now also
accepts explicit timeout/max_retries so the interactive endpoint uses a
bounded budget instead of inheriting the long-polling worker's retry policy.

M1: web_support_messages and tg_support_messages both gain a support_chat_id
column (188 migration for the already-applied tg_support_messages table;
187 edited in place since it hasn't shipped yet). bridge._handle_group_reply
now resolves BOTH the Telegram and web candidate under the CURRENT
support_chat_id and refuses delivery loudly (logger.error) if both match,
instead of silently preferring the Telegram path — closing a cross-table
misroute risk that would surface if the support group is ever recreated.

M2: a media reply to a web-mirrored message (including photos with a
caption) is now refused in full with an explicit notice back in the topic,
instead of silently delivering just the caption text or logging a WARNING
nobody sees.

M3: list_support_messages/get_support_unread/mark_support_read switched
from async def to def — their bodies are pure sync psycopg calls; running
them as async def executed blocking DB work directly on the event loop.

M5: list_messages now takes a bounded LIMIT (last N, chronological) so a
long-lived thread doesn't return its entire history on every poll.

L1-L4: warn when Telegram doesn't return a message_id (routing dead-end),
rate limit is peeked before send and only recorded on success (failed
attempts no longer burn the budget), SlidingWindowLimiter gained the same
empty-bucket cleanup as RateLimitMiddleware, and _require_username now
strips whitespace so a proxy-injected space can't fork a second thread.

L5: removed 187 from _manifest_applied.txt — the deploy pipeline tracks
applied migrations via _schema_migrations, not this file, and keeping an
unmerged migration name out of it preserves the option to rename before
merge without tripping the "can't rename applied migrations" test.
2026-07-26 23:33:09 +03:00
..
scrapers chore(tradein/scrapers): удалить весь legacy scrapers/ каталог — final E (#2397, #2277) 2026-07-04 15:58:15 +03:00
tgbot fix(tradein/support): address deep-review H1/M1/M2/M3/M5/L1-L5 (web chat backend) 2026-07-26 23:33:09 +03:00
__init__.py feat(tradein): cadastral reverse geocode via main backend cad_buildings (#492) 2026-05-24 07:31:12 +00:00
test_cadastral_reverse.py test(tradein): patch _geoportal_house_match in cadastral-before-yandex test 2026-06-19 18:06:28 +03:00
test_dadata.py fix(tradein/dadata): suggest-fallback для house_fias_id когда CLEAN выключен 2026-07-12 19:55:37 +03:00
test_estimator_precision.py feat(tradein): бейдж точности адреса (qc_geo) на карточке оценки (accuracy #1) 2026-05-28 15:37:33 +05:00
test_image_sanitizer.py fix(tradein/photos): защита от pixel-flood DoS в image_sanitizer 2026-07-12 22:12:12 +03:00
test_location_coef.py fix(tradein/location-coef): re-center coefficient band so a strong central location ~= 1.0 (LOW audit R2 #7a) (#2509) 2026-07-12 22:12:10 +00:00
test_proxy_pool.py feat(proxy-pool): pool service (acquire/release/health) + healthcheck scheduler task (#2162) 2026-07-02 16:41:39 +00:00
test_trade_in_pdf_dual_price.py fix(tradein/pdf): МЕРА branding + 4-page report, no empty pages (#2513) 2026-07-13 18:04:07 +00:00