Merge branch 'openapi/scheduled_activities' into 'develop'
[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.Transmogrifier
16 alias Pleroma.Web.CommonAPI
17 alias Pleroma.Web.MastodonAPI.NotificationView
18 alias Pleroma.Web.Push
19 alias Pleroma.Web.Streamer
20
21 describe "create_notifications" do
22 test "creates a notification for an emoji reaction" do
23 user = insert(:user)
24 other_user = insert(:user)
25
26 {:ok, activity} = CommonAPI.post(user, %{"status" => "yeah"})
27 {:ok, activity, _object} = CommonAPI.react_with_emoji(activity.id, other_user, "☕")
28
29 {:ok, [notification]} = Notification.create_notifications(activity)
30
31 assert notification.user_id == user.id
32 end
33
34 test "notifies someone when they are directly addressed" do
35 user = insert(:user)
36 other_user = insert(:user)
37 third_user = insert(:user)
38
39 {:ok, activity} =
40 CommonAPI.post(user, %{
41 "status" => "hey @#{other_user.nickname} and @#{third_user.nickname}"
42 })
43
44 {:ok, [notification, other_notification]} = Notification.create_notifications(activity)
45
46 notified_ids = Enum.sort([notification.user_id, other_notification.user_id])
47 assert notified_ids == [other_user.id, third_user.id]
48 assert notification.activity_id == activity.id
49 assert other_notification.activity_id == activity.id
50 end
51
52 test "it creates a notification for subscribed users" do
53 user = insert(:user)
54 subscriber = insert(:user)
55
56 User.subscribe(subscriber, user)
57
58 {:ok, status} = CommonAPI.post(user, %{"status" => "Akariiiin"})
59 {:ok, [notification]} = Notification.create_notifications(status)
60
61 assert notification.user_id == subscriber.id
62 end
63
64 test "does not create a notification for subscribed users if status is a reply" do
65 user = insert(:user)
66 other_user = insert(:user)
67 subscriber = insert(:user)
68
69 User.subscribe(subscriber, other_user)
70
71 {:ok, activity} = CommonAPI.post(user, %{"status" => "test post"})
72
73 {:ok, _reply_activity} =
74 CommonAPI.post(other_user, %{
75 "status" => "test reply",
76 "in_reply_to_status_id" => activity.id
77 })
78
79 user_notifications = Notification.for_user(user)
80 assert length(user_notifications) == 1
81
82 subscriber_notifications = Notification.for_user(subscriber)
83 assert Enum.empty?(subscriber_notifications)
84 end
85 end
86
87 describe "CommonApi.post/2 notification-related functionality" do
88 test_with_mock "creates but does NOT send notification to blocker user",
89 Push,
90 [:passthrough],
91 [] do
92 user = insert(:user)
93 blocker = insert(:user)
94 {:ok, _user_relationship} = User.block(blocker, user)
95
96 {:ok, _activity} = CommonAPI.post(user, %{"status" => "hey @#{blocker.nickname}!"})
97
98 blocker_id = blocker.id
99 assert [%Notification{user_id: ^blocker_id}] = Repo.all(Notification)
100 refute called(Push.send(:_))
101 end
102
103 test_with_mock "creates but does NOT send notification to notification-muter user",
104 Push,
105 [:passthrough],
106 [] do
107 user = insert(:user)
108 muter = insert(:user)
109 {:ok, _user_relationships} = User.mute(muter, user)
110
111 {:ok, _activity} = CommonAPI.post(user, %{"status" => "hey @#{muter.nickname}!"})
112
113 muter_id = muter.id
114 assert [%Notification{user_id: ^muter_id}] = Repo.all(Notification)
115 refute called(Push.send(:_))
116 end
117
118 test_with_mock "creates but does NOT send notification to thread-muter user",
119 Push,
120 [:passthrough],
121 [] do
122 user = insert(:user)
123 thread_muter = insert(:user)
124
125 {:ok, activity} = CommonAPI.post(user, %{"status" => "hey @#{thread_muter.nickname}!"})
126
127 {:ok, _} = CommonAPI.add_mute(thread_muter, activity)
128
129 {:ok, _same_context_activity} =
130 CommonAPI.post(user, %{
131 "status" => "hey-hey-hey @#{thread_muter.nickname}!",
132 "in_reply_to_status_id" => activity.id
133 })
134
135 [pre_mute_notification, post_mute_notification] =
136 Repo.all(from(n in Notification, where: n.user_id == ^thread_muter.id, order_by: n.id))
137
138 pre_mute_notification_id = pre_mute_notification.id
139 post_mute_notification_id = post_mute_notification.id
140
141 assert called(
142 Push.send(
143 :meck.is(fn
144 %Notification{id: ^pre_mute_notification_id} -> true
145 _ -> false
146 end)
147 )
148 )
149
150 refute called(
151 Push.send(
152 :meck.is(fn
153 %Notification{id: ^post_mute_notification_id} -> true
154 _ -> false
155 end)
156 )
157 )
158 end
159 end
160
161 describe "create_notification" do
162 @tag needs_streamer: true
163 test "it creates a notification for user and send to the 'user' and the 'user:notification' stream" do
164 user = insert(:user)
165 task = Task.async(fn -> assert_receive {:text, _}, 4_000 end)
166 task_user_notification = Task.async(fn -> assert_receive {:text, _}, 4_000 end)
167 Streamer.add_socket("user", %{transport_pid: task.pid, assigns: %{user: user}})
168
169 Streamer.add_socket(
170 "user:notification",
171 %{transport_pid: task_user_notification.pid, assigns: %{user: user}}
172 )
173
174 activity = insert(:note_activity)
175
176 notify = Notification.create_notification(activity, user)
177 assert notify.user_id == user.id
178 Task.await(task)
179 Task.await(task_user_notification)
180 end
181
182 test "it creates a notification for user if the user blocks the activity author" do
183 activity = insert(:note_activity)
184 author = User.get_cached_by_ap_id(activity.data["actor"])
185 user = insert(:user)
186 {:ok, _user_relationship} = User.block(user, author)
187
188 assert Notification.create_notification(activity, user)
189 end
190
191 test "it creates a notification for the user if the user mutes the activity author" do
192 muter = insert(:user)
193 muted = insert(:user)
194 {:ok, _} = User.mute(muter, muted)
195 muter = Repo.get(User, muter.id)
196 {:ok, activity} = CommonAPI.post(muted, %{"status" => "Hi @#{muter.nickname}"})
197
198 assert Notification.create_notification(activity, muter)
199 end
200
201 test "notification created if user is muted without notifications" do
202 muter = insert(:user)
203 muted = insert(:user)
204
205 {:ok, _user_relationships} = User.mute(muter, muted, false)
206
207 {:ok, activity} = CommonAPI.post(muted, %{"status" => "Hi @#{muter.nickname}"})
208
209 assert Notification.create_notification(activity, muter)
210 end
211
212 test "it creates a notification for an activity from a muted thread" do
213 muter = insert(:user)
214 other_user = insert(:user)
215 {:ok, activity} = CommonAPI.post(muter, %{"status" => "hey"})
216 CommonAPI.add_mute(muter, activity)
217
218 {:ok, activity} =
219 CommonAPI.post(other_user, %{
220 "status" => "Hi @#{muter.nickname}",
221 "in_reply_to_status_id" => activity.id
222 })
223
224 assert Notification.create_notification(activity, muter)
225 end
226
227 test "it disables notifications from followers" do
228 follower = insert(:user)
229
230 followed =
231 insert(:user, notification_settings: %Pleroma.User.NotificationSetting{followers: false})
232
233 User.follow(follower, followed)
234 {:ok, activity} = CommonAPI.post(follower, %{"status" => "hey @#{followed.nickname}"})
235 refute Notification.create_notification(activity, followed)
236 end
237
238 test "it disables notifications from non-followers" do
239 follower = insert(:user)
240
241 followed =
242 insert(:user,
243 notification_settings: %Pleroma.User.NotificationSetting{non_followers: false}
244 )
245
246 {:ok, activity} = CommonAPI.post(follower, %{"status" => "hey @#{followed.nickname}"})
247 refute Notification.create_notification(activity, followed)
248 end
249
250 test "it disables notifications from people the user follows" do
251 follower =
252 insert(:user, notification_settings: %Pleroma.User.NotificationSetting{follows: false})
253
254 followed = insert(:user)
255 User.follow(follower, followed)
256 follower = Repo.get(User, follower.id)
257 {:ok, activity} = CommonAPI.post(followed, %{"status" => "hey @#{follower.nickname}"})
258 refute Notification.create_notification(activity, follower)
259 end
260
261 test "it disables notifications from people the user does not follow" do
262 follower =
263 insert(:user, notification_settings: %Pleroma.User.NotificationSetting{non_follows: false})
264
265 followed = insert(:user)
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] = notifs = Notification.for_user(other_user)
449 assert length(notifs) == 2
450
451 assert n2.id > n1.id
452
453 {:ok, _activity} =
454 CommonAPI.post(user, %{
455 "status" => "hey yet again @#{other_user.nickname}!"
456 })
457
458 Notification.set_read_up_to(other_user, n2.id)
459
460 [n3, n2, n1] = Notification.for_user(other_user)
461
462 assert n1.seen == true
463 assert n2.seen == true
464 assert n3.seen == false
465 end
466 end
467
468 describe "for_user_since/2" do
469 defp days_ago(days) do
470 NaiveDateTime.add(
471 NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second),
472 -days * 60 * 60 * 24,
473 :second
474 )
475 end
476
477 test "Returns recent notifications" do
478 user1 = insert(:user)
479 user2 = insert(:user)
480
481 Enum.each(0..10, fn i ->
482 {:ok, _activity} =
483 CommonAPI.post(user1, %{
484 "status" => "hey ##{i} @#{user2.nickname}!"
485 })
486 end)
487
488 {old, new} = Enum.split(Notification.for_user(user2), 5)
489
490 Enum.each(old, fn notification ->
491 notification
492 |> cast(%{updated_at: days_ago(10)}, [:updated_at])
493 |> Pleroma.Repo.update!()
494 end)
495
496 recent_notifications_ids =
497 user2
498 |> Notification.for_user_since(
499 NaiveDateTime.add(NaiveDateTime.utc_now(), -5 * 86_400, :second)
500 )
501 |> Enum.map(& &1.id)
502
503 Enum.each(old, fn %{id: id} ->
504 refute id in recent_notifications_ids
505 end)
506
507 Enum.each(new, fn %{id: id} ->
508 assert id in recent_notifications_ids
509 end)
510 end
511 end
512
513 describe "notification target determination / get_notified_from_activity/2" do
514 test "it sends notifications to addressed users in new messages" do
515 user = insert(:user)
516 other_user = insert(:user)
517
518 {:ok, activity} =
519 CommonAPI.post(user, %{
520 "status" => "hey @#{other_user.nickname}!"
521 })
522
523 {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity)
524
525 assert other_user in enabled_receivers
526 end
527
528 test "it sends notifications to mentioned users in new messages" do
529 user = insert(:user)
530 other_user = insert(:user)
531
532 create_activity = %{
533 "@context" => "https://www.w3.org/ns/activitystreams",
534 "type" => "Create",
535 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
536 "actor" => user.ap_id,
537 "object" => %{
538 "type" => "Note",
539 "content" => "message with a Mention tag, but no explicit tagging",
540 "tag" => [
541 %{
542 "type" => "Mention",
543 "href" => other_user.ap_id,
544 "name" => other_user.nickname
545 }
546 ],
547 "attributedTo" => user.ap_id
548 }
549 }
550
551 {:ok, activity} = Transmogrifier.handle_incoming(create_activity)
552
553 {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity)
554
555 assert other_user in enabled_receivers
556 end
557
558 test "it does not send notifications to users who are only cc in new messages" do
559 user = insert(:user)
560 other_user = insert(:user)
561
562 create_activity = %{
563 "@context" => "https://www.w3.org/ns/activitystreams",
564 "type" => "Create",
565 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
566 "cc" => [other_user.ap_id],
567 "actor" => user.ap_id,
568 "object" => %{
569 "type" => "Note",
570 "content" => "hi everyone",
571 "attributedTo" => user.ap_id
572 }
573 }
574
575 {:ok, activity} = Transmogrifier.handle_incoming(create_activity)
576
577 {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity)
578
579 assert other_user not in enabled_receivers
580 end
581
582 test "it does not send notification to mentioned users in likes" do
583 user = insert(:user)
584 other_user = insert(:user)
585 third_user = insert(:user)
586
587 {:ok, activity_one} =
588 CommonAPI.post(user, %{
589 "status" => "hey @#{other_user.nickname}!"
590 })
591
592 {:ok, activity_two} = CommonAPI.favorite(third_user, activity_one.id)
593
594 {enabled_receivers, _disabled_receivers} =
595 Notification.get_notified_from_activity(activity_two)
596
597 assert other_user not in enabled_receivers
598 end
599
600 test "it does not send notification to mentioned users in announces" do
601 user = insert(:user)
602 other_user = insert(:user)
603 third_user = insert(:user)
604
605 {:ok, activity_one} =
606 CommonAPI.post(user, %{
607 "status" => "hey @#{other_user.nickname}!"
608 })
609
610 {:ok, activity_two, _} = CommonAPI.repeat(activity_one.id, third_user)
611
612 {enabled_receivers, _disabled_receivers} =
613 Notification.get_notified_from_activity(activity_two)
614
615 assert other_user not in enabled_receivers
616 end
617
618 test "it returns blocking recipient in disabled recipients list" do
619 user = insert(:user)
620 other_user = insert(:user)
621 {:ok, _user_relationship} = User.block(other_user, user)
622
623 {:ok, activity} = CommonAPI.post(user, %{"status" => "hey @#{other_user.nickname}!"})
624
625 {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
626
627 assert [] == enabled_receivers
628 assert [other_user] == disabled_receivers
629 end
630
631 test "it returns notification-muting recipient in disabled recipients list" do
632 user = insert(:user)
633 other_user = insert(:user)
634 {:ok, _user_relationships} = User.mute(other_user, user)
635
636 {:ok, activity} = CommonAPI.post(user, %{"status" => "hey @#{other_user.nickname}!"})
637
638 {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
639
640 assert [] == enabled_receivers
641 assert [other_user] == disabled_receivers
642 end
643
644 test "it returns thread-muting recipient in disabled recipients list" do
645 user = insert(:user)
646 other_user = insert(:user)
647
648 {:ok, activity} = CommonAPI.post(user, %{"status" => "hey @#{other_user.nickname}!"})
649
650 {:ok, _} = CommonAPI.add_mute(other_user, activity)
651
652 {:ok, same_context_activity} =
653 CommonAPI.post(user, %{
654 "status" => "hey-hey-hey @#{other_user.nickname}!",
655 "in_reply_to_status_id" => activity.id
656 })
657
658 {enabled_receivers, disabled_receivers} =
659 Notification.get_notified_from_activity(same_context_activity)
660
661 assert [other_user] == disabled_receivers
662 refute other_user in enabled_receivers
663 end
664
665 test "it returns non-following domain-blocking recipient in disabled recipients list" do
666 blocked_domain = "blocked.domain"
667 user = insert(:user, %{ap_id: "https://#{blocked_domain}/@actor"})
668 other_user = insert(:user)
669
670 {:ok, other_user} = User.block_domain(other_user, blocked_domain)
671
672 {:ok, activity} = CommonAPI.post(user, %{"status" => "hey @#{other_user.nickname}!"})
673
674 {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
675
676 assert [] == enabled_receivers
677 assert [other_user] == disabled_receivers
678 end
679
680 test "it returns following domain-blocking recipient in enabled recipients list" do
681 blocked_domain = "blocked.domain"
682 user = insert(:user, %{ap_id: "https://#{blocked_domain}/@actor"})
683 other_user = insert(:user)
684
685 {:ok, other_user} = User.block_domain(other_user, blocked_domain)
686 {:ok, other_user} = User.follow(other_user, user)
687
688 {:ok, activity} = CommonAPI.post(user, %{"status" => "hey @#{other_user.nickname}!"})
689
690 {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
691
692 assert [other_user] == enabled_receivers
693 assert [] == disabled_receivers
694 end
695 end
696
697 describe "notification lifecycle" do
698 test "liking an activity results in 1 notification, then 0 if the activity is deleted" do
699 user = insert(:user)
700 other_user = insert(:user)
701
702 {:ok, activity} = CommonAPI.post(user, %{"status" => "test post"})
703
704 assert Enum.empty?(Notification.for_user(user))
705
706 {:ok, _} = CommonAPI.favorite(other_user, activity.id)
707
708 assert length(Notification.for_user(user)) == 1
709
710 {:ok, _} = CommonAPI.delete(activity.id, user)
711
712 assert Enum.empty?(Notification.for_user(user))
713 end
714
715 test "liking an activity results in 1 notification, then 0 if the activity is unliked" do
716 user = insert(:user)
717 other_user = insert(:user)
718
719 {:ok, activity} = CommonAPI.post(user, %{"status" => "test post"})
720
721 assert Enum.empty?(Notification.for_user(user))
722
723 {:ok, _} = CommonAPI.favorite(other_user, activity.id)
724
725 assert length(Notification.for_user(user)) == 1
726
727 {:ok, _, _, _} = CommonAPI.unfavorite(activity.id, other_user)
728
729 assert Enum.empty?(Notification.for_user(user))
730 end
731
732 test "repeating an activity results in 1 notification, then 0 if the activity is deleted" do
733 user = insert(:user)
734 other_user = insert(:user)
735
736 {:ok, activity} = CommonAPI.post(user, %{"status" => "test post"})
737
738 assert Enum.empty?(Notification.for_user(user))
739
740 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
741
742 assert length(Notification.for_user(user)) == 1
743
744 {:ok, _} = CommonAPI.delete(activity.id, user)
745
746 assert Enum.empty?(Notification.for_user(user))
747 end
748
749 test "repeating an activity results in 1 notification, then 0 if the activity is unrepeated" do
750 user = insert(:user)
751 other_user = insert(:user)
752
753 {:ok, activity} = CommonAPI.post(user, %{"status" => "test post"})
754
755 assert Enum.empty?(Notification.for_user(user))
756
757 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
758
759 assert length(Notification.for_user(user)) == 1
760
761 {:ok, _, _} = CommonAPI.unrepeat(activity.id, other_user)
762
763 assert Enum.empty?(Notification.for_user(user))
764 end
765
766 test "liking an activity which is already deleted does not generate a notification" do
767 user = insert(:user)
768 other_user = insert(:user)
769
770 {:ok, activity} = CommonAPI.post(user, %{"status" => "test post"})
771
772 assert Enum.empty?(Notification.for_user(user))
773
774 {:ok, _deletion_activity} = CommonAPI.delete(activity.id, user)
775
776 assert Enum.empty?(Notification.for_user(user))
777
778 {:error, :not_found} = CommonAPI.favorite(other_user, activity.id)
779
780 assert Enum.empty?(Notification.for_user(user))
781 end
782
783 test "repeating an activity which is already deleted does not generate a notification" do
784 user = insert(:user)
785 other_user = insert(:user)
786
787 {:ok, activity} = CommonAPI.post(user, %{"status" => "test post"})
788
789 assert Enum.empty?(Notification.for_user(user))
790
791 {:ok, _deletion_activity} = CommonAPI.delete(activity.id, user)
792
793 assert Enum.empty?(Notification.for_user(user))
794
795 {:error, _} = CommonAPI.repeat(activity.id, other_user)
796
797 assert Enum.empty?(Notification.for_user(user))
798 end
799
800 test "replying to a deleted post without tagging does not generate a notification" do
801 user = insert(:user)
802 other_user = insert(:user)
803
804 {:ok, activity} = CommonAPI.post(user, %{"status" => "test post"})
805 {:ok, _deletion_activity} = CommonAPI.delete(activity.id, user)
806
807 {:ok, _reply_activity} =
808 CommonAPI.post(other_user, %{
809 "status" => "test reply",
810 "in_reply_to_status_id" => activity.id
811 })
812
813 assert Enum.empty?(Notification.for_user(user))
814 end
815
816 test "notifications are deleted if a local user is deleted" do
817 user = insert(:user)
818 other_user = insert(:user)
819
820 {:ok, _activity} =
821 CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}", "visibility" => "direct"})
822
823 refute Enum.empty?(Notification.for_user(other_user))
824
825 {:ok, job} = User.delete(user)
826 ObanHelpers.perform(job)
827
828 assert Enum.empty?(Notification.for_user(other_user))
829 end
830
831 test "notifications are deleted if a remote user is deleted" do
832 remote_user = insert(:user)
833 local_user = insert(:user)
834
835 dm_message = %{
836 "@context" => "https://www.w3.org/ns/activitystreams",
837 "type" => "Create",
838 "actor" => remote_user.ap_id,
839 "id" => remote_user.ap_id <> "/activities/test",
840 "to" => [local_user.ap_id],
841 "cc" => [],
842 "object" => %{
843 "type" => "Note",
844 "content" => "Hello!",
845 "tag" => [
846 %{
847 "type" => "Mention",
848 "href" => local_user.ap_id,
849 "name" => "@#{local_user.nickname}"
850 }
851 ],
852 "to" => [local_user.ap_id],
853 "cc" => [],
854 "attributedTo" => remote_user.ap_id
855 }
856 }
857
858 {:ok, _dm_activity} = Transmogrifier.handle_incoming(dm_message)
859
860 refute Enum.empty?(Notification.for_user(local_user))
861
862 delete_user_message = %{
863 "@context" => "https://www.w3.org/ns/activitystreams",
864 "id" => remote_user.ap_id <> "/activities/delete",
865 "actor" => remote_user.ap_id,
866 "type" => "Delete",
867 "object" => remote_user.ap_id
868 }
869
870 remote_user_url = remote_user.ap_id
871
872 Tesla.Mock.mock(fn
873 %{method: :get, url: ^remote_user_url} ->
874 %Tesla.Env{status: 404, body: ""}
875 end)
876
877 {:ok, _delete_activity} = Transmogrifier.handle_incoming(delete_user_message)
878 ObanHelpers.perform_all()
879
880 assert Enum.empty?(Notification.for_user(local_user))
881 end
882
883 @tag capture_log: true
884 test "move activity generates a notification" do
885 %{ap_id: old_ap_id} = old_user = insert(:user)
886 %{ap_id: new_ap_id} = new_user = insert(:user, also_known_as: [old_ap_id])
887 follower = insert(:user)
888 other_follower = insert(:user, %{allow_following_move: false})
889
890 User.follow(follower, old_user)
891 User.follow(other_follower, old_user)
892
893 old_user_url = old_user.ap_id
894
895 body =
896 File.read!("test/fixtures/users_mock/localhost.json")
897 |> String.replace("{{nickname}}", old_user.nickname)
898 |> Jason.encode!()
899
900 Tesla.Mock.mock(fn
901 %{method: :get, url: ^old_user_url} ->
902 %Tesla.Env{status: 200, body: body}
903 end)
904
905 Pleroma.Web.ActivityPub.ActivityPub.move(old_user, new_user)
906 ObanHelpers.perform_all()
907
908 assert [
909 %{
910 activity: %{
911 data: %{"type" => "Move", "actor" => ^old_ap_id, "target" => ^new_ap_id}
912 }
913 }
914 ] = Notification.for_user(follower)
915
916 assert [
917 %{
918 activity: %{
919 data: %{"type" => "Move", "actor" => ^old_ap_id, "target" => ^new_ap_id}
920 }
921 }
922 ] = Notification.for_user(other_follower)
923 end
924 end
925
926 describe "for_user" do
927 test "it returns notifications for muted user without notifications" do
928 user = insert(:user)
929 muted = insert(:user)
930 {:ok, _user_relationships} = User.mute(user, muted, false)
931
932 {:ok, _activity} = CommonAPI.post(muted, %{"status" => "hey @#{user.nickname}"})
933
934 assert length(Notification.for_user(user)) == 1
935 end
936
937 test "it doesn't return notifications for muted user with notifications" do
938 user = insert(:user)
939 muted = insert(:user)
940 {:ok, _user_relationships} = User.mute(user, muted)
941
942 {:ok, _activity} = CommonAPI.post(muted, %{"status" => "hey @#{user.nickname}"})
943
944 assert Notification.for_user(user) == []
945 end
946
947 test "it doesn't return notifications for blocked user" do
948 user = insert(:user)
949 blocked = insert(:user)
950 {:ok, _user_relationship} = User.block(user, blocked)
951
952 {:ok, _activity} = CommonAPI.post(blocked, %{"status" => "hey @#{user.nickname}"})
953
954 assert Notification.for_user(user) == []
955 end
956
957 test "it doesn't return notifications for domain-blocked non-followed user" do
958 user = insert(:user)
959 blocked = insert(:user, ap_id: "http://some-domain.com")
960 {:ok, user} = User.block_domain(user, "some-domain.com")
961
962 {:ok, _activity} = CommonAPI.post(blocked, %{"status" => "hey @#{user.nickname}"})
963
964 assert Notification.for_user(user) == []
965 end
966
967 test "it returns notifications for domain-blocked but followed user" do
968 user = insert(:user)
969 blocked = insert(:user, ap_id: "http://some-domain.com")
970
971 {:ok, user} = User.block_domain(user, "some-domain.com")
972 {:ok, _} = User.follow(user, blocked)
973
974 {:ok, _activity} = CommonAPI.post(blocked, %{"status" => "hey @#{user.nickname}"})
975
976 assert length(Notification.for_user(user)) == 1
977 end
978
979 test "it doesn't return notifications for muted thread" do
980 user = insert(:user)
981 another_user = insert(:user)
982
983 {:ok, activity} = CommonAPI.post(another_user, %{"status" => "hey @#{user.nickname}"})
984
985 {:ok, _} = Pleroma.ThreadMute.add_mute(user.id, activity.data["context"])
986 assert Notification.for_user(user) == []
987 end
988
989 test "it returns notifications from a muted user when with_muted is set" do
990 user = insert(:user)
991 muted = insert(:user)
992 {:ok, _user_relationships} = User.mute(user, muted)
993
994 {:ok, _activity} = CommonAPI.post(muted, %{"status" => "hey @#{user.nickname}"})
995
996 assert length(Notification.for_user(user, %{with_muted: true})) == 1
997 end
998
999 test "it doesn't return notifications from a blocked user when with_muted is set" do
1000 user = insert(:user)
1001 blocked = insert(:user)
1002 {:ok, _user_relationship} = User.block(user, blocked)
1003
1004 {:ok, _activity} = CommonAPI.post(blocked, %{"status" => "hey @#{user.nickname}"})
1005
1006 assert Enum.empty?(Notification.for_user(user, %{with_muted: true}))
1007 end
1008
1009 test "when with_muted is set, " <>
1010 "it doesn't return notifications from a domain-blocked non-followed user" do
1011 user = insert(:user)
1012 blocked = insert(:user, ap_id: "http://some-domain.com")
1013 {:ok, user} = User.block_domain(user, "some-domain.com")
1014
1015 {:ok, _activity} = CommonAPI.post(blocked, %{"status" => "hey @#{user.nickname}"})
1016
1017 assert Enum.empty?(Notification.for_user(user, %{with_muted: true}))
1018 end
1019
1020 test "it returns notifications from muted threads when with_muted is set" do
1021 user = insert(:user)
1022 another_user = insert(:user)
1023
1024 {:ok, activity} = CommonAPI.post(another_user, %{"status" => "hey @#{user.nickname}"})
1025
1026 {:ok, _} = Pleroma.ThreadMute.add_mute(user.id, activity.data["context"])
1027 assert length(Notification.for_user(user, %{with_muted: true})) == 1
1028 end
1029 end
1030 end