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