"""Unit tests for User model""" import pytest from app.models.user import User @pytest.mark.unit def test_user_creation(app): """Test User model instantiation""" with app.app_context(): user = User( user_id='usr_test', username='testuser', email='test@example.com', password_hash='hash123', role='administrator', product_ids=['prod_001'], is_active=True ) assert user.user_id == 'usr_test' assert user.username == 'testuser' assert user.email == 'test@example.com' assert user.password_hash == 'hash123' assert user.role == 'administrator' assert user.product_ids == ['prod_001'] assert user.is_active == True @pytest.mark.unit def test_user_is_active_property(app): """Test is_active property (Flask-Login integration) Bug: AttributeError: can't set attribute 'is_active' Fix: Use private _is_active attribute with property decorator """ with app.app_context(): # Test active user active_user = User( user_id='usr_001', username='active', email='active@example.com', password_hash='hash', role='administrator', is_active=True ) assert active_user.is_active == True # Test inactive user inactive_user = User( user_id='usr_002', username='inactive', email='inactive@example.com', password_hash='hash', role='administrator', is_active=False ) assert inactive_user.is_active == False @pytest.mark.unit def test_user_flask_login_properties(app): """Test Flask-Login required properties""" with app.app_context(): user = User( user_id='usr_001', username='test', email='test@example.com', password_hash='hash', role='administrator' ) # Flask-Login required properties assert user.is_authenticated == True assert user.is_anonymous == False assert user.is_active == True assert user.get_id() == 'usr_001' @pytest.mark.unit def test_user_password_hashing(app): """Test password hashing with bcrypt""" with app.app_context(): password = 'test_password_123' hashed = User.hash_password(password) # Hash should be different from plain password assert hashed != password # Hash should be bcrypt format assert hashed.startswith('$2b$') # Same password should produce different hashes (salt) hashed2 = User.hash_password(password) assert hashed != hashed2 @pytest.mark.unit def test_user_password_verification(app): """Test password verification""" with app.app_context(): password = 'correct_password' wrong_password = 'wrong_password' user = User( user_id='usr_001', username='test', email='test@example.com', password_hash=User.hash_password(password), role='administrator' ) # Correct password should verify assert user.check_password(password) == True # Wrong password should not verify assert user.check_password(wrong_password) == False @pytest.mark.unit def test_user_to_dict(app): """Test user serialization to dictionary""" with app.app_context(): user = User( user_id='usr_001', username='testuser', email='test@example.com', password_hash='hash123', role='product_owner', product_ids=['prod_001', 'prod_002'], is_active=True ) user_dict = user.to_dict() assert user_dict['user_id'] == 'usr_001' assert user_dict['username'] == 'testuser' assert user_dict['email'] == 'test@example.com' assert user_dict['password_hash'] == 'hash123' assert user_dict['role'] == 'product_owner' assert user_dict['product_ids'] == ['prod_001', 'prod_002'] assert user_dict['is_active'] == True @pytest.mark.unit def test_user_from_dict(app): """Test user deserialization from dictionary""" with app.app_context(): user_data = { 'user_id': 'usr_001', 'username': 'testuser', 'email': 'test@example.com', 'password_hash': 'hash123', 'role': 'administrator', 'product_ids': ['prod_001'], 'is_active': True } user = User.from_dict(user_data) assert user.user_id == 'usr_001' assert user.username == 'testuser' assert user.email == 'test@example.com' assert user.password_hash == 'hash123' assert user.role == 'administrator' assert user.product_ids == ['prod_001'] assert user.is_active == True @pytest.mark.unit def test_user_from_dict_with_defaults(app): """Test user deserialization with missing optional fields""" with app.app_context(): user_data = { 'user_id': 'usr_001', 'username': 'testuser', 'email': 'test@example.com', 'password_hash': 'hash123', 'role': 'administrator' # Missing product_ids and is_active } user = User.from_dict(user_data) assert user.product_ids == [] # Default empty list assert user.is_active == True # Default True @pytest.mark.unit def test_user_create(app): """Test user creation with auto-generated ID""" with app.app_context(): user = User.create( username='newuser', email='new@example.com', password='password123', role='product_owner', product_ids=['prod_001'] ) # User should be created assert user.user_id.startswith('usr_') assert user.username == 'newuser' assert user.email == 'new@example.com' assert user.role == 'product_owner' assert user.product_ids == ['prod_001'] assert user.is_active == True # Password should be hashed assert user.password_hash != 'password123' assert user.check_password('password123') == True # Cleanup user.delete() @pytest.mark.unit def test_user_create_duplicate_username(app): """Test user creation with duplicate username raises error""" with app.app_context(): # Create first user user1 = User.create( username='duplicate', email='user1@example.com', password='password123', role='administrator' ) # Try to create second user with same username with pytest.raises(ValueError, match="Username already exists"): User.create( username='duplicate', email='user2@example.com', password='password456', role='administrator' ) # Cleanup user1.delete() @pytest.mark.unit def test_user_create_invalid_role(app): """Test user creation with invalid role raises error""" with app.app_context(): with pytest.raises(ValueError, match="Invalid role"): User.create( username='testuser', email='test@example.com', password='password123', role='invalid_role' ) @pytest.mark.unit def test_user_save_and_load(app): """Test user persistence (save and load)""" with app.app_context(): # Create and save user user = User.create( username='persistent', email='persist@example.com', password='password123', role='administrator' ) user_id = user.user_id # Load user from storage loaded_user = User.get_by_id(user_id) assert loaded_user is not None assert loaded_user.user_id == user_id assert loaded_user.username == 'persistent' assert loaded_user.email == 'persist@example.com' assert loaded_user.role == 'administrator' assert loaded_user.check_password('password123') == True # Cleanup user.delete() @pytest.mark.unit def test_user_get_by_username(app): """Test loading user by username""" with app.app_context(): # Create user user = User.create( username='findme', email='findme@example.com', password='password123', role='product_owner' ) # Find by username found_user = User.get_by_username('findme') assert found_user is not None assert found_user.username == 'findme' assert found_user.email == 'findme@example.com' # Non-existent username not_found = User.get_by_username('doesnotexist') assert not_found is None # Cleanup user.delete() @pytest.mark.unit def test_user_get_all(app): """Test getting all users""" with app.app_context(): # Create multiple users user1 = User.create('user1', 'user1@example.com', 'pass1', 'administrator') user2 = User.create('user2', 'user2@example.com', 'pass2', 'product_owner') # Get all users all_users = User.get_all() # Should include at least our test users usernames = [u.username for u in all_users] assert 'user1' in usernames assert 'user2' in usernames # Cleanup user1.delete() user2.delete() @pytest.mark.unit def test_user_delete(app): """Test user deletion""" with app.app_context(): # Create user user = User.create( username='deleteme', email='delete@example.com', password='password123', role='administrator' ) user_id = user.user_id # Delete user user.delete() # User should no longer exist deleted_user = User.get_by_id(user_id) assert deleted_user is None