X-Git-Url: http://git.squeep.com/?a=blobdiff_plain;f=test%2Fnotification_test.exs;h=28f8df49dfb44daf2a35bbd5ec505bc0556e463b;hb=e9993acdbbd1649bbcbf3fb36581b91145fe6055;hp=12b4292aa8c4259aa977303b3a0695abb9dbcf5c;hpb=92a0210fb03ca3e0aefe769fb6b0ab7bda6e5336;p=akkoma diff --git a/test/notification_test.exs b/test/notification_test.exs index 12b4292aa..2200d03ea 100644 --- a/test/notification_test.exs +++ b/test/notification_test.exs @@ -1,15 +1,20 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2018 Pleroma Authors +# Copyright © 2017-2019 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.NotificationTest do use Pleroma.DataCase + + import Pleroma.Factory + alias Pleroma.Notification + alias Pleroma.Tests.ObanHelpers alias Pleroma.User alias Pleroma.Web.ActivityPub.Transmogrifier alias Pleroma.Web.CommonAPI - alias Pleroma.Web.TwitterAPI.TwitterAPI - import Pleroma.Factory + alias Pleroma.Web.Streamer + + import ExUnit.CaptureLog describe "create_notifications" do test "notifies someone when they are directly addressed" do @@ -18,7 +23,7 @@ defmodule Pleroma.NotificationTest do third_user = insert(:user) {:ok, activity} = - TwitterAPI.create_status(user, %{ + CommonAPI.post(user, %{ "status" => "hey @#{other_user.nickname} and @#{third_user.nickname}" }) @@ -29,60 +34,175 @@ defmodule Pleroma.NotificationTest do assert notification.activity_id == activity.id assert other_notification.activity_id == activity.id end + + test "it creates a notification for subscribed users" do + user = insert(:user) + subscriber = insert(:user) + + User.subscribe(subscriber, user) + + {:ok, status} = CommonAPI.post(user, %{"status" => "Akariiiin"}) + {:ok, [notification]} = Notification.create_notifications(status) + + assert notification.user_id == subscriber.id + end + + test "does not create a notification for subscribed users if status is a reply" do + user = insert(:user) + other_user = insert(:user) + subscriber = insert(:user) + + User.subscribe(subscriber, other_user) + + {:ok, activity} = CommonAPI.post(user, %{"status" => "test post"}) + + {:ok, _reply_activity} = + CommonAPI.post(other_user, %{ + "status" => "test reply", + "in_reply_to_status_id" => activity.id + }) + + user_notifications = Notification.for_user(user) + assert length(user_notifications) == 1 + + subscriber_notifications = Notification.for_user(subscriber) + assert Enum.empty?(subscriber_notifications) + end end describe "create_notification" do - test "it doesn't create a notification for user if the user blocks the activity author" do + @tag needs_streamer: true + test "it creates a notification for user and send to the 'user' and the 'user:notification' stream" do + user = insert(:user) + task = Task.async(fn -> assert_receive {:text, _}, 4_000 end) + task_user_notification = Task.async(fn -> assert_receive {:text, _}, 4_000 end) + Streamer.add_socket("user", %{transport_pid: task.pid, assigns: %{user: user}}) + + Streamer.add_socket( + "user:notification", + %{transport_pid: task_user_notification.pid, assigns: %{user: user}} + ) + activity = insert(:note_activity) - author = User.get_by_ap_id(activity.data["actor"]) + + notify = Notification.create_notification(activity, user) + assert notify.user_id == user.id + Task.await(task) + Task.await(task_user_notification) + end + + test "it creates a notification for user if the user blocks the activity author" do + activity = insert(:note_activity) + author = User.get_cached_by_ap_id(activity.data["actor"]) user = insert(:user) {:ok, user} = User.block(user, author) - assert nil == Notification.create_notification(activity, user) + assert Notification.create_notification(activity, user) + end + + test "it creates a notificatin for the user if the user mutes the activity author" do + muter = insert(:user) + muted = insert(:user) + {:ok, _} = User.mute(muter, muted) + muter = Repo.get(User, muter.id) + {:ok, activity} = CommonAPI.post(muted, %{"status" => "Hi @#{muter.nickname}"}) + + assert Notification.create_notification(activity, muter) + end + + test "notification created if user is muted without notifications" do + muter = insert(:user) + muted = insert(:user) + + {:ok, muter} = User.mute(muter, muted, false) + + {:ok, activity} = CommonAPI.post(muted, %{"status" => "Hi @#{muter.nickname}"}) + + assert Notification.create_notification(activity, muter) + end + + test "it creates a notification for an activity from a muted thread" do + muter = insert(:user) + other_user = insert(:user) + {:ok, activity} = CommonAPI.post(muter, %{"status" => "hey"}) + CommonAPI.add_mute(muter, activity) + + {:ok, activity} = + CommonAPI.post(other_user, %{ + "status" => "Hi @#{muter.nickname}", + "in_reply_to_status_id" => activity.id + }) + + assert Notification.create_notification(activity, muter) + end + + test "it disables notifications from followers" do + follower = insert(:user) + followed = insert(:user, notification_settings: %{"followers" => false}) + User.follow(follower, followed) + {:ok, activity} = CommonAPI.post(follower, %{"status" => "hey @#{followed.nickname}"}) + refute Notification.create_notification(activity, followed) + end + + test "it disables notifications from non-followers" do + follower = insert(:user) + followed = insert(:user, notification_settings: %{"non_followers" => false}) + {:ok, activity} = CommonAPI.post(follower, %{"status" => "hey @#{followed.nickname}"}) + refute Notification.create_notification(activity, followed) + end + + test "it disables notifications from people the user follows" do + follower = insert(:user, notification_settings: %{"follows" => false}) + followed = insert(:user) + User.follow(follower, followed) + follower = Repo.get(User, follower.id) + {:ok, activity} = CommonAPI.post(followed, %{"status" => "hey @#{follower.nickname}"}) + refute Notification.create_notification(activity, follower) + end + + test "it disables notifications from people the user does not follow" do + follower = insert(:user, notification_settings: %{"non_follows" => false}) + followed = insert(:user) + {:ok, activity} = CommonAPI.post(followed, %{"status" => "hey @#{follower.nickname}"}) + refute Notification.create_notification(activity, follower) end test "it doesn't create a notification for user if he is the activity author" do activity = insert(:note_activity) - author = User.get_by_ap_id(activity.data["actor"]) + author = User.get_cached_by_ap_id(activity.data["actor"]) - assert nil == Notification.create_notification(activity, author) + refute Notification.create_notification(activity, author) end test "it doesn't create a notification for follow-unfollow-follow chains" do user = insert(:user) followed_user = insert(:user) - {:ok, _, _, activity} = TwitterAPI.follow(user, %{"user_id" => followed_user.id}) + {:ok, _, _, activity} = CommonAPI.follow(user, followed_user) Notification.create_notification(activity, followed_user) - TwitterAPI.unfollow(user, %{"user_id" => followed_user.id}) - {:ok, _, _, activity_dupe} = TwitterAPI.follow(user, %{"user_id" => followed_user.id}) - assert nil == Notification.create_notification(activity_dupe, followed_user) + CommonAPI.unfollow(user, followed_user) + {:ok, _, _, activity_dupe} = CommonAPI.follow(user, followed_user) + refute Notification.create_notification(activity_dupe, followed_user) end - test "it doesn't create a notification for like-unlike-like chains" do + test "it doesn't create duplicate notifications for follow+subscribed users" do user = insert(:user) - liked_user = insert(:user) - {:ok, status} = TwitterAPI.create_status(liked_user, %{"status" => "Yui is best yuru"}) - {:ok, fav_status} = TwitterAPI.fav(user, status.id) - Notification.create_notification(fav_status, liked_user) - TwitterAPI.unfav(user, status.id) - {:ok, dupe} = TwitterAPI.fav(user, status.id) - assert nil == Notification.create_notification(dupe, liked_user) + subscriber = insert(:user) + + {:ok, _, _, _} = CommonAPI.follow(subscriber, user) + User.subscribe(subscriber, user) + {:ok, status} = CommonAPI.post(user, %{"status" => "Akariiiin"}) + {:ok, [_notif]} = Notification.create_notifications(status) end - test "it doesn't create a notification for repeat-unrepeat-repeat chains" do + test "it doesn't create subscription notifications if the recipient cannot see the status" do user = insert(:user) - retweeted_user = insert(:user) + subscriber = insert(:user) - {:ok, status} = - TwitterAPI.create_status(retweeted_user, %{ - "status" => "Send dupe notifications to the shadow realm" - }) + User.subscribe(subscriber, user) + + {:ok, status} = CommonAPI.post(user, %{"status" => "inwisible", "visibility" => "direct"}) - {:ok, retweeted_activity} = TwitterAPI.repeat(user, status.id) - Notification.create_notification(retweeted_activity, retweeted_user) - TwitterAPI.unrepeat(user, status.id) - {:ok, dupe} = TwitterAPI.repeat(user, status.id) - assert nil == Notification.create_notification(dupe, retweeted_user) + assert {:ok, []} == Notification.create_notifications(status) end end @@ -91,8 +211,7 @@ defmodule Pleroma.NotificationTest do user = insert(:user) other_user = insert(:user) - {:ok, activity} = - TwitterAPI.create_status(user, %{"status" => "hey @#{other_user.nickname}"}) + {:ok, activity} = CommonAPI.post(user, %{"status" => "hey @#{other_user.nickname}"}) {:ok, [notification]} = Notification.create_notifications(activity) {:ok, notification} = Notification.get(other_user, notification.id) @@ -104,8 +223,7 @@ defmodule Pleroma.NotificationTest do user = insert(:user) other_user = insert(:user) - {:ok, activity} = - TwitterAPI.create_status(user, %{"status" => "hey @#{other_user.nickname}"}) + {:ok, activity} = CommonAPI.post(user, %{"status" => "hey @#{other_user.nickname}"}) {:ok, [notification]} = Notification.create_notifications(activity) {:error, _notification} = Notification.get(user, notification.id) @@ -117,8 +235,7 @@ defmodule Pleroma.NotificationTest do user = insert(:user) other_user = insert(:user) - {:ok, activity} = - TwitterAPI.create_status(user, %{"status" => "hey @#{other_user.nickname}"}) + {:ok, activity} = CommonAPI.post(user, %{"status" => "hey @#{other_user.nickname}"}) {:ok, [notification]} = Notification.create_notifications(activity) {:ok, notification} = Notification.dismiss(other_user, notification.id) @@ -130,8 +247,7 @@ defmodule Pleroma.NotificationTest do user = insert(:user) other_user = insert(:user) - {:ok, activity} = - TwitterAPI.create_status(user, %{"status" => "hey @#{other_user.nickname}"}) + {:ok, activity} = CommonAPI.post(user, %{"status" => "hey @#{other_user.nickname}"}) {:ok, [notification]} = Notification.create_notifications(activity) {:error, _notification} = Notification.dismiss(user, notification.id) @@ -145,14 +261,14 @@ defmodule Pleroma.NotificationTest do third_user = insert(:user) {:ok, activity} = - TwitterAPI.create_status(user, %{ + CommonAPI.post(user, %{ "status" => "hey @#{other_user.nickname} and @#{third_user.nickname} !" }) {:ok, _notifs} = Notification.create_notifications(activity) {:ok, activity} = - TwitterAPI.create_status(user, %{ + CommonAPI.post(user, %{ "status" => "hey again @#{other_user.nickname} and @#{third_user.nickname} !" }) @@ -170,12 +286,12 @@ defmodule Pleroma.NotificationTest do other_user = insert(:user) {:ok, _activity} = - TwitterAPI.create_status(user, %{ + CommonAPI.post(user, %{ "status" => "hey @#{other_user.nickname}!" }) {:ok, _activity} = - TwitterAPI.create_status(user, %{ + CommonAPI.post(user, %{ "status" => "hey again @#{other_user.nickname}!" }) @@ -185,7 +301,7 @@ defmodule Pleroma.NotificationTest do assert n2.id > n1.id {:ok, _activity} = - TwitterAPI.create_status(user, %{ + CommonAPI.post(user, %{ "status" => "hey yet again @#{other_user.nickname}!" }) @@ -199,6 +315,51 @@ defmodule Pleroma.NotificationTest do end end + describe "for_user_since/2" do + defp days_ago(days) do + NaiveDateTime.add( + NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second), + -days * 60 * 60 * 24, + :second + ) + end + + test "Returns recent notifications" do + user1 = insert(:user) + user2 = insert(:user) + + Enum.each(0..10, fn i -> + {:ok, _activity} = + CommonAPI.post(user1, %{ + "status" => "hey ##{i} @#{user2.nickname}!" + }) + end) + + {old, new} = Enum.split(Notification.for_user(user2), 5) + + Enum.each(old, fn notification -> + notification + |> cast(%{updated_at: days_ago(10)}, [:updated_at]) + |> Pleroma.Repo.update!() + end) + + recent_notifications_ids = + user2 + |> Notification.for_user_since( + NaiveDateTime.add(NaiveDateTime.utc_now(), -5 * 86_400, :second) + ) + |> Enum.map(& &1.id) + + Enum.each(old, fn %{id: id} -> + refute id in recent_notifications_ids + end) + + Enum.each(new, fn %{id: id} -> + assert id in recent_notifications_ids + end) + end + end + describe "notification target determination" do test "it sends notifications to addressed users in new messages" do user = insert(:user) @@ -272,7 +433,7 @@ defmodule Pleroma.NotificationTest do "status" => "hey @#{other_user.nickname}!" }) - {:ok, activity_two, _} = CommonAPI.favorite(activity_one.id, third_user) + {:ok, activity_two} = CommonAPI.favorite(third_user, activity_one.id) assert other_user not in Notification.get_notified_from_activity(activity_two) end @@ -302,7 +463,7 @@ defmodule Pleroma.NotificationTest do assert Enum.empty?(Notification.for_user(user)) - {:ok, _, _} = CommonAPI.favorite(activity.id, other_user) + {:ok, _} = CommonAPI.favorite(other_user, activity.id) assert length(Notification.for_user(user)) == 1 @@ -319,7 +480,7 @@ defmodule Pleroma.NotificationTest do assert Enum.empty?(Notification.for_user(user)) - {:ok, _, _} = CommonAPI.favorite(activity.id, other_user) + {:ok, _} = CommonAPI.favorite(other_user, activity.id) assert length(Notification.for_user(user)) == 1 @@ -374,7 +535,9 @@ defmodule Pleroma.NotificationTest do assert Enum.empty?(Notification.for_user(user)) - {:error, _} = CommonAPI.favorite(activity.id, other_user) + assert capture_log(fn -> + {:error, _} = CommonAPI.favorite(other_user, activity.id) + end) =~ "[error]" assert Enum.empty?(Notification.for_user(user)) end @@ -411,5 +574,186 @@ defmodule Pleroma.NotificationTest do assert Enum.empty?(Notification.for_user(user)) end + + test "notifications are deleted if a local user is deleted" do + user = insert(:user) + other_user = insert(:user) + + {:ok, _activity} = + CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}", "visibility" => "direct"}) + + refute Enum.empty?(Notification.for_user(other_user)) + + {:ok, job} = User.delete(user) + ObanHelpers.perform(job) + + assert Enum.empty?(Notification.for_user(other_user)) + end + + test "notifications are deleted if a remote user is deleted" do + remote_user = insert(:user) + local_user = insert(:user) + + dm_message = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "type" => "Create", + "actor" => remote_user.ap_id, + "id" => remote_user.ap_id <> "/activities/test", + "to" => [local_user.ap_id], + "cc" => [], + "object" => %{ + "type" => "Note", + "content" => "Hello!", + "tag" => [ + %{ + "type" => "Mention", + "href" => local_user.ap_id, + "name" => "@#{local_user.nickname}" + } + ], + "to" => [local_user.ap_id], + "cc" => [], + "attributedTo" => remote_user.ap_id + } + } + + {:ok, _dm_activity} = Transmogrifier.handle_incoming(dm_message) + + refute Enum.empty?(Notification.for_user(local_user)) + + delete_user_message = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "id" => remote_user.ap_id <> "/activities/delete", + "actor" => remote_user.ap_id, + "type" => "Delete", + "object" => remote_user.ap_id + } + + {:ok, _delete_activity} = Transmogrifier.handle_incoming(delete_user_message) + ObanHelpers.perform_all() + + assert Enum.empty?(Notification.for_user(local_user)) + end + + test "move activity generates a notification" do + %{ap_id: old_ap_id} = old_user = insert(:user) + %{ap_id: new_ap_id} = new_user = insert(:user, also_known_as: [old_ap_id]) + follower = insert(:user) + other_follower = insert(:user, %{allow_following_move: false}) + + User.follow(follower, old_user) + User.follow(other_follower, old_user) + + Pleroma.Web.ActivityPub.ActivityPub.move(old_user, new_user) + ObanHelpers.perform_all() + + assert [ + %{ + activity: %{ + data: %{"type" => "Move", "actor" => ^old_ap_id, "target" => ^new_ap_id} + } + } + ] = Notification.for_user(follower) + + assert [ + %{ + activity: %{ + data: %{"type" => "Move", "actor" => ^old_ap_id, "target" => ^new_ap_id} + } + } + ] = Notification.for_user(other_follower) + end + end + + describe "for_user" do + test "it returns notifications for muted user without notifications" do + user = insert(:user) + muted = insert(:user) + {:ok, user} = User.mute(user, muted, false) + + {:ok, _activity} = CommonAPI.post(muted, %{"status" => "hey @#{user.nickname}"}) + + assert length(Notification.for_user(user)) == 1 + end + + test "it doesn't return notifications for muted user with notifications" do + user = insert(:user) + muted = insert(:user) + {:ok, user} = User.mute(user, muted) + + {:ok, _activity} = CommonAPI.post(muted, %{"status" => "hey @#{user.nickname}"}) + + assert Notification.for_user(user) == [] + end + + test "it doesn't return notifications for blocked user" do + user = insert(:user) + blocked = insert(:user) + {:ok, user} = User.block(user, blocked) + + {:ok, _activity} = CommonAPI.post(blocked, %{"status" => "hey @#{user.nickname}"}) + + assert Notification.for_user(user) == [] + end + + test "it doesn't return notificatitons for blocked domain" do + user = insert(:user) + blocked = insert(:user, ap_id: "http://some-domain.com") + {:ok, user} = User.block_domain(user, "some-domain.com") + + {:ok, _activity} = CommonAPI.post(blocked, %{"status" => "hey @#{user.nickname}"}) + + assert Notification.for_user(user) == [] + end + + test "it doesn't return notifications for muted thread" do + user = insert(:user) + another_user = insert(:user) + + {:ok, activity} = CommonAPI.post(another_user, %{"status" => "hey @#{user.nickname}"}) + + {:ok, _} = Pleroma.ThreadMute.add_mute(user.id, activity.data["context"]) + assert Notification.for_user(user) == [] + end + + test "it returns notifications from a muted user when with_muted is set" do + user = insert(:user) + muted = insert(:user) + {:ok, user} = User.mute(user, muted) + + {:ok, _activity} = CommonAPI.post(muted, %{"status" => "hey @#{user.nickname}"}) + + assert length(Notification.for_user(user, %{with_muted: true})) == 1 + end + + test "it doesn't return notifications from a blocked user when with_muted is set" do + user = insert(:user) + blocked = insert(:user) + {:ok, user} = User.block(user, blocked) + + {:ok, _activity} = CommonAPI.post(blocked, %{"status" => "hey @#{user.nickname}"}) + + assert length(Notification.for_user(user, %{with_muted: true})) == 0 + end + + test "it doesn't return notifications from a domain-blocked user when with_muted is set" do + user = insert(:user) + blocked = insert(:user, ap_id: "http://some-domain.com") + {:ok, user} = User.block_domain(user, "some-domain.com") + + {:ok, _activity} = CommonAPI.post(blocked, %{"status" => "hey @#{user.nickname}"}) + + assert length(Notification.for_user(user, %{with_muted: true})) == 0 + end + + test "it returns notifications from muted threads when with_muted is set" do + user = insert(:user) + another_user = insert(:user) + + {:ok, activity} = CommonAPI.post(another_user, %{"status" => "hey @#{user.nickname}"}) + + {:ok, _} = Pleroma.ThreadMute.add_mute(user.id, activity.data["context"]) + assert length(Notification.for_user(user, %{with_muted: true})) == 1 + end end end