1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.NotificationTest do
11 alias Pleroma.FollowingRelationship
12 alias Pleroma.Notification
14 alias Pleroma.Tests.ObanHelpers
16 alias Pleroma.Web.ActivityPub.ActivityPub
17 alias Pleroma.Web.ActivityPub.Builder
18 alias Pleroma.Web.ActivityPub.Transmogrifier
19 alias Pleroma.Web.CommonAPI
20 alias Pleroma.Web.MastodonAPI.NotificationView
21 alias Pleroma.Web.Push
22 alias Pleroma.Web.Streamer
24 describe "create_notifications" do
25 test "never returns nil" do
27 other_user = insert(:user, %{invisible: true})
29 {:ok, activity} = CommonAPI.post(user, %{status: "yeah"})
30 {:ok, activity} = CommonAPI.react_with_emoji(activity.id, other_user, "☕")
32 refute {:ok, [nil]} == Notification.create_notifications(activity)
35 test "creates a notification for a report" do
36 reporting_user = insert(:user)
37 reported_user = insert(:user)
38 {:ok, moderator_user} = insert(:user) |> User.admin_api_update(%{is_moderator: true})
40 {:ok, activity} = CommonAPI.report(reporting_user, %{account_id: reported_user.id})
42 {:ok, [notification]} = Notification.create_notifications(activity)
44 assert notification.user_id == moderator_user.id
45 assert notification.type == "pleroma:report"
48 test "suppresses notification to reporter if reporter is an admin" do
49 reporting_admin = insert(:user, is_admin: true)
50 reported_user = insert(:user)
51 other_admin = insert(:user, is_admin: true)
53 {:ok, activity} = CommonAPI.report(reporting_admin, %{account_id: reported_user.id})
55 {:ok, [notification]} = Notification.create_notifications(activity)
57 refute notification.user_id == reporting_admin.id
58 assert notification.user_id == other_admin.id
59 assert notification.type == "pleroma:report"
62 test "creates a notification for an emoji reaction" do
64 other_user = insert(:user)
66 {:ok, activity} = CommonAPI.post(user, %{status: "yeah"})
67 {:ok, activity} = CommonAPI.react_with_emoji(activity.id, other_user, "☕")
69 {:ok, [notification]} = Notification.create_notifications(activity)
71 assert notification.user_id == user.id
72 assert notification.type == "pleroma:emoji_reaction"
75 test "notifies someone when they are directly addressed" do
77 other_user = insert(:user)
78 third_user = insert(:user)
81 CommonAPI.post(user, %{
82 status: "hey @#{other_user.nickname} and @#{third_user.nickname}"
85 {:ok, [notification, other_notification]} = Notification.create_notifications(activity)
87 notified_ids = Enum.sort([notification.user_id, other_notification.user_id])
88 assert notified_ids == [other_user.id, third_user.id]
89 assert notification.activity_id == activity.id
90 assert notification.type == "mention"
91 assert other_notification.activity_id == activity.id
93 assert [%Pleroma.Marker{unread_count: 2}] =
94 Pleroma.Marker.get_markers(other_user, ["notifications"])
97 test "it creates a notification for subscribed users" do
99 subscriber = insert(:user)
101 User.subscribe(subscriber, user)
103 {:ok, status} = CommonAPI.post(user, %{status: "Akariiiin"})
104 {:ok, [notification]} = Notification.create_notifications(status)
106 assert notification.user_id == subscriber.id
109 test "does not create a notification for subscribed users if status is a reply" do
111 other_user = insert(:user)
112 subscriber = insert(:user)
114 User.subscribe(subscriber, other_user)
116 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
118 {:ok, _reply_activity} =
119 CommonAPI.post(other_user, %{
120 status: "test reply",
121 in_reply_to_status_id: activity.id
124 user_notifications = Notification.for_user(user)
125 assert length(user_notifications) == 1
127 subscriber_notifications = Notification.for_user(subscriber)
128 assert Enum.empty?(subscriber_notifications)
131 test "it sends edited notifications to those who repeated a status" do
133 repeated_user = insert(:user)
134 other_user = insert(:user)
136 {:ok, activity_one} =
137 CommonAPI.post(user, %{
138 status: "hey @#{other_user.nickname}!"
141 {:ok, _activity_two} = CommonAPI.repeat(activity_one.id, repeated_user)
143 {:ok, _edit_activity} =
144 CommonAPI.update(user, activity_one, %{
145 status: "hey @#{other_user.nickname}! mew mew"
148 assert [%{type: "reblog"}] = Notification.for_user(user)
149 assert [%{type: "update"}] = Notification.for_user(repeated_user)
150 assert [%{type: "mention"}] = Notification.for_user(other_user)
154 test "create_poll_notifications/1" do
155 [user1, user2, user3, _, _] = insert_list(5, :user)
156 question = insert(:question, user: user1)
157 activity = insert(:question_activity, question: question)
159 {:ok, _, _} = CommonAPI.vote(user2, question, [0])
160 {:ok, _, _} = CommonAPI.vote(user3, question, [1])
162 {:ok, notifications} = Notification.create_poll_notifications(activity)
164 assert [user2.id, user3.id, user1.id] == Enum.map(notifications, & &1.user_id)
167 describe "CommonApi.post/2 notification-related functionality" do
168 test_with_mock "creates but does NOT send notification to blocker user",
173 blocker = insert(:user)
174 {:ok, _user_relationship} = User.block(blocker, user)
176 {:ok, _activity} = CommonAPI.post(user, %{status: "hey @#{blocker.nickname}!"})
178 blocker_id = blocker.id
179 assert [%Notification{user_id: ^blocker_id}] = Repo.all(Notification)
180 refute called(Push.send(:_))
183 test_with_mock "creates but does NOT send notification to notification-muter user",
188 muter = insert(:user)
189 {:ok, _user_relationships} = User.mute(muter, user)
191 {:ok, _activity} = CommonAPI.post(user, %{status: "hey @#{muter.nickname}!"})
194 assert [%Notification{user_id: ^muter_id}] = Repo.all(Notification)
195 refute called(Push.send(:_))
198 test_with_mock "creates but does NOT send notification to thread-muter user",
203 thread_muter = insert(:user)
205 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{thread_muter.nickname}!"})
207 {:ok, _} = CommonAPI.add_mute(thread_muter, activity)
209 {:ok, _same_context_activity} =
210 CommonAPI.post(user, %{
211 status: "hey-hey-hey @#{thread_muter.nickname}!",
212 in_reply_to_status_id: activity.id
215 [pre_mute_notification, post_mute_notification] =
216 Repo.all(from(n in Notification, where: n.user_id == ^thread_muter.id, order_by: n.id))
218 pre_mute_notification_id = pre_mute_notification.id
219 post_mute_notification_id = post_mute_notification.id
224 %Notification{id: ^pre_mute_notification_id} -> true
233 %Notification{id: ^post_mute_notification_id} -> true
241 describe "create_notification" do
242 @tag needs_streamer: true
243 test "it creates a notification for user and send to the 'user' and the 'user:notification' stream" do
244 %{user: user, token: oauth_token} = oauth_access(["read"])
248 {:ok, _topic} = Streamer.get_topic_and_add_socket("user", user, oauth_token)
249 assert_receive {:render_with_user, _, _, _, "user"}, 4_000
252 task_user_notification =
255 Streamer.get_topic_and_add_socket("user:notification", user, oauth_token)
257 assert_receive {:render_with_user, _, _, _, "user:notification"}, 4_000
260 activity = insert(:note_activity)
262 notify = Notification.create_notification(activity, user)
263 assert notify.user_id == user.id
265 Task.await(task_user_notification)
268 test "it creates a notification for user if the user blocks the activity author" do
269 activity = insert(:note_activity)
270 author = User.get_cached_by_ap_id(activity.data["actor"])
272 {:ok, _user_relationship} = User.block(user, author)
274 assert Notification.create_notification(activity, user)
277 test "it creates a notification for the user if the user mutes the activity author" do
278 muter = insert(:user)
279 muted = insert(:user)
280 {:ok, _} = User.mute(muter, muted)
281 muter = Repo.get(User, muter.id)
282 {:ok, activity} = CommonAPI.post(muted, %{status: "Hi @#{muter.nickname}"})
284 notification = Notification.create_notification(activity, muter)
286 assert notification.id
287 assert notification.seen
290 test "notification created if user is muted without notifications" do
291 muter = insert(:user)
292 muted = insert(:user)
294 {:ok, _user_relationships} = User.mute(muter, muted, %{notifications: false})
296 {:ok, activity} = CommonAPI.post(muted, %{status: "Hi @#{muter.nickname}"})
298 assert Notification.create_notification(activity, muter)
301 test "it creates a notification for an activity from a muted thread" do
302 muter = insert(:user)
303 other_user = insert(:user)
304 {:ok, activity} = CommonAPI.post(muter, %{status: "hey"})
305 CommonAPI.add_mute(muter, activity)
308 CommonAPI.post(other_user, %{
309 status: "Hi @#{muter.nickname}",
310 in_reply_to_status_id: activity.id
313 notification = Notification.create_notification(activity, muter)
315 assert notification.id
316 assert notification.seen
319 test "it disables notifications from strangers" do
320 follower = insert(:user)
324 notification_settings: %Pleroma.User.NotificationSetting{block_from_strangers: true}
327 {:ok, activity} = CommonAPI.post(follower, %{status: "hey @#{followed.nickname}"})
328 refute Notification.create_notification(activity, followed)
331 test "it doesn't create a notification for user if he is the activity author" do
332 activity = insert(:note_activity)
333 author = User.get_cached_by_ap_id(activity.data["actor"])
335 refute Notification.create_notification(activity, author)
338 test "it doesn't create duplicate notifications for follow+subscribed users" do
340 subscriber = insert(:user)
342 {:ok, _, _, _} = CommonAPI.follow(subscriber, user)
343 User.subscribe(subscriber, user)
344 {:ok, status} = CommonAPI.post(user, %{status: "Akariiiin"})
345 {:ok, [_notif]} = Notification.create_notifications(status)
348 test "it doesn't create subscription notifications if the recipient cannot see the status" do
350 subscriber = insert(:user)
352 User.subscribe(subscriber, user)
354 {:ok, status} = CommonAPI.post(user, %{status: "inwisible", visibility: "direct"})
356 assert {:ok, []} == Notification.create_notifications(status)
359 test "it disables notifications from people who are invisible" do
360 author = insert(:user, invisible: true)
363 {:ok, status} = CommonAPI.post(author, %{status: "hey @#{user.nickname}"})
364 refute Notification.create_notification(status, user)
367 test "it doesn't create notifications if content matches with an irreversible filter" do
369 subscriber = insert(:user)
371 User.subscribe(subscriber, user)
372 insert(:filter, user: subscriber, phrase: "cofe", hide: true)
374 {:ok, status} = CommonAPI.post(user, %{status: "got cofe?"})
376 assert {:ok, []} == Notification.create_notifications(status)
379 test "it creates notifications if content matches with a not irreversible filter" do
381 subscriber = insert(:user)
383 User.subscribe(subscriber, user)
384 insert(:filter, user: subscriber, phrase: "cofe", hide: false)
386 {:ok, status} = CommonAPI.post(user, %{status: "got cofe?"})
387 {:ok, [notification]} = Notification.create_notifications(status)
390 refute notification.seen
393 test "it creates notifications when someone likes user's status with a filtered word" do
395 other_user = insert(:user)
396 insert(:filter, user: user, phrase: "tesla", hide: true)
398 {:ok, activity_one} = CommonAPI.post(user, %{status: "wow tesla"})
399 {:ok, activity_two} = CommonAPI.favorite(other_user, activity_one.id)
401 {:ok, [notification]} = Notification.create_notifications(activity_two)
404 refute notification.seen
408 describe "follow / follow_request notifications" do
409 test "it creates `follow` notification for approved Follow activity" do
411 followed_user = insert(:user, is_locked: false)
413 {:ok, _, _, _activity} = CommonAPI.follow(user, followed_user)
414 assert FollowingRelationship.following?(user, followed_user)
415 assert [notification] = Notification.for_user(followed_user)
417 assert %{type: "follow"} =
418 NotificationView.render("show.json", %{
419 notification: notification,
424 test "it creates `follow_request` notification for pending Follow activity" do
426 followed_user = insert(:user, is_locked: true)
428 {:ok, _, _, _activity} = CommonAPI.follow(user, followed_user)
429 refute FollowingRelationship.following?(user, followed_user)
430 assert [notification] = Notification.for_user(followed_user)
432 render_opts = %{notification: notification, for: followed_user}
433 assert %{type: "follow_request"} = NotificationView.render("show.json", render_opts)
435 # After request is accepted, the same notification is rendered with type "follow":
436 assert {:ok, _} = CommonAPI.accept_follow_request(user, followed_user)
439 Repo.get(Notification, notification.id)
440 |> Repo.preload(:activity)
442 assert %{type: "follow"} =
443 NotificationView.render("show.json", notification: notification, for: followed_user)
446 test "it doesn't create a notification for follow-unfollow-follow chains" do
448 followed_user = insert(:user, is_locked: false)
450 {:ok, _, _, _activity} = CommonAPI.follow(user, followed_user)
451 assert FollowingRelationship.following?(user, followed_user)
452 assert [_notification] = Notification.for_user(followed_user)
454 CommonAPI.unfollow(user, followed_user)
455 {:ok, _, _, _activity_dupe} = CommonAPI.follow(user, followed_user)
457 assert Enum.count(Notification.for_user(followed_user)) == 1
460 test "dismisses the notification on follow request rejection" do
461 user = insert(:user, is_locked: true)
462 follower = insert(:user)
463 {:ok, _, _, _follow_activity} = CommonAPI.follow(follower, user)
464 assert [_notification] = Notification.for_user(user)
465 {:ok, _follower} = CommonAPI.reject_follow_request(follower, user)
466 assert [] = Notification.for_user(user)
470 describe "get notification" do
471 test "it gets a notification that belongs to the user" do
473 other_user = insert(:user)
475 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}"})
477 {:ok, [notification]} = Notification.create_notifications(activity)
478 {:ok, notification} = Notification.get(other_user, notification.id)
480 assert notification.user_id == other_user.id
483 test "it returns error if the notification doesn't belong to the user" do
485 other_user = insert(:user)
487 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}"})
489 {:ok, [notification]} = Notification.create_notifications(activity)
490 {:error, _notification} = Notification.get(user, notification.id)
494 describe "dismiss notification" do
495 test "it dismisses a notification that belongs to the user" do
497 other_user = insert(:user)
499 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}"})
501 {:ok, [notification]} = Notification.create_notifications(activity)
502 {:ok, notification} = Notification.dismiss(other_user, notification.id)
504 assert notification.user_id == other_user.id
507 test "it returns error if the notification doesn't belong to the user" do
509 other_user = insert(:user)
511 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}"})
513 {:ok, [notification]} = Notification.create_notifications(activity)
514 {:error, _notification} = Notification.dismiss(user, notification.id)
518 describe "clear notification" do
519 test "it clears all notifications belonging to the user" do
521 other_user = insert(:user)
522 third_user = insert(:user)
525 CommonAPI.post(user, %{
526 status: "hey @#{other_user.nickname} and @#{third_user.nickname} !"
529 {:ok, _notifs} = Notification.create_notifications(activity)
532 CommonAPI.post(user, %{
533 status: "hey again @#{other_user.nickname} and @#{third_user.nickname} !"
536 {:ok, _notifs} = Notification.create_notifications(activity)
537 Notification.clear(other_user)
539 assert Notification.for_user(other_user) == []
540 assert Notification.for_user(third_user) != []
544 describe "destroy_multiple_from_types/2" do
545 test "clears all notifications of a certain type for a given user" do
546 report_activity = insert(:report_activity)
547 user1 = insert(:user, is_moderator: true, is_admin: true)
548 user2 = insert(:user, is_moderator: true, is_admin: true)
549 {:ok, _} = Notification.create_notifications(report_activity)
552 CommonAPI.post(user2, %{
553 status: "hey @#{user1.nickname} !"
556 Notification.destroy_multiple_from_types(user1, ["pleroma:report"])
558 assert [%Pleroma.Notification{type: "mention"}] = Notification.for_user(user1)
559 assert [%Pleroma.Notification{type: "pleroma:report"}] = Notification.for_user(user2)
563 describe "set_read_up_to()" do
564 test "it sets all notifications as read up to a specified notification ID" do
566 other_user = insert(:user)
569 CommonAPI.post(user, %{
570 status: "hey @#{other_user.nickname}!"
574 CommonAPI.post(user, %{
575 status: "hey again @#{other_user.nickname}!"
578 [n2, n1] = Notification.for_user(other_user)
583 CommonAPI.post(user, %{
584 status: "hey yet again @#{other_user.nickname}!"
587 [_, read_notification] = Notification.set_read_up_to(other_user, n2.id)
589 assert read_notification.activity.object
591 [n3, n2, n1] = Notification.for_user(other_user)
593 assert n1.seen == true
594 assert n2.seen == true
595 assert n3.seen == false
597 assert %Pleroma.Marker{} =
601 user_id: other_user.id,
602 timeline: "notifications"
605 assert m.last_read_id == to_string(n2.id)
609 describe "for_user_since/2" do
610 defp days_ago(days) do
612 NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second),
613 -days * 60 * 60 * 24,
618 test "Returns recent notifications" do
619 user1 = insert(:user)
620 user2 = insert(:user)
622 Enum.each(0..10, fn i ->
624 CommonAPI.post(user1, %{
625 status: "hey ##{i} @#{user2.nickname}!"
629 {old, new} = Enum.split(Notification.for_user(user2), 5)
631 Enum.each(old, fn notification ->
633 |> cast(%{updated_at: days_ago(10)}, [:updated_at])
634 |> Pleroma.Repo.update!()
637 recent_notifications_ids =
639 |> Notification.for_user_since(
640 NaiveDateTime.add(NaiveDateTime.utc_now(), -5 * 86_400, :second)
644 Enum.each(old, fn %{id: id} ->
645 refute id in recent_notifications_ids
648 Enum.each(new, fn %{id: id} ->
649 assert id in recent_notifications_ids
654 describe "notification target determination / get_notified_from_activity/2" do
655 test "it sends notifications to addressed users in new messages" do
657 other_user = insert(:user)
660 CommonAPI.post(user, %{
661 status: "hey @#{other_user.nickname}!"
664 {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity)
666 assert other_user in enabled_receivers
669 test "it sends notifications to mentioned users in new messages" do
671 other_user = insert(:user)
674 "@context" => "https://www.w3.org/ns/activitystreams",
676 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
677 "actor" => user.ap_id,
680 "id" => Pleroma.Web.ActivityPub.Utils.generate_object_id(),
681 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
682 "content" => "message with a Mention tag, but no explicit tagging",
686 "href" => other_user.ap_id,
687 "name" => other_user.nickname
690 "attributedTo" => user.ap_id
694 {:ok, activity} = Transmogrifier.handle_incoming(create_activity)
696 {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity)
698 assert other_user in enabled_receivers
701 test "it does not send notifications to users who are only cc in new messages" do
703 other_user = insert(:user)
706 "@context" => "https://www.w3.org/ns/activitystreams",
708 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
709 "cc" => [other_user.ap_id],
710 "actor" => user.ap_id,
713 "id" => Pleroma.Web.ActivityPub.Utils.generate_object_id(),
714 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
715 "cc" => [other_user.ap_id],
716 "content" => "hi everyone",
717 "attributedTo" => user.ap_id
721 {:ok, activity} = Transmogrifier.handle_incoming(create_activity)
723 {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity)
725 assert other_user not in enabled_receivers
728 test "it does not send notification to mentioned users in likes" do
730 other_user = insert(:user)
731 third_user = insert(:user)
733 {:ok, activity_one} =
734 CommonAPI.post(user, %{
735 status: "hey @#{other_user.nickname}!"
738 {:ok, activity_two} = CommonAPI.favorite(third_user, activity_one.id)
740 {enabled_receivers, _disabled_receivers} =
741 Notification.get_notified_from_activity(activity_two)
743 assert other_user not in enabled_receivers
746 test "it only notifies the post's author in likes" do
748 other_user = insert(:user)
749 third_user = insert(:user)
751 {:ok, activity_one} =
752 CommonAPI.post(user, %{
753 status: "hey @#{other_user.nickname}!"
756 {:ok, like_data, _} = Builder.like(third_user, activity_one.object)
760 |> Map.put("to", [other_user.ap_id | like_data["to"]])
761 |> ActivityPub.persist(local: true)
763 {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(like)
765 assert other_user not in enabled_receivers
768 test "it does not send notification to mentioned users in announces" do
770 other_user = insert(:user)
771 third_user = insert(:user)
773 {:ok, activity_one} =
774 CommonAPI.post(user, %{
775 status: "hey @#{other_user.nickname}!"
778 {:ok, activity_two} = CommonAPI.repeat(activity_one.id, third_user)
780 {enabled_receivers, _disabled_receivers} =
781 Notification.get_notified_from_activity(activity_two)
783 assert other_user not in enabled_receivers
786 test "it returns blocking recipient in disabled recipients list" do
788 other_user = insert(:user)
789 {:ok, _user_relationship} = User.block(other_user, user)
791 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
793 {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
795 assert [] == enabled_receivers
796 assert [other_user] == disabled_receivers
799 test "it returns notification-muting recipient in disabled recipients list" do
801 other_user = insert(:user)
802 {:ok, _user_relationships} = User.mute(other_user, user)
804 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
806 {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
808 assert [] == enabled_receivers
809 assert [other_user] == disabled_receivers
812 test "it returns thread-muting recipient in disabled recipients list" do
814 other_user = insert(:user)
816 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
818 {:ok, _} = CommonAPI.add_mute(other_user, activity)
820 {:ok, same_context_activity} =
821 CommonAPI.post(user, %{
822 status: "hey-hey-hey @#{other_user.nickname}!",
823 in_reply_to_status_id: activity.id
826 {enabled_receivers, disabled_receivers} =
827 Notification.get_notified_from_activity(same_context_activity)
829 assert [other_user] == disabled_receivers
830 refute other_user in enabled_receivers
833 test "it returns non-following domain-blocking recipient in disabled recipients list" do
834 blocked_domain = "blocked.domain"
835 user = insert(:user, %{ap_id: "https://#{blocked_domain}/@actor"})
836 other_user = insert(:user)
838 {:ok, other_user} = User.block_domain(other_user, blocked_domain)
840 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
842 {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
844 assert [] == enabled_receivers
845 assert [other_user] == disabled_receivers
848 test "it returns following domain-blocking recipient in enabled recipients list" do
849 blocked_domain = "blocked.domain"
850 user = insert(:user, %{ap_id: "https://#{blocked_domain}/@actor"})
851 other_user = insert(:user)
853 {:ok, other_user} = User.block_domain(other_user, blocked_domain)
854 {:ok, other_user, user} = User.follow(other_user, user)
856 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
858 {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
860 assert [other_user] == enabled_receivers
861 assert [] == disabled_receivers
864 test "it sends edited notifications to those who repeated a status" do
866 repeated_user = insert(:user)
867 other_user = insert(:user)
869 {:ok, activity_one} =
870 CommonAPI.post(user, %{
871 status: "hey @#{other_user.nickname}!"
874 {:ok, _activity_two} = CommonAPI.repeat(activity_one.id, repeated_user)
876 {:ok, edit_activity} =
877 CommonAPI.update(user, activity_one, %{
878 status: "hey @#{other_user.nickname}! mew mew"
881 {enabled_receivers, _disabled_receivers} =
882 Notification.get_notified_from_activity(edit_activity)
884 assert repeated_user in enabled_receivers
885 assert other_user not in enabled_receivers
889 describe "notification lifecycle" do
890 test "liking an activity results in 1 notification, then 0 if the activity is deleted" do
892 other_user = insert(:user)
894 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
896 assert Enum.empty?(Notification.for_user(user))
898 {:ok, _} = CommonAPI.favorite(other_user, activity.id)
900 assert length(Notification.for_user(user)) == 1
902 {:ok, _} = CommonAPI.delete(activity.id, user)
904 assert Enum.empty?(Notification.for_user(user))
907 test "liking an activity results in 1 notification, then 0 if the activity is unliked" do
909 other_user = insert(:user)
911 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
913 assert Enum.empty?(Notification.for_user(user))
915 {:ok, _} = CommonAPI.favorite(other_user, activity.id)
917 assert length(Notification.for_user(user)) == 1
919 {:ok, _} = CommonAPI.unfavorite(activity.id, other_user)
921 assert Enum.empty?(Notification.for_user(user))
924 test "repeating an activity results in 1 notification, then 0 if the activity is deleted" do
926 other_user = insert(:user)
928 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
930 assert Enum.empty?(Notification.for_user(user))
932 {:ok, _} = CommonAPI.repeat(activity.id, other_user)
934 assert length(Notification.for_user(user)) == 1
936 {:ok, _} = CommonAPI.delete(activity.id, user)
938 assert Enum.empty?(Notification.for_user(user))
941 test "repeating an activity results in 1 notification, then 0 if the activity is unrepeated" do
943 other_user = insert(:user)
945 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
947 assert Enum.empty?(Notification.for_user(user))
949 {:ok, _} = CommonAPI.repeat(activity.id, other_user)
951 assert length(Notification.for_user(user)) == 1
953 {:ok, _} = CommonAPI.unrepeat(activity.id, other_user)
955 assert Enum.empty?(Notification.for_user(user))
958 test "liking an activity which is already deleted does not generate a notification" do
960 other_user = insert(:user)
962 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
964 assert Enum.empty?(Notification.for_user(user))
966 {:ok, _deletion_activity} = CommonAPI.delete(activity.id, user)
968 assert Enum.empty?(Notification.for_user(user))
970 {:error, :not_found} = CommonAPI.favorite(other_user, activity.id)
972 assert Enum.empty?(Notification.for_user(user))
975 test "repeating an activity which is already deleted does not generate a notification" do
977 other_user = insert(:user)
979 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
981 assert Enum.empty?(Notification.for_user(user))
983 {:ok, _deletion_activity} = CommonAPI.delete(activity.id, user)
985 assert Enum.empty?(Notification.for_user(user))
987 {:error, _} = CommonAPI.repeat(activity.id, other_user)
989 assert Enum.empty?(Notification.for_user(user))
992 test "replying to a deleted post without tagging does not generate a notification" do
994 other_user = insert(:user)
996 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
997 {:ok, _deletion_activity} = CommonAPI.delete(activity.id, user)
999 {:ok, _reply_activity} =
1000 CommonAPI.post(other_user, %{
1001 status: "test reply",
1002 in_reply_to_status_id: activity.id
1005 assert Enum.empty?(Notification.for_user(user))
1008 test "notifications are deleted if a local user is deleted" do
1009 user = insert(:user)
1010 other_user = insert(:user)
1013 CommonAPI.post(user, %{status: "hi @#{other_user.nickname}", visibility: "direct"})
1015 refute Enum.empty?(Notification.for_user(other_user))
1017 {:ok, job} = User.delete(user)
1018 ObanHelpers.perform(job)
1020 assert Enum.empty?(Notification.for_user(other_user))
1023 test "notifications are deleted if a remote user is deleted" do
1024 remote_user = insert(:user)
1025 local_user = insert(:user)
1028 "@context" => "https://www.w3.org/ns/activitystreams",
1030 "actor" => remote_user.ap_id,
1031 "id" => remote_user.ap_id <> "/activities/test",
1032 "to" => [local_user.ap_id],
1036 "id" => remote_user.ap_id <> "/objects/test",
1037 "content" => "Hello!",
1040 "type" => "Mention",
1041 "href" => local_user.ap_id,
1042 "name" => "@#{local_user.nickname}"
1045 "to" => [local_user.ap_id],
1047 "attributedTo" => remote_user.ap_id
1051 {:ok, _dm_activity} = Transmogrifier.handle_incoming(dm_message)
1053 refute Enum.empty?(Notification.for_user(local_user))
1055 delete_user_message = %{
1056 "@context" => "https://www.w3.org/ns/activitystreams",
1057 "id" => remote_user.ap_id <> "/activities/delete",
1058 "actor" => remote_user.ap_id,
1060 "object" => remote_user.ap_id
1063 remote_user_url = remote_user.ap_id
1066 %{method: :get, url: ^remote_user_url} ->
1067 %Tesla.Env{status: 404, body: ""}
1070 {:ok, _delete_activity} = Transmogrifier.handle_incoming(delete_user_message)
1071 ObanHelpers.perform_all()
1073 assert Enum.empty?(Notification.for_user(local_user))
1076 test "move activity generates a notification" do
1077 %{ap_id: old_ap_id} = old_user = insert(:user)
1078 %{ap_id: new_ap_id} = new_user = insert(:user, also_known_as: [old_ap_id])
1079 follower = insert(:user)
1080 other_follower = insert(:user, %{allow_following_move: false})
1082 User.follow(follower, old_user)
1083 User.follow(other_follower, old_user)
1085 Pleroma.Web.ActivityPub.ActivityPub.move(old_user, new_user)
1086 ObanHelpers.perform_all()
1091 data: %{"type" => "Move", "actor" => ^old_ap_id, "target" => ^new_ap_id}
1094 ] = Notification.for_user(follower)
1099 data: %{"type" => "Move", "actor" => ^old_ap_id, "target" => ^new_ap_id}
1102 ] = Notification.for_user(other_follower)
1106 describe "for_user" do
1108 user = insert(:user)
1110 {:ok, %{user: user}}
1113 test "it returns notifications for muted user without notifications", %{user: user} do
1114 muted = insert(:user)
1115 {:ok, _user_relationships} = User.mute(user, muted, %{notifications: false})
1117 {:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"})
1119 [notification] = Notification.for_user(user)
1121 assert notification.activity.object
1122 assert notification.seen
1125 test "it doesn't return notifications for muted user with notifications", %{user: user} do
1126 muted = insert(:user)
1127 {:ok, _user_relationships} = User.mute(user, muted)
1129 {:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"})
1131 assert Notification.for_user(user) == []
1134 test "it doesn't return notifications for blocked user", %{user: user} do
1135 blocked = insert(:user)
1136 {:ok, _user_relationship} = User.block(user, blocked)
1138 {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
1140 assert Notification.for_user(user) == []
1143 test "it doesn't return notifications for domain-blocked non-followed user", %{user: user} do
1144 blocked = insert(:user, ap_id: "http://some-domain.com")
1145 {:ok, user} = User.block_domain(user, "some-domain.com")
1147 {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
1149 assert Notification.for_user(user) == []
1152 test "it doesn't return notifications for muted thread", %{user: user} do
1153 another_user = insert(:user)
1155 {:ok, activity} = CommonAPI.post(another_user, %{status: "hey @#{user.nickname}"})
1157 {:ok, _} = Pleroma.ThreadMute.add_mute(user.id, activity.data["context"])
1158 assert Notification.for_user(user) == []
1161 test "it returns notifications from a muted user when with_muted is set", %{user: user} do
1162 muted = insert(:user)
1163 {:ok, _user_relationships} = User.mute(user, muted)
1165 {:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"})
1167 assert length(Notification.for_user(user, %{with_muted: true})) == 1
1170 test "it doesn't return notifications from a blocked user when with_muted is set", %{
1173 blocked = insert(:user)
1174 {:ok, _user_relationship} = User.block(user, blocked)
1176 {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
1178 assert Enum.empty?(Notification.for_user(user, %{with_muted: true}))
1181 test "when with_muted is set, " <>
1182 "it doesn't return notifications from a domain-blocked non-followed user",
1184 blocked = insert(:user, ap_id: "http://some-domain.com")
1185 {:ok, user} = User.block_domain(user, "some-domain.com")
1187 {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
1189 assert Enum.empty?(Notification.for_user(user, %{with_muted: true}))
1192 test "it returns notifications from muted threads when with_muted is set", %{user: user} do
1193 another_user = insert(:user)
1195 {:ok, activity} = CommonAPI.post(another_user, %{status: "hey @#{user.nickname}"})
1197 {:ok, _} = Pleroma.ThreadMute.add_mute(user.id, activity.data["context"])
1198 assert length(Notification.for_user(user, %{with_muted: true})) == 1
1201 test "it doesn't return notifications about mentions with filtered word", %{user: user} do
1202 insert(:filter, user: user, phrase: "cofe", hide: true)
1203 another_user = insert(:user)
1205 {:ok, _activity} = CommonAPI.post(another_user, %{status: "@#{user.nickname} got cofe?"})
1207 assert Enum.empty?(Notification.for_user(user))
1210 test "it returns notifications about mentions with not hidden filtered word", %{user: user} do
1211 insert(:filter, user: user, phrase: "test", hide: false)
1212 another_user = insert(:user)
1214 {:ok, _} = CommonAPI.post(another_user, %{status: "@#{user.nickname} test"})
1216 assert length(Notification.for_user(user)) == 1
1219 test "it returns notifications about favorites with filtered word", %{user: user} do
1220 insert(:filter, user: user, phrase: "cofe", hide: true)
1221 another_user = insert(:user)
1223 {:ok, activity} = CommonAPI.post(user, %{status: "Give me my cofe!"})
1224 {:ok, _} = CommonAPI.favorite(another_user, activity.id)
1226 assert length(Notification.for_user(user)) == 1