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