test_thread_model.py 15 KB

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