test_utils.py 9.6 KB

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