Files
fast-next-template/backend/tests/models/test_user.py
Felipe Cardoso 16ee4e0cb3 Initial implementation of OAuth models, endpoints, and migrations
- Added models for `OAuthClient`, `OAuthState`, and `OAuthAccount`.
- Created Pydantic schemas to support OAuth flows, client management, and linked accounts.
- Implemented skeleton endpoints for OAuth Provider mode: authorization, token, and revocation.
- Updated router imports to include new `/oauth` and `/oauth/provider` routes.
- Added Alembic migration script to create OAuth-related database tables.
- Enhanced `users` table to allow OAuth-only accounts by making `password_hash` nullable.
2025-11-25 00:37:23 +01:00

251 lines
7.5 KiB
Python
Executable File

# tests/models/test_user.py
import uuid
from datetime import datetime
import pytest
from sqlalchemy.exc import IntegrityError
from app.models.user import User
def test_create_user(db_session):
"""Test creating a basic user."""
# Arrange
user_id = uuid.uuid4()
new_user = User(
id=user_id,
email="test@example.com",
password_hash="hashedpassword",
first_name="Test",
last_name="User",
phone_number="1234567890",
is_active=True,
is_superuser=False,
preferences={"theme": "dark"},
)
db_session.add(new_user)
# Act
db_session.commit()
created_user = db_session.query(User).filter_by(email="test@example.com").first()
# Assert
assert created_user is not None
assert created_user.email == "test@example.com"
assert created_user.first_name == "Test"
assert created_user.last_name == "User"
assert created_user.phone_number == "1234567890"
assert created_user.is_active is True
assert created_user.is_superuser is False
assert created_user.preferences == {"theme": "dark"}
# UUID should be preserved
assert created_user.id == user_id
# Timestamps should be set
assert isinstance(created_user.created_at, datetime)
assert isinstance(created_user.updated_at, datetime)
def test_update_user(db_session):
"""Test updating an existing user."""
# Arrange - Create a user
user_id = uuid.uuid4()
user = User(
id=user_id,
email="update@example.com",
password_hash="hashedpassword",
first_name="Before",
last_name="Update",
)
db_session.add(user)
db_session.commit()
# Record the original creation timestamp
original_created_at = user.created_at
# Act - Update the user
user.first_name = "After"
user.last_name = "Updated"
user.phone_number = "9876543210"
user.preferences = {"theme": "light", "notifications": True}
db_session.commit()
# Fetch the updated user to verify changes were persisted
updated_user = db_session.query(User).filter_by(id=user_id).first()
# Assert
assert updated_user.first_name == "After"
assert updated_user.last_name == "Updated"
assert updated_user.phone_number == "9876543210"
assert updated_user.preferences == {"theme": "light", "notifications": True}
# created_at shouldn't change on update
assert updated_user.created_at == original_created_at
# updated_at should be newer than created_at
assert updated_user.updated_at > original_created_at
def test_delete_user(db_session):
"""Test deleting a user."""
# Arrange - Create a user
user_id = uuid.uuid4()
user = User(
id=user_id,
email="delete@example.com",
password_hash="hashedpassword",
first_name="Delete",
last_name="Me",
)
db_session.add(user)
db_session.commit()
# Act - Delete the user
db_session.delete(user)
db_session.commit()
# Assert
deleted_user = db_session.query(User).filter_by(id=user_id).first()
assert deleted_user is None
def test_user_unique_email_constraint(db_session):
"""Test that users cannot have duplicate emails."""
# Arrange - Create a user
user1 = User(
id=uuid.uuid4(),
email="duplicate@example.com",
password_hash="hashedpassword",
first_name="First",
last_name="User",
)
db_session.add(user1)
db_session.commit()
# Act & Assert - Try to create another user with the same email
user2 = User(
id=uuid.uuid4(),
email="duplicate@example.com", # Same email
password_hash="differenthash",
first_name="Second",
last_name="User",
)
db_session.add(user2)
# Should raise IntegrityError due to unique constraint
with pytest.raises(IntegrityError):
db_session.commit()
# Rollback for cleanup
db_session.rollback()
def test_user_required_fields(db_session):
"""Test that required fields are enforced."""
# Test each required field by creating a user without it
# Missing email
user_no_email = User(
id=uuid.uuid4(),
# email is missing
password_hash="hashedpassword",
first_name="Test",
last_name="User",
)
db_session.add(user_no_email)
with pytest.raises(IntegrityError):
db_session.commit()
db_session.rollback()
def test_user_oauth_only_without_password(db_session):
"""Test that OAuth-only users can be created without password_hash."""
# OAuth-only users don't have a password set
user_no_password = User(
id=uuid.uuid4(),
email="oauthonly@example.com",
password_hash=None, # OAuth-only user
first_name="OAuth",
last_name="User",
)
db_session.add(user_no_password)
db_session.commit()
# Retrieve and verify
retrieved = db_session.query(User).filter_by(email="oauthonly@example.com").first()
assert retrieved is not None
assert retrieved.password_hash is None
assert retrieved.has_password is False # Test has_password property
def test_user_defaults(db_session):
"""Test that default values are correctly set."""
# Arrange - Create a minimal user with only required fields
minimal_user = User(
id=uuid.uuid4(),
email="minimal@example.com",
password_hash="hashedpassword",
first_name="Minimal",
last_name="User",
)
db_session.add(minimal_user)
db_session.commit()
# Act - Retrieve the user
created_user = db_session.query(User).filter_by(email="minimal@example.com").first()
# Assert - Check default values
assert created_user.is_active is True # Default should be True
assert created_user.is_superuser is False # Default should be False
assert created_user.phone_number is None # Optional field
assert created_user.preferences is None # Optional field
def test_user_string_representation(db_session):
"""Test the string representation of a user."""
# Arrange
user = User(
id=uuid.uuid4(),
email="repr@example.com",
password_hash="hashedpassword",
first_name="String",
last_name="Repr",
)
# Act & Assert
assert str(user) == "<User repr@example.com>"
assert repr(user) == "<User repr@example.com>"
def test_user_with_complex_json_preferences(db_session):
"""Test storing and retrieving complex JSON preferences."""
# Arrange - Create a user with nested JSON preferences
complex_preferences = {
"theme": {"mode": "dark", "colors": {"primary": "#333", "secondary": "#666"}},
"notifications": {
"email": True,
"sms": False,
"push": {"enabled": True, "quiet_hours": [22, 7]},
},
"tags": ["important", "family", "events"],
}
user = User(
id=uuid.uuid4(),
email="complex@example.com",
password_hash="hashedpassword",
first_name="Complex",
last_name="JSON",
preferences=complex_preferences,
)
db_session.add(user)
db_session.commit()
# Act - Retrieve the user
retrieved_user = (
db_session.query(User).filter_by(email="complex@example.com").first()
)
# Assert - The complex JSON should be preserved
assert retrieved_user.preferences == complex_preferences
assert retrieved_user.preferences["theme"]["colors"]["primary"] == "#333"
assert retrieved_user.preferences["notifications"]["push"]["quiet_hours"] == [22, 7]
assert "important" in retrieved_user.preferences["tags"]