test_utils.py 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  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_threads, get_thread_id_from_url
  5. class AddCategoriesToThreadsTests(MisagoTestCase):
  6. def setUp(self):
  7. super(AddCategoriesToThreadsTests, 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_threads(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_threads(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_threads(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_threads(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_threads(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_threads(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_threads(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_threads(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. # we can 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. # we can 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. # we can 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. # we can 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. # we can 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. # we can 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. for case in TEST_CASES:
  179. pk = get_thread_id_from_url(case['request'], case['url'])
  180. self.assertEqual(
  181. pk, case['pk'], 'get_thread_id_from_url for {} should return {}'.format(case['url'], case['pk']))
  182. def test_get_thread_id_from_invalid_urls(self):
  183. TEST_CASES = (
  184. {
  185. # invalid wsgi alias
  186. 'request': MockRequest('https', 'testforum.com'),
  187. 'url': 'http://testforum.com/discuss/thread/test-thread-123/'
  188. },
  189. {
  190. # invalid hostname
  191. 'request': MockRequest('http', 'misago-project.org', '/discuss/'),
  192. 'url': 'https://testforum.com/discuss/thread/test-thread-432/post/12321/'
  193. },
  194. {
  195. # non-thread url
  196. 'request': MockRequest('http', 'testforum.com'),
  197. 'url': 'https://testforum.com/user/bobboberson-123/'
  198. },
  199. {
  200. # rubbish url
  201. 'request': MockRequest('http', 'testforum.com'),
  202. 'url': 'asdsadsasadsaSA&das8as*S(A*sa'
  203. },
  204. {
  205. # blank url
  206. 'request': MockRequest('http', 'testforum.com'),
  207. 'url': '/'
  208. },
  209. {
  210. # empty url
  211. 'request': MockRequest('http', 'testforum.com'),
  212. 'url': ''
  213. }
  214. )
  215. for case in TEST_CASES:
  216. pk = get_thread_id_from_url(case['request'], case['url'])
  217. self.assertIsNone(pk, 'get_thread_id_from_url for {} should fail'.format(case['url']))