1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.NotificationTest do
6 use Pleroma.DataCase, async: false
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
25 Mox.stub_with(Pleroma.UnstubbedConfigMock, Pleroma.Config)
29 describe "create_notifications" do
30 test "never returns nil" do
32 other_user = insert(:user, %{invisible: true})
34 {:ok, activity} = CommonAPI.post(user, %{status: "yeah"})
35 {:ok, activity} = CommonAPI.react_with_emoji(activity.id, other_user, "☕")
37 refute {:ok, [nil]} == Notification.create_notifications(activity)
40 test "creates a report notification only for privileged users" do
41 reporting_user = insert(:user)
42 reported_user = insert(:user)
43 moderator_user = insert(:user, is_moderator: true)
45 clear_config([:instance, :moderator_privileges], [])
46 {:ok, activity1} = CommonAPI.report(reporting_user, %{account_id: reported_user.id})
47 {:ok, []} = Notification.create_notifications(activity1)
49 clear_config([:instance, :moderator_privileges], [:reports_manage_reports])
50 {:ok, activity2} = CommonAPI.report(reporting_user, %{account_id: reported_user.id})
51 {:ok, [notification]} = Notification.create_notifications(activity2)
53 assert notification.user_id == moderator_user.id
54 assert notification.type == "pleroma:report"
57 test "suppresses notifications for own reports" do
58 clear_config([:instance, :admin_privileges], [:reports_manage_reports])
60 reporting_admin = insert(:user, is_admin: true)
61 reported_user = insert(:user)
62 other_admin = insert(:user, is_admin: true)
64 {:ok, activity} = CommonAPI.report(reporting_admin, %{account_id: reported_user.id})
66 {:ok, [notification]} = Notification.create_notifications(activity)
68 refute notification.user_id == reporting_admin.id
69 assert notification.user_id == other_admin.id
70 assert notification.type == "pleroma:report"
73 test "creates a notification for an emoji reaction" do
75 other_user = insert(:user)
77 {:ok, activity} = CommonAPI.post(user, %{status: "yeah"})
78 {:ok, activity} = CommonAPI.react_with_emoji(activity.id, other_user, "☕")
80 {:ok, [notification]} = Notification.create_notifications(activity)
82 assert notification.user_id == user.id
83 assert notification.type == "pleroma:emoji_reaction"
86 test "notifies someone when they are directly addressed" do
88 other_user = insert(:user)
89 third_user = insert(:user)
92 CommonAPI.post(user, %{
93 status: "hey @#{other_user.nickname} and @#{third_user.nickname}"
96 {:ok, [notification, other_notification]} = Notification.create_notifications(activity)
98 notified_ids = Enum.sort([notification.user_id, other_notification.user_id])
99 assert notified_ids == [other_user.id, third_user.id]
100 assert notification.activity_id == activity.id
101 assert notification.type == "mention"
102 assert other_notification.activity_id == activity.id
104 assert [%Pleroma.Marker{unread_count: 2}] =
105 Pleroma.Marker.get_markers(other_user, ["notifications"])
108 test "it creates a notification for subscribed users" do
110 subscriber = insert(:user)
112 User.subscribe(subscriber, user)
114 {:ok, status} = CommonAPI.post(user, %{status: "Akariiiin"})
115 {:ok, [notification]} = Notification.create_notifications(status)
117 assert notification.user_id == subscriber.id
120 test "does not create a notification for subscribed users if status is a reply" do
122 other_user = insert(:user)
123 subscriber = insert(:user)
125 User.subscribe(subscriber, other_user)
127 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
129 {:ok, _reply_activity} =
130 CommonAPI.post(other_user, %{
131 status: "test reply",
132 in_reply_to_status_id: activity.id
135 user_notifications = Notification.for_user(user)
136 assert length(user_notifications) == 1
138 subscriber_notifications = Notification.for_user(subscriber)
139 assert Enum.empty?(subscriber_notifications)
142 test "it sends edited notifications to those who repeated a status" do
144 repeated_user = insert(:user)
145 other_user = insert(:user)
147 {:ok, activity_one} =
148 CommonAPI.post(user, %{
149 status: "hey @#{other_user.nickname}!"
152 {:ok, _activity_two} = CommonAPI.repeat(activity_one.id, repeated_user)
154 {:ok, _edit_activity} =
155 CommonAPI.update(user, activity_one, %{
156 status: "hey @#{other_user.nickname}! mew mew"
159 assert [%{type: "reblog"}] = Notification.for_user(user)
160 assert [%{type: "update"}] = Notification.for_user(repeated_user)
161 assert [%{type: "mention"}] = Notification.for_user(other_user)
165 test "create_poll_notifications/1" do
166 [user1, user2, user3, _, _] = insert_list(5, :user)
167 question = insert(:question, user: user1)
168 activity = insert(:question_activity, question: question)
170 {:ok, _, _} = CommonAPI.vote(user2, question, [0])
171 {:ok, _, _} = CommonAPI.vote(user3, question, [1])
173 {:ok, notifications} = Notification.create_poll_notifications(activity)
175 assert [user2.id, user3.id, user1.id] == Enum.map(notifications, & &1.user_id)
178 describe "CommonApi.post/2 notification-related functionality" do
179 test_with_mock "creates but does NOT send notification to blocker user",
184 blocker = insert(:user)
185 {:ok, _user_relationship} = User.block(blocker, user)
187 {:ok, _activity} = CommonAPI.post(user, %{status: "hey @#{blocker.nickname}!"})
189 blocker_id = blocker.id
190 assert [%Notification{user_id: ^blocker_id}] = Repo.all(Notification)
191 refute called(Push.send(:_))
194 test_with_mock "creates but does NOT send notification to notification-muter user",
199 muter = insert(:user)
200 {:ok, _user_relationships} = User.mute(muter, user)
202 {:ok, _activity} = CommonAPI.post(user, %{status: "hey @#{muter.nickname}!"})
205 assert [%Notification{user_id: ^muter_id}] = Repo.all(Notification)
206 refute called(Push.send(:_))
209 test_with_mock "creates but does NOT send notification to thread-muter user",
214 thread_muter = insert(:user)
216 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{thread_muter.nickname}!"})
218 {:ok, _} = CommonAPI.add_mute(thread_muter, activity)
220 {:ok, _same_context_activity} =
221 CommonAPI.post(user, %{
222 status: "hey-hey-hey @#{thread_muter.nickname}!",
223 in_reply_to_status_id: activity.id
226 [pre_mute_notification, post_mute_notification] =
227 Repo.all(from(n in Notification, where: n.user_id == ^thread_muter.id, order_by: n.id))
229 pre_mute_notification_id = pre_mute_notification.id
230 post_mute_notification_id = post_mute_notification.id
235 %Notification{id: ^pre_mute_notification_id} -> true
244 %Notification{id: ^post_mute_notification_id} -> true
252 describe "create_notification" do
253 @tag needs_streamer: true
254 test "it creates a notification for user and send to the 'user' and the 'user:notification' stream" do
255 %{user: user, token: oauth_token} = oauth_access(["read"])
259 {:ok, _topic} = Streamer.get_topic_and_add_socket("user", user, oauth_token)
260 assert_receive {:render_with_user, _, _, _, _}, 4_000
263 task_user_notification =
266 Streamer.get_topic_and_add_socket("user:notification", user, oauth_token)
268 assert_receive {:render_with_user, _, _, _, _}, 4_000
271 activity = insert(:note_activity)
273 notify = Notification.create_notification(activity, user)
274 assert notify.user_id == user.id
276 Task.await(task_user_notification)
279 test "it creates a notification for user if the user blocks the activity author" do
280 activity = insert(:note_activity)
281 author = User.get_cached_by_ap_id(activity.data["actor"])
283 {:ok, _user_relationship} = User.block(user, author)
285 assert Notification.create_notification(activity, user)
288 test "it creates a notification for the user if the user mutes the activity author" do
289 muter = insert(:user)
290 muted = insert(:user)
291 {:ok, _} = User.mute(muter, muted)
292 muter = Repo.get(User, muter.id)
293 {:ok, activity} = CommonAPI.post(muted, %{status: "Hi @#{muter.nickname}"})
295 notification = Notification.create_notification(activity, muter)
297 assert notification.id
298 assert notification.seen
301 test "notification created if user is muted without notifications" do
302 muter = insert(:user)
303 muted = insert(:user)
305 {:ok, _user_relationships} = User.mute(muter, muted, %{notifications: false})
307 {:ok, activity} = CommonAPI.post(muted, %{status: "Hi @#{muter.nickname}"})
309 assert Notification.create_notification(activity, muter)
312 test "it creates a notification for an activity from a muted thread" do
313 muter = insert(:user)
314 other_user = insert(:user)
315 {:ok, activity} = CommonAPI.post(muter, %{status: "hey"})
316 CommonAPI.add_mute(muter, activity)
319 CommonAPI.post(other_user, %{
320 status: "Hi @#{muter.nickname}",
321 in_reply_to_status_id: activity.id
324 notification = Notification.create_notification(activity, muter)
326 assert notification.id
327 assert notification.seen
330 test "it disables notifications from strangers" do
331 follower = insert(:user)
335 notification_settings: %Pleroma.User.NotificationSetting{block_from_strangers: true}
338 {:ok, activity} = CommonAPI.post(follower, %{status: "hey @#{followed.nickname}"})
339 refute Notification.create_notification(activity, followed)
342 test "it disables notifications from non-followees" do
343 follower = insert(:user)
347 notification_settings: %Pleroma.User.NotificationSetting{block_from_strangers: true}
350 CommonAPI.follow(follower, followed)
351 {:ok, activity} = CommonAPI.post(follower, %{status: "hey @#{followed.nickname}"})
352 refute Notification.create_notification(activity, followed)
355 test "it allows notifications from followees" do
356 poster = insert(:user)
360 notification_settings: %Pleroma.User.NotificationSetting{block_from_strangers: true}
363 CommonAPI.follow(receiver, poster)
364 {:ok, activity} = CommonAPI.post(poster, %{status: "hey @#{receiver.nickname}"})
365 assert Notification.create_notification(activity, receiver)
368 test "it doesn't create a notification for user if he is the activity author" do
369 activity = insert(:note_activity)
370 author = User.get_cached_by_ap_id(activity.data["actor"])
372 refute Notification.create_notification(activity, author)
375 test "it doesn't create duplicate notifications for follow+subscribed users" do
377 subscriber = insert(:user)
379 {:ok, _, _, _} = CommonAPI.follow(subscriber, user)
380 User.subscribe(subscriber, user)
381 {:ok, status} = CommonAPI.post(user, %{status: "Akariiiin"})
382 {:ok, [_notif]} = Notification.create_notifications(status)
385 test "it doesn't create subscription notifications if the recipient cannot see the status" do
387 subscriber = insert(:user)
389 User.subscribe(subscriber, user)
391 {:ok, status} = CommonAPI.post(user, %{status: "inwisible", visibility: "direct"})
393 assert {:ok, []} == Notification.create_notifications(status)
396 test "it disables notifications from people who are invisible" do
397 author = insert(:user, invisible: true)
400 {:ok, status} = CommonAPI.post(author, %{status: "hey @#{user.nickname}"})
401 refute Notification.create_notification(status, user)
404 test "it doesn't create notifications if content matches with an irreversible filter" do
406 subscriber = insert(:user)
408 User.subscribe(subscriber, user)
409 insert(:filter, user: subscriber, phrase: "cofe", hide: true)
411 {:ok, status} = CommonAPI.post(user, %{status: "got cofe?"})
413 assert {:ok, []} == Notification.create_notifications(status)
416 test "it creates notifications if content matches with a not irreversible filter" do
418 subscriber = insert(:user)
420 User.subscribe(subscriber, user)
421 insert(:filter, user: subscriber, phrase: "cofe", hide: false)
423 {:ok, status} = CommonAPI.post(user, %{status: "got cofe?"})
424 {:ok, [notification]} = Notification.create_notifications(status)
427 refute notification.seen
430 test "it creates notifications when someone likes user's status with a filtered word" do
432 other_user = insert(:user)
433 insert(:filter, user: user, phrase: "tesla", hide: true)
435 {:ok, activity_one} = CommonAPI.post(user, %{status: "wow tesla"})
436 {:ok, activity_two} = CommonAPI.favorite(other_user, activity_one.id)
438 {:ok, [notification]} = Notification.create_notifications(activity_two)
441 refute notification.seen
445 describe "follow / follow_request notifications" do
446 test "it creates `follow` notification for approved Follow activity" 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 assert %{type: "follow"} =
455 NotificationView.render("show.json", %{
456 notification: notification,
461 test "it creates `follow_request` notification for pending Follow activity" do
463 followed_user = insert(:user, is_locked: true)
465 {:ok, _, _, _activity} = CommonAPI.follow(user, followed_user)
466 refute FollowingRelationship.following?(user, followed_user)
467 assert [notification] = Notification.for_user(followed_user)
469 render_opts = %{notification: notification, for: followed_user}
470 assert %{type: "follow_request"} = NotificationView.render("show.json", render_opts)
472 # After request is accepted, the same notification is rendered with type "follow":
473 assert {:ok, _} = CommonAPI.accept_follow_request(user, followed_user)
476 Repo.get(Notification, notification.id)
477 |> Repo.preload(:activity)
479 assert %{type: "follow"} =
480 NotificationView.render("show.json", notification: notification, for: followed_user)
483 test "it doesn't create a notification for follow-unfollow-follow chains" do
485 followed_user = insert(:user, is_locked: false)
487 {:ok, _, _, _activity} = CommonAPI.follow(user, followed_user)
488 assert FollowingRelationship.following?(user, followed_user)
489 assert [notification] = Notification.for_user(followed_user)
491 CommonAPI.unfollow(user, followed_user)
492 {:ok, _, _, _activity_dupe} = CommonAPI.follow(user, followed_user)
494 notification_id = notification.id
495 assert [%{id: ^notification_id}] = Notification.for_user(followed_user)
498 test "dismisses the notification on follow request rejection" do
499 user = insert(:user, is_locked: true)
500 follower = insert(:user)
501 {:ok, _, _, _follow_activity} = CommonAPI.follow(follower, user)
502 assert [_notification] = Notification.for_user(user)
503 {:ok, _follower} = CommonAPI.reject_follow_request(follower, user)
504 assert [] = Notification.for_user(user)
508 describe "get notification" do
509 test "it gets a notification that belongs to the user" do
511 other_user = insert(:user)
513 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}"})
515 {:ok, [notification]} = Notification.create_notifications(activity)
516 {:ok, notification} = Notification.get(other_user, notification.id)
518 assert notification.user_id == other_user.id
521 test "it returns error if the notification doesn't belong to the user" do
523 other_user = insert(:user)
525 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}"})
527 {:ok, [notification]} = Notification.create_notifications(activity)
528 {:error, _notification} = Notification.get(user, notification.id)
532 describe "dismiss notification" do
533 test "it dismisses a notification that belongs to the user" do
535 other_user = insert(:user)
537 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}"})
539 {:ok, [notification]} = Notification.create_notifications(activity)
540 {:ok, notification} = Notification.dismiss(other_user, notification.id)
542 assert notification.user_id == other_user.id
545 test "it returns error if the notification doesn't belong to the user" do
547 other_user = insert(:user)
549 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}"})
551 {:ok, [notification]} = Notification.create_notifications(activity)
552 {:error, _notification} = Notification.dismiss(user, notification.id)
556 describe "clear notification" do
557 test "it clears all notifications belonging to the user" do
559 other_user = insert(:user)
560 third_user = insert(:user)
563 CommonAPI.post(user, %{
564 status: "hey @#{other_user.nickname} and @#{third_user.nickname} !"
567 {:ok, _notifs} = Notification.create_notifications(activity)
570 CommonAPI.post(user, %{
571 status: "hey again @#{other_user.nickname} and @#{third_user.nickname} !"
574 {:ok, _notifs} = Notification.create_notifications(activity)
575 Notification.clear(other_user)
577 assert Notification.for_user(other_user) == []
578 assert Notification.for_user(third_user) != []
582 describe "set_read_up_to()" do
583 test "it sets all notifications as read up to a specified notification ID" do
585 other_user = insert(:user)
588 CommonAPI.post(user, %{
589 status: "hey @#{other_user.nickname}!"
593 CommonAPI.post(user, %{
594 status: "hey again @#{other_user.nickname}!"
597 [n2, n1] = Notification.for_user(other_user)
602 CommonAPI.post(user, %{
603 status: "hey yet again @#{other_user.nickname}!"
606 [_, read_notification] = Notification.set_read_up_to(other_user, n2.id)
608 assert read_notification.activity.object
610 [n3, n2, n1] = Notification.for_user(other_user)
612 assert n1.seen == true
613 assert n2.seen == true
614 assert n3.seen == false
616 assert %Pleroma.Marker{} =
620 user_id: other_user.id,
621 timeline: "notifications"
624 assert m.last_read_id == to_string(n2.id)
628 describe "for_user_since/2" do
629 defp days_ago(days) do
631 NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second),
632 -days * 60 * 60 * 24,
637 test "Returns recent notifications" do
638 user1 = insert(:user)
639 user2 = insert(:user)
641 Enum.each(0..10, fn i ->
643 CommonAPI.post(user1, %{
644 status: "hey ##{i} @#{user2.nickname}!"
648 {old, new} = Enum.split(Notification.for_user(user2), 5)
650 Enum.each(old, fn notification ->
652 |> cast(%{updated_at: days_ago(10)}, [:updated_at])
653 |> Pleroma.Repo.update!()
656 recent_notifications_ids =
658 |> Notification.for_user_since(
659 NaiveDateTime.add(NaiveDateTime.utc_now(), -5 * 86_400, :second)
663 Enum.each(old, fn %{id: id} ->
664 refute id in recent_notifications_ids
667 Enum.each(new, fn %{id: id} ->
668 assert id in recent_notifications_ids
673 describe "notification target determination / get_notified_from_activity/2" do
674 test "it sends notifications to addressed users in new messages" do
676 other_user = insert(:user)
679 CommonAPI.post(user, %{
680 status: "hey @#{other_user.nickname}!"
683 {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity)
685 assert other_user in enabled_receivers
688 test "it sends notifications to mentioned users in new messages" do
690 other_user = insert(:user)
693 "@context" => "https://www.w3.org/ns/activitystreams",
695 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
696 "actor" => user.ap_id,
699 "id" => Pleroma.Web.ActivityPub.Utils.generate_object_id(),
700 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
701 "content" => "message with a Mention tag, but no explicit tagging",
705 "href" => other_user.ap_id,
706 "name" => other_user.nickname
709 "attributedTo" => user.ap_id
713 {:ok, activity} = Transmogrifier.handle_incoming(create_activity)
715 {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity)
717 assert other_user in enabled_receivers
720 test "it does not send notifications to users who are only cc in new messages" do
722 other_user = insert(:user)
725 "@context" => "https://www.w3.org/ns/activitystreams",
727 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
728 "cc" => [other_user.ap_id],
729 "actor" => user.ap_id,
732 "id" => Pleroma.Web.ActivityPub.Utils.generate_object_id(),
733 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
734 "cc" => [other_user.ap_id],
735 "content" => "hi everyone",
736 "attributedTo" => user.ap_id
740 {:ok, activity} = Transmogrifier.handle_incoming(create_activity)
742 {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity)
744 assert other_user not in enabled_receivers
747 test "it does not send notification to mentioned users in likes" do
749 other_user = insert(:user)
750 third_user = insert(:user)
752 {:ok, activity_one} =
753 CommonAPI.post(user, %{
754 status: "hey @#{other_user.nickname}!"
757 {:ok, activity_two} = CommonAPI.favorite(third_user, activity_one.id)
759 {enabled_receivers, _disabled_receivers} =
760 Notification.get_notified_from_activity(activity_two)
762 assert other_user not in enabled_receivers
765 test "it only notifies the post's author in likes" do
767 other_user = insert(:user)
768 third_user = insert(:user)
770 {:ok, activity_one} =
771 CommonAPI.post(user, %{
772 status: "hey @#{other_user.nickname}!"
775 {:ok, like_data, _} = Builder.like(third_user, activity_one.object)
779 |> Map.put("to", [other_user.ap_id | like_data["to"]])
780 |> ActivityPub.persist(local: true)
782 {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(like)
784 assert other_user not in enabled_receivers
787 test "it does not send notification to mentioned users in announces" do
789 other_user = insert(:user)
790 third_user = insert(:user)
792 {:ok, activity_one} =
793 CommonAPI.post(user, %{
794 status: "hey @#{other_user.nickname}!"
797 {:ok, activity_two} = CommonAPI.repeat(activity_one.id, third_user)
799 {enabled_receivers, _disabled_receivers} =
800 Notification.get_notified_from_activity(activity_two)
802 assert other_user not in enabled_receivers
805 test "it returns blocking recipient in disabled recipients list" do
807 other_user = insert(:user)
808 {:ok, _user_relationship} = User.block(other_user, user)
810 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
812 {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
814 assert [] == enabled_receivers
815 assert [other_user] == disabled_receivers
818 test "it returns notification-muting recipient in disabled recipients list" do
820 other_user = insert(:user)
821 {:ok, _user_relationships} = User.mute(other_user, user)
823 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
825 {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
827 assert [] == enabled_receivers
828 assert [other_user] == disabled_receivers
831 test "it returns thread-muting recipient in disabled recipients list" do
833 other_user = insert(:user)
835 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
837 {:ok, _} = CommonAPI.add_mute(other_user, activity)
839 {:ok, same_context_activity} =
840 CommonAPI.post(user, %{
841 status: "hey-hey-hey @#{other_user.nickname}!",
842 in_reply_to_status_id: activity.id
845 {enabled_receivers, disabled_receivers} =
846 Notification.get_notified_from_activity(same_context_activity)
848 assert [other_user] == disabled_receivers
849 refute other_user in enabled_receivers
852 test "it returns non-following domain-blocking recipient in disabled recipients list" do
853 blocked_domain = "blocked.domain"
854 user = insert(:user, %{ap_id: "https://#{blocked_domain}/@actor"})
855 other_user = insert(:user)
857 {:ok, other_user} = User.block_domain(other_user, blocked_domain)
859 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
861 {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
863 assert [] == enabled_receivers
864 assert [other_user] == disabled_receivers
867 test "it returns following domain-blocking recipient in enabled recipients list" do
868 blocked_domain = "blocked.domain"
869 user = insert(:user, %{ap_id: "https://#{blocked_domain}/@actor"})
870 other_user = insert(:user)
872 {:ok, other_user} = User.block_domain(other_user, blocked_domain)
873 {:ok, other_user, user} = User.follow(other_user, user)
875 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
877 {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
879 assert [other_user] == enabled_receivers
880 assert [] == disabled_receivers
883 test "it sends edited notifications to those who repeated a status" do
885 repeated_user = insert(:user)
886 other_user = insert(:user)
888 {:ok, activity_one} =
889 CommonAPI.post(user, %{
890 status: "hey @#{other_user.nickname}!"
893 {:ok, _activity_two} = CommonAPI.repeat(activity_one.id, repeated_user)
895 {:ok, edit_activity} =
896 CommonAPI.update(user, activity_one, %{
897 status: "hey @#{other_user.nickname}! mew mew"
900 {enabled_receivers, _disabled_receivers} =
901 Notification.get_notified_from_activity(edit_activity)
903 assert repeated_user in enabled_receivers
904 assert other_user not in enabled_receivers
908 describe "notification lifecycle" do
909 test "liking an activity results in 1 notification, then 0 if the activity is deleted" do
911 other_user = insert(:user)
913 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
915 assert Enum.empty?(Notification.for_user(user))
917 {:ok, _} = CommonAPI.favorite(other_user, activity.id)
919 assert length(Notification.for_user(user)) == 1
921 {:ok, _} = CommonAPI.delete(activity.id, user)
923 assert Enum.empty?(Notification.for_user(user))
926 test "liking an activity results in 1 notification, then 0 if the activity is unliked" do
928 other_user = insert(:user)
930 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
932 assert Enum.empty?(Notification.for_user(user))
934 {:ok, _} = CommonAPI.favorite(other_user, activity.id)
936 assert length(Notification.for_user(user)) == 1
938 {:ok, _} = CommonAPI.unfavorite(activity.id, other_user)
940 assert Enum.empty?(Notification.for_user(user))
943 test "repeating an activity results in 1 notification, then 0 if the activity is deleted" do
945 other_user = insert(:user)
947 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
949 assert Enum.empty?(Notification.for_user(user))
951 {:ok, _} = CommonAPI.repeat(activity.id, other_user)
953 assert length(Notification.for_user(user)) == 1
955 {:ok, _} = CommonAPI.delete(activity.id, user)
957 assert Enum.empty?(Notification.for_user(user))
960 test "repeating an activity results in 1 notification, then 0 if the activity is unrepeated" do
962 other_user = insert(:user)
964 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
966 assert Enum.empty?(Notification.for_user(user))
968 {:ok, _} = CommonAPI.repeat(activity.id, other_user)
970 assert length(Notification.for_user(user)) == 1
972 {:ok, _} = CommonAPI.unrepeat(activity.id, other_user)
974 assert Enum.empty?(Notification.for_user(user))
977 test "liking an activity which is already deleted does not generate a notification" do
979 other_user = insert(:user)
981 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
983 assert Enum.empty?(Notification.for_user(user))
985 {:ok, _deletion_activity} = CommonAPI.delete(activity.id, user)
987 assert Enum.empty?(Notification.for_user(user))
989 {:error, :not_found} = CommonAPI.favorite(other_user, activity.id)
991 assert Enum.empty?(Notification.for_user(user))
994 test "repeating an activity which is already deleted does not generate a notification" do
996 other_user = insert(:user)
998 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
1000 assert Enum.empty?(Notification.for_user(user))
1002 {:ok, _deletion_activity} = CommonAPI.delete(activity.id, user)
1004 assert Enum.empty?(Notification.for_user(user))
1006 {:error, _} = CommonAPI.repeat(activity.id, other_user)
1008 assert Enum.empty?(Notification.for_user(user))
1011 test "replying to a deleted post without tagging does not generate a notification" do
1012 user = insert(:user)
1013 other_user = insert(:user)
1015 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
1016 {:ok, _deletion_activity} = CommonAPI.delete(activity.id, user)
1018 {:ok, _reply_activity} =
1019 CommonAPI.post(other_user, %{
1020 status: "test reply",
1021 in_reply_to_status_id: activity.id
1024 assert Enum.empty?(Notification.for_user(user))
1027 test "notifications are deleted if a local user is deleted" do
1028 user = insert(:user)
1029 other_user = insert(:user)
1032 CommonAPI.post(user, %{status: "hi @#{other_user.nickname}", visibility: "direct"})
1034 refute Enum.empty?(Notification.for_user(other_user))
1036 {:ok, job} = User.delete(user)
1037 ObanHelpers.perform(job)
1039 assert Enum.empty?(Notification.for_user(other_user))
1042 test "notifications are deleted if a remote user is deleted" do
1043 remote_user = insert(:user)
1044 local_user = insert(:user)
1047 "@context" => "https://www.w3.org/ns/activitystreams",
1049 "actor" => remote_user.ap_id,
1050 "id" => remote_user.ap_id <> "/activities/test",
1051 "to" => [local_user.ap_id],
1055 "id" => remote_user.ap_id <> "/objects/test",
1056 "content" => "Hello!",
1059 "type" => "Mention",
1060 "href" => local_user.ap_id,
1061 "name" => "@#{local_user.nickname}"
1064 "to" => [local_user.ap_id],
1066 "attributedTo" => remote_user.ap_id
1070 {:ok, _dm_activity} = Transmogrifier.handle_incoming(dm_message)
1072 refute Enum.empty?(Notification.for_user(local_user))
1074 delete_user_message = %{
1075 "@context" => "https://www.w3.org/ns/activitystreams",
1076 "id" => remote_user.ap_id <> "/activities/delete",
1077 "actor" => remote_user.ap_id,
1079 "object" => remote_user.ap_id
1082 remote_user_url = remote_user.ap_id
1085 %{method: :get, url: ^remote_user_url} ->
1086 %Tesla.Env{status: 404, body: ""}
1089 {:ok, _delete_activity} = Transmogrifier.handle_incoming(delete_user_message)
1090 ObanHelpers.perform_all()
1092 assert Enum.empty?(Notification.for_user(local_user))
1095 test "move activity generates a notification" do
1096 %{ap_id: old_ap_id} = old_user = insert(:user)
1097 %{ap_id: new_ap_id} = new_user = insert(:user, also_known_as: [old_ap_id])
1098 follower = insert(:user)
1099 other_follower = insert(:user, %{allow_following_move: false})
1101 User.follow(follower, old_user)
1102 User.follow(other_follower, old_user)
1104 Pleroma.Web.ActivityPub.ActivityPub.move(old_user, new_user)
1105 ObanHelpers.perform_all()
1110 data: %{"type" => "Move", "actor" => ^old_ap_id, "target" => ^new_ap_id}
1113 ] = Notification.for_user(follower)
1118 data: %{"type" => "Move", "actor" => ^old_ap_id, "target" => ^new_ap_id}
1121 ] = Notification.for_user(other_follower)
1125 describe "for_user" do
1127 user = insert(:user)
1129 {:ok, %{user: user}}
1132 test "it returns notifications for muted user without notifications", %{user: user} do
1133 muted = insert(:user)
1134 {:ok, _user_relationships} = User.mute(user, muted, %{notifications: false})
1136 {:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"})
1138 [notification] = Notification.for_user(user)
1140 assert notification.activity.object
1141 assert notification.seen
1144 test "it doesn't return notifications for muted user with notifications", %{user: user} do
1145 muted = insert(:user)
1146 {:ok, _user_relationships} = User.mute(user, muted)
1148 {:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"})
1150 assert Notification.for_user(user) == []
1153 test "it doesn't return notifications for blocked user", %{user: user} do
1154 blocked = insert(:user)
1155 {:ok, _user_relationship} = User.block(user, blocked)
1157 {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
1159 assert Notification.for_user(user) == []
1162 test "it doesn't return notifications for domain-blocked non-followed user", %{user: user} do
1163 blocked = insert(:user, ap_id: "http://some-domain.com")
1164 {:ok, user} = User.block_domain(user, "some-domain.com")
1166 {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
1168 assert Notification.for_user(user) == []
1171 test "it returns notifications for domain-blocked but followed user" do
1172 user = insert(:user)
1173 blocked = insert(:user, ap_id: "http://some-domain.com")
1175 {:ok, user} = User.block_domain(user, "some-domain.com")
1176 {:ok, _, _} = User.follow(user, blocked)
1178 {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
1180 assert length(Notification.for_user(user)) == 1
1183 test "it doesn't return notifications for muted thread", %{user: user} do
1184 another_user = insert(:user)
1186 {:ok, activity} = CommonAPI.post(another_user, %{status: "hey @#{user.nickname}"})
1188 {:ok, _} = Pleroma.ThreadMute.add_mute(user.id, activity.data["context"])
1189 assert Notification.for_user(user) == []
1192 test "it returns notifications from a muted user when with_muted is set", %{user: user} do
1193 muted = insert(:user)
1194 {:ok, _user_relationships} = User.mute(user, muted)
1196 {:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"})
1198 assert length(Notification.for_user(user, %{with_muted: true})) == 1
1201 test "it doesn't return notifications from a blocked user when with_muted is set", %{
1204 blocked = insert(:user)
1205 {:ok, _user_relationship} = User.block(user, blocked)
1207 {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
1209 assert Enum.empty?(Notification.for_user(user, %{with_muted: true}))
1212 test "when with_muted is set, " <>
1213 "it doesn't return notifications from a domain-blocked non-followed user",
1215 blocked = insert(:user, ap_id: "http://some-domain.com")
1216 {:ok, user} = User.block_domain(user, "some-domain.com")
1218 {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
1220 assert Enum.empty?(Notification.for_user(user, %{with_muted: true}))
1223 test "it returns notifications from muted threads when with_muted is set", %{user: user} do
1224 another_user = insert(:user)
1226 {:ok, activity} = CommonAPI.post(another_user, %{status: "hey @#{user.nickname}"})
1228 {:ok, _} = Pleroma.ThreadMute.add_mute(user.id, activity.data["context"])
1229 assert length(Notification.for_user(user, %{with_muted: true})) == 1
1232 test "it doesn't return notifications about mentions with filtered word", %{user: user} do
1233 insert(:filter, user: user, phrase: "cofe", hide: true)
1234 another_user = insert(:user)
1236 {:ok, _activity} = CommonAPI.post(another_user, %{status: "@#{user.nickname} got cofe?"})
1238 assert Enum.empty?(Notification.for_user(user))
1241 test "it returns notifications about mentions with not hidden filtered word", %{user: user} do
1242 insert(:filter, user: user, phrase: "test", hide: false)
1243 another_user = insert(:user)
1245 {:ok, _} = CommonAPI.post(another_user, %{status: "@#{user.nickname} test"})
1247 assert length(Notification.for_user(user)) == 1
1250 test "it returns notifications about favorites with filtered word", %{user: user} do
1251 insert(:filter, user: user, phrase: "cofe", hide: true)
1252 another_user = insert(:user)
1254 {:ok, activity} = CommonAPI.post(user, %{status: "Give me my cofe!"})
1255 {:ok, _} = CommonAPI.favorite(another_user, activity.id)
1257 assert length(Notification.for_user(user)) == 1
1260 test "it returns notifications when related object is without content and filters are defined",
1262 followed_user = insert(:user, is_locked: true)
1264 insert(:filter, user: followed_user, phrase: "test", hide: true)
1266 {:ok, _, _, _activity} = CommonAPI.follow(user, followed_user)
1267 refute FollowingRelationship.following?(user, followed_user)
1268 assert [notification] = Notification.for_user(followed_user)
1270 assert %{type: "follow_request"} =
1271 NotificationView.render("show.json", %{
1272 notification: notification,
1276 assert {:ok, _} = CommonAPI.accept_follow_request(user, followed_user)
1278 assert [notification] = Notification.for_user(followed_user)
1280 assert %{type: "follow"} =
1281 NotificationView.render("show.json", %{
1282 notification: notification,