Telegram Bot Access Control: From PyTest to Real Users

Testing Telegram Bot Access Control: From Theory to Real Messages
The moment arrived when local unit tests weren’t enough anymore. I’d built a Telegram bot with a new access management system—commands like /manage add and /manage remove to toggle private mode in group chats—but seeing green checkmarks in PyTest doesn’t mean your bot actually works when real users send real messages. Time for integration testing in the wild.
The Challenge: Making Sure Privacy Actually Works
The system seemed solid on paper. When a chat owner runs /manage add, the bot records the chat ID in a managed_chats table and enters private mode—ignoring everyone except the owner. Run /manage remove, and suddenly the bot talks to everyone again. I’d written middleware in permission_check.py to enforce this, plus handlers for /recall and /remember to manage chat memory. But would it actually work?
I spun up the bot locally with python telegram_main.py and started actual testing. First test: my own account sends /manage add. The bot should write to the database and activate private mode. Message sent, response received. ✅ Second test: I ask a friend to send a regular message from a different Telegram account. The middleware should silently drop it. My friend messages. The bot says nothing. ✅ Final test: I send /manage remove to unlock access, and my friend tries again. This time the bot responds normally. ✅
What Real-World Testing Revealed
Integration testing with actual Telegram exposed something unit tests missed: timing matters in async systems. When aiogram’s command handler processes /manage add, it needs to await the database insert before the middleware can see the new record. Without that explicit await, the permission check would fire before the transaction committed, creating a race condition where legitimate users got blocked.
The second surprise involved SQLite itself. When multiple async handlers write to the database simultaneously, you need explicit transaction management. SQLite doesn’t automatically propagate commits across concurrent operations—other handlers won’t see your changes until you explicitly call commit() or use a context manager. Working with aiosqlite meant being extra careful about this.
Beyond Tests: Documentation and Real-World Patterns
After validating everything worked end-to-end, I documented the entire flow. I added a section to README.md with /manage command examples, then created docs/CHAT_MANAGEMENT.md—a complete reference covering the ChatManager class architecture, database schema, and the full API for all access control methods.
This isn’t just about private bots anymore. The pattern works for any scenario where you need selective access: confidential assistants, moderated groups, or admin-only features in shared spaces.
The biggest lesson: unit tests and integration tests answer different questions. PyTest tells you if your logic is correct in isolation. Real Telegram testing tells you if your async handlers coordinate properly, if database transactions commit reliably, and if your business logic survives contact with reality. Both matter. Always prefer testing in production conditions before you declare victory.
😄 Why did the async function break up with the database? Because it couldn’t commit to the transaction without causing a race condition.
Metadata
- Session ID:
- grouped_C--projects-bot-social-publisher_20260209_1217
- Branch:
- main
- Dev Joke
- Почему Kotlin лучший друг разработчика? Потому что без него ничего не работает. С ним тоже, но хотя бы есть кого винить