mirror of
https://github.com/Balshgit/gpt_chat_bot.git
synced 2025-12-16 21:20:39 +03:00
add more tests (#19)
This commit is contained in:
@@ -4,23 +4,25 @@ pytest framework. A common change is to allow monkeypatching of the class member
|
||||
enforcing slots in the subclasses."""
|
||||
import asyncio
|
||||
from asyncio import AbstractEventLoop
|
||||
from contextlib import contextmanager
|
||||
from datetime import tzinfo
|
||||
from typing import Any, AsyncGenerator
|
||||
from typing import Any, AsyncGenerator, Iterator
|
||||
|
||||
import pytest
|
||||
import pytest_asyncio
|
||||
from fastapi import FastAPI
|
||||
from httpx import AsyncClient
|
||||
import respx
|
||||
from httpx import AsyncClient, Response
|
||||
from pytest_asyncio.plugin import SubRequest
|
||||
from telegram import Bot, User
|
||||
from telegram.ext import Application, ApplicationBuilder, Defaults, ExtBot
|
||||
|
||||
from constants import CHAT_GPT_BASE_URI
|
||||
from core.bot import BotApplication
|
||||
from core.handlers import bot_event_handlers
|
||||
from main import Application as AppApplication
|
||||
from settings.config import AppSettings, get_settings
|
||||
from tests.integration.bot.networking import NonchalantHttpxRequest
|
||||
from tests.integration.factories.bot import BotInfoFactory
|
||||
from tests.integration.factories.bot import BotInfoFactory, BotUserFactory
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
@@ -123,6 +125,7 @@ def bot_info() -> dict[str, Any]:
|
||||
async def bot_application(bot_info: dict[str, Any]) -> AsyncGenerator[Any, None]:
|
||||
# We build a new bot each time so that we use `app` in a context manager without problems
|
||||
application = ApplicationBuilder().bot(make_bot(bot_info)).application_class(PytestApplication).build()
|
||||
await application.initialize()
|
||||
yield application
|
||||
if application.running:
|
||||
await application.stop()
|
||||
@@ -226,27 +229,41 @@ def provider_token(bot_info: dict[str, Any]) -> str:
|
||||
@pytest_asyncio.fixture(scope="session")
|
||||
async def main_application(
|
||||
bot_application: PytestApplication, test_settings: AppSettings
|
||||
) -> AsyncGenerator[FastAPI, None]:
|
||||
) -> AsyncGenerator[AppApplication, None]:
|
||||
bot_app = BotApplication(
|
||||
application=bot_application,
|
||||
settings=test_settings,
|
||||
handlers=bot_event_handlers.handlers,
|
||||
)
|
||||
fast_api_app = AppApplication(settings=test_settings, bot_app=bot_app).fastapi_app
|
||||
bot_app.application._initialized = True
|
||||
bot_app.application.bot = make_bot(BotInfoFactory())
|
||||
bot_app.application.bot._bot_user = BotUserFactory()
|
||||
fast_api_app = AppApplication(settings=test_settings, bot_app=bot_app)
|
||||
yield fast_api_app
|
||||
|
||||
|
||||
@pytest_asyncio.fixture()
|
||||
async def rest_client(
|
||||
main_application: FastAPI,
|
||||
main_application: AppApplication,
|
||||
) -> AsyncGenerator[AsyncClient, None]:
|
||||
"""
|
||||
Default http client. Use to test unauthorized requests, public endpoints
|
||||
or special authorization methods.
|
||||
"""
|
||||
async with AsyncClient(
|
||||
app=main_application,
|
||||
app=main_application.fastapi_app,
|
||||
base_url="http://test",
|
||||
headers={"Content-Type": "application/json"},
|
||||
) as client:
|
||||
yield client
|
||||
|
||||
|
||||
@contextmanager
|
||||
def mocked_ask_question_api(host: str) -> Iterator[respx.MockRouter]:
|
||||
with respx.mock(
|
||||
assert_all_mocked=True,
|
||||
assert_all_called=True,
|
||||
base_url=host,
|
||||
) as respx_mock:
|
||||
ask_question_route = respx_mock.post(url=CHAT_GPT_BASE_URI, name="ask_question")
|
||||
ask_question_route.return_value = Response(status_code=200, text="Привет! Как я могу помочь вам сегодня?")
|
||||
yield respx_mock
|
||||
|
||||
@@ -1,24 +1,30 @@
|
||||
import asyncio
|
||||
import time
|
||||
from asyncio import AbstractEventLoop
|
||||
from typing import Any
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
import telegram
|
||||
from assertpy import assert_that
|
||||
from faker import Faker
|
||||
from httpx import AsyncClient
|
||||
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
|
||||
|
||||
from constants import BotStagesEnum
|
||||
from core.bot import BotApplication, BotQueue
|
||||
from main import Application
|
||||
from settings.config import AppSettings
|
||||
from tests.integration.bot.conftest import mocked_ask_question_api
|
||||
from tests.integration.bot.networking import MockedRequest
|
||||
from tests.integration.factories.bot import (
|
||||
BotChatFactory,
|
||||
BotEntitleFactory,
|
||||
BotUserFactory,
|
||||
BotCallBackQueryFactory,
|
||||
BotMessageFactory,
|
||||
BotUpdateFactory,
|
||||
CallBackFactory,
|
||||
)
|
||||
|
||||
pytestmark = [
|
||||
pytest.mark.asyncio,
|
||||
pytest.mark.enable_socket,
|
||||
]
|
||||
|
||||
|
||||
@@ -34,10 +40,10 @@ async def test_bot_webhook_endpoint(
|
||||
rest_client: AsyncClient,
|
||||
main_application: Application,
|
||||
) -> None:
|
||||
bot_update = create_bot_update()
|
||||
bot_update = BotUpdateFactory(message=BotMessageFactory.create_instance(text="/help"))
|
||||
response = await rest_client.post(url="/api/123456789:AABBCCDDEEFFaabbccddeeff-1234567890", json=bot_update)
|
||||
assert response.status_code == 202
|
||||
update = await main_application.state._state["queue"].queue.get() # type: ignore[attr-defined]
|
||||
update = await main_application.fastapi_app.state._state["queue"].queue.get()
|
||||
update = update.to_dict()
|
||||
assert update["update_id"] == bot_update["update_id"]
|
||||
assert_that(update["message"]).is_equal_to(
|
||||
@@ -51,22 +57,124 @@ async def test_bot_queue(
|
||||
) -> None:
|
||||
bot_queue = BotQueue(bot_app=bot)
|
||||
event_loop.create_task(bot_queue.get_updates_from_queue())
|
||||
bot_update = create_bot_update()
|
||||
|
||||
bot_update = BotUpdateFactory(message=BotMessageFactory.create_instance(text="/help"))
|
||||
|
||||
mocked_request = MockedRequest(bot_update)
|
||||
await bot_queue.put_updates_on_queue(mocked_request) # type: ignore
|
||||
await asyncio.sleep(1)
|
||||
assert bot_queue.queue.empty()
|
||||
|
||||
|
||||
def create_bot_update() -> dict[str, Any]:
|
||||
bot_update: dict[str, Any] = {}
|
||||
bot_update["update_id"] = faker.random_int(min=10**8, max=10**9 - 1)
|
||||
bot_update["message"] = {
|
||||
"message_id": faker.random_int(min=10**8, max=10**9 - 1),
|
||||
"from": BotUserFactory()._asdict(),
|
||||
"chat": BotChatFactory()._asdict(),
|
||||
"date": time.time(),
|
||||
"text": "/chatid",
|
||||
"entities": [BotEntitleFactory()],
|
||||
}
|
||||
return bot_update
|
||||
async def test_help_command(
|
||||
main_application: Application,
|
||||
test_settings: AppSettings,
|
||||
) -> None:
|
||||
with mock.patch.object(
|
||||
telegram._bot.Bot, "send_message", return_value=lambda *args, **kwargs: (args, kwargs)
|
||||
) as mocked_send_message:
|
||||
bot_update = BotUpdateFactory(message=BotMessageFactory.create_instance(text="/help"))
|
||||
|
||||
await main_application.bot_app.application.process_update(
|
||||
update=Update.de_json(data=bot_update, bot=main_application.bot_app.bot)
|
||||
)
|
||||
|
||||
assert_that(mocked_send_message.call_args.kwargs).is_equal_to(
|
||||
{
|
||||
"text": "Help!",
|
||||
"api_kwargs": {"text": "Список основных команд:"},
|
||||
"chat_id": bot_update["message"]["chat"]["id"],
|
||||
"reply_markup": InlineKeyboardMarkup(
|
||||
inline_keyboard=(
|
||||
(
|
||||
InlineKeyboardButton(callback_data="about_me", text="Обо мне"),
|
||||
InlineKeyboardButton(callback_data="website", text="Веб версия"),
|
||||
),
|
||||
(
|
||||
InlineKeyboardButton(callback_data="help", text="Помощь"),
|
||||
InlineKeyboardButton(callback_data="about_bot", text="О боте"),
|
||||
),
|
||||
)
|
||||
),
|
||||
},
|
||||
include=["text", "api_kwargs", "chat_id", "reply_markup"],
|
||||
)
|
||||
|
||||
|
||||
async def test_about_me_callback_action(
|
||||
main_application: Application,
|
||||
test_settings: AppSettings,
|
||||
) -> None:
|
||||
with mock.patch.object(telegram._message.Message, "reply_text") as mocked_reply_text:
|
||||
bot_update = BotCallBackQueryFactory(
|
||||
message=BotMessageFactory.create_instance(text="Список основных команд:"),
|
||||
callback_query=CallBackFactory(data=BotStagesEnum.about_me),
|
||||
)
|
||||
|
||||
await main_application.bot_app.application.process_update(
|
||||
update=Update.de_json(data=bot_update, bot=main_application.bot_app.bot)
|
||||
)
|
||||
|
||||
assert mocked_reply_text.call_args.args == ("Автор бота: *Дмитрий Афанасьев*\n\nTg nickname: *Balshtg*",)
|
||||
assert mocked_reply_text.call_args.kwargs == {"parse_mode": "MarkdownV2"}
|
||||
|
||||
|
||||
async def test_about_bot_callback_action(
|
||||
main_application: Application,
|
||||
test_settings: AppSettings,
|
||||
) -> None:
|
||||
with mock.patch.object(telegram._message.Message, "reply_text") as mocked_reply_text:
|
||||
bot_update = BotCallBackQueryFactory(
|
||||
message=BotMessageFactory.create_instance(text="Список основных команд:"),
|
||||
callback_query=CallBackFactory(data=BotStagesEnum.about_bot),
|
||||
)
|
||||
|
||||
await main_application.bot_app.application.process_update(
|
||||
update=Update.de_json(data=bot_update, bot=main_application.bot_app.bot)
|
||||
)
|
||||
|
||||
assert mocked_reply_text.call_args.args == (
|
||||
"Бот использует бесплатную модель Chat-GPT3.5 для ответов на вопросы. Принимает запросы на разных языках. "
|
||||
"\n\nБот так же умеет переводить голосовые сообщения в текст. Просто пришлите голосовуху и получите поток "
|
||||
"сознания без запятых в виде текста",
|
||||
)
|
||||
assert mocked_reply_text.call_args.kwargs == {"parse_mode": "Markdown"}
|
||||
|
||||
|
||||
async def test_website_callback_action(
|
||||
main_application: Application,
|
||||
test_settings: AppSettings,
|
||||
) -> None:
|
||||
with mock.patch.object(telegram._message.Message, "reply_text") as mocked_reply_text:
|
||||
bot_update = BotCallBackQueryFactory(
|
||||
message=BotMessageFactory.create_instance(text="Список основных команд:"),
|
||||
callback_query=CallBackFactory(data=BotStagesEnum.website),
|
||||
)
|
||||
|
||||
await main_application.bot_app.application.process_update(
|
||||
update=Update.de_json(data=bot_update, bot=main_application.bot_app.bot)
|
||||
)
|
||||
|
||||
assert mocked_reply_text.call_args.args == ("Веб версия: http://localhost/chat/",)
|
||||
|
||||
|
||||
async def test_ask_question_action(
|
||||
main_application: Application,
|
||||
test_settings: AppSettings,
|
||||
) -> None:
|
||||
with mock.patch.object(
|
||||
telegram._bot.Bot, "send_message", return_value=lambda *args, **kwargs: (args, kwargs)
|
||||
) as mocked_send_message, mocked_ask_question_api(host=test_settings.GPT_BASE_HOST):
|
||||
bot_update = BotUpdateFactory(message=BotMessageFactory.create_instance(text="Привет!"))
|
||||
bot_update["message"].pop("entities")
|
||||
|
||||
await main_application.bot_app.application.process_update(
|
||||
update=Update.de_json(data=bot_update, bot=main_application.bot_app.bot)
|
||||
)
|
||||
assert_that(mocked_send_message.call_args.kwargs).is_equal_to(
|
||||
{
|
||||
"text": "Привет! Как я могу помочь вам сегодня?",
|
||||
"chat_id": bot_update["message"]["chat"]["id"],
|
||||
},
|
||||
include=["text", "chat_id"],
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user