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