test_utils.py 10 KB

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