test_utils.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. from misago.categories.models import Category
  2. from misago.core.testutils import MisagoTestCase
  3. from misago.threads import testutils
  4. from misago.threads.utils import add_categories_to_items, get_thread_id_from_url
  5. class AddCategoriesToItemsTests(MisagoTestCase):
  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. self.clear_state()
  80. Category.objects.partial_rebuild(self.root.tree_id)
  81. self.root = Category.objects.root_category()
  82. self.category_a = Category.objects.get(slug='category-a')
  83. self.category_b = Category.objects.get(slug='category-b')
  84. self.category_c = Category.objects.get(slug='category-c')
  85. self.category_d = Category.objects.get(slug='category-d')
  86. self.category_e = Category.objects.get(slug='category-e')
  87. self.category_f = Category.objects.get(slug='category-f')
  88. self.categories = list(Category.objects.all_categories(include_root=True))
  89. def test_root_thread_from_root(self):
  90. """thread in root category is handled"""
  91. thread = testutils.post_thread(category=self.root)
  92. add_categories_to_items(self.root, self.categories, [thread])
  93. self.assertEqual(thread.category, self.root)
  94. def test_root_thread_from_elsewhere(self):
  95. """thread in root category is handled"""
  96. thread = testutils.post_thread(category=self.root)
  97. add_categories_to_items(self.category_e, self.categories, [thread])
  98. self.assertEqual(thread.category, self.root)
  99. def test_direct_child_thread_from_parent(self):
  100. """thread in direct child category is handled"""
  101. thread = testutils.post_thread(category=self.category_e)
  102. add_categories_to_items(self.root, self.categories, [thread])
  103. self.assertEqual(thread.category, self.category_e)
  104. def test_direct_child_thread_from_elsewhere(self):
  105. """thread in direct child category is handled"""
  106. thread = testutils.post_thread(category=self.category_e)
  107. add_categories_to_items(self.category_b, self.categories, [thread])
  108. self.assertEqual(thread.category, self.category_e)
  109. def test_child_thread_from_root(self):
  110. """thread in child category is handled"""
  111. thread = testutils.post_thread(category=self.category_d)
  112. add_categories_to_items(self.root, self.categories, [thread])
  113. self.assertEqual(thread.category, self.category_d)
  114. def test_child_thread_from_parent(self):
  115. """thread in child category is handled"""
  116. thread = testutils.post_thread(category=self.category_d)
  117. add_categories_to_items(self.category_a, self.categories, [thread])
  118. self.assertEqual(thread.category, self.category_d)
  119. def test_child_thread_from_category(self):
  120. """thread in child category is handled"""
  121. thread = testutils.post_thread(category=self.category_d)
  122. add_categories_to_items(self.category_d, self.categories, [thread])
  123. self.assertEqual(thread.category, self.category_d)
  124. def test_child_thread_from_elsewhere(self):
  125. """thread in child category is handled"""
  126. thread = testutils.post_thread(category=self.category_d)
  127. add_categories_to_items(self.category_f, self.categories, [thread])
  128. self.assertEqual(thread.category, self.category_d)
  129. class MockRequest(object):
  130. def __init__(self, scheme, host, wsgialias=''):
  131. self.scheme = scheme
  132. self.host = host
  133. self.path_info = '/api/threads/123/merge/'
  134. self.path = '{}{}'.format(wsgialias.rstrip('/'), self.path_info)
  135. def get_host(self):
  136. return self.host
  137. def is_secure(self):
  138. return self.scheme == 'https'
  139. class GetThreadIdFromUrlTests(MisagoTestCase):
  140. def test_get_thread_id_from_valid_urls(self):
  141. """get_thread_id_from_url extracts thread pk from valid urls"""
  142. TEST_CASES = [
  143. {
  144. # perfect match
  145. 'request': MockRequest('https', 'testforum.com', '/discuss/'),
  146. 'url': 'https://testforum.com/discuss/t/test-thread/123/',
  147. 'pk': 123,
  148. },
  149. {
  150. # we don't validate scheme in case site recently moved to https
  151. # but user still has old url's saved somewhere
  152. 'request': MockRequest('http', 'testforum.com', '/discuss/'),
  153. 'url': 'http://testforum.com/discuss/t/test-thread/432/post/12321/',
  154. 'pk': 432,
  155. },
  156. {
  157. # extract thread id from other thread urls
  158. 'request': MockRequest('https', 'testforum.com', '/discuss/'),
  159. 'url': 'http://testforum.com/discuss/t/test-thread/432/post/12321/',
  160. 'pk': 432,
  161. },
  162. {
  163. # extract thread id from thread page url
  164. 'request': MockRequest('http', 'testforum.com', '/discuss/'),
  165. 'url': 'http://testforum.com/discuss/t/test-thread/432/123/',
  166. 'pk': 432,
  167. },
  168. {
  169. # extract thread id from thread last post url with relative schema
  170. 'request': MockRequest('http', 'testforum.com', '/discuss/'),
  171. 'url': '//testforum.com/discuss/t/test-thread/18/last/',
  172. 'pk': 18,
  173. },
  174. {
  175. # extract thread id from url that lacks scheme
  176. 'request': MockRequest('http', 'testforum.com', ''),
  177. 'url': 'testforum.com/t/test-thread/12/last/',
  178. 'pk': 12,
  179. },
  180. {
  181. # extract thread id from schemaless thread last post url
  182. 'request': MockRequest('http', 'testforum.com', '/discuss/'),
  183. 'url': 'testforum.com/discuss/t/test-thread/18/last/',
  184. 'pk': 18,
  185. },
  186. {
  187. # extract thread id from url that lacks scheme and hostname
  188. 'request': MockRequest('http', 'testforum.com', ''),
  189. 'url': '/t/test-thread/13/',
  190. 'pk': 13,
  191. },
  192. {
  193. # extract thread id from url that has port name
  194. 'request': MockRequest('http', '127.0.0.1:8000', ''),
  195. 'url': 'https://127.0.0.1:8000/t/test-thread/13/',
  196. 'pk': 13,
  197. },
  198. {
  199. # extract thread id from url that isn't trimmed
  200. 'request': MockRequest('http', '127.0.0.1:8000', ''),
  201. 'url': ' /t/test-thread/13/ ',
  202. 'pk': 13,
  203. }
  204. ]
  205. for case in TEST_CASES:
  206. pk = get_thread_id_from_url(case['request'], case['url'])
  207. self.assertEqual(
  208. pk, case['pk'],
  209. 'get_thread_id_from_url for {} should return {}'.format(case['url'], case['pk'])
  210. )
  211. def test_get_thread_id_from_invalid_urls(self):
  212. TEST_CASES = [
  213. {
  214. # lacking wsgi alias
  215. 'request': MockRequest('https', 'testforum.com'),
  216. 'url': 'http://testforum.com/discuss/t/test-thread-123/',
  217. },
  218. {
  219. # invalid wsgi alias
  220. 'request': MockRequest('https', 'testforum.com', '/discuss/'),
  221. 'url': 'http://testforum.com/forum/t/test-thread-123/',
  222. },
  223. {
  224. # invalid hostname
  225. 'request': MockRequest('http', 'misago-project.org', '/discuss/'),
  226. 'url': 'https://testforum.com/discuss/t/test-thread-432/post/12321/',
  227. },
  228. {
  229. # old thread url
  230. 'request': MockRequest('http', 'testforum.com'),
  231. 'url': 'https://testforum.com/thread/bobboberson-123/',
  232. },
  233. {
  234. # dashed thread url
  235. 'request': MockRequest('http', 'testforum.com'),
  236. 'url': 'https://testforum.com/t/bobboberson-123/',
  237. },
  238. {
  239. # non-thread url
  240. 'request': MockRequest('http', 'testforum.com'),
  241. 'url': 'https://testforum.com/user/bobboberson-123/',
  242. },
  243. {
  244. # rubbish url
  245. 'request': MockRequest('http', 'testforum.com'),
  246. 'url': 'asdsadsasadsaSA&das8as*S(A*sa'
  247. },
  248. {
  249. # blank url
  250. 'request': MockRequest('http', 'testforum.com'),
  251. 'url': '/'
  252. },
  253. {
  254. # empty url
  255. 'request': MockRequest('http', 'testforum.com'),
  256. 'url': ''
  257. }
  258. ]
  259. for case in TEST_CASES:
  260. pk = get_thread_id_from_url(case['request'], case['url'])
  261. self.assertIsNone(pk, 'get_thread_id_from_url for {} should fail'.format(case['url']))