BorisovAI
Все публикации
Новая функцияC--projects-bot-social-publisherClaude Code

Когда unit-тесты лгут: боевые испытания Telegram-бота

Когда unit-тесты лгут: боевые испытания Telegram-бота

Telegram-бот на боевых испытаниях: когда unit-тесты не подстраховывают

Проект bot-social-publisher начинался просто. Полнофункциональный Telegram-бот с памятью, командами, интеграциями. Но вот на очередную спринт-планерку я заявил: добавим систему управления доступом. Идея казалась пустяковой — дать владельцам возможность приватизировать свои чаты, чтобы только они могли с ботом общаться. Типичный use case: персональный AI-ассистент или модератор в закрытой группе.

Теория была прекрасна. Я развернул ChatManager — специальный класс с методом is_allowed(), который проверяет, разрешена ли пользователю отправка сообщений в конкретный чат. Добавил миграцию SQLite для таблицы managed_chats, прошил middleware в aiogram, написал обработчики команд /manage add, /manage remove, /manage status, /manage list. Unit-тесты прошли с зелёным светом — pytest даже не чихнул. Документация пока отложена, но это же детали!

Потом наступил момент истины.

Запустил бота локально через python telegram_main.py, переключился в личный чат и отправил первую /manage add. Бот записал ID чата, переключился в режим приватности. Нормально! Попробовал отправить обычное сообщение — ответ пришёл. Открыл чат со своего второго аккаунта, отправил то же самое — тишина. Бот ничего не ответил. Перфект, middleware работает.

Но не всё было так гладко.

Первая проблема вылезла при быстрых командах подряд. В асинхронной архитектуре aiogram и aiosqlite есть коварная особенность: middleware может проверить разрешения раньше, чем транзакция успела закоммититься. Получилась гонка условий — бот получал /manage add, начинал записывать в БД, но его собственная система контроля доступа успевала выполнить проверку за доли секунды до того, как данные попали в таблицу. Казалось бы, логические ошибки не могут быть незаметны в коде, но тут они проявились только в полевых условиях.

Вторая проблема — SQLite при одновременной работе нескольких асинхронных обработчиков. Один handler записывал изменение в БД, а другой в это время проверял состояние — и видел старые данные, потому что commit() ещё не произошёл. Гарантировать консистентность мне помогли явные транзакции и аккуратная работа с await’ами.

Вот в чём прелесть интеграционного тестирования: ты отправляешь реальное сообщение через Telegram-серверы, оно проходит через webhook, пробегает весь стек middleware, обрабатывается обработчиком, записывается в БД и возвращается пользователю. Unit-тесты проверяют логику функции. Интеграционные тесты проверяют, работает ли всё это вместе в реальности. И оказалось, что между «работает в тесте» и «работает в реальности» огромная разница.

После всех боевых испытаний я заполнил чеклист: проверка импортов класса, валидация миграции, тестирование всех команд в Telegram, запуск полного набора pytest, документирование в docs/CHAT_MANAGEMENT.md с примерами и описанием архитектуры. Восемь пунктов — восемь потенциальных точек отказа, которые благополучно миновали.

Урок на будущее: когда работаешь с асинхронностью и базами данных, unit-тесты — это необходимо, но недостаточно. Реальный Telegram, реальные пользователи, реальная асинхронность покажут то, что никогда не отловить в тестовом окружении.

😄 Иногда мне кажется, что в облаке GCP ошибка при доступе просто уходит в облака, так что никто её не найдёт.

Метаданные

Session ID:
grouped_C--projects-bot-social-publisher_20260209_1219
Branch:
main
Dev Joke
GCP: решение проблемы, о существовании которой ты не знал, способом, который не понимаешь.

Оцените материал

0/1000