test_thread_model.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. from datetime import timedelta
  2. from django.contrib.auth import get_user_model
  3. from django.test import TestCase
  4. from django.utils import timezone
  5. from misago.categories.models import Category
  6. from misago.threads import testutils
  7. from misago.threads.models import Poll, Post, Thread, ThreadParticipant
  8. UserModel = get_user_model()
  9. class ThreadModelTests(TestCase):
  10. def setUp(self):
  11. datetime = timezone.now()
  12. self.category = Category.objects.all_categories()[:1][0]
  13. self.thread = Thread(
  14. category=self.category,
  15. started_on=datetime,
  16. starter_name='Tester',
  17. starter_slug='tester',
  18. last_post_on=datetime,
  19. last_poster_name='Tester',
  20. last_poster_slug='tester',
  21. )
  22. self.thread.set_title("Test thread")
  23. self.thread.save()
  24. Post.objects.create(
  25. category=self.category,
  26. thread=self.thread,
  27. poster_name='Tester',
  28. original="Hello! I am test message!",
  29. parsed="<p>Hello! I am test message!</p>",
  30. checksum="nope",
  31. posted_on=datetime,
  32. updated_on=datetime,
  33. )
  34. self.thread.synchronize()
  35. self.thread.save()
  36. def test_synchronize(self):
  37. """synchronize method updates thread data to reflect its contents"""
  38. user = UserModel.objects.create_user("Bob", "bob@boberson.com", "Pass.123")
  39. self.assertEqual(self.thread.replies, 0)
  40. datetime = timezone.now() + timedelta(5)
  41. post = Post.objects.create(
  42. category=self.category,
  43. thread=self.thread,
  44. poster=user,
  45. poster_name=user.username,
  46. original="Hello! I am test message!",
  47. parsed="<p>Hello! I am test message!</p>",
  48. checksum="nope",
  49. posted_on=datetime,
  50. updated_on=datetime,
  51. )
  52. # first sync call, updates last thread
  53. self.thread.synchronize()
  54. self.assertEqual(self.thread.last_post, post)
  55. self.assertEqual(self.thread.last_post_on, post.posted_on)
  56. self.assertEqual(self.thread.last_poster, user)
  57. self.assertEqual(self.thread.last_poster_name, user.username)
  58. self.assertEqual(self.thread.last_poster_slug, user.slug)
  59. self.assertFalse(self.thread.has_reported_posts)
  60. self.assertFalse(self.thread.has_unapproved_posts)
  61. self.assertFalse(self.thread.has_hidden_posts)
  62. self.assertEqual(self.thread.replies, 1)
  63. # add unapproved post
  64. unapproved_post = Post.objects.create(
  65. category=self.category,
  66. thread=self.thread,
  67. poster=user,
  68. poster_name=user.username,
  69. original="Hello! I am test message!",
  70. parsed="<p>Hello! I am test message!</p>",
  71. checksum="nope",
  72. posted_on=datetime + timedelta(5),
  73. updated_on=datetime + timedelta(5),
  74. is_unapproved=True,
  75. )
  76. self.thread.synchronize()
  77. self.assertEqual(self.thread.last_post, post)
  78. self.assertEqual(self.thread.last_post_on, post.posted_on)
  79. self.assertEqual(self.thread.last_poster, user)
  80. self.assertEqual(self.thread.last_poster_name, user.username)
  81. self.assertEqual(self.thread.last_poster_slug, user.slug)
  82. self.assertFalse(self.thread.has_reported_posts)
  83. self.assertTrue(self.thread.has_unapproved_posts)
  84. self.assertFalse(self.thread.has_hidden_posts)
  85. self.assertEqual(self.thread.replies, 1)
  86. # add hidden post
  87. hidden_post = Post.objects.create(
  88. category=self.category,
  89. thread=self.thread,
  90. poster=user,
  91. poster_name=user.username,
  92. original="Hello! I am test message!",
  93. parsed="<p>Hello! I am test message!</p>",
  94. checksum="nope",
  95. posted_on=datetime + timedelta(10),
  96. updated_on=datetime + timedelta(10),
  97. is_hidden=True,
  98. )
  99. self.thread.synchronize()
  100. self.assertEqual(self.thread.last_post, hidden_post)
  101. self.assertEqual(self.thread.last_post_on, hidden_post.posted_on)
  102. self.assertEqual(self.thread.last_poster, user)
  103. self.assertEqual(self.thread.last_poster_name, user.username)
  104. self.assertEqual(self.thread.last_poster_slug, user.slug)
  105. self.assertFalse(self.thread.has_reported_posts)
  106. self.assertTrue(self.thread.has_unapproved_posts)
  107. self.assertTrue(self.thread.has_hidden_posts)
  108. self.assertEqual(self.thread.replies, 2)
  109. # unhide post
  110. hidden_post.is_hidden = False
  111. hidden_post.save()
  112. # last post changed to unhidden one
  113. self.thread.synchronize()
  114. self.assertEqual(self.thread.last_post, hidden_post)
  115. self.assertEqual(self.thread.last_post_on, hidden_post.posted_on)
  116. self.assertEqual(self.thread.last_poster, user)
  117. self.assertEqual(self.thread.last_poster_name, user.username)
  118. self.assertEqual(self.thread.last_poster_slug, user.slug)
  119. self.assertFalse(self.thread.has_reported_posts)
  120. self.assertTrue(self.thread.has_unapproved_posts)
  121. self.assertFalse(self.thread.has_hidden_posts)
  122. self.assertEqual(self.thread.replies, 2)
  123. # unmoderate post
  124. unapproved_post.is_unapproved = False
  125. unapproved_post.save()
  126. # last post not changed, but flags and count did
  127. self.thread.synchronize()
  128. self.assertEqual(self.thread.last_post, hidden_post)
  129. self.assertEqual(self.thread.last_post_on, hidden_post.posted_on)
  130. self.assertEqual(self.thread.last_poster, user)
  131. self.assertEqual(self.thread.last_poster_name, user.username)
  132. self.assertEqual(self.thread.last_poster_slug, user.slug)
  133. self.assertFalse(self.thread.has_reported_posts)
  134. self.assertFalse(self.thread.has_unapproved_posts)
  135. self.assertFalse(self.thread.has_hidden_posts)
  136. self.assertEqual(self.thread.replies, 3)
  137. # add event post
  138. event = Post.objects.create(
  139. category=self.category,
  140. thread=self.thread,
  141. poster=user,
  142. poster_name=user.username,
  143. original="-",
  144. parsed="-",
  145. checksum="nope",
  146. posted_on=datetime + timedelta(10),
  147. updated_on=datetime + timedelta(10),
  148. is_event=True,
  149. )
  150. self.thread.synchronize()
  151. self.assertEqual(self.thread.last_post, event)
  152. self.assertEqual(self.thread.last_post_on, event.posted_on)
  153. self.assertEqual(self.thread.last_poster, user)
  154. self.assertEqual(self.thread.last_poster_name, user.username)
  155. self.assertEqual(self.thread.last_poster_slug, user.slug)
  156. self.assertTrue(self.thread.last_post_is_event)
  157. self.assertTrue(self.thread.has_events)
  158. self.assertFalse(self.thread.has_reported_posts)
  159. self.assertFalse(self.thread.has_unapproved_posts)
  160. self.assertFalse(self.thread.has_hidden_posts)
  161. # events don't count to reply count
  162. self.assertEqual(self.thread.replies, 3)
  163. # create another post to provoke other has_events resolution path
  164. Post.objects.create(
  165. category=self.category,
  166. thread=self.thread,
  167. poster=user,
  168. poster_name=user.username,
  169. original="Hello! I am test message!",
  170. parsed="<p>Hello! I am test message!</p>",
  171. checksum="nope",
  172. posted_on=datetime,
  173. updated_on=datetime,
  174. )
  175. self.thread.synchronize()
  176. self.assertFalse(self.thread.last_post_is_event)
  177. self.assertTrue(self.thread.has_events)
  178. # remove event
  179. event.delete()
  180. self.thread.synchronize()
  181. self.assertFalse(self.thread.last_post_is_event)
  182. self.assertFalse(self.thread.has_events)
  183. # has poll flag
  184. self.assertFalse(self.thread.has_poll)
  185. Poll.objects.create(
  186. thread=self.thread,
  187. category=self.category,
  188. poster_name='test',
  189. poster_slug='test',
  190. choices=[],
  191. )
  192. self.thread.synchronize()
  193. self.assertTrue(self.thread.has_poll)
  194. def test_set_first_post(self):
  195. """set_first_post sets first post and poster data on thread"""
  196. user = UserModel.objects.create_user("Bob", "bob@boberson.com", "Pass.123")
  197. datetime = timezone.now() + timedelta(5)
  198. post = Post.objects.create(
  199. category=self.category,
  200. thread=self.thread,
  201. poster=user,
  202. poster_name=user.username,
  203. original="Hello! I am test message!",
  204. parsed="<p>Hello! I am test message!</p>",
  205. checksum="nope",
  206. posted_on=datetime,
  207. updated_on=datetime,
  208. )
  209. self.thread.set_first_post(post)
  210. self.assertEqual(self.thread.first_post, post)
  211. self.assertEqual(self.thread.started_on, post.posted_on)
  212. self.assertEqual(self.thread.starter, user)
  213. self.assertEqual(self.thread.starter_name, user.username)
  214. self.assertEqual(self.thread.starter_slug, user.slug)
  215. def test_set_last_post(self):
  216. """set_last_post sets first post and poster data on thread"""
  217. user = UserModel.objects.create_user("Bob", "bob@boberson.com", "Pass.123")
  218. datetime = timezone.now() + timedelta(5)
  219. post = Post.objects.create(
  220. category=self.category,
  221. thread=self.thread,
  222. poster=user,
  223. poster_name=user.username,
  224. original="Hello! I am test message!",
  225. parsed="<p>Hello! I am test message!</p>",
  226. checksum="nope",
  227. posted_on=datetime,
  228. updated_on=datetime,
  229. )
  230. self.thread.set_last_post(post)
  231. self.assertEqual(self.thread.last_post, post)
  232. self.assertEqual(self.thread.last_post_on, post.posted_on)
  233. self.assertEqual(self.thread.last_poster, user)
  234. self.assertEqual(self.thread.last_poster_name, user.username)
  235. self.assertEqual(self.thread.last_poster_slug, user.slug)
  236. def test_set_best_answer(self):
  237. """set_best_answer sets best answer and setter data on thread"""
  238. user = UserModel.objects.create_user("Bob", "bob@boberson.com", "Pass.123")
  239. best_answer = Post.objects.create(
  240. category=self.category,
  241. thread=self.thread,
  242. poster=user,
  243. poster_name=user.username,
  244. original="Hello! I am test message!",
  245. parsed="<p>Hello! I am test message!</p>",
  246. checksum="nope",
  247. posted_on=timezone.now(),
  248. updated_on=timezone.now(),
  249. is_protected=True,
  250. )
  251. self.thread.synchronize()
  252. self.thread.save()
  253. self.thread.set_best_answer(user, best_answer)
  254. self.thread.save()
  255. self.assertEqual(self.thread.best_answer, best_answer)
  256. self.assertTrue(self.thread.has_best_answer)
  257. self.assertTrue(self.thread.best_answer_is_protected)
  258. self.assertTrue(self.thread.best_answer_marked_on)
  259. self.assertEqual(self.thread.best_answer_marked_by, user)
  260. self.assertEqual(self.thread.best_answer_marked_by_name, user.username)
  261. self.assertEqual(self.thread.best_answer_marked_by_slug, user.slug)
  262. # clear best answer
  263. self.thread.clear_best_answer()
  264. self.assertIsNone(self.thread.best_answer)
  265. self.assertFalse(self.thread.has_best_answer)
  266. self.assertFalse(self.thread.best_answer_is_protected)
  267. self.assertIsNone(self.thread.best_answer_marked_on)
  268. self.assertIsNone(self.thread.best_answer_marked_by)
  269. self.assertIsNone(self.thread.best_answer_marked_by_name)
  270. self.assertIsNone(self.thread.best_answer_marked_by_slug)
  271. def test_set_invalid_best_answer(self):
  272. """set_best_answer implements some assertions for data integrity"""
  273. user = UserModel.objects.create_user("Bob", "bob@boberson.com", "Pass.123")
  274. other_thread = testutils.post_thread(self.category)
  275. with self.assertRaises(ValueError):
  276. self.thread.set_best_answer(user, other_thread.first_post)
  277. with self.assertRaises(ValueError):
  278. self.thread.set_best_answer(user, self.thread.first_post)
  279. with self.assertRaises(ValueError):
  280. reply = testutils.reply_thread(self.thread, is_hidden=True)
  281. self.thread.set_best_answer(user, reply)
  282. with self.assertRaises(ValueError):
  283. reply = testutils.reply_thread(self.thread, is_unapproved=True)
  284. self.thread.set_best_answer(user, reply)
  285. def test_move(self):
  286. """move(new_category) moves thread to other category"""
  287. root_category = Category.objects.root_category()
  288. Category(
  289. name='New Category',
  290. slug='new-category',
  291. ).insert_at(
  292. root_category,
  293. position='last-child',
  294. save=True,
  295. )
  296. new_category = Category.objects.get(slug='new-category')
  297. self.thread.move(new_category)
  298. self.assertEqual(self.thread.category, new_category)
  299. for post in self.thread.post_set.all():
  300. self.assertEqual(post.category_id, new_category.id)
  301. def test_merge(self):
  302. """merge(other_thread) moves other thread content to this thread"""
  303. with self.assertRaises(ValueError):
  304. self.thread.merge(self.thread)
  305. datetime = timezone.now() + timedelta(5)
  306. other_thread = Thread(
  307. category=self.category,
  308. started_on=datetime,
  309. starter_name='Tester',
  310. starter_slug='tester',
  311. last_post_on=datetime,
  312. last_poster_name='Tester',
  313. last_poster_slug='tester',
  314. )
  315. other_thread.set_title("Other thread")
  316. other_thread.save()
  317. post = Post.objects.create(
  318. category=self.category,
  319. thread=other_thread,
  320. poster_name='Admin',
  321. original="Hello! I am other message!",
  322. parsed="<p>Hello! I am other message!</p>",
  323. checksum="nope",
  324. posted_on=datetime,
  325. updated_on=datetime,
  326. )
  327. other_thread.first_post = post
  328. other_thread.last_post = post
  329. other_thread.save()
  330. self.thread.merge(other_thread)
  331. self.thread.synchronize()
  332. self.assertEqual(self.thread.replies, 1)
  333. self.assertEqual(self.thread.last_post, post)
  334. self.assertEqual(self.thread.last_post_on, post.posted_on)
  335. self.assertEqual(self.thread.last_poster_name, "Admin")
  336. self.assertEqual(self.thread.last_poster_slug, "admin")
  337. def test_delete_private_thread(self):
  338. """
  339. private thread gets deleted automatically
  340. when there are no participants left in it
  341. """
  342. user_a = UserModel.objects.create_user("Bob", "bob@boberson.com", "Pass.123")
  343. user_b = UserModel.objects.create_user("Weebl", "weebl@weeblson.com", "Pass.123")
  344. ThreadParticipant.objects.add_participants(self.thread, [user_a, user_b])
  345. self.assertEqual(self.thread.participants.count(), 2)
  346. user_a.delete()
  347. Thread.objects.get(id=self.thread.id)
  348. user_b.delete()
  349. with self.assertRaises(Thread.DoesNotExist):
  350. Thread.objects.get(id=self.thread.id)