test_utils.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. from django.core.exceptions import PermissionDenied
  2. from django.test import TestCase
  3. from django.test.client import RequestFactory
  4. from django.urls import reverse
  5. from misago.core.utils import (
  6. clean_return_path, format_plaintext_for_html, is_referer_local, is_request_to_misago,
  7. parse_iso8601_string, slugify, get_exception_message, clean_ids_list, get_host_from_address)
  8. class IsRequestToMisagoTests(TestCase):
  9. def test_is_request_to_misago(self):
  10. """
  11. is_request_to_misago correctly detects requests directed at Misago
  12. """
  13. VALID_PATHS = ('/', '/threads/', )
  14. INVALID_PATHS = ('', 'somewhere/', )
  15. misago_prefix = reverse('misago:index')
  16. for path in VALID_PATHS:
  17. request = RequestFactory().get('/')
  18. request.path = path
  19. self.assertTrue(
  20. is_request_to_misago(request),
  21. '"%s" is not overlapped by "%s"' % (path, misago_prefix)
  22. )
  23. for path in INVALID_PATHS:
  24. request = RequestFactory().get('/')
  25. request.path = path
  26. self.assertFalse(
  27. is_request_to_misago(request),
  28. '"%s" is overlapped by "%s"' % (path, misago_prefix)
  29. )
  30. class SlugifyTests(TestCase):
  31. def test_valid_slugify_output(self):
  32. """Misago's slugify correctly slugifies string"""
  33. test_cases = [
  34. ('Bob', 'bob'),
  35. ('Eric The Fish', 'eric-the-fish'),
  36. ('John Snow', 'john-snow'),
  37. ('J0n', 'j0n'),
  38. ('An###ne', 'anne'),
  39. ('S**t', 'st'),
  40. ('Łók', 'lok'),
  41. ]
  42. for original, slug in test_cases:
  43. self.assertEqual(slugify(original), slug)
  44. class ParseIso8601StringTests(TestCase):
  45. def test_valid_input(self):
  46. """util parses iso 8601 strings"""
  47. INPUTS = [
  48. '2016-10-22T20:55:39.185085Z',
  49. '2016-10-22T20:55:39.185085-01:00',
  50. '2016-10-22T20:55:39-01:00',
  51. '2016-10-22T20:55:39.185085+01:00',
  52. ]
  53. for test_input in INPUTS:
  54. self.assertTrue(parse_iso8601_string(test_input))
  55. def test_invalid_input(self):
  56. """util throws ValueError on invalid input"""
  57. INPUTS = [
  58. '',
  59. '2016-10-22',
  60. '2016-10-22T30:55:39.185085+11:00',
  61. '2016-10-22T20:55:39.18SSSSS5085Z',
  62. ]
  63. for test_input in INPUTS:
  64. with self.assertRaises(ValueError):
  65. self.assertTrue(parse_iso8601_string(test_input))
  66. PLAINTEXT_FORMAT_CASES = [
  67. ('Lorem ipsum.', '<p>Lorem ipsum.</p>'),
  68. ('Lorem <b>ipsum</b>.', '<p>Lorem &lt;b&gt;ipsum&lt;/b&gt;.</p>'),
  69. ('Lorem "ipsum" dolor met.', '<p>Lorem &quot;ipsum&quot; dolor met.</p>'),
  70. ('Lorem ipsum.\nDolor met.', '<p>Lorem ipsum.<br />Dolor met.</p>'),
  71. ('Lorem ipsum.\n\nDolor met.', '<p>Lorem ipsum.</p>\n\n<p>Dolor met.</p>'),
  72. (
  73. 'http://misago-project.org/login/',
  74. '<p><a href="http://misago-project.org/login/">http://misago-project.org/login/</a></p>'
  75. ),
  76. ]
  77. class FormatPlaintextForHtmlTests(TestCase):
  78. def test_format_plaintext_for_html(self):
  79. """format_plaintext_for_html correctly formats plaintext for html"""
  80. for plaintext, html in PLAINTEXT_FORMAT_CASES:
  81. output = format_plaintext_for_html(plaintext)
  82. assertion_message = """
  83. format_plaintext_for_html failed to produce expected output:
  84. expected: %s
  85. return: %s
  86. """ % (html, output)
  87. self.assertEqual(output, html, assertion_message)
  88. class MockRequest(object):
  89. scheme = 'http'
  90. def __init__(self, method, meta=None, post=None):
  91. self.method = method
  92. self.META = meta or {}
  93. self.POST = post or {}
  94. class CleanReturnPathTests(TestCase):
  95. def test_get_request(self):
  96. """clean_return_path works for GET requests"""
  97. bad_request = MockRequest(
  98. 'GET', {
  99. 'HTTP_REFERER': 'http://cookies.com',
  100. 'HTTP_HOST': 'misago-project.org',
  101. }
  102. )
  103. self.assertIsNone(clean_return_path(bad_request))
  104. bad_request = MockRequest(
  105. 'GET', {
  106. 'HTTP_REFERER': 'https://misago-project.org/',
  107. 'HTTP_HOST': 'misago-project.org/',
  108. }
  109. )
  110. self.assertIsNone(clean_return_path(bad_request))
  111. bad_request = MockRequest(
  112. 'GET', {
  113. 'HTTP_REFERER': 'https://misago-project.org/',
  114. 'HTTP_HOST': 'misago-project.org/assadsa/',
  115. }
  116. )
  117. self.assertIsNone(clean_return_path(bad_request))
  118. ok_request = MockRequest(
  119. 'GET', {
  120. 'HTTP_REFERER': 'http://misago-project.org/',
  121. 'HTTP_HOST': 'misago-project.org/',
  122. }
  123. )
  124. self.assertEqual(clean_return_path(ok_request), '/')
  125. ok_request = MockRequest(
  126. 'GET', {
  127. 'HTTP_REFERER': 'http://misago-project.org/login/',
  128. 'HTTP_HOST': 'misago-project.org/',
  129. }
  130. )
  131. self.assertEqual(clean_return_path(ok_request), '/login/')
  132. def test_post_request(self):
  133. """clean_return_path works for POST requests"""
  134. bad_request = MockRequest(
  135. 'POST', {
  136. 'HTTP_REFERER': 'http://misago-project.org/',
  137. 'HTTP_HOST': 'misago-project.org/',
  138. }, {
  139. 'return_path': '/sdasdsa/',
  140. }
  141. )
  142. self.assertIsNone(clean_return_path(bad_request))
  143. ok_request = MockRequest(
  144. 'POST', {
  145. 'HTTP_REFERER': 'http://misago-project.org/',
  146. 'HTTP_HOST': 'misago-project.org/',
  147. }, {
  148. 'return_path': '/login/',
  149. }
  150. )
  151. self.assertEqual(clean_return_path(ok_request), '/login/')
  152. class IsRefererLocalTests(TestCase):
  153. def test_local_referers(self):
  154. """local referers return true"""
  155. ok_request = MockRequest(
  156. 'GET', {
  157. 'HTTP_REFERER': 'http://misago-project.org/',
  158. 'HTTP_HOST': 'misago-project.org/',
  159. }
  160. )
  161. self.assertTrue(is_referer_local(ok_request))
  162. ok_request = MockRequest(
  163. 'GET', {
  164. 'HTTP_REFERER': 'http://misago-project.org/',
  165. 'HTTP_HOST': 'misago-project.org/',
  166. }
  167. )
  168. self.assertTrue(is_referer_local(ok_request))
  169. ok_request = MockRequest(
  170. 'GET', {
  171. 'HTTP_REFERER': 'http://misago-project.org/login/',
  172. 'HTTP_HOST': 'misago-project.org/',
  173. }
  174. )
  175. self.assertTrue(is_referer_local(ok_request))
  176. def test_foreign_referers(self):
  177. """non-local referers return false"""
  178. bad_request = MockRequest(
  179. 'GET', {
  180. 'HTTP_REFERER': 'http://else-project.org/',
  181. 'HTTP_HOST': 'misago-project.org/',
  182. }
  183. )
  184. self.assertFalse(is_referer_local(bad_request))
  185. bad_request = MockRequest(
  186. 'GET', {
  187. 'HTTP_REFERER': 'https://misago-project.org/',
  188. 'HTTP_HOST': 'misago-project.org/',
  189. }
  190. )
  191. self.assertFalse(is_referer_local(bad_request))
  192. bad_request = MockRequest(
  193. 'GET', {
  194. 'HTTP_REFERER': 'http://misago-project.org/',
  195. 'HTTP_HOST': 'misago-project.org/assadsa/',
  196. }
  197. )
  198. self.assertFalse(is_referer_local(bad_request))
  199. class GetExceptionMessageTests(TestCase):
  200. def test_no_args(self):
  201. """both of helper args are optional"""
  202. message = get_exception_message()
  203. self.assertIsNone(message)
  204. def test_no_default_message(self):
  205. """helper's default message arg is optional"""
  206. message = get_exception_message(PermissionDenied('Lorem Ipsum'))
  207. self.assertEqual(message, 'Lorem Ipsum')
  208. message = get_exception_message(PermissionDenied())
  209. self.assertIsNone(message)
  210. def test_default_message(self):
  211. """helper's default message arg is used"""
  212. message = get_exception_message(PermissionDenied('Lorem Ipsum'), 'Default')
  213. self.assertEqual(message, 'Lorem Ipsum')
  214. message = get_exception_message(PermissionDenied(), 'Default')
  215. self.assertEqual(message, 'Default')
  216. message = get_exception_message(default_message='Lorem Ipsum')
  217. self.assertEqual(message, 'Lorem Ipsum')
  218. class CleanIdsListTests(TestCase):
  219. def test_valid_list(self):
  220. """list of valid ids is cleaned"""
  221. self.assertEqual(clean_ids_list(['1', 3, '42'], None), [1, 3, 42])
  222. def test_empty_list(self):
  223. """empty list passes validation"""
  224. self.assertEqual(clean_ids_list([], None), [])
  225. def test_string_list(self):
  226. """string list passes validation"""
  227. self.assertEqual(clean_ids_list('1234', None), [1, 2, 3, 4])
  228. def test_message(self):
  229. """utility uses passed message for exception"""
  230. with self.assertRaisesMessage(PermissionDenied, "Test error message!"):
  231. clean_ids_list(None, "Test error message!")
  232. def test_invalid_inputs(self):
  233. """utility raises exception for invalid inputs"""
  234. INVALID_INPUTS = (
  235. None,
  236. 'abc',
  237. [None],
  238. [1, 2, 'a', 4],
  239. [1, None, 3],
  240. {1: 2, 'a': 4},
  241. )
  242. for invalid_input in INVALID_INPUTS:
  243. with self.assertRaisesMessage(PermissionDenied, "Test error message!"):
  244. clean_ids_list(invalid_input, "Test error message!")
  245. class GetHostFromAddressTests(TestCase):
  246. def test_none(self):
  247. """get_host_from_address returns None for None"""
  248. result = get_host_from_address(None)
  249. self.assertIsNone(result)
  250. def test_empty_string(self):
  251. """get_host_from_address returns None for empty string"""
  252. result = get_host_from_address('')
  253. self.assertIsNone(result)
  254. def test_hostname(self):
  255. """get_host_from_address returns hostname unchanged"""
  256. result = get_host_from_address('hostname')
  257. self.assertEqual(result, 'hostname')
  258. def test_hostname_with_trailing_slash(self):
  259. """get_host_from_address returns hostname for hostname with trailing slash"""
  260. result = get_host_from_address('//hostname')
  261. self.assertEqual(result, 'hostname')
  262. def test_hostname_with_port(self):
  263. """get_host_from_address returns hostname for hostname with port"""
  264. result = get_host_from_address('hostname:8888')
  265. self.assertEqual(result, 'hostname')
  266. def test_hostname_with_port_path_and_protocol(self):
  267. """get_host_from_address returns hostname for hostname with port and path"""
  268. result = get_host_from_address('https://hostname:8888/somewhere/else/')
  269. self.assertEqual(result, 'hostname')