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