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