Pipeline Ingestion: Note
[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 "id" => Pleroma.Web.ActivityPub.Utils.generate_object_id(),
628 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
629 "content" => "message with a Mention tag, but no explicit tagging",
630 "tag" => [
631 %{
632 "type" => "Mention",
633 "href" => other_user.ap_id,
634 "name" => other_user.nickname
635 }
636 ],
637 "attributedTo" => user.ap_id
638 }
639 }
640
641 {:ok, activity} = Transmogrifier.handle_incoming(create_activity)
642
643 {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity)
644
645 assert other_user in enabled_receivers
646 end
647
648 test "it does not send notifications to users who are only cc in new messages" do
649 user = insert(:user)
650 other_user = insert(:user)
651
652 create_activity = %{
653 "@context" => "https://www.w3.org/ns/activitystreams",
654 "type" => "Create",
655 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
656 "cc" => [other_user.ap_id],
657 "actor" => user.ap_id,
658 "object" => %{
659 "type" => "Note",
660 "id" => Pleroma.Web.ActivityPub.Utils.generate_object_id(),
661 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
662 "cc" => [other_user.ap_id],
663 "content" => "hi everyone",
664 "attributedTo" => user.ap_id
665 }
666 }
667
668 {:ok, activity} = Transmogrifier.handle_incoming(create_activity)
669
670 {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity)
671
672 assert other_user not in enabled_receivers
673 end
674
675 test "it does not send notification to mentioned users in likes" do
676 user = insert(:user)
677 other_user = insert(:user)
678 third_user = insert(:user)
679
680 {:ok, activity_one} =
681 CommonAPI.post(user, %{
682 status: "hey @#{other_user.nickname}!"
683 })
684
685 {:ok, activity_two} = CommonAPI.favorite(third_user, activity_one.id)
686
687 {enabled_receivers, _disabled_receivers} =
688 Notification.get_notified_from_activity(activity_two)
689
690 assert other_user not in enabled_receivers
691 end
692
693 test "it only notifies the post's author in likes" do
694 user = insert(:user)
695 other_user = insert(:user)
696 third_user = insert(:user)
697
698 {:ok, activity_one} =
699 CommonAPI.post(user, %{
700 status: "hey @#{other_user.nickname}!"
701 })
702
703 {:ok, like_data, _} = Builder.like(third_user, activity_one.object)
704
705 {:ok, like, _} =
706 like_data
707 |> Map.put("to", [other_user.ap_id | like_data["to"]])
708 |> ActivityPub.persist(local: true)
709
710 {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(like)
711
712 assert other_user not in enabled_receivers
713 end
714
715 test "it does not send notification to mentioned users in announces" do
716 user = insert(:user)
717 other_user = insert(:user)
718 third_user = insert(:user)
719
720 {:ok, activity_one} =
721 CommonAPI.post(user, %{
722 status: "hey @#{other_user.nickname}!"
723 })
724
725 {:ok, activity_two} = CommonAPI.repeat(activity_one.id, third_user)
726
727 {enabled_receivers, _disabled_receivers} =
728 Notification.get_notified_from_activity(activity_two)
729
730 assert other_user not in enabled_receivers
731 end
732
733 test "it returns blocking recipient in disabled recipients list" do
734 user = insert(:user)
735 other_user = insert(:user)
736 {:ok, _user_relationship} = User.block(other_user, user)
737
738 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
739
740 {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
741
742 assert [] == enabled_receivers
743 assert [other_user] == disabled_receivers
744 end
745
746 test "it returns notification-muting recipient in disabled recipients list" do
747 user = insert(:user)
748 other_user = insert(:user)
749 {:ok, _user_relationships} = User.mute(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 thread-muting recipient in disabled recipients list" do
760 user = insert(:user)
761 other_user = insert(:user)
762
763 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
764
765 {:ok, _} = CommonAPI.add_mute(other_user, activity)
766
767 {:ok, same_context_activity} =
768 CommonAPI.post(user, %{
769 status: "hey-hey-hey @#{other_user.nickname}!",
770 in_reply_to_status_id: activity.id
771 })
772
773 {enabled_receivers, disabled_receivers} =
774 Notification.get_notified_from_activity(same_context_activity)
775
776 assert [other_user] == disabled_receivers
777 refute other_user in enabled_receivers
778 end
779
780 test "it returns non-following domain-blocking recipient in disabled recipients list" do
781 blocked_domain = "blocked.domain"
782 user = insert(:user, %{ap_id: "https://#{blocked_domain}/@actor"})
783 other_user = insert(:user)
784
785 {:ok, other_user} = User.block_domain(other_user, blocked_domain)
786
787 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
788
789 {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
790
791 assert [] == enabled_receivers
792 assert [other_user] == disabled_receivers
793 end
794
795 test "it returns following domain-blocking recipient in enabled recipients list" do
796 blocked_domain = "blocked.domain"
797 user = insert(:user, %{ap_id: "https://#{blocked_domain}/@actor"})
798 other_user = insert(:user)
799
800 {:ok, other_user} = User.block_domain(other_user, blocked_domain)
801 {:ok, other_user, user} = User.follow(other_user, user)
802
803 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
804
805 {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
806
807 assert [other_user] == enabled_receivers
808 assert [] == disabled_receivers
809 end
810 end
811
812 describe "notification lifecycle" do
813 test "liking an activity results in 1 notification, then 0 if the activity is deleted" do
814 user = insert(:user)
815 other_user = insert(:user)
816
817 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
818
819 assert Enum.empty?(Notification.for_user(user))
820
821 {:ok, _} = CommonAPI.favorite(other_user, activity.id)
822
823 assert length(Notification.for_user(user)) == 1
824
825 {:ok, _} = CommonAPI.delete(activity.id, user)
826
827 assert Enum.empty?(Notification.for_user(user))
828 end
829
830 test "liking an activity results in 1 notification, then 0 if the activity is unliked" do
831 user = insert(:user)
832 other_user = insert(:user)
833
834 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
835
836 assert Enum.empty?(Notification.for_user(user))
837
838 {:ok, _} = CommonAPI.favorite(other_user, activity.id)
839
840 assert length(Notification.for_user(user)) == 1
841
842 {:ok, _} = CommonAPI.unfavorite(activity.id, other_user)
843
844 assert Enum.empty?(Notification.for_user(user))
845 end
846
847 test "repeating an activity results in 1 notification, then 0 if the activity is deleted" do
848 user = insert(:user)
849 other_user = insert(:user)
850
851 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
852
853 assert Enum.empty?(Notification.for_user(user))
854
855 {:ok, _} = CommonAPI.repeat(activity.id, other_user)
856
857 assert length(Notification.for_user(user)) == 1
858
859 {:ok, _} = CommonAPI.delete(activity.id, user)
860
861 assert Enum.empty?(Notification.for_user(user))
862 end
863
864 test "repeating an activity results in 1 notification, then 0 if the activity is unrepeated" do
865 user = insert(:user)
866 other_user = insert(:user)
867
868 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
869
870 assert Enum.empty?(Notification.for_user(user))
871
872 {:ok, _} = CommonAPI.repeat(activity.id, other_user)
873
874 assert length(Notification.for_user(user)) == 1
875
876 {:ok, _} = CommonAPI.unrepeat(activity.id, other_user)
877
878 assert Enum.empty?(Notification.for_user(user))
879 end
880
881 test "liking an activity which is already deleted does not generate a notification" do
882 user = insert(:user)
883 other_user = insert(:user)
884
885 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
886
887 assert Enum.empty?(Notification.for_user(user))
888
889 {:ok, _deletion_activity} = CommonAPI.delete(activity.id, user)
890
891 assert Enum.empty?(Notification.for_user(user))
892
893 {:error, :not_found} = CommonAPI.favorite(other_user, activity.id)
894
895 assert Enum.empty?(Notification.for_user(user))
896 end
897
898 test "repeating an activity which is already deleted does not generate a notification" do
899 user = insert(:user)
900 other_user = insert(:user)
901
902 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
903
904 assert Enum.empty?(Notification.for_user(user))
905
906 {:ok, _deletion_activity} = CommonAPI.delete(activity.id, user)
907
908 assert Enum.empty?(Notification.for_user(user))
909
910 {:error, _} = CommonAPI.repeat(activity.id, other_user)
911
912 assert Enum.empty?(Notification.for_user(user))
913 end
914
915 test "replying to a deleted post without tagging does not generate a notification" do
916 user = insert(:user)
917 other_user = insert(:user)
918
919 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
920 {:ok, _deletion_activity} = CommonAPI.delete(activity.id, user)
921
922 {:ok, _reply_activity} =
923 CommonAPI.post(other_user, %{
924 status: "test reply",
925 in_reply_to_status_id: activity.id
926 })
927
928 assert Enum.empty?(Notification.for_user(user))
929 end
930
931 test "notifications are deleted if a local user is deleted" do
932 user = insert(:user)
933 other_user = insert(:user)
934
935 {:ok, _activity} =
936 CommonAPI.post(user, %{status: "hi @#{other_user.nickname}", visibility: "direct"})
937
938 refute Enum.empty?(Notification.for_user(other_user))
939
940 {:ok, job} = User.delete(user)
941 ObanHelpers.perform(job)
942
943 assert Enum.empty?(Notification.for_user(other_user))
944 end
945
946 test "notifications are deleted if a remote user is deleted" do
947 remote_user = insert(:user)
948 local_user = insert(:user)
949
950 dm_message = %{
951 "@context" => "https://www.w3.org/ns/activitystreams",
952 "type" => "Create",
953 "actor" => remote_user.ap_id,
954 "id" => remote_user.ap_id <> "/activities/test",
955 "to" => [local_user.ap_id],
956 "cc" => [],
957 "object" => %{
958 "type" => "Note",
959 "id" => remote_user.ap_id <> "/objects/test",
960 "content" => "Hello!",
961 "tag" => [
962 %{
963 "type" => "Mention",
964 "href" => local_user.ap_id,
965 "name" => "@#{local_user.nickname}"
966 }
967 ],
968 "to" => [local_user.ap_id],
969 "cc" => [],
970 "attributedTo" => remote_user.ap_id
971 }
972 }
973
974 {:ok, _dm_activity} = Transmogrifier.handle_incoming(dm_message)
975
976 refute Enum.empty?(Notification.for_user(local_user))
977
978 delete_user_message = %{
979 "@context" => "https://www.w3.org/ns/activitystreams",
980 "id" => remote_user.ap_id <> "/activities/delete",
981 "actor" => remote_user.ap_id,
982 "type" => "Delete",
983 "object" => remote_user.ap_id
984 }
985
986 remote_user_url = remote_user.ap_id
987
988 Tesla.Mock.mock(fn
989 %{method: :get, url: ^remote_user_url} ->
990 %Tesla.Env{status: 404, body: ""}
991 end)
992
993 {:ok, _delete_activity} = Transmogrifier.handle_incoming(delete_user_message)
994 ObanHelpers.perform_all()
995
996 assert Enum.empty?(Notification.for_user(local_user))
997 end
998
999 test "move activity generates a notification" do
1000 %{ap_id: old_ap_id} = old_user = insert(:user)
1001 %{ap_id: new_ap_id} = new_user = insert(:user, also_known_as: [old_ap_id])
1002 follower = insert(:user)
1003 other_follower = insert(:user, %{allow_following_move: false})
1004
1005 User.follow(follower, old_user)
1006 User.follow(other_follower, old_user)
1007
1008 Pleroma.Web.ActivityPub.ActivityPub.move(old_user, new_user)
1009 ObanHelpers.perform_all()
1010
1011 assert [
1012 %{
1013 activity: %{
1014 data: %{"type" => "Move", "actor" => ^old_ap_id, "target" => ^new_ap_id}
1015 }
1016 }
1017 ] = Notification.for_user(follower)
1018
1019 assert [
1020 %{
1021 activity: %{
1022 data: %{"type" => "Move", "actor" => ^old_ap_id, "target" => ^new_ap_id}
1023 }
1024 }
1025 ] = Notification.for_user(other_follower)
1026 end
1027 end
1028
1029 describe "for_user" do
1030 setup do
1031 user = insert(:user)
1032
1033 {:ok, %{user: user}}
1034 end
1035
1036 test "it returns notifications for muted user without notifications", %{user: user} do
1037 muted = insert(:user)
1038 {:ok, _user_relationships} = User.mute(user, muted, %{notifications: false})
1039
1040 {:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"})
1041
1042 [notification] = Notification.for_user(user)
1043
1044 assert notification.activity.object
1045 assert notification.seen
1046 end
1047
1048 test "it doesn't return notifications for muted user with notifications", %{user: user} do
1049 muted = insert(:user)
1050 {:ok, _user_relationships} = User.mute(user, muted)
1051
1052 {:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"})
1053
1054 assert Notification.for_user(user) == []
1055 end
1056
1057 test "it doesn't return notifications for blocked user", %{user: user} do
1058 blocked = insert(:user)
1059 {:ok, _user_relationship} = User.block(user, blocked)
1060
1061 {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
1062
1063 assert Notification.for_user(user) == []
1064 end
1065
1066 test "it doesn't return notifications for domain-blocked non-followed user", %{user: user} do
1067 blocked = insert(:user, ap_id: "http://some-domain.com")
1068 {:ok, user} = User.block_domain(user, "some-domain.com")
1069
1070 {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
1071
1072 assert Notification.for_user(user) == []
1073 end
1074
1075 test "it returns notifications for domain-blocked but followed user" do
1076 user = insert(:user)
1077 blocked = insert(:user, ap_id: "http://some-domain.com")
1078
1079 {:ok, user} = User.block_domain(user, "some-domain.com")
1080 {:ok, _, _} = User.follow(user, blocked)
1081
1082 {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
1083
1084 assert length(Notification.for_user(user)) == 1
1085 end
1086
1087 test "it doesn't return notifications for muted thread", %{user: user} do
1088 another_user = insert(:user)
1089
1090 {:ok, activity} = CommonAPI.post(another_user, %{status: "hey @#{user.nickname}"})
1091
1092 {:ok, _} = Pleroma.ThreadMute.add_mute(user.id, activity.data["context"])
1093 assert Notification.for_user(user) == []
1094 end
1095
1096 test "it returns notifications from a muted user when with_muted is set", %{user: user} do
1097 muted = insert(:user)
1098 {:ok, _user_relationships} = User.mute(user, muted)
1099
1100 {:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"})
1101
1102 assert length(Notification.for_user(user, %{with_muted: true})) == 1
1103 end
1104
1105 test "it doesn't return notifications from a blocked user when with_muted is set", %{
1106 user: user
1107 } do
1108 blocked = insert(:user)
1109 {:ok, _user_relationship} = User.block(user, blocked)
1110
1111 {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
1112
1113 assert Enum.empty?(Notification.for_user(user, %{with_muted: true}))
1114 end
1115
1116 test "when with_muted is set, " <>
1117 "it doesn't return notifications from a domain-blocked non-followed user",
1118 %{user: user} do
1119 blocked = insert(:user, ap_id: "http://some-domain.com")
1120 {:ok, user} = User.block_domain(user, "some-domain.com")
1121
1122 {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
1123
1124 assert Enum.empty?(Notification.for_user(user, %{with_muted: true}))
1125 end
1126
1127 test "it returns notifications from muted threads when with_muted is set", %{user: user} do
1128 another_user = insert(:user)
1129
1130 {:ok, activity} = CommonAPI.post(another_user, %{status: "hey @#{user.nickname}"})
1131
1132 {:ok, _} = Pleroma.ThreadMute.add_mute(user.id, activity.data["context"])
1133 assert length(Notification.for_user(user, %{with_muted: true})) == 1
1134 end
1135
1136 test "it doesn't return notifications about mentions with filtered word", %{user: user} do
1137 insert(:filter, user: user, phrase: "cofe", hide: true)
1138 another_user = insert(:user)
1139
1140 {:ok, _activity} = CommonAPI.post(another_user, %{status: "@#{user.nickname} got cofe?"})
1141
1142 assert Enum.empty?(Notification.for_user(user))
1143 end
1144
1145 test "it returns notifications about mentions with not hidden filtered word", %{user: user} do
1146 insert(:filter, user: user, phrase: "test", hide: false)
1147 another_user = insert(:user)
1148
1149 {:ok, _} = CommonAPI.post(another_user, %{status: "@#{user.nickname} test"})
1150
1151 assert length(Notification.for_user(user)) == 1
1152 end
1153
1154 test "it returns notifications about favorites with filtered word", %{user: user} do
1155 insert(:filter, user: user, phrase: "cofe", hide: true)
1156 another_user = insert(:user)
1157
1158 {:ok, activity} = CommonAPI.post(user, %{status: "Give me my cofe!"})
1159 {:ok, _} = CommonAPI.favorite(another_user, activity.id)
1160
1161 assert length(Notification.for_user(user)) == 1
1162 end
1163 end
1164 end