Merge branch 'remake-remodel-2' into 'develop'

Ingestion Pipeline Revamp

See merge request pleroma/pleroma!2315
This commit is contained in:
lain 2020-04-06 11:59:01 +00:00
commit 349b9d86dc
38 changed files with 915 additions and 88 deletions

View file

@ -1,4 +1,4 @@
Unless otherwise stated this repository is copyright © 2017-2019 Unless otherwise stated this repository is copyright © 2017-2020
Pleroma Authors <https://pleroma.social/>, and is distributed under Pleroma Authors <https://pleroma.social/>, and is distributed under
The GNU Affero General Public License Version 3, you should have received a The GNU Affero General Public License Version 3, you should have received a
copy of the license file as AGPL-3. copy of the license file as AGPL-3.
@ -23,7 +23,7 @@ priv/static/images/pleroma-fox-tan-shy.png
--- ---
The following files are copyright © 2017-2019 Pleroma Authors The following files are copyright © 2017-2020 Pleroma Authors
<https://pleroma.social/>, and are distributed under the Creative Commons <https://pleroma.social/>, and are distributed under the Creative Commons
Attribution-ShareAlike 4.0 International license, you should have received Attribution-ShareAlike 4.0 International license, you should have received
a copy of the license file as CC-BY-SA-4.0. a copy of the license file as CC-BY-SA-4.0.

View file

@ -32,6 +32,18 @@ def get_actor(%{"actor" => nil, "attributedTo" => actor}) when not is_nil(actor)
get_actor(%{"actor" => actor}) get_actor(%{"actor" => actor})
end end
def get_object(%{"object" => id}) when is_binary(id) do
id
end
def get_object(%{"object" => %{"id" => id}}) when is_binary(id) do
id
end
def get_object(_) do
nil
end
# TODO: We explicitly allow 'tag' URIs through, due to references to legacy OStatus # TODO: We explicitly allow 'tag' URIs through, due to references to legacy OStatus
# objects being present in the test suite environment. Once these objects are # objects being present in the test suite environment. Once these objects are
# removed, please also remove this. # removed, please also remove this.

View file

