chore: интерфесы бота

This commit is contained in:
protokey committed 2026-09-24 17:33:10 +04:00
1 parent 5f53cadf89
commit 6d322f36a9
42 files changed
+1731 -111

No files matched your search

+51 -25
View File
@@ -1,21 +1,14 @@
from types import SimpleNamespace
from unittest import mock
from django.test import TestCase
from django.test import TestCase, override_settings
from telebot import types
from . import interfaces
from .dispatcher import handle_update
from .interfaces import Interface, command, register, state, text
from .models import Bot, Chat, Message, User
class FakeApi:
def __init__(self):
self.sent = []
def send_message(self, chat_id, text, reply_markup=None, parse_mode=None):
self.sent.append((chat_id, text, reply_markup))
return SimpleNamespace(message_id=len(self.sent))
from .testing import FakeApi, message_update
@register
@@ -33,6 +26,15 @@ class EchoInterface(Interface):
ctx.state = 'await_name'
ctx.reply('name?')
@text('отмена', state='await_name')
def cancel_name(self, ctx):
ctx.state = ''
ctx.reply('cancelled')
@text('отмена')
def cancel(self, ctx):
ctx.reply('nothing to cancel')
@state('await_name')
def name(self, ctx):
ctx.data['name'] = ctx.text
@@ -50,19 +52,6 @@ class ChildInterface(EchoInterface):
ctx.reply('child')
def message_update(update_id, text, chat_type='private'):
return types.Update.de_json({
'update_id': update_id,
'message': {
'message_id': update_id,
'date': 0,
'text': text,
'chat': {'id': 42, 'type': chat_type, 'title': 'group' if chat_type != 'private' else None},
'from': {'id': 7, 'is_bot': False, 'first_name': 'Ann', 'username': 'ann'},
},
})
def member_update(update_id, status):
return types.Update.de_json({
'update_id': update_id,
@@ -83,7 +72,7 @@ class DispatcherTests(TestCase):
def send(self, text, update_id=1, **kwargs):
handle_update(self.api, self.bot, message_update(update_id, text, **kwargs))
return self.api.sent[-1][1] if self.api.sent else None
return self.api.last_text
def test_command_text_state_fallback(self):
self.assertEqual(self.send('/start'), 'menu')
@@ -92,13 +81,23 @@ class DispatcherTests(TestCase):
self.assertEqual(self.send('whatever'), 'echo whatever')
self.assertEqual(Chat.objects.get().data, {'name': 'Bob'})
def test_state_button_has_priority(self):
self.assertEqual(self.send('отмена'), 'nothing to cancel')
self.send('ask')
self.assertEqual(self.send('отмена'), 'cancelled')
self.assertEqual(Chat.objects.get().state, '')
def test_global_button_works_in_state(self):
self.send('ask')
self.assertEqual(self.send('меню'), 'menu')
def test_command_with_bot_name_and_args(self):
self.assertEqual(self.send('/START@my_bot payload'), 'menu')
def test_saves_user_chat_and_messages(self):
self.send('/start')
user = User.objects.get()
self.assertEqual((user.tg_id, user.username), (7, 'ann'))
self.assertEqual((user.tg_id, user.username), (7, 'user7'))
chat = Chat.objects.get()
self.assertEqual((chat.chat_id, chat.user, chat.type), (42, user, 'private'))
self.assertEqual(
@@ -128,6 +127,33 @@ class DispatcherTests(TestCase):
self.assertEqual(Chat.objects.count(), 1)
class PublishTests(TestCase):
def setUp(self):
self.bot = Bot.objects.create(token='1:test')
self.api = mock.Mock()
self.api.get_me.return_value = SimpleNamespace(username='my_bot', first_name='My')
def publish(self):
with mock.patch.object(Bot, 'api', return_value=self.api), \
mock.patch('Telegram.models.log_out_from_cloud') as log_out:
self.bot.publish()
return log_out
@override_settings(TELEGRAM_API_URL='http://127.0.0.1:8081')
def test_logs_out_from_cloud_once(self):
self.assertEqual(self.publish().call_count, 1)
self.bot.refresh_from_db()
self.assertTrue(self.bot.cloud_logged_out)
self.assertTrue(self.bot.is_active)
self.assertEqual(self.bot.username, 'my_bot')
self.assertEqual(self.publish().call_count, 0)
@override_settings(TELEGRAM_API_URL='')
def test_cloud_api_without_log_out(self):
self.assertEqual(self.publish().call_count, 0)
self.assertFalse(self.bot.cloud_logged_out)
class InterfaceRegistryTests(TestCase):
def test_registered_interfaces_are_choices(self):
self.assertIn((interfaces.key(EchoInterface), 'Echo'), interfaces.choices())