logo

pleroma

My custom branche(s) on git.pleroma.social/pleroma/pleroma git clone https://hacktivis.me/git/pleroma.git

notification_test.exs (43043B)


  1. # Pleroma: A lightweight social networking server
  2. # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
  3. # SPDX-License-Identifier: AGPL-3.0-only
  4. defmodule Pleroma.NotificationTest do
  5. use Pleroma.DataCase, async: false
  6. import Pleroma.Factory
  7. import Mock
  8. alias Pleroma.FollowingRelationship
  9. alias Pleroma.Notification
  10. alias Pleroma.Repo
  11. alias Pleroma.Tests.ObanHelpers
  12. alias Pleroma.User
  13. alias Pleroma.Web.ActivityPub.ActivityPub
  14. alias Pleroma.Web.ActivityPub.Builder
  15. alias Pleroma.Web.ActivityPub.Transmogrifier
  16. alias Pleroma.Web.CommonAPI
  17. alias Pleroma.Web.MastodonAPI.NotificationView
  18. alias Pleroma.Web.Push
  19. alias Pleroma.Web.Streamer
  20. setup do
  21. Mox.stub_with(Pleroma.UnstubbedConfigMock, Pleroma.Config)
  22. :ok
  23. end
  24. describe "create_notifications" do
  25. test "never returns nil" do
  26. user = insert(:user)
  27. other_user = insert(:user, %{invisible: true})
  28. {:ok, activity} = CommonAPI.post(user, %{status: "yeah"})
  29. {:ok, activity} = CommonAPI.react_with_emoji(activity.id, other_user, "☕")
  30. refute {:ok, [nil]} == Notification.create_notifications(activity)
  31. end
  32. test "creates a report notification only for privileged users" do
  33. reporting_user = insert(:user)
  34. reported_user = insert(:user)
  35. moderator_user = insert(:user, is_moderator: true)
  36. clear_config([:instance, :moderator_privileges], [])
  37. {:ok, activity1} = CommonAPI.report(reporting_user, %{account_id: reported_user.id})
  38. {:ok, []} = Notification.create_notifications(activity1)
  39. clear_config([:instance, :moderator_privileges], [:reports_manage_reports])
  40. {:ok, activity2} = CommonAPI.report(reporting_user, %{account_id: reported_user.id})
  41. {:ok, [notification]} = Notification.create_notifications(activity2)
  42. assert notification.user_id == moderator_user.id
  43. assert notification.type == "pleroma:report"
  44. end
  45. test "suppresses notifications for own reports" do
  46. clear_config([:instance, :admin_privileges], [:reports_manage_reports])
  47. reporting_admin = insert(:user, is_admin: true)
  48. reported_user = insert(:user)
  49. other_admin = insert(:user, is_admin: true)
  50. {:ok, activity} = CommonAPI.report(reporting_admin, %{account_id: reported_user.id})
  51. {:ok, [notification]} = Notification.create_notifications(activity)
  52. refute notification.user_id == reporting_admin.id
  53. assert notification.user_id == other_admin.id
  54. assert notification.type == "pleroma:report"
  55. end
  56. test "creates a notification for an emoji reaction" do
  57. user = insert(:user)
  58. other_user = insert(:user)
  59. {:ok, activity} = CommonAPI.post(user, %{status: "yeah"})
  60. {:ok, activity} = CommonAPI.react_with_emoji(activity.id, other_user, "☕")
  61. {:ok, [notification]} = Notification.create_notifications(activity)
  62. assert notification.user_id == user.id
  63. assert notification.type == "pleroma:emoji_reaction"
  64. end
  65. test "notifies someone when they are directly addressed" do
  66. user = insert(:user)
  67. other_user = insert(:user)
  68. third_user = insert(:user)
  69. {:ok, activity} =
  70. CommonAPI.post(user, %{
  71. status: "hey @#{other_user.nickname} and @#{third_user.nickname}"
  72. })
  73. {:ok, [notification, other_notification]} = Notification.create_notifications(activity)
  74. notified_ids = Enum.sort([notification.user_id, other_notification.user_id])
  75. assert notified_ids == [other_user.id, third_user.id]
  76. assert notification.activity_id == activity.id
  77. assert notification.type == "mention"
  78. assert other_notification.activity_id == activity.id
  79. assert [%Pleroma.Marker{unread_count: 2}] =
  80. Pleroma.Marker.get_markers(other_user, ["notifications"])
  81. end
  82. test "it creates a notification for subscribed users" do
  83. user = insert(:user)
  84. subscriber = insert(:user)
  85. User.subscribe(subscriber, user)
  86. {:ok, status} = CommonAPI.post(user, %{status: "Akariiiin"})
  87. {:ok, [notification]} = Notification.create_notifications(status)
  88. assert notification.user_id == subscriber.id
  89. end
  90. test "does not create a notification for subscribed users if status is a reply" do
  91. user = insert(:user)
  92. other_user = insert(:user)
  93. subscriber = insert(:user)
  94. User.subscribe(subscriber, other_user)
  95. {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
  96. {:ok, _reply_activity} =
  97. CommonAPI.post(other_user, %{
  98. status: "test reply",
  99. in_reply_to_status_id: activity.id
  100. })
  101. user_notifications = Notification.for_user(user)
  102. assert length(user_notifications) == 1
  103. subscriber_notifications = Notification.for_user(subscriber)
  104. assert Enum.empty?(subscriber_notifications)
  105. end
  106. test "it sends edited notifications to those who repeated a status" do
  107. user = insert(:user)
  108. repeated_user = insert(:user)
  109. other_user = insert(:user)
  110. {:ok, activity_one} =
  111. CommonAPI.post(user, %{
  112. status: "hey @#{other_user.nickname}!"
  113. })
  114. {:ok, _activity_two} = CommonAPI.repeat(activity_one.id, repeated_user)
  115. {:ok, _edit_activity} =
  116. CommonAPI.update(user, activity_one, %{
  117. status: "hey @#{other_user.nickname}! mew mew"
  118. })
  119. assert [%{type: "reblog"}] = Notification.for_user(user)
  120. assert [%{type: "update"}] = Notification.for_user(repeated_user)
  121. assert [%{type: "mention"}] = Notification.for_user(other_user)
  122. end
  123. end
  124. test "create_poll_notifications/1" do
  125. [user1, user2, user3, _, _] = insert_list(5, :user)
  126. question = insert(:question, user: user1)
  127. activity = insert(:question_activity, question: question)
  128. {:ok, _, _} = CommonAPI.vote(user2, question, [0])
  129. {:ok, _, _} = CommonAPI.vote(user3, question, [1])
  130. {:ok, notifications} = Notification.create_poll_notifications(activity)
  131. assert [user2.id, user3.id, user1.id] == Enum.map(notifications, & &1.user_id)
  132. end
  133. describe "CommonApi.post/2 notification-related functionality" do
  134. test_with_mock "creates but does NOT send notification to blocker user",
  135. Push,
  136. [:passthrough],
  137. [] do
  138. user = insert(:user)
  139. blocker = insert(:user)
  140. {:ok, _user_relationship} = User.block(blocker, user)
  141. {:ok, _activity} = CommonAPI.post(user, %{status: "hey @#{blocker.nickname}!"})
  142. blocker_id = blocker.id
  143. assert [%Notification{user_id: ^blocker_id}] = Repo.all(Notification)
  144. refute called(Push.send(:_))
  145. end
  146. test_with_mock "creates but does NOT send notification to notification-muter user",
  147. Push,
  148. [:passthrough],
  149. [] do
  150. user = insert(:user)
  151. muter = insert(:user)
  152. {:ok, _user_relationships} = User.mute(muter, user)
  153. {:ok, _activity} = CommonAPI.post(user, %{status: "hey @#{muter.nickname}!"})
  154. muter_id = muter.id
  155. assert [%Notification{user_id: ^muter_id}] = Repo.all(Notification)
  156. refute called(Push.send(:_))
  157. end
  158. test_with_mock "creates but does NOT send notification to thread-muter user",
  159. Push,
  160. [:passthrough],
  161. [] do
  162. user = insert(:user)
  163. thread_muter = insert(:user)
  164. {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{thread_muter.nickname}!"})
  165. {:ok, _} = CommonAPI.add_mute(thread_muter, activity)
  166. {:ok, _same_context_activity} =
  167. CommonAPI.post(user, %{
  168. status: "hey-hey-hey @#{thread_muter.nickname}!",
  169. in_reply_to_status_id: activity.id
  170. })
  171. [pre_mute_notification, post_mute_notification] =
  172. Repo.all(from(n in Notification, where: n.user_id == ^thread_muter.id, order_by: n.id))
  173. pre_mute_notification_id = pre_mute_notification.id
  174. post_mute_notification_id = post_mute_notification.id
  175. assert called(
  176. Push.send(
  177. :meck.is(fn
  178. %Notification{id: ^pre_mute_notification_id} -> true
  179. _ -> false
  180. end)
  181. )
  182. )
  183. refute called(
  184. Push.send(
  185. :meck.is(fn
  186. %Notification{id: ^post_mute_notification_id} -> true
  187. _ -> false
  188. end)
  189. )
  190. )
  191. end
  192. end
  193. describe "create_notification" do
  194. @tag needs_streamer: true
  195. test "it creates a notification for user and send to the 'user' and the 'user:notification' stream" do
  196. %{user: user, token: oauth_token} = oauth_access(["read"])
  197. task =
  198. Task.async(fn ->
  199. {:ok, _topic} = Streamer.get_topic_and_add_socket("user", user, oauth_token)
  200. assert_receive {:render_with_user, _, _, _, _}, 4_000
  201. end)
  202. task_user_notification =
  203. Task.async(fn ->
  204. {:ok, _topic} =
  205. Streamer.get_topic_and_add_socket("user:notification", user, oauth_token)
  206. assert_receive {:render_with_user, _, _, _, _}, 4_000
  207. end)
  208. activity = insert(:note_activity)
  209. notify = Notification.create_notification(activity, user)
  210. assert notify.user_id == user.id
  211. Task.await(task)
  212. Task.await(task_user_notification)
  213. end
  214. test "it creates a notification for user if the user blocks the activity author" do
  215. activity = insert(:note_activity)
  216. author = User.get_cached_by_ap_id(activity.data["actor"])
  217. user = insert(:user)
  218. {:ok, _user_relationship} = User.block(user, author)
  219. assert Notification.create_notification(activity, user)
  220. end
  221. test "it creates a notification for the user if the user mutes the activity author" do
  222. muter = insert(:user)
  223. muted = insert(:user)
  224. {:ok, _} = User.mute(muter, muted)
  225. muter = Repo.get(User, muter.id)
  226. {:ok, activity} = CommonAPI.post(muted, %{status: "Hi @#{muter.nickname}"})
  227. notification = Notification.create_notification(activity, muter)
  228. assert notification.id
  229. assert notification.seen
  230. end
  231. test "notification created if user is muted without notifications" do
  232. muter = insert(:user)
  233. muted = insert(:user)
  234. {:ok, _user_relationships} = User.mute(muter, muted, %{notifications: false})
  235. {:ok, activity} = CommonAPI.post(muted, %{status: "Hi @#{muter.nickname}"})
  236. assert Notification.create_notification(activity, muter)
  237. end
  238. test "it creates a notification for an activity from a muted thread" do
  239. muter = insert(:user)
  240. other_user = insert(:user)
  241. {:ok, activity} = CommonAPI.post(muter, %{status: "hey"})
  242. CommonAPI.add_mute(muter, activity)
  243. {:ok, activity} =
  244. CommonAPI.post(other_user, %{
  245. status: "Hi @#{muter.nickname}",
  246. in_reply_to_status_id: activity.id
  247. })
  248. notification = Notification.create_notification(activity, muter)
  249. assert notification.id
  250. assert notification.seen
  251. end
  252. test "it disables notifications from strangers" do
  253. follower = insert(:user)
  254. followed =
  255. insert(:user,
  256. notification_settings: %Pleroma.User.NotificationSetting{block_from_strangers: true}
  257. )
  258. {:ok, activity} = CommonAPI.post(follower, %{status: "hey @#{followed.nickname}"})
  259. refute Notification.create_notification(activity, followed)
  260. end
  261. test "it disables notifications from non-followees" do
  262. follower = insert(:user)
  263. followed =
  264. insert(:user,
  265. notification_settings: %Pleroma.User.NotificationSetting{block_from_strangers: true}
  266. )
  267. CommonAPI.follow(follower, followed)
  268. {:ok, activity} = CommonAPI.post(follower, %{status: "hey @#{followed.nickname}"})
  269. refute Notification.create_notification(activity, followed)
  270. end
  271. test "it allows notifications from followees" do
  272. poster = insert(:user)
  273. receiver =
  274. insert(:user,
  275. notification_settings: %Pleroma.User.NotificationSetting{block_from_strangers: true}
  276. )
  277. CommonAPI.follow(receiver, poster)
  278. {:ok, activity} = CommonAPI.post(poster, %{status: "hey @#{receiver.nickname}"})
  279. assert Notification.create_notification(activity, receiver)
  280. end
  281. test "it doesn't create a notification for user if he is the activity author" do
  282. activity = insert(:note_activity)
  283. author = User.get_cached_by_ap_id(activity.data["actor"])
  284. refute Notification.create_notification(activity, author)
  285. end
  286. test "it doesn't create duplicate notifications for follow+subscribed users" do
  287. user = insert(:user)
  288. subscriber = insert(:user)
  289. {:ok, _, _, _} = CommonAPI.follow(subscriber, user)
  290. User.subscribe(subscriber, user)
  291. {:ok, status} = CommonAPI.post(user, %{status: "Akariiiin"})
  292. {:ok, [_notif]} = Notification.create_notifications(status)
  293. end
  294. test "it doesn't create subscription notifications if the recipient cannot see the status" do
  295. user = insert(:user)
  296. subscriber = insert(:user)
  297. User.subscribe(subscriber, user)
  298. {:ok, status} = CommonAPI.post(user, %{status: "inwisible", visibility: "direct"})
  299. assert {:ok, []} == Notification.create_notifications(status)
  300. end
  301. test "it disables notifications from people who are invisible" do
  302. author = insert(:user, invisible: true)
  303. user = insert(:user)
  304. {:ok, status} = CommonAPI.post(author, %{status: "hey @#{user.nickname}"})
  305. refute Notification.create_notification(status, user)
  306. end
  307. test "it doesn't create notifications if content matches with an irreversible filter" do
  308. user = insert(:user)
  309. subscriber = insert(:user)
  310. User.subscribe(subscriber, user)
  311. insert(:filter, user: subscriber, phrase: "cofe", hide: true)
  312. {:ok, status} = CommonAPI.post(user, %{status: "got cofe?"})
  313. assert {:ok, []} == Notification.create_notifications(status)
  314. end
  315. test "it creates notifications if content matches with a not irreversible filter" do
  316. user = insert(:user)
  317. subscriber = insert(:user)
  318. User.subscribe(subscriber, user)
  319. insert(:filter, user: subscriber, phrase: "cofe", hide: false)
  320. {:ok, status} = CommonAPI.post(user, %{status: "got cofe?"})
  321. {:ok, [notification]} = Notification.create_notifications(status)
  322. assert notification
  323. refute notification.seen
  324. end
  325. test "it creates notifications when someone likes user's status with a filtered word" do
  326. user = insert(:user)
  327. other_user = insert(:user)
  328. insert(:filter, user: user, phrase: "tesla", hide: true)
  329. {:ok, activity_one} = CommonAPI.post(user, %{status: "wow tesla"})
  330. {:ok, activity_two} = CommonAPI.favorite(other_user, activity_one.id)
  331. {:ok, [notification]} = Notification.create_notifications(activity_two)
  332. assert notification
  333. refute notification.seen
  334. end
  335. end
  336. describe "follow / follow_request notifications" do
  337. test "it creates `follow` notification for approved Follow activity" do
  338. user = insert(:user)
  339. followed_user = insert(:user, is_locked: false)
  340. {:ok, _, _, _activity} = CommonAPI.follow(user, followed_user)
  341. assert FollowingRelationship.following?(user, followed_user)
  342. assert [notification] = Notification.for_user(followed_user)
  343. assert %{type: "follow"} =
  344. NotificationView.render("show.json", %{
  345. notification: notification,
  346. for: followed_user
  347. })
  348. end
  349. test "it creates `follow_request` notification for pending Follow activity" do
  350. user = insert(:user)
  351. followed_user = insert(:user, is_locked: true)
  352. {:ok, _, _, _activity} = CommonAPI.follow(user, followed_user)
  353. refute FollowingRelationship.following?(user, followed_user)
  354. assert [notification] = Notification.for_user(followed_user)
  355. render_opts = %{notification: notification, for: followed_user}
  356. assert %{type: "follow_request"} = NotificationView.render("show.json", render_opts)
  357. # After request is accepted, the same notification is rendered with type "follow":
  358. assert {:ok, _} = CommonAPI.accept_follow_request(user, followed_user)
  359. notification =
  360. Repo.get(Notification, notification.id)
  361. |> Repo.preload(:activity)
  362. assert %{type: "follow"} =
  363. NotificationView.render("show.json", notification: notification, for: followed_user)
  364. end
  365. test "it doesn't create a notification for follow-unfollow-follow chains" do
  366. user = insert(:user)
  367. followed_user = insert(:user, is_locked: false)
  368. {:ok, _, _, _activity} = CommonAPI.follow(user, followed_user)
  369. assert FollowingRelationship.following?(user, followed_user)
  370. assert [notification] = Notification.for_user(followed_user)
  371. CommonAPI.unfollow(user, followed_user)
  372. {:ok, _, _, _activity_dupe} = CommonAPI.follow(user, followed_user)
  373. notification_id = notification.id
  374. assert [%{id: ^notification_id}] = Notification.for_user(followed_user)
  375. end
  376. test "dismisses the notification on follow request rejection" do
  377. user = insert(:user, is_locked: true)
  378. follower = insert(:user)
  379. {:ok, _, _, _follow_activity} = CommonAPI.follow(follower, user)
  380. assert [_notification] = Notification.for_user(user)
  381. {:ok, _follower} = CommonAPI.reject_follow_request(follower, user)
  382. assert [] = Notification.for_user(user)
  383. end
  384. end
  385. describe "get notification" do
  386. test "it gets a notification that belongs to the user" do
  387. user = insert(:user)
  388. other_user = insert(:user)
  389. {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}"})
  390. {:ok, [notification]} = Notification.create_notifications(activity)
  391. {:ok, notification} = Notification.get(other_user, notification.id)
  392. assert notification.user_id == other_user.id
  393. end
  394. test "it returns error if the notification doesn't belong to the user" do
  395. user = insert(:user)
  396. other_user = insert(:user)
  397. {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}"})
  398. {:ok, [notification]} = Notification.create_notifications(activity)
  399. {:error, _notification} = Notification.get(user, notification.id)
  400. end
  401. end
  402. describe "dismiss notification" do
  403. test "it dismisses a notification that belongs to the user" do
  404. user = insert(:user)
  405. other_user = insert(:user)
  406. {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}"})
  407. {:ok, [notification]} = Notification.create_notifications(activity)
  408. {:ok, notification} = Notification.dismiss(other_user, notification.id)
  409. assert notification.user_id == other_user.id
  410. end
  411. test "it returns error if the notification doesn't belong to the user" do
  412. user = insert(:user)
  413. other_user = insert(:user)
  414. {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}"})
  415. {:ok, [notification]} = Notification.create_notifications(activity)
  416. {:error, _notification} = Notification.dismiss(user, notification.id)
  417. end
  418. end
  419. describe "clear notification" do
  420. test "it clears all notifications belonging to the user" do
  421. user = insert(:user)
  422. other_user = insert(:user)
  423. third_user = insert(:user)
  424. {:ok, activity} =
  425. CommonAPI.post(user, %{
  426. status: "hey @#{other_user.nickname} and @#{third_user.nickname} !"
  427. })
  428. {:ok, _notifs} = Notification.create_notifications(activity)
  429. {:ok, activity} =
  430. CommonAPI.post(user, %{
  431. status: "hey again @#{other_user.nickname} and @#{third_user.nickname} !"
  432. })
  433. {:ok, _notifs} = Notification.create_notifications(activity)
  434. Notification.clear(other_user)
  435. assert Notification.for_user(other_user) == []
  436. assert Notification.for_user(third_user) != []
  437. end
  438. end
  439. describe "set_read_up_to()" do
  440. test "it sets all notifications as read up to a specified notification ID" do
  441. user = insert(:user)
  442. other_user = insert(:user)
  443. {:ok, _activity} =
  444. CommonAPI.post(user, %{
  445. status: "hey @#{other_user.nickname}!"
  446. })
  447. {:ok, _activity} =
  448. CommonAPI.post(user, %{
  449. status: "hey again @#{other_user.nickname}!"
  450. })
  451. [n2, n1] = Notification.for_user(other_user)
  452. assert n2.id > n1.id
  453. {:ok, _activity} =
  454. CommonAPI.post(user, %{
  455. status: "hey yet again @#{other_user.nickname}!"
  456. })
  457. [_, read_notification] = Notification.set_read_up_to(other_user, n2.id)
  458. assert read_notification.activity.object
  459. [n3, n2, n1] = Notification.for_user(other_user)
  460. assert n1.seen == true
  461. assert n2.seen == true
  462. assert n3.seen == false
  463. assert %Pleroma.Marker{} =
  464. m =
  465. Pleroma.Repo.get_by(
  466. Pleroma.Marker,
  467. user_id: other_user.id,
  468. timeline: "notifications"
  469. )
  470. assert m.last_read_id == to_string(n2.id)
  471. end
  472. end
  473. describe "for_user_since/2" do
  474. defp days_ago(days) do
  475. NaiveDateTime.add(
  476. NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second),
  477. -days * 60 * 60 * 24,
  478. :second
  479. )
  480. end
  481. test "Returns recent notifications" do
  482. user1 = insert(:user)
  483. user2 = insert(:user)
  484. Enum.each(0..10, fn i ->
  485. {:ok, _activity} =
  486. CommonAPI.post(user1, %{
  487. status: "hey ##{i} @#{user2.nickname}!"
  488. })
  489. end)
  490. {old, new} = Enum.split(Notification.for_user(user2), 5)
  491. Enum.each(old, fn notification ->
  492. notification
  493. |> cast(%{updated_at: days_ago(10)}, [:updated_at])
  494. |> Pleroma.Repo.update!()
  495. end)
  496. recent_notifications_ids =
  497. user2
  498. |> Notification.for_user_since(
  499. NaiveDateTime.add(NaiveDateTime.utc_now(), -5 * 86_400, :second)
  500. )
  501. |> Enum.map(& &1.id)
  502. Enum.each(old, fn %{id: id} ->
  503. refute id in recent_notifications_ids
  504. end)
  505. Enum.each(new, fn %{id: id} ->
  506. assert id in recent_notifications_ids
  507. end)
  508. end
  509. end
  510. describe "notification target determination / get_notified_from_activity/2" do
  511. test "it sends notifications to addressed users in new messages" do
  512. user = insert(:user)
  513. other_user = insert(:user)
  514. {:ok, activity} =
  515. CommonAPI.post(user, %{
  516. status: "hey @#{other_user.nickname}!"
  517. })
  518. {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity)
  519. assert other_user in enabled_receivers
  520. end
  521. test "it sends notifications to mentioned users in new messages" do
  522. user = insert(:user)
  523. other_user = insert(:user)
  524. create_activity = %{
  525. "@context" => "https://www.w3.org/ns/activitystreams",
  526. "type" => "Create",
  527. "to" => ["https://www.w3.org/ns/activitystreams#Public"],
  528. "actor" => user.ap_id,
  529. "object" => %{
  530. "type" => "Note",
  531. "id" => Pleroma.Web.ActivityPub.Utils.generate_object_id(),
  532. "to" => ["https://www.w3.org/ns/activitystreams#Public"],
  533. "content" => "message with a Mention tag, but no explicit tagging",
  534. "tag" => [
  535. %{
  536. "type" => "Mention",
  537. "href" => other_user.ap_id,
  538. "name" => other_user.nickname
  539. }
  540. ],
  541. "attributedTo" => user.ap_id
  542. }
  543. }
  544. {:ok, activity} = Transmogrifier.handle_incoming(create_activity)
  545. {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity)
  546. assert other_user in enabled_receivers
  547. end
  548. test "it does not send notifications to users who are only cc in new messages" do
  549. user = insert(:user)
  550. other_user = insert(:user)
  551. create_activity = %{
  552. "@context" => "https://www.w3.org/ns/activitystreams",
  553. "type" => "Create",
  554. "to" => ["https://www.w3.org/ns/activitystreams#Public"],
  555. "cc" => [other_user.ap_id],
  556. "actor" => user.ap_id,
  557. "object" => %{
  558. "type" => "Note",
  559. "id" => Pleroma.Web.ActivityPub.Utils.generate_object_id(),
  560. "to" => ["https://www.w3.org/ns/activitystreams#Public"],
  561. "cc" => [other_user.ap_id],
  562. "content" => "hi everyone",
  563. "attributedTo" => user.ap_id
  564. }
  565. }
  566. {:ok, activity} = Transmogrifier.handle_incoming(create_activity)
  567. {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity)
  568. assert other_user not in enabled_receivers
  569. end
  570. test "it does not send notification to mentioned users in likes" do
  571. user = insert(:user)
  572. other_user = insert(:user)
  573. third_user = insert(:user)
  574. {:ok, activity_one} =
  575. CommonAPI.post(user, %{
  576. status: "hey @#{other_user.nickname}!"
  577. })
  578. {:ok, activity_two} = CommonAPI.favorite(third_user, activity_one.id)
  579. {enabled_receivers, _disabled_receivers} =
  580. Notification.get_notified_from_activity(activity_two)
  581. assert other_user not in enabled_receivers
  582. end
  583. test "it only notifies the post's author in likes" do
  584. user = insert(:user)
  585. other_user = insert(:user)
  586. third_user = insert(:user)
  587. {:ok, activity_one} =
  588. CommonAPI.post(user, %{
  589. status: "hey @#{other_user.nickname}!"
  590. })
  591. {:ok, like_data, _} = Builder.like(third_user, activity_one.object)
  592. {:ok, like, _} =
  593. like_data
  594. |> Map.put("to", [other_user.ap_id | like_data["to"]])
  595. |> ActivityPub.persist(local: true)
  596. {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(like)
  597. assert other_user not in enabled_receivers
  598. end
  599. test "it does not send notification to mentioned users in announces" do
  600. user = insert(:user)
  601. other_user = insert(:user)
  602. third_user = insert(:user)
  603. {:ok, activity_one} =
  604. CommonAPI.post(user, %{
  605. status: "hey @#{other_user.nickname}!"
  606. })
  607. {:ok, activity_two} = CommonAPI.repeat(activity_one.id, third_user)
  608. {enabled_receivers, _disabled_receivers} =
  609. Notification.get_notified_from_activity(activity_two)
  610. assert other_user not in enabled_receivers
  611. end
  612. test "it returns blocking recipient in disabled recipients list" do
  613. user = insert(:user)
  614. other_user = insert(:user)
  615. {:ok, _user_relationship} = User.block(other_user, user)
  616. {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
  617. {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
  618. assert [] == enabled_receivers
  619. assert [other_user] == disabled_receivers
  620. end
  621. test "it returns notification-muting recipient in disabled recipients list" do
  622. user = insert(:user)
  623. other_user = insert(:user)
  624. {:ok, _user_relationships} = User.mute(other_user, user)
  625. {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
  626. {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
  627. assert [] == enabled_receivers
  628. assert [other_user] == disabled_receivers
  629. end
  630. test "it returns thread-muting recipient in disabled recipients list" do
  631. user = insert(:user)
  632. other_user = insert(:user)
  633. {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
  634. {:ok, _} = CommonAPI.add_mute(other_user, activity)
  635. {:ok, same_context_activity} =
  636. CommonAPI.post(user, %{
  637. status: "hey-hey-hey @#{other_user.nickname}!",
  638. in_reply_to_status_id: activity.id
  639. })
  640. {enabled_receivers, disabled_receivers} =
  641. Notification.get_notified_from_activity(same_context_activity)
  642. assert [other_user] == disabled_receivers
  643. refute other_user in enabled_receivers
  644. end
  645. test "it returns non-following domain-blocking recipient in disabled recipients list" do
  646. blocked_domain = "blocked.domain"
  647. user = insert(:user, %{ap_id: "https://#{blocked_domain}/@actor"})
  648. other_user = insert(:user)
  649. {:ok, other_user} = User.block_domain(other_user, blocked_domain)
  650. {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
  651. {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
  652. assert [] == enabled_receivers
  653. assert [other_user] == disabled_receivers
  654. end
  655. test "it returns following domain-blocking recipient in enabled recipients list" do
  656. blocked_domain = "blocked.domain"
  657. user = insert(:user, %{ap_id: "https://#{blocked_domain}/@actor"})
  658. other_user = insert(:user)
  659. {:ok, other_user} = User.block_domain(other_user, blocked_domain)
  660. {:ok, other_user, user} = User.follow(other_user, user)
  661. {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
  662. {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
  663. assert [other_user] == enabled_receivers
  664. assert [] == disabled_receivers
  665. end
  666. test "it sends edited notifications to those who repeated a status" do
  667. user = insert(:user)
  668. repeated_user = insert(:user)
  669. other_user = insert(:user)
  670. {:ok, activity_one} =
  671. CommonAPI.post(user, %{
  672. status: "hey @#{other_user.nickname}!"
  673. })
  674. {:ok, _activity_two} = CommonAPI.repeat(activity_one.id, repeated_user)
  675. {:ok, edit_activity} =
  676. CommonAPI.update(user, activity_one, %{
  677. status: "hey @#{other_user.nickname}! mew mew"
  678. })
  679. {enabled_receivers, _disabled_receivers} =
  680. Notification.get_notified_from_activity(edit_activity)
  681. assert repeated_user in enabled_receivers
  682. assert other_user not in enabled_receivers
  683. end
  684. end
  685. describe "notification lifecycle" do
  686. test "liking an activity results in 1 notification, then 0 if the activity is deleted" do
  687. user = insert(:user)
  688. other_user = insert(:user)
  689. {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
  690. assert Enum.empty?(Notification.for_user(user))
  691. {:ok, _} = CommonAPI.favorite(other_user, activity.id)
  692. assert length(Notification.for_user(user)) == 1
  693. {:ok, _} = CommonAPI.delete(activity.id, user)
  694. assert Enum.empty?(Notification.for_user(user))
  695. end
  696. test "liking an activity results in 1 notification, then 0 if the activity is unliked" do
  697. user = insert(:user)
  698. other_user = insert(:user)
  699. {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
  700. assert Enum.empty?(Notification.for_user(user))
  701. {:ok, _} = CommonAPI.favorite(other_user, activity.id)
  702. assert length(Notification.for_user(user)) == 1
  703. {:ok, _} = CommonAPI.unfavorite(activity.id, other_user)
  704. assert Enum.empty?(Notification.for_user(user))
  705. end
  706. test "repeating an activity results in 1 notification, then 0 if the activity is deleted" do
  707. user = insert(:user)
  708. other_user = insert(:user)
  709. {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
  710. assert Enum.empty?(Notification.for_user(user))
  711. {:ok, _} = CommonAPI.repeat(activity.id, other_user)
  712. assert length(Notification.for_user(user)) == 1
  713. {:ok, _} = CommonAPI.delete(activity.id, user)
  714. assert Enum.empty?(Notification.for_user(user))
  715. end
  716. test "repeating an activity results in 1 notification, then 0 if the activity is unrepeated" do
  717. user = insert(:user)
  718. other_user = insert(:user)
  719. {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
  720. assert Enum.empty?(Notification.for_user(user))
  721. {:ok, _} = CommonAPI.repeat(activity.id, other_user)
  722. assert length(Notification.for_user(user)) == 1
  723. {:ok, _} = CommonAPI.unrepeat(activity.id, other_user)
  724. assert Enum.empty?(Notification.for_user(user))
  725. end
  726. test "liking an activity which is already deleted does not generate a notification" do
  727. user = insert(:user)
  728. other_user = insert(:user)
  729. {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
  730. assert Enum.empty?(Notification.for_user(user))
  731. {:ok, _deletion_activity} = CommonAPI.delete(activity.id, user)
  732. assert Enum.empty?(Notification.for_user(user))
  733. {:error, :not_found} = CommonAPI.favorite(other_user, activity.id)
  734. assert Enum.empty?(Notification.for_user(user))
  735. end
  736. test "repeating an activity which is already deleted does not generate a notification" do
  737. user = insert(:user)
  738. other_user = insert(:user)
  739. {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
  740. assert Enum.empty?(Notification.for_user(user))
  741. {:ok, _deletion_activity} = CommonAPI.delete(activity.id, user)
  742. assert Enum.empty?(Notification.for_user(user))
  743. {:error, _} = CommonAPI.repeat(activity.id, other_user)
  744. assert Enum.empty?(Notification.for_user(user))
  745. end
  746. test "replying to a deleted post without tagging does not generate a notification" do
  747. user = insert(:user)
  748. other_user = insert(:user)
  749. {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
  750. {:ok, _deletion_activity} = CommonAPI.delete(activity.id, user)
  751. {:ok, _reply_activity} =
  752. CommonAPI.post(other_user, %{
  753. status: "test reply",
  754. in_reply_to_status_id: activity.id
  755. })
  756. assert Enum.empty?(Notification.for_user(user))
  757. end
  758. test "notifications are deleted if a local user is deleted" do
  759. user = insert(:user)
  760. other_user = insert(:user)
  761. {:ok, _activity} =
  762. CommonAPI.post(user, %{status: "hi @#{other_user.nickname}", visibility: "direct"})
  763. refute Enum.empty?(Notification.for_user(other_user))
  764. {:ok, job} = User.delete(user)
  765. ObanHelpers.perform(job)
  766. assert Enum.empty?(Notification.for_user(other_user))
  767. end
  768. test "notifications are deleted if a remote user is deleted" do
  769. remote_user = insert(:user)
  770. local_user = insert(:user)
  771. dm_message = %{
  772. "@context" => "https://www.w3.org/ns/activitystreams",
  773. "type" => "Create",
  774. "actor" => remote_user.ap_id,
  775. "id" => remote_user.ap_id <> "/activities/test",
  776. "to" => [local_user.ap_id],
  777. "cc" => [],
  778. "object" => %{
  779. "type" => "Note",
  780. "id" => remote_user.ap_id <> "/objects/test",
  781. "content" => "Hello!",
  782. "tag" => [
  783. %{
  784. "type" => "Mention",
  785. "href" => local_user.ap_id,
  786. "name" => "@#{local_user.nickname}"
  787. }
  788. ],
  789. "to" => [local_user.ap_id],
  790. "cc" => [],
  791. "attributedTo" => remote_user.ap_id
  792. }
  793. }
  794. {:ok, _dm_activity} = Transmogrifier.handle_incoming(dm_message)
  795. refute Enum.empty?(Notification.for_user(local_user))
  796. delete_user_message = %{
  797. "@context" => "https://www.w3.org/ns/activitystreams",
  798. "id" => remote_user.ap_id <> "/activities/delete",
  799. "actor" => remote_user.ap_id,
  800. "type" => "Delete",
  801. "object" => remote_user.ap_id
  802. }
  803. remote_user_url = remote_user.ap_id
  804. Tesla.Mock.mock(fn
  805. %{method: :get, url: ^remote_user_url} ->
  806. %Tesla.Env{status: 404, body: ""}
  807. end)
  808. {:ok, _delete_activity} = Transmogrifier.handle_incoming(delete_user_message)
  809. ObanHelpers.perform_all()
  810. assert Enum.empty?(Notification.for_user(local_user))
  811. end
  812. test "move activity generates a notification" do
  813. %{ap_id: old_ap_id} = old_user = insert(:user)
  814. %{ap_id: new_ap_id} = new_user = insert(:user, also_known_as: [old_ap_id])
  815. follower = insert(:user)
  816. other_follower = insert(:user, %{allow_following_move: false})
  817. User.follow(follower, old_user)
  818. User.follow(other_follower, old_user)
  819. Pleroma.Web.ActivityPub.ActivityPub.move(old_user, new_user)
  820. ObanHelpers.perform_all()
  821. assert [
  822. %{
  823. activity: %{
  824. data: %{"type" => "Move", "actor" => ^old_ap_id, "target" => ^new_ap_id}
  825. }
  826. }
  827. ] = Notification.for_user(follower)
  828. assert [
  829. %{
  830. activity: %{
  831. data: %{"type" => "Move", "actor" => ^old_ap_id, "target" => ^new_ap_id}
  832. }
  833. }
  834. ] = Notification.for_user(other_follower)
  835. end
  836. end
  837. describe "for_user" do
  838. setup do
  839. user = insert(:user)
  840. {:ok, %{user: user}}
  841. end
  842. test "it returns notifications for muted user without notifications", %{user: user} do
  843. muted = insert(:user)
  844. {:ok, _user_relationships} = User.mute(user, muted, %{notifications: false})
  845. {:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"})
  846. [notification] = Notification.for_user(user)
  847. assert notification.activity.object
  848. assert notification.seen
  849. end
  850. test "it doesn't return notifications for muted user with notifications", %{user: user} do
  851. muted = insert(:user)
  852. {:ok, _user_relationships} = User.mute(user, muted)
  853. {:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"})
  854. assert Notification.for_user(user) == []
  855. end
  856. test "it doesn't return notifications for blocked user", %{user: user} do
  857. blocked = insert(:user)
  858. {:ok, _user_relationship} = User.block(user, blocked)
  859. {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
  860. assert Notification.for_user(user) == []
  861. end
  862. test "it doesn't return notifications for domain-blocked non-followed user", %{user: user} do
  863. blocked = insert(:user, ap_id: "http://some-domain.com")
  864. {:ok, user} = User.block_domain(user, "some-domain.com")
  865. {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
  866. assert Notification.for_user(user) == []
  867. end
  868. test "it returns notifications for domain-blocked but followed user" do
  869. user = insert(:user)
  870. blocked = insert(:user, ap_id: "http://some-domain.com")
  871. {:ok, user} = User.block_domain(user, "some-domain.com")
  872. {:ok, _, _} = User.follow(user, blocked)
  873. {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
  874. assert length(Notification.for_user(user)) == 1
  875. end
  876. test "it doesn't return notifications for muted thread", %{user: user} do
  877. another_user = insert(:user)
  878. {:ok, activity} = CommonAPI.post(another_user, %{status: "hey @#{user.nickname}"})
  879. {:ok, _} = Pleroma.ThreadMute.add_mute(user.id, activity.data["context"])
  880. assert Notification.for_user(user) == []
  881. end
  882. test "it returns notifications from a muted user when with_muted is set", %{user: user} do
  883. muted = insert(:user)
  884. {:ok, _user_relationships} = User.mute(user, muted)
  885. {:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"})
  886. assert length(Notification.for_user(user, %{with_muted: true})) == 1
  887. end
  888. test "it doesn't return notifications from a blocked user when with_muted is set", %{
  889. user: user
  890. } do
  891. blocked = insert(:user)
  892. {:ok, _user_relationship} = User.block(user, blocked)
  893. {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
  894. assert Enum.empty?(Notification.for_user(user, %{with_muted: true}))
  895. end
  896. test "when with_muted is set, " <>
  897. "it doesn't return notifications from a domain-blocked non-followed user",
  898. %{user: user} do
  899. blocked = insert(:user, ap_id: "http://some-domain.com")
  900. {:ok, user} = User.block_domain(user, "some-domain.com")
  901. {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
  902. assert Enum.empty?(Notification.for_user(user, %{with_muted: true}))
  903. end
  904. test "it returns notifications from muted threads when with_muted is set", %{user: user} do
  905. another_user = insert(:user)
  906. {:ok, activity} = CommonAPI.post(another_user, %{status: "hey @#{user.nickname}"})
  907. {:ok, _} = Pleroma.ThreadMute.add_mute(user.id, activity.data["context"])
  908. assert length(Notification.for_user(user, %{with_muted: true})) == 1
  909. end
  910. test "it doesn't return notifications about mentions with filtered word", %{user: user} do
  911. insert(:filter, user: user, phrase: "cofe", hide: true)
  912. another_user = insert(:user)
  913. {:ok, _activity} = CommonAPI.post(another_user, %{status: "@#{user.nickname} got cofe?"})
  914. assert Enum.empty?(Notification.for_user(user))
  915. end
  916. test "it returns notifications about mentions with not hidden filtered word", %{user: user} do
  917. insert(:filter, user: user, phrase: "test", hide: false)
  918. another_user = insert(:user)
  919. {:ok, _} = CommonAPI.post(another_user, %{status: "@#{user.nickname} test"})
  920. assert length(Notification.for_user(user)) == 1
  921. end
  922. test "it returns notifications about favorites with filtered word", %{user: user} do
  923. insert(:filter, user: user, phrase: "cofe", hide: true)
  924. another_user = insert(:user)
  925. {:ok, activity} = CommonAPI.post(user, %{status: "Give me my cofe!"})
  926. {:ok, _} = CommonAPI.favorite(another_user, activity.id)
  927. assert length(Notification.for_user(user)) == 1
  928. end
  929. test "it returns notifications when related object is without content and filters are defined",
  930. %{user: user} do
  931. followed_user = insert(:user, is_locked: true)
  932. insert(:filter, user: followed_user, phrase: "test", hide: true)
  933. {:ok, _, _, _activity} = CommonAPI.follow(user, followed_user)
  934. refute FollowingRelationship.following?(user, followed_user)
  935. assert [notification] = Notification.for_user(followed_user)
  936. assert %{type: "follow_request"} =
  937. NotificationView.render("show.json", %{
  938. notification: notification,
  939. for: followed_user
  940. })
  941. assert {:ok, _} = CommonAPI.accept_follow_request(user, followed_user)
  942. assert [notification] = Notification.for_user(followed_user)
  943. assert %{type: "follow"} =
  944. NotificationView.render("show.json", %{
  945. notification: notification,
  946. for: followed_user
  947. })
  948. end
  949. end
  950. end