Merge branch 'remake-remodel' into develop
authorlain <lain@soykaf.club>
Thu, 19 Mar 2020 17:00:55 +0000 (18:00 +0100)
committerlain <lain@soykaf.club>
Thu, 19 Mar 2020 17:00:55 +0000 (18:00 +0100)
22 files changed:
1  2 
lib/pleroma/object/containment.ex
lib/pleroma/web/activity_pub/activity_pub.ex
lib/pleroma/web/activity_pub/object_validator.ex
lib/pleroma/web/activity_pub/transmogrifier.ex
lib/pleroma/web/common_api/common_api.ex
lib/pleroma/web/mastodon_api/controllers/status_controller.ex
test/notification_test.exs
test/object_test.exs
test/stat_test.exs
test/tasks/database_test.exs
test/user_test.exs
test/web/activity_pub/activity_pub_test.exs
test/web/activity_pub/transmogrifier_test.exs
test/web/activity_pub/views/object_view_test.exs
test/web/common_api/common_api_test.exs
test/web/mastodon_api/controllers/notification_controller_test.exs
test/web/mastodon_api/controllers/status_controller_test.exs
test/web/mastodon_api/views/notification_view_test.exs
test/web/ostatus/ostatus_controller_test.exs
test/web/pleroma_api/controllers/account_controller_test.exs
test/web/push/impl_test.exs
test/web/streamer/streamer_test.exs

Simple merge
index d9f74b6a4928004f074025369bd26183aa517253,673fc8a9979be478ec7d4052151addad6af437f5..d9f30e629607ae6a90664ac798908ecf7d30db0f
@@@ -125,7 -124,23 +125,24 @@@ defmodule Pleroma.Web.ActivityPub.Activ
  
    def increase_poll_votes_if_vote(_create_data), do: :noop
  
 +  @spec insert(map(), boolean(), boolean(), boolean()) :: {:ok, Activity.t()} | {:error, any()}
+   # TODO rewrite in with style
+   @spec persist(map(), keyword()) :: {:ok, Activity.t() | Object.t()}
+   def persist(object, meta) do
+     local = Keyword.fetch!(meta, :local)
+     {recipients, _, _} = get_recipients(object)
+     {:ok, activity} =
+       Repo.insert(%Activity{
+         data: object,
+         local: local,
+         recipients: recipients,
+         actor: object["actor"]
+       })
+     {:ok, activity, meta}
+   end
    def insert(map, local \\ true, fake \\ false, bypass_actor_check \\ false) when is_map(map) do
      with nil <- Activity.normalize(map),
           map <- lazy_put_activity_defaults(map, fake),
index 0000000000000000000000000000000000000000,539be11437f133b60b04cfa4ec50d8810e7dece3..cff92404721846f2b8b90c8b03d46364f5211bb0
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,41 +1,38 @@@
 -    with {_, {:ok, object}} <-
 -           {:validate_object,
 -            object |> LikeValidator.cast_and_validate() |> Ecto.Changeset.apply_action(:insert)} do
+ # Pleroma: A lightweight social networking server
+ # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+ # SPDX-License-Identifier: AGPL-3.0-only
+ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
+   @moduledoc """
+   This module is responsible for validating an object (which can be an activity)
+   and checking if it is both well formed and also compatible with our view of
+   the system.
+   """
+   alias Pleroma.Object
+   alias Pleroma.User
+   alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator
+   @spec validate(map(), keyword()) :: {:ok, map(), keyword()} | {:error, any()}
+   def validate(object, meta)
+   def validate(%{"type" => "Like"} = object, meta) do
 -    else
 -      e -> {:error, e}
++    with {:ok, object} <-
++           object |> LikeValidator.cast_and_validate() |> Ecto.Changeset.apply_action(:insert) do
+       object = stringify_keys(object |> Map.from_struct())
+       {:ok, object, meta}
+     end
+   end
+   def stringify_keys(object) do
+     object
+     |> Enum.map(fn {key, val} -> {to_string(key), val} end)
+     |> Enum.into(%{})
+   end
+   def fetch_actor_and_object(object) do
+     User.get_or_fetch_by_ap_id(object["actor"])
+     Object.normalize(object["object"])
+     :ok
+   end
+ end
index 091011c6b1082abf268221c2b80c2a4169e66cf8,a95fffcfcc6f68050823275ba5a8c0dfa00842bb..f882f9fcb790fac49ebd88ac81562cdccd44c9dc
@@@ -10,8 -10,9 +10,10 @@@ defmodule Pleroma.Web.CommonAPI d
    alias Pleroma.Object
    alias Pleroma.ThreadMute
    alias Pleroma.User
 +  alias Pleroma.UserRelationship
    alias Pleroma.Web.ActivityPub.ActivityPub
