Fork of Pleroma with site-specific changes and feature branches https://git.pleroma.social/pleroma/pleroma
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

519 lines
17KB

  1. # Pleroma: A lightweight social networking server
  2. # Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
  3. # SPDX-License-Identifier: AGPL-3.0-only
  4. defmodule Pleroma.NotificationTest do
  5. use Pleroma.DataCase
  6. alias Pleroma.Notification
  7. alias Pleroma.User
  8. alias Pleroma.Web.ActivityPub.Transmogrifier
  9. alias Pleroma.Web.CommonAPI
  10. alias Pleroma.Web.TwitterAPI.TwitterAPI
  11. import Pleroma.Factory
  12. describe "create_notifications" do
  13. test "notifies someone when they are directly addressed" do
  14. user = insert(:user)
  15. other_user = insert(:user)
  16. third_user = insert(:user)
  17. {:ok, activity} =
  18. TwitterAPI.create_status(user, %{
  19. "status" => "hey @#{other_user.nickname} and @#{third_user.nickname}"
  20. })
  21. {:ok, [notification, other_notification]} = Notification.create_notifications(activity)
  22. notified_ids = Enum.sort([notification.user_id, other_notification.user_id])
  23. assert notified_ids == [other_user.id, third_user.id]
  24. assert notification.activity_id == activity.id
  25. assert other_notification.activity_id == activity.id
  26. end
  27. test "it creates a notification for subscribed users" do
  28. user = insert(:user)
  29. subscriber = insert(:user)
  30. User.subscribe(subscriber, user)
  31. {:ok, status} = TwitterAPI.create_status(user, %{"status" => "Akariiiin"})
  32. {:ok, [notification]} = Notification.create_notifications(status)
  33. assert notification.user_id == subscriber.id
  34. end
  35. end
  36. describe "create_notification" do
  37. test "it doesn't create a notification for user if the user blocks the activity author" do
  38. activity = insert(:note_activity)
  39. author = User.get_cached_by_ap_id(activity.data["actor"])
  40. user = insert(:user)
  41. {:ok, user} = User.block(user, author)
  42. assert nil == Notification.create_notification(activity, user)
  43. end
  44. test "it doesn't create a notificatin for the user if the user mutes the activity author" do
  45. muter = insert(:user)
  46. muted = insert(:user)
  47. {:ok, _} = User.mute(muter, muted)
  48. muter = Repo.get(User, muter.id)
  49. {:ok, activity} = CommonAPI.post(muted, %{"status" => "Hi @#{muter.nickname}"})
  50. assert nil == Notification.create_notification(activity, muter)
  51. end
  52. test "it doesn't create a notification for an activity from a muted thread" do
  53. muter = insert(:user)
  54. other_user = insert(:user)
  55. {:ok, activity} = CommonAPI.post(muter, %{"status" => "hey"})
  56. CommonAPI.add_mute(muter, activity)
  57. {:ok, activity} =
  58. CommonAPI.post(other_user, %{
  59. "status" => "Hi @#{muter.nickname}",
  60. "in_reply_to_status_id" => activity.id
  61. })
  62. assert nil == Notification.create_notification(activity, muter)
  63. end
  64. test "it disables notifications from people on remote instances" do
  65. user = insert(:user, info: %{notification_settings: %{"remote" => false}})
  66. other_user = insert(:user)
  67. create_activity = %{
  68. "@context" => "https://www.w3.org/ns/activitystreams",
  69. "type" => "Create",
  70. "to" => ["https://www.w3.org/ns/activitystreams#Public"],
  71. "actor" => other_user.ap_id,
  72. "object" => %{
  73. "type" => "Note",
  74. "content" => "Hi @#{user.nickname}",
  75. "attributedTo" => other_user.ap_id
  76. }
  77. }
  78. {:ok, %{local: false} = activity} = Transmogrifier.handle_incoming(create_activity)
  79. assert nil == Notification.create_notification(activity, user)
  80. end
  81. test "it disables notifications from people on the local instance" do
  82. user = insert(:user, info: %{notification_settings: %{"local" => false}})
  83. other_user = insert(:user)
  84. {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
  85. assert nil == Notification.create_notification(activity, user)
  86. end
  87. test "it disables notifications from followers" do
  88. follower = insert(:user)
  89. followed = insert(:user, info: %{notification_settings: %{"followers" => false}})
  90. User.follow(follower, followed)
  91. {:ok, activity} = CommonAPI.post(follower, %{"status" => "hey @#{followed.nickname}"})
  92. assert nil == Notification.create_notification(activity, followed)
  93. end
  94. test "it disables notifications from people the user follows" do
  95. follower = insert(:user, info: %{notification_settings: %{"follows" => false}})
  96. followed = insert(:user)
  97. User.follow(follower, followed)
  98. follower = Repo.get(User, follower.id)
  99. {:ok, activity} = CommonAPI.post(followed, %{"status" => "hey @#{follower.nickname}"})
  100. assert nil == Notification.create_notification(activity, follower)
  101. end
  102. test "it doesn't create a notification for user if he is the activity author" do
  103. activity = insert(:note_activity)
  104. author = User.get_cached_by_ap_id(activity.data["actor"])
  105. assert nil == Notification.create_notification(activity, author)
  106. end
  107. test "it doesn't create a notification for follow-unfollow-follow chains" do
  108. user = insert(:user)
  109. followed_user = insert(:user)
  110. {:ok, _, _, activity} = TwitterAPI.follow(user, %{"user_id" => followed_user.id})
  111. Notification.create_notification(activity, followed_user)
  112. TwitterAPI.unfollow(user, %{"user_id" => followed_user.id})
  113. {:ok, _, _, activity_dupe} = TwitterAPI.follow(user, %{"user_id" => followed_user.id})
  114. assert nil == Notification.create_notification(activity_dupe, followed_user)
  115. end
  116. test "it doesn't create a notification for like-unlike-like chains" do
  117. user = insert(:user)
  118. liked_user = insert(:user)
  119. {:ok, status} = TwitterAPI.create_status(liked_user, %{"status" => "Yui is best yuru"})
  120. {:ok, fav_status} = TwitterAPI.fav(user, status.id)
  121. Notification.create_notification(fav_status, liked_user)
  122. TwitterAPI.unfav(user, status.id)
  123. {:ok, dupe} = TwitterAPI.fav(user, status.id)
  124. assert nil == Notification.create_notification(dupe, liked_user)
  125. end
  126. test "it doesn't create a notification for repeat-unrepeat-repeat chains" do
  127. user = insert(:user)
  128. retweeted_user = insert(:user)
  129. {:ok, status} =
  130. TwitterAPI.create_status(retweeted_user, %{
  131. "status" => "Send dupe notifications to the shadow realm"
  132. })
  133. {:ok, retweeted_activity} = TwitterAPI.repeat(user, status.id)
  134. Notification.create_notification(retweeted_activity, retweeted_user)
  135. TwitterAPI.unrepeat(user, status.id)
  136. {:ok, dupe} = TwitterAPI.repeat(user, status.id)
  137. assert nil == Notification.create_notification(dupe, retweeted_user)
  138. end
  139. test "it doesn't create duplicate notifications for follow+subscribed users" do
  140. user = insert(:user)
  141. subscriber = insert(:user)
  142. {:ok, _, _, _} = TwitterAPI.follow(subscriber, %{"user_id" => user.id})
  143. User.subscribe(subscriber, user)
  144. {:ok, status} = TwitterAPI.create_status(user, %{"status" => "Akariiiin"})
  145. {:ok, [_notif]} = Notification.create_notifications(status)
  146. end
  147. test "it doesn't create subscription notifications if the recipient cannot see the status" do
  148. user = insert(:user)
  149. subscriber = insert(:user)
  150. User.subscribe(subscriber, user)
  151. {:ok, status} =
  152. TwitterAPI.create_status(user, %{"status" => "inwisible", "visibility" => "direct"})
  153. assert {:ok, []} == Notification.create_notifications(status)
  154. end
  155. end
  156. describe "get notification" do
  157. test "it gets a notification that belongs to the user" do
  158. user = insert(:user)
  159. other_user = insert(:user)
  160. {:ok, activity} =
  161. TwitterAPI.create_status(user, %{"status" => "hey @#{other_user.nickname}"})
  162. {:ok, [notification]} = Notification.create_notifications(activity)
  163. {:ok, notification} = Notification.get(other_user, notification.id)
  164. assert notification.user_id == other_user.id
  165. end
  166. test "it returns error if the notification doesn't belong to the user" do
  167. user = insert(:user)
  168. other_user = insert(:user)
  169. {:ok, activity} =
  170. TwitterAPI.create_status(user, %{"status" => "hey @#{other_user.nickname}"})
  171. {:ok, [notification]} = Notification.create_notifications(activity)
  172. {:error, _notification} = Notification.get(user, notification.id)
  173. end
  174. end
  175. describe "dismiss notification" do
  176. test "it dismisses a notification that belongs to the user" do
  177. user = insert(:user)
  178. other_user = insert(:user)
  179. {:ok, activity} =
  180. TwitterAPI.create_status(user, %{"status" => "hey @#{other_user.nickname}"})
  181. {:ok, [notification]} = Notification.create_notifications(activity)
  182. {:ok, notification} = Notification.dismiss(other_user, notification.id)
  183. assert notification.user_id == other_user.id
  184. end
  185. test "it returns error if the notification doesn't belong to the user" do
  186. user = insert(:user)
  187. other_user = insert(:user)
  188. {:ok, activity} =
  189. TwitterAPI.create_status(user, %{"status" => "hey @#{other_user.nickname}"})
  190. {:ok, [notification]} = Notification.create_notifications(activity)
  191. {:error, _notification} = Notification.dismiss(user, notification.id)
  192. end
  193. end
  194. describe "clear notification" do
  195. test "it clears all notifications belonging to the user" do
  196. user = insert(:user)
  197. other_user = insert(:user)
  198. third_user = insert(:user)
  199. {:ok, activity} =
  200. TwitterAPI.create_status(user, %{
  201. "status" => "hey @#{other_user.nickname} and @#{third_user.nickname} !"
  202. })
  203. {:ok, _notifs} = Notification.create_notifications(activity)
  204. {:ok, activity} =
  205. TwitterAPI.create_status(user, %{
  206. "status" => "hey again @#{other_user.nickname} and @#{third_user.nickname} !"
  207. })
  208. {:ok, _notifs} = Notification.create_notifications(activity)
  209. Notification.clear(other_user)
  210. assert Notification.for_user(other_user) == []
  211. assert Notification.for_user(third_user) != []
  212. end
  213. end
  214. describe "set_read_up_to()" do
  215. test "it sets all notifications as read up to a specified notification ID" do
  216. user = insert(:user)
  217. other_user = insert(:user)
  218. {:ok, _activity} =
  219. TwitterAPI.create_status(user, %{
  220. "status" => "hey @#{other_user.nickname}!"
  221. })
  222. {:ok, _activity} =
  223. TwitterAPI.create_status(user, %{
  224. "status" => "hey again @#{other_user.nickname}!"
  225. })
  226. [n2, n1] = notifs = Notification.for_user(other_user)
  227. assert length(notifs) == 2
  228. assert n2.id > n1.id
  229. {:ok, _activity} =
  230. TwitterAPI.create_status(user, %{
  231. "status" => "hey yet again @#{other_user.nickname}!"
  232. })
  233. Notification.set_read_up_to(other_user, n2.id)
  234. [n3, n2, n1] = Notification.for_user(other_user)
  235. assert n1.seen == true
  236. assert n2.seen == true
  237. assert n3.seen == false
  238. end
  239. end
  240. describe "notification target determination" do
  241. test "it sends notifications to addressed users in new messages" do
  242. user = insert(:user)
  243. other_user = insert(:user)
  244. {:ok, activity} =
  245. CommonAPI.post(user, %{
  246. "status" => "hey @#{other_user.nickname}!"
  247. })
  248. assert other_user in Notification.get_notified_from_activity(activity)
  249. end
  250. test "it sends notifications to mentioned users in new messages" do
  251. user = insert(:user)
  252. other_user = insert(:user)
  253. create_activity = %{
  254. "@context" => "https://www.w3.org/ns/activitystreams",
  255. "type" => "Create",
  256. "to" => ["https://www.w3.org/ns/activitystreams#Public"],
  257. "actor" => user.ap_id,
  258. "object" => %{
  259. "type" => "Note",
  260. "content" => "message with a Mention tag, but no explicit tagging",
  261. "tag" => [
  262. %{
  263. "type" => "Mention",
  264. "href" => other_user.ap_id,
  265. "name" => other_user.nickname
  266. }
  267. ],
  268. "attributedTo" => user.ap_id
  269. }
  270. }
  271. {:ok, activity} = Transmogrifier.handle_incoming(create_activity)
  272. assert other_user in Notification.get_notified_from_activity(activity)
  273. end
  274. test "it does not send notifications to users who are only cc in new messages" do
  275. user = insert(:user)
  276. other_user = insert(:user)
  277. create_activity = %{
  278. "@context" => "https://www.w3.org/ns/activitystreams",
  279. "type" => "Create",
  280. "to" => ["https://www.w3.org/ns/activitystreams#Public"],
  281. "cc" => [other_user.ap_id],
  282. "actor" => user.ap_id,
  283. "object" => %{
  284. "type" => "Note",
  285. "content" => "hi everyone",
  286. "attributedTo" => user.ap_id
  287. }
  288. }
  289. {:ok, activity} = Transmogrifier.handle_incoming(create_activity)
  290. assert other_user not in Notification.get_notified_from_activity(activity)
  291. end
  292. test "it does not send notification to mentioned users in likes" do
  293. user = insert(:user)
  294. other_user = insert(:user)
  295. third_user = insert(:user)
  296. {:ok, activity_one} =
  297. CommonAPI.post(user, %{
  298. "status" => "hey @#{other_user.nickname}!"
  299. })
  300. {:ok, activity_two, _} = CommonAPI.favorite(activity_one.id, third_user)
  301. assert other_user not in Notification.get_notified_from_activity(activity_two)
  302. end
  303. test "it does not send notification to mentioned users in announces" do
  304. user = insert(:user)
  305. other_user = insert(:user)
  306. third_user = insert(:user)
  307. {:ok, activity_one} =
  308. CommonAPI.post(user, %{
  309. "status" => "hey @#{other_user.nickname}!"
  310. })
  311. {:ok, activity_two, _} = CommonAPI.repeat(activity_one.id, third_user)
  312. assert other_user not in Notification.get_notified_from_activity(activity_two)
  313. end
  314. end
  315. describe "notification lifecycle" do
  316. test "liking an activity results in 1 notification, then 0 if the activity is deleted" do
  317. user = insert(:user)
  318. other_user = insert(:user)
  319. {:ok, activity} = CommonAPI.post(user, %{"status" => "test post"})
  320. assert Enum.empty?(Notification.for_user(user))
  321. {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
  322. assert length(Notification.for_user(user)) == 1
  323. {:ok, _} = CommonAPI.delete(activity.id, user)
  324. assert Enum.empty?(Notification.for_user(user))
  325. end
  326. test "liking an activity results in 1 notification, then 0 if the activity is unliked" do
  327. user = insert(:user)
  328. other_user = insert(:user)
  329. {:ok, activity} = CommonAPI.post(user, %{"status" => "test post"})
  330. assert Enum.empty?(Notification.for_user(user))
  331. {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
  332. assert length(Notification.for_user(user)) == 1
  333. {:ok, _, _, _} = CommonAPI.unfavorite(activity.id, other_user)
  334. assert Enum.empty?(Notification.for_user(user))
  335. end
  336. test "repeating an activity results in 1 notification, then 0 if the activity is deleted" do
  337. user = insert(:user)
  338. other_user = insert(:user)
  339. {:ok, activity} = CommonAPI.post(user, %{"status" => "test post"})
  340. assert Enum.empty?(Notification.for_user(user))
  341. {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
  342. assert length(Notification.for_user(user)) == 1
  343. {:ok, _} = CommonAPI.delete(activity.id, user)
  344. assert Enum.empty?(Notification.for_user(user))
  345. end
  346. test "repeating an activity results in 1 notification, then 0 if the activity is unrepeated" do
  347. user = insert(:user)
  348. other_user = insert(:user)
  349. {:ok, activity} = CommonAPI.post(user, %{"status" => "test post"})
  350. assert Enum.empty?(Notification.for_user(user))
  351. {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
  352. assert length(Notification.for_user(user)) == 1
  353. {:ok, _, _} = CommonAPI.unrepeat(activity.id, other_user)
  354. assert Enum.empty?(Notification.for_user(user))
  355. end
  356. test "liking an activity which is already deleted does not generate a notification" do
  357. user = insert(:user)
  358. other_user = insert(:user)
  359. {:ok, activity} = CommonAPI.post(user, %{"status" => "test post"})
  360. assert Enum.empty?(Notification.for_user(user))
  361. {:ok, _deletion_activity} = CommonAPI.delete(activity.id, user)
  362. assert Enum.empty?(Notification.for_user(user))
  363. {:error, _} = CommonAPI.favorite(activity.id, other_user)
  364. assert Enum.empty?(Notification.for_user(user))
  365. end
  366. test "repeating an activity which is already deleted does not generate a notification" do
  367. user = insert(:user)
  368. other_user = insert(:user)
  369. {:ok, activity} = CommonAPI.post(user, %{"status" => "test post"})
  370. assert Enum.empty?(Notification.for_user(user))
  371. {:ok, _deletion_activity} = CommonAPI.delete(activity.id, user)
  372. assert Enum.empty?(Notification.for_user(user))
  373. {:error, _} = CommonAPI.repeat(activity.id, other_user)
  374. assert Enum.empty?(Notification.for_user(user))
  375. end
  376. test "replying to a deleted post without tagging does not generate a notification" do
  377. user = insert(:user)
  378. other_user = insert(:user)
  379. {:ok, activity} = CommonAPI.post(user, %{"status" => "test post"})
  380. {:ok, _deletion_activity} = CommonAPI.delete(activity.id, user)
  381. {:ok, _reply_activity} =
  382. CommonAPI.post(other_user, %{
  383. "status" => "test reply",
  384. "in_reply_to_status_id" => activity.id
  385. })
  386. assert Enum.empty?(Notification.for_user(user))
  387. end
  388. end
  389. end