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