test_privatethread_start_api.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. from django.contrib.auth import get_user_model
  2. from django.core import mail
  3. from django.urls import reverse
  4. from django.utils.encoding import smart_str
  5. from misago.acl.testutils import override_acl
  6. from misago.categories.models import Category
  7. from misago.threads.models import ThreadParticipant
  8. from misago.users.testutils import AuthenticatedUserTestCase
  9. UserModel = get_user_model()
  10. class StartPrivateThreadTests(AuthenticatedUserTestCase):
  11. def setUp(self):
  12. super().setUp()
  13. self.category = Category.objects.private_threads()
  14. self.api_link = reverse('misago:api:private-thread-list')
  15. self.other_user = UserModel.objects.create_user(
  16. 'BobBoberson', 'bob@boberson.com', 'pass123'
  17. )
  18. def test_cant_start_thread_as_guest(self):
  19. """user has to be authenticated to be able to post private thread"""
  20. self.logout_user()
  21. response = self.client.post(self.api_link)
  22. self.assertEqual(response.status_code, 403)
  23. def test_cant_use_private_threads(self):
  24. """has no permission to use private threads"""
  25. override_acl(self.user, {'can_use_private_threads': 0})
  26. response = self.client.post(self.api_link)
  27. self.assertEqual(response.status_code, 403)
  28. self.assertEqual(response.json(), {
  29. "detail": "You can't use private threads.",
  30. })
  31. def test_cant_start_private_thread(self):
  32. """permission to start private thread is validated"""
  33. override_acl(self.user, {'can_start_private_threads': 0})
  34. response = self.client.post(self.api_link)
  35. self.assertEqual(response.status_code, 403)
  36. self.assertEqual(response.json(), {
  37. "detail": "You can't start private threads.",
  38. })
  39. def test_empty_data(self):
  40. """no data sent handling has no showstoppers"""
  41. response = self.client.post(self.api_link, data={})
  42. self.assertEqual(response.status_code, 400)
  43. self.assertEqual(
  44. response.json(), {
  45. 'to': ["You have to enter user names."],
  46. 'title': ["You have to enter thread title."],
  47. 'post': ["You have to enter a message."],
  48. }
  49. )
  50. def test_title_is_validated(self):
  51. """title is validated"""
  52. response = self.client.post(
  53. self.api_link,
  54. data={
  55. 'to': [self.other_user.username],
  56. 'title': "------",
  57. 'post': "Lorem ipsum dolor met, sit amet elit!",
  58. }
  59. )
  60. self.assertEqual(response.status_code, 400)
  61. self.assertEqual(
  62. response.json(), {
  63. 'title': ["Thread title should contain alpha-numeric characters."],
  64. }
  65. )
  66. def test_post_is_validated(self):
  67. """post is validated"""
  68. response = self.client.post(
  69. self.api_link,
  70. data={
  71. 'to': [self.other_user.username],
  72. 'title': "Lorem ipsum dolor met",
  73. 'post': "a",
  74. }
  75. )
  76. self.assertEqual(response.status_code, 400)
  77. self.assertEqual(
  78. response.json(), {
  79. 'post': ["Posted message should be at least 5 characters long (it has 1)."],
  80. }
  81. )
  82. def test_cant_invite_self(self):
  83. """api validates that you cant invite yourself to private thread"""
  84. response = self.client.post(
  85. self.api_link,
  86. data={
  87. 'to': [self.user.username],
  88. 'title': "Lorem ipsum dolor met",
  89. 'post': "Lorem ipsum dolor.",
  90. }
  91. )
  92. self.assertEqual(response.status_code, 400)
  93. self.assertEqual(
  94. response.json(), {
  95. 'to': ["You can't include yourself on the list of users to invite to new thread."],
  96. }
  97. )
  98. def test_cant_invite_nonexisting(self):
  99. """api validates that you cant invite nonexisting user to thread"""
  100. response = self.client.post(
  101. self.api_link,
  102. data={
  103. 'to': ['Ab', 'Cd'],
  104. 'title': "Lorem ipsum dolor met",
  105. 'post': "Lorem ipsum dolor.",
  106. }
  107. )
  108. self.assertEqual(response.status_code, 400)
  109. self.assertEqual(
  110. response.json(), {
  111. 'to': ["One or more users could not be found: ab, cd"],
  112. }
  113. )
  114. def test_cant_invite_too_many(self):
  115. """api validates that you cant invite too many users to thread"""
  116. response = self.client.post(
  117. self.api_link,
  118. data={
  119. 'to': ['Username%s' % i for i in range(50)],
  120. 'title': "Lorem ipsum dolor met",
  121. 'post': "Lorem ipsum dolor.",
  122. }
  123. )
  124. self.assertEqual(response.status_code, 400)
  125. self.assertEqual(
  126. response.json(), {
  127. 'to': ["You can't add more than 3 users to private thread (you've added 50)."],
  128. }
  129. )
  130. def test_cant_invite_no_permission(self):
  131. """api validates invited user permission to private thread"""
  132. override_acl(self.other_user, {'can_use_private_threads': 0})
  133. response = self.client.post(
  134. self.api_link,
  135. data={
  136. 'to': [self.other_user.username],
  137. 'title': "Lorem ipsum dolor met",
  138. 'post': "Lorem ipsum dolor.",
  139. }
  140. )
  141. self.assertEqual(response.status_code, 400)
  142. self.assertEqual(
  143. response.json(), {
  144. 'to': ["BobBoberson can't participate in private threads."],
  145. }
  146. )
  147. def test_cant_invite_blocking(self):
  148. """api validates that you cant invite blocking user to thread"""
  149. self.other_user.blocks.add(self.user)
  150. response = self.client.post(
  151. self.api_link,
  152. data={
  153. 'to': [self.other_user.username],
  154. 'title': "Lorem ipsum dolor met",
  155. 'post': "Lorem ipsum dolor.",
  156. }
  157. )
  158. self.assertEqual(response.status_code, 400)
  159. self.assertEqual(response.json(), {
  160. 'to': ["BobBoberson is blocking you."],
  161. })
  162. # allow us to bypass blocked check
  163. override_acl(self.user, {'can_add_everyone_to_private_threads': 1})
  164. response = self.client.post(
  165. self.api_link,
  166. data={
  167. 'to': [self.other_user.username],
  168. 'title': "-----",
  169. 'post': "Lorem ipsum dolor.",
  170. }
  171. )
  172. self.assertEqual(response.status_code, 400)
  173. self.assertEqual(
  174. response.json(), {
  175. 'title': ["Thread title should contain alpha-numeric characters."],
  176. }
  177. )
  178. def test_cant_invite_followers_only(self):
  179. """api validates that you cant invite followers-only user to thread"""
  180. user_constant = UserModel.LIMIT_INVITES_TO_FOLLOWED
  181. self.other_user.limits_private_thread_invites_to = user_constant
  182. self.other_user.save()
  183. response = self.client.post(
  184. self.api_link,
  185. data={
  186. 'to': [self.other_user.username],
  187. 'title': "Lorem ipsum dolor met",
  188. 'post': "Lorem ipsum dolor.",
  189. }
  190. )
  191. self.assertEqual(response.status_code, 400)
  192. self.assertEqual(
  193. response.json(), {
  194. 'to': ["BobBoberson limits invitations to private threads to followed users."],
  195. }
  196. )
  197. # allow us to bypass following check
  198. override_acl(self.user, {'can_add_everyone_to_private_threads': 1})
  199. response = self.client.post(
  200. self.api_link,
  201. data={
  202. 'to': [self.other_user.username],
  203. 'title': "-----",
  204. 'post': "Lorem ipsum dolor.",
  205. }
  206. )
  207. self.assertEqual(response.status_code, 400)
  208. self.assertEqual(
  209. response.json(), {
  210. 'title': ["Thread title should contain alpha-numeric characters."],
  211. }
  212. )
  213. # make user follow us
  214. override_acl(self.user, {'can_add_everyone_to_private_threads': 0})
  215. self.other_user.follows.add(self.user)
  216. response = self.client.post(
  217. self.api_link,
  218. data={
  219. 'to': [self.other_user.username],
  220. 'title': "-----",
  221. 'post': "Lorem ipsum dolor.",
  222. }
  223. )
  224. self.assertEqual(response.status_code, 400)
  225. self.assertEqual(
  226. response.json(), {
  227. 'title': ["Thread title should contain alpha-numeric characters."],
  228. }
  229. )
  230. def test_cant_invite_anyone(self):
  231. """api validates that you cant invite nobody user to thread"""
  232. user_constant = UserModel.LIMIT_INVITES_TO_NOBODY
  233. self.other_user.limits_private_thread_invites_to = user_constant
  234. self.other_user.save()
  235. response = self.client.post(
  236. self.api_link,
  237. data={
  238. 'to': [self.other_user.username],
  239. 'title': "Lorem ipsum dolor met",
  240. 'post': "Lorem ipsum dolor.",
  241. }
  242. )
  243. self.assertEqual(response.status_code, 400)
  244. self.assertEqual(
  245. response.json(), {
  246. 'to': ["BobBoberson is not allowing invitations to private threads."],
  247. }
  248. )
  249. # allow us to bypass user preference check
  250. override_acl(self.user, {'can_add_everyone_to_private_threads': 1})
  251. response = self.client.post(
  252. self.api_link,
  253. data={
  254. 'to': [self.other_user.username],
  255. 'title': "-----",
  256. 'post': "Lorem ipsum dolor.",
  257. }
  258. )
  259. self.assertEqual(response.status_code, 400)
  260. self.assertEqual(
  261. response.json(), {
  262. 'title': ["Thread title should contain alpha-numeric characters."],
  263. }
  264. )
  265. def test_can_start_thread(self):
  266. """endpoint creates new thread"""
  267. response = self.client.post(
  268. self.api_link,
  269. data={
  270. 'to': [self.other_user.username],
  271. 'title': "Hello, I am test thread!",
  272. 'post': "Lorem ipsum dolor met!",
  273. }
  274. )
  275. self.assertEqual(response.status_code, 200)
  276. thread = self.user.thread_set.all()[:1][0]
  277. response_json = response.json()
  278. self.assertEqual(response_json['url'], thread.get_absolute_url())
  279. response = self.client.get(thread.get_absolute_url())
  280. self.assertContains(response, self.category.name)
  281. self.assertContains(response, thread.title)
  282. self.assertContains(response, "<p>Lorem ipsum dolor met!</p>")
  283. # don't count private threads
  284. self.reload_user()
  285. self.assertEqual(self.user.threads, 0)
  286. self.assertEqual(self.user.posts, 0)
  287. self.assertEqual(thread.category_id, self.category.pk)
  288. self.assertEqual(thread.title, "Hello, I am test thread!")
  289. self.assertEqual(thread.starter_id, self.user.id)
  290. self.assertEqual(thread.starter_name, self.user.username)
  291. self.assertEqual(thread.starter_slug, self.user.slug)
  292. self.assertEqual(thread.last_poster_id, self.user.id)
  293. self.assertEqual(thread.last_poster_name, self.user.username)
  294. self.assertEqual(thread.last_poster_slug, self.user.slug)
  295. post = self.user.post_set.all()[:1][0]
  296. self.assertEqual(post.category_id, self.category.pk)
  297. self.assertEqual(post.original, 'Lorem ipsum dolor met!')
  298. self.assertEqual(post.poster_id, self.user.id)
  299. self.assertEqual(post.poster_name, self.user.username)
  300. self.assertEqual(self.user.audittrail_set.count(), 1)
  301. # thread has two participants
  302. self.assertEqual(thread.participants.count(), 2)
  303. # we are thread owner
  304. ThreadParticipant.objects.get(thread=thread, user=self.user, is_owner=True)
  305. # other user was added to thread
  306. ThreadParticipant.objects.get(thread=thread, user=self.other_user, is_owner=False)
  307. # other user has sync_unread_private_threads flag
  308. user_to_sync = UserModel.objects.get(sync_unread_private_threads=True)
  309. self.assertEqual(user_to_sync, self.other_user)
  310. # notification about new private thread was sent to other user
  311. self.assertEqual(len(mail.outbox), 1)
  312. email = mail.outbox[-1]
  313. self.assertIn(self.user.username, email.subject)
  314. self.assertIn(thread.title, email.subject)
  315. email_body = smart_str(email.body)
  316. self.assertIn(self.user.username, email_body)
  317. self.assertIn(thread.title, email_body)
  318. self.assertIn(thread.get_absolute_url(), email_body)
  319. def test_post_unicode(self):
  320. """unicode characters can be posted"""
  321. response = self.client.post(
  322. self.api_link,
  323. data={
  324. 'to': [self.other_user.username],
  325. 'title': "Brzęczyżczykiewicz",
  326. 'post': "Chrzążczyżewoszyce, powiat Łękółody.",
  327. }
  328. )
  329. self.assertEqual(response.status_code, 200)