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