@ -125,6 +125,21 @@ def increase_poll_votes_if_vote(%{
def increase_poll_votes_if_vote(_create_data), do: :noop def increase_poll_votes_if_vote(_create_data), do: :noop
@spec persist(map(), keyword()) :: {:ok, Activity.t() | Object.t()}
def persist(object, meta) do
with local <- Keyword.fetch!(meta, :local),
{recipients, _, _} <- get_recipients(object),
{:ok, activity} <-
Repo.insert(%Activity{
data: object,
local: local,
recipients: recipients,
actor: object["actor"]
}) do
{:ok, activity, meta}
end
end
@spec insert(map(), boolean(), boolean(), boolean()) :: {:ok, Activity.t()} | {:error, any()} @spec insert(map(), boolean(), boolean(), boolean()) :: {:ok, Activity.t()} | {:error, any()}
def insert(map, local \\ true, fake \\ false, bypass_actor_check \\ false) when is_map(map) do def insert(map, local \\ true, fake \\ false, bypass_actor_check \\ false) when is_map(map) do
with nil <- Activity.normalize(map), with nil <- Activity.normalize(map),

View file

@ -0,0 +1,43 @@
defmodule Pleroma.Web.ActivityPub.Builder do
@moduledoc """
This module builds the objects. Meant to be used for creating local objects.
This module encodes our addressing policies and general shape of our objects.
"""
alias Pleroma.Object
alias Pleroma.User
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.ActivityPub.Visibility
@spec like(User.t(), Object.t()) :: {:ok, map(), keyword()}
def like(actor, object) do
object_actor = User.get_cached_by_ap_id(object.data["actor"])
# Address the actor of the object, and our actor's follower collection if the post is public.
to =
if Visibility.is_public?(object) do
[actor.follower_address, object.data["actor"]]
else
[object.data["actor"]]
end
# CC everyone who's been addressed in the object, except ourself and the object actor's
# follower collection
cc =
(object.data["to"] ++ (object.data["cc"] || []))
|> List.delete(actor.ap_id)
|> List.delete(object_actor.follower_address)
{:ok,
%{
"id" => Utils.generate_activity_id(),
"actor" => actor.ap_id,
"type" => "Like",
"object" => object.data["id"],
"to" => to,
"cc" => cc,
"context" => object.data["context"]
}, []}
end
end

View file

@ -0,0 +1,37 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 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
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
|> Map.new(fn {key, val} -> {to_string(key), val} end)
end
def fetch_actor_and_object(object) do
User.get_or_fetch_by_ap_id(object["actor"])
Object.normalize(object["object"])
:ok
end
end

View file

@ -0,0 +1,32 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations do
import Ecto.Changeset
alias Pleroma.Object
alias Pleroma.User
def validate_actor_presence(cng, field_name \\ :actor) do
cng
|> validate_change(field_name, fn field_name, actor ->
if User.get_cached_by_ap_id(actor) do
[]
else
[{field_name, "can't find user"}]
end
end)
end
def validate_object_presence(cng, field_name \\ :object) do
cng
|> validate_change(field_name, fn field_name, object ->
if Object.get_cached_by_ap_id(object) do
[]
else
[{field_name, "can't find object"}]
end
end)
end
end

View file

@ -0,0 +1,30 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateNoteValidator do
use Ecto.Schema
alias Pleroma.Web.ActivityPub.ObjectValidators.NoteValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.Types
import Ecto.Changeset
@primary_key false
embedded_schema do
field(:id, Types.ObjectID, primary_key: true)
field(:actor, Types.ObjectID)
field(:type, :string)
field(:to, {:array, :string})
field(:cc, {:array, :string})
field(:bto, {:array, :string}, default: [])
field(:bcc, {:array, :string}, default: [])
embeds_one(:object, NoteValidator)
end
def cast_data(data) do
cast(%__MODULE__{}, data, __schema__(:fields))
end
end

View file

@ -0,0 +1,57 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator do
use Ecto.Schema
alias Pleroma.Web.ActivityPub.ObjectValidators.Types
alias Pleroma.Web.ActivityPub.Utils
import Ecto.Changeset
import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
@primary_key false
embedded_schema do
field(:id, Types.ObjectID, primary_key: true)
field(:type, :string)
field(:object, Types.ObjectID)
field(:actor, Types.ObjectID)
field(:context, :string)
field(:to, {:array, :string})
field(:cc, {:array, :string})
end
def cast_and_validate(data) do
data
|> cast_data()
|> validate_data()
end
def cast_data(data) do
%__MODULE__{}
|> cast(data, [:id, :type, :object, :actor, :context, :to, :cc])
end
def validate_data(data_cng) do
data_cng
|> validate_inclusion(:type, ["Like"])
|> validate_required([:id, :type, :object, :actor, :context, :to, :cc])
|> validate_actor_presence()
|> validate_object_presence()
|> validate_existing_like()
end
def validate_existing_like(%{changes: %{actor: actor, object: object}} = cng) do
if Utils.get_existing_like(actor, %{data: %{"id" => object}}) do
cng
|> add_error(:actor, "already liked this object")
|> add_error(:object, "already liked by this actor")
else
cng
end
end
def validate_existing_like(cng), do: cng
end

View file

@ -0,0 +1,63 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.ObjectValidators.NoteValidator do
use Ecto.Schema
alias Pleroma.Web.ActivityPub.ObjectValidators.Types
import Ecto.Changeset
@primary_key false
embedded_schema do
field(:id, Types.ObjectID, primary_key: true)
field(:to, {:array, :string}, default: [])
field(:cc, {:array, :string}, default: [])
field(:bto, {:array, :string}, default: [])
field(:bcc, {:array, :string}, default: [])
# TODO: Write type
field(:tag, {:array, :map}, default: [])
field(:type, :string)
field(:content, :string)
field(:context, :string)
field(:actor, Types.ObjectID)
field(:attributedTo, Types.ObjectID)
field(:summary, :string)
field(:published, Types.DateTime)
# TODO: Write type
field(:emoji, :map, default: %{})
field(:sensitive, :boolean, default: false)
# TODO: Write type
field(:attachment, {:array, :map}, default: [])
field(:replies_count, :integer, default: 0)
field(:like_count, :integer, default: 0)
field(:announcement_count, :integer, default: 0)
field(:inRepyTo, :string)
field(:likes, {:array, :string}, default: [])
field(:announcements, {:array, :string}, default: [])
# see if needed
field(:conversation, :string)
field(:context_id, :string)
end
def cast_and_validate(data) do
data
|> cast_data()
|> validate_data()
end
def cast_data(data) do
%__MODULE__{}
|> cast(data, __schema__(:fields))
end
def validate_data(data_cng) do
data_cng
|> validate_inclusion(:type, ["Note"])
|> validate_required([:id, :actor, :to, :cc, :type, :content, :context])
end
end

View file

@ -0,0 +1,34 @@
defmodule Pleroma.Web.ActivityPub.ObjectValidators.Types.DateTime do
@moduledoc """
The AP standard defines the date fields in AP as xsd:DateTime. Elixir's
DateTime can't parse this, but it can parse the related iso8601. This
module punches the date until it looks like iso8601 and normalizes to
it.
DateTimes without a timezone offset are treated as UTC.
Reference: https://www.w3.org/TR/activitystreams-vocabulary/#dfn-published
"""
use Ecto.Type
def type, do: :string
def cast(datetime) when is_binary(datetime) do
with {:ok, datetime, _} <- DateTime.from_iso8601(datetime) do
{:ok, DateTime.to_iso8601(datetime)}
else
{:error, :missing_offset} -> cast("#{datetime}Z")
_e -> :error
end
end
def cast(_), do: :error
def dump(data) do
{:ok, data}
end
def load(data) do
{:ok, data}
end
end

View file

@ -0,0 +1,33 @@
defmodule Pleroma.Web.ActivityPub.ObjectValidators.Types.ObjectID do
use Ecto.Type
def type, do: :string
def cast(object) when is_binary(object) do
# Host has to be present and scheme has to be an http scheme (for now)
case URI.parse(object) do
%URI{host: nil} ->
:error
%URI{scheme: scheme} when scheme in ["https", "http"] ->
{:ok, object}
_ ->
:error
end
end
def cast(%{"id" => object}), do: cast(object)
def cast(_) do
:error
end
def dump(data) do
{:ok, data}
end
def load(data) do
{:ok, data}
end
end

View file

@ -0,0 +1,42 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.Pipeline do
alias Pleroma.Activity
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.MRF
alias Pleroma.Web.ActivityPub.ObjectValidator
alias Pleroma.Web.ActivityPub.SideEffects
alias Pleroma.Web.Federator
@spec common_pipeline(map(), keyword()) :: {:ok, Activity.t(), keyword()} | {:error, any()}
def common_pipeline(object, meta) do
with {_, {:ok, validated_object, meta}} <-
{:validate_object, ObjectValidator.validate(object, meta)},
{_, {:ok, mrfd_object}} <- {:mrf_object, MRF.filter(validated_object)},
{_, {:ok, %Activity{} = activity, meta}} <-
{:persist_object, ActivityPub.persist(mrfd_object, meta)},
{_, {:ok, %Activity{} = activity, meta}} <-
{:execute_side_effects, SideEffects.handle(activity, meta)},
{_, {:ok, _}} <- {:federation, maybe_federate(activity, meta)} do
{:ok, activity, meta}
else
{:mrf_object, {:reject, _}} -> {:ok, nil, meta}
e -> {:error, e}
end
end
defp maybe_federate(activity, meta) do
with {:ok, local} <- Keyword.fetch(meta, :local) do
if local do
Federator.publish(activity)
{:ok, :federated}
else
{:ok, :not_federated}
end
else
_e -> {:error, :badarg}
end
end
end

View file

@ -0,0 +1,28 @@
defmodule Pleroma.Web.ActivityPub.SideEffects do
@moduledoc """
This module looks at an inserted object and executes the side effects that it
implies. For example, a `Like` activity will increase the like count on the
liked object, a `Follow` activity will add the user to the follower
collection, and so on.
"""
alias Pleroma.Notification
alias Pleroma.Object
alias Pleroma.Web.ActivityPub.Utils
def handle(object, meta \\ [])
# Tasks this handles:
# - Add like to object
# - Set up notification
def handle(%{data: %{"type" => "Like"}} = object, meta) do
liked_object = Object.get_by_ap_id(object.data["object"])
Utils.add_like_to_object(object, liked_object)
Notification.create_notifications(object)
{:ok, object, meta}
end
# Nothing to do
def handle(object, meta) do
{:ok, object, meta}
end
end

View file

@ -13,6 +13,9 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
alias Pleroma.Repo alias Pleroma.Repo
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.ObjectValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator
alias Pleroma.Web.ActivityPub.Pipeline
alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.ActivityPub.Visibility alias Pleroma.Web.ActivityPub.Visibility
alias Pleroma.Web.Federator alias Pleroma.Web.Federator
@ -609,17 +612,20 @@ def handle_incoming(
|> handle_incoming(options) |> handle_incoming(options)
end end
def handle_incoming( def handle_incoming(%{"type" => "Like"} = data, _options) do
%{"type" => "Like", "object" => object_id, "actor" => _actor, "id" => id} = data, with {_, {:ok, cast_data_sym}} <-
_options {:casting_data,
) do data |> LikeValidator.cast_data() |> Ecto.Changeset.apply_action(:insert)},
with actor <- Containment.get_actor(data), cast_data = ObjectValidator.stringify_keys(Map.from_struct(cast_data_sym)),
{:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(actor), :ok <- ObjectValidator.fetch_actor_and_object(cast_data),
{:ok, object} <- get_obj_helper(object_id), {_, {:ok, cast_data}} <- {:ensure_context_presence, ensure_context_presence(cast_data)},
{:ok, activity, _object} <- ActivityPub.like(actor, object, id, false) do {_, {:ok, cast_data}} <-
{:ensure_recipients_presence, ensure_recipients_presence(cast_data)},
{_, {:ok, activity, _meta}} <-
{:common_pipeline, Pipeline.common_pipeline(cast_data, local: false)} do
{:ok, activity} {:ok, activity}
else else
_e -> :error e -> {:error, e}
end end
end end
@ -1243,4 +1249,45 @@ def maybe_fix_user_url(%{"url" => url} = data) when is_map(url) do
def maybe_fix_user_url(data), do: data def maybe_fix_user_url(data), do: data
def maybe_fix_user_object(data), do: maybe_fix_user_url(data) def maybe_fix_user_object(data), do: maybe_fix_user_url(data)
defp ensure_context_presence(%{"context" => context} = data) when is_binary(context),
do: {:ok, data}
defp ensure_context_presence(%{"object" => object} = data) when is_binary(object) do
with %{data: %{"context" => context}} when is_binary(context) <- Object.normalize(object) do
{:ok, Map.put(data, "context", context)}
else
_ ->
{:error, :no_context}
end
end
defp ensure_context_presence(_) do
{:error, :no_context}
end
defp ensure_recipients_presence(%{"to" => [_ | _], "cc" => [_ | _]} = data),
do: {:ok, data}
defp ensure_recipients_presence(%{"object" => object} = data) do
case Object.normalize(object) do
%{data: %{"actor" => actor}} ->
data =
data
|> Map.put("to", [actor])
|> Map.put("cc", data["cc"] || [])
{:ok, data}
nil ->
{:error, :no_object}
_ ->
{:error, :no_actor}
end
end
defp ensure_recipients_presence(_) do
{:error, :no_object}
end
end end

View file

@ -12,6 +12,8 @@ defmodule Pleroma.Web.CommonAPI do
alias Pleroma.User alias Pleroma.User
alias Pleroma.UserRelationship alias Pleroma.UserRelationship
alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Builder
alias Pleroma.Web.ActivityPub.Pipeline
alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.ActivityPub.Visibility alias Pleroma.Web.ActivityPub.Visibility
@ -19,6 +21,7 @@ defmodule Pleroma.Web.CommonAPI do
import Pleroma.Web.CommonAPI.Utils import Pleroma.Web.CommonAPI.Utils
require Pleroma.Constants require Pleroma.Constants
require Logger
def follow(follower, followed) do def follow(follower, followed) do
timeout = Pleroma.Config.get([:activitypub, :follow_handshake_timeout]) timeout = Pleroma.Config.get([:activitypub, :follow_handshake_timeout])
@ -109,18 +112,51 @@ def unrepeat(id_or_ap_id, user) do
end end
end end
def favorite(id_or_ap_id, user) do @spec favorite(User.t(), binary()) :: {:ok, Activity.t() | :already_liked} | {:error, any()}
with {_, %Activity{} = activity} <- {:find_activity, get_by_id_or_ap_id(id_or_ap_id)}, def favorite(%User{} = user, id) do
object <- Object.normalize(activity), case favorite_helper(user, id) do
like_activity <- Utils.get_existing_like(user.ap_id, object) do {:ok, _} = res ->
if like_activity do res
{:ok, like_activity, object}
else {:error, :not_found} = res ->
ActivityPub.like(user, object) res
{:error, e} ->
Logger.error("Could not favorite #{id}. Error: #{inspect(e, pretty: true)}")
{:error, dgettext("errors", "Could not favorite")}
end end
end
def favorite_helper(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 else
{:find_activity, _} -> {:error, :not_found} {:find_object, _} ->
_ -> {:error, dgettext("errors", "Could not favorite")} {: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
{:error, e}
end
e ->
{:error, e}
end end
end end

View file

@ -207,9 +207,9 @@ def unreblog(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
end end
@doc "POST /api/v1/statuses/:id/favourite" @doc "POST /api/v1/statuses/:id/favourite"
def favourite(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do def favourite(%{assigns: %{user: user}} = conn, %{"id" => activity_id}) do
with {:ok, _fav, %{data: %{"id" => id}}} <- CommonAPI.favorite(ap_id_or_id, user), with {:ok, _fav} <- CommonAPI.favorite(user, activity_id),
%Activity{} = activity <- Activity.get_create_by_object_ap_id(id) do %Activity{} = activity <- Activity.get_by_id(activity_id) do
try_render(conn, "show.json", activity: activity, for: user, as: :activity) try_render(conn, "show.json", activity: activity, for: user, as: :activity)
end end
end end

View file

@ -537,7 +537,7 @@ test "it does not send notification to mentioned users in likes" do
"status" => "hey @#{other_user.nickname}!" "status" => "hey @#{other_user.nickname}!"
}) })
{:ok, activity_two, _} = CommonAPI.favorite(activity_one.id, third_user) {:ok, activity_two} = CommonAPI.favorite(third_user, activity_one.id)
{enabled_receivers, _disabled_receivers} = {enabled_receivers, _disabled_receivers} =
Notification.get_notified_from_activity(activity_two) Notification.get_notified_from_activity(activity_two)
@ -620,7 +620,7 @@ test "liking an activity results in 1 notification, then 0 if the activity is de
assert Enum.empty?(Notification.for_user(user)) assert Enum.empty?(Notification.for_user(user))
{:ok, _, _} = CommonAPI.favorite(activity.id, other_user) {:ok, _} = CommonAPI.favorite(other_user, activity.id)
assert length(Notification.for_user(user)) == 1 assert length(Notification.for_user(user)) == 1
@ -637,7 +637,7 @@ test "liking an activity results in 1 notification, then 0 if the activity is un
assert Enum.empty?(Notification.for_user(user)) assert Enum.empty?(Notification.for_user(user))
{:ok, _, _} = CommonAPI.favorite(activity.id, other_user) {:ok, _} = CommonAPI.favorite(other_user, activity.id)
assert length(Notification.for_user(user)) == 1 assert length(Notification.for_user(user)) == 1
@ -692,7 +692,7 @@ test "liking an activity which is already deleted does not generate a notificati
assert Enum.empty?(Notification.for_user(user)) assert Enum.empty?(Notification.for_user(user))
{:error, _} = CommonAPI.favorite(activity.id, other_user) {:error, :not_found} = CommonAPI.favorite(other_user, activity.id)
assert Enum.empty?(Notification.for_user(user)) assert Enum.empty?(Notification.for_user(user))
end end

View file

@ -380,7 +380,8 @@ test "preserves internal fields on refetch", %{mock_modified: mock_modified} do
user = insert(:user) user = insert(:user)
activity = Activity.get_create_by_object_ap_id(object.data["id"]) activity = Activity.get_create_by_object_ap_id(object.data["id"])
{:ok, _activity, object} = CommonAPI.favorite(activity.id, user) {:ok, activity} = CommonAPI.favorite(user, activity.id)
object = Object.get_by_ap_id(activity.data["object"])
assert object.data["like_count"] == 1 assert object.data["like_count"] == 1

View file

@ -60,7 +60,7 @@ test "doesn't count unrelated activities" do
other_user = insert(:user) other_user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{"visibility" => "public", "status" => "hey"}) {:ok, activity} = CommonAPI.post(user, %{"visibility" => "public", "status" => "hey"})
_ = CommonAPI.follow(user, other_user) _ = CommonAPI.follow(user, other_user)
CommonAPI.favorite(activity.id, other_user) CommonAPI.favorite(other_user, activity.id)
CommonAPI.repeat(activity.id, other_user) CommonAPI.repeat(activity.id, other_user)
assert %{direct: 0, private: 0, public: 1, unlisted: 0} = assert %{direct: 0, private: 0, public: 1, unlisted: 0} =

View file

@ -102,7 +102,7 @@ test "it turns OrderedCollection likes into empty arrays" do
{:ok, %{id: id, object: object}} = CommonAPI.post(user, %{"status" => "test"}) {:ok, %{id: id, object: object}} = CommonAPI.post(user, %{"status" => "test"})
{:ok, %{object: object2}} = CommonAPI.post(user, %{"status" => "test test"}) {:ok, %{object: object2}} = CommonAPI.post(user, %{"status" => "test test"})
CommonAPI.favorite(id, user2) CommonAPI.favorite(user2, id)
likes = %{ likes = %{
"first" => "first" =>

View file

@ -1141,8 +1141,8 @@ test "it deletes a user, all follow relationships and all activities", %{user: u
object_two = insert(:note, user: follower) object_two = insert(:note, user: follower)
activity_two = insert(:note_activity, user: follower, note: object_two) activity_two = insert(:note_activity, user: follower, note: object_two)
{:ok, like, _} = CommonAPI.favorite(activity_two.id, user) {:ok, like} = CommonAPI.favorite(user, activity_two.id)
{:ok, like_two, _} = CommonAPI.favorite(activity.id, follower) {:ok, like_two} = CommonAPI.favorite(follower, activity.id)
{:ok, repeat, _} = CommonAPI.repeat(activity_two.id, user) {:ok, repeat, _} = CommonAPI.repeat(activity_two.id, user)
{:ok, job} = User.delete(user) {:ok, job} = User.delete(user)

View file

@ -1900,14 +1900,14 @@ test "returns a favourite activities sorted by adds to favorite" do
{:ok, a4} = CommonAPI.post(user2, %{"status" => "Agent Smith "}) {:ok, a4} = CommonAPI.post(user2, %{"status" => "Agent Smith "})
{:ok, a5} = CommonAPI.post(user1, %{"status" => "Red or Blue "}) {:ok, a5} = CommonAPI.post(user1, %{"status" => "Red or Blue "})
{:ok, _, _} = CommonAPI.favorite(a4.id, user) {:ok, _} = CommonAPI.favorite(user, a4.id)
{:ok, _, _} = CommonAPI.favorite(a3.id, other_user) {:ok, _} = CommonAPI.favorite(other_user, a3.id)
{:ok, _, _} = CommonAPI.favorite(a3.id, user) {:ok, _} = CommonAPI.favorite(user, a3.id)
{:ok, _, _} = CommonAPI.favorite(a5.id, other_user) {:ok, _} = CommonAPI.favorite(other_user, a5.id)
{:ok, _, _} = CommonAPI.favorite(a5.id, user) {:ok, _} = CommonAPI.favorite(user, a5.id)
{:ok, _, _} = CommonAPI.favorite(a4.id, other_user) {:ok, _} = CommonAPI.favorite(other_user, a4.id)
{:ok, _, _} = CommonAPI.favorite(a1.id, user) {:ok, _} = CommonAPI.favorite(user, a1.id)
{:ok, _, _} = CommonAPI.favorite(a1.id, other_user) {:ok, _} = CommonAPI.favorite(other_user, a1.id)
result = ActivityPub.fetch_favourites(user) result = ActivityPub.fetch_favourites(user)
assert Enum.map(result, & &1.id) == [a1.id, a5.id, a3.id, a4.id] assert Enum.map(result, & &1.id) == [a1.id, a5.id, a3.id, a4.id]

View file

@ -0,0 +1,83 @@
defmodule Pleroma.Web.ActivityPub.ObjectValidatorTest do
use Pleroma.DataCase
alias Pleroma.Web.ActivityPub.ObjectValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.CommonAPI
import Pleroma.Factory
describe "likes" do
setup do
user = insert(:user)
{:ok, post_activity} = CommonAPI.post(user, %{"status" => "uguu"})
valid_like = %{
"to" => [user.ap_id],
"cc" => [],
"type" => "Like",
"id" => Utils.generate_activity_id(),
"object" => post_activity.data["object"],
"actor" => user.ap_id,
"context" => "a context"
}
%{valid_like: valid_like, user: user, post_activity: post_activity}
end
test "returns ok when called in the ObjectValidator", %{valid_like: valid_like} do
{:ok, object, _meta} = ObjectValidator.validate(valid_like, [])
assert "id" in Map.keys(object)
end
test "is valid for a valid object", %{valid_like: valid_like} do
assert LikeValidator.cast_and_validate(valid_like).valid?
end
test "it errors when the actor is missing or not known", %{valid_like: valid_like} do
without_actor = Map.delete(valid_like, "actor")
refute LikeValidator.cast_and_validate(without_actor).valid?
with_invalid_actor = Map.put(valid_like, "actor", "invalidactor")
refute LikeValidator.cast_and_validate(with_invalid_actor).valid?
end
test "it errors when the object is missing or not known", %{valid_like: valid_like} do
without_object = Map.delete(valid_like, "object")
refute LikeValidator.cast_and_validate(without_object).valid?
with_invalid_object = Map.put(valid_like, "object", "invalidobject")
refute LikeValidator.cast_and_validate(with_invalid_object).valid?
end
test "it errors when the actor has already like the object", %{
valid_like: valid_like,
user: user,
post_activity: post_activity
} do
_like = CommonAPI.favorite(user, post_activity.id)
refute LikeValidator.cast_and_validate(valid_like).valid?
end
test "it works when actor or object are wrapped in maps", %{valid_like: valid_like} do
wrapped_like =
valid_like
|> Map.put("actor", %{"id" => valid_like["actor"]})
|> Map.put("object", %{"id" => valid_like["object"]})
validated = LikeValidator.cast_and_validate(wrapped_like)
assert validated.valid?
assert {:actor, valid_like["actor"]} in validated.changes
assert {:object, valid_like["object"]} in validated.changes
end
end
end

View file

@ -0,0 +1,35 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.ObjectValidators.NoteValidatorTest do
use Pleroma.DataCase
alias Pleroma.Web.ActivityPub.ObjectValidators.NoteValidator
alias Pleroma.Web.ActivityPub.Utils
import Pleroma.Factory
describe "Notes" do
setup do
user = insert(:user)
note = %{
"id" => Utils.generate_activity_id(),
"type" => "Note",
"actor" => user.ap_id,
"to" => [user.follower_address],
"cc" => [],
"content" => "Hellow this is content.",
"context" => "xxx",
"summary" => "a post"
}
%{user: user, note: note}
end
test "a basic note validates", %{note: note} do
%{valid?: true} = NoteValidator.cast_and_validate(note)
end
end
end

View file

@ -0,0 +1,32 @@
defmodule Pleroma.Web.ActivityPub.ObjectValidators.Types.DateTimeTest do
alias Pleroma.Web.ActivityPub.ObjectValidators.Types.DateTime
use Pleroma.DataCase
test "it validates an xsd:Datetime" do
valid_strings = [
"2004-04-12T13:20:00",
"2004-04-12T13:20:15.5",
"2004-04-12T13:20:00-05:00",
"2004-04-12T13:20:00Z"
]
invalid_strings = [
"2004-04-12T13:00",
"2004-04-1213:20:00",
"99-04-12T13:00",
"2004-04-12"
]
assert {:ok, "2004-04-01T12:00:00Z"} == DateTime.cast("2004-04-01T12:00:00Z")
Enum.each(valid_strings, fn date_time ->
result = DateTime.cast(date_time)
assert {:ok, _} = result
end)
Enum.each(invalid_strings, fn date_time ->
result = DateTime.cast(date_time)
assert :error == result
end)
end
end

View file

@ -0,0 +1,37 @@
defmodule Pleroma.Web.ObjectValidators.Types.ObjectIDTest do
alias Pleroma.Web.ActivityPub.ObjectValidators.Types.ObjectID
use Pleroma.DataCase
@uris [
"http://lain.com/users/lain",
"http://lain.com",
"https://lain.com/object/1"
]
@non_uris [
"https://",
"rin",
1,
:x,
%{"1" => 2}
]
test "it accepts http uris" do
Enum.each(@uris, fn uri ->
assert {:ok, uri} == ObjectID.cast(uri)
end)
end
test "it accepts an object with a nested uri id" do
Enum.each(@uris, fn uri ->
assert {:ok, uri} == ObjectID.cast(%{"id" => uri})
end)
end
test "it rejects non-uri strings" do
Enum.each(@non_uris, fn non_uri ->
assert :error == ObjectID.cast(non_uri)
assert :error == ObjectID.cast(%{"id" => non_uri})
end)
end
end

View file

@ -0,0 +1,87 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.PipelineTest do
use Pleroma.DataCase
import Mock
import Pleroma.Factory
describe "common_pipeline/2" do
test "it goes through validation, filtering, persisting, side effects and federation for local activities" do
activity = insert(:note_activity)
meta = [local: true]
with_mocks([
{Pleroma.Web.ActivityPub.ObjectValidator, [], [validate: fn o, m -> {:ok, o, m} end]},
{
Pleroma.Web.ActivityPub.MRF,
[],
[filter: fn o -> {:ok, o} end]
},
{
Pleroma.Web.ActivityPub.ActivityPub,
[],
[persist: fn o, m -> {:ok, o, m} end]
},
{
Pleroma.Web.ActivityPub.SideEffects,
[],
[handle: fn o, m -> {:ok, o, m} end]
},
{
Pleroma.Web.Federator,
[],
[publish: fn _o -> :ok end]
}
]) do
assert {:ok, ^activity, ^meta} =
Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity, meta)
assert_called(Pleroma.Web.ActivityPub.ObjectValidator.validate(activity, meta))
assert_called(Pleroma.Web.ActivityPub.MRF.filter(activity))
assert_called(Pleroma.Web.ActivityPub.ActivityPub.persist(activity, meta))
assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta))
assert_called(Pleroma.Web.Federator.publish(activity))
end
end
test "it goes through validation, filtering, persisting, side effects without federation for remote activities" do
activity = insert(:note_activity)
meta = [local: false]
with_mocks([
{Pleroma.Web.ActivityPub.ObjectValidator, [], [validate: fn o, m -> {:ok, o, m} end]},
{
Pleroma.Web.ActivityPub.MRF,
[],
[filter: fn o -> {:ok, o} end]
},
{
Pleroma.Web.ActivityPub.ActivityPub,
[],
[persist: fn o, m -> {:ok, o, m} end]
},
{
Pleroma.Web.ActivityPub.SideEffects,
[],
[handle: fn o, m -> {:ok, o, m} end]
},
{
Pleroma.Web.Federator,
[],
[]
}
]) do
assert {:ok, ^activity, ^meta} =
Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity, meta)
assert_called(Pleroma.Web.ActivityPub.ObjectValidator.validate(activity, meta))
assert_called(Pleroma.Web.ActivityPub.MRF.filter(activity))
assert_called(Pleroma.Web.ActivityPub.ActivityPub.persist(activity, meta))
assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta))
end
end
end
end

