test_utils.py 11 KB

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