# coding=utf-8
import sys
import pytest
from fqdn import FQDN
@pytest.fixture(params=(True, False))
def a_u(request):
return request.param
class TestFQDNValidation:
def test_constructor(self, a_u):
with pytest.raises(ValueError):
FQDN(None, allow_underscores=a_u)
# Python 3-specific tests
if sys.version_info >= (3, 0):
def test_constructor_raises_on_bytes(self, a_u):
with pytest.raises(ValueError):
FQDN(b"", allow_underscores=a_u)
with pytest.raises(ValueError):
FQDN(b"helloworld", allow_underscores=a_u)
def test_str(self, a_u):
d = "greatdomain.com"
f = FQDN(d, allow_underscores=a_u)
assert f.absolute == str(f)
def test_rfc_1035_s_2_3_4__label_max_length(self, a_u):
assert FQDN(
"www.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk.com",
allow_underscores=a_u,
).is_valid
assert FQDN(
"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk",
allow_underscores=a_u,
).is_valid
def test_rfc_1035_s_2_3_4__label_too_long(self, a_u):
self.__assert_invalid_from_seq("A" * 64, "com", allow_underscores=a_u)
self.__assert_invalid_from_seq("b" * 63, "A" * 64, "com", allow_underscores=a_u)
self.__assert_invalid_from_seq("com", "b" * 63, "A" * 64, allow_underscores=a_u)
def test_rfc_1035_s_2_3_4__name_too_long_254_octets(self, a_u):
parts = [(chr(ord("A") + i % 26)) for i in range(int(254 / 2) - 1)]
parts.append("co")
fqdn = ".".join(parts)
assert len(fqdn) == 254
self.__assert_invalid_from_seq(fqdn, allow_underscores=a_u)
def test_rfc_1035_s_2_3_4__name_ok_253_octets(self, a_u):
parts = [(chr(ord("A") + i % 26)) for i in range(int(254 / 2))]
fqdn = ".".join(parts)
assert len(fqdn) == 253
self.__assert_valid_from_seq(fqdn, allow_underscores=a_u)
def test_rfc_1035_s_3_1__trailing_byte(self, a_u):
parts = [(chr(ord("A") + i % 26)) for i in range(int(254 / 2))]
fqdn = ".".join(parts) + "."
assert len(fqdn) == 254
self.__assert_valid_from_seq(fqdn, allow_underscores=a_u)
def test_rfc_3696_s_2__label_invalid_starts_or_ends_with_hyphen(self):
self.__assert_invalid_fwd_and_bkwd_from_seq("-a", "com", allow_underscores=a_u)
self.__assert_invalid_fwd_and_bkwd_from_seq("a-", "com", allow_underscores=a_u)
self.__assert_invalid_fwd_and_bkwd_from_seq("-a-", "com", allow_underscores=a_u)
def test_rfc_3696_s_2__preferred_form_invalid_chars(self, a_u):
# these should use punycode instead
self.__assert_invalid_fwd_and_bkwd_from_seq("є", "com", allow_underscores=a_u)
self.__assert_invalid_fwd_and_bkwd_from_seq(
"le-tour-est-joué", "com", allow_underscores=a_u
)
self.__assert_invalid_fwd_and_bkwd_from_seq(
"invalid", "cóm", allow_underscores=a_u
)
self.__assert_invalid_fwd_and_bkwd_from_seq(
"ich-hätte-gern-ein-Umlaut", "de", allow_underscores=a_u
)
self.__assert_invalid_fwd_and_bkwd_from_seq(
"\x01", "com", allow_underscores=a_u
)
self.__assert_invalid_fwd_and_bkwd_from_seq(
"x", "\x01\x02\x01", allow_underscores=a_u
)
def test_underscores_extra_mode(self):
self.__assert_valid_fwd_and_bkwd_from_seq("_", "dog", allow_underscores=True)
self.__assert_valid_fwd_and_bkwd_from_seq("i_", "dog", allow_underscores=True)
self.__assert_valid_fwd_and_bkwd_from_seq("o_o", "dog", allow_underscores=True)
self.__assert_invalid_fwd_and_bkwd_from_seq("_", "dog", allow_underscores=False)
self.__assert_invalid_fwd_and_bkwd_from_seq(
"i_", "dog", allow_underscores=False
)
self.__assert_invalid_fwd_and_bkwd_from_seq(
"o_o", "dog", allow_underscores=False
)
def test_rfc_3696_s_2__valid(self):
assert FQDN("net", min_labels=1, allow_underscores=a_u).is_valid
assert FQDN("who.is", allow_underscores=a_u).is_valid
assert FQDN("bbc.co.uk", allow_underscores=a_u).is_valid
self.__assert_valid_fwd_and_bkwd_from_seq(
"sh4d05-7357", "c00-mm", allow_underscores=a_u
)
def test_rfc_1035_s_2_3_1__label_can_have_inital_digit(self, a_u):
self.__assert_valid_fwd_and_bkwd_from_seq("www", "1", allow_underscores=a_u)
self.__assert_valid_fwd_and_bkwd_from_seq("1w", "1", allow_underscores=a_u)
self.__assert_valid_fwd_and_bkwd_from_seq("1w", "a", allow_underscores=a_u)
self.__assert_valid_fwd_and_bkwd_from_seq("1w1", "d", allow_underscores=a_u)
self.__assert_valid_fwd_and_bkwd_from_seq("111", "a", allow_underscores=a_u)
self.__assert_valid_fwd_and_bkwd_from_seq("www", "1a", allow_underscores=a_u)
def test_rfc_1123__label_can_have_medial_and_terminal_digits(self, a_u):
self.__assert_valid_fwd_and_bkwd_from_seq("www1", "a", allow_underscores=a_u)
self.__assert_valid_fwd_and_bkwd_from_seq("ww1a", "c", allow_underscores=a_u)
self.__assert_valid_fwd_and_bkwd_from_seq("w2w", "c", allow_underscores=a_u)
self.__assert_valid_fwd_and_bkwd_from_seq("a111", "a", allow_underscores=a_u)
self.__assert_valid_fwd_and_bkwd_from_seq("a1c1", "a", allow_underscores=a_u)
def __assert_valid_fwd_and_bkwd_from_seq(self, *seq, **kwargs):
rseq = reversed(seq)
self.__assert_valid_from_seq(*rseq, **kwargs)
def __assert_invalid_fwd_and_bkwd_from_seq(self, *seq, **kwargs):
rseq = reversed(seq)
self.__assert_invalid_from_seq(*rseq, **kwargs)
def __assert_invalid_from_seq(self, *seq, **kwargs):
assert not (self.__is_valid_fqdn_from_labels_seq(seq, **kwargs))
def __assert_valid_from_seq(self, *seq, **kwargs):
assert self.__is_valid_fqdn_from_labels_seq(seq, **kwargs)
def __is_valid_fqdn_from_labels_seq(self, fqdn_labels_seq, **kwargs):
fqdn = ".".join(fqdn_labels_seq)
return FQDN(fqdn, **kwargs).is_valid
class TestMinLabels:
def test_labels_count(self, a_u):
assert FQDN("label").labels_count == 1
assert FQDN("label.").labels_count == 1
assert FQDN("label.babel").labels_count == 2
assert FQDN("label.babel.").labels_count == 2
assert FQDN(".label.babel.").labels_count == 3
def test_min_labels_defaults_to_require_2(self):
dn = FQDN("label")
assert dn._min_labels == 2
assert dn.labels_count == 1
assert not dn.is_valid
def test_min_labels_valid_set_to_1(self):
with pytest.raises(ValueError):
FQDN("", min_labels=1).is_valid
assert FQDN("label", min_labels=1).is_valid
assert not FQDN(".label", min_labels=1).is_valid
assert FQDN("label.babel", min_labels=1).is_valid
assert FQDN("label.babel.", min_labels=1).is_valid
assert not FQDN(".label.babel", min_labels=1).is_valid
def test_min_labels_valid_set_to_3(self):
assert not FQDN("label", min_labels=3).is_valid
assert not FQDN("label.babel", min_labels=3).is_valid
assert not FQDN(".babel", min_labels=3).is_valid
assert not FQDN("babel.", min_labels=3).is_valid
assert not FQDN(".babel.", min_labels=3).is_valid
assert not FQDN("label.babel.", min_labels=3).is_valid
assert not FQDN(".label.babel.", min_labels=3).is_valid
assert FQDN("fable.label.babel.", min_labels=3).is_valid
assert FQDN("fable.label.babel", min_labels=3).is_valid
class TestAbsoluteFQDN:
def test_absolute_fqdn(self, a_u):
assert FQDN("trainwreck.com.", allow_underscores=a_u).is_valid_absolute is True
def test_absolute_fqdn__fail(self, a_u):
assert FQDN("trainwreck.com", allow_underscores=a_u).is_valid_absolute is False
def test_to_absolute_fqdn_from_relative(self, a_u):
assert (
FQDN("trainwreck.com", allow_underscores=a_u).absolute == "trainwreck.com."
)
def test_to_absolute_fqdn_from_absolute(self, a_u):
assert (
FQDN("absolutetrainwreck.com.", allow_underscores=a_u).absolute
== "absolutetrainwreck.com."
)
def test_to_absolute_fqdn__raises_ValueError(self, a_u):
with pytest.raises(ValueError):
FQDN("trainwreckcom", allow_underscores=a_u).absolute
def test_relative_fqdn_true(self, a_u):
assert FQDN("relative.com", allow_underscores=a_u).is_valid_relative is True
def test_relative_fqdn_false(self, a_u):
assert FQDN("relative.com.", allow_underscores=a_u).is_valid_relative is False
class TestRelativeFQDN:
def test_relative_fqdn_from_relative(self, a_u):
assert (
FQDN("trainwreck.com", allow_underscores=a_u).relative == "trainwreck.com"
)
def test_relative_fqdn_from_absolute(self, a_u):
assert (
FQDN("trainwreck.com.", allow_underscores=a_u).relative == "trainwreck.com"
)
def test_relative_fqdn_from_invalid(self, a_u):
with pytest.raises(ValueError):
FQDN("trainwreck..", allow_underscores=a_u).relative
class TestEquality:
def test_absolutes_are_equal(self, a_u):
assert FQDN("trainwreck.com.", allow_underscores=a_u) == FQDN(
"trainwreck.com.", allow_underscores=a_u
)
def test_relatives_are_equal(self, a_u):
assert FQDN("trainwreck.com", allow_underscores=a_u) == FQDN(
"trainwreck.com", allow_underscores=a_u
)
def test_mismatch_are_equal(self, a_u):
assert FQDN("trainwreck.com.", allow_underscores=a_u) == FQDN(
"trainwreck.com", allow_underscores=a_u
)
def test_equality_is_case_insensitive(self, a_u):
assert FQDN(
"all-letters-were-created-equal.com.", allow_underscores=a_u
) == FQDN("ALL-LETTERS-WERE-CREATED-EQUAL.COM.", allow_underscores=a_u)
def test_strict_and_loose_can_be_equal(self):
assert FQDN("trainwreck.com.", allow_underscores=False) == FQDN(
"trainwreck.com", allow_underscores=True
)
class TestHash:
def test_is_hashable(self, a_u):
assert hash(FQDN("trainwreck.com."))
def test_absolutes_are_equal(self, a_u):
assert hash(FQDN("trainwreck.com.", allow_underscores=a_u)) == hash(
FQDN("trainwreck.com.", allow_underscores=a_u)
)
def test_relatives_are_equal(self, a_u):
assert hash(FQDN("trainwreck.com", allow_underscores=a_u)) == hash(
FQDN("trainwreck.com", allow_underscores=a_u)
)
def test_mismatch_are_equal(self, a_u):
assert hash(FQDN("trainwreck.com.", allow_underscores=a_u)) == hash(
FQDN("trainwreck.com", allow_underscores=a_u)
)
def test_equality_is_case_insensitive(self, a_u):
assert hash(
FQDN("all-letters-were-created-equal.com.", allow_underscores=a_u)
) == hash(FQDN("ALL-LETTERS-WERE-CREATED-EQUAL.COM.", allow_underscores=a_u))
def test_not_equal_to_string(self, a_u):
assert hash(FQDN("trainwreck.com.", allow_underscores=a_u)) != hash(
"trainwreck.com."
)
def test_different_fqdns_are_not_equal(self, a_u):
assert hash(FQDN("trainwreck.com.")) != hash(FQDN("test.com."))
def test_strict_and_loose_hashs_are_equal(self):
assert hash(FQDN("trainwreck.com.", allow_underscores=False)) == hash(
FQDN("trainwreck.com", allow_underscores=True)
)