First
[anni] / test / pleroma / notification_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.NotificationTest do
6   use Pleroma.DataCase, async: false
7
8   import Pleroma.Factory
9   import Mock
10
11   alias Pleroma.FollowingRelationship
12   alias Pleroma.Notification
13   alias Pleroma.Repo
14   alias Pleroma.Tests.ObanHelpers
15   alias Pleroma.User
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
23
24   describe "create_notifications" do
25     test "never returns nil" do
26       user = insert(:user)
27       other_user = insert(:user, %{invisible: true})
28
29       {:ok, activity} = CommonAPI.post(user, %{status: "yeah"})
30       {:ok, activity} = CommonAPI.react_with_emoji(activity.id, other_user, "☕")
31
32       refute {:ok, [nil]} == Notification.create_notifications(activity)
33     end
34
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)
39
40       clear_config([:instance, :moderator_privileges], [])
41       {:ok, activity1} = CommonAPI.report(reporting_user, %{account_id: reported_user.id})
42       {:ok, []} = Notification.create_notifications(activity1)
43
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)
47
48       assert notification.user_id == moderator_user.id
49       assert notification.type == "pleroma:report"
50     end
51
52     test "suppresses notifications for own reports" do
53       clear_config([:instance, :admin_privileges], [:reports_manage_reports])
54
55       reporting_admin = insert(:user, is_admin: true)
56       reported_user = insert(:user)
57       other_admin = insert(:user, is_admin: true)
58
59       {:ok, activity} = CommonAPI.report(reporting_admin, %{account_id: reported_user.id})
60
61       {:ok, [notification]} = Notification.create_notifications(activity)
62
63       refute notification.user_id == reporting_admin.id
64       assert notification.user_id == other_admin.id
65       assert notification.type == "pleroma:report"
66     end
67
68     test "creates a notification for an emoji reaction" do
69       user = insert(:user)
70       other_user = insert(:user)
71
72       {:ok, activity} = CommonAPI.post(user, %{status: "yeah"})
73       {:ok, activity} = CommonAPI.react_with_emoji(activity.id, other_user, "☕")
74
75       {:ok, [notification]} = Notification.create_notifications(activity)
76
77       assert notification.user_id == user.id
78       assert notification.type == "pleroma:emoji_reaction"
79     end
80
81     test "notifies someone when they are directly addressed" do
82       user = insert(:user)
83       other_user = insert(:user)
84       third_user = insert(:user)
85
86       {:ok, activity} =
87         CommonAPI.post(user, %{
88           status: "hey @#{other_user.nickname} and @#{third_user.nickname}"
89         })
90
91       {:ok, [notification, other_notification]} = Notification.create_notifications(activity)
92
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
98
99       assert [%Pleroma.Marker{unread_count: 2}] =
100                Pleroma.Marker.get_markers(other_user, ["notifications"])
101     end
102
103     test "it creates a notification for subscribed users" do
104       user = insert(:user)
105       subscriber = insert(:user)
106
107       User.subscribe(subscriber, user)
108
109       {:ok, status} = CommonAPI.post(user, %{status: "Akariiiin"})
110       {:ok, [notification]} = Notification.create_notifications(status)
111
112       assert notification.user_id == subscriber.id
113     end
114
115     test "does not create a notification for subscribed users if status is a reply" do
116       user = insert(:user)
117       other_user = insert(:user)
118       subscriber = insert(:user)
119
120       User.subscribe(subscriber, other_user)
121
122       {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
123
124       {:ok, _reply_activity} =
125         CommonAPI.post(other_user, %{
126           status: "test reply",
127           in_reply_to_status_id: activity.id
128         })
129
130       user_notifications = Notification.for_user(user)
131       assert length(user_notifications) == 1
132
133       subscriber_notifications = Notification.for_user(subscriber)
134       assert Enum.empty?(subscriber_notifications)
135     end
136
137     test "it sends edited notifications to those who repeated a status" do
138       user = insert(:user)
139       repeated_user = insert(:user)
140       other_user = insert(:user)
141
142       {:ok, activity_one} =
143         CommonAPI.post(user, %{
144           status: "hey @#{other_user.nickname}!"
145         })
146
147       {:ok, _activity_two} = CommonAPI.repeat(activity_one.id, repeated_user)
148
149       {:ok, _edit_activity} =
150         CommonAPI.update(user, activity_one, %{
151           status: "hey @#{other_user.nickname}! mew mew"
152         })
153
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)
157     end
158   end
159
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)
164
165     {:ok, _, _} = CommonAPI.vote(user2, question, [0])
166     {:ok, _, _} = CommonAPI.vote(user3, question, [1])
167
168     {:ok, notifications} = Notification.create_poll_notifications(activity)
169
170     assert [user2.id, user3.id, user1.id] == Enum.map(notifications, & &1.user_id)
171   end
172
173   describe "CommonApi.post/2 notification-related functionality" do
174     test_with_mock "creates but does NOT send notification to blocker user",
175                    Push,
176                    [:passthrough],
177                    [] do
178       user = insert(:user)
179       blocker = insert(:user)
180       {:ok, _user_relationship} = User.block(blocker, user)
181
182       {:ok, _activity} = CommonAPI.post(user, %{status: "hey @#{blocker.nickname}!"})
183
184       blocker_id = blocker.id
185       assert [%Notification{user_id: ^blocker_id}] = Repo.all(Notification)
186       refute called(Push.send(:_))
187     end
188
189     test_with_mock "creates but does NOT send notification to notification-muter user",
190                    Push,
191                    [:passthrough],
192                    [] do
193       user = insert(:user)
194       muter = insert(:user)
195       {:ok, _user_relationships} = User.mute(muter, user)
196
197       {:ok, _activity} = CommonAPI.post(user, %{status: "hey @#{muter.nickname}!"})
198
199       muter_id = muter.id
200       assert [%Notification{user_id: ^muter_id}] = Repo.all(Notification)
201       refute called(Push.send(:_))
202     end
203
204     test_with_mock "creates but does NOT send notification to thread-muter user",
205                    Push,
206                    [:passthrough],
207                    [] do
208       user = insert(:user)
209       thread_muter = insert(:user)
210
211       {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{thread_muter.nickname}!"})
212
213       {:ok, _} = CommonAPI.add_mute(thread_muter, activity)
214
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
219         })
220
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))
223
224       pre_mute_notification_id = pre_mute_notification.id
225       post_mute_notification_id = post_mute_notification.id
226
227       assert called(
228                Push.send(
229                  :meck.is(fn
230                    %Notification{id: ^pre_mute_notification_id} -> true
231                    _ -> false
232                  end)
233                )
234              )
235
236       refute called(
237                Push.send(
238                  :meck.is(fn
239                    %Notification{id: ^post_mute_notification_id} -> true
240                    _ -> false
241                  end)
242                )
243              )
244     end
245   end
246
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"])
251
252       task =
253         Task.async(fn ->
254           {:ok, _topic} = Streamer.get_topic_and_add_socket("user", user, oauth_token)
255           assert_receive {:render_with_user, _, _, _}, 4_000
256         end)
257
258       task_user_notification =
259         Task.async(fn ->
260           {:ok, _topic} =
261             Streamer.get_topic_and_add_socket("user:notification", user, oauth_token)
262
263           assert_receive {:render_with_user, _, _, _}, 4_000
264         end)
265
266       activity = insert(:note_activity)
267
268       notify = Notification.create_notification(activity, user)
269       assert notify.user_id == user.id
270       Task.await(task)
271       Task.await(task_user_notification)
272     end
273
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"])
277       user = insert(:user)
278       {:ok, _user_relationship} = User.block(user, author)
279
280       assert Notification.create_notification(activity, user)
281     end
282
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}"})
289
290       notification = Notification.create_notification(activity, muter)
291
292       assert notification.id
293       assert notification.seen
294     end
295
296     test "notification created if user is muted without notifications" do
297       muter = insert(:user)
298       muted = insert(:user)
299
300       {:ok, _user_relationships} = User.mute(muter, muted, %{notifications: false})
301
302       {:ok, activity} = CommonAPI.post(muted, %{status: "Hi @#{muter.nickname}"})
303
304       assert Notification.create_notification(activity, muter)
305     end
306
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)
312
313       {:ok, activity} =
314         CommonAPI.post(other_user, %{
315           status: "Hi @#{muter.nickname}",
316           in_reply_to_status_id: activity.id
317         })
318
319       notification = Notification.create_notification(activity, muter)
320
321       assert notification.id
322       assert notification.seen
323     end
324
325     test "it disables notifications from strangers" do
326       follower = insert(:user)
327
328       followed =
329         insert(:user,
330           notification_settings: %Pleroma.User.NotificationSetting{block_from_strangers: true}
331         )
332
333       {:ok, activity} = CommonAPI.post(follower, %{status: "hey @#{followed.nickname}"})
334       refute Notification.create_notification(activity, followed)
335     end
336
337     test "it disables notifications from non-followees" do
338       follower = insert(:user)
339
340       followed =
341         insert(:user,
342           notification_settings: %Pleroma.User.NotificationSetting{block_from_strangers: true}
343         )
344
345       CommonAPI.follow(follower, followed)
346       {:ok, activity} = CommonAPI.post(follower, %{status: "hey @#{followed.nickname}"})
347       refute Notification.create_notification(activity, followed)
348     end
349
350     test "it allows notifications from followees" do
351       poster = insert(:user)
352
353       receiver =
354         insert(:user,
355           notification_settings: %Pleroma.User.NotificationSetting{block_from_strangers: true}
356         )
357
358       CommonAPI.follow(receiver, poster)
359       {:ok, activity} = CommonAPI.post(poster, %{status: "hey @#{receiver.nickname}"})
360       assert Notification.create_notification(activity, receiver)
361     end
362
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"])
366
367       refute Notification.create_notification(activity, author)
368     end
369
370     test "it doesn't create duplicate notifications for follow+subscribed users" do
371       user = insert(:user)
372       subscriber = insert(:user)
373
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)
378     end
379
380     test "it doesn't create subscription notifications if the recipient cannot see the status" do
381       user = insert(:user)
382       subscriber = insert(:user)
383
384       User.subscribe(subscriber, user)
385
386       {:ok, status} = CommonAPI.post(user, %{status: "inwisible", visibility: "direct"})
387
388       assert {:ok, []} == Notification.create_notifications(status)
389     end
390
391     test "it disables notifications from people who are invisible" do
392       author = insert(:user, invisible: true)
393       user = insert(:user)
394
395       {:ok, status} = CommonAPI.post(author, %{status: "hey @#{user.nickname}"})
396       refute Notification.create_notification(status, user)
397     end
398
399     test "it doesn't create notifications if content matches with an irreversible filter" do
400       user = insert(:user)
401       subscriber = insert(:user)
402
403       User.subscribe(subscriber, user)
404       insert(:filter, user: subscriber, phrase: "cofe", hide: true)
405
406       {:ok, status} = CommonAPI.post(user, %{status: "got cofe?"})
407
408       assert {:ok, []} == Notification.create_notifications(status)
409     end
410
411     test "it creates notifications if content matches with a not irreversible filter" do
412       user = insert(:user)
413       subscriber = insert(:user)
414
415       User.subscribe(subscriber, user)
416       insert(:filter, user: subscriber, phrase: "cofe", hide: false)
417
418       {:ok, status} = CommonAPI.post(user, %{status: "got cofe?"})
419       {:ok, [notification]} = Notification.create_notifications(status)
420
421       assert notification
422       refute notification.seen
423     end
424
425     test "it creates notifications when someone likes user's status with a filtered word" do
426       user = insert(:user)
427       other_user = insert(:user)
428       insert(:filter, user: user, phrase: "tesla", hide: true)
429
430       {:ok, activity_one} = CommonAPI.post(user, %{status: "wow tesla"})
431       {:ok, activity_two} = CommonAPI.favorite(other_user, activity_one.id)
432
433       {:ok, [notification]} = Notification.create_notifications(activity_two)
434
435       assert notification
436       refute notification.seen
437     end
438   end
439
440   describe "follow / follow_request notifications" do
441     test "it creates `follow` notification for approved Follow activity" do
442       user = insert(:user)
443       followed_user = insert(:user, is_locked: false)
444
445       {:ok, _, _, _activity} = CommonAPI.follow(user, followed_user)
446       assert FollowingRelationship.following?(user, followed_user)
447       assert [notification] = Notification.for_user(followed_user)
448
449       assert %{type: "follow"} =
450                NotificationView.render("show.json", %{
451                  notification: notification,
452                  for: followed_user
453                })
454     end
455
456     test "it creates `follow_request` notification for pending Follow activity" do
457       user = insert(:user)
458       followed_user = insert(:user, is_locked: true)
459
460       {:ok, _, _, _activity} = CommonAPI.follow(user, followed_user)
461       refute FollowingRelationship.following?(user, followed_user)
462       assert [notification] = Notification.for_user(followed_user)
463
464       render_opts = %{notification: notification, for: followed_user}
465       assert %{type: "follow_request"} = NotificationView.render("show.json", render_opts)
466
467       # After request is accepted, the same notification is rendered with type "follow":
468       assert {:ok, _} = CommonAPI.accept_follow_request(user, followed_user)
469
470       notification =
471         Repo.get(Notification, notification.id)
472         |> Repo.preload(:activity)
473
474       assert %{type: "follow"} =
475                NotificationView.render("show.json", notification: notification, for: followed_user)
476     end
477
478     test "it doesn't create a notification for follow-unfollow-follow chains" do
479       user = insert(:user)
480       followed_user = insert(:user, is_locked: false)
481
482       {:ok, _, _, _activity} = CommonAPI.follow(user, followed_user)
483       assert FollowingRelationship.following?(user, followed_user)
484       assert [notification] = Notification.for_user(followed_user)
485
486       CommonAPI.unfollow(user, followed_user)
487       {:ok, _, _, _activity_dupe} = CommonAPI.follow(user, followed_user)
488
489       notification_id = notification.id
490       assert [%{id: ^notification_id}] = Notification.for_user(followed_user)
491     end
492
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)
500     end
501   end
502
503   describe "get notification" do
504     test "it gets a notification that belongs to the user" do
505       user = insert(:user)
506       other_user = insert(:user)
507
508       {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}"})
509
510       {:ok, [notification]} = Notification.create_notifications(activity)
511       {:ok, notification} = Notification.get(other_user, notification.id)
512
513       assert notification.user_id == other_user.id
514     end
515
516     test "it returns error if the notification doesn't belong to the user" do
517       user = insert(:user)
518       other_user = insert(:user)
519
520       {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}"})
521
522       {:ok, [notification]} = Notification.create_notifications(activity)
523       {:error, _notification} = Notification.get(user, notification.id)
524     end
525   end
526
527   describe "dismiss notification" do
528     test "it dismisses a notification that belongs to the user" do
529       user = insert(:user)
530       other_user = insert(:user)
531
532       {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}"})
533
534       {:ok, [notification]} = Notification.create_notifications(activity)
535       {:ok, notification} = Notification.dismiss(other_user, notification.id)
536
537       assert notification.user_id == other_user.id
538     end
539
540     test "it returns error if the notification doesn't belong to the user" do
541       user = insert(:user)
542       other_user = insert(:user)
543
544       {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}"})
545
546       {:ok, [notification]} = Notification.create_notifications(activity)
547       {:error, _notification} = Notification.dismiss(user, notification.id)
548     end
549   end
550
551   describe "clear notification" do
552     test "it clears all notifications belonging to the user" do
553       user = insert(:user)
554       other_user = insert(:user)
555       third_user = insert(:user)
556
557       {:ok, activity} =
558         CommonAPI.post(user, %{
559           status: "hey @#{other_user.nickname} and @#{third_user.nickname} !"
560         })
561
562       {:ok, _notifs} = Notification.create_notifications(activity)
563
564       {:ok, activity} =
565         CommonAPI.post(user, %{
566           status: "hey again @#{other_user.nickname} and @#{third_user.nickname} !"
567         })
568
569       {:ok, _notifs} = Notification.create_notifications(activity)
570       Notification.clear(other_user)
571
572       assert Notification.for_user(other_user) == []
573       assert Notification.for_user(third_user) != []
574     end
575   end
576
577   describe "set_read_up_to()" do
578     test "it sets all notifications as read up to a specified notification ID" do
579       user = insert(:user)
580       other_user = insert(:user)
581
582       {:ok, _activity} =
583         CommonAPI.post(user, %{
584           status: "hey @#{other_user.nickname}!"
585         })
586
587       {:ok, _activity} =
588         CommonAPI.post(user, %{
589           status: "hey again @#{other_user.nickname}!"
590         })
591
592       [n2, n1] = Notification.for_user(other_user)
593
594       assert n2.id > n1.id
595
596       {:ok, _activity} =
597         CommonAPI.post(user, %{
598           status: "hey yet again @#{other_user.nickname}!"
599         })
600
601       [_, read_notification] = Notification.set_read_up_to(other_user, n2.id)
602
603       assert read_notification.activity.object
604
605       [n3, n2, n1] = Notification.for_user(other_user)
606
607       assert n1.seen == true
608       assert n2.seen == true
609       assert n3.seen == false
610
611       assert %Pleroma.Marker{} =
612                m =
613                Pleroma.Repo.get_by(
614                  Pleroma.Marker,
615                  user_id: other_user.id,
616                  timeline: "notifications"
617                )
618
619       assert m.last_read_id == to_string(n2.id)
620     end
621   end
622
623   describe "for_user_since/2" do
624     defp days_ago(days) do
625       NaiveDateTime.add(
626         NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second),
627         -days * 60 * 60 * 24,
628         :second
629       )
630     end
631
632     test "Returns recent notifications" do
633       user1 = insert(:user)
634       user2 = insert(:user)
635
636       Enum.each(0..10, fn i ->
637         {:ok, _activity} =
638           CommonAPI.post(user1, %{
639             status: "hey ##{i} @#{user2.nickname}!"
640           })
641       end)
642
643       {old, new} = Enum.split(Notification.for_user(user2), 5)
644
645       Enum.each(old, fn notification ->
646         notification
647         |> cast(%{updated_at: days_ago(10)}, [:updated_at])
648         |> Pleroma.Repo.update!()
649       end)
650
651       recent_notifications_ids =
652         user2
653         |> Notification.for_user_since(
654           NaiveDateTime.add(NaiveDateTime.utc_now(), -5 * 86_400, :second)
655         )
656         |> Enum.map(& &1.id)
657
658       Enum.each(old, fn %{id: id} ->
659         refute id in recent_notifications_ids
660       end)
661
662       Enum.each(new, fn %{id: id} ->
663         assert id in recent_notifications_ids
664       end)
665     end
666   end
667
668   describe "notification target determination / get_notified_from_activity/2" do
669     test "it sends notifications to addressed users in new messages" do
670       user = insert(:user)
671       other_user = insert(:user)
672
673       {:ok, activity} =
674         CommonAPI.post(user, %{
675           status: "hey @#{other_user.nickname}!"
676         })
677
678       {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity)
679
680       assert other_user in enabled_receivers
681     end
682
683     test "it sends notifications to mentioned users in new messages" do
684       user = insert(:user)
685       other_user = insert(:user)
686
687       create_activity = %{
688         "@context" => "https://www.w3.org/ns/activitystreams",
689         "type" => "Create",
690         "to" => ["https://www.w3.org/ns/activitystreams#Public"],
691         "actor" => user.ap_id,
692         "object" => %{
693           "type" => "Note",
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",
697           "tag" => [
698             %{
699               "type" => "Mention",
700               "href" => other_user.ap_id,
701               "name" => other_user.nickname
702             }
703           ],
704           "attributedTo" => user.ap_id
705         }
706       }
707
708       {:ok, activity} = Transmogrifier.handle_incoming(create_activity)
709
710       {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity)
711
712       assert other_user in enabled_receivers
713     end
714
715     test "it does not send notifications to users who are only cc in new messages" do
716       user = insert(:user)
717       other_user = insert(:user)
718
719       create_activity = %{
720         "@context" => "https://www.w3.org/ns/activitystreams",
721         "type" => "Create",
722         "to" => ["https://www.w3.org/ns/activitystreams#Public"],
723         "cc" => [other_user.ap_id],
724         "actor" => user.ap_id,
725         "object" => %{
726           "type" => "Note",
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
732         }
733       }
734
735       {:ok, activity} = Transmogrifier.handle_incoming(create_activity)
736
737       {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity)
738
739       assert other_user not in enabled_receivers
740     end
741
742     test "it does not send notification to mentioned users in likes" do
743       user = insert(:user)
744       other_user = insert(:user)
745       third_user = insert(:user)
746
747       {:ok, activity_one} =
748         CommonAPI.post(user, %{
749           status: "hey @#{other_user.nickname}!"
750         })
751
752       {:ok, activity_two} = CommonAPI.favorite(third_user, activity_one.id)
753
754       {enabled_receivers, _disabled_receivers} =
755         Notification.get_notified_from_activity(activity_two)
756
757       assert other_user not in enabled_receivers
758     end
759
760     test "it only notifies the post's author in likes" do
761       user = insert(:user)
762       other_user = insert(:user)
763       third_user = insert(:user)
764
765       {:ok, activity_one} =
766         CommonAPI.post(user, %{
767           status: "hey @#{other_user.nickname}!"
768         })
769
770       {:ok, like_data, _} = Builder.like(third_user, activity_one.object)
771
772       {:ok, like, _} =
773         like_data
774         |> Map.put("to", [other_user.ap_id | like_data["to"]])
775         |> ActivityPub.persist(local: true)
776
777       {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(like)
778
779       assert other_user not in enabled_receivers
780     end
781
782     test "it does not send notification to mentioned users in announces" do
783       user = insert(:user)
784       other_user = insert(:user)
785       third_user = insert(:user)
786
787       {:ok, activity_one} =
788         CommonAPI.post(user, %{
789           status: "hey @#{other_user.nickname}!"
790         })
791
792       {:ok, activity_two} = CommonAPI.repeat(activity_one.id, third_user)
793
794       {enabled_receivers, _disabled_receivers} =
795         Notification.get_notified_from_activity(activity_two)
796
797       assert other_user not in enabled_receivers
798     end
799
800     test "it returns blocking recipient in disabled recipients list" do
801       user = insert(:user)
802       other_user = insert(:user)
803       {:ok, _user_relationship} = User.block(other_user, user)
804
805       {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
806
807       {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
808
809       assert [] == enabled_receivers
810       assert [other_user] == disabled_receivers
811     end
812
813     test "it returns notification-muting recipient in disabled recipients list" do
814       user = insert(:user)
815       other_user = insert(:user)
816       {:ok, _user_relationships} = User.mute(other_user, user)
817
818       {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
819
820       {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
821
822       assert [] == enabled_receivers
823       assert [other_user] == disabled_receivers
824     end
825
826     test "it returns thread-muting recipient in disabled recipients list" do
827       user = insert(:user)
828       other_user = insert(:user)
829
830       {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
831
832       {:ok, _} = CommonAPI.add_mute(other_user, activity)
833
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
838         })
839
840       {enabled_receivers, disabled_receivers} =
841         Notification.get_notified_from_activity(same_context_activity)
842
843       assert [other_user] == disabled_receivers
844       refute other_user in enabled_receivers
845     end
846
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)
851
852       {:ok, other_user} = User.block_domain(other_user, blocked_domain)
853
854       {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
855
856       {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
857
858       assert [] == enabled_receivers
859       assert [other_user] == disabled_receivers
860     end
861
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)
866
867       {:ok, other_user} = User.block_domain(other_user, blocked_domain)
868       {:ok, other_user, user} = User.follow(other_user, user)
869
870       {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
871
872       {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
873
874       assert [other_user] == enabled_receivers
875       assert [] == disabled_receivers
876     end
877
878     test "it sends edited notifications to those who repeated a status" do
879       user = insert(:user)
880       repeated_user = insert(:user)
881       other_user = insert(:user)
882
883       {:ok, activity_one} =
884         CommonAPI.post(user, %{
885           status: "hey @#{other_user.nickname}!"
886         })
887
888       {:ok, _activity_two} = CommonAPI.repeat(activity_one.id, repeated_user)
889
890       {:ok, edit_activity} =
891         CommonAPI.update(user, activity_one, %{
892           status: "hey @#{other_user.nickname}! mew mew"
893         })
894
895       {enabled_receivers, _disabled_receivers} =
896         Notification.get_notified_from_activity(edit_activity)
897
898       assert repeated_user in enabled_receivers
899       assert other_user not in enabled_receivers
900     end
901   end
902
903   describe "notification lifecycle" do
904     test "liking an activity results in 1 notification, then 0 if the activity is deleted" do
905       user = insert(:user)
906       other_user = insert(:user)
907
908       {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
909
910       assert Enum.empty?(Notification.for_user(user))
911
912       {:ok, _} = CommonAPI.favorite(other_user, activity.id)
913
914       assert length(Notification.for_user(user)) == 1
915
916       {:ok, _} = CommonAPI.delete(activity.id, user)
917
918       assert Enum.empty?(Notification.for_user(user))
919     end
920
921     test "liking an activity results in 1 notification, then 0 if the activity is unliked" do
922       user = insert(:user)
923       other_user = insert(:user)
924
925       {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
926
927       assert Enum.empty?(Notification.for_user(user))
928
929       {:ok, _} = CommonAPI.favorite(other_user, activity.id)
930
931       assert length(Notification.for_user(user)) == 1
932
933       {:ok, _} = CommonAPI.unfavorite(activity.id, other_user)
934
935       assert Enum.empty?(Notification.for_user(user))
936     end
937
938     test "repeating an activity results in 1 notification, then 0 if the activity is deleted" do
939       user = insert(:user)
940       other_user = insert(:user)
941
942       {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
943
944       assert Enum.empty?(Notification.for_user(user))
945
946       {:ok, _} = CommonAPI.repeat(activity.id, other_user)
947
948       assert length(Notification.for_user(user)) == 1
949
950       {:ok, _} = CommonAPI.delete(activity.id, user)
951
952       assert Enum.empty?(Notification.for_user(user))
953     end
954
955     test "repeating an activity results in 1 notification, then 0 if the activity is unrepeated" do
956       user = insert(:user)
957       other_user = insert(:user)
958
959       {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
960
961       assert Enum.empty?(Notification.for_user(user))
962
963       {:ok, _} = CommonAPI.repeat(activity.id, other_user)
964
965       assert length(Notification.for_user(user)) == 1
966
967       {:ok, _} = CommonAPI.unrepeat(activity.id, other_user)
968
969       assert Enum.empty?(Notification.for_user(user))
970     end
971
972     test "liking an activity which is already deleted does not generate a notification" do
973       user = insert(:user)
974       other_user = insert(:user)
975
976       {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
977
978       assert Enum.empty?(Notification.for_user(user))
979
980       {:ok, _deletion_activity} = CommonAPI.delete(activity.id, user)
981
982       assert Enum.empty?(Notification.for_user(user))
983
984       {:error, :not_found} = CommonAPI.favorite(other_user, activity.id)
985
986       assert Enum.empty?(Notification.for_user(user))
987     end
988
989     test "repeating an activity which is already deleted does not generate a notification" do
990       user = insert(:user)
991       other_user = insert(:user)
992
993       {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
994
995       assert Enum.empty?(Notification.for_user(user))
996
997       {:ok, _deletion_activity} = CommonAPI.delete(activity.id, user)
998
999       assert Enum.empty?(Notification.for_user(user))
1000
1001       {:error, _} = CommonAPI.repeat(activity.id, other_user)
1002
1003       assert Enum.empty?(Notification.for_user(user))
1004     end
1005
1006     test "replying to a deleted post without tagging does not generate a notification" do
1007       user = insert(:user)
1008       other_user = insert(:user)
1009
1010       {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
1011       {:ok, _deletion_activity} = CommonAPI.delete(activity.id, user)
1012
1013       {:ok, _reply_activity} =
1014         CommonAPI.post(other_user, %{
1015           status: "test reply",
1016           in_reply_to_status_id: activity.id
1017         })
1018
1019       assert Enum.empty?(Notification.for_user(user))
1020     end
1021
1022     test "notifications are deleted if a local user is deleted" do
1023       user = insert(:user)
1024       other_user = insert(:user)
1025
1026       {:ok, _activity} =
1027         CommonAPI.post(user, %{status: "hi @#{other_user.nickname}", visibility: "direct"})
1028
1029       refute Enum.empty?(Notification.for_user(other_user))
1030
1031       {:ok, job} = User.delete(user)
1032       ObanHelpers.perform(job)
1033
1034       assert Enum.empty?(Notification.for_user(other_user))
1035     end
1036
1037     test "notifications are deleted if a remote user is deleted" do
1038       remote_user = insert(:user)
1039       local_user = insert(:user)
1040
1041       dm_message = %{
1042         "@context" => "https://www.w3.org/ns/activitystreams",
1043         "type" => "Create",
1044         "actor" => remote_user.ap_id,
1045         "id" => remote_user.ap_id <> "/activities/test",
1046         "to" => [local_user.ap_id],
1047         "cc" => [],
1048         "object" => %{
1049           "type" => "Note",
1050           "id" => remote_user.ap_id <> "/objects/test",
1051           "content" => "Hello!",
1052           "tag" => [
1053             %{
1054               "type" => "Mention",
1055               "href" => local_user.ap_id,
1056               "name" => "@#{local_user.nickname}"
1057             }
1058           ],
1059           "to" => [local_user.ap_id],
1060           "cc" => [],
1061           "attributedTo" => remote_user.ap_id
1062         }
1063       }
1064
1065       {:ok, _dm_activity} = Transmogrifier.handle_incoming(dm_message)
1066
1067       refute Enum.empty?(Notification.for_user(local_user))
1068
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,
1073         "type" => "Delete",
1074         "object" => remote_user.ap_id
1075       }
1076
1077       remote_user_url = remote_user.ap_id
1078
1079       Tesla.Mock.mock(fn
1080         %{method: :get, url: ^remote_user_url} ->
1081           %Tesla.Env{status: 404, body: ""}
1082       end)
1083
1084       {:ok, _delete_activity} = Transmogrifier.handle_incoming(delete_user_message)
1085       ObanHelpers.perform_all()
1086
1087       assert Enum.empty?(Notification.for_user(local_user))
1088     end
1089
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})
1095
1096       User.follow(follower, old_user)
1097       User.follow(other_follower, old_user)
1098
1099       Pleroma.Web.ActivityPub.ActivityPub.move(old_user, new_user)
1100       ObanHelpers.perform_all()
1101
1102       assert [
1103                %{
1104                  activity: %{
1105                    data: %{"type" => "Move", "actor" => ^old_ap_id, "target" => ^new_ap_id}
1106                  }
1107                }
1108              ] = Notification.for_user(follower)
1109
1110       assert [
1111                %{
1112                  activity: %{
1113                    data: %{"type" => "Move", "actor" => ^old_ap_id, "target" => ^new_ap_id}
1114                  }
1115                }
1116              ] = Notification.for_user(other_follower)
1117     end
1118   end
1119
1120   describe "for_user" do
1121     setup do
1122       user = insert(:user)
1123
1124       {:ok, %{user: user}}
1125     end
1126
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})
1130
1131       {:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"})
1132
1133       [notification] = Notification.for_user(user)
1134
1135       assert notification.activity.object
1136       assert notification.seen
1137     end
1138
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)
1142
1143       {:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"})
1144
1145       assert Notification.for_user(user) == []
1146     end
1147
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)
1151
1152       {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
1153
1154       assert Notification.for_user(user) == []
1155     end
1156
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")
1160
1161       {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
1162
1163       assert Notification.for_user(user) == []
1164     end
1165
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")
1169
1170       {:ok, user} = User.block_domain(user, "some-domain.com")
1171       {:ok, _, _} = User.follow(user, blocked)
1172
1173       {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
1174
1175       assert length(Notification.for_user(user)) == 1
1176     end
1177
1178     test "it doesn't return notifications for muted thread", %{user: user} do
1179       another_user = insert(:user)
1180
1181       {:ok, activity} = CommonAPI.post(another_user, %{status: "hey @#{user.nickname}"})
1182
1183       {:ok, _} = Pleroma.ThreadMute.add_mute(user.id, activity.data["context"])
1184       assert Notification.for_user(user) == []
1185     end
1186
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)
1190
1191       {:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"})
1192
1193       assert length(Notification.for_user(user, %{with_muted: true})) == 1
1194     end
1195
1196     test "it doesn't return notifications from a blocked user when with_muted is set", %{
1197       user: user
1198     } do
1199       blocked = insert(:user)
1200       {:ok, _user_relationship} = User.block(user, blocked)
1201
1202       {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
1203
1204       assert Enum.empty?(Notification.for_user(user, %{with_muted: true}))
1205     end
1206
1207     test "when with_muted is set, " <>
1208            "it doesn't return notifications from a domain-blocked non-followed user",
1209          %{user: user} do
1210       blocked = insert(:user, ap_id: "http://some-domain.com")
1211       {:ok, user} = User.block_domain(user, "some-domain.com")
1212
1213       {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
1214
1215       assert Enum.empty?(Notification.for_user(user, %{with_muted: true}))
1216     end
1217
1218     test "it returns notifications from muted threads when with_muted is set", %{user: user} do
1219       another_user = insert(:user)
1220
1221       {:ok, activity} = CommonAPI.post(another_user, %{status: "hey @#{user.nickname}"})
1222
1223       {:ok, _} = Pleroma.ThreadMute.add_mute(user.id, activity.data["context"])
1224       assert length(Notification.for_user(user, %{with_muted: true})) == 1
1225     end
1226
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)
1230
1231       {:ok, _activity} = CommonAPI.post(another_user, %{status: "@#{user.nickname} got cofe?"})
1232
1233       assert Enum.empty?(Notification.for_user(user))
1234     end
1235
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)
1239
1240       {:ok, _} = CommonAPI.post(another_user, %{status: "@#{user.nickname} test"})
1241
1242       assert length(Notification.for_user(user)) == 1
1243     end
1244
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)
1248
1249       {:ok, activity} = CommonAPI.post(user, %{status: "Give me my cofe!"})
1250       {:ok, _} = CommonAPI.favorite(another_user, activity.id)
1251
1252       assert length(Notification.for_user(user)) == 1
1253     end
1254
1255     test "it returns notifications when related object is without content and filters are defined",
1256          %{user: user} do
1257       followed_user = insert(:user, is_locked: true)
1258
1259       insert(:filter, user: followed_user, phrase: "test", hide: true)
1260
1261       {:ok, _, _, _activity} = CommonAPI.follow(user, followed_user)
1262       refute FollowingRelationship.following?(user, followed_user)
1263       assert [notification] = Notification.for_user(followed_user)
1264
1265       assert %{type: "follow_request"} =
1266                NotificationView.render("show.json", %{
1267                  notification: notification,
1268                  for: followed_user
1269                })
1270
1271       assert {:ok, _} = CommonAPI.accept_follow_request(user, followed_user)
1272
1273       assert [notification] = Notification.for_user(followed_user)
1274
1275       assert %{type: "follow"} =
1276                NotificationView.render("show.json", %{
1277                  notification: notification,
1278                  for: followed_user
1279                })
1280     end
1281   end
1282 end