Pytest-набор: валидаторы, БД, интеграционные флоу
tests/ — 19 тестов: validators (телефон/имя/формат), database (услуги, FAQ, вакансии, отклики, админы, лиды, подписчики), интеграция (полный флоу заявки, карьера игрока, анти-флуд не роняет бота, доступ к админке). Работают на временной БД, реальную не трогают. Запуск: python -m pytest tests/ -q Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
# Корень проекта в путь импорта
|
||||
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
if ROOT not in sys.path:
|
||||
sys.path.insert(0, ROOT)
|
||||
|
||||
from database import DatabaseManager
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def tmp_db(tmp_path):
|
||||
"""Чистая БД во временном файле."""
|
||||
return DatabaseManager(str(tmp_path / "test.db"))
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def app(tmp_path, monkeypatch):
|
||||
"""Экземпляр бота в режиме эмуляции с временной БД и перехватом отправки."""
|
||||
import bot
|
||||
bot.config.VK_TOKEN = "your_vk_community_token_here" # эмулятор, без сети
|
||||
test_db = bot.DatabaseManager(str(tmp_path / "bot.db"))
|
||||
monkeypatch.setattr(bot, "db", test_db)
|
||||
application = bot.VKBotApplication()
|
||||
sent = []
|
||||
application.send_message = (
|
||||
lambda peer_id, message, keyboard=None, attachment=None, template=None:
|
||||
sent.append({"to": peer_id, "msg": message, "kb": keyboard})
|
||||
)
|
||||
application._sent = sent
|
||||
return application
|
||||
@@ -0,0 +1,58 @@
|
||||
def test_services_crud(tmp_db):
|
||||
assert len(tmp_db.get_services()) >= 1 # сид
|
||||
nid = tmp_db.add_service("X-услуга", "100 ₽", "описание")
|
||||
assert nid in tmp_db.get_services()
|
||||
tmp_db.update_service(nid, "price", "200 ₽")
|
||||
assert tmp_db.get_services()[nid]["price"] == "200 ₽"
|
||||
tmp_db.delete_service(nid)
|
||||
assert nid not in tmp_db.get_services()
|
||||
|
||||
|
||||
def test_custom_faq(tmp_db):
|
||||
fid = tmp_db.add_custom_faq("привет", "Здравствуйте!")
|
||||
assert tmp_db.match_custom_faq("ну привет всем") == "Здравствуйте!"
|
||||
assert tmp_db.delete_custom_faq(fid)
|
||||
assert tmp_db.match_custom_faq("привет") is None
|
||||
|
||||
|
||||
def test_vacancy_options(tmp_db):
|
||||
assert len(tmp_db.get_vacancy_options("player")) == 3
|
||||
vid = tmp_db.add_vacancy_option("worker", "Аналитик")
|
||||
assert any(l == "Аналитик" for _, l in tmp_db.get_vacancy_options("worker"))
|
||||
tmp_db.delete_vacancy_option(vid)
|
||||
assert not any(l == "Аналитик" for _, l in tmp_db.get_vacancy_options("worker"))
|
||||
|
||||
|
||||
def test_applications_and_stats(tmp_db):
|
||||
tmp_db.add_application(1, "player", {
|
||||
"discipline": "CS2", "rank": "8", "role": "awp", "experience": "x", "contact": "@a"})
|
||||
st = tmp_db.get_application_stats()
|
||||
assert st["players"] == 1 and st["total"] == 1
|
||||
rows = tmp_db.get_recent_applications()
|
||||
assert tmp_db.set_application_status(rows[0]["id"], "принят")
|
||||
|
||||
|
||||
def test_admins(tmp_db):
|
||||
assert tmp_db.add_admin(555)
|
||||
assert 555 in tmp_db.get_admins()
|
||||
assert not tmp_db.add_admin(555) # дубль
|
||||
assert tmp_db.remove_admin(555)
|
||||
assert 555 not in tmp_db.get_admins()
|
||||
|
||||
|
||||
def test_leads_and_vac_data(tmp_db):
|
||||
tmp_db.add_lead(user_id=1, source="bot", name="A", phone="+79990001122",
|
||||
service="CS2", call_time="завтра", promo=None, marketing=True)
|
||||
s = tmp_db.get_lead_stats()
|
||||
assert s["total"] == 1 and s["from_bot"] == 1 and s["marketing_yes"] == 1
|
||||
tmp_db.set_vac_field(2, "discipline", "CS2")
|
||||
assert tmp_db.get_vac_data(2)["discipline"] == "CS2"
|
||||
tmp_db.clear_vac_data(2)
|
||||
assert tmp_db.get_vac_data(2) == {}
|
||||
|
||||
|
||||
def test_marketing_subscribers(tmp_db):
|
||||
tmp_db.update_user_field(10, "consent_2", "Согласен")
|
||||
tmp_db.update_user_field(11, "consent_2", "Не согласен")
|
||||
subs = tmp_db.get_marketing_subscribers()
|
||||
assert 10 in subs and 11 not in subs
|
||||
@@ -0,0 +1,48 @@
|
||||
import bot
|
||||
|
||||
|
||||
def _state(uid):
|
||||
return bot.db.get_user_data(uid)["state"]
|
||||
|
||||
|
||||
def test_full_lead_flow(app):
|
||||
u = 70001
|
||||
assert (app.handle_message(u, "Заполнить заявку 📝"), _state(u))[1] == "FORM_NAME"
|
||||
app.handle_message(u, "Иван"); assert _state(u) == "FORM_PHONE"
|
||||
app.handle_message(u, "89990001122"); assert _state(u) == "FORM_QUESTION"
|
||||
app.handle_message(u, "кс"); assert _state(u) == "FORM_CALLDATE"
|
||||
app.handle_message(u, "Завтра"); assert _state(u) == "FORM_CALLTIME"
|
||||
app.handle_message(u, "14:00"); assert _state(u) == "FORM_REVIEW"
|
||||
app.handle_message(u, "Да, всё верно! ✅"); assert _state(u) == "FORM_CONSENT_1"
|
||||
app.handle_message(u, "Согласен(а) и подтверждаю ✅"); assert _state(u) == "FORM_CONSENT_2"
|
||||
app.handle_message(u, "Без рассылки ➡️"); assert _state(u) == "FREE_CHAT"
|
||||
stats = bot.db.get_lead_stats()
|
||||
assert stats["total"] == 1 and stats["from_bot"] == 1
|
||||
|
||||
|
||||
def test_career_player_flow(app):
|
||||
u = 70003
|
||||
app.handle_message(u, "Карьера в ИКП 💼"); assert _state(u) == "VAC_CHOOSE"
|
||||
app.handle_message(u, "Игроком в состав 🎮"); assert _state(u) == "VAC_P_DISCIPLINE"
|
||||
app.handle_message(u, "Counter-Strike 2 🔫"); assert _state(u) == "VAC_P_RANK"
|
||||
|
||||
|
||||
def test_flood_does_not_crash(app):
|
||||
u = 70002
|
||||
for _ in range(25):
|
||||
app.handle_message(u, "спам")
|
||||
# бот не падает; пользователь остаётся в FREE_CHAT (флуд/бан только игнорят)
|
||||
assert _state(u) == "FREE_CHAT"
|
||||
|
||||
|
||||
def test_admin_panel_visible_to_super(app):
|
||||
admin = bot.config.SUPER_ADMIN_IDS[0]
|
||||
app._sent.clear()
|
||||
app.handle_message(admin, "Админка ⚙️")
|
||||
assert any("Статистика" in (s["kb"] or "") for s in app._sent)
|
||||
|
||||
|
||||
def test_non_admin_cannot_open_panel(app):
|
||||
app._sent.clear()
|
||||
app.handle_message(424242, "Админка ⚙️")
|
||||
assert not any("Админ-панель" in s["msg"] for s in app._sent)
|
||||
@@ -0,0 +1,34 @@
|
||||
from validators import normalize_phone, format_phone_display, is_valid_name
|
||||
|
||||
|
||||
def test_normalize_ru_8():
|
||||
assert normalize_phone("89991234567") == "+79991234567"
|
||||
|
||||
|
||||
def test_normalize_ru_formatted():
|
||||
assert normalize_phone("+7 (999) 123-45-67") == "+79991234567"
|
||||
|
||||
|
||||
def test_normalize_international():
|
||||
assert normalize_phone("+380501234567") == "+380501234567"
|
||||
|
||||
|
||||
def test_normalize_invalid():
|
||||
assert normalize_phone("12345") is None
|
||||
assert normalize_phone("abcdef") is None
|
||||
|
||||
|
||||
def test_format_phone_display():
|
||||
assert format_phone_display("+79991234567") == "+7 (999) 123-45-67"
|
||||
assert format_phone_display("+380501234567") == "+380501234567" # межд. как есть
|
||||
|
||||
|
||||
def test_name_valid():
|
||||
assert is_valid_name("Иван-Петров")
|
||||
assert is_valid_name("John")
|
||||
|
||||
|
||||
def test_name_invalid():
|
||||
assert not is_valid_name("Иван123")
|
||||
assert not is_valid_name("!!!")
|
||||
assert not is_valid_name("a")
|
||||
Reference in New Issue
Block a user