test_utils.py 11 KB

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