Files
haproxy-mcp/tests/unit/test_validation.py
kaffa 6bcfee519c refactor: Improve code quality, error handling, and test coverage
- Add file_lock context manager to eliminate duplicate locking patterns
- Add ValidationError, ConfigurationError, CertificateError exceptions
- Improve rollback logic in haproxy_add_servers (track successful ops only)
- Decompose haproxy_add_domain into smaller helper functions
- Consolidate certificate constants (CERTS_DIR, ACME_HOME) to config.py
- Enhance docstrings for internal functions and magic numbers
- Add pytest framework with 48 new tests (269 -> 317 total)
- Increase test coverage from 76% to 86%
  - servers.py: 58% -> 82%
  - certificates.py: 67% -> 86%
  - configuration.py: 69% -> 94%

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 12:50:00 +09:00

276 lines
9.2 KiB
Python

"""Unit tests for validation module."""
import pytest
from haproxy_mcp.validation import (
validate_domain,
validate_ip,
validate_port,
validate_backend_name,
domain_to_backend,
)
class TestValidateDomain:
"""Tests for validate_domain function."""
def test_valid_simple_domain(self):
"""Valid simple domain."""
assert validate_domain("example.com") is True
def test_valid_subdomain(self):
"""Valid subdomain."""
assert validate_domain("api.example.com") is True
def test_valid_deep_subdomain(self):
"""Valid deep subdomain."""
assert validate_domain("a.b.c.example.com") is True
def test_valid_domain_with_numbers(self):
"""Valid domain with numbers."""
assert validate_domain("api123.example.com") is True
def test_valid_domain_with_hyphen(self):
"""Valid domain with hyphens."""
assert validate_domain("my-api.example-site.com") is True
def test_valid_single_char_labels(self):
"""Valid domain with single character labels."""
assert validate_domain("a.b.c") is True
def test_valid_max_label_length(self):
"""Valid domain with max label length (63 chars)."""
label = "a" * 63
assert validate_domain(f"{label}.com") is True
def test_invalid_empty_domain(self):
"""Empty domain is invalid."""
assert validate_domain("") is False
def test_invalid_none_domain(self):
"""None domain is invalid."""
assert validate_domain(None) is False
def test_invalid_starts_with_hyphen(self):
"""Domain starting with hyphen is invalid."""
assert validate_domain("-example.com") is False
def test_invalid_ends_with_hyphen(self):
"""Domain label ending with hyphen is invalid."""
assert validate_domain("example-.com") is False
def test_invalid_double_dot(self):
"""Domain with double dot is invalid."""
assert validate_domain("example..com") is False
def test_invalid_starts_with_dot(self):
"""Domain starting with dot is invalid."""
assert validate_domain(".example.com") is False
def test_invalid_special_characters(self):
"""Domain with special characters is invalid."""
assert validate_domain("example@.com") is False
assert validate_domain("example!.com") is False
assert validate_domain("example$.com") is False
def test_invalid_underscore(self):
"""Domain with underscore is invalid."""
assert validate_domain("my_api.example.com") is False
def test_invalid_too_long(self):
"""Domain exceeding 253 chars is invalid."""
long_domain = "a" * 254
assert validate_domain(long_domain) is False
def test_invalid_label_too_long(self):
"""Domain label exceeding 63 chars is invalid."""
label = "a" * 64
assert validate_domain(f"{label}.com") is False
def test_valid_numeric_domain(self):
"""Domain with all numeric label is valid."""
assert validate_domain("123.example.com") is True
def test_invalid_only_dots(self):
"""Domain with only dots is invalid."""
assert validate_domain("...") is False
class TestValidateIP:
"""Tests for validate_ip function."""
def test_valid_ipv4(self):
"""Valid IPv4 address."""
assert validate_ip("192.168.1.1") is True
assert validate_ip("10.0.0.1") is True
assert validate_ip("255.255.255.255") is True
assert validate_ip("0.0.0.0") is True
def test_valid_ipv6(self):
"""Valid IPv6 address."""
assert validate_ip("::1") is True
assert validate_ip("2001:db8::1") is True
assert validate_ip("fe80::1") is True
assert validate_ip("2001:0db8:0000:0000:0000:0000:0000:0001") is True
def test_invalid_empty_string(self):
"""Empty string is invalid by default."""
assert validate_ip("") is False
def test_valid_empty_string_when_allowed(self):
"""Empty string is valid when allow_empty=True."""
assert validate_ip("", allow_empty=True) is True
def test_invalid_none(self):
"""None is invalid."""
assert validate_ip(None) is False
def test_invalid_hostname(self):
"""Hostname is not a valid IP."""
assert validate_ip("example.com") is False
def test_invalid_ipv4_out_of_range(self):
"""IPv4 with octets out of range is invalid."""
assert validate_ip("256.1.1.1") is False
assert validate_ip("1.1.1.300") is False
def test_invalid_ipv4_format(self):
"""Invalid IPv4 format."""
assert validate_ip("192.168.1") is False
assert validate_ip("192.168.1.1.1") is False
def test_invalid_ipv6_format(self):
"""Invalid IPv6 format."""
assert validate_ip("2001:db8:::1") is False
assert validate_ip("gggg::1") is False
def test_invalid_mixed_format(self):
"""Mixed invalid format."""
assert validate_ip("192.168.1.1:8080") is False
class TestValidatePort:
"""Tests for validate_port function."""
def test_valid_port_min(self):
"""Valid minimum port."""
assert validate_port("1") is True
def test_valid_port_max(self):
"""Valid maximum port."""
assert validate_port("65535") is True
def test_valid_port_common(self):
"""Valid common ports."""
assert validate_port("80") is True
assert validate_port("443") is True
assert validate_port("8080") is True
def test_invalid_port_zero(self):
"""Port 0 is invalid."""
assert validate_port("0") is False
def test_invalid_port_negative(self):
"""Negative port is invalid."""
assert validate_port("-1") is False
def test_invalid_port_too_high(self):
"""Port above 65535 is invalid."""
assert validate_port("65536") is False
def test_invalid_port_empty(self):
"""Empty port is invalid."""
assert validate_port("") is False
def test_invalid_port_none(self):
"""None port is invalid."""
assert validate_port(None) is False
def test_invalid_port_not_numeric(self):
"""Non-numeric port is invalid."""
assert validate_port("abc") is False
assert validate_port("80a") is False
def test_invalid_port_float(self):
"""Float port is invalid."""
assert validate_port("80.5") is False
class TestValidateBackendName:
"""Tests for validate_backend_name function."""
def test_valid_pool_name(self):
"""Valid pool backend names."""
assert validate_backend_name("pool_1") is True
assert validate_backend_name("pool_100") is True
def test_valid_alphanumeric(self):
"""Valid alphanumeric names."""
assert validate_backend_name("backend1") is True
assert validate_backend_name("my_backend") is True
assert validate_backend_name("my-backend") is True
def test_valid_mixed(self):
"""Valid mixed character names."""
assert validate_backend_name("api_example_com_backend") is True
assert validate_backend_name("my-api-backend-1") is True
def test_invalid_empty(self):
"""Empty name is invalid."""
assert validate_backend_name("") is False
def test_invalid_none(self):
"""None name is invalid."""
assert validate_backend_name(None) is False
def test_invalid_special_chars(self):
"""Names with special characters are invalid."""
assert validate_backend_name("backend@1") is False
assert validate_backend_name("my.backend") is False
assert validate_backend_name("my/backend") is False
assert validate_backend_name("my backend") is False
def test_invalid_too_long(self):
"""Name exceeding 255 chars is invalid."""
long_name = "a" * 256
assert validate_backend_name(long_name) is False
def test_valid_max_length(self):
"""Name at exactly 255 chars is valid."""
max_name = "a" * 255
assert validate_backend_name(max_name) is True
class TestDomainToBackend:
"""Tests for domain_to_backend function."""
def test_simple_domain(self):
"""Simple domain conversion."""
assert domain_to_backend("example.com") == "example_com"
def test_subdomain(self):
"""Subdomain conversion."""
assert domain_to_backend("api.example.com") == "api_example_com"
def test_domain_with_hyphens(self):
"""Domain with hyphens."""
result = domain_to_backend("my-api.example.com")
assert result == "my_api_example_com"
def test_complex_domain(self):
"""Complex domain conversion."""
result = domain_to_backend("a.b.c.example-site.com")
assert result == "a_b_c_example_site_com"
def test_already_simple(self):
"""Domain that's already mostly valid."""
result = domain_to_backend("example123")
assert result == "example123"
def test_invalid_result_raises(self):
"""Invalid conversion result raises ValueError."""
# This should never happen with real domains, but test the safeguard
with pytest.raises(ValueError):
# Mock a case where conversion would fail
domain_to_backend("")