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