User: Don't error out when following a user that's already followed.
authorlain <lain@soykaf.club>
Wed, 5 Jun 2019 10:06:45 +0000 (12:06 +0200)
committerlain <lain@soykaf.club>
Wed, 5 Jun 2019 10:06:45 +0000 (12:06 +0200)
This leads to a few situations where it is impossible to follow a user.

lib/pleroma/user.ex
test/web/activity_pub/transmogrifier/follow_handling_test.exs [new file with mode: 0644]
test/web/activity_pub/transmogrifier_test.exs
test/web/twitter_api/twitter_api_test.exs

index dc534b05c1e42aa9da56abba0dcf01cdafbf1461..48b9f1d7d44ff7c6506c765af2ffe56ec515ec54 100644 (file)
@@ -370,8 +370,8 @@ defmodule Pleroma.User do
     ap_followers = followed.follower_address
 
     cond do
-      following?(follower, followed) or info.deactivated ->
-        {:error, "Could not follow user: #{followed.nickname} is already on your list."}
+      info.deactivated ->
+        {:error, "Could not follow user: You are deactivatedt."}
 
       deny_follow_blocked and blocks?(followed, follower) ->
         {:error, "Could not follow user: #{followed.nickname} blocked you."}
diff --git a/test/web/activity_pub/transmogrifier/follow_handling_test.exs b/test/web/activity_pub/transmogrifier/follow_handling_test.exs
new file mode 100644 (file)
index 0000000..9f89e87
--- /dev/null
@@ -0,0 +1,115 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.Transmogrifier.FollowHandlingTest do
+  use Pleroma.DataCase
+  alias Pleroma.Activity
+  alias Pleroma.Repo
+  alias Pleroma.User
+  alias Pleroma.Web.ActivityPub.Transmogrifier
+  alias Pleroma.Web.ActivityPub.Utils
+
+  import Pleroma.Factory
+  import Ecto.Query
+
+  setup_all do
+    Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
+    :ok
+  end
+
+  describe "handle_incoming" do
+    test "it works for incoming follow requests" do
+      user = insert(:user)
+
+      data =
+        File.read!("test/fixtures/mastodon-follow-activity.json")
+        |> Poison.decode!()
+        |> Map.put("object", user.ap_id)
+
+      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
+
+      assert data["actor"] == "http://mastodon.example.org/users/admin"
+      assert data["type"] == "Follow"
+      assert data["id"] == "http://mastodon.example.org/users/admin#follows/2"
+      assert User.following?(User.get_cached_by_ap_id(data["actor"]), user)
+    end
+
+    test "it works for follow requests when you are already followed, creating a new accept activity" do
+      # This is important because the remote might have the wrong idea about the current follow status.
+      # This can lead to instance A thinking that x@A is followed by y@B, but B thinks they are not. In
+      # this case, the follow can never go through again because it will never get an Accept.
+      user = insert(:user)
+
+      data =
+        File.read!("test/fixtures/mastodon-follow-activity.json")
+        |> Poison.decode!()
+        |> Map.put("object", user.ap_id)
+
+      {:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(data)
+
+      accepts =
+        from(
+          a in Activity,
+          where: fragment("?->>'type' = ?", a.data, "Accept")
+        )
+        |> Repo.all()
+
+      assert length(accepts) == 1
+
+      data =
+        File.read!("test/fixtures/mastodon-follow-activity.json")
+        |> Poison.decode!()
+        |> Map.put("id", String.replace(data["id"], "2", "3"))
+        |> Map.put("object", user.ap_id)
+
+      {:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(data)
+
+      accepts =
+        from(
+          a in Activity,
+          where: fragment("?->>'type' = ?", a.data, "Accept")
+        )
+        |> Repo.all()
+
+      assert length(accepts) == 2
+    end
+
+    test "it rejects incoming follow requests from blocked users when deny_follow_blocked is enabled" do
+      Pleroma.Config.put([:user, :deny_follow_blocked], true)
+
+      user = insert(:user)
+      {:ok, target} = User.get_or_fetch("http://mastodon.example.org/users/admin")
+
+      {:ok, user} = User.block(user, target)
+
+      data =
+        File.read!("test/fixtures/mastodon-follow-activity.json")
+        |> Poison.decode!()
+        |> Map.put("object", user.ap_id)
+
+      {:ok, %Activity{data: %{"id" => id}}} = Transmogrifier.handle_incoming(data)
+
+      %Activity{} = activity = Activity.get_by_ap_id(id)
+
+      assert activity.data["state"] == "reject"
+    end
+
+    test "it works for incoming follow requests from hubzilla" do
+      user = insert(:user)
+
+      data =
+        File.read!("test/fixtures/hubzilla-follow-activity.json")
+        |> Poison.decode!()
+        |> Map.put("object", user.ap_id)
+        |> Utils.normalize_params()
+
+      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
+
+      assert data["actor"] == "https://hubzilla.example.org/channel/kaniini"
+      assert data["type"] == "Follow"
+      assert data["id"] == "https://hubzilla.example.org/channel/kaniini#follows/2"
+      assert User.following?(User.get_cached_by_ap_id(data["actor"]), user)
+    end
+  end
+end
index 89c8f79c92644dae846ba8f35dc2ef56a2e36553..28971ae450d69ad182721ac02f2457abc297bea9 100644 (file)
@@ -11,7 +11,6 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
   alias Pleroma.User
   alias Pleroma.Web.ActivityPub.ActivityPub
   alias Pleroma.Web.ActivityPub.Transmogrifier
-  alias Pleroma.Web.ActivityPub.Utils
   alias Pleroma.Web.OStatus
   alias Pleroma.Web.Websub.WebsubClientSubscription
 
@@ -248,59 +247,6 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
       assert object_data["cc"] == to
     end
 
-    test "it works for incoming follow requests" do
-      user = insert(:user)
-
-      data =
-        File.read!("test/fixtures/mastodon-follow-activity.json")
-        |> Poison.decode!()
-        |> Map.put("object", user.ap_id)
-
-      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
-
-      assert data["actor"] == "http://mastodon.example.org/users/admin"
-      assert data["type"] == "Follow"
-      assert data["id"] == "http://mastodon.example.org/users/admin#follows/2"
-      assert User.following?(User.get_cached_by_ap_id(data["actor"]), user)
-    end
-
-    test "it rejects incoming follow requests from blocked users when deny_follow_blocked is enabled" do
-      Pleroma.Config.put([:user, :deny_follow_blocked], true)
-
-      user = insert(:user)
-      {:ok, target} = User.get_or_fetch("http://mastodon.example.org/users/admin")
-
-      {:ok, user} = User.block(user, target)
-
-      data =
-        File.read!("test/fixtures/mastodon-follow-activity.json")
-        |> Poison.decode!()
-        |> Map.put("object", user.ap_id)
-
-      {:ok, %Activity{data: %{"id" => id}}} = Transmogrifier.handle_incoming(data)
-
-      %Activity{} = activity = Activity.get_by_ap_id(id)
-
-      assert activity.data["state"] == "reject"
-    end
-
-    test "it works for incoming follow requests from hubzilla" do
-      user = insert(:user)
-
-      data =
-        File.read!("test/fixtures/hubzilla-follow-activity.json")
-        |> Poison.decode!()
-        |> Map.put("object", user.ap_id)
-        |> Utils.normalize_params()
-
-      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
-
-      assert data["actor"] == "https://hubzilla.example.org/channel/kaniini"
-      assert data["type"] == "Follow"
-      assert data["id"] == "https://hubzilla.example.org/channel/kaniini#follows/2"
-      assert User.following?(User.get_cached_by_ap_id(data["actor"]), user)
-    end
-
     test "it works for incoming likes" do
       user = insert(:user)
       {:ok, activity} = CommonAPI.post(user, %{"status" => "hello"})
index d601c8f1f2bbd5a26c96b0acb7002401bffe1c73..475531a097e58dfcde78ae32eb6474e7a9284f76 100644 (file)
@@ -116,8 +116,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
     {:ok, user, followed, _activity} = TwitterAPI.follow(user, %{"user_id" => followed.id})
     assert User.ap_followers(followed) in user.following
 
-    {:error, msg} = TwitterAPI.follow(user, %{"user_id" => followed.id})
-    assert msg == "Could not follow user: #{followed.nickname} is already on your list."
+    {:ok, _, _, _} = TwitterAPI.follow(user, %{"user_id" => followed.id})
   end
 
   test "Follow another user using screen_name" do
@@ -132,8 +131,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
     followed = User.get_cached_by_ap_id(followed.ap_id)
     assert followed.info.follower_count == 1
 
-    {:error, msg} = TwitterAPI.follow(user, %{"screen_name" => followed.nickname})
-    assert msg == "Could not follow user: #{followed.nickname} is already on your list."
+    {:ok, _, _, _} = TwitterAPI.follow(user, %{"screen_name" => followed.nickname})
   end
 
   test "Unfollow another user using user_id" do