""" Test suite for SQLAlchemy database models. This module tests Issue #48 and Issue #3 database models: 1. User model with hashed passwords 2. User model with tax_jurisdiction, timezone, api_key_hash, is_verified (Issue #3) 3. Strategy model with JSON parameters 4. Relationships (User -> Strategies) 5. Model validation and constraints 6. Timestamps (created_at, updated_at) 7. Cascade delete behavior Tests follow TDD - written before implementation. """ import pytest from datetime import datetime from typing import Dict, Any pytestmark = pytest.mark.asyncio # ============================================================================ # Unit Tests: User Model # ============================================================================ class TestUserModel: """Test User database model.""" async def test_create_user(self, db_session): """Test creating a user with required fields.""" # Arrange try: from tradingagents.api.models import User user = User( username="testuser", email="test@example.com", hashed_password="$argon2id$v=19$m=65536,t=3,p=4$fakehash", ) # Act db_session.add(user) await db_session.commit() await db_session.refresh(user) # Assert assert user.id is not None assert user.username == "testuser" assert user.email == "test@example.com" assert user.hashed_password.startswith("$argon2") except ImportError: pytest.skip("Models not implemented yet") async def test_user_unique_username(self, db_session): """Test that username must be unique.""" # Arrange try: from tradingagents.api.models import User from sqlalchemy.exc import IntegrityError user1 = User( username="testuser", email="test1@example.com", hashed_password="hash1", ) user2 = User( username="testuser", # Same username email="test2@example.com", hashed_password="hash2", ) # Act db_session.add(user1) await db_session.commit() db_session.add(user2) # Assert: Should raise IntegrityError with pytest.raises(IntegrityError): await db_session.commit() except ImportError: pytest.skip("Models not implemented yet") async def test_user_unique_email(self, db_session): """Test that email must be unique.""" # Arrange try: from tradingagents.api.models import User from sqlalchemy.exc import IntegrityError user1 = User( username="user1", email="test@example.com", hashed_password="hash1", ) user2 = User( username="user2", email="test@example.com", # Same email hashed_password="hash2", ) # Act db_session.add(user1) await db_session.commit() db_session.add(user2) # Assert with pytest.raises(IntegrityError): await db_session.commit() except ImportError: pytest.skip("Models not implemented yet") async def test_user_timestamps(self, db_session): """Test that user has created_at and updated_at timestamps.""" # Arrange try: from tradingagents.api.models import User user = User( username="timestampuser", email="timestamp@example.com", hashed_password="hash", ) # Act db_session.add(user) await db_session.commit() await db_session.refresh(user) # Assert assert hasattr(user, "created_at") assert hasattr(user, "updated_at") assert isinstance(user.created_at, datetime) assert isinstance(user.updated_at, datetime) except ImportError: pytest.skip("Models not implemented yet") async def test_user_full_name_optional(self, db_session): """Test that full_name is optional.""" # Arrange try: from tradingagents.api.models import User user = User( username="user_no_name", email="noname@example.com", hashed_password="hash", # No full_name provided ) # Act db_session.add(user) await db_session.commit() await db_session.refresh(user) # Assert: Should succeed without full_name assert user.id is not None assert user.full_name is None or user.full_name == "" except ImportError: pytest.skip("Models not implemented yet") async def test_user_is_active_default(self, db_session): """Test that is_active defaults to True.""" # Arrange try: from tradingagents.api.models import User user = User( username="activeuser", email="active@example.com", hashed_password="hash", ) # Act db_session.add(user) await db_session.commit() await db_session.refresh(user) # Assert if hasattr(user, "is_active"): assert user.is_active is True except ImportError: pytest.skip("Models not implemented yet") async def test_user_strategies_relationship(self, db_session): """Test User has strategies relationship.""" # Arrange try: from tradingagents.api.models import User, Strategy user = User( username="reluser", email="rel@example.com", hashed_password="hash", ) db_session.add(user) await db_session.commit() await db_session.refresh(user) strategy = Strategy( name="Test Strategy", description="Test", user_id=user.id, ) db_session.add(strategy) await db_session.commit() # Act: Access relationship await db_session.refresh(user) # Assert: Can access strategies through relationship # This depends on how relationship is configured assert hasattr(user, "strategies") or user.id is not None except ImportError: pytest.skip("Models not implemented yet") # ============================================================================ # Unit Tests: Strategy Model # ============================================================================ class TestStrategyModel: """Test Strategy database model.""" async def test_create_strategy(self, db_session, test_user): """Test creating a strategy with required fields.""" # Arrange if test_user is None: pytest.skip("User model not implemented yet") try: from tradingagents.api.models import Strategy strategy = Strategy( name="Test Strategy", description="A test strategy", user_id=test_user.id, ) # Act db_session.add(strategy) await db_session.commit() await db_session.refresh(strategy) # Assert assert strategy.id is not None assert strategy.name == "Test Strategy" assert strategy.description == "A test strategy" assert strategy.user_id == test_user.id except ImportError: pytest.skip("Models not implemented yet") async def test_strategy_with_parameters(self, db_session, test_user): """Test creating strategy with JSON parameters.""" # Arrange if test_user is None: pytest.skip("User model not implemented yet") try: from tradingagents.api.models import Strategy parameters = { "symbol": "AAPL", "period": 20, "threshold": 0.05, "indicators": ["SMA", "RSI"], } strategy = Strategy( name="Parameterized Strategy", description="Test", parameters=parameters, user_id=test_user.id, ) # Act db_session.add(strategy) await db_session.commit() await db_session.refresh(strategy) # Assert assert strategy.parameters == parameters assert strategy.parameters["symbol"] == "AAPL" assert strategy.parameters["period"] == 20 except ImportError: pytest.skip("Models not implemented yet") async def test_strategy_empty_parameters(self, db_session, test_user): """Test strategy with empty parameters dict.""" # Arrange if test_user is None: pytest.skip("User model not implemented yet") try: from tradingagents.api.models import Strategy strategy = Strategy( name="Empty Params", description="Test", parameters={}, user_id=test_user.id, ) # Act db_session.add(strategy) await db_session.commit() await db_session.refresh(strategy) # Assert assert strategy.parameters == {} except ImportError: pytest.skip("Models not implemented yet") async def test_strategy_null_parameters(self, db_session, test_user): """Test strategy with null parameters.""" # Arrange if test_user is None: pytest.skip("User model not implemented yet") try: from tradingagents.api.models import Strategy strategy = Strategy( name="Null Params", description="Test", parameters=None, user_id=test_user.id, ) # Act db_session.add(strategy) await db_session.commit() await db_session.refresh(strategy) # Assert: Should handle null (may default to {} or stay None) assert strategy.parameters is None or strategy.parameters == {} except ImportError: pytest.skip("Models not implemented yet") async def test_strategy_is_active_default(self, db_session, test_user): """Test that is_active defaults to True.""" # Arrange if test_user is None: pytest.skip("User model not implemented yet") try: from tradingagents.api.models import Strategy strategy = Strategy( name="Active Strategy", description="Test", user_id=test_user.id, ) # Act db_session.add(strategy) await db_session.commit() await db_session.refresh(strategy) # Assert if hasattr(strategy, "is_active"): assert strategy.is_active is True except ImportError: pytest.skip("Models not implemented yet") async def test_strategy_timestamps(self, db_session, test_user): """Test that strategy has timestamps.""" # Arrange if test_user is None: pytest.skip("User model not implemented yet") try: from tradingagents.api.models import Strategy strategy = Strategy( name="Timestamp Strategy", description="Test", user_id=test_user.id, ) # Act db_session.add(strategy) await db_session.commit() await db_session.refresh(strategy) # Assert assert hasattr(strategy, "created_at") assert hasattr(strategy, "updated_at") assert isinstance(strategy.created_at, datetime) assert isinstance(strategy.updated_at, datetime) except ImportError: pytest.skip("Models not implemented yet") async def test_strategy_updated_at_changes(self, db_session, test_user): """Test that updated_at changes on update.""" # Arrange if test_user is None: pytest.skip("User model not implemented yet") try: from tradingagents.api.models import Strategy import asyncio strategy = Strategy( name="Update Test", description="Original", user_id=test_user.id, ) db_session.add(strategy) await db_session.commit() await db_session.refresh(strategy) original_updated_at = strategy.updated_at # Wait a moment to ensure timestamp difference await asyncio.sleep(0.1) # Act: Update strategy strategy.description = "Modified" await db_session.commit() await db_session.refresh(strategy) # Assert: updated_at should change assert strategy.updated_at > original_updated_at except ImportError: pytest.skip("Models not implemented yet") async def test_strategy_foreign_key_constraint(self, db_session): """Test that strategy requires valid user_id.""" # Arrange try: from tradingagents.api.models import Strategy from sqlalchemy.exc import IntegrityError strategy = Strategy( name="Invalid User", description="Test", user_id=99999, # Non-existent user ) # Act & Assert db_session.add(strategy) with pytest.raises(IntegrityError): await db_session.commit() except ImportError: pytest.skip("Models not implemented yet") async def test_strategy_cascade_delete(self, db_session, test_user): """Test that deleting user cascades to strategies.""" # Arrange if test_user is None: pytest.skip("User model not implemented yet") try: from tradingagents.api.models import Strategy, User from sqlalchemy import select strategy = Strategy( name="Cascade Test", description="Test", user_id=test_user.id, ) db_session.add(strategy) await db_session.commit() strategy_id = strategy.id # Act: Delete user await db_session.delete(test_user) await db_session.commit() # Assert: Strategy should be deleted too (cascade) result = await db_session.execute( select(Strategy).where(Strategy.id == strategy_id) ) deleted_strategy = result.scalar_one_or_none() assert deleted_strategy is None except ImportError: pytest.skip("Models not implemented yet") # ============================================================================ # Unit Tests: Model Validation # ============================================================================ class TestModelValidation: """Test model field validation and constraints.""" async def test_user_required_fields(self, db_session): """Test that user requires username, email, hashed_password.""" # Arrange try: from tradingagents.api.models import User from sqlalchemy.exc import IntegrityError # Missing username user = User( email="test@example.com", hashed_password="hash", ) # Act & Assert db_session.add(user) with pytest.raises(IntegrityError): await db_session.commit() except ImportError: pytest.skip("Models not implemented yet") async def test_strategy_required_fields(self, db_session, test_user): """Test that strategy requires name, description, user_id.""" # Arrange if test_user is None: pytest.skip("User model not implemented yet") try: from tradingagents.api.models import Strategy from sqlalchemy.exc import IntegrityError # Missing name strategy = Strategy( description="Test", user_id=test_user.id, ) # Act & Assert db_session.add(strategy) with pytest.raises(IntegrityError): await db_session.commit() except ImportError: pytest.skip("Models not implemented yet") async def test_email_format_not_validated_at_db_level(self, db_session): """Test that email format validation is done at API level, not DB.""" # Arrange try: from tradingagents.api.models import User # Invalid email format user = User( username="testuser", email="not-an-email", hashed_password="hash", ) # Act db_session.add(user) await db_session.commit() # Assert: DB should accept it (validation is at API level) assert user.id is not None except ImportError: pytest.skip("Models not implemented yet") # ============================================================================ # Integration Tests: Complex Queries # ============================================================================ class TestModelQueries: """Test querying models.""" async def test_query_user_by_username(self, db_session, test_user): """Test querying user by username.""" # Arrange if test_user is None: pytest.skip("User model not implemented yet") try: from tradingagents.api.models import User from sqlalchemy import select # Act result = await db_session.execute( select(User).where(User.username == test_user.username) ) user = result.scalar_one_or_none() # Assert assert user is not None assert user.id == test_user.id assert user.username == test_user.username except ImportError: pytest.skip("Models not implemented yet") async def test_query_user_by_email(self, db_session, test_user): """Test querying user by email.""" # Arrange if test_user is None: pytest.skip("User model not implemented yet") try: from tradingagents.api.models import User from sqlalchemy import select # Act result = await db_session.execute( select(User).where(User.email == test_user.email) ) user = result.scalar_one_or_none() # Assert assert user is not None assert user.email == test_user.email except ImportError: pytest.skip("Models not implemented yet") async def test_query_strategies_by_user(self, db_session, test_user, test_strategy): """Test querying all strategies for a user.""" # Arrange if test_user is None or test_strategy is None: pytest.skip("Models not implemented yet") try: from tradingagents.api.models import Strategy from sqlalchemy import select # Act result = await db_session.execute( select(Strategy).where(Strategy.user_id == test_user.id) ) strategies = result.scalars().all() # Assert assert len(strategies) >= 1 assert test_strategy.id in [s.id for s in strategies] except ImportError: pytest.skip("Models not implemented yet") async def test_query_active_strategies(self, db_session, test_user, multiple_strategies): """Test querying only active strategies.""" # Arrange if test_user is None or not multiple_strategies: pytest.skip("Models not implemented yet") try: from tradingagents.api.models import Strategy from sqlalchemy import select # Act result = await db_session.execute( select(Strategy).where( Strategy.user_id == test_user.id, Strategy.is_active == True, ) ) active_strategies = result.scalars().all() # Assert assert len(active_strategies) >= 1 assert all(s.is_active for s in active_strategies) except ImportError: pytest.skip("Models not implemented yet") async def test_order_strategies_by_created_at(self, db_session, test_user, multiple_strategies): """Test ordering strategies by creation time.""" # Arrange if test_user is None or not multiple_strategies: pytest.skip("Models not implemented yet") try: from tradingagents.api.models import Strategy from sqlalchemy import select # Act result = await db_session.execute( select(Strategy) .where(Strategy.user_id == test_user.id) .order_by(Strategy.created_at.desc()) ) strategies = result.scalars().all() # Assert: Sorted by created_at descending assert len(strategies) >= 2 for i in range(len(strategies) - 1): assert strategies[i].created_at >= strategies[i + 1].created_at except ImportError: pytest.skip("Models not implemented yet") async def test_pagination_query(self, db_session, test_user, multiple_strategies): """Test paginated query with limit and offset.""" # Arrange if test_user is None or not multiple_strategies: pytest.skip("Models not implemented yet") try: from tradingagents.api.models import Strategy from sqlalchemy import select # Act: Get first page result = await db_session.execute( select(Strategy) .where(Strategy.user_id == test_user.id) .limit(2) .offset(0) ) page1 = result.scalars().all() # Act: Get second page result = await db_session.execute( select(Strategy) .where(Strategy.user_id == test_user.id) .limit(2) .offset(2) ) page2 = result.scalars().all() # Assert: Pages have different strategies assert len(page1) <= 2 if page1 and page2: assert page1[0].id != page2[0].id except ImportError: pytest.skip("Models not implemented yet") # ============================================================================ # Edge Cases: Models # ============================================================================ class TestModelEdgeCases: """Test edge cases in model behavior.""" async def test_user_very_long_username(self, db_session): """Test user with very long username.""" # Arrange try: from tradingagents.api.models import User user = User( username="a" * 500, email="long@example.com", hashed_password="hash", ) # Act db_session.add(user) await db_session.commit() # Assert: Should either succeed or fail with constraint violation assert user.id is not None or True # Either way is acceptable except Exception: # May raise exception if username has length constraint pass async def test_strategy_with_unicode_name(self, db_session, test_user): """Test strategy with Unicode characters in name.""" # Arrange if test_user is None: pytest.skip("User model not implemented yet") try: from tradingagents.api.models import Strategy strategy = Strategy( name="η­–η•₯ ζ΅‹θ―• πŸš€", description="桋试描述", user_id=test_user.id, ) # Act db_session.add(strategy) await db_session.commit() await db_session.refresh(strategy) # Assert assert strategy.name == "η­–η•₯ ζ΅‹θ―• πŸš€" assert strategy.description == "桋试描述" except ImportError: pytest.skip("Models not implemented yet") async def test_strategy_with_very_deep_json(self, db_session, test_user): """Test strategy with deeply nested JSON parameters.""" # Arrange if test_user is None: pytest.skip("User model not implemented yet") try: from tradingagents.api.models import Strategy deep_params = { "l1": { "l2": { "l3": { "l4": { "l5": {"value": "deep"} } } } } } strategy = Strategy( name="Deep JSON", description="Test", parameters=deep_params, user_id=test_user.id, ) # Act db_session.add(strategy) await db_session.commit() await db_session.refresh(strategy) # Assert assert strategy.parameters["l1"]["l2"]["l3"]["l4"]["l5"]["value"] == "deep" except ImportError: pytest.skip("Models not implemented yet") # ============================================================================ # Unit Tests: Issue #3 - User Model Enhancements # ============================================================================ class TestUserModelIssue3: """Test User model enhancements from Issue #3. New fields tested: - tax_jurisdiction: Nullable string for user's tax jurisdiction - timezone: Nullable string for user's timezone (must be valid IANA timezone) - api_key_hash: Nullable string for hashed API key - is_verified: Boolean flag for email verification (defaults to False) """ async def test_user_tax_jurisdiction_default_none(self, db_session): """Test that tax_jurisdiction defaults to None.""" # Arrange try: from tradingagents.api.models import User user = User( username="taxuser", email="tax@example.com", hashed_password="hash", ) # Act db_session.add(user) await db_session.commit() await db_session.refresh(user) # Assert assert hasattr(user, "tax_jurisdiction") assert user.tax_jurisdiction is None except ImportError: pytest.skip("Models not implemented yet") async def test_user_tax_jurisdiction_custom_value(self, db_session): """Test setting custom tax_jurisdiction value.""" # Arrange try: from tradingagents.api.models import User user = User( username="taxuser2", email="tax2@example.com", hashed_password="hash", tax_jurisdiction="US-CA", ) # Act db_session.add(user) await db_session.commit() await db_session.refresh(user) # Assert assert user.tax_jurisdiction == "US-CA" except ImportError: pytest.skip("Models not implemented yet") async def test_user_timezone_default_none(self, db_session): """Test that timezone defaults to None.""" # Arrange try: from tradingagents.api.models import User user = User( username="tzuser", email="tz@example.com", hashed_password="hash", ) # Act db_session.add(user) await db_session.commit() await db_session.refresh(user) # Assert assert hasattr(user, "timezone") assert user.timezone is None except ImportError: pytest.skip("Models not implemented yet") async def test_user_timezone_valid_value(self, db_session): """Test setting valid timezone value.""" # Arrange try: from tradingagents.api.models import User user = User( username="tzuser2", email="tz2@example.com", hashed_password="hash", timezone="America/New_York", ) # Act db_session.add(user) await db_session.commit() await db_session.refresh(user) # Assert assert user.timezone == "America/New_York" except ImportError: pytest.skip("Models not implemented yet") async def test_user_timezone_various_timezones(self, db_session): """Test various valid IANA timezone values.""" # Arrange try: from tradingagents.api.models import User timezones = [ "UTC", "America/Los_Angeles", "Europe/London", "Asia/Tokyo", "Australia/Sydney", ] for i, tz in enumerate(timezones): user = User( username=f"tzuser_{i}", email=f"tz{i}@example.com", hashed_password="hash", timezone=tz, ) # Act db_session.add(user) await db_session.commit() await db_session.refresh(user) # Assert assert user.timezone == tz except ImportError: pytest.skip("Models not implemented yet") async def test_user_api_key_hash_default_none(self, db_session): """Test that api_key_hash defaults to None.""" # Arrange try: from tradingagents.api.models import User user = User( username="apiuser", email="api@example.com", hashed_password="hash", ) # Act db_session.add(user) await db_session.commit() await db_session.refresh(user) # Assert assert hasattr(user, "api_key_hash") assert user.api_key_hash is None except ImportError: pytest.skip("Models not implemented yet") async def test_user_api_key_hash_custom_value(self, db_session): """Test setting api_key_hash value.""" # Arrange try: from tradingagents.api.models import User # Simulate hashed API key (bcrypt hash format) api_key_hash = "$2b$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMQJqhN8/LewY5ygOy3f3K0E6O" user = User( username="apiuser2", email="api2@example.com", hashed_password="hash", api_key_hash=api_key_hash, ) # Act db_session.add(user) await db_session.commit() await db_session.refresh(user) # Assert assert user.api_key_hash == api_key_hash assert user.api_key_hash.startswith("$2b$") except ImportError: pytest.skip("Models not implemented yet") async def test_user_is_verified_default_false(self, db_session): """Test that is_verified defaults to False.""" # Arrange try: from tradingagents.api.models import User user = User( username="verifyuser", email="verify@example.com", hashed_password="hash", ) # Act db_session.add(user) await db_session.commit() await db_session.refresh(user) # Assert assert hasattr(user, "is_verified") assert user.is_verified is False except ImportError: pytest.skip("Models not implemented yet") async def test_user_is_verified_can_be_true(self, db_session): """Test setting is_verified to True.""" # Arrange try: from tradingagents.api.models import User user = User( username="verifyuser2", email="verify2@example.com", hashed_password="hash", is_verified=True, ) # Act db_session.add(user) await db_session.commit() await db_session.refresh(user) # Assert assert user.is_verified is True except ImportError: pytest.skip("Models not implemented yet") async def test_user_all_new_fields_together(self, db_session): """Test creating user with all Issue #3 fields.""" # Arrange try: from tradingagents.api.models import User user = User( username="fulluser", email="full@example.com", hashed_password="hash", tax_jurisdiction="US-NY", timezone="America/New_York", api_key_hash="$2b$12$hashedapikey123", is_verified=True, ) # Act db_session.add(user) await db_session.commit() await db_session.refresh(user) # Assert assert user.tax_jurisdiction == "US-NY" assert user.timezone == "America/New_York" assert user.api_key_hash == "$2b$12$hashedapikey123" assert user.is_verified is True except ImportError: pytest.skip("Models not implemented yet") async def test_user_update_timezone(self, db_session): """Test updating user's timezone.""" # Arrange try: from tradingagents.api.models import User user = User( username="updatetz", email="updatetz@example.com", hashed_password="hash", timezone="UTC", ) db_session.add(user) await db_session.commit() await db_session.refresh(user) # Act: Update timezone user.timezone = "America/Los_Angeles" await db_session.commit() await db_session.refresh(user) # Assert assert user.timezone == "America/Los_Angeles" except ImportError: pytest.skip("Models not implemented yet") async def test_user_update_tax_jurisdiction(self, db_session): """Test updating user's tax_jurisdiction.""" # Arrange try: from tradingagents.api.models import User user = User( username="updatetax", email="updatetax@example.com", hashed_password="hash", tax_jurisdiction="US-CA", ) db_session.add(user) await db_session.commit() await db_session.refresh(user) # Act: Update tax jurisdiction user.tax_jurisdiction = "US-TX" await db_session.commit() await db_session.refresh(user) # Assert assert user.tax_jurisdiction == "US-TX" except ImportError: pytest.skip("Models not implemented yet") async def test_user_verify_email(self, db_session): """Test verifying user's email.""" # Arrange try: from tradingagents.api.models import User user = User( username="toverify", email="toverify@example.com", hashed_password="hash", is_verified=False, ) db_session.add(user) await db_session.commit() await db_session.refresh(user) assert user.is_verified is False # Act: Verify email user.is_verified = True await db_session.commit() await db_session.refresh(user) # Assert assert user.is_verified is True except ImportError: pytest.skip("Models not implemented yet") async def test_user_query_by_timezone(self, db_session): """Test querying users by timezone.""" # Arrange try: from tradingagents.api.models import User from sqlalchemy import select # Create users with different timezones user1 = User( username="user_utc", email="utc@example.com", hashed_password="hash", timezone="UTC", ) user2 = User( username="user_ny", email="ny@example.com", hashed_password="hash", timezone="America/New_York", ) user3 = User( username="user_utc2", email="utc2@example.com", hashed_password="hash", timezone="UTC", ) db_session.add_all([user1, user2, user3]) await db_session.commit() # Act: Query users in UTC timezone result = await db_session.execute( select(User).where(User.timezone == "UTC") ) utc_users = result.scalars().all() # Assert assert len(utc_users) == 2 assert all(u.timezone == "UTC" for u in utc_users) except ImportError: pytest.skip("Models not implemented yet") async def test_user_query_verified_users(self, db_session): """Test querying only verified users.""" # Arrange try: from tradingagents.api.models import User from sqlalchemy import select # Create verified and unverified users verified_user = User( username="verified", email="verified@example.com", hashed_password="hash", is_verified=True, ) unverified_user = User( username="unverified", email="unverified@example.com", hashed_password="hash", is_verified=False, ) db_session.add_all([verified_user, unverified_user]) await db_session.commit() # Act: Query verified users result = await db_session.execute( select(User).where(User.is_verified == True) ) verified_users = result.scalars().all() # Assert assert len(verified_users) >= 1 assert all(u.is_verified for u in verified_users) except ImportError: pytest.skip("Models not implemented yet") async def test_user_api_key_hash_nullable(self, db_session): """Test that api_key_hash can be set to None.""" # Arrange try: from tradingagents.api.models import User user = User( username="apinull", email="apinull@example.com", hashed_password="hash", api_key_hash="$2b$12$somehash", ) db_session.add(user) await db_session.commit() await db_session.refresh(user) # Act: Remove API key user.api_key_hash = None await db_session.commit() await db_session.refresh(user) # Assert assert user.api_key_hash is None except ImportError: pytest.skip("Models not implemented yet")