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