Files
evo-sync/tests/test_routes_invite.py
mguschin fc65e591b3 test: add test suite with 65 tests, 73% coverage
- Unit tests: password hashing, notification providers, webhook field parsing
- Integration tests: auth routes (register/login/confirm-email/logout),
  invite flow, Evotor webhooks (/user/create, /user/verify, /user/token),
  admin panel (access control, activate/suspend/delete/reset-password)
- conftest: SQLite in-memory engine, transactional sessions, factory-boy
  factories (UserFactory with UserRoleEnum variants)
- Fix bcrypt: replace passlib (broken on Python 3.14 + bcrypt 5.x) with
  direct bcrypt calls; drop passlib from requirements.txt
- Fix datetime.utcnow() deprecation across routes and tests
- Fix Jinja2 TemplateResponse signature (request as first positional arg)
- Add coverage config to pyproject.toml

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 12:27:42 +03:00

96 lines
3.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Integration tests for the Evotor invite flow (/invite)."""
import secrets
from datetime import datetime, timezone, timedelta
from unittest.mock import patch
import pytest
from web.models.user import User, UserStatusEnum
@pytest.mark.asyncio
async def test_invite_get_valid_token(client, override_db, user_factory):
token = secrets.token_urlsafe(32)
user = user_factory.create(
invite_token=token,
invite_expires=datetime.now(timezone.utc).replace(tzinfo=None) + timedelta(hours=48),
password_hash=None,
status=UserStatusEnum.pending,
is_email_confirmed=False,
)
resp = await client.get(f"/invite?token={token}")
assert resp.status_code == 200
assert "Завершение регистрации" in resp.text or "ЭВОСИНК" in resp.text
@pytest.mark.asyncio
async def test_invite_get_expired_token(client, user_factory):
token = secrets.token_urlsafe(32)
user_factory.create(
invite_token=token,
invite_expires=datetime.now(timezone.utc).replace(tzinfo=None) - timedelta(hours=1),
password_hash=None,
status=UserStatusEnum.pending,
)
resp = await client.get(f"/invite?token={token}")
assert resp.status_code == 200
assert "недействительна" in resp.text
@pytest.mark.asyncio
async def test_invite_get_bogus_token(client):
resp = await client.get("/invite?token=notexist")
assert resp.status_code == 200
assert "недействительна" in resp.text
@pytest.mark.asyncio
async def test_invite_post_activates_user(client, override_db, user_factory):
token = secrets.token_urlsafe(32)
user = user_factory.create(
email="invite@test.com",
phone="+79001119999",
invite_token=token,
invite_expires=datetime.now(timezone.utc).replace(tzinfo=None) + timedelta(hours=48),
password_hash=None,
status=UserStatusEnum.pending,
is_email_confirmed=False,
)
resp = await client.post(f"/invite?token={token}", data={
"first_name": "Петр",
"last_name": "Петров",
"email": "invite@test.com",
"phone": "+79001119999",
"password": "newpassword1",
"password_confirm": "newpassword1",
})
assert resp.status_code == 200
assert "активирован" in resp.text.lower()
override_db.refresh(user)
assert user.status == UserStatusEnum.active
assert user.is_email_confirmed is True
assert user.password_hash is not None
assert user.invite_token is None
@pytest.mark.asyncio
async def test_invite_post_password_mismatch(client, user_factory):
token = secrets.token_urlsafe(32)
user_factory.create(
invite_token=token,
invite_expires=datetime.now(timezone.utc).replace(tzinfo=None) + timedelta(hours=48),
password_hash=None,
status=UserStatusEnum.pending,
)
resp = await client.post(f"/invite?token={token}", data={
"first_name": "А",
"last_name": "Б",
"email": "x@test.com",
"phone": "+79001112233",
"password": "password123",
"password_confirm": "different",
})
assert resp.status_code == 200
assert "не совпадают" in resp.text