/
liquid-g
/
liquid-code
Обзор
Документация
Войти
/
liquid-g
/
liquid-code
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
tests/test_validation.py
317 строк
11 KB
User
0.3.0-RC - Добавлена система валидации (Validator, базовые валидаторы, интеграция с ArgumentResolver, генератор `make:validator`)
04 июл 2026, 13:30
04 июл 2026, 13:30
6b33e13
Код
Авторство
О чём код?
"""Тесты для системы валидации LiquidCode.""" import re import pytest from liquidcode.validation import ( ValidationError, Constraint, Required, Email, MinLength, MaxLength, Min, Max, Regex, validate, validate_or_raise, validate_dto, ) class TestValidationError: """Тесты исключения ValidationError.""" def test_validation_error_has_errors(self): """ValidationError содержит словарь ошибок.""" errors = {"email": ["Invalid email"], "age": ["Must be >= 18"]} exc = ValidationError(errors) assert exc.errors == errors assert str(errors) in str(exc) class TestConstraint: """Тесты базового класса Constraint.""" def test_constraint_default_message(self): """Constraint использует дефолтное сообщение.""" constraint = Constraint() assert constraint.message == "Invalid value" def test_constraint_custom_message(self): """Constraint использует кастомное сообщение.""" constraint = Constraint(message="Custom error") assert constraint.message == "Custom error" class TestRequired: """Тесты валидатора Required.""" def test_required_valid(self): """Required проходит валидацию для валидного значения.""" required = Required() assert required("hello") is True assert required(0) is True assert required([]) is True assert required(False) is True def test_required_empty_string(self): """Required не проходит для пустой строки.""" required = Required() assert required("") is False def test_required_none(self): """Required не проходит для None.""" required = Required() assert required(None) is False class TestEmail: """Тесты валидатора Email.""" def test_email_valid(self): """Email проходит валидацию для валидного email.""" email = Email() assert email("test@example.com") is True assert email("user.name@domain.co.uk") is True assert email("user+tag@example.org") is True def test_email_invalid(self): """Email не проходит для невалидного email.""" email = Email() assert email("invalid") is False assert email("test@") is False assert email("@example.com") is False assert email("test@example") is False assert email("") is False def test_email_empty(self): """Email не проходит для пустой строки.""" email = Email() assert email("") is False class TestMinLength: """Тесты валидатора MinLength.""" def test_min_length_valid(self): """MinLength проходит для достаточной длины.""" min_len = MinLength(3) assert min_len("hello") is True assert min_len("abc") is True def test_min_length_invalid(self): """MinLength не проходит для недостаточной длины.""" min_len = MinLength(5) assert min_len("abc") is False assert min_len("ab") is False def test_min_length_custom_message(self): """MinLength использует кастомное сообщение.""" min_len = MinLength(3, message="Too short!") assert min_len.message == "Too short!" def test_min_length_default_message(self): """MinLength использует дефолтное сообщение.""" min_len = MinLength(5) assert "Минимальная длина 5" in min_len.message class TestMaxLength: """Тесты валидатора MaxLength.""" def test_max_length_valid(self): """MaxLength проходит для достаточной длины.""" max_len = MaxLength(10) assert max_len("hello") is True assert max_len("a" * 10) is True def test_max_length_invalid(self): """MaxLength не проходит для избыточной длины.""" max_len = MaxLength(5) assert max_len("hello world") is False assert max_len("abcdefgh") is False def test_max_length_custom_message(self): """MaxLength использует кастомное сообщение.""" max_len = MaxLength(5, message="Too long!") assert max_len.message == "Too long!" def test_max_length_default_message(self): """MaxLength использует дефолтное сообщение.""" max_len = MaxLength(10) assert "Максимальная длина 10" in max_len.message class TestMin: """Тесты валидатора Min.""" def test_min_valid_int(self): """Min проходит для валидного int.""" min_val = Min(10) assert min_val(10) is True assert min_val(15) is True assert min_val(100) is True def test_min_valid_float(self): """Min проходит для валидного float.""" min_val = Min(3.14) assert min_val(3.14) is True assert min_val(5.0) is True def test_min_invalid(self): """Min не проходит для недостаточного значения.""" min_val = Min(10) assert min_val(5) is False assert min_val(0) is False assert min_val(-1) is False def test_min_custom_message(self): """Min использует кастомное сообщение.""" min_val = Min(100, message="Value too low!") assert min_val.message == "Value too low!" def test_min_default_message(self): """Min использует дефолтное сообщение.""" min_val = Min(18) assert "не меньше 18" in min_val.message class TestMax: """Тесты валидатора Max.""" def test_max_valid_int(self): """Max проходит для валидного int.""" max_val = Max(100) assert max_val(100) is True assert max_val(50) is True assert max_val(0) is True def test_max_valid_float(self): """Max проходит для валидного float.""" max_val = Max(10.5) assert max_val(10.5) is True assert max_val(5.0) is True def test_max_invalid(self): """Max не проходит для избыточного значения.""" max_val = Max(10) assert max_val(15) is False assert max_val(100) is False def test_max_custom_message(self): """Max использует кастомное сообщение.""" max_val = Max(100, message="Value too high!") assert max_val.message == "Value too high!" def test_max_default_message(self): """Max использует дефолтное сообщение.""" max_val = Max(18) assert "не больше 18" in max_val.message class TestRegex: """Тесты валидатора Regex.""" def test_regex_valid(self): """Regex проходит для совпадения.""" pattern = Regex(r'^[a-z]+$') assert pattern("hello") is True assert pattern("test") is True def test_regex_invalid(self): """Regex не проходит для несовпадения.""" pattern = Regex(r'^[a-z]+$') assert pattern("HELLO") is False assert pattern("hello123") is False assert pattern("") is False def test_regex_custom_message(self): """Regex использует кастомное сообщение.""" pattern = Regex(r'^[0-9]+$', message="Must be digits!") assert pattern.message == "Must be digits!" def test_regex_default_message(self): """Regex использует дефолтное сообщение.""" pattern = Regex(r'^[a-z]+$') assert "не соответствует формату" in pattern.message class TestValidate: """Тесты функции validate().""" def test_validate_no_errors(self): """validate() возвращает пустой словарь без ошибок.""" data = {"email": "test@example.com", "age": 25} constraints = { "email": [Email()], "age": [Min(0), Max(150)], } errors = validate(data, constraints) assert errors == {} def test_validate_with_errors(self): """validate() возвращает словарь ошибок.""" data = {"email": "invalid", "age": -5} constraints = { "email": [Email()], "age": [Min(0)], } errors = validate(data, constraints) assert "email" in errors assert "age" in errors def test_validate_partial_errors(self): """validate() возвращает только те ошибки, которые есть.""" data = {"email": "invalid", "age": 25} constraints = { "email": [Email()], "age": [Min(0)], } errors = validate(data, constraints) assert "email" in errors assert "age" not in errors def test_validate_missing_field(self): """validate() обрабатывает отсутствующие поля.""" data = {"age": 25} constraints = { "email": [Email(), Required()], "age": [Min(0)], } errors = validate(data, constraints) assert "email" in errors class TestValidateOrRaise: """Тесты функции validate_or_raise().""" def test_validate_or_raise_no_error(self): """validate_or_raise() не выбрасывает при успехе.""" data = {"email": "test@example.com"} constraints = {"email": [Email()]} # Не должно выбросить validate_or_raise(data, constraints) def test_validate_or_raise_raises(self): """validate_or_raise() выбрасывает ValidationError при ошибках.""" data = {"email": "invalid"} constraints = {"email": [Email()]} with pytest.raises(ValidationError) as exc_info: validate_or_raise(data, constraints) assert "email" in exc_info.value.errors class TestValidateDto: """Тесты функции validate_dto().""" def test_validate_dto_simple(self): """validate_dto() работает упрощенно (пока None).""" # Пока упрощённая версия без авто-валидации DTO class UserDTO: name: str age: int data = {"name": "John", "age": 25} result = validate_dto(UserDTO, data) # Пока возвращает None (упрощённая версия) assert result is None