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