View file

@ -0,0 +1,34 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.SideEffectsTest do
use Pleroma.DataCase
alias Pleroma.Object
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Builder
alias Pleroma.Web.ActivityPub.SideEffects
alias Pleroma.Web.CommonAPI
import Pleroma.Factory
describe "like objects" do
setup do
user = insert(:user)
{:ok, post} = CommonAPI.post(user, %{"status" => "hey"})
{:ok, like_data, _meta} = Builder.like(user, post.object)
{:ok, like, _meta} = ActivityPub.persist(like_data, local: true)
%{like: like, user: user}
end
test "add the like to the original object", %{like: like, user: user} do
{:ok, like, _} = SideEffects.handle(like)
object = Object.get_by_ap_id(like.data["object"])
assert object.data["like_count"] == 1
assert user.ap_id in object.data["likes"]
end
end
end

View file

@ -334,7 +334,9 @@ test "it works for incoming likes" do
|> Poison.decode!() |> Poison.decode!()
|> Map.put("object", activity.data["object"]) |> Map.put("object", activity.data["object"])
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) {:ok, %Activity{data: data, local: false} = activity} = Transmogrifier.handle_incoming(data)
refute Enum.empty?(activity.recipients)
assert data["actor"] == "http://mastodon.example.org/users/admin" assert data["actor"] == "http://mastodon.example.org/users/admin"
assert data["type"] == "Like" assert data["type"] == "Like"

