tests.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. # -*- coding: utf-8 -*-
  2. from __future__ import unicode_literals
  3. import io
  4. import os
  5. import re
  6. import types
  7. from datetime import datetime, timedelta
  8. from unittest import TestCase
  9. from django.core.exceptions import ValidationError
  10. from django.core.validators import (
  11. BaseValidator, DecimalValidator, EmailValidator, MaxLengthValidator,
  12. MaxValueValidator, MinLengthValidator, MinValueValidator, RegexValidator,
  13. URLValidator, int_list_validator, validate_comma_separated_integer_list,
  14. validate_email, validate_integer, validate_ipv4_address,
  15. validate_ipv6_address, validate_ipv46_address, validate_slug,
  16. validate_unicode_slug,
  17. )
  18. from django.test import SimpleTestCase
  19. from django.test.utils import str_prefix
  20. from django.utils._os import upath
  21. NOW = datetime.now()
  22. EXTENDED_SCHEMES = ['http', 'https', 'ftp', 'ftps', 'git', 'file', 'git+ssh']
  23. TEST_DATA = [
  24. # (validator, value, expected),
  25. (validate_integer, '42', None),
  26. (validate_integer, '-42', None),
  27. (validate_integer, -42, None),
  28. (validate_integer, -42.5, ValidationError),
  29. (validate_integer, None, ValidationError),
  30. (validate_integer, 'a', ValidationError),
  31. (validate_integer, '\n42', ValidationError),
  32. (validate_integer, '42\n', ValidationError),
  33. (validate_email, 'email@here.com', None),
  34. (validate_email, 'weirder-email@here.and.there.com', None),
  35. (validate_email, 'email@[127.0.0.1]', None),
  36. (validate_email, 'email@[2001:dB8::1]', None),
  37. (validate_email, 'email@[2001:dB8:0:0:0:0:0:1]', None),
  38. (validate_email, 'email@[::fffF:127.0.0.1]', None),
  39. (validate_email, 'example@valid-----hyphens.com', None),
  40. (validate_email, 'example@valid-with-hyphens.com', None),
  41. (validate_email, 'test@domain.with.idn.tld.उदाहरण.परीक्षा', None),
  42. (validate_email, 'email@localhost', None),
  43. (EmailValidator(whitelist=['localdomain']), 'email@localdomain', None),
  44. (validate_email, '"test@test"@example.com', None),
  45. (validate_email, 'example@atm.%s' % ('a' * 63), None),
  46. (validate_email, 'example@%s.atm' % ('a' * 63), None),
  47. (validate_email, 'example@%s.%s.atm' % ('a' * 63, 'b' * 10), None),
  48. (validate_email, 'example@atm.%s' % ('a' * 64), ValidationError),
  49. (validate_email, 'example@%s.atm.%s' % ('b' * 64, 'a' * 63), ValidationError),
  50. (validate_email, None, ValidationError),
  51. (validate_email, '', ValidationError),
  52. (validate_email, 'abc', ValidationError),
  53. (validate_email, 'abc@', ValidationError),
  54. (validate_email, 'abc@bar', ValidationError),
  55. (validate_email, 'a @x.cz', ValidationError),
  56. (validate_email, 'abc@.com', ValidationError),
  57. (validate_email, 'something@@somewhere.com', ValidationError),
  58. (validate_email, 'email@127.0.0.1', ValidationError),
  59. (validate_email, 'email@[127.0.0.256]', ValidationError),
  60. (validate_email, 'email@[2001:db8::12345]', ValidationError),
  61. (validate_email, 'email@[2001:db8:0:0:0:0:1]', ValidationError),
  62. (validate_email, 'email@[::ffff:127.0.0.256]', ValidationError),
  63. (validate_email, 'example@invalid-.com', ValidationError),
  64. (validate_email, 'example@-invalid.com', ValidationError),
  65. (validate_email, 'example@invalid.com-', ValidationError),
  66. (validate_email, 'example@inv-.alid-.com', ValidationError),
  67. (validate_email, 'example@inv-.-alid.com', ValidationError),
  68. (validate_email, 'test@example.com\n\n<script src="x.js">', ValidationError),
  69. # Quoted-string format (CR not allowed)
  70. (validate_email, '"\\\011"@here.com', None),
  71. (validate_email, '"\\\012"@here.com', ValidationError),
  72. (validate_email, 'trailingdot@shouldfail.com.', ValidationError),
  73. # Max length of domain name labels is 63 characters per RFC 1034.
  74. (validate_email, 'a@%s.us' % ('a' * 63), None),
  75. (validate_email, 'a@%s.us' % ('a' * 64), ValidationError),
  76. # Trailing newlines in username or domain not allowed
  77. (validate_email, 'a@b.com\n', ValidationError),
  78. (validate_email, 'a\n@b.com', ValidationError),
  79. (validate_email, '"test@test"\n@example.com', ValidationError),
  80. (validate_email, 'a@[127.0.0.1]\n', ValidationError),
  81. (validate_slug, 'slug-ok', None),
  82. (validate_slug, 'longer-slug-still-ok', None),
  83. (validate_slug, '--------', None),
  84. (validate_slug, 'nohyphensoranything', None),
  85. (validate_slug, 'a', None),
  86. (validate_slug, '1', None),
  87. (validate_slug, 'a1', None),
  88. (validate_slug, '', ValidationError),
  89. (validate_slug, ' text ', ValidationError),
  90. (validate_slug, ' ', ValidationError),
  91. (validate_slug, 'some@mail.com', ValidationError),
  92. (validate_slug, '你好', ValidationError),
  93. (validate_slug, '你 好', ValidationError),
  94. (validate_slug, '\n', ValidationError),
  95. (validate_slug, 'trailing-newline\n', ValidationError),
  96. (validate_unicode_slug, 'slug-ok', None),
  97. (validate_unicode_slug, 'longer-slug-still-ok', None),
  98. (validate_unicode_slug, '--------', None),
  99. (validate_unicode_slug, 'nohyphensoranything', None),
  100. (validate_unicode_slug, 'a', None),
  101. (validate_unicode_slug, '1', None),
  102. (validate_unicode_slug, 'a1', None),
  103. (validate_unicode_slug, '你好', None),
  104. (validate_unicode_slug, '', ValidationError),
  105. (validate_unicode_slug, ' text ', ValidationError),
  106. (validate_unicode_slug, ' ', ValidationError),
  107. (validate_unicode_slug, 'some@mail.com', ValidationError),
  108. (validate_unicode_slug, '\n', ValidationError),
  109. (validate_unicode_slug, '你 好', ValidationError),
  110. (validate_unicode_slug, 'trailing-newline\n', ValidationError),
  111. (validate_ipv4_address, '1.1.1.1', None),
  112. (validate_ipv4_address, '255.0.0.0', None),
  113. (validate_ipv4_address, '0.0.0.0', None),
  114. (validate_ipv4_address, '256.1.1.1', ValidationError),
  115. (validate_ipv4_address, '25.1.1.', ValidationError),
  116. (validate_ipv4_address, '25,1,1,1', ValidationError),
  117. (validate_ipv4_address, '25.1 .1.1', ValidationError),
  118. (validate_ipv4_address, '1.1.1.1\n', ValidationError),
  119. # validate_ipv6_address uses django.utils.ipv6, which
  120. # is tested in much greater detail in its own testcase
  121. (validate_ipv6_address, 'fe80::1', None),
  122. (validate_ipv6_address, '::1', None),
  123. (validate_ipv6_address, '1:2:3:4:5:6:7:8', None),
  124. (validate_ipv6_address, '1:2', ValidationError),
  125. (validate_ipv6_address, '::zzz', ValidationError),
  126. (validate_ipv6_address, '12345::', ValidationError),
  127. (validate_ipv46_address, '1.1.1.1', None),
  128. (validate_ipv46_address, '255.0.0.0', None),
  129. (validate_ipv46_address, '0.0.0.0', None),
  130. (validate_ipv46_address, 'fe80::1', None),
  131. (validate_ipv46_address, '::1', None),
  132. (validate_ipv46_address, '1:2:3:4:5:6:7:8', None),
  133. (validate_ipv46_address, '256.1.1.1', ValidationError),
  134. (validate_ipv46_address, '25.1.1.', ValidationError),
  135. (validate_ipv46_address, '25,1,1,1', ValidationError),
  136. (validate_ipv46_address, '25.1 .1.1', ValidationError),
  137. (validate_ipv46_address, '1:2', ValidationError),
  138. (validate_ipv46_address, '::zzz', ValidationError),
  139. (validate_ipv46_address, '12345::', ValidationError),
  140. (validate_comma_separated_integer_list, '1', None),
  141. (validate_comma_separated_integer_list, '12', None),
  142. (validate_comma_separated_integer_list, '1,2', None),
  143. (validate_comma_separated_integer_list, '1,2,3', None),
  144. (validate_comma_separated_integer_list, '10,32', None),
  145. (validate_comma_separated_integer_list, '', ValidationError),
  146. (validate_comma_separated_integer_list, 'a', ValidationError),
  147. (validate_comma_separated_integer_list, 'a,b,c', ValidationError),
  148. (validate_comma_separated_integer_list, '1, 2, 3', ValidationError),
  149. (validate_comma_separated_integer_list, ',', ValidationError),
  150. (validate_comma_separated_integer_list, '1,2,3,', ValidationError),
  151. (validate_comma_separated_integer_list, '1,2,', ValidationError),
  152. (validate_comma_separated_integer_list, ',1', ValidationError),
  153. (validate_comma_separated_integer_list, '1,,2', ValidationError),
  154. (int_list_validator(sep='.'), '1.2.3', None),
  155. (int_list_validator(sep='.', allow_negative=True), '1.2.3', None),
  156. (int_list_validator(allow_negative=True), '-1,-2,3', None),
  157. (int_list_validator(allow_negative=True), '1,-2,-12', None),
  158. (int_list_validator(), '-1,2,3', ValidationError),
  159. (int_list_validator(sep='.'), '1,2,3', ValidationError),
  160. (int_list_validator(sep='.'), '1.2.3\n', ValidationError),
  161. (MaxValueValidator(10), 10, None),
  162. (MaxValueValidator(10), -10, None),
  163. (MaxValueValidator(10), 0, None),
  164. (MaxValueValidator(NOW), NOW, None),
  165. (MaxValueValidator(NOW), NOW - timedelta(days=1), None),
  166. (MaxValueValidator(0), 1, ValidationError),
  167. (MaxValueValidator(NOW), NOW + timedelta(days=1), ValidationError),
  168. (MinValueValidator(-10), -10, None),
  169. (MinValueValidator(-10), 10, None),
  170. (MinValueValidator(-10), 0, None),
  171. (MinValueValidator(NOW), NOW, None),
  172. (MinValueValidator(NOW), NOW + timedelta(days=1), None),
  173. (MinValueValidator(0), -1, ValidationError),
  174. (MinValueValidator(NOW), NOW - timedelta(days=1), ValidationError),
  175. (MaxLengthValidator(10), '', None),
  176. (MaxLengthValidator(10), 10 * 'x', None),
  177. (MaxLengthValidator(10), 15 * 'x', ValidationError),
  178. (MinLengthValidator(10), 15 * 'x', None),
  179. (MinLengthValidator(10), 10 * 'x', None),
  180. (MinLengthValidator(10), '', ValidationError),
  181. (URLValidator(EXTENDED_SCHEMES), 'file://localhost/path', None),
  182. (URLValidator(EXTENDED_SCHEMES), 'git://example.com/', None),
  183. (URLValidator(EXTENDED_SCHEMES), 'git+ssh://git@github.com/example/hg-git.git', None),
  184. (URLValidator(EXTENDED_SCHEMES), 'git://-invalid.com', ValidationError),
  185. # Trailing newlines not accepted
  186. (URLValidator(), 'http://www.djangoproject.com/\n', ValidationError),
  187. (URLValidator(), 'http://[::ffff:192.9.5.5]\n', ValidationError),
  188. # Trailing junk does not take forever to reject
  189. (URLValidator(), 'http://www.asdasdasdasdsadfm.com.br ', ValidationError),
  190. (URLValidator(), 'http://www.asdasdasdasdsadfm.com.br z', ValidationError),
  191. (BaseValidator(True), True, None),
  192. (BaseValidator(True), False, ValidationError),
  193. (RegexValidator(), '', None),
  194. (RegexValidator(), 'x1x2', None),
  195. (RegexValidator('[0-9]+'), 'xxxxxx', ValidationError),
  196. (RegexValidator('[0-9]+'), '1234', None),
  197. (RegexValidator(re.compile('[0-9]+')), '1234', None),
  198. (RegexValidator('.*'), '', None),
  199. (RegexValidator(re.compile('.*')), '', None),
  200. (RegexValidator('.*'), 'xxxxx', None),
  201. (RegexValidator('x'), 'y', ValidationError),
  202. (RegexValidator(re.compile('x')), 'y', ValidationError),
  203. (RegexValidator('x', inverse_match=True), 'y', None),
  204. (RegexValidator(re.compile('x'), inverse_match=True), 'y', None),
  205. (RegexValidator('x', inverse_match=True), 'x', ValidationError),
  206. (RegexValidator(re.compile('x'), inverse_match=True), 'x', ValidationError),
  207. (RegexValidator('x', flags=re.IGNORECASE), 'y', ValidationError),
  208. (RegexValidator('a'), 'A', ValidationError),
  209. (RegexValidator('a', flags=re.IGNORECASE), 'A', None),
  210. ]
  211. def create_path(filename):
  212. return os.path.abspath(os.path.join(os.path.dirname(upath(__file__)), filename))
  213. # Add valid and invalid URL tests.
  214. # This only tests the validator without extended schemes.
  215. with io.open(create_path('valid_urls.txt'), encoding='utf8') as f:
  216. for url in f:
  217. TEST_DATA.append((URLValidator(), url.strip(), None))
  218. with io.open(create_path('invalid_urls.txt'), encoding='utf8') as f:
  219. for url in f:
  220. TEST_DATA.append((URLValidator(), url.strip(), ValidationError))
  221. def create_simple_test_method(validator, expected, value, num):
  222. if expected is not None and issubclass(expected, Exception):
  223. test_mask = 'test_%s_raises_error_%d'
  224. def test_func(self):
  225. # assertRaises not used, so as to be able to produce an error message
  226. # containing the tested value
  227. try:
  228. validator(value)
  229. except expected:
  230. pass
  231. else:
  232. self.fail("%s not raised when validating '%s'" % (
  233. expected.__name__, value))
  234. else:
  235. test_mask = 'test_%s_%d'
  236. def test_func(self):
  237. try:
  238. self.assertEqual(expected, validator(value))
  239. except ValidationError as e:
  240. self.fail("Validation of '%s' failed. Error message was: %s" % (
  241. value, str(e)))
  242. if isinstance(validator, types.FunctionType):
  243. val_name = validator.__name__
  244. else:
  245. val_name = validator.__class__.__name__
  246. test_name = test_mask % (val_name, num)
  247. return test_name, test_func
  248. # Dynamically assemble a test class with the contents of TEST_DATA
  249. class TestSimpleValidators(SimpleTestCase):
  250. def test_single_message(self):
  251. v = ValidationError('Not Valid')
  252. self.assertEqual(str(v), str_prefix("[%(_)s'Not Valid']"))
  253. self.assertEqual(repr(v), str_prefix("ValidationError([%(_)s'Not Valid'])"))
  254. def test_message_list(self):
  255. v = ValidationError(['First Problem', 'Second Problem'])
  256. self.assertEqual(str(v), str_prefix("[%(_)s'First Problem', %(_)s'Second Problem']"))
  257. self.assertEqual(repr(v), str_prefix("ValidationError([%(_)s'First Problem', %(_)s'Second Problem'])"))
  258. def test_message_dict(self):
  259. v = ValidationError({'first': ['First Problem']})
  260. self.assertEqual(str(v), str_prefix("{%(_)s'first': [%(_)s'First Problem']}"))
  261. self.assertEqual(repr(v), str_prefix("ValidationError({%(_)s'first': [%(_)s'First Problem']})"))
  262. def test_regex_validator_flags(self):
  263. try:
  264. RegexValidator(re.compile('a'), flags=re.IGNORECASE)
  265. except TypeError:
  266. pass
  267. else:
  268. self.fail("TypeError not raised when flags and pre-compiled regex in RegexValidator")
  269. def test_max_length_validator_message(self):
  270. v = MaxLengthValidator(16, message='"%(value)s" has more than %(limit_value)d characters.')
  271. with self.assertRaisesMessage(ValidationError, '"djangoproject.com" has more than 16 characters.'):
  272. v('djangoproject.com')
  273. test_counter = 0
  274. for validator, value, expected in TEST_DATA:
  275. name, method = create_simple_test_method(validator, expected, value, test_counter)
  276. setattr(TestSimpleValidators, name, method)
  277. test_counter += 1
  278. class TestValidatorEquality(TestCase):
  279. """
  280. Tests that validators have valid equality operators (#21638)
  281. """
  282. def test_regex_equality(self):
  283. self.assertEqual(
  284. RegexValidator(r'^(?:[a-z0-9\.\-]*)://'),
  285. RegexValidator(r'^(?:[a-z0-9\.\-]*)://'),
  286. )
  287. self.assertNotEqual(
  288. RegexValidator(r'^(?:[a-z0-9\.\-]*)://'),
  289. RegexValidator(r'^(?:[0-9\.\-]*)://'),
  290. )
  291. self.assertEqual(
  292. RegexValidator(r'^(?:[a-z0-9\.\-]*)://', "oh noes", "invalid"),
  293. RegexValidator(r'^(?:[a-z0-9\.\-]*)://', "oh noes", "invalid"),
  294. )
  295. self.assertNotEqual(
  296. RegexValidator(r'^(?:[a-z0-9\.\-]*)://', "oh", "invalid"),
  297. RegexValidator(r'^(?:[a-z0-9\.\-]*)://', "oh noes", "invalid"),
  298. )
  299. self.assertNotEqual(
  300. RegexValidator(r'^(?:[a-z0-9\.\-]*)://', "oh noes", "invalid"),
  301. RegexValidator(r'^(?:[a-z0-9\.\-]*)://'),
  302. )
  303. self.assertNotEqual(
  304. RegexValidator('', flags=re.IGNORECASE),
  305. RegexValidator(''),
  306. )
  307. self.assertNotEqual(
  308. RegexValidator(''),
  309. RegexValidator('', inverse_match=True),
  310. )
  311. def test_regex_equality_nocache(self):
  312. pattern = r'^(?:[a-z0-9\.\-]*)://'
  313. left = RegexValidator(pattern)
  314. re.purge()
  315. right = RegexValidator(pattern)
  316. self.assertEqual(
  317. left,
  318. right,
  319. )
  320. def test_regex_equality_blank(self):
  321. self.assertEqual(
  322. RegexValidator(),
  323. RegexValidator(),
  324. )
  325. def test_email_equality(self):
  326. self.assertEqual(
  327. EmailValidator(),
  328. EmailValidator(),
  329. )
  330. self.assertNotEqual(
  331. EmailValidator(message="BAD EMAIL"),
  332. EmailValidator(),
  333. )
  334. self.assertEqual(
  335. EmailValidator(message="BAD EMAIL", code="bad"),
  336. EmailValidator(message="BAD EMAIL", code="bad"),
  337. )
  338. def test_basic_equality(self):
  339. self.assertEqual(
  340. MaxValueValidator(44),
  341. MaxValueValidator(44),
  342. )
  343. self.assertNotEqual(
  344. MaxValueValidator(44),
  345. MinValueValidator(44),
  346. )
  347. self.assertNotEqual(
  348. MinValueValidator(45),
  349. MinValueValidator(11),
  350. )
  351. def test_decimal_equality(self):
  352. self.assertEqual(
  353. DecimalValidator(1, 2),
  354. DecimalValidator(1, 2),
  355. )
  356. self.assertNotEqual(
  357. DecimalValidator(1, 2),
  358. DecimalValidator(1, 1),
  359. )
  360. self.assertNotEqual(
  361. DecimalValidator(1, 2),
  362. DecimalValidator(2, 2),
  363. )
  364. self.assertNotEqual(
  365. DecimalValidator(1, 2),
  366. MinValueValidator(11),
  367. )