abf1b04109255ba42a70031cdb6a9167e93f61c8
[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 test "move activity generates a notification" do
994 %{ap_id: old_ap_id} = old_user = insert(:user)
995 %{ap_id: new_ap_id} = new_user = insert(:user, also_known_as: [old_ap_id])
996 follower = insert(:user)
997 other_follower = insert(:user, %{allow_following_move: false})
998
999 User.follow(follower, old_user)
1000 User.follow(other_follower, old_user)
1001
1002 Pleroma.Web.ActivityPub.ActivityPub.move(old_user, new_user)
1003 ObanHelpers.perform_all()
1004
1005 assert [
1006 %{
1007 activity: %{
1008 data: %{"type" => "Move", "actor" => ^old_ap_id, "target" => ^new_ap_id}
1009 }
1010 }
1011 ] = Notification.for_user(follower)
1012
1013 assert [
1014 %{
1015 activity: %{
1016 data: %{"type" => "Move", "actor" => ^old_ap_id, "target" => ^new_ap_id}
1017 }
1018 }
1019 ] = Notification.for_user(other_follower)
1020 end
1021 end
1022
1023 describe "for_user" do
1024 setup do
1025 user = insert(:user)
1026
1027 {:ok, %{user: user}}
1028 end
1029
1030 test "it returns notifications for muted user without notifications", %{user: user} do
1031 muted = insert(:user)
1032 {:ok, _user_relationships} = User.mute(user, muted, %{notifications: false})
1033
1034 {:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"})
1035
1036 [notification] = Notification.for_user(user)
1037
1038 assert notification.activity.object
1039 assert notification.seen
1040 end
1041
1042 test "it doesn't return notifications for muted user with notifications", %{user: user} do
1043 muted = insert(:user)
1044 {:ok, _user_relationships} = User.mute(user, muted)
1045
1046 {:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"})
1047
1048 assert Notification.for_user(user) == []
1049 end
1050
1051 test "it doesn't return notifications for blocked user", %{user: user} do
1052 blocked = insert(:user)
1053 {:ok, _user_relationship} = User.block(user, blocked)
1054
1055 {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
1056
1057 assert Notification.for_user(user) == []
1058 end
1059
1060 test "it doesn't return notifications for domain-blocked non-followed user", %{user: user} do
1061 blocked = insert(:user, ap_id: "http://some-domain.com")
1062 {:ok, user} = User.block_domain(user, "some-domain.com")
1063
1064 {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
1065
1066 assert Notification.for_user(user) == []
1067 end
1068
1069 test "it returns notifications for domain-blocked but followed user" do
1070 user = insert(:user)
1071 blocked = insert(:user, ap_id: "http://some-domain.com")
1072
1073 {:ok, user} = User.block_domain(user, "some-domain.com")
1074 {:ok, _, _} = User.follow(user, blocked)
1075
1076 {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
1077
1078 assert length(Notification.for_user(user)) == 1
1079 end
1080
1081 test "it doesn't return notifications for muted thread", %{user: user} do
1082 another_user = insert(:user)
1083
1084 {:ok, activity} = CommonAPI.post(another_user, %{status: "hey @#{user.nickname}"})
1085
1086 {:ok, _} = Pleroma.ThreadMute.add_mute(user.id, activity.data["context"])
1087 assert Notification.for_user(user) == []
1088 end
1089
1090 test "it returns notifications from a muted user when with_muted is set", %{user: user} do
1091 muted = insert(:user)
1092 {:ok, _user_relationships} = User.mute(user, muted)
1093
1094 {:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"})
1095
1096 assert length(Notification.for_user(user, %{with_muted: true})) == 1
1097 end
1098
1099 test "it doesn't return notifications from a blocked user when with_muted is set", %{
1100 user: user
1101 } do
1102 blocked = insert(:user)
1103 {:ok, _user_relationship} = User.block(user, blocked)
1104
1105 {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
1106
1107 assert Enum.empty?(Notification.for_user(user, %{with_muted: true}))
1108 end
1109
1110 test "when with_muted is set, " <>
1111 "it doesn't return notifications from a domain-blocked non-followed user",
1112 %{user: user} do
1113 blocked = insert(:user, ap_id: "http://some-domain.com")
1114 {:ok, user} = User.block_domain(user, "some-domain.com")
1115
1116 {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
1117
1118 assert Enum.empty?(Notification.for_user(user, %{with_muted: true}))
1119 end
1120
1121 test "it returns notifications from muted threads when with_muted is set", %{user: user} do
1122 another_user = insert(:user)
1123
1124 {:ok, activity} = CommonAPI.post(another_user, %{status: "hey @#{user.nickname}"})
1125
1126 {:ok, _} = Pleroma.ThreadMute.add_mute(user.id, activity.data["context"])
1127 assert length(Notification.for_user(user, %{with_muted: true})) == 1
1128 end
1129
1130 test "it doesn't return notifications about mentions with filtered word", %{user: user} do
1131 insert(:filter, user: user, phrase: "cofe", hide: true)
1132 another_user = insert(:user)
1133
1134 {:ok, _activity} = CommonAPI.post(another_user, %{status: "@#{user.nickname} got cofe?"})
1135
1136 assert Enum.empty?(Notification.for_user(user))
1137 end
1138
1139 test "it returns notifications about mentions with not hidden filtered word", %{user: user} do
1140 insert(:filter, user: user, phrase: "test", hide: false)
1141 another_user = insert(:user)
1142
1143 {:ok, _} = CommonAPI.post(another_user, %{status: "@#{user.nickname} test"})
1144
1145 assert length(Notification.for_user(user)) == 1
1146 end
1147
1148 test "it returns notifications about favorites with filtered word", %{user: user} do
1149 insert(:filter, user: user, phrase: "cofe", hide: true)
1150 another_user = insert(:user)
1151
1152 {:ok, activity} = CommonAPI.post(user, %{status: "Give me my cofe!"})
1153 {:ok, _} = CommonAPI.favorite(another_user, activity.id)
1154
1155 assert length(Notification.for_user(user)) == 1
1156 end
1157 end
1158 end