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