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