Fix following groups + Add interface to manage followers
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
@@ -3,19 +3,73 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier.FollowTest do
|
||||
|
||||
import Mobilizon.Factory
|
||||
alias Mobilizon.Actors
|
||||
alias Mobilizon.Actors.Follower
|
||||
alias Mobilizon.Federation.ActivityPub
|
||||
alias Mobilizon.Federation.ActivityPub.{Activity, Transmogrifier}
|
||||
|
||||
describe "handle incoming follow requests" do
|
||||
test "it works for incoming follow requests" do
|
||||
actor = insert(:actor)
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/mastodon-follow-activity.json")
|
||||
|> Jason.decode!()
|
||||
|> Map.put("object", actor.url)
|
||||
|
||||
{:ok, %Activity{data: data, local: false}, _} = Transmogrifier.handle_incoming(data)
|
||||
|
||||
assert data["actor"] == "https://social.tcit.fr/users/tcit"
|
||||
assert data["type"] == "Follow"
|
||||
assert data["id"] == "https://social.tcit.fr/users/tcit#follows/2"
|
||||
|
||||
actor = Actors.get_actor_with_preload(actor.id)
|
||||
assert Actors.is_following(Actors.get_actor_by_url!(data["actor"], true), actor)
|
||||
end
|
||||
|
||||
test "it rejects activities without a valid ID" do
|
||||
actor = insert(:actor)
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/mastodon-follow-activity.json")
|
||||
|> Jason.decode!()
|
||||
|> Map.put("object", actor.url)
|
||||
|> Map.put("id", "")
|
||||
|
||||
:error = Transmogrifier.handle_incoming(data)
|
||||
end
|
||||
|
||||
# test "it works for incoming follow requests from hubzilla" do
|
||||
# user = insert(:user)
|
||||
|
||||
# data =
|
||||
# File.read!("test/fixtures/hubzilla-follow-activity.json")
|
||||
# |> Jason.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.is_following(User.get_by_ap_id(data["actor"]), user)
|
||||
# end
|
||||
end
|
||||
|
||||
describe "handle incoming follow accept activities" do
|
||||
test "it works for incoming accepts which were pre-accepted" do
|
||||
test "it works for incoming accepts" do
|
||||
follower = insert(:actor)
|
||||
followed = insert(:actor)
|
||||
followed = insert(:actor, manually_approves_followers: false)
|
||||
|
||||
refute Actors.is_following(follower, followed)
|
||||
|
||||
{:ok, follow_activity, _} = ActivityPub.follow(follower, followed)
|
||||
assert Actors.is_following(follower, followed)
|
||||
|
||||
follow_object_id = follow_activity.data["id"]
|
||||
|
||||
assert %Follower{} = Actors.get_follower_by_url(follow_object_id)
|
||||
|
||||
accept_data =
|
||||
File.read!("test/fixtures/mastodon-accept-activity.json")
|
||||
|> Jason.decode!()
|
||||
@@ -24,7 +78,39 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier.FollowTest do
|
||||
object =
|
||||
accept_data["object"]
|
||||
|> Map.put("actor", follower.url)
|
||||
|> Map.put("id", follow_activity.data["id"])
|
||||
|> Map.put("id", follow_object_id)
|
||||
|
||||
accept_data = Map.put(accept_data, "object", object)
|
||||
|
||||
:error = Transmogrifier.handle_incoming(accept_data)
|
||||
|
||||
{:ok, follower} = Actors.get_actor_by_url(follower.url)
|
||||
|
||||
assert Actors.is_following(follower, followed)
|
||||
end
|
||||
|
||||
test "it works for incoming accepts which were pre-accepted" do
|
||||
follower = insert(:actor)
|
||||
followed = insert(:actor, manually_approves_followers: true)
|
||||
|
||||
refute Actors.is_following(follower, followed)
|
||||
|
||||
{:ok, follow_activity, _} = ActivityPub.follow(follower, followed)
|
||||
assert Actors.is_following(follower, followed)
|
||||
|
||||
follow_object_id = follow_activity.data["id"]
|
||||
|
||||
assert %Follower{} = Actors.get_follower_by_url(follow_object_id)
|
||||
|
||||
accept_data =
|
||||
File.read!("test/fixtures/mastodon-accept-activity.json")
|
||||
|> Jason.decode!()
|
||||
|> Map.put("actor", followed.url)
|
||||
|
||||
object =
|
||||
accept_data["object"]
|
||||
|> Map.put("actor", follower.url)
|
||||
|> Map.put("id", follow_object_id)
|
||||
|
||||
accept_data = Map.put(accept_data, "object", object)
|
||||
|
||||
@@ -40,7 +126,7 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier.FollowTest do
|
||||
|
||||
test "it works for incoming accepts which are referenced by IRI only" do
|
||||
follower = insert(:actor)
|
||||
followed = insert(:actor)
|
||||
followed = insert(:actor, manually_approves_followers: true)
|
||||
|
||||
{:ok, follow_activity, _} = ActivityPub.follow(follower, followed)
|
||||
|
||||
|
||||
@@ -556,57 +556,6 @@ defmodule Mobilizon.Federation.ActivityPub.TransmogrifierTest do
|
||||
end
|
||||
end
|
||||
|
||||
describe "handle incoming follow requests" do
|
||||
test "it works for incoming follow requests" do
|
||||
use_cassette "activity_pub/mastodon_follow_activity" do
|
||||
actor = insert(:actor)
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/mastodon-follow-activity.json")
|
||||
|> Jason.decode!()
|
||||
|> Map.put("object", actor.url)
|
||||
|
||||
{:ok, %Activity{data: data, local: false}, _} = Transmogrifier.handle_incoming(data)
|
||||
|
||||
assert data["actor"] == "https://social.tcit.fr/users/tcit"
|
||||
assert data["type"] == "Follow"
|
||||
assert data["id"] == "https://social.tcit.fr/users/tcit#follows/2"
|
||||
|
||||
actor = Actors.get_actor_with_preload(actor.id)
|
||||
assert Actors.is_following(Actors.get_actor_by_url!(data["actor"], true), actor)
|
||||
end
|
||||
end
|
||||
|
||||
test "it rejects activities without a valid ID" do
|
||||
actor = insert(:actor)
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/mastodon-follow-activity.json")
|
||||
|> Jason.decode!()
|
||||
|> Map.put("object", actor.url)
|
||||
|> Map.put("id", "")
|
||||
|
||||
:error = Transmogrifier.handle_incoming(data)
|
||||
end
|
||||
|
||||
# test "it works for incoming follow requests from hubzilla" do
|
||||
# user = insert(:user)
|
||||
|
||||
# data =
|
||||
# File.read!("test/fixtures/hubzilla-follow-activity.json")
|
||||
# |> Jason.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.is_following(User.get_by_ap_id(data["actor"]), user)
|
||||
# end
|
||||
end
|
||||
|
||||
# test "it works for incoming likes" do
|
||||
# %Comment{url: url} = insert(:comment)
|
||||
|
||||
|
||||
275
test/graphql/resolvers/follower_test.exs
Normal file
275
test/graphql/resolvers/follower_test.exs
Normal file
@@ -0,0 +1,275 @@
|
||||
defmodule Mobilizon.Web.Resolvers.FollowerTest do
|
||||
use Mobilizon.Web.ConnCase
|
||||
use Oban.Testing, repo: Mobilizon.Storage.Repo
|
||||
|
||||
alias Mobilizon.Actors
|
||||
alias Mobilizon.Actors.{Actor, Follower}
|
||||
|
||||
import Mobilizon.Factory
|
||||
|
||||
alias Mobilizon.GraphQL.AbsintheHelpers
|
||||
|
||||
setup %{conn: conn} do
|
||||
user = insert(:user)
|
||||
actor = insert(:actor, user: user)
|
||||
group = insert(:group)
|
||||
insert(:member, parent: group, actor: actor, role: :moderator)
|
||||
follower = insert(:follower, target_actor: group)
|
||||
|
||||
{:ok, conn: conn, actor: actor, user: user, group: group, follower: follower}
|
||||
end
|
||||
|
||||
@group_followers_query """
|
||||
query(
|
||||
$name: String!
|
||||
$followersPage: Int
|
||||
$followersLimit: Int
|
||||
$approved: Boolean
|
||||
) {
|
||||
group(preferredUsername: $name) {
|
||||
id
|
||||
preferredUsername
|
||||
name
|
||||
domain
|
||||
followers(
|
||||
page: $followersPage
|
||||
limit: $followersLimit
|
||||
approved: $approved
|
||||
) {
|
||||
total
|
||||
elements {
|
||||
id
|
||||
actor {
|
||||
id
|
||||
preferredUsername
|
||||
name
|
||||
domain
|
||||
avatar {
|
||||
id
|
||||
url
|
||||
}
|
||||
}
|
||||
approved
|
||||
insertedAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
describe "list group followers find_followers_for_group/3" do
|
||||
test "without being logged-in", %{
|
||||
conn: conn,
|
||||
group: %Actor{preferred_username: preferred_username}
|
||||
} do
|
||||
res =
|
||||
conn
|
||||
|> AbsintheHelpers.graphql_query(
|
||||
query: @group_followers_query,
|
||||
variables: %{name: preferred_username}
|
||||
)
|
||||
|
||||
assert hd(res["errors"])["message"] == "unauthenticated"
|
||||
end
|
||||
|
||||
test "without being a member", %{
|
||||
conn: conn,
|
||||
group: %Actor{preferred_username: preferred_username}
|
||||
} do
|
||||
user = insert(:user)
|
||||
insert(:actor, user: user)
|
||||
|
||||
res =
|
||||
conn
|
||||
|> auth_conn(user)
|
||||
|> AbsintheHelpers.graphql_query(
|
||||
query: @group_followers_query,
|
||||
variables: %{name: preferred_username}
|
||||
)
|
||||
|
||||
assert hd(res["errors"])["message"] == "unauthorized"
|
||||
end
|
||||
|
||||
test "without being a moderator", %{
|
||||
conn: conn,
|
||||
group: %Actor{preferred_username: preferred_username} = group
|
||||
} do
|
||||
user = insert(:user)
|
||||
actor = insert(:actor, user: user)
|
||||
insert(:member, parent: group, actor: actor, role: :member)
|
||||
|
||||
res =
|
||||
conn
|
||||
|> auth_conn(user)
|
||||
|> AbsintheHelpers.graphql_query(
|
||||
query: @group_followers_query,
|
||||
variables: %{name: preferred_username}
|
||||
)
|
||||
|
||||
assert hd(res["errors"])["message"] == "unauthorized"
|
||||
end
|
||||
|
||||
test "while being a moderator", %{
|
||||
conn: conn,
|
||||
user: user,
|
||||
group: %Actor{preferred_username: preferred_username, id: group_id} = group,
|
||||
follower: %Follower{id: follower_id}
|
||||
} do
|
||||
res =
|
||||
conn
|
||||
|> auth_conn(user)
|
||||
|> AbsintheHelpers.graphql_query(
|
||||
query: @group_followers_query,
|
||||
variables: %{name: preferred_username}
|
||||
)
|
||||
|
||||
assert res["errors"] == nil
|
||||
assert res["data"]["group"]["id"] == to_string(group_id)
|
||||
assert res["data"]["group"]["followers"]["total"] == 1
|
||||
assert hd(res["data"]["group"]["followers"]["elements"])["id"] == to_string(follower_id)
|
||||
|
||||
Process.sleep(1000)
|
||||
insert(:follower, target_actor: group)
|
||||
Process.sleep(1000)
|
||||
follower3 = insert(:follower, target_actor: group)
|
||||
|
||||
res =
|
||||
conn
|
||||
|> auth_conn(user)
|
||||
|> AbsintheHelpers.graphql_query(
|
||||
query: @group_followers_query,
|
||||
variables: %{
|
||||
name: preferred_username,
|
||||
followersLimit: 2,
|
||||
followersPage: 1
|
||||
}
|
||||
)
|
||||
|
||||
assert res["errors"] == nil
|
||||
assert res["data"]["group"]["id"] == to_string(group_id)
|
||||
assert res["data"]["group"]["followers"]["total"] == 3
|
||||
assert hd(res["data"]["group"]["followers"]["elements"])["id"] == to_string(follower3.id)
|
||||
|
||||
res =
|
||||
conn
|
||||
|> auth_conn(user)
|
||||
|> AbsintheHelpers.graphql_query(
|
||||
query: @group_followers_query,
|
||||
variables: %{
|
||||
name: preferred_username,
|
||||
followersLimit: 2,
|
||||
followersPage: 2
|
||||
}
|
||||
)
|
||||
|
||||
assert res["errors"] == nil
|
||||
assert res["data"]["group"]["id"] == to_string(group_id)
|
||||
assert res["data"]["group"]["followers"]["total"] == 3
|
||||
assert hd(res["data"]["group"]["followers"]["elements"])["id"] == to_string(follower_id)
|
||||
end
|
||||
end
|
||||
|
||||
@update_follower_mutation """
|
||||
mutation UpdateFollower($id: ID!, $approved: Boolean) {
|
||||
updateFollower(id: $id, approved: $approved) {
|
||||
id
|
||||
approved
|
||||
}
|
||||
}
|
||||
"""
|
||||
describe "update a follower update_follower/3" do
|
||||
test "without being logged-in", %{
|
||||
conn: conn,
|
||||
group: %Actor{} = group
|
||||
} do
|
||||
%Follower{id: follower_id} = insert(:follower, target_actor: group)
|
||||
|
||||
res =
|
||||
conn
|
||||
|> AbsintheHelpers.graphql_query(
|
||||
query: @update_follower_mutation,
|
||||
variables: %{id: follower_id, approved: true}
|
||||
)
|
||||
|
||||
assert hd(res["errors"])["message"] == "You need to be logged in"
|
||||
end
|
||||
|
||||
test "without being a member", %{
|
||||
conn: conn,
|
||||
group: %Actor{} = group
|
||||
} do
|
||||
user = insert(:user)
|
||||
insert(:actor, user: user)
|
||||
%Follower{id: follower_id} = insert(:follower, target_actor: group)
|
||||
|
||||
res =
|
||||
conn
|
||||
|> auth_conn(user)
|
||||
|> AbsintheHelpers.graphql_query(
|
||||
query: @update_follower_mutation,
|
||||
variables: %{id: follower_id, approved: true}
|
||||
)
|
||||
|
||||
assert hd(res["errors"])["message"] == "You don't have permission to do this"
|
||||
end
|
||||
|
||||
test "without being a moderator", %{
|
||||
conn: conn,
|
||||
group: %Actor{} = group
|
||||
} do
|
||||
user = insert(:user)
|
||||
actor = insert(:actor, user: user)
|
||||
insert(:member, parent: group, actor: actor, role: :member)
|
||||
%Follower{id: follower_id} = insert(:follower, target_actor: group)
|
||||
|
||||
res =
|
||||
conn
|
||||
|> auth_conn(user)
|
||||
|> AbsintheHelpers.graphql_query(
|
||||
query: @update_follower_mutation,
|
||||
variables: %{id: follower_id, approved: true}
|
||||
)
|
||||
|
||||
assert hd(res["errors"])["message"] == "You don't have permission to do this"
|
||||
end
|
||||
|
||||
test "while being a moderator", %{
|
||||
conn: conn,
|
||||
user: user,
|
||||
follower: %Follower{id: follower_id, approved: false}
|
||||
} do
|
||||
res =
|
||||
conn
|
||||
|> auth_conn(user)
|
||||
|> AbsintheHelpers.graphql_query(
|
||||
query: @update_follower_mutation,
|
||||
variables: %{id: follower_id, approved: true}
|
||||
)
|
||||
|
||||
assert res["errors"] == nil
|
||||
assert res["data"]["updateFollower"]["id"] == to_string(follower_id)
|
||||
|
||||
assert %Follower{approved: true} = Actors.get_follower(follower_id)
|
||||
end
|
||||
|
||||
test "reject deletes the follower", %{
|
||||
conn: conn,
|
||||
user: user,
|
||||
follower: %Follower{id: follower_id, approved: false}
|
||||
} do
|
||||
res =
|
||||
conn
|
||||
|> auth_conn(user)
|
||||
|> AbsintheHelpers.graphql_query(
|
||||
query: @update_follower_mutation,
|
||||
variables: %{id: follower_id, approved: false}
|
||||
)
|
||||
|
||||
assert res["errors"] == nil
|
||||
assert res["data"]["updateFollower"]["id"] == to_string(follower_id)
|
||||
|
||||
assert is_nil(Actors.get_follower(follower_id))
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -55,7 +55,8 @@ defmodule Mobilizon.Factory do
|
||||
shared_inbox_url: "#{Endpoint.url()}/inbox",
|
||||
last_refreshed_at: DateTime.utc_now(),
|
||||
user: build(:user),
|
||||
visibility: :public
|
||||
visibility: :public,
|
||||
manually_approves_followers: false
|
||||
}
|
||||
end
|
||||
|
||||
@@ -108,7 +109,8 @@ defmodule Mobilizon.Factory do
|
||||
target_actor: build(:actor),
|
||||
actor: build(:actor),
|
||||
id: uuid,
|
||||
url: "#{Endpoint.url()}/follows/#{uuid}"
|
||||
url: "#{Endpoint.url()}/follows/#{uuid}",
|
||||
approved: false
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user