Add authentication schemas for user registration and login: - UserRegister: email, password (with strength validation), password_confirm - UserLogin: email, password - UserResponse: id, email, created_at, is_active (orm_mode=True) - TokenResponse: access_token, token_type, expires_in - TokenData: user_id, exp Includes field validators for password strength and password confirmation matching. Test coverage: 19 tests for all schemas
263 lines
8.4 KiB
Python
263 lines
8.4 KiB
Python
"""Tests for authentication Pydantic schemas.
|
|
|
|
T17: Test Pydantic schemas for user authentication.
|
|
"""
|
|
import pytest
|
|
from pydantic import ValidationError, EmailStr
|
|
from datetime import datetime, timezone
|
|
|
|
|
|
class TestUserRegister:
|
|
"""Tests for UserRegister schema."""
|
|
|
|
def test_valid_registration(self):
|
|
"""Test valid user registration data."""
|
|
# This will fail until schema is implemented
|
|
from openrouter_monitor.schemas.auth import UserRegister
|
|
|
|
data = UserRegister(
|
|
email="test@example.com",
|
|
password="SecurePass123!",
|
|
password_confirm="SecurePass123!"
|
|
)
|
|
|
|
assert data.email == "test@example.com"
|
|
assert data.password == "SecurePass123!"
|
|
assert data.password_confirm == "SecurePass123!"
|
|
|
|
def test_invalid_email_format(self):
|
|
"""Test that invalid email format raises ValidationError."""
|
|
from openrouter_monitor.schemas.auth import UserRegister
|
|
|
|
with pytest.raises(ValidationError, match="email"):
|
|
UserRegister(
|
|
email="not-an-email",
|
|
password="SecurePass123!",
|
|
password_confirm="SecurePass123!"
|
|
)
|
|
|
|
def test_password_too_short(self):
|
|
"""Test that password shorter than 12 chars raises ValidationError."""
|
|
from openrouter_monitor.schemas.auth import UserRegister
|
|
|
|
with pytest.raises(ValidationError, match="password"):
|
|
UserRegister(
|
|
email="test@example.com",
|
|
password="Short1!",
|
|
password_confirm="Short1!"
|
|
)
|
|
|
|
def test_password_missing_uppercase(self):
|
|
"""Test that password without uppercase raises ValidationError."""
|
|
from openrouter_monitor.schemas.auth import UserRegister
|
|
|
|
with pytest.raises(ValidationError, match="password"):
|
|
UserRegister(
|
|
email="test@example.com",
|
|
password="lowercase123!",
|
|
password_confirm="lowercase123!"
|
|
)
|
|
|
|
def test_password_missing_lowercase(self):
|
|
"""Test that password without lowercase raises ValidationError."""
|
|
from openrouter_monitor.schemas.auth import UserRegister
|
|
|
|
with pytest.raises(ValidationError, match="password"):
|
|
UserRegister(
|
|
email="test@example.com",
|
|
password="UPPERCASE123!",
|
|
password_confirm="UPPERCASE123!"
|
|
)
|
|
|
|
def test_password_missing_digit(self):
|
|
"""Test that password without digit raises ValidationError."""
|
|
from openrouter_monitor.schemas.auth import UserRegister
|
|
|
|
with pytest.raises(ValidationError, match="password"):
|
|
UserRegister(
|
|
email="test@example.com",
|
|
password="NoDigitsHere!",
|
|
password_confirm="NoDigitsHere!"
|
|
)
|
|
|
|
def test_password_missing_special_char(self):
|
|
"""Test that password without special char raises ValidationError."""
|
|
from openrouter_monitor.schemas.auth import UserRegister
|
|
|
|
with pytest.raises(ValidationError, match="password"):
|
|
UserRegister(
|
|
email="test@example.com",
|
|
password="NoSpecialChars123",
|
|
password_confirm="NoSpecialChars123"
|
|
)
|
|
|
|
def test_passwords_do_not_match(self):
|
|
"""Test that mismatched passwords raise ValidationError."""
|
|
from openrouter_monitor.schemas.auth import UserRegister
|
|
|
|
with pytest.raises(ValidationError, match="match"):
|
|
UserRegister(
|
|
email="test@example.com",
|
|
password="SecurePass123!",
|
|
password_confirm="DifferentPass123!"
|
|
)
|
|
|
|
def test_password_strength_validator_called(self):
|
|
"""Test that validate_password_strength is called."""
|
|
from openrouter_monitor.schemas.auth import UserRegister
|
|
|
|
# Valid password should pass
|
|
data = UserRegister(
|
|
email="test@example.com",
|
|
password="ValidPass123!@#",
|
|
password_confirm="ValidPass123!@#"
|
|
)
|
|
assert data.password == "ValidPass123!@#"
|
|
|
|
|
|
class TestUserLogin:
|
|
"""Tests for UserLogin schema."""
|
|
|
|
def test_valid_login(self):
|
|
"""Test valid login credentials."""
|
|
from openrouter_monitor.schemas.auth import UserLogin
|
|
|
|
data = UserLogin(
|
|
email="test@example.com",
|
|
password="anypassword"
|
|
)
|
|
|
|
assert data.email == "test@example.com"
|
|
assert data.password == "anypassword"
|
|
|
|
def test_invalid_email_format(self):
|
|
"""Test that invalid email format raises ValidationError."""
|
|
from openrouter_monitor.schemas.auth import UserLogin
|
|
|
|
with pytest.raises(ValidationError, match="email"):
|
|
UserLogin(
|
|
email="not-an-email",
|
|
password="password"
|
|
)
|
|
|
|
def test_empty_password(self):
|
|
"""Test that empty password is allowed (validation happens elsewhere)."""
|
|
from openrouter_monitor.schemas.auth import UserLogin
|
|
|
|
data = UserLogin(
|
|
email="test@example.com",
|
|
password=""
|
|
)
|
|
|
|
assert data.password == ""
|
|
|
|
|
|
class TestUserResponse:
|
|
"""Tests for UserResponse schema."""
|
|
|
|
def test_valid_response(self):
|
|
"""Test valid user response."""
|
|
from openrouter_monitor.schemas.auth import UserResponse
|
|
from datetime import datetime
|
|
|
|
created_at = datetime(2024, 1, 1, 12, 0, 0)
|
|
|
|
data = UserResponse(
|
|
id=1,
|
|
email="test@example.com",
|
|
created_at=created_at,
|
|
is_active=True
|
|
)
|
|
|
|
assert data.id == 1
|
|
assert data.email == "test@example.com"
|
|
assert data.created_at == created_at
|
|
assert data.is_active is True
|
|
|
|
def test_from_orm(self):
|
|
"""Test that UserResponse can be created from ORM model."""
|
|
from openrouter_monitor.schemas.auth import UserResponse
|
|
from datetime import datetime
|
|
|
|
# Mock ORM object
|
|
class MockUser:
|
|
id = 1
|
|
email = "test@example.com"
|
|
created_at = datetime(2024, 1, 1, 12, 0, 0)
|
|
is_active = True
|
|
|
|
user = UserResponse.model_validate(MockUser())
|
|
|
|
assert user.id == 1
|
|
assert user.email == "test@example.com"
|
|
|
|
|
|
class TestTokenResponse:
|
|
"""Tests for TokenResponse schema."""
|
|
|
|
def test_valid_token_response(self):
|
|
"""Test valid token response."""
|
|
from openrouter_monitor.schemas.auth import TokenResponse
|
|
|
|
data = TokenResponse(
|
|
access_token="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
|
token_type="bearer",
|
|
expires_in=3600
|
|
)
|
|
|
|
assert data.access_token == "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
|
assert data.token_type == "bearer"
|
|
assert data.expires_in == 3600
|
|
|
|
def test_default_token_type(self):
|
|
"""Test that default token_type is 'bearer'."""
|
|
from openrouter_monitor.schemas.auth import TokenResponse
|
|
|
|
data = TokenResponse(
|
|
access_token="some_token",
|
|
expires_in=3600
|
|
)
|
|
|
|
assert data.token_type == "bearer"
|
|
|
|
|
|
class TestTokenData:
|
|
"""Tests for TokenData schema."""
|
|
|
|
def test_valid_token_data(self):
|
|
"""Test valid token data."""
|
|
from openrouter_monitor.schemas.auth import TokenData
|
|
|
|
exp = datetime.now(timezone.utc)
|
|
|
|
data = TokenData(
|
|
user_id="123",
|
|
exp=exp
|
|
)
|
|
|
|
assert data.user_id == "123"
|
|
assert data.exp == exp
|
|
|
|
def test_user_id_from_sub(self):
|
|
"""Test that user_id can be extracted from sub claim."""
|
|
from openrouter_monitor.schemas.auth import TokenData
|
|
|
|
exp = datetime.now(timezone.utc)
|
|
|
|
# TokenData might be created from JWT payload with 'sub' field
|
|
data = TokenData(user_id="456", exp=exp)
|
|
assert data.user_id == "456"
|
|
|
|
def test_user_id_integer_conversion(self):
|
|
"""Test that user_id handles integer IDs."""
|
|
from openrouter_monitor.schemas.auth import TokenData
|
|
|
|
exp = datetime.now(timezone.utc)
|
|
|
|
data = TokenData(
|
|
user_id=123, # Integer ID
|
|
exp=exp
|
|
)
|
|
|
|
assert data.user_id == 123
|