""" 密码哈希工具测试 """ import pytest from packages.application.auth.password_hasher import PasswordHasher, PasswordValidator class TestPasswordHasher: """密码哈希测试""" @pytest.fixture def hasher(self): """创建密码哈希器""" return PasswordHasher(rounds=4) # 测试用低 cost,加快速度 def test_hash_password(self, hasher): """测试密码哈希""" password = "MySecurePassword123" hashed = hasher.hash_password(password) assert isinstance(hashed, str) assert len(hashed) > 0 assert hashed != password # 哈希后不等于原文 assert hashed.startswith("$2b$") # bcrypt 格式 def test_hash_same_password_different_result(self, hasher): """测试相同密码每次哈希结果不同(因为 salt 不同)""" password = "MySecurePassword123" hash1 = hasher.hash_password(password) hash2 = hasher.hash_password(password) assert hash1 != hash2 # salt 不同,哈希不同 def test_verify_correct_password(self, hasher): """测试验证正确的密码""" password = "MySecurePassword123" hashed = hasher.hash_password(password) assert hasher.verify_password(password, hashed) is True def test_verify_incorrect_password(self, hasher): """测试验证错误的密码""" password = "MySecurePassword123" hashed = hasher.hash_password(password) assert hasher.verify_password("WrongPassword", hashed) is False def test_verify_empty_password(self, hasher): """测试空密码验证""" hashed = hasher.hash_password("test") assert hasher.verify_password("", hashed) is False def test_verify_empty_hash(self, hasher): """测试空哈希验证""" assert hasher.verify_password("test", "") is False def test_verify_invalid_hash(self, hasher): """测试无效的哈希""" assert hasher.verify_password("test", "invalid-hash") is False def test_hash_empty_password(self, hasher): """测试哈希空密码应该失败""" with pytest.raises(ValueError, match="Password cannot be empty"): hasher.hash_password("") def test_invalid_rounds(self): """测试无效的 rounds 参数""" with pytest.raises(ValueError, match="rounds must be between 4 and 31"): PasswordHasher(rounds=2) with pytest.raises(ValueError, match="rounds must be between 4 and 31"): PasswordHasher(rounds=50) def test_unicode_password(self, hasher): """测试 Unicode 密码""" password = "密码123!@#" hashed = hasher.hash_password(password) assert hasher.verify_password(password, hashed) is True assert hasher.verify_password("错误密码", hashed) is False class TestPasswordValidator: """密码验证器测试""" @pytest.fixture def validator(self): """创建密码验证器""" return PasswordValidator( min_length=8, require_uppercase=True, require_lowercase=True, require_digit=True, require_special=False, ) def test_valid_password(self, validator): """测试有效密码""" valid, error = validator.validate("MyPassword123") assert valid is True assert error is None def test_password_too_short(self, validator): """测试密码太短""" valid, error = validator.validate("Pass1") assert valid is False assert "at least 8 characters" in error def test_password_no_uppercase(self, validator): """测试没有大写字母""" valid, error = validator.validate("mypassword123") assert valid is False assert "uppercase letter" in error def test_password_no_lowercase(self, validator): """测试没有小写字母""" valid, error = validator.validate("MYPASSWORD123") assert valid is False assert "lowercase letter" in error def test_password_no_digit(self, validator): """测试没有数字""" valid, error = validator.validate("MyPassword") assert valid is False assert "digit" in error def test_password_with_special_chars(self): """测试要求特殊字符""" validator = PasswordValidator( min_length=8, require_uppercase=True, require_lowercase=True, require_digit=True, require_special=True, ) # 没有特殊字符 valid, error = validator.validate("MyPassword123") assert valid is False assert "special character" in error # 有特殊字符 valid, error = validator.validate("MyPassword123!") assert valid is True assert error is None def test_empty_password(self, validator): """测试空密码""" valid, error = validator.validate("") assert valid is False assert "cannot be empty" in error def test_custom_min_length(self): """测试自定义最小长度""" validator = PasswordValidator( min_length=12, require_uppercase=False, require_lowercase=False, require_digit=False, require_special=False, ) valid, error = validator.validate("short") assert valid is False assert "at least 12 characters" in error valid, error = validator.validate("longenoughpassword") assert valid is True assert error is None class TestPasswordHandler: """Password Handler 委托层测试""" def test_hash_and_verify_password(self): """测试哈希和验证密码""" from packages.application.auth.password_handler import PasswordHandler handler = PasswordHandler(rounds=4) hashed = handler.hash_password("MySecurePass123") assert hashed != "MySecurePass123" assert len(hashed) > 20 assert handler.verify_password("MySecurePass123", hashed) is True assert handler.verify_password("WrongPassword", hashed) is False def test_hash_empty_password_raises(self): """测试空密码抛出异常""" from packages.application.auth.password_handler import PasswordHandler handler = PasswordHandler(rounds=4) with pytest.raises(ValueError): handler.hash_password("") def test_needs_rehash(self): """测试检测需要重新哈希""" from packages.application.auth.password_handler import PasswordHandler handler = PasswordHandler(rounds=4) hashed = handler.hash_password("TestPass123") # 相同 rounds 不需要重新哈希 assert handler.needs_rehash(hashed) is False # 用更高 rounds 的 handler 检查,应该需要重新哈希 # 注意:bcrypt 的 rounds 体现在 hash 中,这里用不同 rounds 测试 high_rounds_handler = PasswordHandler(rounds=5) # 低 rounds 的 hash 在高 rounds 配置下应该需要 rehash assert high_rounds_handler.needs_rehash(hashed) is True def test_validate_strength(self): """测试密码强度验证""" from packages.application.auth.password_handler import PasswordHandler handler = PasswordHandler(rounds=4) # 弱密码 valid, error = handler.validate_strength("weak") assert valid is False assert error is not None # 强密码 valid, error = handler.validate_strength("StrongPass123") assert valid is True assert error is None def test_configure_and_get_default_handler(self): """测试配置和获取全局默认 handler""" from packages.application.auth import password_handler as handler_module from packages.application.auth.password_handler import ( configure_password_handler, get_password_handler, ) # 重置全局状态 handler_module._default_handler = None # 配置 handler = configure_password_handler(rounds=4) assert handler is not None # 获取 same_handler = get_password_handler() assert same_handler is handler # 验证能正常工作 hashed = same_handler.hash_password("TestPass123") assert same_handler.verify_password("TestPass123", hashed) is True # 重置全局状态,避免影响其他测试 handler_module._default_handler = None def test_get_password_handler_auto_creates_default(self): """测试未配置时获取 handler 会自动创建默认实例""" from packages.application.auth import password_handler as handler_module from packages.application.auth.password_handler import get_password_handler # 重置全局状态 handler_module._default_handler = None # 自动创建默认实例 handler = get_password_handler() assert handler is not None # 重置 handler_module._default_handler = None