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