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