+   alias Pleroma.Web.ActivityPub.Builder
+   alias Pleroma.Web.ActivityPub.Pipeline
    alias Pleroma.Web.ActivityPub.Utils
    alias Pleroma.Web.ActivityPub.Visibility
  
      end
    end
  
-   def favorite(id_or_ap_id, user) do
-     with {_, %Activity{} = activity} <- {:find_activity, get_by_id_or_ap_id(id_or_ap_id)},
-          object <- Object.normalize(activity),
-          like_activity <- Utils.get_existing_like(user.ap_id, object) do
-       if like_activity do
-         {:ok, like_activity, object}
-       else
-         ActivityPub.like(user, object)
-       end
+   @spec favorite(User.t(), binary()) :: {:ok, Activity.t()} | {:error, any()}
+   def favorite(%User{} = user, id) do
+     with {_, %Activity{object: object}} <- {:find_object, Activity.get_by_id_with_object(id)},
+          {_, {:ok, like_object, meta}} <- {:build_object, Builder.like(user, object)},
+          {_, {:ok, %Activity{} = activity, _meta}} <-
+            {:common_pipeline,
+             Pipeline.common_pipeline(like_object, Keyword.put(meta, :local, true))} do
+       {:ok, activity}
      else
-       {:find_activity, _} -> {:error, :not_found}
-       _ -> {:error, dgettext("errors", "Could not favorite")}
++      {:find_object, _} ->
++        {:error, :not_found}
++
++      {:common_pipeline,
++       {
++         :error,
++         {
++           :validate_object,
++           {
++             :error,
++             changeset
++           }
++         }
++       }} = e ->
++        if {:object, {"already liked by this actor", []}} in changeset.errors do
++          {:ok, :already_liked}
++        else
++          Logger.error("Could not favorite #{id}. Error: #{inspect(e, pretty: true)}")
++          {:error, dgettext("errors", "Could not favorite"), e}
++        end
++
+       e ->
+         Logger.error("Could not favorite #{id}. Error: #{inspect(e, pretty: true)}")
 -        {:error, dgettext("errors", "Could not favorite")}
++        {:error, dgettext("errors", "Could not favorite"), e}
      end
    end
  
index 56a5818103ecf7a0ecc615f1e95d9db50f580386,2200d03ea76da3843e279dd2fb7ad9c6a6093258..81eb0e2f7a0e05dfdfc87eb91a7694a5fee0ffcc
@@@ -557,7 -535,9 +557,7 @@@ defmodule Pleroma.NotificationTest d
  
        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]"
++      {:error, :not_found} = CommonAPI.favorite(other_user, activity.id)
  
        assert Enum.empty?(Notification.for_user(user))
      end
Simple merge
index 33b77e7e72559454f89dbf019f1121aa64a48486,0000000000000000000000000000000000000000..bccc1c8d07a435bb5d2df6c07d4507a2deb80f5c
mode 100644,000000..100644
--- /dev/null
@@@ -1,70 -1,0 +1,70 @@@
-       CommonAPI.favorite(activity.id, other_user)
 +# Pleroma: A lightweight social networking server
 +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
 +# SPDX-License-Identifier: AGPL-3.0-only
 +
 +defmodule Pleroma.StateTest do
 +  use Pleroma.DataCase
 +  import Pleroma.Factory
 +  alias Pleroma.Web.CommonAPI
 +
 +  describe "status visibility count" do
 +    test "on new status" do
 +      user = insert(:user)
 +      other_user = insert(:user)
 +
 +      CommonAPI.post(user, %{"visibility" => "public", "status" => "hey"})
 +
 +      Enum.each(0..1, fn _ ->
 +        CommonAPI.post(user, %{
 +          "visibility" => "unlisted",
 +          "status" => "hey"
 +        })
 +      end)
 +
 +      Enum.each(0..2, fn _ ->
 +        CommonAPI.post(user, %{
 +          "visibility" => "direct",
 +          "status" => "hey @#{other_user.nickname}"
 +        })
 +      end)
 +
 +      Enum.each(0..3, fn _ ->
 +        CommonAPI.post(user, %{
 +          "visibility" => "private",
 +          "status" => "hey"
 +        })
 +      end)
 +
 +      assert %{direct: 3, private: 4, public: 1, unlisted: 2} =
 +               Pleroma.Stats.get_status_visibility_count()
 +    end
 +
 +    test "on status delete" do
 +      user = insert(:user)
 +      {:ok, activity} = CommonAPI.post(user, %{"visibility" => "public", "status" => "hey"})
 +      assert %{public: 1} = Pleroma.Stats.get_status_visibility_count()
 +      CommonAPI.delete(activity.id, user)
 +      assert %{public: 0} = Pleroma.Stats.get_status_visibility_count()
 +    end
 +
 +    test "on status visibility update" do
 +      user = insert(:user)
 +      {:ok, activity} = CommonAPI.post(user, %{"visibility" => "public", "status" => "hey"})
 +      assert %{public: 1, private: 0} = Pleroma.Stats.get_status_visibility_count()
 +      {:ok, _} = CommonAPI.update_activity_scope(activity.id, %{"visibility" => "private"})
 +      assert %{public: 0, private: 1} = Pleroma.Stats.get_status_visibility_count()
 +    end
 +
 +    test "doesn't count unrelated activities" do
 +      user = insert(:user)
 +      other_user = insert(:user)
 +      {:ok, activity} = CommonAPI.post(user, %{"visibility" => "public", "status" => "hey"})
 +      _ = CommonAPI.follow(user, other_user)
