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