mantimon-tcg/backend/tests/unit/models/test_user.py
Cal Corum ca3aca2b38 Add has_starter_deck to user profile API response
The frontend routing guard checks has_starter_deck to decide whether to
redirect users to starter selection. The field was missing from the API
response, causing authenticated users with a starter deck to be
incorrectly redirected to /starter on page refresh.

- Add has_starter_deck computed property to User model
- Add has_starter_deck field to UserResponse schema
- Add unit tests for User model properties
- Add API tests for has_starter_deck in profile response

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 23:14:04 -06:00

119 lines
3.7 KiB
Python

"""Unit tests for User model properties.
Tests the computed properties on the User model that don't require
database access.
"""
from datetime import UTC, datetime
from unittest.mock import MagicMock
from uuid import uuid4
import pytest
from app.db.models.user import User
@pytest.fixture
def user():
"""Create a test user with minimal attributes.
Returns a User model instance without database persistence.
"""
user = User(
email="test@example.com",
display_name="Test User",
avatar_url=None,
oauth_provider="google",
oauth_id="google-123",
is_premium=False,
premium_until=None,
)
user.id = uuid4()
user.created_at = datetime.now(UTC)
user.updated_at = datetime.now(UTC)
user.decks = []
user.linked_accounts = []
return user
class TestHasStarterDeck:
"""Tests for the has_starter_deck computed property."""
def test_returns_false_when_no_decks(self, user):
"""Test has_starter_deck is False when user has no decks.
New users start with no decks, so has_starter_deck should be False.
This is used by the frontend to redirect to starter selection.
"""
user.decks = []
assert user.has_starter_deck is False
def test_returns_false_when_only_regular_decks(self, user):
"""Test has_starter_deck is False when user only has regular decks.
Users can create custom decks without selecting a starter.
has_starter_deck should only be True for actual starter decks.
"""
regular_deck = MagicMock()
regular_deck.is_starter = False
user.decks = [regular_deck]
assert user.has_starter_deck is False
def test_returns_true_when_has_starter_deck(self, user):
"""Test has_starter_deck is True when user has selected a starter.
After selecting a starter deck, the property should return True.
This allows the frontend to skip the starter selection page.
"""
starter_deck = MagicMock()
starter_deck.is_starter = True
user.decks = [starter_deck]
assert user.has_starter_deck is True
def test_returns_true_when_starter_among_multiple_decks(self, user):
"""Test has_starter_deck is True even with mixed deck types.
Users can have both starter and custom decks. As long as one
starter deck exists, the property should return True.
"""
regular_deck = MagicMock()
regular_deck.is_starter = False
starter_deck = MagicMock()
starter_deck.is_starter = True
user.decks = [regular_deck, starter_deck]
assert user.has_starter_deck is True
class TestMaxDecks:
"""Tests for the max_decks computed property."""
def test_returns_5_for_free_users(self, user):
"""Test free users have 5 deck slots.
Free users should be limited to 5 decks to encourage premium upgrades.
"""
user.is_premium = False
assert user.max_decks == 5
def test_returns_999_for_premium_users(self, user):
"""Test premium users have unlimited deck slots.
Premium users get 999 slots (effectively unlimited) as a benefit.
"""
from datetime import timedelta
user.is_premium = True
user.premium_until = datetime.now(UTC) + timedelta(days=30)
assert user.max_decks == 999
def test_returns_5_for_expired_premium(self, user):
"""Test expired premium users revert to free limits.
When premium expires, users should be treated as free users.
"""
from datetime import timedelta
user.is_premium = True
user.premium_until = datetime.now(UTC) - timedelta(days=1) # Expired
assert user.max_decks == 5