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