test_utils.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. from django.test import TestCase
  2. from misago.categories.models import Category
  3. from misago.threads import testutils
  4. from misago.threads.utils import add_categories_to_items, get_thread_id_from_url
  5. class AddCategoriesToItemsTests(TestCase):
  6. def setUp(self):
  7. """
  8. Create categories tree for test cases:
  9. First category (created by migration)
  10. Category A
  11. + Category B
  12. + Subcategory C
  13. + Subcategory D
  14. Category E
  15. + Subcategory F
  16. """
  17. super().setUp()
  18. self.root = Category.objects.root_category()
  19. Category(
  20. name='Category A',
  21. slug='category-a',
  22. css_class='showing-category-a',
  23. ).insert_at(
  24. self.root,
  25. position='last-child',
  26. save=True,
  27. )
  28. Category(
  29. name='Category E',
  30. slug='category-e',
  31. css_class='showing-category-e',
  32. ).insert_at(
  33. self.root,
  34. position='last-child',
  35. save=True,
  36. )
  37. self.root = Category.objects.root_category()
  38. self.category_a = Category.objects.get(slug='category-a')
  39. Category(
  40. name='Category B',
  41. slug='category-b',
  42. css_class='showing-category-b',
  43. ).insert_at(
  44. self.category_a,
  45. position='last-child',
  46. save=True,
  47. )
  48. self.category_b = Category.objects.get(slug='category-b')
  49. Category(
  50. name='Category C',
  51. slug='category-c',
  52. css_class='showing-category-c',
  53. ).insert_at(
  54. self.category_b,
  55. position='last-child',
  56. save=True,
  57. )
  58. Category(
  59. name='Category D',
  60. slug='category-d',
  61. css_class='showing-category-d',
  62. ).insert_at(
  63. self.category_b,
  64. position='last-child',
  65. save=True,
  66. )
  67. self.category_c = Category.objects.get(slug='category-c')
  68. self.category_d = Category.objects.get(slug='category-d')
  69. self.category_e = Category.objects.get(slug='category-e')
  70. Category(
  71. name='Category F',
  72. slug='category-f',
  73. css_class='showing-category-f',
  74. ).insert_at(
  75. self.category_e,
  76. position='last-child',
  77. save=True,
  78. )
  79. Category.objects.partial_rebuild(self.root.tree_id)
  80. self.root = Category.objects.root_category()
  81. self.category_a = Category.objects.get(slug='category-a')
  82. self.category_b = Category.objects.get(slug='category-b')
  83. self.category_c = Category.objects.get(slug='category-c')
  84. self.category_d = Category.objects.get(slug='category-d')
  85. self.category_e = Category.objects.get(slug='category-e')
  86. self.category_f = Category.objects.get(slug='category-f')
  87. self.categories = list(Category.objects.all_categories(include_root=True))
  88. def test_root_thread_from_root(self):
  89. """thread in root category is handled"""
  90. thread = testutils.post_thread(category=self.root)
  91. add_categories_to_items(self.root, self.categories, [thread])
  92. self.assertEqual(thread.category, self.root)
  93. def test_root_thread_from_elsewhere(self):
  94. """thread in root category is handled"""
  95. thread = testutils.post_thread(category=self.root)
  96. add_categories_to_items(self.category_e, self.categories, [thread])
  97. self.assertEqual(thread.category, self.root)
  98. def test_direct_child_thread_from_parent(self):
  99. """thread in direct child category is handled"""
  100. thread = testutils.post_thread(category=self.category_e)
  101. add_categories_to_items(self.root, self.categories, [thread])
  102. self.assertEqual(thread.category, self.category_e)
  103. def test_direct_child_thread_from_elsewhere(self):
  104. """thread in direct child category is handled"""
  105. thread = testutils.post_thread(category=self.category_e)
  106. add_categories_to_items(self.category_b, self.categories, [thread])
  107. self.assertEqual(thread.category, self.category_e)
  108. def test_child_thread_from_root(self):
  109. """thread in child category is handled"""
  110. thread = testutils.post_thread(category=self.category_d)
  111. add_categories_to_items(self.root, self.categories, [thread])
  112. self.assertEqual(thread.category, self.category_d)
  113. def test_child_thread_from_parent(self):
  114. """thread in child category is handled"""
  115. thread = testutils.post_thread(category=self.category_d)
  116. add_categories_to_items(self.category_a, self.categories, [thread])
  117. self.assertEqual(thread.category, self.category_d)
  118. def test_child_thread_from_category(self):
  119. """thread in child category is handled"""
  120. thread = testutils.post_thread(category=self.category_d)
  121. add_categories_to_items(self.category_d, self.categories, [thread])
  122. self.assertEqual(thread.category, self.category_d)
  123. def test_child_thread_from_elsewhere(self):
  124. """thread in child category is handled"""
  125. thread = testutils.post_thread(category=self.category_d)
  126. add_categories_to_items(self.category_f, self.categories, [thread])
  127. self.assertEqual(thread.category, self.category_d)
  128. class MockRequest(object):
  129. def __init__(self, scheme, host, wsgialias=''):
  130. self.scheme = scheme
  131. self.host = host
  132. self.path_info = '/api/threads/123/merge/'
  133. self.path = '%s%s' % (wsgialias.rstrip('/'), self.path_info)
  134. def get_host(self):
  135. return self.host
  136. def is_secure(self):
  137. return self.scheme == 'https'
  138. class GetThreadIdFromUrlTests(TestCase):
  139. def test_get_thread_id_from_valid_urls(self):
  140. """get_thread_id_from_url extracts thread pk from valid urls"""
  141. TEST_CASES = [
  142. {
  143. # perfect match
  144. 'request': MockRequest('https', 'testforum.com', '/discuss/'),
  145. 'url': 'https://testforum.com/discuss/t/test-thread/123/',
  146. 'pk': 123,
  147. },
  148. {
  149. # we don't validate scheme in case site recently moved to https
  150. # but user still has old url's saved somewhere
  151. 'request': MockRequest('http', 'testforum.com', '/discuss/'),
  152. 'url': 'http://testforum.com/discuss/t/test-thread/432/post/12321/',
  153. 'pk': 432,
  154. },
  155. {
  156. # extract thread id from other thread urls
  157. 'request': MockRequest('https', 'testforum.com', '/discuss/'),
  158. 'url': 'http://testforum.com/discuss/t/test-thread/432/post/12321/',
  159. 'pk': 432,
  160. },
  161. {
  162. # extract thread id from thread page url
  163. 'request': MockRequest('http', 'testforum.com', '/discuss/'),
  164. 'url': 'http://testforum.com/discuss/t/test-thread/432/123/',
  165. 'pk': 432,
  166. },
  167. {
  168. # extract thread id from thread last post url with relative schema
  169. 'request': MockRequest('http', 'testforum.com', '/discuss/'),
  170. 'url': '//testforum.com/discuss/t/test-thread/18/last/',
  171. 'pk': 18,
  172. },
  173. {
  174. # extract thread id from url that lacks scheme
  175. 'request': MockRequest('http', 'testforum.com', ''),
  176. 'url': 'testforum.com/t/test-thread/12/last/',
  177. 'pk': 12,
  178. },
  179. {
  180. # extract thread id from schemaless thread last post url
  181. 'request': MockRequest('http', 'testforum.com', '/discuss/'),
  182. 'url': 'testforum.com/discuss/t/test-thread/18/last/',
  183. 'pk': 18,
  184. },
  185. {
  186. # extract thread id from url that lacks scheme and hostname
  187. 'request': MockRequest('http', 'testforum.com', ''),
  188. 'url': '/t/test-thread/13/',
  189. 'pk': 13,
  190. },
  191. {
  192. # extract thread id from url that has port name
  193. 'request': MockRequest('http', '127.0.0.1:8000', ''),
  194. 'url': 'https://127.0.0.1:8000/t/test-thread/13/',
  195. 'pk': 13,
  196. },
  197. {
  198. # extract thread id from url that isn't trimmed
  199. 'request': MockRequest('http', '127.0.0.1:8000', ''),
  200. 'url': ' /t/test-thread/13/ ',
  201. 'pk': 13,
  202. }
  203. ]
  204. for case in TEST_CASES:
  205. pk = get_thread_id_from_url(case['request'], case['url'])
  206. self.assertEqual(
  207. pk, case['pk'],
  208. 'get_thread_id_from_url for %(url)s should return %(pk)s' % case
  209. )
  210. def test_get_thread_id_from_invalid_urls(self):
  211. TEST_CASES = [
  212. {
  213. # lacking wsgi alias
  214. 'request': MockRequest('https', 'testforum.com'),
  215. 'url': 'http://testforum.com/discuss/t/test-thread-123/',
  216. },
  217. {
  218. # invalid wsgi alias
  219. 'request': MockRequest('https', 'testforum.com', '/discuss/'),
  220. 'url': 'http://testforum.com/forum/t/test-thread-123/',
  221. },
  222. {
  223. # invalid hostname
  224. 'request': MockRequest('http', 'misago-project.org', '/discuss/'),
  225. 'url': 'https://testforum.com/discuss/t/test-thread-432/post/12321/',
  226. },
  227. {
  228. # old thread url
  229. 'request': MockRequest('http', 'testforum.com'),
  230. 'url': 'https://testforum.com/thread/bobboberson-123/',
  231. },
  232. {
  233. # dashed thread url
  234. 'request': MockRequest('http', 'testforum.com'),
  235. 'url': 'https://testforum.com/t/bobboberson-123/',
  236. },
  237. {
  238. # non-thread url
  239. 'request': MockRequest('http', 'testforum.com'),
  240. 'url': 'https://testforum.com/user/bobboberson-123/',
  241. },
  242. {
  243. # rubbish url
  244. 'request': MockRequest('http', 'testforum.com'),
  245. 'url': 'asdsadsasadsaSA&das8as*S(A*sa'
  246. },
  247. {
  248. # blank url
  249. 'request': MockRequest('http', 'testforum.com'),
  250. 'url': '/'
  251. },
  252. {
  253. # empty url
  254. 'request': MockRequest('http', 'testforum.com'),
  255. 'url': ''
  256. }
  257. ]
  258. for case in TEST_CASES:
  259. pk = get_thread_id_from_url(case['request'], case['url'])
  260. self.assertIsNone(pk, 'get_thread_id_from_url for %s should fail' % case['url'])