0d2cacb57a6c465ff930b08f7f0b023d584c560e
[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 [user1.id, user3.id, user2.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, _, _, _}, 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, _, _, _}, 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 notification_id = notification.id
436 assert [%{id: ^notification_id}] = Notification.for_user(followed_user)
437 end
438
439 test "dismisses the notification on follow request rejection" do
440 user = insert(:user, is_locked: true)
441 follower = insert(:user)
442 {:ok, _, _, _follow_activity} = CommonAPI.follow(follower, user)
443 assert [_notification] = Notification.for_user(user)
444 {:ok, _follower} = CommonAPI.reject_follow_request(follower, user)
445 assert [] = Notification.for_user(user)
446 end
447 end
448
449 describe "get notification" do
450 test "it gets a notification that belongs to the user" do
451 user = insert(:user)
452 other_user = insert(:user)
453
454 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}"})
455
456 {:ok, [notification]} = Notification.create_notifications(activity)
457 {:ok, notification} = Notification.get(other_user, notification.id)
458
459 assert notification.user_id == other_user.id
460 end
461
462 test "it returns error if the notification doesn't belong to the user" do
463 user = insert(:user)
464 other_user = insert(:user)
465
466 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}"})
467
468 {:ok, [notification]} = Notification.create_notifications(activity)
469 {:error, _notification} = Notification.get(user, notification.id)
470 end
471 end
472
473 describe "dismiss notification" do
474 test "it dismisses a notification that belongs to the user" do
475 user = insert(:user)
476 other_user = insert(:user)
477
478 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}"})
479
480 {:ok, [notification]} = Notification.create_notifications(activity)
481 {:ok, notification} = Notification.dismiss(other_user, notification.id)
482
483 assert notification.user_id == other_user.id
484 end
485
486 test "it returns error if the notification doesn't belong to the user" do
487 user = insert(:user)
488 other_user = insert(:user)
489
490 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}"})
491
492 {:ok, [notification]} = Notification.create_notifications(activity)
493 {:error, _notification} = Notification.dismiss(user, notification.id)
494 end
495 end
496
497 describe "clear notification" do
498 test "it clears all notifications belonging to the user" do
499 user = insert(:user)
500 other_user = insert(:user)
501 third_user = insert(:user)
502
503 {:ok, activity} =
504 CommonAPI.post(user, %{
505 status: "hey @#{other_user.nickname} and @#{third_user.nickname} !"
506 })
507
508 {:ok, _notifs} = Notification.create_notifications(activity)
509
510 {:ok, activity} =
511 CommonAPI.post(user, %{
512 status: "hey again @#{other_user.nickname} and @#{third_user.nickname} !"
513 })
514
515 {:ok, _notifs} = Notification.create_notifications(activity)
516 Notification.clear(other_user)
517
518 assert Notification.for_user(other_user) == []
519 assert Notification.for_user(third_user) != []
520 end
521 end
522
523 describe "set_read_up_to()" do
524 test "it sets all notifications as read up to a specified notification ID" do
525 user = insert(:user)
526 other_user = insert(:user)
527
528 {:ok, _activity} =
529 CommonAPI.post(user, %{
530 status: "hey @#{other_user.nickname}!"
531 })
532
533 {:ok, _activity} =
534 CommonAPI.post(user, %{
535 status: "hey again @#{other_user.nickname}!"
536 })
537
538 [n2, n1] = Notification.for_user(other_user)
539
540 assert n2.id > n1.id
541
542 {:ok, _activity} =
543 CommonAPI.post(user, %{
544 status: "hey yet again @#{other_user.nickname}!"
545 })
546
547 [_, read_notification] = Notification.set_read_up_to(other_user, n2.id)
548
549 assert read_notification.activity.object
550
551 [n3, n2, n1] = Notification.for_user(other_user)
552
553 assert n1.seen == true
554 assert n2.seen == true
555 assert n3.seen == false
556
557 assert %Pleroma.Marker{} =
558 m =
559 Pleroma.Repo.get_by(
560 Pleroma.Marker,
561 user_id: other_user.id,
562 timeline: "notifications"
563 )
564
565 assert m.last_read_id == to_string(n2.id)
566 end
567 end
568
569 describe "for_user_since/2" do
570 defp days_ago(days) do
571 NaiveDateTime.add(
572 NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second),
573 -days * 60 * 60 * 24,
574 :second
575 )
576 end
577
578 test "Returns recent notifications" do
579 user1 = insert(:user)
580 user2 = insert(:user)
581
582 Enum.each(0..10, fn i ->
583 {:ok, _activity} =
584 CommonAPI.post(user1, %{
585 status: "hey ##{i} @#{user2.nickname}!"
586 })
587 end)
588
589 {old, new} = Enum.split(Notification.for_user(user2), 5)
590
591 Enum.each(old, fn notification ->
592 notification
593 |> cast(%{updated_at: days_ago(10)}, [:updated_at])
594 |> Pleroma.Repo.update!()
595 end)
596
597 recent_notifications_ids =
598 user2
599 |> Notification.for_user_since(
600 NaiveDateTime.add(NaiveDateTime.utc_now(), -5 * 86_400, :second)
601 )
602 |> Enum.map(& &1.id)
603
604 Enum.each(old, fn %{id: id} ->
605 refute id in recent_notifications_ids
606 end)
607
608 Enum.each(new, fn %{id: id} ->
609 assert id in recent_notifications_ids
610 end)
611 end
612 end
613
614 describe "notification target determination / get_notified_from_activity/2" do
615 test "it sends notifications to addressed users in new messages" do
616 user = insert(:user)
617 other_user = insert(:user)
618
619 {:ok, activity} =
620 CommonAPI.post(user, %{
621 status: "hey @#{other_user.nickname}!"
622 })
623
624 {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity)
625
626 assert other_user in enabled_receivers
627 end
628
629 test "it sends notifications to mentioned users in new messages" do
630 user = insert(:user)
631 other_user = insert(:user)
632
633 create_activity = %{
634 "@context" => "https://www.w3.org/ns/activitystreams",
635 "type" => "Create",
636 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
637 "actor" => user.ap_id,
638 "object" => %{
639 "type" => "Note",
640 "content" => "message with a Mention tag, but no explicit tagging",
641 "tag" => [
642 %{
643 "type" => "Mention",
644 "href" => other_user.ap_id,
645 "name" => other_user.nickname
646 }
647 ],
648 "attributedTo" => user.ap_id
649 }
650 }
651
652 {:ok, activity} = Transmogrifier.handle_incoming(create_activity)
653
654 {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity)
655
656 assert other_user in enabled_receivers
657 end
658
659 test "it does not send notifications to users who are only cc in new messages" do
660 user = insert(:user)
661 other_user = insert(:user)
662
663 create_activity = %{
664 "@context" => "https://www.w3.org/ns/activitystreams",
665 "type" => "Create",
666 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
667 "cc" => [other_user.ap_id],
668 "actor" => user.ap_id,
669 "object" => %{
670 "type" => "Note",
671 "content" => "hi everyone",
672 "attributedTo" => user.ap_id
673 }
674 }
675
676 {:ok, activity} = Transmogrifier.handle_incoming(create_activity)
677
678 {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity)
679
680 assert other_user not in enabled_receivers
681 end
682
683 test "it does not send notification to mentioned users in likes" do
684 user = insert(:user)
685 other_user = insert(:user)
686 third_user = insert(:user)
687
688 {:ok, activity_one} =
689 CommonAPI.post(user, %{
690 status: "hey @#{other_user.nickname}!"
691 })
692
693 {:ok, activity_two} = CommonAPI.favorite(third_user, activity_one.id)
694
695 {enabled_receivers, _disabled_receivers} =
696 Notification.get_notified_from_activity(activity_two)
697
698 assert other_user not in enabled_receivers
699 end
700
701 test "it only notifies the post's author in likes" do
702 user = insert(:user)
703 other_user = insert(:user)
704 third_user = insert(:user)
705
706 {:ok, activity_one} =
707 CommonAPI.post(user, %{
708 status: "hey @#{other_user.nickname}!"
709 })
710
711 {:ok, like_data, _} = Builder.like(third_user, activity_one.object)
712
713 {:ok, like, _} =
714 like_data
715 |> Map.put("to", [other_user.ap_id | like_data["to"]])
716 |> ActivityPub.persist(local: true)
717
718 {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(like)
719
720 assert other_user not in enabled_receivers
721 end
722
723 test "it does not send notification to mentioned users in announces" do
724 user = insert(:user)
725 other_user = insert(:user)
726 third_user = insert(:user)
727
728 {:ok, activity_one} =
729 CommonAPI.post(user, %{
730 status: "hey @#{other_user.nickname}!"
731 })
732
733 {:ok, activity_two} = CommonAPI.repeat(activity_one.id, third_user)
734
735 {enabled_receivers, _disabled_receivers} =
736 Notification.get_notified_from_activity(activity_two)
737
738 assert other_user not in enabled_receivers
739 end
740
741 test "it returns blocking recipient in disabled recipients list" do
742 user = insert(:user)
743 other_user = insert(:user)
744 {:ok, _user_relationship} = User.block(other_user, user)
745
746 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
747
748 {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
749
750 assert [] == enabled_receivers
751 assert [other_user] == disabled_receivers
752 end
753
754 test "it returns notification-muting recipient in disabled recipients list" do
755 user = insert(:user)
756 other_user = insert(:user)
757 {:ok, _user_relationships} = User.mute(other_user, user)
758
759 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
760
761 {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
762
763 assert [] == enabled_receivers
764 assert [other_user] == disabled_receivers
765 end
766
767 test "it returns thread-muting recipient in disabled recipients list" do
768 user = insert(:user)
769 other_user = insert(:user)
770
771 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
772
773 {:ok, _} = CommonAPI.add_mute(other_user, activity)
774
775 {:ok, same_context_activity} =
776 CommonAPI.post(user, %{
777 status: "hey-hey-hey @#{other_user.nickname}!",
778 in_reply_to_status_id: activity.id
779 })
780
781 {enabled_receivers, disabled_receivers} =
782 Notification.get_notified_from_activity(same_context_activity)
783
784 assert [other_user] == disabled_receivers
785 refute other_user in enabled_receivers
786 end
787
788 test "it returns non-following domain-blocking recipient in disabled recipients list" do
789 blocked_domain = "blocked.domain"
790 user = insert(:user, %{ap_id: "https://#{blocked_domain}/@actor"})
791 other_user = insert(:user)
792
793 {:ok, other_user} = User.block_domain(other_user, blocked_domain)
794
795 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
796
797 {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
798
799 assert [] == enabled_receivers
800 assert [other_user] == disabled_receivers
801 end
802
803 test "it returns following domain-blocking recipient in enabled recipients list" do
804 blocked_domain = "blocked.domain"
805 user = insert(:user, %{ap_id: "https://#{blocked_domain}/@actor"})
806 other_user = insert(:user)
807
808 {:ok, other_user} = User.block_domain(other_user, blocked_domain)
809 {:ok, other_user, user} = User.follow(other_user, user)
810
811 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
812
813 {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
814
815 assert [other_user] == enabled_receivers
816 assert [] == disabled_receivers
817 end
818 end
819
820 describe "notification lifecycle" do
821 test "liking an activity results in 1 notification, then 0 if the activity is deleted" do
822 user = insert(:user)
823 other_user = insert(:user)
824
825 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
826
827 assert Enum.empty?(Notification.for_user(user))
828
829 {:ok, _} = CommonAPI.favorite(other_user, activity.id)
830
831 assert length(Notification.for_user(user)) == 1
832
833 {:ok, _} = CommonAPI.delete(activity.id, user)
834
835 assert Enum.empty?(Notification.for_user(user))
836 end
837
838 test "liking an activity results in 1 notification, then 0 if the activity is unliked" do
839 user = insert(:user)
840 other_user = insert(:user)
841
842 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
843
844 assert Enum.empty?(Notification.for_user(user))
845
846 {:ok, _} = CommonAPI.favorite(other_user, activity.id)
847
848 assert length(Notification.for_user(user)) == 1
849
850 {:ok, _} = CommonAPI.unfavorite(activity.id, other_user)
851
852 assert Enum.empty?(Notification.for_user(user))
853 end
854
855 test "repeating an activity results in 1 notification, then 0 if the activity is deleted" do
856 user = insert(:user)
857 other_user = insert(:user)
858
859 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
860
861 assert Enum.empty?(Notification.for_user(user))
862
863 {:ok, _} = CommonAPI.repeat(activity.id, other_user)
864
865 assert length(Notification.for_user(user)) == 1
866
867 {:ok, _} = CommonAPI.delete(activity.id, user)
868
869 assert Enum.empty?(Notification.for_user(user))
870 end
871
872 test "repeating an activity results in 1 notification, then 0 if the activity is unrepeated" do
873 user = insert(:user)
874 other_user = insert(:user)
875
876 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
877
878 assert Enum.empty?(Notification.for_user(user))
879
880 {:ok, _} = CommonAPI.repeat(activity.id, other_user)
881
882 assert length(Notification.for_user(user)) == 1
883
884 {:ok, _} = CommonAPI.unrepeat(activity.id, other_user)
885
886 assert Enum.empty?(Notification.for_user(user))
887 end
888
889 test "liking an activity which is already deleted does not generate a notification" do
890 user = insert(:user)
891 other_user = insert(:user)
892
893 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
894
895 assert Enum.empty?(Notification.for_user(user))
896
897 {:ok, _deletion_activity} = CommonAPI.delete(activity.id, user)
898
899 assert Enum.empty?(Notification.for_user(user))
900
901 {:error, :not_found} = CommonAPI.favorite(other_user, activity.id)
902
903 assert Enum.empty?(Notification.for_user(user))
904 end
905
906 test "repeating an activity which is already deleted does not generate a notification" do
907 user = insert(:user)
908 other_user = insert(:user)
909
910 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
911
912 assert Enum.empty?(Notification.for_user(user))
913
914 {:ok, _deletion_activity} = CommonAPI.delete(activity.id, user)
915
916 assert Enum.empty?(Notification.for_user(user))
917
918 {:error, _} = CommonAPI.repeat(activity.id, other_user)
919
920 assert Enum.empty?(Notification.for_user(user))
921 end
922
923 test "replying to a deleted post without tagging does not generate a notification" do
924 user = insert(:user)
925 other_user = insert(:user)
926
927 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
928 {:ok, _deletion_activity} = CommonAPI.delete(activity.id, user)
929
930 {:ok, _reply_activity} =
931 CommonAPI.post(other_user, %{
932 status: "test reply",
933 in_reply_to_status_id: activity.id
934 })
935
936 assert Enum.empty?(Notification.for_user(user))
937 end
938
939 test "notifications are deleted if a local user is deleted" do
940 user = insert(:user)
941 other_user = insert(:user)
942
943 {:ok, _activity} =
944 CommonAPI.post(user, %{status: "hi @#{other_user.nickname}", visibility: "direct"})
945
946 refute Enum.empty?(Notification.for_user(other_user))
947
948 {:ok, job} = User.delete(user)
949 ObanHelpers.perform(job)
950
951 assert Enum.empty?(Notification.for_user(other_user))
952 end
953
954 test "notifications are deleted if a remote user is deleted" do
955 remote_user = insert(:user)
956 local_user = insert(:user)
957
958 dm_message = %{
959 "@context" => "https://www.w3.org/ns/activitystreams",
960 "type" => "Create",
961 "actor" => remote_user.ap_id,
962 "id" => remote_user.ap_id <> "/activities/test",
963 "to" => [local_user.ap_id],
964 "cc" => [],
965 "object" => %{
966 "type" => "Note",
967 "content" => "Hello!",
968 "tag" => [
969 %{
970 "type" => "Mention",
971 "href" => local_user.ap_id,
972 "name" => "@#{local_user.nickname}"
973 }
974 ],
975 "to" => [local_user.ap_id],
976 "cc" => [],
977 "attributedTo" => remote_user.ap_id
978 }
979 }
980
981 {:ok, _dm_activity} = Transmogrifier.handle_incoming(dm_message)
982
983 refute Enum.empty?(Notification.for_user(local_user))
984
985 delete_user_message = %{
986 "@context" => "https://www.w3.org/ns/activitystreams",
987 "id" => remote_user.ap_id <> "/activities/delete",
988 "actor" => remote_user.ap_id,
989 "type" => "Delete",
990 "object" => remote_user.ap_id
991 }
992
993 remote_user_url = remote_user.ap_id
994
995 Tesla.Mock.mock(fn
996 %{method: :get, url: ^remote_user_url} ->
997 %Tesla.Env{status: 404, body: ""}
998 end)
999
1000 {:ok, _delete_activity} = Transmogrifier.handle_incoming(delete_user_message)
1001 ObanHelpers.perform_all()
1002
1003 assert Enum.empty?(Notification.for_user(local_user))
1004 end
1005
1006 test "move activity generates a notification" do
1007 %{ap_id: old_ap_id} = old_user = insert(:user)
1008 %{ap_id: new_ap_id} = new_user = insert(:user, also_known_as: [old_ap_id])
1009 follower = insert(:user)
1010 other_follower = insert(:user, %{allow_following_move: false})
1011
1012 User.follow(follower, old_user)
1013 User.follow(other_follower, old_user)
1014
1015 Pleroma.Web.ActivityPub.ActivityPub.move(old_user, new_user)
1016 ObanHelpers.perform_all()
1017
1018 assert [
1019 %{
1020 activity: %{
1021 data: %{"type" => "Move", "actor" => ^old_ap_id, "target" => ^new_ap_id}
1022 }
1023 }
1024 ] = Notification.for_user(follower)
1025
1026 assert [
1027 %{
1028 activity: %{
1029 data: %{"type" => "Move", "actor" => ^old_ap_id, "target" => ^new_ap_id}
1030 }
1031 }
1032 ] = Notification.for_user(other_follower)
1033 end
1034 end
1035
1036 describe "for_user" do
1037 setup do
1038 user = insert(:user)
1039
1040 {:ok, %{user: user}}
1041 end
1042
1043 test "it returns notifications for muted user without notifications", %{user: user} do
1044 muted = insert(:user)
1045 {:ok, _user_relationships} = User.mute(user, muted, %{notifications: false})
1046
1047 {:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"})
1048
1049 [notification] = Notification.for_user(user)
1050
1051 assert notification.activity.object
1052 assert notification.seen
1053 end
1054
1055 test "it doesn't return notifications for muted user with notifications", %{user: user} do
1056 muted = insert(:user)
1057 {:ok, _user_relationships} = User.mute(user, muted)
1058
1059 {:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"})
1060
1061 assert Notification.for_user(user) == []
1062 end
1063
1064 test "it doesn't return notifications for blocked user", %{user: user} do
1065 blocked = insert(:user)
1066 {:ok, _user_relationship} = User.block(user, blocked)
1067
1068 {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
1069
1070 assert Notification.for_user(user) == []
1071 end
1072
1073 test "it doesn't return notifications for domain-blocked non-followed user", %{user: user} do
1074 blocked = insert(:user, ap_id: "http://some-domain.com")
1075 {:ok, user} = User.block_domain(user, "some-domain.com")
1076
1077 {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
1078
1079 assert Notification.for_user(user) == []
1080 end
1081
1082 test "it returns notifications for domain-blocked but followed user" do
1083 user = insert(:user)
1084 blocked = insert(:user, ap_id: "http://some-domain.com")
1085
1086 {:ok, user} = User.block_domain(user, "some-domain.com")
1087 {:ok, _, _} = User.follow(user, blocked)
1088
1089 {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
1090
1091 assert length(Notification.for_user(user)) == 1
1092 end
1093
1094 test "it doesn't return notifications for muted thread", %{user: user} do
1095 another_user = insert(:user)
1096
1097 {:ok, activity} = CommonAPI.post(another_user, %{status: "hey @#{user.nickname}"})
1098
1099 {:ok, _} = Pleroma.ThreadMute.add_mute(user.id, activity.data["context"])
1100 assert Notification.for_user(user) == []
1101 end
1102
1103 test "it returns notifications from a muted user when with_muted is set", %{user: user} do
1104 muted = insert(:user)
1105 {:ok, _user_relationships} = User.mute(user, muted)
1106
1107 {:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"})
1108
1109 assert length(Notification.for_user(user, %{with_muted: true})) == 1
1110 end
1111
1112 test "it doesn't return notifications from a blocked user when with_muted is set", %{
1113 user: user
1114 } do
1115 blocked = insert(:user)
1116 {:ok, _user_relationship} = User.block(user, blocked)
1117
1118 {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
1119
1120 assert Enum.empty?(Notification.for_user(user, %{with_muted: true}))
1121 end
1122
1123 test "when with_muted is set, " <>
1124 "it doesn't return notifications from a domain-blocked non-followed user",
1125 %{user: user} do
1126 blocked = insert(:user, ap_id: "http://some-domain.com")
1127 {:ok, user} = User.block_domain(user, "some-domain.com")
1128
1129 {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
1130
1131 assert Enum.empty?(Notification.for_user(user, %{with_muted: true}))
1132 end
1133
1134 test "it returns notifications from muted threads when with_muted is set", %{user: user} do
1135 another_user = insert(:user)
1136
1137 {:ok, activity} = CommonAPI.post(another_user, %{status: "hey @#{user.nickname}"})
1138
1139 {:ok, _} = Pleroma.ThreadMute.add_mute(user.id, activity.data["context"])
1140 assert length(Notification.for_user(user, %{with_muted: true})) == 1
1141 end
1142
1143 test "it doesn't return notifications about mentions with filtered word", %{user: user} do
1144 insert(:filter, user: user, phrase: "cofe", hide: true)
1145 another_user = insert(:user)
1146
1147 {:ok, _activity} = CommonAPI.post(another_user, %{status: "@#{user.nickname} got cofe?"})
1148
1149 assert Enum.empty?(Notification.for_user(user))
1150 end
1151
1152 test "it returns notifications about mentions with not hidden filtered word", %{user: user} do
1153 insert(:filter, user: user, phrase: "test", hide: false)
1154 another_user = insert(:user)
1155
1156 {:ok, _} = CommonAPI.post(another_user, %{status: "@#{user.nickname} test"})
1157
1158 assert length(Notification.for_user(user)) == 1
1159 end
1160
1161 test "it returns notifications about favorites with filtered word", %{user: user} do
1162 insert(:filter, user: user, phrase: "cofe", hide: true)
1163 another_user = insert(:user)
1164
1165 {:ok, activity} = CommonAPI.post(user, %{status: "Give me my cofe!"})
1166 {:ok, _} = CommonAPI.favorite(another_user, activity.id)
1167
1168 assert length(Notification.for_user(user)) == 1
1169 end
1170 end
1171 end