mirror of
https://github.com/Balshgit/gpt_chat_bot.git
synced 2025-09-11 22:30:41 +03:00
add queue tests
This commit is contained in:
parent
010a228380
commit
1ecf95631d
2
.github/workflows/check-lint.yml
vendored
2
.github/workflows/check-lint.yml
vendored
@ -3,7 +3,7 @@ name: lint
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches-ignore:
|
branches-ignore:
|
||||||
- test
|
- develop
|
||||||
tags-ignore:
|
tags-ignore:
|
||||||
- "*"
|
- "*"
|
||||||
pull_request:
|
pull_request:
|
||||||
|
5
.github/workflows/poetry-test.yml
vendored
5
.github/workflows/poetry-test.yml
vendored
@ -3,13 +3,16 @@ name: test
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches-ignore:
|
branches-ignore:
|
||||||
- test
|
- develop
|
||||||
tags-ignore:
|
tags-ignore:
|
||||||
- "*"
|
- "*"
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- 'release/**'
|
- 'release/**'
|
||||||
|
|
||||||
|
env:
|
||||||
|
STAGE: runtests
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
42
README.md
42
README.md
@ -1,20 +1,20 @@
|
|||||||
# MosGotTrans bot
|
# Chat gpt bot
|
||||||
Бот для получения расписания конкретных автобусов для конкретных остановок
|
Бот для запросов в chatgpt
|
||||||
|
|
||||||
Использует **Selenium** для парсинга сайта "яндекс карты"
|
Использует **Selenium** и API chatgpt для запросов
|
||||||
|
|
||||||
## Install & Update
|
## Install & Update
|
||||||
|
|
||||||
install service
|
install service
|
||||||
|
|
||||||
sudo cp scripts/mosgortrans.service /etc/systemd/system
|
sudo cp scripts/chat-gpt.service /etc/systemd/system
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd ~/PycharmProjects/mosgortrans
|
cd ~/PycharmProjects/chat_gpt_bot
|
||||||
sudo systemctl stop healthcheck_bot.service
|
sudo systemctl stop chat_gpt_bot.service
|
||||||
git pull balshgit master
|
git pull balshgit main
|
||||||
udo rsync -a --delete --progress ~/mosgortrans/* /opt/mosgortrans/ --exclude .git
|
sudo rsync -a --delete --progress ~/PycharmProjects/chat_gpt_bot/* /opt/chat_gpt_bot/ --exclude .git
|
||||||
sudo systemctl start healthcheck_bot.service
|
sudo systemctl start chat_gpt_bot.service
|
||||||
```
|
```
|
||||||
|
|
||||||
## Local start
|
## Local start
|
||||||
@ -22,6 +22,10 @@ sudo systemctl start healthcheck_bot.service
|
|||||||
python main.py
|
python main.py
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```shell
|
||||||
|
poetry run uvicorn --host 0.0.0.0 --factory app.main:create_app --port 8000 --reload --reload-dir=app --reload-dir=settings
|
||||||
|
```
|
||||||
|
|
||||||
- set `START_WITH_WEBHOOK` to blank
|
- set `START_WITH_WEBHOOK` to blank
|
||||||
|
|
||||||
## Delete or set webhook manually
|
## Delete or set webhook manually
|
||||||
@ -33,26 +37,24 @@ methods:
|
|||||||
- set
|
- set
|
||||||
|
|
||||||
|
|
||||||
## Local development clean:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
killall geckodriver
|
|
||||||
killall firefox
|
|
||||||
killall python
|
|
||||||
```
|
|
||||||
|
|
||||||
## Tests
|
## Tests
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd tests
|
poetry run pytest
|
||||||
SELENOIDTEST=1 docker-compose run test-bot python -m pytest tests/bot/test_bot_selenoid.py::test_selenoid_text -vv
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Docs
|
||||||
|
Docs can be found at
|
||||||
|
|
||||||
|
- {domain}/{url_prefix}/{api_prefix}/docs
|
||||||
|
- {domain}/{url_prefix}/{api_prefix}/redoc
|
||||||
|
|
||||||
|
on local start can be found at http://localhost/gpt/api/docs
|
||||||
|
|
||||||
## Help article
|
## Help article
|
||||||
|
|
||||||
[Пишем асинхронного Телеграм-бота](https://habr.com/ru/company/kts/blog/598575/)
|
[Пишем асинхронного Телеграм-бота](https://habr.com/ru/company/kts/blog/598575/)
|
||||||
|
|
||||||
[fast_api_aiogram](https://programtalk.com/vs4/python/daya0576/he-weather-bot/telegram_bot/dependencies.py/)
|
|
||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
|
@ -19,10 +19,11 @@ async def healthcheck() -> ORJSONResponse:
|
|||||||
|
|
||||||
|
|
||||||
@router.post(
|
@router.post(
|
||||||
f"/{settings.bot_webhook_url}",
|
f"/{settings.TELEGRAM_API_TOKEN}",
|
||||||
name="system:process_bot_updates",
|
name="system:process_bot_updates",
|
||||||
status_code=status.HTTP_202_ACCEPTED,
|
status_code=status.HTTP_202_ACCEPTED,
|
||||||
summary="process bot updates",
|
summary="process bot updates",
|
||||||
|
include_in_schema=False,
|
||||||
)
|
)
|
||||||
async def process_bot_updates(request: Request) -> ORJSONResponse:
|
async def process_bot_updates(request: Request) -> ORJSONResponse:
|
||||||
await request.app.state.queue.put_updates_on_queue(request)
|
await request.app.state.queue.put_updates_on_queue(request)
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import os
|
import os
|
||||||
import time
|
|
||||||
from asyncio import Queue, sleep
|
from asyncio import Queue, sleep
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from functools import cached_property
|
from functools import cached_property
|
||||||
@ -10,18 +9,17 @@ from fastapi import Request, Response
|
|||||||
from telegram import Update
|
from telegram import Update
|
||||||
from telegram.ext import Application, CommandHandler, ContextTypes
|
from telegram.ext import Application, CommandHandler, ContextTypes
|
||||||
|
|
||||||
from app.constants import API_PREFIX
|
from settings.config import AppSettings
|
||||||
from settings.config import Settings
|
|
||||||
|
|
||||||
|
|
||||||
class BotApplication:
|
class BotApplication:
|
||||||
def __init__(self, settings: Settings, start_with_webhook: bool = False) -> None:
|
def __init__(self, settings: AppSettings) -> None:
|
||||||
self.application: Application = ( # type: ignore
|
self.application: Application = ( # type: ignore
|
||||||
Application.builder().token(token=settings.TELEGRAM_API_TOKEN).build()
|
Application.builder().token(token=settings.TELEGRAM_API_TOKEN).build()
|
||||||
)
|
)
|
||||||
self.add_handlers()
|
self.add_handlers()
|
||||||
self.settings = settings
|
self.settings = settings
|
||||||
self.start_with_webhook = start_with_webhook
|
self.start_with_webhook = settings.START_WITH_WEBHOOK
|
||||||
|
|
||||||
async def set_webhook(self) -> None:
|
async def set_webhook(self) -> None:
|
||||||
await self.application.initialize()
|
await self.application.initialize()
|
||||||
@ -39,11 +37,7 @@ class BotApplication:
|
|||||||
await update.message.reply_text(
|
await update.message.reply_text(
|
||||||
"Help!",
|
"Help!",
|
||||||
disable_notification=True,
|
disable_notification=True,
|
||||||
api_kwargs={
|
api_kwargs={"text": "Hello World"},
|
||||||
"text": "Hello World",
|
|
||||||
"date": int(time.time()) + 30,
|
|
||||||
"schedule_date": int(time.time()) + 30,
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -60,25 +54,20 @@ class BotApplication:
|
|||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def webhook_url(self) -> str:
|
def webhook_url(self) -> str:
|
||||||
return os.path.join(
|
return os.path.join(self.settings.DOMAIN.strip("/"), self.settings.bot_webhook_url.strip("/"))
|
||||||
self.settings.WEBHOOK_HOST.strip("/"),
|
|
||||||
API_PREFIX.strip("/"),
|
|
||||||
self.settings.URL_PREFIX.strip("/"),
|
|
||||||
self.settings.TELEGRAM_API_TOKEN.strip("/"),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class BotQueue:
|
class BotQueue:
|
||||||
bot_app: Application # type: ignore[type-arg]
|
bot_app: BotApplication
|
||||||
queue: Queue = asyncio.Queue() # type: ignore[type-arg]
|
queue: Queue = asyncio.Queue() # type: ignore[type-arg]
|
||||||
|
|
||||||
async def put_updates_on_queue(self, request: Request) -> Response:
|
async def put_updates_on_queue(self, request: Request) -> Response:
|
||||||
"""
|
"""
|
||||||
Listen {URL_PREFIX}/{TELEGRAM_WEB_TOKEN} path and proxy post request to bot
|
Listen /{URL_PREFIX}/{API_PREFIX}/{TELEGRAM_WEB_TOKEN} path and proxy post request to bot
|
||||||
"""
|
"""
|
||||||
data = await request.json()
|
data = await request.json()
|
||||||
tg_update = Update.de_json(data=data, bot=self.bot_app.bot)
|
tg_update = Update.de_json(data=data, bot=self.bot_app.application.bot)
|
||||||
self.queue.put_nowait(tg_update)
|
self.queue.put_nowait(tg_update)
|
||||||
|
|
||||||
return Response(status_code=HTTPStatus.ACCEPTED)
|
return Response(status_code=HTTPStatus.ACCEPTED)
|
||||||
@ -86,6 +75,5 @@ class BotQueue:
|
|||||||
async def get_updates_from_queue(self) -> None:
|
async def get_updates_from_queue(self) -> None:
|
||||||
while True:
|
while True:
|
||||||
update = await self.queue.get()
|
update = await self.queue.get()
|
||||||
print(update)
|
await self.bot_app.application.process_update(update)
|
||||||
await self.bot_app.process_update(update)
|
|
||||||
await sleep(0)
|
await sleep(0)
|
||||||
|
20
app/main.py
20
app/main.py
@ -8,7 +8,7 @@ from loguru import logger
|
|||||||
|
|
||||||
from app.core.bot import BotApplication, BotQueue
|
from app.core.bot import BotApplication, BotQueue
|
||||||
from app.routers import api_router
|
from app.routers import api_router
|
||||||
from settings.config import Settings, get_settings
|
from settings.config import AppSettings, get_settings
|
||||||
|
|
||||||
logger.remove()
|
logger.remove()
|
||||||
logger.add(
|
logger.add(
|
||||||
@ -20,18 +20,18 @@ logger.add(
|
|||||||
|
|
||||||
|
|
||||||
class Application:
|
class Application:
|
||||||
def __init__(self, settings: Settings, bot_app: BotApplication) -> None:
|
def __init__(self, settings: AppSettings, bot_app: BotApplication) -> None:
|
||||||
self.app = FastAPI(
|
self.app = FastAPI(
|
||||||
title="Health check bot",
|
title="Chat gpt bot",
|
||||||
description="Bot which check all services are working",
|
description="Bot for proxy to chat gpt in telegram",
|
||||||
version="0.0.3",
|
version="0.0.3",
|
||||||
docs_url=f"{settings.URL_PREFIX}/docs",
|
docs_url="/" + "/".join([settings.api_prefix.strip("/"), "docs"]),
|
||||||
redoc_url=f"{settings.URL_PREFIX}/redocs",
|
redoc_url="/" + "/".join([settings.api_prefix.strip("/"), "redocs"]),
|
||||||
openapi_url=f"{settings.URL_PREFIX}/api/openapi.json",
|
openapi_url="/" + "/".join([settings.api_prefix.strip("/"), "openapi.json"]),
|
||||||
default_response_class=UJSONResponse,
|
default_response_class=UJSONResponse,
|
||||||
)
|
)
|
||||||
self.app.state.settings = settings
|
self.app.state.settings = settings
|
||||||
self.app.state.queue = BotQueue(bot_app=bot_app.application)
|
self.app.state.queue = BotQueue(bot_app=bot_app)
|
||||||
self.bot_app = bot_app
|
self.bot_app = bot_app
|
||||||
|
|
||||||
self.app.include_router(api_router)
|
self.app.include_router(api_router)
|
||||||
@ -58,9 +58,9 @@ class Application:
|
|||||||
await asyncio.gather(self.bot_app.delete_webhook(), self.bot_app.shutdown())
|
await asyncio.gather(self.bot_app.delete_webhook(), self.bot_app.shutdown())
|
||||||
|
|
||||||
|
|
||||||
def create_app(settings: Settings | None = None) -> FastAPI:
|
def create_app(settings: AppSettings | None = None) -> FastAPI:
|
||||||
settings = settings or get_settings()
|
settings = settings or get_settings()
|
||||||
bot_app = BotApplication(settings=settings, start_with_webhook=settings.START_WITH_WEBHOOK)
|
bot_app = BotApplication(settings=settings)
|
||||||
|
|
||||||
return Application(settings=settings, bot_app=bot_app).fastapi_app
|
return Application(settings=settings, bot_app=bot_app).fastapi_app
|
||||||
|
|
||||||
|
@ -2,9 +2,14 @@ from fastapi import APIRouter
|
|||||||
from fastapi.responses import ORJSONResponse
|
from fastapi.responses import ORJSONResponse
|
||||||
|
|
||||||
from app.api.system.controllers import router as system_router
|
from app.api.system.controllers import router as system_router
|
||||||
from app.constants import API_PREFIX
|
from settings.config import get_settings
|
||||||
|
|
||||||
api_router = APIRouter(prefix=API_PREFIX, default_response_class=ORJSONResponse)
|
settings = get_settings()
|
||||||
|
|
||||||
|
api_router = APIRouter(
|
||||||
|
prefix=settings.api_prefix,
|
||||||
|
default_response_class=ORJSONResponse,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
api_router.include_router(system_router, tags=["system"])
|
api_router.include_router(system_router, tags=["system"])
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=Healthcheck bot
|
Description=Chat-gpt bot
|
||||||
Wants=network-online.target
|
Wants=network-online.target
|
||||||
After=network-online.target
|
After=network-online.target
|
||||||
[Service]
|
[Service]
|
||||||
Restart=always
|
Restart=always
|
||||||
WorkingDirectory=/opt/healthcheck_bot
|
WorkingDirectory=/opt/chat_gpt_bot
|
||||||
ExecStart=/usr/local/bin/docker-compose -f /opt/healthcheck_bot/docker-compose.yml up
|
ExecStart=bash -c "docker compose -f /opt/chat_gpt_bot/docker-compose.yml up"
|
||||||
ExecStop=/usr/local/bin/docker-compose -f /opt/healthcheck_bot/docker-compose.yml down
|
ExecStop=bash -c "docker compose -f /opt/chat_gpt_bot/docker-compose.yml down"
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
@ -3,8 +3,19 @@ STAGE="runtests"
|
|||||||
APP_HOST="0.0.0.0"
|
APP_HOST="0.0.0.0"
|
||||||
APP_PORT="8000"
|
APP_PORT="8000"
|
||||||
|
|
||||||
POSTGRES_HOST="postgres"
|
USER="web"
|
||||||
POSTGRES_PORT="5432"
|
|
||||||
POSTGRES_DB="relevancer"
|
TELEGRAM_API_TOKEN="123456789:AABBCCDDEEFFaabbccddeeff-1234567890"
|
||||||
POSTGRES_USER="user"
|
|
||||||
POSTGRES_PASSWORD="postgrespwd"
|
# webhook settings
|
||||||
|
DOMAIN="http://localhost"
|
||||||
|
URL_PREFIX=
|
||||||
|
|
||||||
|
# set to true to start with webhook. Else bot will start on polling method
|
||||||
|
START_WITH_WEBHOOK="true"
|
||||||
|
|
||||||
|
# quantity of workers for uvicorn
|
||||||
|
WORKERS_COUNT=1
|
||||||
|
# Enable uvicorn reloading
|
||||||
|
RELOAD="true"
|
||||||
|
DEBUG="true"
|
||||||
|
@ -1 +1,21 @@
|
|||||||
STAGE="runtests"
|
STAGE="runtests"
|
||||||
|
|
||||||
|
APP_HOST="0.0.0.0"
|
||||||
|
APP_PORT="8000"
|
||||||
|
|
||||||
|
USER="web"
|
||||||
|
|
||||||
|
TELEGRAM_API_TOKEN="123456789:AABBCCDDEEFFaabbccddeeff-1234567890"
|
||||||
|
|
||||||
|
# webhook settings
|
||||||
|
DOMAIN="http://localhost"
|
||||||
|
URL_PREFIX=
|
||||||
|
|
||||||
|
# set to true to start with webhook. Else bot will start on polling method
|
||||||
|
START_WITH_WEBHOOK="true"
|
||||||
|
|
||||||
|
# quantity of workers for uvicorn
|
||||||
|
WORKERS_COUNT=1
|
||||||
|
# Enable uvicorn reloading
|
||||||
|
RELOAD="true"
|
||||||
|
DEBUG="true"
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
APP_HOST="0.0.0.0"
|
|
||||||
APP_PORT="8000"
|
|
||||||
|
|
||||||
POSTGRES_HOST="postgres"
|
|
||||||
POSTGRES_PORT="5432"
|
|
||||||
POSTGRES_DB="relevancer"
|
|
||||||
POSTGRES_USER="user"
|
|
||||||
POSTGRES_PASSWORD="postgrespwd"
|
|
@ -8,7 +8,7 @@ USER="web"
|
|||||||
TELEGRAM_API_TOKEN="123456789:AABBCCDDEEFFaabbccddeeff-1234567890"
|
TELEGRAM_API_TOKEN="123456789:AABBCCDDEEFFaabbccddeeff-1234567890"
|
||||||
|
|
||||||
# webhook settings
|
# webhook settings
|
||||||
WEBHOOK_HOST="https://mydomain.com"
|
DOMAIN="https://mydomain.com"
|
||||||
URL_PREFIX="/gpt"
|
URL_PREFIX="/gpt"
|
||||||
|
|
||||||
# set to true to start with webhook. Else bot will start on polling method
|
# set to true to start with webhook. Else bot will start on polling method
|
||||||
|
@ -5,6 +5,8 @@ from pathlib import Path
|
|||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from pydantic_settings import BaseSettings
|
from pydantic_settings import BaseSettings
|
||||||
|
|
||||||
|
from app.constants import API_PREFIX
|
||||||
|
|
||||||
BASE_DIR = Path(__file__).parent.parent
|
BASE_DIR = Path(__file__).parent.parent
|
||||||
SHARED_DIR = BASE_DIR.resolve().joinpath("shared")
|
SHARED_DIR = BASE_DIR.resolve().joinpath("shared")
|
||||||
SHARED_DIR.mkdir(exist_ok=True)
|
SHARED_DIR.mkdir(exist_ok=True)
|
||||||
@ -23,10 +25,10 @@ if environ.get("STAGE") == "runtests":
|
|||||||
load_dotenv(env_path, override=True)
|
load_dotenv(env_path, override=True)
|
||||||
|
|
||||||
|
|
||||||
class Settings(BaseSettings):
|
class AppSettings(BaseSettings):
|
||||||
"""Application settings."""
|
"""Application settings."""
|
||||||
|
|
||||||
PROJECT_NAME: str = "healthcheck bot"
|
PROJECT_NAME: str = "chat gpt bot"
|
||||||
APP_HOST: str = "0.0.0.0"
|
APP_HOST: str = "0.0.0.0"
|
||||||
APP_PORT: int = 8000
|
APP_PORT: int = 8000
|
||||||
STAGE: str = "dev"
|
STAGE: str = "dev"
|
||||||
@ -35,7 +37,7 @@ class Settings(BaseSettings):
|
|||||||
TELEGRAM_API_TOKEN: str = "123456789:AABBCCDDEEFFaabbccddeeff-1234567890"
|
TELEGRAM_API_TOKEN: str = "123456789:AABBCCDDEEFFaabbccddeeff-1234567890"
|
||||||
# webhook settings
|
# webhook settings
|
||||||
START_WITH_WEBHOOK: bool = False
|
START_WITH_WEBHOOK: bool = False
|
||||||
WEBHOOK_HOST: str = "https://mydomain.com"
|
DOMAIN: str = "https://localhost"
|
||||||
URL_PREFIX: str = ""
|
URL_PREFIX: str = ""
|
||||||
|
|
||||||
# quantity of workers for uvicorn
|
# quantity of workers for uvicorn
|
||||||
@ -43,13 +45,19 @@ class Settings(BaseSettings):
|
|||||||
# Enable uvicorn reloading
|
# Enable uvicorn reloading
|
||||||
RELOAD: bool = False
|
RELOAD: bool = False
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def api_prefix(self) -> str:
|
||||||
|
if self.URL_PREFIX:
|
||||||
|
return "/" + "/".join([self.URL_PREFIX.strip("/"), API_PREFIX.strip("/")])
|
||||||
|
return API_PREFIX
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def bot_webhook_url(self) -> str:
|
def bot_webhook_url(self) -> str:
|
||||||
return "/" + self.TELEGRAM_API_TOKEN
|
return "/".join([self.api_prefix, self.TELEGRAM_API_TOKEN])
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
case_sensitive = True
|
case_sensitive = True
|
||||||
|
|
||||||
|
|
||||||
def get_settings() -> Settings:
|
def get_settings() -> AppSettings:
|
||||||
return Settings()
|
return AppSettings()
|
||||||
|
@ -17,11 +17,16 @@ from telegram.ext import Application, ApplicationBuilder, Defaults, ExtBot
|
|||||||
|
|
||||||
from app.core.bot import BotApplication
|
from app.core.bot import BotApplication
|
||||||
from app.main import Application as AppApplication
|
from app.main import Application as AppApplication
|
||||||
from settings.config import get_settings
|
from settings.config import AppSettings, get_settings
|
||||||
from tests.integration.bot.networking import NonchalantHttpxRequest
|
from tests.integration.bot.networking import NonchalantHttpxRequest
|
||||||
from tests.integration.factories.bot import BotInfoFactory
|
from tests.integration.factories.bot import BotInfoFactory
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="session")
|
||||||
|
def test_settings() -> AppSettings:
|
||||||
|
return get_settings()
|
||||||
|
|
||||||
|
|
||||||
class PytestExtBot(ExtBot): # type: ignore
|
class PytestExtBot(ExtBot): # type: ignore
|
||||||
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
@ -213,12 +218,13 @@ async def bot_application(bot_info: dict[str, Any]) -> AsyncGenerator[Any, None]
|
|||||||
|
|
||||||
|
|
||||||
@pytest_asyncio.fixture(scope="session")
|
@pytest_asyncio.fixture(scope="session")
|
||||||
async def main_application(bot_application: PytestApplication) -> FastAPI:
|
async def main_application(
|
||||||
settings = get_settings()
|
bot_application: PytestApplication, test_settings: AppSettings
|
||||||
bot_app = BotApplication(settings=settings)
|
) -> AsyncGenerator[FastAPI, None]:
|
||||||
|
bot_app = BotApplication(settings=test_settings)
|
||||||
bot_app.application = bot_application
|
bot_app.application = bot_application
|
||||||
fast_api_app = AppApplication(settings=settings, bot_app=bot_app).fastapi_app
|
fast_api_app = AppApplication(settings=test_settings, bot_app=bot_app).fastapi_app
|
||||||
return fast_api_app
|
yield fast_api_app
|
||||||
|
|
||||||
|
|
||||||
@pytest_asyncio.fixture()
|
@pytest_asyncio.fixture()
|
||||||
|
@ -99,3 +99,11 @@ async def send_webhook_message(
|
|||||||
data=payload, # type: ignore
|
data=payload, # type: ignore
|
||||||
headers=headers,
|
headers=headers,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class MockedRequest:
|
||||||
|
def __init__(self, data: dict[str, Any]) -> None:
|
||||||
|
self.data = data
|
||||||
|
|
||||||
|
async def json(self) -> dict[str, Any]:
|
||||||
|
return self.data
|
||||||
|
@ -1,6 +1,15 @@
|
|||||||
|
import asyncio
|
||||||
|
import time
|
||||||
|
from asyncio import AbstractEventLoop
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from httpx import AsyncClient
|
from httpx import AsyncClient
|
||||||
|
|
||||||
|
from app.core.bot import BotApplication, BotQueue
|
||||||
|
from app.main import Application
|
||||||
|
from tests.integration.bot.networking import MockedRequest
|
||||||
|
|
||||||
pytestmark = [
|
pytestmark = [
|
||||||
pytest.mark.asyncio,
|
pytest.mark.asyncio,
|
||||||
]
|
]
|
||||||
@ -10,3 +19,76 @@ async def test_bot_updates(rest_client: AsyncClient) -> None:
|
|||||||
response = await rest_client.get("/api/healthcheck")
|
response = await rest_client.get("/api/healthcheck")
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
|
async def test_bot_webhook_endpoint(
|
||||||
|
rest_client: AsyncClient,
|
||||||
|
) -> None:
|
||||||
|
response = await rest_client.post(
|
||||||
|
url="/api/123456789:AABBCCDDEEFFaabbccddeeff-1234567890",
|
||||||
|
json={
|
||||||
|
"update_id": 957250703,
|
||||||
|
"message": {
|
||||||
|
"message_id": 417070387,
|
||||||
|
"from": {
|
||||||
|
"id": 1000,
|
||||||
|
"is_bot": "false",
|
||||||
|
"first_name": "William",
|
||||||
|
"last_name": "Dalton",
|
||||||
|
"username": "bolshakovfortunat",
|
||||||
|
"language_code": "ru",
|
||||||
|
},
|
||||||
|
"chat": {
|
||||||
|
"id": 1,
|
||||||
|
"first_name": "Gabrielle",
|
||||||
|
"last_name": "Smith",
|
||||||
|
"username": "arefi_2019",
|
||||||
|
"type": "private",
|
||||||
|
},
|
||||||
|
"date": time.time(),
|
||||||
|
"text": "/chatid",
|
||||||
|
"entities": [{"type": "bot_command", "offset": 0, "length": 7}],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
assert response.status_code == 202
|
||||||
|
|
||||||
|
|
||||||
|
async def test_bot_queue(
|
||||||
|
bot: BotApplication,
|
||||||
|
bot_application: Any,
|
||||||
|
main_application: Application,
|
||||||
|
event_loop: AbstractEventLoop,
|
||||||
|
) -> None:
|
||||||
|
bot.application = bot_application
|
||||||
|
bot_queue = BotQueue(bot_app=bot)
|
||||||
|
event_loop.create_task(bot_queue.get_updates_from_queue())
|
||||||
|
mocked_request = MockedRequest(
|
||||||
|
{
|
||||||
|
"update_id": 957250703,
|
||||||
|
"message": {
|
||||||
|
"message_id": 417070387,
|
||||||
|
"from": {
|
||||||
|
"id": 1000,
|
||||||
|
"is_bot": "false",
|
||||||
|
"first_name": "William",
|
||||||
|
"last_name": "Dalton",
|
||||||
|
"username": "bolshakovfortunat",
|
||||||
|
"language_code": "ru",
|
||||||
|
},
|
||||||
|
"chat": {
|
||||||
|
"id": 1,
|
||||||
|
"first_name": "Gabrielle",
|
||||||
|
"last_name": "Smith",
|
||||||
|
"username": "arefi_2019",
|
||||||
|
"type": "private",
|
||||||
|
},
|
||||||
|
"date": time.time(),
|
||||||
|
"text": "/chatid",
|
||||||
|
"entities": [{"type": "bot_command", "offset": 0, "length": 7}],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
await bot_queue.put_updates_on_queue(mocked_request) # type: ignore
|
||||||
|
await asyncio.sleep(1)
|
||||||
|
assert bot_queue.queue.empty()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user