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