1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
|
from django.core.exceptions import ValidationError
from django.core.validators import URLValidator
from django.forms import URLField
from django.test import SimpleTestCase
from . import FormFieldAssertionsMixin
class URLFieldTest(FormFieldAssertionsMixin, SimpleTestCase):
def test_urlfield_widget(self):
f = URLField()
self.assertWidgetRendersTo(f, '<input type="url" name="f" id="id_f" required>')
def test_urlfield_widget_max_min_length(self):
f = URLField(min_length=15, max_length=20)
self.assertEqual("http://example.com", f.clean("http://example.com"))
self.assertWidgetRendersTo(
f,
'<input id="id_f" type="url" name="f" maxlength="20" '
'minlength="15" required>',
)
msg = "'Ensure this value has at least 15 characters (it has 12).'"
with self.assertRaisesMessage(ValidationError, msg):
f.clean("http://f.com")
msg = "'Ensure this value has at most 20 characters (it has 37).'"
with self.assertRaisesMessage(ValidationError, msg):
f.clean("http://abcdefghijklmnopqrstuvwxyz.com")
def test_urlfield_clean(self):
f = URLField(required=False)
tests = [
("http://localhost", "http://localhost"),
("http://example.com", "http://example.com"),
("http://example.com/test", "http://example.com/test"),
("http://example.com.", "http://example.com."),
("http://www.example.com", "http://www.example.com"),
("http://www.example.com:8000/test", "http://www.example.com:8000/test"),
(
"http://example.com?some_param=some_value",
"http://example.com?some_param=some_value",
),
("valid-with-hyphens.com", "https://valid-with-hyphens.com"),
("subdomain.domain.com", "https://subdomain.domain.com"),
("http://200.8.9.10", "http://200.8.9.10"),
("http://200.8.9.10:8000/test", "http://200.8.9.10:8000/test"),
("http://valid-----hyphens.com", "http://valid-----hyphens.com"),
(
"http://some.idn.xyzäöüßabc.domain.com:123/blah",
"http://some.idn.xyz\xe4\xf6\xfc\xdfabc.domain.com:123/blah",
),
(
"www.example.com/s/http://code.djangoproject.com/ticket/13804",
"https://www.example.com/s/http://code.djangoproject.com/ticket/13804",
),
# Normalization.
("http://example.com/ ", "http://example.com/"),
# Valid IDN.
("http://עברית.idn.icann.org/", "http://עברית.idn.icann.org/"),
("http://sãopaulo.com/", "http://sãopaulo.com/"),
("http://sãopaulo.com.br/", "http://sãopaulo.com.br/"),
("http://пример.испытание/", "http://пример.испытание/"),
("http://مثال.إختبار/", "http://مثال.إختبار/"),
("http://例子.测试/", "http://例子.测试/"),
("http://例子.測試/", "http://例子.測試/"),
(
"http://उदाहरण.परीक्षा/",
"http://उदाहरण.परीक्षा/",
),
("http://例え.テスト/", "http://例え.テスト/"),
("http://مثال.آزمایشی/", "http://مثال.آزمایشی/"),
("http://실례.테스트/", "http://실례.테스트/"),
("http://العربية.idn.icann.org/", "http://العربية.idn.icann.org/"),
# IPv6.
("http://[12:34::3a53]/", "http://[12:34::3a53]/"),
("http://[a34:9238::]:8080/", "http://[a34:9238::]:8080/"),
# IPv6 without scheme.
("[12:34::3a53]/", "https://[12:34::3a53]/"),
# IDN domain without scheme but with port.
("ñandú.es:8080/", "https://ñandú.es:8080/"),
# Scheme-relative.
("//example.com", "https://example.com"),
("//example.com/path", "https://example.com/path"),
# Whitespace stripped.
("\t\n//example.com \n\t\n", "https://example.com"),
("\t\nhttp://example.com \n\t\n", "http://example.com"),
]
for url, expected in tests:
with self.subTest(url=url):
self.assertEqual(f.clean(url), expected)
def test_urlfield_clean_invalid(self):
f = URLField()
tests = [
"foo",
"com.",
".",
"http://",
"http://example",
"http://example.",
"http://.com",
"http://invalid-.com",
"http://-invalid.com",
"http://inv-.alid-.com",
"http://inv-.-alid.com",
"[a",
"http://[a",
# Non-string.
23,
# Hangs "forever" before fixing a catastrophic backtracking,
# see #11198.
"http://%s" % ("X" * 60,),
# A second example, to make sure the problem is really addressed,
# even on domains that don't fail the domain label length check in
# the regex.
"http://%s" % ("X" * 200,),
# Scheme prepend yields a structurally invalid URL.
"////]@N.AN",
# Scheme prepend yields an empty hostname.
"#@A.bO",
# Known problematic unicode chars.
"http://" + "¾" * 200,
# Non-ASCII character before the first colon.
"¾:example.com",
# ASCII digit before the first colon.
"1http://example.com",
# Empty scheme.
"://example.com",
":example.com",
]
msg = "'Enter a valid URL.'"
for value in tests:
with self.subTest(value=value):
with self.assertRaisesMessage(ValidationError, msg):
f.clean(value)
def test_urlfield_clean_required(self):
f = URLField()
msg = "'This field is required.'"
with self.assertRaisesMessage(ValidationError, msg):
f.clean(None)
with self.assertRaisesMessage(ValidationError, msg):
f.clean("")
def test_urlfield_clean_not_required(self):
f = URLField(required=False)
self.assertEqual(f.clean(None), "")
self.assertEqual(f.clean(""), "")
def test_urlfield_strip_on_none_value(self):
f = URLField(required=False, empty_value=None)
self.assertIsNone(f.clean(""))
self.assertIsNone(f.clean(None))
def test_urlfield_unable_to_set_strip_kwarg(self):
msg = "got multiple values for keyword argument 'strip'"
with self.assertRaisesMessage(TypeError, msg):
URLField(strip=False)
def test_urlfield_assume_scheme(self):
f = URLField()
self.assertEqual(f.clean("example.com"), "https://example.com")
f = URLField(assume_scheme="http")
self.assertEqual(f.clean("example.com"), "http://example.com")
f = URLField(assume_scheme="https")
self.assertEqual(f.clean("example.com"), "https://example.com")
def test_urlfield_assume_scheme_when_colons(self):
f = URLField()
tests = [
# Port number.
("http://example.com:8080/", "http://example.com:8080/"),
("https://example.com:443/path", "https://example.com:443/path"),
# Userinfo with password.
("http://user:pass@example.com", "http://user:pass@example.com"),
(
"http://user:pass@example.com:8080/",
"http://user:pass@example.com:8080/",
),
# Colon in path segment.
("http://example.com/path:segment", "http://example.com/path:segment"),
("http://example.com/a:b/c:d", "http://example.com/a:b/c:d"),
# Colon in query string.
("http://example.com/?key=val:ue", "http://example.com/?key=val:ue"),
# Colon in fragment.
("http://example.com/#section:1", "http://example.com/#section:1"),
# IPv6 -- multiple colons in host.
("http://[::1]/", "http://[::1]/"),
("http://[2001:db8::1]/", "http://[2001:db8::1]/"),
("http://[2001:db8::1]:8080/", "http://[2001:db8::1]:8080/"),
# Colons across multiple components.
(
"http://user:pass@example.com:8080/path:x?q=a:b#id:1",
"http://user:pass@example.com:8080/path:x?q=a:b#id:1",
),
# FTP with port and userinfo.
(
"ftp://user:pass@ftp.example.com:21/file",
"ftp://user:pass@ftp.example.com:21/file",
),
(
"ftps://user:pass@ftp.example.com:990/",
"ftps://user:pass@ftp.example.com:990/",
),
# Scheme-relative URLs, starts with "//".
("//example.com:8080/path", "https://example.com:8080/path"),
("//user:pass@example.com/", "https://user:pass@example.com/"),
]
for value, expected in tests:
with self.subTest(value=value):
self.assertEqual(f.clean(value), expected)
def test_custom_validator_longer_max_length(self):
class CustomLongURLValidator(URLValidator):
max_length = 4096
class CustomURLField(URLField):
default_validators = [CustomLongURLValidator()]
field = CustomURLField()
# A URL with 4096 chars is valid given the custom validator.
prefix = "https://example.com/"
url = prefix + "a" * (4096 - len(prefix))
self.assertEqual(len(url), 4096)
# No ValidationError is raised.
field.clean(url)
|