test_thread_model.py 15 KB

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