From 3d41ccc47bd59cb17e7c18a368e3da3fd885ff29 Mon Sep 17 00:00:00 2001
From: Tusooa Zhu <tusooa@kazv.moe>
Date: Fri, 17 Dec 2021 14:17:51 -0500
Subject: [PATCH 1/5] Allow updating accepted follow activities in
 Web.ActivityPub.Utils.update_follow_state_for_all/2

Mastodon uses the Reject activity also for the purpose of removing
a follower, in addition to reject a follow request. We should
also update the original Follow activity in this case.
---
 lib/pleroma/web/activity_pub/utils.ex        |  2 +-
 test/pleroma/web/activity_pub/utils_test.exs | 14 ++++++++++++++
 2 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex
index 1df53f79a..c1f6b2b49 100644
--- a/lib/pleroma/web/activity_pub/utils.ex
+++ b/lib/pleroma/web/activity_pub/utils.ex
@@ -446,7 +446,7 @@ def update_follow_state_for_all(
     |> Activity.Queries.by_type()
     |> Activity.Queries.by_actor(actor)
     |> Activity.Queries.by_object_id(object)
-    |> where(fragment("data->>'state' = 'pending'"))
+    |> where(fragment("data->>'state' = 'pending'") or fragment("data->>'state' = 'accept'"))
     |> update(set: [data: fragment("jsonb_set(data, '{state}', ?)", ^state)])
     |> Repo.update_all([])
 
diff --git a/test/pleroma/web/activity_pub/utils_test.exs b/test/pleroma/web/activity_pub/utils_test.exs
index ee3e1014e..62dc02f61 100644
--- a/test/pleroma/web/activity_pub/utils_test.exs
+++ b/test/pleroma/web/activity_pub/utils_test.exs
@@ -213,6 +213,20 @@ test "updates the state of all Follow activities with the same actor and object"
       assert refresh_record(follow_activity).data["state"] == "accept"
       assert refresh_record(follow_activity_two).data["state"] == "accept"
     end
+
+    test "also updates the state of accepted follows" do
+      user = insert(:user)
+      follower = insert(:user)
+
+      {:ok, _, _, follow_activity} = CommonAPI.follow(follower, user)
+      {:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user)
+
+      {:ok, follow_activity_two} =
+        Utils.update_follow_state_for_all(follow_activity_two, "reject")
+
+      assert refresh_record(follow_activity).data["state"] == "reject"
+      assert refresh_record(follow_activity_two).data["state"] == "reject"
+    end
   end
 
   describe "update_follow_state/2" do

From bfd870380c6dca1c3d460991181438a02c4915f9 Mon Sep 17 00:00:00 2001
From: Tusooa Zhu <tusooa@kazv.moe>
Date: Fri, 17 Dec 2021 14:42:45 -0500
Subject: [PATCH 2/5] Add test to ensure the blocked cease to have follow
 relationship to the blocker

https://git.pleroma.social/pleroma/pleroma/-/issues/2766
---
 test/pleroma/web/activity_pub/side_effects_test.exs | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/test/pleroma/web/activity_pub/side_effects_test.exs b/test/pleroma/web/activity_pub/side_effects_test.exs
index d0988619d..5ca68ccc8 100644
--- a/test/pleroma/web/activity_pub/side_effects_test.exs
+++ b/test/pleroma/web/activity_pub/side_effects_test.exs
@@ -88,6 +88,16 @@ test "it unfollows and blocks", %{user: user, blocked: blocked, block: block} do
       assert User.blocks?(user, blocked)
     end
 
+    test "it updates following relationship", %{user: user, blocked: blocked, block: block} do
+      {:ok, _, _} = SideEffects.handle(block)
+
+      refute Pleroma.FollowingRelationship.get(user, blocked)
+      assert User.get_follow_state(user, blocked) == nil
+      assert User.get_follow_state(blocked, user) == nil
+      assert User.get_follow_state(user, blocked, nil) == nil
+      assert User.get_follow_state(blocked, user, nil) == nil
+    end
+
     test "it blocks but does not unfollow if the relevant setting is set", %{
       user: user,
       blocked: blocked,

From 951d1592c7958c2225a868360455693dd36def96 Mon Sep 17 00:00:00 2001
From: Tusooa Zhu <tusooa@kazv.moe>
Date: Fri, 17 Dec 2021 16:44:22 -0500
Subject: [PATCH 3/5] Add test to ensure removed follower cease to have
 relationship with ex-followee

https://git.pleroma.social/pleroma/pleroma/-/issues/2802
---
 .../web/activity_pub/side_effects_test.exs    | 67 +++++++++++++++++++
 1 file changed, 67 insertions(+)

diff --git a/test/pleroma/web/activity_pub/side_effects_test.exs b/test/pleroma/web/activity_pub/side_effects_test.exs
index 5ca68ccc8..30dd63d4d 100644
--- a/test/pleroma/web/activity_pub/side_effects_test.exs
+++ b/test/pleroma/web/activity_pub/side_effects_test.exs
@@ -552,4 +552,71 @@ test "it streams out the announce", %{announce: announce} do
       end
     end
   end
+
+  describe "removing a follower" do
+    setup do
+      user = insert(:user)
+      followed = insert(:user)
+
+      {:ok, _, _, follow_activity} = CommonAPI.follow(user, followed)
+
+      {:ok, reject_data, []} = Builder.reject(followed, follow_activity)
+      {:ok, reject, _meta} = ActivityPub.persist(reject_data, local: true)
+
+      %{user: user, followed: followed, reject: reject}
+    end
+
+    test "", %{user: user, followed: followed, reject: reject} do
+      assert User.following?(user, followed)
+      assert Pleroma.FollowingRelationship.get(user, followed)
+
+      {:ok, _, _} = SideEffects.handle(reject)
+
+      refute User.following?(user, followed)
+      refute Pleroma.FollowingRelationship.get(user, followed)
+      assert User.get_follow_state(user, followed) == nil
+      assert User.get_follow_state(user, followed, nil) == nil
+    end
+  end
+
+  describe "removing a follower from remote" do
+    setup do
+      user = insert(:user)
+      followed = insert(:user, local: false)
+
+      # Mock a local-to-remote follow
+      {:ok, follow_data, []} = Builder.follow(user, followed)
+      follow_data =
+        follow_data
+        |> Map.put("state", "accept")
+      {:ok, follow, _meta} = ActivityPub.persist(follow_data, local: true)
+      {:ok, _, _} = SideEffects.handle(follow)
+
+      # Mock a remote-to-local accept
+      {:ok, accept_data, _} = Builder.accept(followed, follow)
+      {:ok, accept, _} = ActivityPub.persist(accept_data, local: false)
+      {:ok, _, _} = SideEffects.handle(accept)
+
+      # Mock a remote-to-local reject
+      {:ok, reject_data, []} = Builder.reject(followed, follow)
+      {:ok, reject, _meta} = ActivityPub.persist(reject_data, local: false)
+
+      %{user: user, followed: followed, reject: reject}
+    end
+
+    test "", %{user: user, followed: followed, reject: reject} do
+      assert User.following?(user, followed)
+      assert Pleroma.FollowingRelationship.get(user, followed)
+
+      {:ok, _, _} = SideEffects.handle(reject)
+
+      refute User.following?(user, followed)
+      refute Pleroma.FollowingRelationship.get(user, followed)
+
+      assert Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(user, followed).data["state"] == "reject"
+
+      assert User.get_follow_state(user, followed) == nil
+      assert User.get_follow_state(user, followed, nil) == nil
+    end
+  end
 end

From 538d5ac2100aac57814fbc11bb205be7bb205b96 Mon Sep 17 00:00:00 2001
From: Tusooa Zhu <tusooa@kazv.moe>
Date: Fri, 17 Dec 2021 16:47:48 -0500
Subject: [PATCH 4/5] Add changelog for
 https://git.pleroma.social/pleroma/pleroma/-/merge_requests/3568

---
 CHANGELOG.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index ecefba381..a3034c53b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -18,6 +18,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 
 ### Fixed
 - Subscription(Bell) Notifications: Don't create from Pipeline Ingested replies
+- Handle Reject for already-accepted Follows properly
 
 ### Removed
 

From 8376e83f61b8dbe61134e814e093e8fe7288440f Mon Sep 17 00:00:00 2001
From: Tusooa Zhu <tusooa@kazv.moe>
Date: Fri, 17 Dec 2021 16:52:50 -0500
Subject: [PATCH 5/5] Lint

---
 test/pleroma/web/activity_pub/side_effects_test.exs | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/test/pleroma/web/activity_pub/side_effects_test.exs b/test/pleroma/web/activity_pub/side_effects_test.exs
index 30dd63d4d..c6155ed18 100644
--- a/test/pleroma/web/activity_pub/side_effects_test.exs
+++ b/test/pleroma/web/activity_pub/side_effects_test.exs
@@ -586,9 +586,11 @@ test "", %{user: user, followed: followed, reject: reject} do
 
       # Mock a local-to-remote follow
       {:ok, follow_data, []} = Builder.follow(user, followed)
+
       follow_data =
         follow_data
         |> Map.put("state", "accept")
+
       {:ok, follow, _meta} = ActivityPub.persist(follow_data, local: true)
       {:ok, _, _} = SideEffects.handle(follow)
 
@@ -613,7 +615,8 @@ test "", %{user: user, followed: followed, reject: reject} do
       refute User.following?(user, followed)
       refute Pleroma.FollowingRelationship.get(user, followed)
 
-      assert Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(user, followed).data["state"] == "reject"
+      assert Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(user, followed).data["state"] ==
+               "reject"
 
       assert User.get_follow_state(user, followed) == nil
       assert User.get_follow_state(user, followed, nil) == nil