"""Базовые тесты для валидаторов из app.utils.validators.""" import pytest from app.utils import validators @pytest.mark.parametrize( 'email,is_valid', [ ('user@example.com', True), ('user.name+tag@sub.domain.ru', True), ('plain-address', False), ('missing-at.example.com', False), ('user@invalid', False), ], ) def test_validate_email_handles_expected_patterns(email: str, is_valid: bool) -> None: """Проверяем типичные корректные и некорректные адреса.""" assert validators.validate_email(email) is is_valid @pytest.mark.parametrize( 'phone,is_valid', [ ('+71234567890', True), ('+1 (202) 555-0101', True), ('12345', True), ('+0 123456789', False), ('abc', False), ], ) def test_validate_phone_strips_formatting_and_checks_pattern(phone: str, is_valid: bool) -> None: """Телефон должен соответствовать стандарту E.164 после очистки.""" assert validators.validate_phone(phone) is is_valid @pytest.mark.parametrize( 'username,is_valid', [ ('@valid_name', True), ('simpleUser', True), ('bad', False), ('toolongusername_more_than32_chars', False), ('', False), ], ) def test_validate_telegram_username_enforces_length(username: str, is_valid: bool) -> None: """Telegram-логин должен быть 5-32 символов и содержать допустимые символы.""" assert validators.validate_telegram_username(username) is is_valid def test_validate_amount_returns_float_within_bounds() -> None: """Числа должны конвертироваться с уважением к диапазону.""" assert validators.validate_amount('10.5', min_amount=5, max_amount=20) == pytest.approx(10.5) assert validators.validate_amount('2', min_amount=5, max_amount=20) is None assert validators.validate_amount('abc', min_amount=0, max_amount=10) is None def test_validate_positive_integer_enforces_upper_bound() -> None: """Положительное целое число выходит за пределы — возвращаем None.""" assert validators.validate_positive_integer('12', max_value=20) == 12 assert validators.validate_positive_integer('0', max_value=20) is None assert validators.validate_positive_integer('50', max_value=20) is None assert validators.validate_positive_integer('NaN') is None @pytest.mark.parametrize( 'value,expected', [ ('500', 500), ('10gb', 10240), ('2 TB', 2097152), ('безлимит', 0), ('invalid', None), ], ) def test_validate_traffic_amount_supports_units(value: str, expected: int | None) -> None: """Валидатор трафика распознаёт разные единицы измерения и особые значения.""" assert validators.validate_traffic_amount(value) == expected def test_validate_subscription_period_accepts_reasonable_range() -> None: """Диапазон допустимой длительности от 1 до 3650 дней.""" assert validators.validate_subscription_period('30') == 30 assert validators.validate_subscription_period(0) is None assert validators.validate_subscription_period(4000) is None def test_validate_uuid_detects_standard_format() -> None: """UUID должен соответствовать HEX шаблону версии 4/5.""" sample = '123e4567-e89b-12d3-a456-426614174000' assert validators.validate_uuid(sample) is True assert validators.validate_uuid('not-a-uuid') is False def test_validate_url_recognises_https_links() -> None: """Валидатор URL допускает http/https ссылки и отклоняет произвольные строки.""" assert validators.validate_url('https://example.com/path?query=1') assert not validators.validate_url('ftp://example.com') def test_validate_html_tags_rejects_unknown_tags() -> None: """Неизвестные HTML теги должны приводить к отказу.""" ok, message = validators.validate_html_tags('bold') assert ok is True bad, error = validators.validate_html_tags('run') assert bad is False assert 'Неподдерживаемый тег' in error def test_validate_html_structure_detects_wrong_nesting() -> None: """Неправильная вложенность тегов должна сообщаться пользователю.""" ok, message = validators.validate_html_structure('text') assert ok is True bad, error = validators.validate_html_structure('text') assert bad is False assert 'Неправильная вложенность' in error def test_fix_html_tags_repairs_missing_quotes() -> None: """Автоисправление должно добавлять кавычки у ссылок.""" broken = 'link' fixed = validators.fix_html_tags(broken) assert 'href="https://example.com"' in fixed def test_validate_rules_content_detects_structure_error() -> None: """При нарушении структуры должны вернуться сообщение и отсутствие подсказки.""" is_valid, message, suggestion = validators.validate_rules_content('text') assert is_valid is False assert 'Неправильная вложенность' in message assert suggestion is None def test_validate_rules_content_accepts_supported_markup() -> None: """Корректный HTML должен проходить проверку без сообщений.""" is_valid, message, suggestion = validators.validate_rules_content('Добро пожаловать!') assert is_valid is True assert message == '' assert suggestion is None