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