View file

@ -59,7 +59,7 @@ test "renders a like activity" do
object = Object.normalize(note) object = Object.normalize(note)
user = insert(:user) user = insert(:user)
{:ok, like_activity, _} = CommonAPI.favorite(note.id, user) {:ok, like_activity} = CommonAPI.favorite(user, note.id)
result = ObjectView.render("object.json", %{object: like_activity}) result = ObjectView.render("object.json", %{object: like_activity})

View file

@ -284,9 +284,12 @@ test "favoriting a status" do
user = insert(:user) user = insert(:user)
other_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 end
test "retweeting a status twice returns the status" do test "retweeting a status twice returns the status" do
@ -298,13 +301,13 @@ test "retweeting a status twice returns the status" do
{:ok, ^activity, ^object} = CommonAPI.repeat(activity.id, user) {:ok, ^activity, ^object} = CommonAPI.repeat(activity.id, user)
end end
test "favoriting a status twice returns the status" do test "favoriting a status twice returns ok, but without the like activity" do
user = insert(:user) user = insert(:user)
other_user = insert(:user) other_user = insert(:user)
{:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"}) {:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"})
{:ok, %Activity{} = activity, object} = CommonAPI.favorite(activity.id, user) {:ok, %Activity{}} = CommonAPI.favorite(user, activity.id)
{:ok, ^activity, ^object} = CommonAPI.favorite(activity.id, user) assert {:ok, :already_liked} = CommonAPI.favorite(user, activity.id)
end end
end end

