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