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