View file

@ -194,10 +194,10 @@ test "filters notifications for Like activities" do
{:ok, private_activity} = {:ok, private_activity} =
CommonAPI.post(other_user, %{"status" => ".", "visibility" => "private"}) CommonAPI.post(other_user, %{"status" => ".", "visibility" => "private"})
{:ok, _, _} = CommonAPI.favorite(public_activity.id, user) {:ok, _} = CommonAPI.favorite(user, public_activity.id)
{:ok, _, _} = CommonAPI.favorite(direct_activity.id, user) {:ok, _} = CommonAPI.favorite(user, direct_activity.id)
{:ok, _, _} = CommonAPI.favorite(unlisted_activity.id, user) {:ok, _} = CommonAPI.favorite(user, unlisted_activity.id)
{:ok, _, _} = CommonAPI.favorite(private_activity.id, user) {:ok, _} = CommonAPI.favorite(user, private_activity.id)
activity_ids = activity_ids =
conn conn
@ -274,7 +274,7 @@ test "filters notifications using exclude_types" do
{:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"}) {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
{:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"}) {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
{:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user) {:ok, favorite_activity} = CommonAPI.favorite(other_user, create_activity.id)
{:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user) {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
{:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user) {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
@ -310,7 +310,7 @@ test "filters notifications using include_types" do
{:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"}) {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
{:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"}) {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
{:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user) {:ok, favorite_activity} = CommonAPI.favorite(other_user, create_activity.id)
{:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user) {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
{:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user) {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)

View file

@ -775,7 +775,7 @@ test "reblogged status for another user" do
user1 = insert(:user) user1 = insert(:user)
user2 = insert(:user) user2 = insert(:user)
user3 = insert(:user) user3 = insert(:user)
CommonAPI.favorite(activity.id, user2) {:ok, _} = CommonAPI.favorite(user2, activity.id)
{:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id) {:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
{:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1) {:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
{:ok, _, _object} = CommonAPI.repeat(activity.id, user2) {:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
@ -850,11 +850,15 @@ test "favoriting twice will just return 200", %{conn: conn} do
activity = insert(:note_activity) activity = insert(:note_activity)
post(conn, "/api/v1/statuses/#{activity.id}/favourite") post(conn, "/api/v1/statuses/#{activity.id}/favourite")
assert post(conn, "/api/v1/statuses/#{activity.id}/favourite") |> json_response(200)
assert post(conn, "/api/v1/statuses/#{activity.id}/favourite")
|> json_response(200)
end end
test "returns 404 error for a wrong id", %{conn: conn} do 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"} assert json_response(conn, 404) == %{"error" => "Record not found"}
end end
@ -866,7 +870,7 @@ test "returns 404 error for a wrong id", %{conn: conn} do
test "unfavorites a status and returns it", %{user: user, conn: conn} do test "unfavorites a status and returns it", %{user: user, conn: conn} do
activity = insert(:note_activity) activity = insert(:note_activity)
{:ok, _, _} = CommonAPI.favorite(activity.id, user) {:ok, _} = CommonAPI.favorite(user, activity.id)
conn = post(conn, "/api/v1/statuses/#{activity.id}/unfavourite") conn = post(conn, "/api/v1/statuses/#{activity.id}/unfavourite")
@ -1176,7 +1180,7 @@ test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{c
test "returns users who have favorited the status", %{conn: conn, activity: activity} do test "returns users who have favorited the status", %{conn: conn, activity: activity} do
other_user = insert(:user) other_user = insert(:user)
{:ok, _, _} = CommonAPI.favorite(activity.id, other_user) {:ok, _} = CommonAPI.favorite(other_user, activity.id)
response = response =
conn conn
@ -1207,7 +1211,7 @@ test "does not return users who have favorited the status but are blocked", %{
other_user = insert(:user) other_user = insert(:user)
{:ok, _user_relationship} = 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 = response =
conn conn
@ -1219,7 +1223,7 @@ test "does not return users who have favorited the status but are blocked", %{
test "does not fail on an unauthenticated request", %{activity: activity} do test "does not fail on an unauthenticated request", %{activity: activity} do
other_user = insert(:user) other_user = insert(:user)
{:ok, _, _} = CommonAPI.favorite(activity.id, other_user) {:ok, _} = CommonAPI.favorite(other_user, activity.id)
response = response =
build_conn() build_conn()
@ -1239,7 +1243,7 @@ test "requires authentication for private posts", %{user: user} do
"visibility" => "direct" "visibility" => "direct"
}) })
{:ok, _, _} = CommonAPI.favorite(activity.id, other_user) {:ok, _} = CommonAPI.favorite(other_user, activity.id)
favourited_by_url = "/api/v1/statuses/#{activity.id}/favourited_by" favourited_by_url = "/api/v1/statuses/#{activity.id}/favourited_by"
@ -1399,7 +1403,7 @@ test "returns the favorites of a user" do
{:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"}) {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
{:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"}) {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
{:ok, _, _} = CommonAPI.favorite(activity.id, user) {:ok, _} = CommonAPI.favorite(user, activity.id)
first_conn = get(conn, "/api/v1/favourites") first_conn = get(conn, "/api/v1/favourites")
@ -1416,7 +1420,7 @@ test "returns the favorites of a user" do
"Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful." "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
}) })
{:ok, _, _} = CommonAPI.favorite(second_activity.id, user) {:ok, _} = CommonAPI.favorite(user, second_activity.id)
last_like = status["id"] last_like = status["id"]

View file

@ -54,7 +54,7 @@ test "Favourite notification" do
user = insert(:user) user = insert(:user)
another_user = insert(:user) another_user = insert(:user)
{:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"}) {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
{:ok, favorite_activity, _object} = CommonAPI.favorite(create_activity.id, another_user) {:ok, favorite_activity} = CommonAPI.favorite(another_user, create_activity.id)
{:ok, [notification]} = Notification.create_notifications(favorite_activity) {:ok, [notification]} = Notification.create_notifications(favorite_activity)
create_activity = Activity.get_by_id(create_activity.id) create_activity = Activity.get_by_id(create_activity.id)

View file

@ -136,7 +136,7 @@ test "render html for redirect for html format", %{conn: conn} do
user = insert(:user) user = insert(:user)
{:ok, like_activity, _} = CommonAPI.favorite(note_activity.id, user) {:ok, like_activity} = CommonAPI.favorite(user, note_activity.id)
assert like_activity.data["type"] == "Like" assert like_activity.data["type"] == "Like"

View file

@ -138,7 +138,7 @@ test "returns list of statuses favorited by specified user", %{
user: user user: user
} do } do
[activity | _] = insert_pair(:note_activity) [activity | _] = insert_pair(:note_activity)
CommonAPI.favorite(activity.id, user) CommonAPI.favorite(user, activity.id)
response = response =
conn conn
@ -155,7 +155,7 @@ test "does not return favorites for specified user_id when user is not logged in
user: user user: user
} do } do
activity = insert(:note_activity) activity = insert(:note_activity)
CommonAPI.favorite(activity.id, user) CommonAPI.favorite(user, activity.id)
build_conn() build_conn()
|> get("/api/v1/pleroma/accounts/#{user.id}/favourites") |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
@ -172,7 +172,7 @@ test "returns favorited DM only when user is logged in and he is one of recipien
"visibility" => "direct" "visibility" => "direct"
}) })
CommonAPI.favorite(direct.id, user) CommonAPI.favorite(user, direct.id)
for u <- [user, current_user] do for u <- [user, current_user] do
response = response =
@ -202,7 +202,7 @@ test "does not return others' favorited DM when user is not one of recipients",
"visibility" => "direct" "visibility" => "direct"
}) })
CommonAPI.favorite(direct.id, user) CommonAPI.favorite(user, direct.id)
response = response =
conn conn
@ -219,7 +219,7 @@ test "paginates favorites using since_id and max_id", %{
activities = insert_list(10, :note_activity) activities = insert_list(10, :note_activity)
Enum.each(activities, fn activity -> Enum.each(activities, fn activity ->
CommonAPI.favorite(activity.id, user) CommonAPI.favorite(user, activity.id)
end) end)
third_activity = Enum.at(activities, 2) third_activity = Enum.at(activities, 2)
@ -245,7 +245,7 @@ test "limits favorites using limit parameter", %{
7 7
|> insert_list(:note_activity) |> insert_list(:note_activity)
|> Enum.each(fn activity -> |> Enum.each(fn activity ->
CommonAPI.favorite(activity.id, user) CommonAPI.favorite(user, activity.id)
end) end)
response = response =
@ -277,7 +277,7 @@ test "returns 404 error when specified user is not exist", %{conn: conn} do
test "returns 403 error when user has hidden own favorites", %{conn: conn} do test "returns 403 error when user has hidden own favorites", %{conn: conn} do
user = insert(:user, hide_favorites: true) user = insert(:user, hide_favorites: true)
activity = insert(:note_activity) activity = insert(:note_activity)
CommonAPI.favorite(activity.id, user) CommonAPI.favorite(user, activity.id)
conn = get(conn, "/api/v1/pleroma/accounts/#{user.id}/favourites") conn = get(conn, "/api/v1/pleroma/accounts/#{user.id}/favourites")
@ -287,7 +287,7 @@ test "returns 403 error when user has hidden own favorites", %{conn: conn} do
test "hides favorites for new users by default", %{conn: conn} do test "hides favorites for new users by default", %{conn: conn} do
user = insert(:user) user = insert(:user)
activity = insert(:note_activity) activity = insert(:note_activity)
CommonAPI.favorite(activity.id, user) CommonAPI.favorite(user, activity.id)
assert user.hide_favorites assert user.hide_favorites
conn = get(conn, "/api/v1/pleroma/accounts/#{user.id}/favourites") conn = get(conn, "/api/v1/pleroma/accounts/#{user.id}/favourites")

View file

@ -170,7 +170,7 @@ test "renders title and body for like activity" do
"<span>Lorem ipsum dolor sit amet</span>, consectetur :firefox: adipiscing elit. Fusce sagittis finibus turpis." "<span>Lorem ipsum dolor sit amet</span>, consectetur :firefox: adipiscing elit. Fusce sagittis finibus turpis."
}) })
{:ok, activity, _} = CommonAPI.favorite(activity.id, user) {:ok, activity} = CommonAPI.favorite(user, activity.id)
object = Object.normalize(activity) object = Object.normalize(activity)
assert Impl.format_body(%{activity: activity}, user, object) == "@Bob has favorited your post" assert Impl.format_body(%{activity: activity}, user, object) == "@Bob has favorited your post"

