test_forum_models.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667
  1. from datetime import datetime
  2. from flask import current_app
  3. from flask_login import login_user, current_user, logout_user
  4. from flaskbb.forum.models import Category, Forum, Topic, Post, ForumsRead, \
  5. TopicsRead, Report
  6. from flaskbb.user.models import User
  7. from flaskbb.utils.settings import flaskbb_config
  8. def test_category_save(database):
  9. """Test the save category method."""
  10. category = Category(title="Test Category")
  11. category.save()
  12. assert category.title == "Test Category"
  13. def test_category_delete(category):
  14. """Test the delete category method."""
  15. category.delete()
  16. category = Category.query.filter_by(id=category.id).first()
  17. assert category is None
  18. def test_category_delete_with_user(topic):
  19. """Test the delete category method with recounting the users post
  20. counts.
  21. """
  22. user = topic.user
  23. forum = topic.forum
  24. category = topic.forum.category
  25. assert user.post_count == 1
  26. assert forum.post_count == 1
  27. assert forum.topic_count == 1
  28. category.delete([user])
  29. assert user.post_count == 0
  30. category = Category.query.filter_by(id=category.id).first()
  31. topic = Topic.query.filter_by(id=topic.id).first()
  32. assert category is None
  33. # The topic should also be deleted
  34. assert topic is None
  35. def test_category_delete_with_forum(forum):
  36. """When deleting a category, all of his forums should also be deleted."""
  37. forum.category.delete()
  38. assert forum is not None
  39. assert forum.category is not None
  40. category = Category.query.filter_by(id=forum.category.id).first()
  41. forum = Forum.query.filter_by(id=forum.id).first()
  42. assert forum is None
  43. assert category is None
  44. def test_category_get_forums(forum, user):
  45. category = forum.category
  46. with current_app.test_request_context():
  47. # Test with logged in user
  48. login_user(user)
  49. assert current_user.is_authenticated
  50. cat, forums = Category.get_forums(category.id, current_user)
  51. # Check if it is a list because in a category there are normally more
  52. # than one forum in it (not in these tests)
  53. assert isinstance(forums, list) is True
  54. assert forums == [(forum, None)]
  55. assert cat == category
  56. # Test the same thing with a logged out user
  57. logout_user()
  58. assert not current_user.is_authenticated
  59. cat, forums = Category.get_forums(category.id, current_user)
  60. # Check if it is a list because in a category there are normally more
  61. # than one forum in it (not in these tests)
  62. assert isinstance(forums, list) is True
  63. assert forums == [(forum, None)]
  64. assert cat == category
  65. def test_category_get_all(forum, user):
  66. category = forum.category
  67. with current_app.test_request_context():
  68. # Test with logged in user
  69. login_user(user)
  70. assert current_user.is_authenticated
  71. categories = Category.get_all(current_user)
  72. # All categories are stored in a list
  73. assert isinstance(categories, list)
  74. # The forums for a category are also stored in a list
  75. assert isinstance(categories[0][1], list)
  76. assert categories == [(category, [(forum, None)])]
  77. # Test with logged out user
  78. logout_user()
  79. assert not current_user.is_authenticated
  80. categories = Category.get_all(current_user)
  81. # All categories are stored in a list
  82. assert isinstance(categories, list)
  83. # The forums for a category are also stored in a list
  84. assert isinstance(categories[0][1], list)
  85. assert categories == [(category, [(forum, None)])]
  86. def test_forum_save(category, moderator_user):
  87. """Test the save forum method"""
  88. forum = Forum(title="Test Forum", category_id=category.id)
  89. forum.moderators = [moderator_user]
  90. forum.save()
  91. assert forum.title == "Test Forum"
  92. assert forum.moderators == [moderator_user]
  93. def test_forum_delete(forum):
  94. """Test the delete forum method."""
  95. forum.delete()
  96. forum = Forum.query.filter_by(id=forum.id).first()
  97. assert forum is None
  98. def test_forum_delete_with_user_and_topic(topic, user):
  99. """Now test the delete forum method with a topic inside."""
  100. assert user.post_count == 1
  101. topic.forum.delete([user])
  102. forum = Forum.query.filter_by(id=topic.forum_id).first()
  103. assert forum is None
  104. assert user.post_count == 0
  105. def test_forum_update_last_post(topic, user):
  106. """Test the update last post method."""
  107. post = Post(content="Test Content 2")
  108. post.save(topic=topic, user=user)
  109. assert topic.forum.last_post == post
  110. post.delete()
  111. topic.forum.update_last_post()
  112. assert topic.forum.last_post == topic.first_post
  113. def test_forum_update_read(database, user, topic):
  114. """Test the update read method."""
  115. forumsread = ForumsRead.query.\
  116. filter(ForumsRead.user_id == user.id,
  117. ForumsRead.forum_id == topic.forum_id).first()
  118. topicsread = TopicsRead.query.\
  119. filter(TopicsRead.user_id == user.id,
  120. TopicsRead.topic_id == topic.id).first()
  121. forum = topic.forum
  122. with current_app.test_request_context():
  123. # Test with logged in user
  124. login_user(user)
  125. # Should return False because topicsread is None
  126. assert not forum.update_read(current_user, forumsread, topicsread)
  127. # This is the first time the user visits the topic
  128. topicsread = TopicsRead()
  129. topicsread.user_id = user.id
  130. topicsread.topic_id = topic.id
  131. topicsread.forum_id = topic.forum_id
  132. topicsread.last_read = datetime.utcnow()
  133. topicsread.save()
  134. # hence, we also need to create a new forumsread entry
  135. assert forum.update_read(current_user, forumsread, topicsread)
  136. forumsread = ForumsRead.query.\
  137. filter(ForumsRead.user_id == user.id,
  138. ForumsRead.forum_id == topic.forum_id).first()
  139. # everything should be up-to-date now
  140. assert not forum.update_read(current_user, forumsread, topicsread)
  141. post = Post(content="Test Content")
  142. post.save(user=user, topic=topic)
  143. # Updating the topicsread tracker
  144. topicsread.last_read = datetime.utcnow()
  145. topicsread.save()
  146. # now the forumsread tracker should also need a update
  147. assert forum.update_read(current_user, forumsread, topicsread)
  148. logout_user()
  149. # should fail because the user is logged out
  150. assert not forum.update_read(current_user, forumsread, topicsread)
  151. def test_forum_update_read_two_topics(database, user, topic, topic_moderator):
  152. """Test if the ForumsRead tracker will be updated if there are two topics
  153. and where one is unread and the other is read.
  154. """
  155. forumsread = ForumsRead.query.\
  156. filter(ForumsRead.user_id == user.id,
  157. ForumsRead.forum_id == topic.forum_id).first()
  158. forum = topic.forum
  159. with current_app.test_request_context():
  160. # Test with logged in user
  161. login_user(user)
  162. # This is the first time the user visits the topic
  163. topicsread = TopicsRead()
  164. topicsread.user_id = user.id
  165. topicsread.topic_id = topic.id
  166. topicsread.forum_id = topic.forum_id
  167. topicsread.last_read = datetime.utcnow()
  168. topicsread.save()
  169. # will not create a entry because there is still one unread topic
  170. assert not forum.update_read(current_user, forumsread, topicsread)
  171. def test_forum_url(forum):
  172. assert forum.url == "http://localhost:5000/forum/1-test-forum"
  173. def test_forum_slugify(forum):
  174. assert forum.slug == "test-forum"
  175. def test_forum_get_forum(forum, user):
  176. with current_app.test_request_context():
  177. # Test with logged in user
  178. login_user(user)
  179. forum_with_forumsread = \
  180. Forum.get_forum(forum_id=forum.id, user=current_user)
  181. assert forum_with_forumsread == (forum, None)
  182. # Test with logged out user
  183. logout_user()
  184. forum_with_forumsread = \
  185. Forum.get_forum(forum_id=forum.id, user=current_user)
  186. assert forum_with_forumsread == (forum, None)
  187. def test_forum_get_topics(topic, user):
  188. forum = topic.forum
  189. with current_app.test_request_context():
  190. # Test with logged in user
  191. login_user(user)
  192. topics = Forum.get_topics(forum_id=forum.id, user=current_user)
  193. assert topics.items == [(topic, topic.last_post, None)]
  194. # Test with logged out user
  195. logout_user()
  196. topics = Forum.get_topics(forum_id=forum.id, user=current_user)
  197. assert topics.items == [(topic, topic.last_post, None)]
  198. def test_topic_save(forum, user):
  199. """Test the save topic method with creating and editing a topic."""
  200. post = Post(content="Test Content")
  201. topic = Topic(title="Test Title")
  202. assert forum.last_post_id is None
  203. assert forum.post_count == 0
  204. assert forum.topic_count == 0
  205. topic.save(forum=forum, post=post, user=user)
  206. assert topic.title == "Test Title"
  207. topic.title = "Test Edit Title"
  208. topic.save()
  209. assert topic.title == "Test Edit Title"
  210. # The first post in the topic is also the last post
  211. assert topic.first_post_id == post.id
  212. assert topic.last_post_id == post.id
  213. assert forum.last_post_id == post.id
  214. assert forum.post_count == 1
  215. assert forum.topic_count == 1
  216. def test_topic_delete(topic):
  217. """Test the delete topic method"""
  218. assert topic.user.post_count == 1
  219. assert topic.post_count == 1
  220. assert topic.forum.topic_count == 1
  221. assert topic.forum.post_count == 1
  222. topic.delete()
  223. forum = Forum.query.filter_by(id=topic.forum_id).first()
  224. user = User.query.filter_by(id=topic.user_id).first()
  225. topic = Topic.query.filter_by(id=topic.id).first()
  226. assert topic is None
  227. assert user.post_count == 0
  228. assert forum.topic_count == 0
  229. assert forum.post_count == 0
  230. assert forum.last_post_id is None
  231. def test_topic_move(topic):
  232. """Tests the topic move method."""
  233. forum_other = Forum(title="Test Forum 2", category_id=1)
  234. forum_other.save()
  235. forum_old = Forum.query.filter_by(id=topic.forum_id).first()
  236. assert topic.move(forum_other)
  237. assert forum_old.topics.all() == []
  238. assert forum_old.last_post_id is None
  239. assert forum_old.topic_count == 0
  240. assert forum_old.post_count == 0
  241. assert forum_other.last_post_id == topic.last_post_id
  242. assert forum_other.topic_count == 1
  243. assert forum_other.post_count == 1
  244. def test_topic_move_same_forum(topic):
  245. """You cannot move a topic within the same forum."""
  246. assert not topic.move(topic.forum)
  247. def test_topic_tracker_needs_update(database, user, topic):
  248. """Tests if the topicsread tracker needs an update if a new post has been
  249. submitted.
  250. """
  251. forumsread = ForumsRead.query.\
  252. filter(ForumsRead.user_id == user.id,
  253. ForumsRead.forum_id == topic.forum_id).first()
  254. topicsread = TopicsRead.query.\
  255. filter(TopicsRead.user_id == user.id,
  256. TopicsRead.topic_id == topic.id).first()
  257. with current_app.test_request_context():
  258. assert topic.tracker_needs_update(forumsread, topicsread)
  259. # Update the tracker
  260. topicsread = TopicsRead()
  261. topicsread.user_id = user.id
  262. topicsread.topic_id = topic.id
  263. topicsread.forum_id = topic.forum_id
  264. topicsread.last_read = datetime.utcnow()
  265. topicsread.save()
  266. forumsread = ForumsRead()
  267. forumsread.user_id = user.id
  268. forumsread.forum_id = topic.forum_id
  269. forumsread.last_read = datetime.utcnow()
  270. forumsread.save()
  271. # Now the topic should be read
  272. assert not topic.tracker_needs_update(forumsread, topicsread)
  273. post = Post(content="Test Content")
  274. post.save(topic=topic, user=user)
  275. assert topic.tracker_needs_update(forumsread, topicsread)
  276. def test_untracking_topic_does_not_delete_it(database, user, topic):
  277. user.track_topic(topic)
  278. user.save()
  279. user.untrack_topic(topic)
  280. # Note that instead of returning None from the query below, the
  281. # unpatched verson will actually raise a DetachedInstanceError here,
  282. # due to the fact that some relationships don't get configured in
  283. # tests correctly and deleting would break them or something. The
  284. # test still fails for the same reason, however: the topic gets
  285. # (albeit unsuccessfully) deleted when it is removed from
  286. # topictracker, when it clearly shouldn't be.
  287. user.save()
  288. assert Topic.query.filter(Topic.id == topic.id).first()
  289. def test_topic_tracker_needs_update_cleared(database, user, topic):
  290. """Tests if the topicsread needs an update if the forum has been marked
  291. as cleared.
  292. """
  293. forumsread = ForumsRead.query.\
  294. filter(ForumsRead.user_id == user.id,
  295. ForumsRead.forum_id == topic.forum_id).first()
  296. topicsread = TopicsRead.query.\
  297. filter(TopicsRead.user_id == user.id,
  298. TopicsRead.topic_id == topic.id).first()
  299. with current_app.test_request_context():
  300. assert topic.tracker_needs_update(forumsread, topicsread)
  301. # Update the tracker
  302. forumsread = ForumsRead()
  303. forumsread.user_id = user.id
  304. forumsread.forum_id = topic.forum_id
  305. forumsread.last_read = datetime.utcnow()
  306. forumsread.cleared = datetime.utcnow()
  307. forumsread.save()
  308. # Now the topic should be read
  309. assert not topic.tracker_needs_update(forumsread, topicsread)
  310. def test_topic_update_read(database, user, topic):
  311. """Tests the update read method if the topic is unread/read."""
  312. forumsread = ForumsRead.query.\
  313. filter(ForumsRead.user_id == user.id,
  314. ForumsRead.forum_id == topic.forum_id).first()
  315. with current_app.test_request_context():
  316. # Test with logged in user
  317. login_user(user)
  318. assert current_user.is_authenticated
  319. # Update the tracker
  320. assert topic.update_read(current_user, topic.forum, forumsread)
  321. # Because the tracker is already up-to-date, it shouldn't update it
  322. # again.
  323. assert not topic.update_read(current_user, topic.forum, forumsread)
  324. # Adding a new post - now the tracker shouldn't be up-to-date anymore.
  325. post = Post(content="Test Content")
  326. post.save(topic=topic, user=user)
  327. forumsread = ForumsRead.query.\
  328. filter(ForumsRead.user_id == user.id,
  329. ForumsRead.forum_id == topic.forum_id).first()
  330. # Test tracker length
  331. flaskbb_config["TRACKER_LENGTH"] = 0
  332. assert not topic.update_read(current_user, topic.forum, forumsread)
  333. flaskbb_config["TRACKER_LENGTH"] = 1
  334. assert topic.update_read(current_user, topic.forum, forumsread)
  335. # Test with logged out user
  336. logout_user()
  337. assert not current_user.is_authenticated
  338. assert not topic.update_read(current_user, topic.forum, forumsread)
  339. def test_topic_url(topic):
  340. assert topic.url == "http://localhost:5000/topic/1-test-topic-normal"
  341. def test_topic_slug(topic):
  342. assert topic.slug == "test-topic-normal"
  343. def test_post_save(topic, user):
  344. """Tests the save post method."""
  345. post = Post(content="Test Content")
  346. post.save(topic=topic, user=user)
  347. assert post.content == "Test Content"
  348. post.content = "Test Edit Content"
  349. post.save()
  350. assert post.content == "Test Edit Content"
  351. assert topic.user.post_count == 2
  352. assert topic.post_count == 2
  353. assert topic.last_post == post
  354. assert topic.forum.post_count == 2
  355. def test_post_delete(topic):
  356. """Tests the delete post method with three different post types.
  357. The three types are:
  358. * First Post
  359. * A post between the first and last post (middle)
  360. * Last Post
  361. """
  362. post_middle = Post(content="Test Content Middle")
  363. post_middle.save(topic=topic, user=topic.user)
  364. assert topic.post_count == 2 # post_middle + first_post
  365. post_last = Post(content="Test Content Last")
  366. post_last.save(topic=topic, user=topic.user)
  367. # first post + post_middle + post_last
  368. assert topic.post_count == 3
  369. assert topic.forum.post_count == 3
  370. assert topic.user.post_count == 3
  371. post_middle.delete()
  372. # Check the last posts
  373. assert topic.last_post == post_last
  374. assert topic.forum.last_post == post_last
  375. assert topic.post_count == 2
  376. post_last.delete()
  377. # only the first_post remains
  378. assert topic.post_count == 1
  379. assert topic.forum.post_count == 1
  380. assert topic.user.post_count == 1
  381. assert topic.first_post_id == topic.last_post_id
  382. assert topic.forum.last_post_id == topic.last_post_id
  383. def test_report(topic, user):
  384. """Tests if the reports can be saved/edited and deleted with the
  385. implemented save and delete methods."""
  386. report = Report(reason="Test Report")
  387. report.save(user=user, post=topic.first_post)
  388. assert report.reason == "Test Report"
  389. report.reason = "Test Report Edited"
  390. report.save()
  391. assert report.reason == "Test Report Edited"
  392. report.delete()
  393. report = Report.query.filter_by(id=report.id).first()
  394. assert report is None
  395. def test_forumsread(topic, user):
  396. """Tests if the forumsread tracker can be saved/edited and deleted with the
  397. implemented save and delete methods."""
  398. forumsread = ForumsRead()
  399. forumsread.user_id = user.id
  400. forumsread.forum_id = topic.forum_id
  401. forumsread.last_read = datetime.utcnow()
  402. forumsread.save()
  403. assert forumsread is not None
  404. forumsread.delete()
  405. forumsread = ForumsRead.query.\
  406. filter_by(forum_id=forumsread.forum_id).\
  407. first()
  408. assert forumsread is None
  409. def test_topicsread(topic, user):
  410. """Tests if the topicsread trakcer can be saved/edited and deleted with the
  411. implemented save and delete methods."""
  412. topicsread = TopicsRead()
  413. topicsread.user_id = user.id
  414. topicsread.topic_id = topic.id
  415. topicsread.forum_id = topic.forum_id
  416. topicsread.last_read = datetime.utcnow()
  417. topicsread.save()
  418. assert topicsread is not None
  419. topicsread.delete()
  420. topicsread = TopicsRead.query.\
  421. filter_by(topic_id=topicsread.topic_id).\
  422. first()
  423. assert topicsread is None
  424. def test_hiding_post_updates_counts(forum, topic, user):
  425. new_post = Post(content='spam')
  426. new_post.save(user=user, topic=topic)
  427. new_post.hide(user)
  428. assert user.post_count == 1
  429. assert topic.post_count == 1
  430. assert forum.post_count == 1
  431. assert topic.last_post != new_post
  432. assert forum.last_post != new_post
  433. assert new_post.hidden_by == user
  434. new_post.unhide()
  435. assert topic.post_count == 2
  436. assert user.post_count == 2
  437. assert forum.post_count == 2
  438. assert topic.last_post == new_post
  439. assert forum.last_post == new_post
  440. assert new_post.hidden_by is None
  441. def test_hiding_topic_updates_counts(forum, topic, user):
  442. assert forum.post_count == 1
  443. topic.hide(user)
  444. assert forum.post_count == 0
  445. assert topic.hidden_by == user
  446. assert forum.last_post is None
  447. topic.unhide()
  448. assert forum.post_count == 1
  449. assert topic.hidden_by is None
  450. assert forum.last_post == topic.last_post
  451. def test_hiding_first_post_hides_topic(forum, topic, user):
  452. assert forum.post_count == 1
  453. topic.first_post.hide(user)
  454. assert forum.post_count == 0
  455. assert topic.hidden_by == user
  456. assert forum.last_post is None
  457. topic.first_post.unhide()
  458. assert forum.post_count == 1
  459. assert topic.hidden_by is None
  460. assert forum.last_post == topic.last_post
  461. def test_retrieving_hidden_posts(topic, user):
  462. new_post = Post(content='stuff')
  463. new_post.save(user, topic)
  464. new_post.hide(user)
  465. assert Post.query.get(new_post.id) is None
  466. assert Post.query.get(new_post.id, include_hidden=True) == new_post
  467. assert Post.query.filter(Post.id == new_post.id).first() is None
  468. hidden_post = Post.query\
  469. .with_hidden()\
  470. .filter(Post.id == new_post.id)\
  471. .first()
  472. assert hidden_post == new_post
  473. def test_retrieving_hidden_topics(topic, user):
  474. topic.hide(user)
  475. assert Topic.query.get(topic.id) is None
  476. assert Topic.query.get(topic.id, include_hidden=True) == topic
  477. assert Topic.query.filter(Topic.id == topic.id).first() is None
  478. hidden_topic = Topic.query\
  479. .with_hidden()\
  480. .filter(Topic.id == topic.id)\
  481. .first()
  482. assert hidden_topic == topic