diff --git a/tests/test_domain.py b/tests/test_domain.py index 1766212..d606ad7 100644 --- a/tests/test_domain.py +++ b/tests/test_domain.py @@ -110,3 +110,60 @@ def test_returns_failed_validation_invalid_top_level_domain( domain(value, consider_tld=consider_tld, rfc_1034=rfc_1034, rfc_2782=rfc_2782), ValidationError, ) + + +def _name_of_length(length: int): + """Build a dotted name of exactly `length` characters, ending with `.com`.""" + labels: list[str] = [] + remaining = length - len(".com") + while remaining > 0: + size = min(61, remaining) + if remaining - size == 1: # avoid leaving a lone "." behind + size -= 1 + labels.append("a" * size) + remaining -= size + 1 + name = ".".join(labels) + ".com" + assert len(name) == length + return name + + +@pytest.mark.parametrize( + "value", + [ + # total length: at most 253 characters (RFC 1035) + _name_of_length(253), + # label length: at most 63 characters (RFC 1035) + "a" * 63 + ".com", + # a label may start with a digit (RFC 1123) + "1host.example.com", + # the limit is in octets of the encoded name, not characters: 189 characters + # here, but 225 octets once IDNA-encoded + ".".join(["ü" * 30] * 6) + ".com", + ], +) +def test_returns_true_on_domain_within_rfc_length_limits(value: str): + """Test returns true on domain within RFC length limits.""" + assert domain(value) + + +@pytest.mark.parametrize( + "value", + [ + _name_of_length(254), + _name_of_length(255), + # many short, dot-separated labels that exceed 253 characters; see #413 + ".".join(["kld8MXQh6YalMqKRbfs895gMjW5T4p2EwToPoCSThPHHbXgmXc"] * 4) + + ".kld8MXQh6YalMqKRbfs895gMjW5T4p2EwToPoCSThPHHbXgmXcab", + "a" * 64 + ".com", + # fewer than 253 characters (220), but 262 octets once IDNA-encoded + ".".join(["ü" * 30] * 7) + ".com", + # the example from #407 (261 characters) + "www." + ".".join(["abcdefghijklmnop"] * 15) + ".fr", + # a label must not start or end with a hyphen + "-host.example.com", + "host-.example.com", + ], +) +def test_returns_failed_validation_on_domain_beyond_rfc_limits(value: str): + """Test returns failed validation on domain beyond RFC limits.""" + assert isinstance(domain(value), ValidationError) diff --git a/tests/test_hostname.py b/tests/test_hostname.py index b28e804..1fe2468 100644 --- a/tests/test_hostname.py +++ b/tests/test_hostname.py @@ -70,3 +70,42 @@ def test_returns_true_on_valid_hostname(value: str, rfc_1034: bool, rfc_2782: bo def test_returns_failed_validation_on_invalid_hostname(value: str, rfc_1034: bool, rfc_2782: bool): """Test returns failed validation on invalid hostname.""" assert isinstance(hostname(value, rfc_1034=rfc_1034, rfc_2782=rfc_2782), ValidationError) + + +@pytest.mark.parametrize( + "value", + [ + # total length: at most 253 characters (RFC 1035) + ".".join(["a" * 63, "a" * 63, "a" * 63, "a" * 61]), + # label length: at most 63 characters (RFC 1035) + "a" * 63 + ".com", + # a label may start with a digit (RFC 1123) + "1host.example.com", + # the limit is in octets of the encoded name, not characters + ".".join(["ü" * 30] * 6) + ".com", + ], +) +def test_returns_true_on_hostname_within_rfc_length_limits(value: str): + """Test returns true on hostname within RFC length limits.""" + assert hostname(value) + + +@pytest.mark.parametrize( + "value", + [ + # many short, dot-separated labels that exceed 253 characters; see #413 + ".".join(["kld8MXQh6YalMqKRbfs895gMjW5T4p2EwToPoCSThPHHbXgmXc"] * 4) + + ".kld8MXQh6YalMqKRbfs895gMjW5T4p2EwToPoCSThPHHbXgmXcab", + ".".join(["a" * 63, "a" * 63, "a" * 63, "a" * 62]), + "a" * 64 + ".com", + # fewer than 253 characters (220), but 262 octets once IDNA-encoded + ".".join(["ü" * 30] * 7) + ".com", + # a label must not start or end with a hyphen, nor contain an underscore + "-host.example.com", + "host-.example.com", + "ho_st.example.com", + ], +) +def test_returns_failed_validation_on_hostname_beyond_rfc_limits(value: str): + """Test returns failed validation on hostname beyond RFC limits.""" + assert isinstance(hostname(value), ValidationError)