++      CommonAPI.favorite(other_user, activity.id)
 +      CommonAPI.repeat(activity.id, other_user)
 +
 +      assert %{direct: 0, private: 0, public: 1, unlisted: 0} =
 +               Pleroma.Stats.get_status_visibility_count()
 +    end
 +  end
 +end
Simple merge
Simple merge
index 3dd3dd04dcd878959cbba77e38b9398d5cc3ce12,2677b9e36bbd48b1a700a2dc9f5811f997dc237a..d5dd44cc31cd067816ff4d4cbf8814300cdd7b86
@@@ -1842,73 -1572,6 +1842,73 @@@ defmodule Pleroma.Web.ActivityPub.Activ
        assert follow_info.following_count == 32
        assert follow_info.hide_follows == true
      end
-       {:ok, _, _} = CommonAPI.favorite(a4.id, user)
-       {:ok, _, _} = CommonAPI.favorite(a3.id, other_user)
-       {:ok, _, _} = CommonAPI.favorite(a3.id, user)
-       {:ok, _, _} = CommonAPI.favorite(a5.id, other_user)
-       {:ok, _, _} = CommonAPI.favorite(a5.id, user)
-       {:ok, _, _} = CommonAPI.favorite(a4.id, other_user)
-       {:ok, _, _} = CommonAPI.favorite(a1.id, user)
-       {:ok, _, _} = CommonAPI.favorite(a1.id, other_user)
 +
 +    test "doesn't crash when follower and following counters are hidden" do
 +      mock(fn env ->
 +        case env.url do
 +          "http://localhost:4001/users/masto_hidden_counters/following" ->
 +            json(%{
 +              "@context" => "https://www.w3.org/ns/activitystreams",
 +              "id" => "http://localhost:4001/users/masto_hidden_counters/followers"
 +            })
 +
 +          "http://localhost:4001/users/masto_hidden_counters/following?page=1" ->
 +            %Tesla.Env{status: 403, body: ""}
 +
 +          "http://localhost:4001/users/masto_hidden_counters/followers" ->
 +            json(%{
 +              "@context" => "https://www.w3.org/ns/activitystreams",
 +              "id" => "http://localhost:4001/users/masto_hidden_counters/following"
 +            })
 +
 +          "http://localhost:4001/users/masto_hidden_counters/followers?page=1" ->
 +            %Tesla.Env{status: 403, body: ""}
 +        end
 +      end)
 +
 +      user =
 +        insert(:user,
 +          local: false,
 +          follower_address: "http://localhost:4001/users/masto_hidden_counters/followers",
 +          following_address: "http://localhost:4001/users/masto_hidden_counters/following"
 +        )
 +
 +      {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
 +
 +      assert follow_info.hide_followers == true
 +      assert follow_info.follower_count == 0
 +      assert follow_info.hide_follows == true
 +      assert follow_info.following_count == 0
 +    end
 +  end
 +
 +  describe "fetch_favourites/3" do
 +    test "returns a favourite activities sorted by adds to favorite" do
 +      user = insert(:user)
 +      other_user = insert(:user)
 +      user1 = insert(:user)
 +      user2 = insert(:user)
 +      {:ok, a1} = CommonAPI.post(user1, %{"status" => "bla"})
 +      {:ok, _a2} = CommonAPI.post(user2, %{"status" => "traps are happy"})
 +      {:ok, a3} = CommonAPI.post(user2, %{"status" => "Trees Are "})
 +      {:ok, a4} = CommonAPI.post(user2, %{"status" => "Agent Smith "})
 +      {:ok, a5} = CommonAPI.post(user1, %{"status" => "Red or Blue "})
 +
++      {:ok, _} = CommonAPI.favorite(user, a4.id)
++      {:ok, _} = CommonAPI.favorite(other_user, a3.id)
++      {:ok, _} = CommonAPI.favorite(user, a3.id)
++      {:ok, _} = CommonAPI.favorite(other_user, a5.id)
++      {:ok, _} = CommonAPI.favorite(user, a5.id)
++      {:ok, _} = CommonAPI.favorite(other_user, a4.id)
++      {:ok, _} = CommonAPI.favorite(user, a1.id)
++      {:ok, _} = CommonAPI.favorite(other_user, a1.id)
 +      result = ActivityPub.fetch_favourites(user)
 +
 +      assert Enum.map(result, & &1.id) == [a1.id, a5.id, a3.id, a4.id]
 +
 +      result = ActivityPub.fetch_favourites(user, %{"limit" => 2})
 +      assert Enum.map(result, & &1.id) == [a1.id, a5.id]
 +    end
    end
  
    describe "Move activity" do
index b80523160eca66bdf4f85d05c3cf44874a332396,d641f74787aa8f50eadbafbff9c1a7123d907cc9..c2ed1c7897db16aaa0edfb18f0bec9bdd1f4ae57
@@@ -284,27 -280,33 +284,30 @@@ defmodule Pleroma.Web.CommonAPITest d
        user = insert(:user)
        other_user = insert(:user)
  
-       {:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"})
+       {:ok, post_activity} = CommonAPI.post(other_user, %{"status" => "cofe"})
  
-       {:ok, %Activity{}, _} = CommonAPI.favorite(activity.id, user)
+       {:ok, %Activity{data: data}} = CommonAPI.favorite(user, post_activity.id)
+       assert data["type"] == "Like"
+       assert data["actor"] == user.ap_id
+       assert data["object"] == post_activity.data["object"]
      end
  
 -    test "retweeting a status twice returns an error" do
 +    test "retweeting a status twice returns the status" do
        user = insert(:user)
        other_user = insert(:user)
  
        {:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"})
 -      {:ok, %Activity{}, _object} = CommonAPI.repeat(activity.id, user)
 -      {:error, _} = CommonAPI.repeat(activity.id, user)
 +      {:ok, %Activity{} = activity, object} = CommonAPI.repeat(activity.id, user)
 +      {:ok, ^activity, ^object} = CommonAPI.repeat(activity.id, user)
      end
  
-     test "favoriting a status twice returns the status" do
 -    test "favoriting a status twice returns an error" do
++    test "favoriting a status twice returns ok, but without the like activity" do
        user = insert(:user)
        other_user = insert(:user)
  
        {:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"})
-       {:ok, %Activity{} = activity, object} = CommonAPI.favorite(activity.id, user)
-       {:ok, ^activity, ^object} = CommonAPI.favorite(activity.id, user)
+       {:ok, %Activity{}} = CommonAPI.favorite(user, activity.id)
 -
 -      assert capture_log(fn ->
 -               assert {:error, _} = CommonAPI.favorite(user, activity.id)
 -             end) =~ "[error]"
++      assert {:ok, :already_liked} = CommonAPI.favorite(user, activity.id)
      end
    end
  
index d452ddbdd5d45d4479ed261dedb20bef6d4c54a5,c0b3621de9c98fbbc0fd1578f73b1e37d11c68f7..3bc9aff16e7131bf4c7e9cd99d4b1d0ee8552524
@@@ -128,148 -137,59 +128,148 @@@ defmodule Pleroma.Web.MastodonAPI.Notif
      assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
    end
  
 -  test "filters notifications using exclude_visibilities", %{conn: conn} do
 -    user = insert(:user)
 -    other_user = insert(:user)
 -
 -    {:ok, public_activity} =
 -      CommonAPI.post(other_user, %{"status" => "@#{user.nickname}", "visibility" => "public"})
 +  describe "exclude_visibilities" do
 +    test "filters notifications for mentions" do
 +      %{user: user, conn: conn} = oauth_access(["read:notifications"])
 +      other_user = insert(:user)
  
 -    {:ok, direct_activity} =
 -      CommonAPI.post(other_user, %{"status" => "@#{user.nickname}", "visibility" => "direct"})
 -
 -    {:ok, unlisted_activity} =
 -      CommonAPI.post(other_user, %{"status" => "@#{user.nickname}", "visibility" => "unlisted"})
 -
 -    {:ok, private_activity} =
 -      CommonAPI.post(other_user, %{"status" => "@#{user.nickname}", "visibility" => "private"})
 -
 -    conn = assign(conn, :user, user)
 -
 -    conn_res =
 -      get(conn, "/api/v1/notifications", %{
 -        exclude_visibilities: ["public", "unlisted", "private"]
 -      })
 -
 -    assert [%{"status" => %{"id" => id}}] = json_response(conn_res, 200)
 -    assert id == direct_activity.id
 -
 -    conn_res =
 -      get(conn, "/api/v1/notifications", %{
 -        exclude_visibilities: ["public", "unlisted", "direct"]
 -      })
 -
 -    assert [%{"status" => %{"id" => id}}] = json_response(conn_res, 200)
 -    assert id == private_activity.id
 -
 -    conn_res =
 -      get(conn, "/api/v1/notifications", %{
 -        exclude_visibilities: ["public", "private", "direct"]
 -      })
 -
 -    assert [%{"status" => %{"id" => id}}] = json_response(conn_res, 200)
 -    assert id == unlisted_activity.id
 -
 -    conn_res =
 -      get(conn, "/api/v1/notifications", %{
 -        exclude_visibilities: ["unlisted", "private", "direct"]
 -      })
 +      {:ok, public_activity} =
 +        CommonAPI.post(other_user, %{"status" => "@#{user.nickname}", "visibility" => "public"})
 +
 +      {:ok, direct_activity} =
 +        CommonAPI.post(other_user, %{"status" => "@#{user.nickname}", "visibility" => "direct"})
 +
 +      {:ok, unlisted_activity} =
 +        CommonAPI.post(other_user, %{"status" => "@#{user.nickname}", "visibility" => "unlisted"})
 +
 +      {:ok, private_activity} =
 +        CommonAPI.post(other_user, %{"status" => "@#{user.nickname}", "visibility" => "private"})
 +
 +      conn_res =
 +        get(conn, "/api/v1/notifications", %{
 +          exclude_visibilities: ["public", "unlisted", "private"]
 +        })
 +
 +      assert [%{"status" => %{"id" => id}}] = json_response(conn_res, 200)
 +      assert id == direct_activity.id
 +
 +      conn_res =
 +        get(conn, "/api/v1/notifications", %{
 +          exclude_visibilities: ["public", "unlisted", "direct"]
 +        })
  
 -    assert [%{"status" => %{"id" => id}}] = json_response(conn_res, 200)
 -    assert id == public_activity.id
 +      assert [%{"status" => %{"id" => id}}] = json_response(conn_res, 200)
 +      assert id == private_activity.id
 +
 +      conn_res =
 +        get(conn, "/api/v1/notifications", %{
 +          exclude_visibilities: ["public", "private", "direct"]
 +        })
 +
 +      assert [%{"status" => %{"id" => id}}] = json_response(conn_res, 200)
 +      assert id == unlisted_activity.id
 +
 +      conn_res =
 +        get(conn, "/api/v1/notifications", %{
 +          exclude_visibilities: ["unlisted", "private", "direct"]
 +        })
 +
 +      assert [%{"status" => %{"id" => id}}] = json_response(conn_res, 200)
 +      assert id == public_activity.id
 +    end
 +
 +    test "filters notifications for Like activities" do
 +      user = insert(:user)
 +      %{user: other_user, conn: conn} = oauth_access(["read:notifications"])
 +
 +      {:ok, public_activity} =
 +        CommonAPI.post(other_user, %{"status" => ".", "visibility" => "public"})
 +
 +      {:ok, direct_activity} =
 +        CommonAPI.post(other_user, %{"status" => "@#{user.nickname}", "visibility" => "direct"})
 +
 +      {:ok, unlisted_activity} =
 +        CommonAPI.post(other_user, %{"status" => ".", "visibility" => "unlisted"})
 +
 +      {:ok, private_activity} =
 +        CommonAPI.post(other_user, %{"status" => ".", "visibility" => "private"})
 +
-       {:ok, _, _} = CommonAPI.favorite(public_activity.id, user)
-       {:ok, _, _} = CommonAPI.favorite(direct_activity.id, user)
-       {:ok, _, _} = CommonAPI.favorite(unlisted_activity.id, user)
-       {:ok, _, _} = CommonAPI.favorite(private_activity.id, user)
++      {:ok, _} = CommonAPI.favorite(user, public_activity.id)
++      {:ok, _} = CommonAPI.favorite(user, direct_activity.id)
++      {:ok, _} = CommonAPI.favorite(user, unlisted_activity.id)
++      {:ok, _} = CommonAPI.favorite(user, private_activity.id)
 +
 +      activity_ids =
 +        conn
 +        |> get("/api/v1/notifications", %{exclude_visibilities: ["direct"]})
 +        |> json_response(200)
 +        |> Enum.map(& &1["status"]["id"])
 +
 +      assert public_activity.id in activity_ids
 +      assert unlisted_activity.id in activity_ids
 +      assert private_activity.id in activity_ids
 +      refute direct_activity.id in activity_ids
 +
 +      activity_ids =
 +        conn
 +        |> get("/api/v1/notifications", %{exclude_visibilities: ["unlisted"]})
 +        |> json_response(200)
 +        |> Enum.map(& &1["status"]["id"])
 +
 +      assert public_activity.id in activity_ids
 +      refute unlisted_activity.id in activity_ids
 +      assert private_activity.id in activity_ids
 +      assert direct_activity.id in activity_ids
 +
 +      activity_ids =
 +        conn
 +        |> get("/api/v1/notifications", %{exclude_visibilities: ["private"]})
 +        |> json_response(200)
 +        |> Enum.map(& &1["status"]["id"])
 +
 +      assert public_activity.id in activity_ids
 +      assert unlisted_activity.id in activity_ids
 +      refute private_activity.id in activity_ids
 +      assert direct_activity.id in activity_ids
 +
 +      activity_ids =
 +        conn
 +        |> get("/api/v1/notifications", %{exclude_visibilities: ["public"]})
 +        |> json_response(200)
 +        |> Enum.map(& &1["status"]["id"])
 +
 +      refute public_activity.id in activity_ids
 +      assert unlisted_activity.id in activity_ids
 +      assert private_activity.id in activity_ids
 +      assert direct_activity.id in activity_ids
 +    end
 +
 +    test "filters notifications for Announce activities" do
 +      user = insert(:user)
 +      %{user: other_user, conn: conn} = oauth_access(["read:notifications"])
 +
 +      {:ok, public_activity} =
 +        CommonAPI.post(other_user, %{"status" => ".", "visibility" => "public"})
 +
 +      {:ok, unlisted_activity} =
 +        CommonAPI.post(other_user, %{"status" => ".", "visibility" => "unlisted"})
 +
 +      {:ok, _, _} = CommonAPI.repeat(public_activity.id, user)
 +      {:ok, _, _} = CommonAPI.repeat(unlisted_activity.id, user)
 +
 +      activity_ids =
 +        conn
 +        |> get("/api/v1/notifications", %{exclude_visibilities: ["unlisted"]})
 +        |> json_response(200)
 +        |> Enum.map(& &1["status"]["id"])
 +
 +      assert public_activity.id in activity_ids
 +      refute unlisted_activity.id in activity_ids
 +    end
    end
  
 -  test "filters notifications using exclude_types", %{conn: conn} do
 -    user = insert(:user)
 +  test "filters notifications using exclude_types" do
 +    %{user: user, conn: conn} = oauth_access(["read:notifications"])
      other_user = insert(:user)
  
      {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
index fbf63f608be841266a7889ca7d8891b759dff27e,2ce201e2e288bef40e1ca7926561f615d8b962f4..f36552041af93f2ab955277319d9448858da68b5
@@@ -693,29 -711,31 +693,33 @@@ defmodule Pleroma.Web.MastodonAPI.Statu
        assert to_string(activity.id) == id
      end
  
 -    test "returns 400 error for a wrong id", %{conn: conn} do
 -      user = insert(:user)
 +    test "favoriting twice will just return 200", %{conn: conn} do
 +      activity = insert(:note_activity)
  
 -      assert capture_log(fn ->
 -               conn =
 -                 conn
 -                 |> assign(:user, user)
 -                 |> post("/api/v1/statuses/1/favourite")
 +      post(conn, "/api/v1/statuses/#{activity.id}/favourite")
-       assert post(conn, "/api/v1/statuses/#{activity.id}/favourite") |> json_response(200)
 -               assert json_response(conn, 400) == %{"error" => "Could not favorite"}
 -             end) =~ "[error]"
++      assert post(conn, "/api/v1/statuses/#{activity.id}/favourite")
++             |> json_response(200)
 +    end
 +
 +    test "returns 404 error for a wrong id", %{conn: conn} do
-       conn = post(conn, "/api/v1/statuses/1/favourite")
++      conn =
++        conn
++        |> post("/api/v1/statuses/1/favourite")
 +
 +      assert json_response(conn, 404) == %{"error" => "Record not found"}
      end
    end
  
    describe "unfavoriting" do
 -    test "unfavorites a status and returns it", %{conn: conn} do
 +    setup do: oauth_access(["write:favourites"])
 +
 +    test "unfavorites a status and returns it", %{user: user, conn: conn} do
        activity = insert(:note_activity)
 -      user = insert(:user)
  
-       {:ok, _, _} = CommonAPI.favorite(activity.id, user)
+       {:ok, _} = CommonAPI.favorite(user, activity.id)
  
 -      conn =
 -        conn
 -        |> assign(:user, user)
 -        |> post("/api/v1/statuses/#{activity.id}/unfavourite")
 +      conn = post(conn, "/api/v1/statuses/#{activity.id}/unfavourite")
  
        assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
                 json_response(conn, 200)
        activity: activity
      } do
        other_user = insert(:user)
 -      {:ok, user} = User.block(user, other_user)
 +      {:ok, _user_relationship} = User.block(user, other_user)
  
-       {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
+       {:ok, _} = CommonAPI.favorite(other_user, activity.id)
  
        response =
          conn
        assert Enum.empty?(response)
      end
  
 -    test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
 +    test "does not fail on an unauthenticated request", %{activity: activity} do
        other_user = insert(:user)
-       {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
+       {:ok, _} = CommonAPI.favorite(other_user, activity.id)
  
        response =
 -        conn
 -        |> assign(:user, nil)
 +        build_conn()
          |> get("/api/v1/statuses/#{activity.id}/favourited_by")
          |> json_response(:ok)
  
            "visibility" => "direct"
          })
  
-       {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
+       {:ok, _} = CommonAPI.favorite(other_user, activity.id)
  
 -      conn
 -      |> assign(:user, nil)
 -      |> get("/api/v1/statuses/#{activity.id}/favourited_by")
 +      favourited_by_url = "/api/v1/statuses/#{activity.id}/favourited_by"
 +
 +      build_conn()
 +      |> get(favourited_by_url)
        |> json_response(404)
  
 -      response =
 +      conn =
          build_conn()
          |> assign(:user, other_user)
 -        |> get("/api/v1/statuses/#{activity.id}/favourited_by")
 +        |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["read:accounts"]))
 +
 +      conn
 +      |> assign(:token, nil)
 +      |> get(favourited_by_url)
 +      |> json_response(404)
 +
 +      response =
 +        conn
 +        |> get(favourited_by_url)
          |> json_response(200)
  
        [%{"id" => id}] = response
      {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
      {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
  
-     {:ok, _, _} = CommonAPI.favorite(activity.id, user)
+     {:ok, _} = CommonAPI.favorite(user, activity.id)
  
 -    first_conn =
 -      conn
 -      |> assign(:user, user)
 -      |> get("/api/v1/favourites")
 +    first_conn = get(conn, "/api/v1/favourites")
  
      assert [status] = json_response(first_conn, 200)
      assert status["id"] == to_string(activity.id)
index 245cc15796125cea78acfced7c3148e1bad505ad,c9f67c28071d58769d181ec25166e54ad9f4bdcf..3853a9bbb8bc97c173a10230167625a067cabb7b
@@@ -157,11 -183,14 +157,11 @@@ defmodule Pleroma.Web.PleromaAPI.Accoun
        user: user
      } do
        activity = insert(:note_activity)
-       CommonAPI.favorite(activity.id, user)
+       CommonAPI.favorite(user, activity.id)
  
 -      response =
 -        conn
 -        |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
 -        |> json_response(:ok)
 -
 -      assert length(response) == 1
 +      build_conn()
 +      |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
 +      |> json_response(403)
      end
  
      test "returns favorited DM only when user is logged in and he is one of recipients", %{
            "visibility" => "direct"
          })
  
-       CommonAPI.favorite(direct.id, user)
+       CommonAPI.favorite(user, direct.id)
  
 -      response =
 -        conn
 -        |> assign(:user, current_user)
 -        |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
 -        |> json_response(:ok)
 +      for u <- [user, current_user] do
 +        response =
 +          build_conn()
 +          |> assign(:user, u)
 +          |> assign(:token, insert(:oauth_token, user: u, scopes: ["read:favourites"]))
 +          |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
 +          |> json_response(:ok)
  
 -      assert length(response) == 1
 +        assert length(response) == 1
 +      end
  
 -      anonymous_response =
 -        conn
 -        |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
 -        |> json_response(:ok)
 -
 -      assert Enum.empty?(anonymous_response)
 +      build_conn()
 +      |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
 +      |> json_response(403)
      end
  
      test "does not return others' favorited DM when user is not one of recipients", %{
        assert json_response(conn, 404) == %{"error" => "Record not found"}
      end
  
 -    test "returns 403 error when user has hidden own favorites", %{
 -      conn: conn,
 -      current_user: current_user
 -    } do
 +    test "returns 403 error when user has hidden own favorites", %{conn: conn} do
        user = insert(:user, hide_favorites: true)
        activity = insert(:note_activity)
-       CommonAPI.favorite(activity.id, user)
+       CommonAPI.favorite(user, activity.id)
  
 -      conn =
 -        conn
 -        |> assign(:user, current_user)
 -        |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
 +      conn = get(conn, "/api/v1/pleroma/accounts/#{user.id}/favourites")
  
        assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
      end
  
 -    test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
 +    test "hides favorites for new users by default", %{conn: conn} do
        user = insert(:user)
        activity = insert(:note_activity)
-       CommonAPI.favorite(activity.id, user)
+       CommonAPI.favorite(user, activity.id)
  
 -      conn =
 -        conn
 -        |> assign(:user, current_user)
 -        |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
 -
        assert user.hide_favorites
 +      conn = get(conn, "/api/v1/pleroma/accounts/#{user.id}/favourites")
 +
        assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
      end
    end
Simple merge
index 339f99bbf8b39f2dc2b13fc8971bac19995e6a77,5a5b3514793f423b97b93f6e07eece6a4fbf9612..f0bafc0931da46f672eb5ca4f4fa351031ea2672
@@@ -63,12 -59,9 +63,9 @@@ defmodule Pleroma.Web.StreamerTest d
        user: user
      } do
        blocked = insert(:user)
 -      {:ok, user} = User.block(user, blocked)
 +      {:ok, _user_relationship} = User.block(user, blocked)
  
-       {:ok, activity} = CommonAPI.post(user, %{"status" => ":("})
-       {:ok, notif, _} = CommonAPI.favorite(activity.id, blocked)
 -      task = Task.async(fn -> refute_receive {:text, _}, 4_000 end)
 +      task = Task.async(fn -> refute_receive {:text, _}, @streamer_timeout end)
  
        Streamer.add_socket(
          "user:notification",
        user: user
      } do
        user2 = insert(:user)
 -      task = Task.async(fn -> refute_receive {:text, _}, 4_000 end)
 +
-       {:ok, activity} = CommonAPI.post(user, %{"status" => "super hot take"})
-       {:ok, activity} = CommonAPI.add_mute(user, activity)
-       {:ok, notif, _} = CommonAPI.favorite(activity.id, user2)
 +      task = Task.async(fn -> refute_receive {:text, _}, @streamer_timeout end)
  
        Streamer.add_socket(
          "user:notification",
          %{transport_pid: task.pid, assigns: %{user: user}}
        )
  
+       {:ok, activity} = CommonAPI.post(user, %{"status" => "super hot take"})
+       {:ok, activity} = CommonAPI.add_mute(user, activity)
+       {:ok, notif} = CommonAPI.favorite(user2, activity.id)
++
        Streamer.stream("user:notification", notif)
        Task.await(task)
      end
        user: user
      } do
        user2 = insert(:user, %{ap_id: "https://hecking-lewd-place.com/user/meanie"})
 -      task = Task.async(fn -> refute_receive {:text, _}, 4_000 end)
 +
-       {:ok, user} = User.block_domain(user, "hecking-lewd-place.com")
-       {:ok, activity} = CommonAPI.post(user, %{"status" => "super hot take"})
-       {:ok, notif, _} = CommonAPI.favorite(activity.id, user2)
 +      task = Task.async(fn -> refute_receive {:text, _}, @streamer_timeout end)
  
        Streamer.add_socket(
          "user:notification",
        user: user1
      }
  
 +    topics = %{
 +      "public" => [fake_socket]
 +    }
 +
 +    Worker.push_to_socket(topics, "public", announce_activity)
 +
 +    Task.await(task)
 +  end
 +
 +  test "it does send non-reblog notification for reblog-muted actors" do
 +    user1 = insert(:user)
 +    user2 = insert(:user)
 +    user3 = insert(:user)
 +    CommonAPI.hide_reblogs(user1, user2)
 +
      {:ok, create_activity} = CommonAPI.post(user3, %{"status" => "I'm kawen"})
-     {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, user2)
 -    {:ok, announce_activity, _} = CommonAPI.repeat(create_activity.id, user2)
++    {:ok, favorite_activity} = CommonAPI.favorite(user2, create_activity.id)
 +
 +    task =
 +      Task.async(fn ->
 +        assert_receive {:text, _}, 1_000
 +      end)
 +
 +    fake_socket = %StreamerSocket{
 +      transport_pid: task.pid,
 +      user: user1
 +    }
  
      topics = %{
        "public" => [fake_socket]