Refactor Mobilizon.Federation.ActivityPub.Audience and add tests

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel 2021-07-29 17:48:28 +02:00
parent 221dd0609e
commit 78dc7613bc
No known key found for this signature in database
GPG Key ID: A061B9DDE0CA0773
9 changed files with 444 additions and 136 deletions

View File

@ -426,7 +426,7 @@ defmodule Mobilizon.Federation.ActivityPub do
"id" => "#{Endpoint.url()}/leave/event/#{participant.id}"
},
audience <-
Audience.calculate_to_and_cc_from_mentions(participant),
Audience.get_audience(participant),
{:ok, activity} <- create_activity(Map.merge(leave_data, audience), local),
:ok <- maybe_federate(activity) do
{:ok, activity, participant}
@ -803,7 +803,7 @@ defmodule Mobilizon.Federation.ActivityPub do
Scheduler.trigger_notifications_for_participant(participant),
participant_as_data <- Convertible.model_to_as(participant),
audience <-
Audience.calculate_to_and_cc_from_mentions(participant),
Audience.get_audience(participant),
update_data <-
make_accept_join_data(
participant_as_data,
@ -837,7 +837,7 @@ defmodule Mobilizon.Federation.ActivityPub do
),
member_as_data <- Convertible.model_to_as(member),
audience <-
Audience.calculate_to_and_cc_from_mentions(member),
Audience.get_audience(member),
update_data <-
make_accept_join_data(
member_as_data,
@ -899,7 +899,7 @@ defmodule Mobilizon.Federation.ActivityPub do
participant_as_data <- Convertible.model_to_as(participant),
audience <-
participant
|> Audience.calculate_to_and_cc_from_mentions()
|> Audience.get_audience()
|> Map.merge(additional),
reject_data <- %{
"type" => "Reject",
@ -925,7 +925,7 @@ defmodule Mobilizon.Federation.ActivityPub do
with {:ok, %Follower{} = follower} <- Actors.delete_follower(follower),
follower_as_data <- Convertible.model_to_as(follower),
audience <-
follower.actor |> Audience.calculate_to_and_cc_from_mentions() |> Map.merge(additional),
follower.actor |> Audience.get_audience() |> Map.merge(additional),
reject_data <- %{
"to" => [follower.actor.url],
"type" => "Reject",

View File

@ -3,18 +3,95 @@ defmodule Mobilizon.Federation.ActivityPub.Audience do
Tools for calculating content audience
"""
alias Mobilizon.Actors
alias Mobilizon.{Actors, Events, Share}
alias Mobilizon.Actors.{Actor, Member}
alias Mobilizon.Discussions.{Comment, Discussion}
alias Mobilizon.Events.{Event, Participant}
alias Mobilizon.Posts.Post
alias Mobilizon.Share
alias Mobilizon.Storage.Repo
require Logger
@ap_public "https://www.w3.org/ns/activitystreams#Public"
@type audience :: %{required(String.t()) => list(String.t())}
@doc """
Get audience for an entity
"""
@spec get_audience(Entity.t()) :: audience()
def get_audience(%Event{} = event) do
extract_actors_from_event(event)
end
def get_audience(%Post{draft: true} = post) do
get_audience(%Post{post | visibility: :private, draft: false})
end
def get_audience(%Post{attributed_to: %Actor{} = group, visibility: visibility}) do
{to, cc} = get_to_and_cc(group, [], visibility)
%{"to" => to, "cc" => cc}
end
def get_audience(%Discussion{actor: actor}) do
%{"to" => maybe_add_group_members([], actor), "cc" => []}
end
def get_audience(%Comment{discussion: %Discussion{} = discussion}) do
get_audience(discussion)
end
def get_audience(%Comment{
mentions: mentions,
actor: %Actor{} = actor,
visibility: visibility,
in_reply_to_comment: in_reply_to_comment,
event: event,
origin_comment: origin_comment,
url: url
}) do
with {to, cc} <-
extract_actors_from_mentions(mentions, actor, visibility),
{to, cc} <- {Enum.uniq(to ++ add_in_reply_to(in_reply_to_comment)), cc},
{to, cc} <- {Enum.uniq(to ++ add_event_author(event)), cc},
{to, cc} <-
{to,
Enum.uniq(
cc ++
add_comments_authors([origin_comment]) ++
add_shares_actors_followers(url)
)} do
%{"to" => to, "cc" => cc}
end
end
def get_audience(%Participant{} = participant) do
event = Events.get_event_with_preload!(participant.event_id)
actor_participants_urls =
event.id
|> Mobilizon.Events.list_actors_participants_for_event()
|> Enum.map(& &1.url)
%{
"to" => [participant.actor.url, group_or_organizer_event(event).url],
"cc" => actor_participants_urls
}
end
def get_audience(%Member{} = member) do
%{"to" => [member.parent.url, member.parent.members_url], "cc" => []}
end
def get_audience(%Actor{} = actor) do
%{
"to" => [@ap_public],
"cc" =>
maybe_add_group_members([actor.followers_url], actor) ++
add_actors_that_had_our_content(actor.id)
}
end
@doc """
Determines the full audience based on mentions for an audience
@ -39,6 +116,8 @@ defmodule Mobilizon.Federation.ActivityPub.Audience do
to = [@ap_public | mentions]
cc = [actor.followers_url]
cc = maybe_add_group_members(cc, actor)
{to, cc}
end
@ -47,13 +126,18 @@ defmodule Mobilizon.Federation.ActivityPub.Audience do
to = [actor.followers_url | mentions]
cc = [@ap_public]
to = maybe_add_group_members(to, actor)
{to, cc}
end
@spec get_to_and_cc(Actor.t(), list(), String.t()) :: {list(), list()}
def get_to_and_cc(%Actor{} = actor, mentions, :private) do
{to, cc} = get_to_and_cc(actor, mentions, :direct)
{[actor.followers_url | to], cc}
to = maybe_add_group_members(to, actor)
{to, cc}
end
@spec get_to_and_cc(Actor.t(), list(), String.t()) :: {list(), list()}
@ -65,125 +149,24 @@ defmodule Mobilizon.Federation.ActivityPub.Audience do
{mentions, []}
end
@spec maybe_add_group_members(List.t(), Actor.t()) :: List.t()
defp maybe_add_group_members(collection, %Actor{type: :Group, members_url: members_url}) do
[members_url | collection]
end
defp maybe_add_group_members(collection, %Actor{type: _}), do: collection
def get_addressed_actors(mentioned_users, _), do: mentioned_users
def calculate_to_and_cc_from_mentions(
%Comment{discussion: %Discussion{actor_id: actor_id}} = _comment
) do
with %Actor{type: :Group, members_url: members_url} <- Actors.get_actor(actor_id) do
%{"to" => [members_url], "cc" => []}
end
end
def calculate_to_and_cc_from_mentions(%Comment{} = comment) do
with {to, cc} <-
extract_actors_from_mentions(comment.mentions, comment.actor, comment.visibility),
{to, cc} <- {Enum.uniq(to ++ add_in_reply_to(comment.in_reply_to_comment)), cc},
{to, cc} <- {Enum.uniq(to ++ add_event_author(comment.event)), cc},
{to, cc} <-
{to,
Enum.uniq(
cc ++
add_comments_authors([comment.origin_comment]) ++
add_shares_actors_followers(comment.url)
)} do
%{"to" => to, "cc" => cc}
end
end
def calculate_to_and_cc_from_mentions(%Discussion{actor_id: actor_id}) do
with %Actor{type: :Group, members_url: members_url} <- Actors.get_actor(actor_id) do
%{"to" => [members_url], "cc" => []}
end
end
def calculate_to_and_cc_from_mentions(
%Event{
attributed_to: %Actor{members_url: members_url},
visibility: visibility
} = event
) do
%{"to" => to, "cc" => cc} = extract_actors_from_event(event)
case visibility do
:public ->
%{"to" => [@ap_public, members_url] ++ to, "cc" => [] ++ cc}
:unlisted ->
%{"to" => [members_url] ++ to, "cc" => [@ap_public] ++ cc}
:private ->
# Private is restricted to only the members
%{"to" => [members_url], "cc" => []}
end
end
def calculate_to_and_cc_from_mentions(%Event{} = event) do
extract_actors_from_event(event)
end
def calculate_to_and_cc_from_mentions(%Post{
attributed_to: %Actor{members_url: members_url, followers_url: followers_url},
visibility: visibility,
draft: draft
}) do
cond do
# If the post is draft we send it only to members
draft == true ->
%{"to" => [members_url], "cc" => []}
# If public everyone
visibility == :public ->
%{"to" => [@ap_public, members_url], "cc" => [followers_url]}
# Otherwise just followers
visibility == :unlisted ->
%{"to" => [followers_url, members_url], "cc" => [@ap_public]}
visibility == :private ->
# Private is restricted to only the members
%{"to" => [members_url], "cc" => []}
true ->
%{"to" => [], "cc" => []}
end
end
def calculate_to_and_cc_from_mentions(%Participant{} = participant) do
participant = Repo.preload(participant, [:actor, :event])
actor_participants_urls =
participant.event.id
|> Mobilizon.Events.list_actors_participants_for_event()
|> Enum.map(& &1.url)
%{"to" => [participant.actor.url], "cc" => actor_participants_urls}
end
def calculate_to_and_cc_from_mentions(%Member{} = member) do
member = Repo.preload(member, [:parent])
%{"to" => [member.parent.members_url], "cc" => []}
end
def calculate_to_and_cc_from_mentions(%Actor{} = actor) do
%{
"to" => [@ap_public],
"cc" => [actor.followers_url] ++ add_actors_that_had_our_content(actor.id)
}
end
defp add_in_reply_to(%Comment{actor: %Actor{url: url}} = _comment), do: [url]
defp add_in_reply_to(%Event{organizer_actor: %Actor{url: url}} = _event), do: [url]
defp add_in_reply_to(_), do: []
defp add_event_author(nil), do: []
defp add_event_author(%Event{} = event) do
[Repo.preload(event, [:organizer_actor]).organizer_actor.url]
end
defp add_comment_author(nil), do: nil
defp add_event_author(_), do: []
defp add_comment_author(%Comment{} = comment) do
case Repo.preload(comment, [:actor]) do
@ -195,6 +178,8 @@ defmodule Mobilizon.Federation.ActivityPub.Audience do
end
end
defp add_comment_author(_), do: nil
defp add_comments_authors(comments) do
authors =
comments
@ -208,8 +193,6 @@ defmodule Mobilizon.Federation.ActivityPub.Audience do
defp add_shares_actors_followers(uri) do
uri
|> Share.get_actors_by_share_uri()
|> Enum.map(&Actors.list_followers_actors_for_actor/1)
|> List.flatten()
|> Enum.map(& &1.url)
|> Enum.uniq()
end
@ -217,8 +200,6 @@ defmodule Mobilizon.Federation.ActivityPub.Audience do
defp add_actors_that_had_our_content(actor_id) do
actor_id
|> Share.get_actors_by_owner_actor_id()
|> Enum.map(&Actors.list_followers_actors_for_actor/1)
|> List.flatten()
|> Enum.map(& &1.url)
|> Enum.uniq()
end
@ -241,7 +222,11 @@ defmodule Mobilizon.Federation.ActivityPub.Audience do
defp extract_actors_from_event(%Event{} = event) do
with {to, cc} <-
extract_actors_from_mentions(event.mentions, event.organizer_actor, event.visibility),
extract_actors_from_mentions(
event.mentions,
group_or_organizer_event(event),
event.visibility
),
{to, cc} <-
{to,
Enum.uniq(
@ -253,4 +238,8 @@ defmodule Mobilizon.Federation.ActivityPub.Audience do
%{"to" => [], "cc" => []}
end
end
@spec group_or_organizer_event(Event.t()) :: Actor.t()
defp group_or_organizer_event(%Event{attributed_to: %Actor{} = group}), do: group
defp group_or_organizer_event(%Event{organizer_actor: %Actor{} = actor}), do: actor
end

View File

@ -47,7 +47,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Actors do
actor_as_data <- Convertible.model_to_as(new_actor),
{:ok, true} <- Cachex.del(:activity_pub, "actor_#{new_actor.preferred_username}"),
audience <-
Audience.calculate_to_and_cc_from_mentions(new_actor),
Audience.get_audience(new_actor),
additional <- Map.merge(additional, %{"actor" => old_actor.url}),
update_data <- make_update_data(actor_as_data, Map.merge(audience, additional)) do
{:ok, new_actor, update_data}
@ -142,7 +142,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Actors do
"object" => group.url
},
audience <-
Audience.calculate_to_and_cc_from_mentions(member) do
Audience.get_audience(member) do
approve_if_default_role_is_member(
group,
actor,

View File

@ -32,7 +32,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Comments do
:ok <- maybe_publish_graphql_subscription(discussion_id),
comment_as_data <- Convertible.model_to_as(comment),
audience <-
Audience.calculate_to_and_cc_from_mentions(comment),
Audience.get_audience(comment),
create_data <-
make_create_data(comment_as_data, Map.merge(audience, additional)) do
{:ok, comment, create_data}
@ -47,7 +47,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Comments do
{:ok, true} <- Cachex.del(:activity_pub, "comment_#{new_comment.uuid}"),
comment_as_data <- Convertible.model_to_as(new_comment),
audience <-
Audience.calculate_to_and_cc_from_mentions(new_comment),
Audience.get_audience(new_comment),
update_data <- make_update_data(comment_as_data, Map.merge(audience, additional)) do
{:ok, new_comment, update_data}
else
@ -79,7 +79,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Comments do
force_deletion = Map.get(options, :force, false)
with audience <-
Audience.calculate_to_and_cc_from_mentions(comment),
Audience.get_audience(comment),
{:ok, %Comment{} = updated_comment} <-
Discussions.delete_comment(comment, force: force_deletion),
{:ok, true} <- Cachex.del(:activity_pub, "comment_#{comment.uuid}"),

View File

@ -31,7 +31,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Discussions do
:ok <- maybe_publish_graphql_subscription(discussion),
comment_as_data <- Convertible.model_to_as(last_comment),
audience <-
Audience.calculate_to_and_cc_from_mentions(discussion),
Audience.get_audience(discussion),
create_data <-
make_create_data(comment_as_data, Map.merge(audience, additional)) do
{:ok, discussion, create_data}
@ -48,7 +48,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Discussions do
DiscussionActivity.insert_activity(discussion, subject: "discussion_created"),
discussion_as_data <- Convertible.model_to_as(discussion),
audience <-
Audience.calculate_to_and_cc_from_mentions(discussion),
Audience.get_audience(discussion),
create_data <-
make_create_data(discussion_as_data, Map.merge(audience, additional)) do
{:ok, discussion, create_data}
@ -68,7 +68,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Discussions do
{:ok, true} <- Cachex.del(:activity_pub, "discussion_#{new_discussion.slug}"),
discussion_as_data <- Convertible.model_to_as(new_discussion),
audience <-
Audience.calculate_to_and_cc_from_mentions(new_discussion),
Audience.get_audience(new_discussion),
update_data <- make_update_data(discussion_as_data, Map.merge(audience, additional)) do
{:ok, new_discussion, update_data}
else

View File

@ -29,7 +29,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Events do
EventActivity.insert_activity(event, subject: "event_created"),
event_as_data <- Convertible.model_to_as(event),
audience <-
Audience.calculate_to_and_cc_from_mentions(event),
Audience.get_audience(event),
create_data <-
make_create_data(event_as_data, Map.merge(audience, additional)) do
{:ok, event, create_data}
@ -46,7 +46,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Events do
{:ok, true} <- Cachex.del(:activity_pub, "event_#{new_event.uuid}"),
event_as_data <- Convertible.model_to_as(new_event),
audience <-
Audience.calculate_to_and_cc_from_mentions(new_event),
Audience.get_audience(new_event),
update_data <- make_update_data(event_as_data, Map.merge(audience, additional)) do
{:ok, new_event, update_data}
else
@ -69,7 +69,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Events do
}
with audience <-
Audience.calculate_to_and_cc_from_mentions(event),
Audience.get_audience(event),
{:ok, %Event{} = event} <- EventsManager.delete_event(event),
{:ok, _} <-
EventActivity.insert_activity(event, subject: "event_deleted"),
@ -124,7 +124,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Events do
}),
join_data <- Convertible.model_to_as(participant),
audience <-
Audience.calculate_to_and_cc_from_mentions(participant) do
Audience.get_audience(participant) do
approve_if_default_role_is_participant(
event,
Map.merge(join_data, audience),

View File

@ -47,7 +47,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Posts do
post_as_data <-
Convertible.model_to_as(%{post | attributed_to: group, author: creator}),
audience <-
Audience.calculate_to_and_cc_from_mentions(post) do
Audience.get_audience(post) do
update_data = make_update_data(post_as_data, Map.merge(audience, additional))
{:ok, post, update_data}

View File

@ -41,7 +41,7 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Post do
}
} = post
) do
audience = Audience.calculate_to_and_cc_from_mentions(post)
audience = Audience.get_audience(post)
%{
"type" => "Article",

View File

@ -0,0 +1,319 @@
defmodule Mobilizon.Federation.ActivityPub.AudienceTest do
use Mobilizon.DataCase
import Mobilizon.Factory
alias Mobilizon.Actors.{Actor, Member}
alias Mobilizon.Mention
alias Mobilizon.Discussions.{Comment, Discussion}
alias Mobilizon.Events.{Event, Participant}
alias Mobilizon.Federation.ActivityPub.Audience
alias Mobilizon.Posts.Post
alias Mobilizon.Storage.Repo
@ap_public "https://www.w3.org/ns/activitystreams#Public"
describe "get audience for an event created from a profile" do
test "when the event is public" do
%Event{} = event = insert(:event)
event = Repo.preload(event, [:comments])
assert %{"cc" => [event.organizer_actor.followers_url], "to" => [@ap_public]} ==
Audience.get_audience(event)
end
test "when the event is unlisted" do
%Event{} = event = insert(:event, visibility: :unlisted)
event = Repo.preload(event, [:comments])
assert %{"cc" => [@ap_public], "to" => [event.organizer_actor.followers_url]} ==
Audience.get_audience(event)
end
test "when the event is unlisted and mentions some actors" do
%Actor{id: mentionned_actor_id, url: mentionned_actor_url} =
insert(:actor, domain: "somewhere.else", url: "https://somewhere.else/@someone")
%Event{} = event = insert(:event, visibility: :unlisted)
event = Repo.preload(event, [:comments])
mentions = [%Mention{actor_id: mentionned_actor_id}]
event = %Event{event | mentions: mentions}
assert %{
"cc" => [@ap_public],
"to" => [event.organizer_actor.followers_url, mentionned_actor_url]
} ==
Audience.get_audience(event)
end
test "with interactions" do
%Actor{} = interactor = insert(:actor)
%Event{} = event = insert(:event)
insert(:share, owner_actor: event.organizer_actor, actor: interactor, uri: event.url)
event = Repo.preload(event, [:comments])
assert %{
"cc" => [event.organizer_actor.followers_url, interactor.url],
"to" => [@ap_public]
} ==
Audience.get_audience(event)
end
end
describe "get audience for an event created from a group member" do
test "when the event is public" do
%Actor{} = actor = insert(:actor)
%Actor{
followers_url: followers_url,
members_url: members_url
} = group = insert(:group, domain: "somewhere.else", url: "https://somewhere.else/@someone")
%Event{} = event = insert(:event, attributed_to: group, organizer_actor: actor)
event = Repo.preload(event, [:comments])
assert %{
"cc" => [members_url, followers_url],
"to" => [@ap_public]
} ==
Audience.get_audience(event)
end
test "when the event is unlisted" do
%Actor{} = actor = insert(:actor)
%Actor{
followers_url: followers_url,
members_url: members_url
} = group = insert(:group, domain: "somewhere.else", url: "https://somewhere.else/@someone")
%Event{} =
event =
insert(:event, visibility: :unlisted, attributed_to: group, organizer_actor: actor)
event = Repo.preload(event, [:comments])
assert %{
"cc" => [@ap_public],
"to" => [members_url, followers_url]
} ==
Audience.get_audience(event)
end
test "when the event is unlisted and mentions some actors" do
%Actor{id: mentionned_actor_id, url: mentionned_actor_url} =
insert(:actor, domain: "somewhere.else", url: "https://somewhere.else/@someone")
%Actor{} = actor = insert(:actor)
%Actor{
followers_url: followers_url,
members_url: members_url
} = group = insert(:group, domain: "somewhere.else", url: "https://somewhere.else/@a_group")
%Event{} =
event =
insert(:event, visibility: :unlisted, attributed_to: group, organizer_actor: actor)
event = Repo.preload(event, [:comments])
mentions = [%Mention{actor_id: mentionned_actor_id}]
event = %Event{event | mentions: mentions}
assert %{
"cc" => [@ap_public],
"to" => [members_url, followers_url, mentionned_actor_url]
} ==
Audience.get_audience(event)
end
end
describe "get audience for a post" do
test "when it's public" do
%Actor{
followers_url: followers_url,
members_url: members_url
} = group = insert(:group, domain: "somewhere.else", url: "https://somewhere.else/@someone")
%Post{} = post = insert(:post, attributed_to: group)
assert %{"to" => [@ap_public], "cc" => [members_url, followers_url]} ==
Audience.get_audience(post)
end
test "when it's unlisted" do
%Actor{
followers_url: followers_url,
members_url: members_url
} = group = insert(:group, domain: "somewhere.else", url: "https://somewhere.else/@someone")
%Post{} = post = insert(:post, attributed_to: group, visibility: :unlisted)
assert %{"to" => [members_url, followers_url], "cc" => [@ap_public]} ==
Audience.get_audience(post)
end
test "when it's private" do
%Actor{
members_url: members_url
} = group = insert(:group, domain: "somewhere.else", url: "https://somewhere.else/@someone")
%Post{} = post = insert(:post, attributed_to: group, visibility: :private)
assert %{"to" => [members_url], "cc" => []} ==
Audience.get_audience(post)
end
test "when it's still a draft" do
%Actor{
members_url: members_url
} = group = insert(:group, domain: "somewhere.else", url: "https://somewhere.else/@someone")
%Post{} = post = insert(:post, attributed_to: group, draft: true)
assert %{"to" => [members_url], "cc" => []} ==
Audience.get_audience(post)
end
end
describe "get audience for a discussion" do
test "basic" do
%Actor{
members_url: members_url
} = group = insert(:group, domain: "somewhere.else", url: "https://somewhere.else/@someone")
%Discussion{} = discussion = insert(:discussion, actor: group)
assert %{"to" => [members_url], "cc" => []} ==
Audience.get_audience(discussion)
end
end
describe "get audience for a comment" do
test "basic" do
%Actor{id: mentionned_actor_id, url: mentionned_actor_url} =
insert(:actor, domain: "somewhere.else", url: "https://somewhere.else/@someone")
%Comment{} = comment = insert(:comment)
mentions = [%Mention{actor_id: mentionned_actor_id}]
comment = %Comment{comment | mentions: mentions}
assert %{
"cc" => [comment.actor.followers_url],
"to" => [@ap_public, mentionned_actor_url, comment.event.organizer_actor.url]
} ==
Audience.get_audience(comment)
end
test "in reply to other comments" do
%Actor{id: mentionned_actor_id, url: mentionned_actor_url} =
insert(:actor, domain: "somewhere.else", url: "https://somewhere.else/@someone")
%Comment{} = original_comment = insert(:comment)
%Comment{} =
reply_comment =
insert(:comment, in_reply_to_comment: original_comment, origin_comment: original_comment)
%Comment{} =
comment =
insert(:comment, in_reply_to_comment: reply_comment, origin_comment: original_comment)
mentions = [%Mention{actor_id: mentionned_actor_id}]
comment = %Comment{comment | mentions: mentions}
assert %{
"cc" => [comment.actor.followers_url, original_comment.actor.url],
"to" => [
@ap_public,
mentionned_actor_url,
reply_comment.actor.url,
comment.event.organizer_actor.url
]
} ==
Audience.get_audience(comment)
end
test "part of a discussion" do
%Actor{
members_url: members_url
} = group = insert(:group, domain: "somewhere.else", url: "https://somewhere.else/@someone")
%Discussion{} = discussion = insert(:discussion, actor: group)
%Comment{} = comment = insert(:comment, discussion: discussion)
assert %{"to" => [members_url], "cc" => []} ==
Audience.get_audience(comment)
end
end
describe "participant" do
test "basic" do
%Event{} = event = insert(:event)
%Participant{} = participant2 = insert(:participant, event: event)
%Participant{} = participant = insert(:participant, event: event)
assert %{
"to" => [participant.actor.url, participant.event.organizer_actor.url],
"cc" => [participant2.actor.url, participant.actor.url]
} == Audience.get_audience(participant)
end
test "to a group event" do
%Actor{} =
group = insert(:group, domain: "somewhere.else", url: "https://somewhere.else/@someone")
%Event{} = event = insert(:event, attributed_to: group)
%Participant{} = participant2 = insert(:participant, event: event)
%Participant{} = participant = insert(:participant, event: event)
assert %{
"to" => [participant.actor.url, participant.event.attributed_to.url],
"cc" => [participant2.actor.url, participant.actor.url]
} == Audience.get_audience(participant)
end
end
describe "member" do
test "basic" do
%Member{} = member = insert(:member)
assert %{"to" => [member.parent.url, member.parent.members_url], "cc" => []} ==
Audience.get_audience(member)
end
end
describe "actor" do
test "basic" do
%Actor{followers_url: followers_url} = actor = insert(:actor)
assert %{"to" => [@ap_public], "cc" => [followers_url]} ==
Audience.get_audience(actor)
end
test "group" do
%Actor{followers_url: followers_url, members_url: members_url, type: :Group} =
group = insert(:group)
assert %{"to" => [@ap_public], "cc" => [members_url, followers_url]} ==
Audience.get_audience(group)
end
test "with interactions" do
%Actor{followers_url: followers_url, members_url: members_url, type: :Group} =
group = insert(:group)
%Actor{} = interactor = insert(:actor)
insert(:share, owner_actor: group, actor: interactor)
assert %{
"to" => [@ap_public],
"cc" => [members_url, followers_url, interactor.url]
} ==
Audience.get_audience(group)
end
end
end