Merge branch 'develop' of git.pleroma.social:pleroma/pleroma into remake-remodel-dms
[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 end
310
311 describe "follow / follow_request notifications" do
312 test "it creates `follow` notification for approved Follow activity" do
313 user = insert(:user)
314 followed_user = insert(:user, locked: false)
315
316 {:ok, _, _, _activity} = CommonAPI.follow(user, followed_user)
317 assert FollowingRelationship.following?(user, followed_user)
318 assert [notification] = Notification.for_user(followed_user)
319
320 assert %{type: "follow"} =
321 NotificationView.render("show.json", %{
322 notification: notification,
323 for: followed_user
324 })
325 end
326
327 test "it creates `follow_request` notification for pending Follow activity" do
328 user = insert(:user)
329 followed_user = insert(:user, locked: true)
330
331 {:ok, _, _, _activity} = CommonAPI.follow(user, followed_user)
332 refute FollowingRelationship.following?(user, followed_user)
333 assert [notification] = Notification.for_user(followed_user)
334
335 render_opts = %{notification: notification, for: followed_user}
336 assert %{type: "follow_request"} = NotificationView.render("show.json", render_opts)
337
338 # After request is accepted, the same notification is rendered with type "follow":
339 assert {:ok, _} = CommonAPI.accept_follow_request(user, followed_user)
340
341 notification =
342 Repo.get(Notification, notification.id)
343 |> Repo.preload(:activity)
344
345 assert %{type: "follow"} =
346 NotificationView.render("show.json", notification: notification, for: followed_user)
347 end
348
349 test "it doesn't create a notification for follow-unfollow-follow chains" do
350 user = insert(:user)
351 followed_user = insert(:user, locked: false)
352
353 {:ok, _, _, _activity} = CommonAPI.follow(user, followed_user)
354 assert FollowingRelationship.following?(user, followed_user)
355 assert [notification] = Notification.for_user(followed_user)
356
357 CommonAPI.unfollow(user, followed_user)
358 {:ok, _, _, _activity_dupe} = CommonAPI.follow(user, followed_user)
359
360 notification_id = notification.id
361 assert [%{id: ^notification_id}] = Notification.for_user(followed_user)
362 end
363
364 test "dismisses the notification on follow request rejection" do
365 user = insert(:user, locked: true)
366 follower = insert(:user)
367 {:ok, _, _, _follow_activity} = CommonAPI.follow(follower, user)
368 assert [notification] = Notification.for_user(user)
369 {:ok, _follower} = CommonAPI.reject_follow_request(follower, user)
370 assert [] = Notification.for_user(user)
371 end
372 end
373
374 describe "get notification" do
375 test "it gets a notification that belongs to the user" do
376 user = insert(:user)
377 other_user = insert(:user)
378
379 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}"})
380
381 {:ok, [notification]} = Notification.create_notifications(activity)
382 {:ok, notification} = Notification.get(other_user, notification.id)
383
384 assert notification.user_id == other_user.id
385 end
386
387 test "it returns error if the notification doesn't belong to the user" do
388 user = insert(:user)
389 other_user = insert(:user)
390
391 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}"})
392
393 {:ok, [notification]} = Notification.create_notifications(activity)
394 {:error, _notification} = Notification.get(user, notification.id)
395 end
396 end
397
398 describe "dismiss notification" do
399 test "it dismisses a notification that belongs to the user" do
400 user = insert(:user)
401 other_user = insert(:user)
402
403 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}"})
404
405 {:ok, [notification]} = Notification.create_notifications(activity)
406 {:ok, notification} = Notification.dismiss(other_user, notification.id)
407
408 assert notification.user_id == other_user.id
409 end
410
411 test "it returns error if the notification doesn't belong to the user" do
412 user = insert(:user)
413 other_user = insert(:user)
414
415 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}"})
416
417 {:ok, [notification]} = Notification.create_notifications(activity)
418 {:error, _notification} = Notification.dismiss(user, notification.id)
419 end
420 end
421
422 describe "clear notification" do
423 test "it clears all notifications belonging to the user" do
424 user = insert(:user)
425 other_user = insert(:user)
426 third_user = insert(:user)
427
428 {:ok, activity} =
429 CommonAPI.post(user, %{
430 status: "hey @#{other_user.nickname} and @#{third_user.nickname} !"
431 })
432
433 {:ok, _notifs} = Notification.create_notifications(activity)
434
435 {:ok, activity} =
436 CommonAPI.post(user, %{
437 status: "hey again @#{other_user.nickname} and @#{third_user.nickname} !"
438 })
439
440 {:ok, _notifs} = Notification.create_notifications(activity)
441 Notification.clear(other_user)
442
443 assert Notification.for_user(other_user) == []
444 assert Notification.for_user(third_user) != []
445 end
446 end
447
448 describe "set_read_up_to()" do
449 test "it sets all notifications as read up to a specified notification ID" do
450 user = insert(:user)
451 other_user = insert(:user)
452
453 {:ok, _activity} =
454 CommonAPI.post(user, %{
455 status: "hey @#{other_user.nickname}!"
456 })
457
458 {:ok, _activity} =
459 CommonAPI.post(user, %{
460 status: "hey again @#{other_user.nickname}!"
461 })
462
463 [n2, n1] = Notification.for_user(other_user)
464
465 assert n2.id > n1.id
466
467 {:ok, _activity} =
468 CommonAPI.post(user, %{
469 status: "hey yet again @#{other_user.nickname}!"
470 })
471
472 [_, read_notification] = Notification.set_read_up_to(other_user, n2.id)
473
474 assert read_notification.activity.object
475
476 [n3, n2, n1] = Notification.for_user(other_user)
477
478 assert n1.seen == true
479 assert n2.seen == true
480 assert n3.seen == false
481
482 assert %Pleroma.Marker{} =
483 m =
484 Pleroma.Repo.get_by(
485 Pleroma.Marker,
486 user_id: other_user.id,
487 timeline: "notifications"
488 )
489
490 assert m.last_read_id == to_string(n2.id)
491 end
492 end
493
494 describe "for_user_since/2" do
495 defp days_ago(days) do
496 NaiveDateTime.add(
497 NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second),
498 -days * 60 * 60 * 24,
499 :second
500 )
501 end
502
503 test "Returns recent notifications" do
504 user1 = insert(:user)
505 user2 = insert(:user)
506
507 Enum.each(0..10, fn i ->
508 {:ok, _activity} =
509 CommonAPI.post(user1, %{
510 status: "hey ##{i} @#{user2.nickname}!"
511 })
512 end)
513
514 {old, new} = Enum.split(Notification.for_user(user2), 5)
515
516 Enum.each(old, fn notification ->
517 notification
518 |> cast(%{updated_at: days_ago(10)}, [:updated_at])
519 |> Pleroma.Repo.update!()
520 end)
521
522 recent_notifications_ids =
523 user2
524 |> Notification.for_user_since(
525 NaiveDateTime.add(NaiveDateTime.utc_now(), -5 * 86_400, :second)
526 )
527 |> Enum.map(& &1.id)
528
529 Enum.each(old, fn %{id: id} ->
530 refute id in recent_notifications_ids
531 end)
532
533 Enum.each(new, fn %{id: id} ->
534 assert id in recent_notifications_ids
535 end)
536 end
537 end
538
539 describe "notification target determination / get_notified_from_activity/2" do
540 test "it sends notifications to addressed users in new messages" do
541 user = insert(:user)
542 other_user = insert(:user)
543
544 {:ok, activity} =
545 CommonAPI.post(user, %{
546 status: "hey @#{other_user.nickname}!"
547 })
548
549 {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity)
550
551 assert other_user in enabled_receivers
552 end
553
554 test "it sends notifications to mentioned users in new messages" do
555 user = insert(:user)
556 other_user = insert(:user)
557
558 create_activity = %{
559 "@context" => "https://www.w3.org/ns/activitystreams",
560 "type" => "Create",
561 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
562 "actor" => user.ap_id,
563 "object" => %{
564 "type" => "Note",
565 "content" => "message with a Mention tag, but no explicit tagging",
566 "tag" => [
567 %{
568 "type" => "Mention",
569 "href" => other_user.ap_id,
570 "name" => other_user.nickname
571 }
572 ],
573 "attributedTo" => user.ap_id
574 }
575 }
576
577 {:ok, activity} = Transmogrifier.handle_incoming(create_activity)
578
579 {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity)
580
581 assert other_user in enabled_receivers
582 end
583
584 test "it does not send notifications to users who are only cc in new messages" do
585 user = insert(:user)
586 other_user = insert(:user)
587
588 create_activity = %{
589 "@context" => "https://www.w3.org/ns/activitystreams",
590 "type" => "Create",
591 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
592 "cc" => [other_user.ap_id],
593 "actor" => user.ap_id,
594 "object" => %{
595 "type" => "Note",
596 "content" => "hi everyone",
597 "attributedTo" => user.ap_id
598 }
599 }
600
601 {:ok, activity} = Transmogrifier.handle_incoming(create_activity)
602
603 {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity)
604
605 assert other_user not in enabled_receivers
606 end
607
608 test "it does not send notification to mentioned users in likes" do
609 user = insert(:user)
610 other_user = insert(:user)
611 third_user = insert(:user)
612
613 {:ok, activity_one} =
614 CommonAPI.post(user, %{
615 status: "hey @#{other_user.nickname}!"
616 })
617
618 {:ok, activity_two} = CommonAPI.favorite(third_user, activity_one.id)
619
620 {enabled_receivers, _disabled_receivers} =
621 Notification.get_notified_from_activity(activity_two)
622
623 assert other_user not in enabled_receivers
624 end
625
626 test "it only notifies the post's author in likes" do
627 user = insert(:user)
628 other_user = insert(:user)
629 third_user = insert(:user)
630
631 {:ok, activity_one} =
632 CommonAPI.post(user, %{
633 status: "hey @#{other_user.nickname}!"
634 })
635
636 {:ok, like_data, _} = Builder.like(third_user, activity_one.object)
637
638 {:ok, like, _} =
639 like_data
640 |> Map.put("to", [other_user.ap_id | like_data["to"]])
641 |> ActivityPub.persist(local: true)
642
643 {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(like)
644
645 assert other_user not in enabled_receivers
646 end
647
648 test "it does not send notification to mentioned users in announces" do
649 user = insert(:user)
650 other_user = insert(:user)
651 third_user = insert(:user)
652
653 {:ok, activity_one} =
654 CommonAPI.post(user, %{
655 status: "hey @#{other_user.nickname}!"
656 })
657
658 {:ok, activity_two} = CommonAPI.repeat(activity_one.id, third_user)
659
660 {enabled_receivers, _disabled_receivers} =
661 Notification.get_notified_from_activity(activity_two)
662
663 assert other_user not in enabled_receivers
664 end
665
666 test "it returns blocking recipient in disabled recipients list" do
667 user = insert(:user)
668 other_user = insert(:user)
669 {:ok, _user_relationship} = User.block(other_user, user)
670
671 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
672
673 {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
674
675 assert [] == enabled_receivers
676 assert [other_user] == disabled_receivers
677 end
678
679 test "it returns notification-muting recipient in disabled recipients list" do
680 user = insert(:user)
681 other_user = insert(:user)
682 {:ok, _user_relationships} = User.mute(other_user, user)
683
684 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
685
686 {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
687
688 assert [] == enabled_receivers
689 assert [other_user] == disabled_receivers
690 end
691
692 test "it returns thread-muting recipient in disabled recipients list" do
693 user = insert(:user)
694 other_user = insert(:user)
695
696 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
697
698 {:ok, _} = CommonAPI.add_mute(other_user, activity)
699
700 {:ok, same_context_activity} =
701 CommonAPI.post(user, %{
702 status: "hey-hey-hey @#{other_user.nickname}!",
703 in_reply_to_status_id: activity.id
704 })
705
706 {enabled_receivers, disabled_receivers} =
707 Notification.get_notified_from_activity(same_context_activity)
708
709 assert [other_user] == disabled_receivers
710 refute other_user in enabled_receivers
711 end
712
713 test "it returns non-following domain-blocking recipient in disabled recipients list" do
714 blocked_domain = "blocked.domain"
715 user = insert(:user, %{ap_id: "https://#{blocked_domain}/@actor"})
716 other_user = insert(:user)
717
718 {:ok, other_user} = User.block_domain(other_user, blocked_domain)
719
720 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
721
722 {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
723
724 assert [] == enabled_receivers
725 assert [other_user] == disabled_receivers
726 end
727
728 test "it returns following domain-blocking recipient in enabled recipients list" do
729 blocked_domain = "blocked.domain"
730 user = insert(:user, %{ap_id: "https://#{blocked_domain}/@actor"})
731 other_user = insert(:user)
732
733 {:ok, other_user} = User.block_domain(other_user, blocked_domain)
734 {:ok, other_user} = User.follow(other_user, user)
735
736 {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
737
738 {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
739
740 assert [other_user] == enabled_receivers
741 assert [] == disabled_receivers
742 end
743 end
744
745 describe "notification lifecycle" do
746 test "liking an activity results in 1 notification, then 0 if the activity is deleted" do
747 user = insert(:user)
748 other_user = insert(:user)
749
750 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
751
752 assert Enum.empty?(Notification.for_user(user))
753
754 {:ok, _} = CommonAPI.favorite(other_user, activity.id)
755
756 assert length(Notification.for_user(user)) == 1
757
758 {:ok, _} = CommonAPI.delete(activity.id, user)
759
760 assert Enum.empty?(Notification.for_user(user))
761 end
762
763 test "liking an activity results in 1 notification, then 0 if the activity is unliked" do
764 user = insert(:user)
765 other_user = insert(:user)
766
767 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
768
769 assert Enum.empty?(Notification.for_user(user))
770
771 {:ok, _} = CommonAPI.favorite(other_user, activity.id)
772
773 assert length(Notification.for_user(user)) == 1
774
775 {:ok, _} = CommonAPI.unfavorite(activity.id, other_user)
776
777 assert Enum.empty?(Notification.for_user(user))
778 end
779
780 test "repeating an activity results in 1 notification, then 0 if the activity is deleted" do
781 user = insert(:user)
782 other_user = insert(:user)
783
784 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
785
786 assert Enum.empty?(Notification.for_user(user))
787
788 {:ok, _} = CommonAPI.repeat(activity.id, other_user)
789
790 assert length(Notification.for_user(user)) == 1
791
792 {:ok, _} = CommonAPI.delete(activity.id, user)
793
794 assert Enum.empty?(Notification.for_user(user))
795 end
796
797 test "repeating an activity results in 1 notification, then 0 if the activity is unrepeated" do
798 user = insert(:user)
799 other_user = insert(:user)
800
801 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
802
803 assert Enum.empty?(Notification.for_user(user))
804
805 {:ok, _} = CommonAPI.repeat(activity.id, other_user)
806
807 assert length(Notification.for_user(user)) == 1
808
809 {:ok, _} = CommonAPI.unrepeat(activity.id, other_user)
810
811 assert Enum.empty?(Notification.for_user(user))
812 end
813
814 test "liking an activity which is already deleted does not generate a notification" do
815 user = insert(:user)
816 other_user = insert(:user)
817
818 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
819
820 assert Enum.empty?(Notification.for_user(user))
821
822 {:ok, _deletion_activity} = CommonAPI.delete(activity.id, user)
823
824 assert Enum.empty?(Notification.for_user(user))
825
826 {:error, :not_found} = CommonAPI.favorite(other_user, activity.id)
827
828 assert Enum.empty?(Notification.for_user(user))
829 end
830
831 test "repeating an activity which is already deleted does not generate a notification" do
832 user = insert(:user)
833 other_user = insert(:user)
834
835 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
836
837 assert Enum.empty?(Notification.for_user(user))
838
839 {:ok, _deletion_activity} = CommonAPI.delete(activity.id, user)
840
841 assert Enum.empty?(Notification.for_user(user))
842
843 {:error, _} = CommonAPI.repeat(activity.id, other_user)
844
845 assert Enum.empty?(Notification.for_user(user))
846 end
847
848 test "replying to a deleted post without tagging does not generate a notification" do
849 user = insert(:user)
850 other_user = insert(:user)
851
852 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
853 {:ok, _deletion_activity} = CommonAPI.delete(activity.id, user)
854
855 {:ok, _reply_activity} =
856 CommonAPI.post(other_user, %{
857 status: "test reply",
858 in_reply_to_status_id: activity.id
859 })
860
861 assert Enum.empty?(Notification.for_user(user))
862 end
863
864 test "notifications are deleted if a local user is deleted" do
865 user = insert(:user)
866 other_user = insert(:user)
867
868 {:ok, _activity} =
869 CommonAPI.post(user, %{status: "hi @#{other_user.nickname}", visibility: "direct"})
870
871 refute Enum.empty?(Notification.for_user(other_user))
872
873 {:ok, job} = User.delete(user)
874 ObanHelpers.perform(job)
875
876 assert Enum.empty?(Notification.for_user(other_user))
877 end
878
879 test "notifications are deleted if a remote user is deleted" do
880 remote_user = insert(:user)
881 local_user = insert(:user)
882
883 dm_message = %{
884 "@context" => "https://www.w3.org/ns/activitystreams",
885 "type" => "Create",
886 "actor" => remote_user.ap_id,
887 "id" => remote_user.ap_id <> "/activities/test",
888 "to" => [local_user.ap_id],
889 "cc" => [],
890 "object" => %{
891 "type" => "Note",
892 "content" => "Hello!",
893 "tag" => [
894 %{
895 "type" => "Mention",
896 "href" => local_user.ap_id,
897 "name" => "@#{local_user.nickname}"
898 }
899 ],
900 "to" => [local_user.ap_id],
901 "cc" => [],
902 "attributedTo" => remote_user.ap_id
903 }
904 }
905
906 {:ok, _dm_activity} = Transmogrifier.handle_incoming(dm_message)
907
908 refute Enum.empty?(Notification.for_user(local_user))
909
910 delete_user_message = %{
911 "@context" => "https://www.w3.org/ns/activitystreams",
912 "id" => remote_user.ap_id <> "/activities/delete",
913 "actor" => remote_user.ap_id,
914 "type" => "Delete",
915 "object" => remote_user.ap_id
916 }
917
918 remote_user_url = remote_user.ap_id
919
920 Tesla.Mock.mock(fn
921 %{method: :get, url: ^remote_user_url} ->
922 %Tesla.Env{status: 404, body: ""}
923 end)
924
925 {:ok, _delete_activity} = Transmogrifier.handle_incoming(delete_user_message)
926 ObanHelpers.perform_all()
927
928 assert Enum.empty?(Notification.for_user(local_user))
929 end
930
931 @tag capture_log: true
932 test "move activity generates a notification" do
933 %{ap_id: old_ap_id} = old_user = insert(:user)
934 %{ap_id: new_ap_id} = new_user = insert(:user, also_known_as: [old_ap_id])
935 follower = insert(:user)
936 other_follower = insert(:user, %{allow_following_move: false})
937
938 User.follow(follower, old_user)
939 User.follow(other_follower, old_user)
940
941 old_user_url = old_user.ap_id
942
943 body =
944 File.read!("test/fixtures/users_mock/localhost.json")
945 |> String.replace("{{nickname}}", old_user.nickname)
946 |> Jason.encode!()
947
948 Tesla.Mock.mock(fn
949 %{method: :get, url: ^old_user_url} ->
950 %Tesla.Env{status: 200, body: body}
951 end)
952
953 Pleroma.Web.ActivityPub.ActivityPub.move(old_user, new_user)
954 ObanHelpers.perform_all()
955
956 assert [
957 %{
958 activity: %{
959 data: %{"type" => "Move", "actor" => ^old_ap_id, "target" => ^new_ap_id}
960 }
961 }
962 ] = Notification.for_user(follower)
963
964 assert [
965 %{
966 activity: %{
967 data: %{"type" => "Move", "actor" => ^old_ap_id, "target" => ^new_ap_id}
968 }
969 }
970 ] = Notification.for_user(other_follower)
971 end
972 end
973
974 describe "for_user" do
975 test "it returns notifications for muted user without notifications" do
976 user = insert(:user)
977 muted = insert(:user)
978 {:ok, _user_relationships} = User.mute(user, muted, false)
979
980 {:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"})
981
982 [notification] = Notification.for_user(user)
983
984 assert notification.activity.object
985 end
986
987 test "it doesn't return notifications for muted user with notifications" do
988 user = insert(:user)
989 muted = insert(:user)
990 {:ok, _user_relationships} = User.mute(user, muted)
991
992 {:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"})
993
994 assert Notification.for_user(user) == []
995 end
996
997 test "it doesn't return notifications for blocked user" do
998 user = insert(:user)
999 blocked = insert(:user)
1000 {:ok, _user_relationship} = User.block(user, blocked)
1001
1002 {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
1003
1004 assert Notification.for_user(user) == []
1005 end
1006
1007 test "it doesn't return notifications for domain-blocked non-followed user" do
1008 user = insert(:user)
1009 blocked = insert(:user, ap_id: "http://some-domain.com")
1010 {:ok, user} = User.block_domain(user, "some-domain.com")
1011
1012 {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
1013
1014 assert Notification.for_user(user) == []
1015 end
1016
1017 test "it returns notifications for domain-blocked but followed user" do
1018 user = insert(:user)
1019 blocked = insert(:user, ap_id: "http://some-domain.com")
1020
1021 {:ok, user} = User.block_domain(user, "some-domain.com")
1022 {:ok, _} = User.follow(user, blocked)
1023
1024 {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
1025
1026 assert length(Notification.for_user(user)) == 1
1027 end
1028
1029 test "it doesn't return notifications for muted thread" do
1030 user = insert(:user)
1031 another_user = insert(:user)
1032
1033 {:ok, activity} = CommonAPI.post(another_user, %{status: "hey @#{user.nickname}"})
1034
1035 {:ok, _} = Pleroma.ThreadMute.add_mute(user.id, activity.data["context"])
1036 assert Notification.for_user(user) == []
1037 end
1038
1039 test "it returns notifications from a muted user when with_muted is set" do
1040 user = insert(:user)
1041 muted = insert(:user)
1042 {:ok, _user_relationships} = User.mute(user, muted)
1043
1044 {:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"})
1045
1046 assert length(Notification.for_user(user, %{with_muted: true})) == 1
1047 end
1048
1049 test "it doesn't return notifications from a blocked user when with_muted is set" do
1050 user = insert(:user)
1051 blocked = insert(:user)
1052 {:ok, _user_relationship} = User.block(user, blocked)
1053
1054 {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
1055
1056 assert Enum.empty?(Notification.for_user(user, %{with_muted: true}))
1057 end
1058
1059 test "when with_muted is set, " <>
1060 "it doesn't return notifications from a domain-blocked non-followed user" do
1061 user = insert(:user)
1062 blocked = insert(:user, ap_id: "http://some-domain.com")
1063 {:ok, user} = User.block_domain(user, "some-domain.com")
1064
1065 {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
1066
1067 assert Enum.empty?(Notification.for_user(user, %{with_muted: true}))
1068 end
1069
1070 test "it returns notifications from muted threads when with_muted is set" do
1071 user = insert(:user)
1072 another_user = insert(:user)
1073
1074 {:ok, activity} = CommonAPI.post(another_user, %{status: "hey @#{user.nickname}"})
1075
1076 {:ok, _} = Pleroma.ThreadMute.add_mute(user.id, activity.data["context"])
1077 assert length(Notification.for_user(user, %{with_muted: true})) == 1
1078 end
1079 end
1080 end