View file

@ -64,9 +64,6 @@ test "it doesn't send notify to the 'user:notification' stream when a user is bl
blocked = insert(:user) blocked = insert(:user)
{:ok, _user_relationship} = 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, _}, @streamer_timeout end) task = Task.async(fn -> refute_receive {:text, _}, @streamer_timeout end)
Streamer.add_socket( Streamer.add_socket(
@ -74,6 +71,9 @@ test "it doesn't send notify to the 'user:notification' stream when a user is bl
%{transport_pid: task.pid, assigns: %{user: user}} %{transport_pid: task.pid, assigns: %{user: user}}
) )
{:ok, activity} = CommonAPI.post(user, %{"status" => ":("})
{:ok, notif} = CommonAPI.favorite(blocked, activity.id)
Streamer.stream("user:notification", notif) Streamer.stream("user:notification", notif)
Task.await(task) Task.await(task)
end end
@ -83,10 +83,6 @@ test "it doesn't send notify to the 'user:notification' stream when a thread is
} do } do
user2 = insert(:user) user2 = insert(:user)
{: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) task = Task.async(fn -> refute_receive {:text, _}, @streamer_timeout end)
Streamer.add_socket( Streamer.add_socket(
@ -94,6 +90,10 @@ test "it doesn't send notify to the 'user:notification' stream when a thread is
%{transport_pid: task.pid, assigns: %{user: user}} %{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) Streamer.stream("user:notification", notif)
Task.await(task) Task.await(task)
end end
@ -103,10 +103,6 @@ test "it doesn't send notify to the 'user:notification' stream' when a domain is
} do } do
user2 = insert(:user, %{ap_id: "https://hecking-lewd-place.com/user/meanie"}) user2 = insert(:user, %{ap_id: "https://hecking-lewd-place.com/user/meanie"})
{: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) task = Task.async(fn -> refute_receive {:text, _}, @streamer_timeout end)
Streamer.add_socket( Streamer.add_socket(
@ -114,6 +110,10 @@ test "it doesn't send notify to the 'user:notification' stream' when a domain is
%{transport_pid: task.pid, assigns: %{user: user}} %{transport_pid: task.pid, assigns: %{user: user}}
) )
{:ok, user} = User.block_domain(user, "hecking-lewd-place.com")
{:ok, activity} = CommonAPI.post(user, %{"status" => "super hot take"})
{:ok, notif} = CommonAPI.favorite(user2, activity.id)
Streamer.stream("user:notification", notif) Streamer.stream("user:notification", notif)
Task.await(task) Task.await(task)
end end
@ -476,7 +476,7 @@ test "it does send non-reblog notification for reblog-muted actors" do
CommonAPI.hide_reblogs(user1, user2) CommonAPI.hide_reblogs(user1, user2)
{:ok, create_activity} = CommonAPI.post(user3, %{"status" => "I'm kawen"}) {:ok, create_activity} = CommonAPI.post(user3, %{"status" => "I'm kawen"})
{:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, user2) {:ok, favorite_activity} = CommonAPI.favorite(user2, create_activity.id)
task = task =
Task.async(fn -> Task.async(fn ->