183 lines
6.1 KiB
Python
183 lines
6.1 KiB
Python
|
|
"""Integration tests for auth routes (register / login / confirm-email / logout)."""
|
||
|
|
import secrets
|
||
|
|
from unittest.mock import patch
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
|
||
|
|
from web.models.user import User, UserStatusEnum
|
||
|
|
|
||
|
|
|
||
|
|
# ── /register ────────────────────────────────────────────────────────────────
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_register_get(client):
|
||
|
|
resp = await client.get("/register")
|
||
|
|
assert resp.status_code == 200
|
||
|
|
assert "Регистрация" in resp.text
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
@patch("web.routes.auth.send_email_task")
|
||
|
|
async def test_register_creates_pending_user(mock_task, client, override_db):
|
||
|
|
resp = await client.post("/register", data={
|
||
|
|
"first_name": "Иван",
|
||
|
|
"last_name": "Иванов",
|
||
|
|
"email": "ivan@test.com",
|
||
|
|
"phone": "+79001234567",
|
||
|
|
"password": "password123",
|
||
|
|
"password_confirm": "password123",
|
||
|
|
})
|
||
|
|
assert resp.status_code == 200
|
||
|
|
assert "Подтвердите" in resp.text
|
||
|
|
|
||
|
|
user = override_db.query(User).filter(User.email == "ivan@test.com").first()
|
||
|
|
assert user is not None
|
||
|
|
assert user.status == UserStatusEnum.pending
|
||
|
|
assert user.is_email_confirmed is False
|
||
|
|
assert user.email_confirm_token is not None
|
||
|
|
mock_task.delay.assert_called_once()
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
@patch("web.routes.auth.send_email_task")
|
||
|
|
async def test_register_duplicate_email(mock_task, client, active_user):
|
||
|
|
resp = await client.post("/register", data={
|
||
|
|
"first_name": "X",
|
||
|
|
"last_name": "Y",
|
||
|
|
"email": active_user.email,
|
||
|
|
"phone": "+79999999999",
|
||
|
|
"password": "password123",
|
||
|
|
"password_confirm": "password123",
|
||
|
|
})
|
||
|
|
assert resp.status_code == 200
|
||
|
|
assert "уже существует" in resp.text
|
||
|
|
mock_task.delay.assert_not_called()
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_register_password_mismatch(client):
|
||
|
|
resp = await client.post("/register", data={
|
||
|
|
"email": "new@test.com",
|
||
|
|
"phone": "+79000000001",
|
||
|
|
"password": "password123",
|
||
|
|
"password_confirm": "different",
|
||
|
|
})
|
||
|
|
assert resp.status_code == 200
|
||
|
|
assert "не совпадают" in resp.text
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_register_short_password(client):
|
||
|
|
resp = await client.post("/register", data={
|
||
|
|
"email": "new@test.com",
|
||
|
|
"phone": "+79000000002",
|
||
|
|
"password": "short",
|
||
|
|
"password_confirm": "short",
|
||
|
|
})
|
||
|
|
assert resp.status_code == 200
|
||
|
|
assert "минимум 8" in resp.text
|
||
|
|
|
||
|
|
|
||
|
|
# ── /confirm-email ────────────────────────────────────────────────────────────
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_confirm_email_valid_token(client, override_db, user_factory):
|
||
|
|
token = secrets.token_urlsafe(32)
|
||
|
|
user = user_factory.create(
|
||
|
|
is_email_confirmed=False,
|
||
|
|
email_confirm_token=token,
|
||
|
|
status=UserStatusEnum.pending,
|
||
|
|
)
|
||
|
|
resp = await client.get(f"/confirm-email?token={token}")
|
||
|
|
assert resp.status_code == 200
|
||
|
|
assert "подтвержден" in resp.text.lower()
|
||
|
|
|
||
|
|
override_db.refresh(user)
|
||
|
|
assert user.is_email_confirmed is True
|
||
|
|
assert user.status == UserStatusEnum.active
|
||
|
|
assert user.email_confirm_token is None
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_confirm_email_invalid_token(client):
|
||
|
|
resp = await client.get("/confirm-email?token=bogustoken")
|
||
|
|
assert resp.status_code == 200
|
||
|
|
assert "Ошибка" in resp.text
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_confirm_email_missing_token(client):
|
||
|
|
resp = await client.get("/confirm-email")
|
||
|
|
assert resp.status_code == 200
|
||
|
|
assert "Ошибка" in resp.text
|
||
|
|
|
||
|
|
|
||
|
|
# ── /login ────────────────────────────────────────────────────────────────────
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_login_get(client):
|
||
|
|
resp = await client.get("/login")
|
||
|
|
assert resp.status_code == 200
|
||
|
|
assert "Вход" in resp.text
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_login_success(client, active_user):
|
||
|
|
resp = await client.post("/login", data={
|
||
|
|
"email": active_user.email,
|
||
|
|
"password": "testpass123",
|
||
|
|
}, follow_redirects=False)
|
||
|
|
assert resp.status_code == 303
|
||
|
|
assert resp.headers["location"] == "/profile"
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_login_wrong_password(client, active_user):
|
||
|
|
resp = await client.post("/login", data={
|
||
|
|
"email": active_user.email,
|
||
|
|
"password": "wrongpassword",
|
||
|
|
})
|
||
|
|
assert resp.status_code == 200
|
||
|
|
assert "Неверный" in resp.text
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_login_unknown_email(client):
|
||
|
|
resp = await client.post("/login", data={
|
||
|
|
"email": "nobody@test.com",
|
||
|
|
"password": "testpass123",
|
||
|
|
})
|
||
|
|
assert resp.status_code == 200
|
||
|
|
assert "Неверный" in resp.text
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_login_suspended_user(client, user_factory):
|
||
|
|
user = user_factory.create(status=UserStatusEnum.suspended)
|
||
|
|
resp = await client.post("/login", data={
|
||
|
|
"email": user.email,
|
||
|
|
"password": "testpass123",
|
||
|
|
})
|
||
|
|
assert resp.status_code == 200
|
||
|
|
assert "заблокирован" in resp.text.lower()
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_login_unconfirmed_email(client, user_factory):
|
||
|
|
user = user_factory.create(is_email_confirmed=False, status=UserStatusEnum.pending)
|
||
|
|
resp = await client.post("/login", data={
|
||
|
|
"email": user.email,
|
||
|
|
"password": "testpass123",
|
||
|
|
})
|
||
|
|
assert resp.status_code == 200
|
||
|
|
assert "подтвердите" in resp.text.lower()
|
||
|
|
|
||
|
|
|
||
|
|
# ── /logout ───────────────────────────────────────────────────────────────────
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_logout_redirects(client):
|
||
|
|
resp = await client.get("/logout", follow_redirects=False)
|
||
|
|
assert resp.status_code == 303
|
||
|
|
assert resp.headers["location"] == "/login"
|