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