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