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
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 report notification only for privileged users" do
36 reporting_user = insert(:user)
37 reported_user = insert(:user)
38 moderator_user = insert(:user, is_moderator: true)
40 clear_config([:instance, :moderator_privileges], [])
41 {:ok, activity1} = CommonAPI.report(reporting_user, %{account_id: reported_user.id})
42 {:ok, []} = Notification.create_notifications(activity1)
44 clear_config([:instance, :moderator_privileges], [:reports_manage_reports])
45 {:ok, activity2} = CommonAPI.report(reporting_user, %{account_id: reported_user.id})
46 {:ok, [notification]} = Notification.create_notifications(activity2)
48 assert notification.user_id == moderator_user.id
49 assert notification.type == "pleroma:report"
52 test "suppresses notifications for own reports" do
53 clear_config([:instance, :admin_privileges], [:reports_manage_reports])
55 reporting_admin = insert(:user, is_admin: true)
56 reported_user = insert(:user)
57 other_admin = insert(:user, is_admin: true)
59 {:ok, activity} = CommonAPI.report(reporting_admin, %{account_id: reported_user.id})
61 {:ok, [notification]} = Notification.create_notifications(activity)
63 refute notification.user_id == reporting_admin.id
64 assert notification.user_id == other_admin.id
65 assert notification.type == "pleroma:report"
68 test "creates a notification for an emoji reaction" do
70 other_user = insert(:user)
72 {:ok, activity} = CommonAPI.post(user, %{status: "yeah"})
73 {:ok, activity} = CommonAPI.react_with_emoji(activity.id, other_user, "☕")
75 {:ok, [notification]} = Notification.create_notifications(activity)
77 assert notification.user_id == user.id
78 assert notification.type == "pleroma:emoji_reaction"
81 test "notifies someone when they are directly addressed" do
83 other_user = insert(:user)
84 third_user = insert(:user)
87 CommonAPI.post(user, %{
88 status: "hey @#{other_user.nickname} and @#{third_user.nickname}"
91 {:ok, [notification, other_notification]} = Notification.create_notifications(activity)
93 notified_ids = Enum.sort([notification.user_id, other_notification.user_id])
94 assert notified_ids == [other_user.id, third_user.id]
95 assert notification.activity_id == activity.id
96 assert notification.type == "mention"
97 assert other_notification.activity_id == activity.id
99 assert [%Pleroma.Marker{unread_count: 2}] =
100 Pleroma.Marker.get_markers(other_user, ["notifications"])
103 test "it creates a notification for subscribed users" do
105 subscriber = insert(:user)
107 User.subscribe(subscriber, user)
109 {:ok, status} = CommonAPI.post(user, %{status: "Akariiiin"})
110 {:ok, [notification]} = Notification.create_notifications(status)
112 assert notification.user_id == subscriber.id
115 test "does not create a notification for subscribed users if status is a reply" do
117 other_user = insert(:user)
118 subscriber = insert(:user)
120 User.subscribe(subscriber, other_user)
122 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
124 {:ok, _reply_activity} =
125 CommonAPI.post(other_user, %{
126 status: "test reply",
127 in_reply_to_status_id: activity.id
130 user_notifications = Notification.for_user(user)
131 assert length(user_notifications) == 1
133 subscriber_notifications = Notification.for_user(subscriber)
134 assert Enum.empty?(subscriber_notifications)
137 test "it sends edited notifications to those who repeated a status" do
139 repeated_user = insert(:user)
140 other_user = insert(:user)
142 {:ok, activity_one} =
143 CommonAPI.post(user, %{
144 status: "hey @#{other_user.nickname}!"
147 {:ok, _activity_two} = CommonAPI.repeat(activity_one.id, repeated_user)
149 {:ok, _edit_activity} =
150 CommonAPI.update(user, activity_one, %{
151 status: "hey @#{other_user.nickname}! mew mew"
154 assert [%{type: "reblog"}] = Notification.for_user(user)
155 assert [%{type: "update"}] = Notification.for_user(repeated_user)
156 assert [%{type: "mention"}] = Notification.for_user(other_user)
160 test "create_poll_notifications/1" do
161 [user1, user2, user3, _, _] = insert_list(5, :user)
162 question = insert(:question, user: user1)
163 activity = insert(:question_activity, question: question)
165 {:ok, _, _} = CommonAPI.vote(user2, question, [0])
166 {:ok, _, _} = CommonAPI.vote(user3, question, [1])
168 {:ok, notifications} = Notification.create_poll_notifications(activity)
170 assert [user2.id, user3.id, user1.id] == Enum.map(notifications, & &1.user_id)
173 describe "CommonApi.post/2 notification-related functionality" do
174 test_with_mock "creates but does NOT send notification to blocker user",
179 blocker = insert(:user)
180 {:ok, _user_relationship} = User.block(blocker, user)
182 {:ok, _activity} = CommonAPI.post(user, %{status: "hey @#{blocker.nickname}!"})
184 blocker_id = blocker.id
185 assert [%Notification{user_id: ^blocker_id}] = Repo.all(Notification)
186 refute called(Push.send(:_))
189 test_with_mock "creates but does NOT send notification to notification-muter user",
194 muter = insert(:user)
195 {:ok, _user_relationships} = User.mute(muter, user)
197 {:ok, _activity} = CommonAPI.post(user, %{status: "hey @#{muter.nickname}!"})
200 assert [%Notification{user_id: ^muter_id}] = Repo.all(Notification)
201 refute called(Push.send(:_))
204 test_with_mock "creates but does NOT send notification to thread-muter user",
209 thread_muter = insert(:user)
211 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{thread_muter.nickname}!"})
213 {:ok, _} = CommonAPI.add_mute(thread_muter, activity)
215 {:ok, _same_context_activity} =
216 CommonAPI.post(user, %{
217 status: "hey-hey-hey @#{thread_muter.nickname}!",
218 in_reply_to_status_id: activity.id
221 [pre_mute_notification, post_mute_notification] =
222 Repo.all(from(n in Notification, where: n.user_id == ^thread_muter.id, order_by: n.id))
224 pre_mute_notification_id = pre_mute_notification.id
225 post_mute_notification_id = post_mute_notification.id
230 %Notification{id: ^pre_mute_notification_id} -> true
239 %Notification{id: ^post_mute_notification_id} -> true
247 describe "create_notification" do
248 @tag needs_streamer: true
249 test "it creates a notification for user and send to the 'user' and the 'user:notification' stream" do
250 %{user: user, token: oauth_token} = oauth_access(["read"])
254 {:ok, _topic} = Streamer.get_topic_and_add_socket("user", user, oauth_token)
255 assert_receive {:render_with_user, _, _, _}, 4_000
258 task_user_notification =
261 Streamer.get_topic_and_add_socket("user:notification", user, oauth_token)
263 assert_receive {:render_with_user, _, _, _}, 4_000
266 activity = insert(:note_activity)
268 notify = Notification.create_notification(activity, user)
269 assert notify.user_id == user.id
271 Task.await(task_user_notification)
274 test "it creates a notification for user if the user blocks the activity author" do
275 activity = insert(:note_activity)
276 author = User.get_cached_by_ap_id(activity.data["actor"])
278 {:ok, _user_relationship} = User.block(user, author)
280 assert Notification.create_notification(activity, user)
283 test "it creates a notification for the user if the user mutes the activity author" do
284 muter = insert(:user)
285 muted = insert(:user)
286 {:ok, _} = User.mute(muter, muted)
287 muter = Repo.get(User, muter.id)
288 {:ok, activity} = CommonAPI.post(muted, %{status: "Hi @#{muter.nickname}"})
290 notification = Notification.create_notification(activity, muter)
292 assert notification.id
293 assert notification.seen
296 test "notification created if user is muted without notifications" do
297 muter = insert(:user)
298 muted = insert(:user)
300 {:ok, _user_relationships} = User.mute(muter, muted, %{notifications: false})
302 {:ok, activity} = CommonAPI.post(muted, %{status: "Hi @#{muter.nickname}"})
304 assert Notification.create_notification(activity, muter)
307 test "it creates a notification for an activity from a muted thread" do
308 muter = insert(:user)
309 other_user = insert(:user)
310 {:ok, activity} = CommonAPI.post(muter, %{status: "hey"})
311 CommonAPI.add_mute(muter, activity)
314 CommonAPI.post(other_user, %{
315 status: "Hi @#{muter.nickname}",
316 in_reply_to_status_id: activity.id
319 notification = Notification.create_notification(activity, muter)
321 assert notification.id
322 assert notification.seen
325 test "it disables notifications from strangers" do
326 follower = insert(:user)
330 notification_settings: %Pleroma.User.NotificationSetting{block_from_strangers: true}
333 {:ok, activity} = CommonAPI.post(follower, %{status: "hey @#{followed.nickname}"})
334 refute Notification.create_notification(activity, followed)
337 test "it disables notifications from non-followees" do
338 follower = insert(:user)
342 notification_settings: %Pleroma.User.NotificationSetting{block_from_strangers: true}
345 CommonAPI.follow(follower, followed)
346 {:ok, activity} = CommonAPI.post(follower, %{status: "hey @#{followed.nickname}"})
347 refute Notification.create_notification(activity, followed)
350 test "it allows notifications from followees" do
351 poster = insert(:user)
355 notification_settings: %Pleroma.User.NotificationSetting{block_from_strangers: true}
358 CommonAPI.follow(receiver, poster)
359 {:ok, activity} = CommonAPI.post(poster, %{status: "hey @#{receiver.nickname}"})
360 assert Notification.create_notification(activity, receiver)
363 test "it doesn't create a notification for user if he is the activity author" do
364 activity = insert(:note_activity)
365 author = User.get_cached_by_ap_id(activity.data["actor"])
367 refute Notification.create_notification(activity, author)
370 test "it doesn't create duplicate notifications for follow+subscribed users" do
372 subscriber = insert(:user)
374 {:ok, _, _, _} = CommonAPI.follow(subscriber, user)
375 User.subscribe(subscriber, user)
376 {:ok, status} = CommonAPI.post(user, %{status: "Akariiiin"})
377 {:ok, [_notif]} = Notification.create_notifications(status)
380 test "it doesn't create subscription notifications if the recipient cannot see the status" do
382 subscriber = insert(:user)
384 User.subscribe(subscriber, user)
386 {:ok, status} = CommonAPI.post(user, %{status: "inwisible", visibility: "direct"})
388 assert {:ok, []} == Notification.create_notifications(status)
391 test "it disables notifications from people who are invisible" do
392 author = insert(:user, invisible: true)
395 {:ok, status} = CommonAPI.post(author, %{status: "hey @#{user.nickname}"})
396 refute Notification.create_notification(status, user)
399 test "it doesn't create notifications if content matches with an irreversible filter" do
401 subscriber = insert(:user)
403 User.subscribe(subscriber, user)
404 insert(:filter, user: subscriber, phrase: "cofe", hide: true)
406 {:ok, status} = CommonAPI.post(user, %{status: "got cofe?"})
408 assert {:ok, []} == Notification.create_notifications(status)
411 test "it creates notifications if content matches with a not irreversible filter" do
413 subscriber = insert(:user)
415 User.subscribe(subscriber, user)
416 insert(:filter, user: subscriber, phrase: "cofe", hide: false)
418 {:ok, status} = CommonAPI.post(user, %{status: "got cofe?"})
419 {:ok, [notification]} = Notification.create_notifications(status)
422 refute notification.seen
425 test "it creates notifications when someone likes user's status with a filtered word" do
427 other_user = insert(:user)
428 insert(:filter, user: user, phrase: "tesla", hide: true)
430 {:ok, activity_one} = CommonAPI.post(user, %{status: "wow tesla"})
431 {:ok, activity_two} = CommonAPI.favorite(other_user, activity_one.id)
433 {:ok, [notification]} = Notification.create_notifications(activity_two)
436 refute notification.seen
440 describe "follow / follow_request notifications" do
441 test "it creates `follow` notification for approved Follow activity" do
443 followed_user = insert(:user, is_locked: false)
445 {:ok, _, _, _activity} = CommonAPI.follow(user, followed_user)
446 assert FollowingRelationship.following?(user, followed_user)
447 assert [notification] = Notification.for_user(followed_user)
449 assert %{type: "follow"} =
450 NotificationView.render("show.json", %{
451 notification: notification,
456 test "it creates `follow_request` notification for pending Follow activity" do
458 followed_user = insert(:user, is_locked: true)
460 {:ok, _, _, _activity} = CommonAPI.follow(user, followed_user)
461 refute FollowingRelationship.following?(user, followed_user)
462 assert [notification] = Notification.for_user(followed_user)
464 render_opts = %{notification: notification, for: followed_user}
465 assert %{type: "follow_request"} = NotificationView.render("show.json", render_opts)
467 # After request is accepted, the same notification is rendered with type "follow":
468 assert {:ok, _} = CommonAPI.accept_follow_request(user, followed_user)
471 Repo.get(Notification, notification.id)
472 |> Repo.preload(:activity)
474 assert %{type: "follow"} =
475 NotificationView.render("show.json", notification: notification, for: followed_user)
478 test "it doesn't create a notification for follow-unfollow-follow chains" do
480 followed_user = insert(:user, is_locked: false)
482 {:ok, _, _, _activity} = CommonAPI.follow(user, followed_user)
483 assert FollowingRelationship.following?(user, followed_user)
484 assert [notification] = Notification.for_user(followed_user)
486 CommonAPI.unfollow(user, followed_user)
487 {:ok, _, _, _activity_dupe} = CommonAPI.follow(user, followed_user)
489 notification_id = notification.id
490 assert [%{id: ^notification_id}] = Notification.for_user(followed_user)
493 test "dismisses the notification on follow request rejection" do
494 user = insert(:user, is_locked: true)
495 follower = insert(:user)
496 {:ok, _, _, _follow_activity} = CommonAPI.follow(follower, user)
497 assert [_notification] = Notification.for_user(user)
498 {:ok, _follower} = CommonAPI.reject_follow_request(follower, user)
499 assert [] = Notification.for_user(user)
503 describe "get notification" do
504 test "it gets a notification that belongs to the user" do
506 other_user = insert(:user)
508 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}"})
510 {:ok, [notification]} = Notification.create_notifications(activity)
511 {:ok, notification} = Notification.get(other_user, notification.id)
513 assert notification.user_id == other_user.id
516 test "it returns error if the notification doesn't belong to the user" do
518 other_user = insert(:user)
520 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}"})
522 {:ok, [notification]} = Notification.create_notifications(activity)
523 {:error, _notification} = Notification.get(user, notification.id)
527 describe "dismiss notification" do
528 test "it dismisses a notification that belongs to the user" do
530 other_user = insert(:user)
532 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}"})
534 {:ok, [notification]} = Notification.create_notifications(activity)
535 {:ok, notification} = Notification.dismiss(other_user, notification.id)
537 assert notification.user_id == other_user.id
540 test "it returns error if the notification doesn't belong to the user" do
542 other_user = insert(:user)
544 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}"})
546 {:ok, [notification]} = Notification.create_notifications(activity)
547 {:error, _notification} = Notification.dismiss(user, notification.id)
551 describe "clear notification" do
552 test "it clears all notifications belonging to the user" do
554 other_user = insert(:user)
555 third_user = insert(:user)
558 CommonAPI.post(user, %{
559 status: "hey @#{other_user.nickname} and @#{third_user.nickname} !"
562 {:ok, _notifs} = Notification.create_notifications(activity)
565 CommonAPI.post(user, %{
566 status: "hey again @#{other_user.nickname} and @#{third_user.nickname} !"
569 {:ok, _notifs} = Notification.create_notifications(activity)
570 Notification.clear(other_user)
572 assert Notification.for_user(other_user) == []
573 assert Notification.for_user(third_user) != []
577 describe "set_read_up_to()" do
578 test "it sets all notifications as read up to a specified notification ID" do
580 other_user = insert(:user)
583 CommonAPI.post(user, %{
584 status: "hey @#{other_user.nickname}!"
588 CommonAPI.post(user, %{
589 status: "hey again @#{other_user.nickname}!"
592 [n2, n1] = Notification.for_user(other_user)
597 CommonAPI.post(user, %{
598 status: "hey yet again @#{other_user.nickname}!"
601 [_, read_notification] = Notification.set_read_up_to(other_user, n2.id)
603 assert read_notification.activity.object
605 [n3, n2, n1] = Notification.for_user(other_user)
607 assert n1.seen == true
608 assert n2.seen == true
609 assert n3.seen == false
611 assert %Pleroma.Marker{} =
615 user_id: other_user.id,
616 timeline: "notifications"
619 assert m.last_read_id == to_string(n2.id)
623 describe "for_user_since/2" do
624 defp days_ago(days) do
626 NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second),
627 -days * 60 * 60 * 24,
632 test "Returns recent notifications" do
633 user1 = insert(:user)
634 user2 = insert(:user)
636 Enum.each(0..10, fn i ->
638 CommonAPI.post(user1, %{
639 status: "hey ##{i} @#{user2.nickname}!"
643 {old, new} = Enum.split(Notification.for_user(user2), 5)
645 Enum.each(old, fn notification ->
647 |> cast(%{updated_at: days_ago(10)}, [:updated_at])
648 |> Pleroma.Repo.update!()
651 recent_notifications_ids =
653 |> Notification.for_user_since(
654 NaiveDateTime.add(NaiveDateTime.utc_now(), -5 * 86_400, :second)
658 Enum.each(old, fn %{id: id} ->
659 refute id in recent_notifications_ids
662 Enum.each(new, fn %{id: id} ->
663 assert id in recent_notifications_ids
668 describe "notification target determination / get_notified_from_activity/2" do
669 test "it sends notifications to addressed users in new messages" do
671 other_user = insert(:user)
674 CommonAPI.post(user, %{
675 status: "hey @#{other_user.nickname}!"
678 {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity)
680 assert other_user in enabled_receivers
683 test "it sends notifications to mentioned users in new messages" do
685 other_user = insert(:user)
688 "@context" => "https://www.w3.org/ns/activitystreams",
690 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
691 "actor" => user.ap_id,
694 "id" => Pleroma.Web.ActivityPub.Utils.generate_object_id(),
695 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
696 "content" => "message with a Mention tag, but no explicit tagging",
700 "href" => other_user.ap_id,
701 "name" => other_user.nickname
704 "attributedTo" => user.ap_id
708 {:ok, activity} = Transmogrifier.handle_incoming(create_activity)
710 {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity)
712 assert other_user in enabled_receivers
715 test "it does not send notifications to users who are only cc in new messages" do
717 other_user = insert(:user)
720 "@context" => "https://www.w3.org/ns/activitystreams",
722 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
723 "cc" => [other_user.ap_id],
724 "actor" => user.ap_id,
727 "id" => Pleroma.Web.ActivityPub.Utils.generate_object_id(),
728 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
729 "cc" => [other_user.ap_id],
730 "content" => "hi everyone",
731 "attributedTo" => user.ap_id
735 {:ok, activity} = Transmogrifier.handle_incoming(create_activity)
737 {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity)
739 assert other_user not in enabled_receivers
742 test "it does not send notification to mentioned users in likes" do
744 other_user = insert(:user)
745 third_user = insert(:user)
747 {:ok, activity_one} =
748 CommonAPI.post(user, %{
749 status: "hey @#{other_user.nickname}!"
752 {:ok, activity_two} = CommonAPI.favorite(third_user, activity_one.id)
754 {enabled_receivers, _disabled_receivers} =
755 Notification.get_notified_from_activity(activity_two)
757 assert other_user not in enabled_receivers
760 test "it only notifies the post's author in likes" do
762 other_user = insert(:user)
763 third_user = insert(:user)
765 {:ok, activity_one} =
766 CommonAPI.post(user, %{
767 status: "hey @#{other_user.nickname}!"
770 {:ok, like_data, _} = Builder.like(third_user, activity_one.object)
774 |> Map.put("to", [other_user.ap_id | like_data["to"]])
775 |> ActivityPub.persist(local: true)
777 {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(like)
779 assert other_user not in enabled_receivers
782 test "it does not send notification to mentioned users in announces" do
784 other_user = insert(:user)
785 third_user = insert(:user)
787 {:ok, activity_one} =
788 CommonAPI.post(user, %{
789 status: "hey @#{other_user.nickname}!"
792 {:ok, activity_two} = CommonAPI.repeat(activity_one.id, third_user)
794 {enabled_receivers, _disabled_receivers} =
795 Notification.get_notified_from_activity(activity_two)
797 assert other_user not in enabled_receivers
800 test "it returns blocking recipient in disabled recipients list" do
802 other_user = insert(:user)
803 {:ok, _user_relationship} = User.block(other_user, user)
805 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
807 {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
809 assert [] == enabled_receivers
810 assert [other_user] == disabled_receivers
813 test "it returns notification-muting recipient in disabled recipients list" do
815 other_user = insert(:user)
816 {:ok, _user_relationships} = User.mute(other_user, user)
818 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
820 {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
822 assert [] == enabled_receivers
823 assert [other_user] == disabled_receivers
826 test "it returns thread-muting recipient in disabled recipients list" do
828 other_user = insert(:user)
830 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
832 {:ok, _} = CommonAPI.add_mute(other_user, activity)
834 {:ok, same_context_activity} =
835 CommonAPI.post(user, %{
836 status: "hey-hey-hey @#{other_user.nickname}!",
837 in_reply_to_status_id: activity.id
840 {enabled_receivers, disabled_receivers} =
841 Notification.get_notified_from_activity(same_context_activity)
843 assert [other_user] == disabled_receivers
844 refute other_user in enabled_receivers
847 test "it returns non-following domain-blocking recipient in disabled recipients list" do
848 blocked_domain = "blocked.domain"
849 user = insert(:user, %{ap_id: "https://#{blocked_domain}/@actor"})
850 other_user = insert(:user)
852 {:ok, other_user} = User.block_domain(other_user, blocked_domain)
854 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
856 {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
858 assert [] == enabled_receivers
859 assert [other_user] == disabled_receivers
862 test "it returns following domain-blocking recipient in enabled recipients list" do
863 blocked_domain = "blocked.domain"
864 user = insert(:user, %{ap_id: "https://#{blocked_domain}/@actor"})
865 other_user = insert(:user)
867 {:ok, other_user} = User.block_domain(other_user, blocked_domain)
868 {:ok, other_user, user} = User.follow(other_user, user)
870 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
872 {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
874 assert [other_user] == enabled_receivers
875 assert [] == disabled_receivers
878 test "it sends edited notifications to those who repeated a status" do
880 repeated_user = insert(:user)
881 other_user = insert(:user)
883 {:ok, activity_one} =
884 CommonAPI.post(user, %{
885 status: "hey @#{other_user.nickname}!"
888 {:ok, _activity_two} = CommonAPI.repeat(activity_one.id, repeated_user)
890 {:ok, edit_activity} =
891 CommonAPI.update(user, activity_one, %{
892 status: "hey @#{other_user.nickname}! mew mew"
895 {enabled_receivers, _disabled_receivers} =
896 Notification.get_notified_from_activity(edit_activity)
898 assert repeated_user in enabled_receivers
899 assert other_user not in enabled_receivers
903 describe "notification lifecycle" do
904 test "liking an activity results in 1 notification, then 0 if the activity is deleted" do
906 other_user = insert(:user)
908 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
910 assert Enum.empty?(Notification.for_user(user))
912 {:ok, _} = CommonAPI.favorite(other_user, activity.id)
914 assert length(Notification.for_user(user)) == 1
916 {:ok, _} = CommonAPI.delete(activity.id, user)
918 assert Enum.empty?(Notification.for_user(user))
921 test "liking an activity results in 1 notification, then 0 if the activity is unliked" do
923 other_user = insert(:user)
925 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
927 assert Enum.empty?(Notification.for_user(user))
929 {:ok, _} = CommonAPI.favorite(other_user, activity.id)
931 assert length(Notification.for_user(user)) == 1
933 {:ok, _} = CommonAPI.unfavorite(activity.id, other_user)
935 assert Enum.empty?(Notification.for_user(user))
938 test "repeating an activity results in 1 notification, then 0 if the activity is deleted" do
940 other_user = insert(:user)
942 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
944 assert Enum.empty?(Notification.for_user(user))
946 {:ok, _} = CommonAPI.repeat(activity.id, other_user)
948 assert length(Notification.for_user(user)) == 1
950 {:ok, _} = CommonAPI.delete(activity.id, user)
952 assert Enum.empty?(Notification.for_user(user))
955 test "repeating an activity results in 1 notification, then 0 if the activity is unrepeated" do
957 other_user = insert(:user)
959 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
961 assert Enum.empty?(Notification.for_user(user))
963 {:ok, _} = CommonAPI.repeat(activity.id, other_user)
965 assert length(Notification.for_user(user)) == 1
967 {:ok, _} = CommonAPI.unrepeat(activity.id, other_user)
969 assert Enum.empty?(Notification.for_user(user))
972 test "liking an activity which is already deleted does not generate a notification" do
974 other_user = insert(:user)
976 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
978 assert Enum.empty?(Notification.for_user(user))
980 {:ok, _deletion_activity} = CommonAPI.delete(activity.id, user)
982 assert Enum.empty?(Notification.for_user(user))
984 {:error, :not_found} = CommonAPI.favorite(other_user, activity.id)
986 assert Enum.empty?(Notification.for_user(user))
989 test "repeating an activity which is already deleted does not generate a notification" do
991 other_user = insert(:user)
993 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
995 assert Enum.empty?(Notification.for_user(user))
997 {:ok, _deletion_activity} = CommonAPI.delete(activity.id, user)
999 assert Enum.empty?(Notification.for_user(user))
1001 {:error, _} = CommonAPI.repeat(activity.id, other_user)
1003 assert Enum.empty?(Notification.for_user(user))
1006 test "replying to a deleted post without tagging does not generate a notification" do
1007 user = insert(:user)
1008 other_user = insert(:user)
1010 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
1011 {:ok, _deletion_activity} = CommonAPI.delete(activity.id, user)
1013 {:ok, _reply_activity} =
1014 CommonAPI.post(other_user, %{
1015 status: "test reply",
1016 in_reply_to_status_id: activity.id
1019 assert Enum.empty?(Notification.for_user(user))
1022 test "notifications are deleted if a local user is deleted" do
1023 user = insert(:user)
1024 other_user = insert(:user)
1027 CommonAPI.post(user, %{status: "hi @#{other_user.nickname}", visibility: "direct"})
1029 refute Enum.empty?(Notification.for_user(other_user))
1031 {:ok, job} = User.delete(user)
1032 ObanHelpers.perform(job)
1034 assert Enum.empty?(Notification.for_user(other_user))
1037 test "notifications are deleted if a remote user is deleted" do
1038 remote_user = insert(:user)
1039 local_user = insert(:user)
1042 "@context" => "https://www.w3.org/ns/activitystreams",
1044 "actor" => remote_user.ap_id,
1045 "id" => remote_user.ap_id <> "/activities/test",
1046 "to" => [local_user.ap_id],
1050 "id" => remote_user.ap_id <> "/objects/test",
1051 "content" => "Hello!",
1054 "type" => "Mention",
1055 "href" => local_user.ap_id,
1056 "name" => "@#{local_user.nickname}"
1059 "to" => [local_user.ap_id],
1061 "attributedTo" => remote_user.ap_id
1065 {:ok, _dm_activity} = Transmogrifier.handle_incoming(dm_message)
1067 refute Enum.empty?(Notification.for_user(local_user))
1069 delete_user_message = %{
1070 "@context" => "https://www.w3.org/ns/activitystreams",
1071 "id" => remote_user.ap_id <> "/activities/delete",
1072 "actor" => remote_user.ap_id,
1074 "object" => remote_user.ap_id
1077 remote_user_url = remote_user.ap_id
1080 %{method: :get, url: ^remote_user_url} ->
1081 %Tesla.Env{status: 404, body: ""}
1084 {:ok, _delete_activity} = Transmogrifier.handle_incoming(delete_user_message)
1085 ObanHelpers.perform_all()
1087 assert Enum.empty?(Notification.for_user(local_user))
1090 test "move activity generates a notification" do
1091 %{ap_id: old_ap_id} = old_user = insert(:user)
1092 %{ap_id: new_ap_id} = new_user = insert(:user, also_known_as: [old_ap_id])
1093 follower = insert(:user)
1094 other_follower = insert(:user, %{allow_following_move: false})
1096 User.follow(follower, old_user)
1097 User.follow(other_follower, old_user)
1099 Pleroma.Web.ActivityPub.ActivityPub.move(old_user, new_user)
1100 ObanHelpers.perform_all()
1105 data: %{"type" => "Move", "actor" => ^old_ap_id, "target" => ^new_ap_id}
1108 ] = Notification.for_user(follower)
1113 data: %{"type" => "Move", "actor" => ^old_ap_id, "target" => ^new_ap_id}
1116 ] = Notification.for_user(other_follower)
1120 describe "for_user" do
1122 user = insert(:user)
1124 {:ok, %{user: user}}
1127 test "it returns notifications for muted user without notifications", %{user: user} do
1128 muted = insert(:user)
1129 {:ok, _user_relationships} = User.mute(user, muted, %{notifications: false})
1131 {:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"})
1133 [notification] = Notification.for_user(user)
1135 assert notification.activity.object
1136 assert notification.seen
1139 test "it doesn't return notifications for muted user with notifications", %{user: user} do
1140 muted = insert(:user)
1141 {:ok, _user_relationships} = User.mute(user, muted)
1143 {:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"})
1145 assert Notification.for_user(user) == []
1148 test "it doesn't return notifications for blocked user", %{user: user} do
1149 blocked = insert(:user)
1150 {:ok, _user_relationship} = User.block(user, blocked)
1152 {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
1154 assert Notification.for_user(user) == []
1157 test "it doesn't return notifications for domain-blocked non-followed user", %{user: user} do
1158 blocked = insert(:user, ap_id: "http://some-domain.com")
1159 {:ok, user} = User.block_domain(user, "some-domain.com")
1161 {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
1163 assert Notification.for_user(user) == []
1166 test "it returns notifications for domain-blocked but followed user" do
1167 user = insert(:user)
1168 blocked = insert(:user, ap_id: "http://some-domain.com")
1170 {:ok, user} = User.block_domain(user, "some-domain.com")
1171 {:ok, _, _} = User.follow(user, blocked)
1173 {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
1175 assert length(Notification.for_user(user)) == 1
1178 test "it doesn't return notifications for muted thread", %{user: user} do
1179 another_user = insert(:user)
1181 {:ok, activity} = CommonAPI.post(another_user, %{status: "hey @#{user.nickname}"})
1183 {:ok, _} = Pleroma.ThreadMute.add_mute(user.id, activity.data["context"])
1184 assert Notification.for_user(user) == []
1187 test "it returns notifications from a muted user when with_muted is set", %{user: user} do
1188 muted = insert(:user)
1189 {:ok, _user_relationships} = User.mute(user, muted)
1191 {:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"})
1193 assert length(Notification.for_user(user, %{with_muted: true})) == 1
1196 test "it doesn't return notifications from a blocked user when with_muted is set", %{
1199 blocked = insert(:user)
1200 {:ok, _user_relationship} = User.block(user, blocked)
1202 {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
1204 assert Enum.empty?(Notification.for_user(user, %{with_muted: true}))
1207 test "when with_muted is set, " <>
1208 "it doesn't return notifications from a domain-blocked non-followed user",
1210 blocked = insert(:user, ap_id: "http://some-domain.com")
1211 {:ok, user} = User.block_domain(user, "some-domain.com")
1213 {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
1215 assert Enum.empty?(Notification.for_user(user, %{with_muted: true}))
1218 test "it returns notifications from muted threads when with_muted is set", %{user: user} do
1219 another_user = insert(:user)
1221 {:ok, activity} = CommonAPI.post(another_user, %{status: "hey @#{user.nickname}"})
1223 {:ok, _} = Pleroma.ThreadMute.add_mute(user.id, activity.data["context"])
1224 assert length(Notification.for_user(user, %{with_muted: true})) == 1
1227 test "it doesn't return notifications about mentions with filtered word", %{user: user} do
1228 insert(:filter, user: user, phrase: "cofe", hide: true)
1229 another_user = insert(:user)
1231 {:ok, _activity} = CommonAPI.post(another_user, %{status: "@#{user.nickname} got cofe?"})
1233 assert Enum.empty?(Notification.for_user(user))
1236 test "it returns notifications about mentions with not hidden filtered word", %{user: user} do
1237 insert(:filter, user: user, phrase: "test", hide: false)
1238 another_user = insert(:user)
1240 {:ok, _} = CommonAPI.post(another_user, %{status: "@#{user.nickname} test"})
1242 assert length(Notification.for_user(user)) == 1
1245 test "it returns notifications about favorites with filtered word", %{user: user} do
1246 insert(:filter, user: user, phrase: "cofe", hide: true)
1247 another_user = insert(:user)
1249 {:ok, activity} = CommonAPI.post(user, %{status: "Give me my cofe!"})
1250 {:ok, _} = CommonAPI.favorite(another_user, activity.id)
1252 assert length(Notification.for_user(user)) == 1
1255 test "it returns notifications when related object is without content and filters are defined",
1257 followed_user = insert(:user, is_locked: true)
1259 insert(:filter, user: followed_user, phrase: "test", hide: true)
1261 {:ok, _, _, _activity} = CommonAPI.follow(user, followed_user)
1262 refute FollowingRelationship.following?(user, followed_user)
1263 assert [notification] = Notification.for_user(followed_user)
1265 assert %{type: "follow_request"} =
1266 NotificationView.render("show.json", %{
1267 notification: notification,
1271 assert {:ok, _} = CommonAPI.accept_follow_request(user, followed_user)
1273 assert [notification] = Notification.for_user(followed_user)
1275 assert %{type: "follow"} =
1276 NotificationView.render("show.json", %{
1277 notification: notification,