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