Files
office_translator/tests/test_user_model.py
Sepehr Ramezani 26bd096a06 feat: production deployment - full update with providers, admin, glossaries, pricing, tests
Major changes across backend, frontend, infrastructure:
- Provider system with model selection (Google, DeepL, OpenAI, Ollama, Google Cloud)
- Admin panel: user management, pricing, settings
- Glossary system with CSV import/export
- Subscription and tier quota management
- Security hardening (rate limiting, API key auth, path traversal fixes)
- Docker compose for dev, prod, and IONOS deployment
- Alembic migrations for new tables
- Frontend: dashboard, pricing page, landing page, i18n (en/fr)
- Test suite and verification scripts

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-25 15:01:47 +02:00

201 lines
6.3 KiB
Python

"""
Tests for User model - Task 1 validation
"""
import uuid
import pytest
import pytest_asyncio
from sqlalchemy import select, text
from sqlalchemy.ext.asyncio import AsyncSession
from database.models import User, PlanType
class TestUserModelFields:
"""Test User model has all required fields (AC1)"""
@pytest.mark.asyncio
async def test_user_has_tier_field_with_default_free(
self, async_session: AsyncSession
):
"""AC1: User should have tier field with default 'free'"""
user = User(
email="test@example.com",
name="Test User",
hashed_password="hashed_abc123",
)
async_session.add(user)
await async_session.commit()
assert user.tier == "free", "Default tier should be 'free'"
@pytest.mark.asyncio
async def test_user_tier_can_be_pro(self, async_session: AsyncSession):
"""AC1: User tier can be set to 'pro'"""
user = User(
email="pro@example.com",
name="Pro User",
hashed_password="hashed_abc123",
tier="pro",
)
async_session.add(user)
await async_session.commit()
assert user.tier == "pro"
@pytest.mark.asyncio
async def test_user_has_daily_translation_count_default_zero(
self, async_session: AsyncSession
):
"""AC1: User should have daily_translation_count with default 0"""
user = User(
email="daily@example.com",
name="Daily User",
hashed_password="hashed_abc123",
)
async_session.add(user)
await async_session.commit()
assert user.daily_translation_count == 0
@pytest.mark.asyncio
async def test_user_has_hashed_password_field(self, async_session: AsyncSession):
"""AC1: User should have hashed_password field (renamed from password_hash)"""
user = User(
email="hash@example.com",
name="Hash User",
hashed_password="hashed_password_value",
)
async_session.add(user)
await async_session.commit()
assert hasattr(user, "hashed_password")
assert user.hashed_password == "hashed_password_value"
@pytest.mark.asyncio
async def test_user_has_uuid_id(self, async_session: AsyncSession):
"""AC1: User id should be UUID type"""
user = User(
email="uuid@example.com",
name="UUID User",
hashed_password="hashed_abc123",
)
async_session.add(user)
await async_session.commit()
assert user.id is not None
uuid.UUID(user.id)
@pytest.mark.asyncio
async def test_user_has_all_base_fields(self, async_session: AsyncSession):
"""AC1: User should have all required base fields"""
user = User(
email="base@example.com",
name="Base User",
hashed_password="hashed_abc123",
)
async_session.add(user)
await async_session.commit()
assert user.email == "base@example.com"
assert user.name == "Base User"
assert user.created_at is not None
assert user.updated_at is not None
@pytest.mark.asyncio
async def test_user_keeps_deprecated_plan_field_for_compatibility(
self, async_session: AsyncSession
):
"""Task 1.4: Keep existing plan field for backward compatibility"""
user = User(
email="compat@example.com",
name="Compat User",
hashed_password="hashed_abc123",
)
async_session.add(user)
await async_session.commit()
assert hasattr(user, "plan"), (
"plan field should still exist for backward compatibility"
)
class TestUserModelAsyncOperations:
"""Test async database operations (AC2)"""
@pytest.mark.asyncio
async def test_create_user_async(self, async_session: AsyncSession):
"""AC2: Should be able to create user with async session"""
user = User(
email="async_create@example.com",
name="Async Create",
hashed_password="hashed_abc123",
)
async_session.add(user)
await async_session.commit()
result = await async_session.execute(
select(User).where(User.email == "async_create@example.com")
)
found_user = result.scalar_one()
assert found_user.email == "async_create@example.com"
@pytest.mark.asyncio
async def test_read_user_async(self, async_session: AsyncSession):
"""AC2: Should be able to read user with async session"""
user = User(
email="async_read@example.com",
name="Async Read",
hashed_password="hashed_abc123",
)
async_session.add(user)
await async_session.commit()
result = await async_session.execute(
select(User).where(User.email == "async_read@example.com")
)
found_user = result.scalar_one()
assert found_user.name == "Async Read"
@pytest.mark.asyncio
async def test_update_user_async(self, async_session: AsyncSession):
"""AC2: Should be able to update user with async session"""
user = User(
email="async_update@example.com",
name="Async Update",
hashed_password="hashed_abc123",
)
async_session.add(user)
await async_session.commit()
user.tier = "pro"
user.daily_translation_count = 5
await async_session.commit()
result = await async_session.execute(
select(User).where(User.email == "async_update@example.com")
)
updated_user = result.scalar_one()
assert updated_user.tier == "pro"
assert updated_user.daily_translation_count == 5
@pytest.mark.asyncio
async def test_delete_user_async(self, async_session: AsyncSession):
"""AC2: Should be able to delete user with async session"""
user = User(
email="async_delete@example.com",
name="Async Delete",
hashed_password="hashed_abc123",
)
async_session.add(user)
await async_session.commit()
await async_session.delete(user)
await async_session.commit()
result = await async_session.execute(
select(User).where(User.email == "async_delete@example.com")
)
assert result.scalar_one_or_none() is None