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