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