Merge branch 'develop' of git.pleroma.social:pleroma/pleroma into develop
This commit is contained in:
		
						commit
						a5c9dd0a2a
					
				
					 14 changed files with 280 additions and 26 deletions
				
			
		| 
						 | 
				
			
			@ -144,6 +144,24 @@ def announce(
 | 
			
		|||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def unannounce(
 | 
			
		||||
        %User{} = actor,
 | 
			
		||||
        %Object{} = object,
 | 
			
		||||
        activity_id \\ nil,
 | 
			
		||||
        local \\ true
 | 
			
		||||
      ) do
 | 
			
		||||
    with %Activity{} = announce_activity <- get_existing_announce(actor.ap_id, object),
 | 
			
		||||
         unannounce_data <- make_unannounce_data(actor, announce_activity, activity_id),
 | 
			
		||||
         {:ok, unannounce_activity} <- insert(unannounce_data, local),
 | 
			
		||||
         :ok <- maybe_federate(unannounce_activity),
 | 
			
		||||
         {:ok, _activity} <- Repo.delete(announce_activity),
 | 
			
		||||
         {:ok, object} <- remove_announce_from_object(announce_activity, object) do
 | 
			
		||||
      {:ok, unannounce_activity, announce_activity, object}
 | 
			
		||||
    else
 | 
			
		||||
      _e -> {:ok, object}
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def follow(follower, followed, activity_id \\ nil, local \\ true) do
 | 
			
		||||
    with data <- make_follow_data(follower, followed, activity_id),
 | 
			
		||||
         {:ok, activity} <- insert(data, local),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -223,9 +223,27 @@ def handle_incoming(
 | 
			
		|||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_incoming(
 | 
			
		||||
        %{
 | 
			
		||||
          "type" => "Undo",
 | 
			
		||||
          "object" => %{"type" => "Announce", "object" => object_id},
 | 
			
		||||
          "actor" => actor,
 | 
			
		||||
          "id" => id
 | 
			
		||||
        } = data
 | 
			
		||||
      ) do
 | 
			
		||||
    with %User{} = actor <- User.get_or_fetch_by_ap_id(actor),
 | 
			
		||||
         {:ok, object} <-
 | 
			
		||||
           get_obj_helper(object_id) || ActivityPub.fetch_object_from_id(object_id),
 | 
			
		||||
         {:ok, activity, _, _} <- ActivityPub.unannounce(actor, object, id, false) do
 | 
			
		||||
      {:ok, activity}
 | 
			
		||||
    else
 | 
			
		||||
      e -> :error
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  # TODO
 | 
			
		||||
  # Accept
 | 
			
		||||
  # Undo
 | 
			
		||||
  # Undo for non-Announce
 | 
			
		||||
 | 
			
		||||
  def handle_incoming(_), do: :error
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -237,6 +237,28 @@ def fetch_latest_follow(%User{ap_id: follower_id}, %User{ap_id: followed_id}) do
 | 
			
		|||
 | 
			
		||||
  #### Announce-related helpers
 | 
			
		||||
 | 
			
		||||
  @doc """
 | 
			
		||||
  Retruns an existing announce activity if the notice has already been announced
 | 
			
		||||
  """
 | 
			
		||||
  def get_existing_announce(actor, %{data: %{"id" => id}}) do
 | 
			
		||||
    query =
 | 
			
		||||
      from(
 | 
			
		||||
        activity in Activity,
 | 
			
		||||
        where: fragment("(?)->>'actor' = ?", activity.data, ^actor),
 | 
			
		||||
        # this is to use the index
 | 
			
		||||
        where:
 | 
			
		||||
          fragment(
 | 
			
		||||
            "coalesce((?)->'object'->>'id', (?)->>'object') = ?",
 | 
			
		||||
            activity.data,
 | 
			
		||||
            activity.data,
 | 
			
		||||
            ^id
 | 
			
		||||
          ),
 | 
			
		||||
        where: fragment("(?)->>'type' = 'Announce'", activity.data)
 | 
			
		||||
      )
 | 
			
		||||
 | 
			
		||||
    Repo.one(query)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @doc """
 | 
			
		||||
  Make announce activity data for the given actor and object
 | 
			
		||||
  """
 | 
			
		||||
| 
						 | 
				
			
			@ -257,12 +279,38 @@ def make_announce_data(
 | 
			
		|||
    if activity_id, do: Map.put(data, "id", activity_id), else: data
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @doc """
 | 
			
		||||
  Make unannounce activity data for the given actor and object
 | 
			
		||||
  """
 | 
			
		||||
  def make_unannounce_data(
 | 
			
		||||
        %User{ap_id: ap_id} = user,
 | 
			
		||||
        %Activity{data: %{"context" => context}} = activity,
 | 
			
		||||
        activity_id
 | 
			
		||||
      ) do
 | 
			
		||||
    data = %{
 | 
			
		||||
      "type" => "Undo",
 | 
			
		||||
      "actor" => ap_id,
 | 
			
		||||
      "object" => activity.data,
 | 
			
		||||
      "to" => [user.follower_address, activity.data["actor"]],
 | 
			
		||||
      "cc" => ["https://www.w3.org/ns/activitystreams#Public"],
 | 
			
		||||
      "context" => context
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if activity_id, do: Map.put(data, "id", activity_id), else: data
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def add_announce_to_object(%Activity{data: %{"actor" => actor}}, object) do
 | 
			
		||||
    with announcements <- [actor | object.data["announcements"] || []] |> Enum.uniq() do
 | 
			
		||||
      update_element_in_object("announcement", announcements, object)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def remove_announce_from_object(%Activity{data: %{"actor" => actor}}, object) do
 | 
			
		||||
    with announcements <- (object.data["announcements"] || []) |> List.delete(actor) do
 | 
			
		||||
      update_element_in_object("announcement", announcements, object)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  #### Unfollow-related helpers
 | 
			
		||||
 | 
			
		||||
  def make_unfollow_data(follower, followed, follow_activity) do
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,6 +24,16 @@ def repeat(id_or_ap_id, user) do
 | 
			
		|||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def unrepeat(id_or_ap_id, user) do
 | 
			
		||||
    with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id),
 | 
			
		||||
         object <- Object.get_by_ap_id(activity.data["object"]["id"]) do
 | 
			
		||||
      ActivityPub.unannounce(user, object)
 | 
			
		||||
    else
 | 
			
		||||
      _ ->
 | 
			
		||||
        {:error, "Could not unrepeat"}
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def favorite(id_or_ap_id, user) do
 | 
			
		||||
    with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id),
 | 
			
		||||
         false <- activity.data["actor"] == user.ap_id,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -308,6 +308,13 @@ def reblog_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
 | 
			
		|||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def unreblog_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
 | 
			
		||||
    with {:ok, _, _, %{data: %{"id" => id}}} = CommonAPI.unrepeat(ap_id_or_id, user),
 | 
			
		||||
         %Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do
 | 
			
		||||
      render(conn, StatusView, "status.json", %{activity: activity, for: user, as: :activity})
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def fav_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
 | 
			
		||||
    with {:ok, _fav, %{data: %{"id" => id}}} = CommonAPI.favorite(ap_id_or_id, user),
 | 
			
		||||
         %Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -239,27 +239,35 @@ def to_simple_form(%{data: %{"type" => "Undo"}} = activity, user, with_author) d
 | 
			
		|||
    inserted_at = activity.data["published"]
 | 
			
		||||
 | 
			
		||||
    author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: []
 | 
			
		||||
    follow_activity = Activity.get_by_ap_id(activity.data["object"])
 | 
			
		||||
 | 
			
		||||
    follow_activity =
 | 
			
		||||
      if is_map(activity.data["object"]) do
 | 
			
		||||
        Activity.get_by_ap_id(activity.data["object"]["id"])
 | 
			
		||||
      else
 | 
			
		||||
        Activity.get_by_ap_id(activity.data["object"])
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
    mentions = (activity.recipients || []) |> get_mentions
 | 
			
		||||
 | 
			
		||||
    [
 | 
			
		||||
      {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/activity']},
 | 
			
		||||
      {:"activity:verb", ['http://activitystrea.ms/schema/1.0/unfollow']},
 | 
			
		||||
      {:id, h.(activity.data["id"])},
 | 
			
		||||
      {:title, ['#{user.nickname} stopped following #{follow_activity.data["object"]}']},
 | 
			
		||||
      {:content, [type: 'html'],
 | 
			
		||||
       ['#{user.nickname} stopped following #{follow_activity.data["object"]}']},
 | 
			
		||||
      {:published, h.(inserted_at)},
 | 
			
		||||
      {:updated, h.(updated_at)},
 | 
			
		||||
      {:"activity:object",
 | 
			
		||||
       [
 | 
			
		||||
         {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/person']},
 | 
			
		||||
         {:id, h.(follow_activity.data["object"])},
 | 
			
		||||
         {:uri, h.(follow_activity.data["object"])}
 | 
			
		||||
       ]},
 | 
			
		||||
      {:link, [rel: 'self', type: ['application/atom+xml'], href: h.(activity.data["id"])], []}
 | 
			
		||||
    ] ++ mentions ++ author
 | 
			
		||||
    if follow_activity do
 | 
			
		||||
      [
 | 
			
		||||
        {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/activity']},
 | 
			
		||||
        {:"activity:verb", ['http://activitystrea.ms/schema/1.0/unfollow']},
 | 
			
		||||
        {:id, h.(activity.data["id"])},
 | 
			
		||||
        {:title, ['#{user.nickname} stopped following #{follow_activity.data["object"]}']},
 | 
			
		||||
        {:content, [type: 'html'],
 | 
			
		||||
         ['#{user.nickname} stopped following #{follow_activity.data["object"]}']},
 | 
			
		||||
        {:published, h.(inserted_at)},
 | 
			
		||||
        {:updated, h.(updated_at)},
 | 
			
		||||
        {:"activity:object",
 | 
			
		||||
         [
 | 
			
		||||
           {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/person']},
 | 
			
		||||
           {:id, h.(follow_activity.data["object"])},
 | 
			
		||||
           {:uri, h.(follow_activity.data["object"])}
 | 
			
		||||
         ]},
 | 
			
		||||
        {:link, [rel: 'self', type: ['application/atom+xml'], href: h.(activity.data["id"])], []}
 | 
			
		||||
      ] ++ mentions ++ author
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def to_simple_form(%{data: %{"type" => "Delete"}} = activity, user, with_author) do
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -112,6 +112,7 @@ def user_fetcher(username) do
 | 
			
		|||
    delete("/statuses/:id", MastodonAPIController, :delete_status)
 | 
			
		||||
 | 
			
		||||
    post("/statuses/:id/reblog", MastodonAPIController, :reblog_status)
 | 
			
		||||
    post("/statuses/:id/unreblog", MastodonAPIController, :unreblog_status)
 | 
			
		||||
    post("/statuses/:id/favourite", MastodonAPIController, :fav_status)
 | 
			
		||||
    post("/statuses/:id/unfavourite", MastodonAPIController, :unfav_status)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -187,13 +187,14 @@ def publish(user, activity, poster \\ &@httpoison.post/4)
 | 
			
		|||
 | 
			
		||||
  def publish(%{info: %{"keys" => keys}} = user, %{data: %{"type" => type}} = activity, poster)
 | 
			
		||||
      when type in @supported_activities do
 | 
			
		||||
    feed =
 | 
			
		||||
      ActivityRepresenter.to_simple_form(activity, user, true)
 | 
			
		||||
      |> ActivityRepresenter.wrap_with_entry()
 | 
			
		||||
      |> :xmerl.export_simple(:xmerl_xml)
 | 
			
		||||
      |> to_string
 | 
			
		||||
    feed = ActivityRepresenter.to_simple_form(activity, user, true)
 | 
			
		||||
 | 
			
		||||
    if feed do
 | 
			
		||||
      feed =
 | 
			
		||||
        ActivityRepresenter.wrap_with_entry(feed)
 | 
			
		||||
        |> :xmerl.export_simple(:xmerl_xml)
 | 
			
		||||
        |> to_string
 | 
			
		||||
 | 
			
		||||
      {:ok, private, _} = keys_from_pem(keys)
 | 
			
		||||
      {:ok, feed} = encode(private, feed)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,6 +11,18 @@ def create_status(%User{} = user, %{"status" => _} = data) do
 | 
			
		|||
    CommonAPI.post(user, data)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def delete(%User{} = user, id) do
 | 
			
		||||
    # TwitterAPI does not have an "unretweet" endpoint; instead this is done
 | 
			
		||||
    # via the "destroy" endpoint.  Therefore, we need to handle
 | 
			
		||||
    # when the status to "delete" is actually an Announce (repeat) object.
 | 
			
		||||
    with %Activity{data: %{"type" => type}} <- Repo.get(Activity, id) do
 | 
			
		||||
      case type do
 | 
			
		||||
        "Announce" -> unrepeat(user, id)
 | 
			
		||||
        _ -> CommonAPI.delete(id, user)
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def follow(%User{} = follower, params) do
 | 
			
		||||
    with {:ok, %User{} = followed} <- get_user(params),
 | 
			
		||||
         {:ok, follower} <- User.follow(follower, followed),
 | 
			
		||||
| 
						 | 
				
			
			@ -63,6 +75,12 @@ def repeat(%User{} = user, ap_id_or_id) do
 | 
			
		|||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp unrepeat(%User{} = user, ap_id_or_id) do
 | 
			
		||||
    with {:ok, _unannounce, activity, _object} <- CommonAPI.unrepeat(ap_id_or_id, user) do
 | 
			
		||||
      {:ok, activity}
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def fav(%User{} = user, ap_id_or_id) do
 | 
			
		||||
    with {:ok, _announce, %{data: %{"id" => id}}} = CommonAPI.favorite(ap_id_or_id, user),
 | 
			
		||||
         %Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -157,8 +157,8 @@ def unblock(%{assigns: %{user: user}} = conn, params) do
 | 
			
		|||
  end
 | 
			
		||||
 | 
			
		||||
  def delete_post(%{assigns: %{user: user}} = conn, %{"id" => id}) do
 | 
			
		||||
    with {:ok, delete} <- CommonAPI.delete(id, user) do
 | 
			
		||||
      render(conn, ActivityView, "activity.json", %{activity: delete, for: user})
 | 
			
		||||
    with {:ok, activity} <- TwitterAPI.delete(user, id) do
 | 
			
		||||
      render(conn, ActivityView, "activity.json", %{activity: activity, for: user})
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										47
									
								
								test/fixtures/mastodon-undo-announce.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								test/fixtures/mastodon-undo-announce.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,47 @@
 | 
			
		|||
{
 | 
			
		||||
   "type": "Undo",
 | 
			
		||||
   "signature": {
 | 
			
		||||
     "type": "RsaSignature2017",
 | 
			
		||||
     "signatureValue": "VU9AmHf3Pus9cWtMG/TOdxr+MRQfPHdTVKBBgFJBXhAlMhxEtcbxsu7zmqBgfIz6u0HpTCi5jRXEMftc228OJf/aBUkr4hyWADgcdmhPQgpibouDLgQf9BmnrPqb2rMbzZyt49GJkQZma8taLh077TTq6OKcnsAAJ1evEKOcRYS4OxBSwh4nI726bOXzZWoNzpTcrnm+llcUEN980sDSAS0uyZdb8AxZdfdG6DJQX4AkUD5qTpfqP/vC1ISirrNphvVhlxjUV9Amr4SYTsLx80vdZe5NjeL5Ir4jTIIQLedpxaDu1M9Q+Jpc0fYByQ2hOwUq8JxEmvHvarKjrq0Oww==",
 | 
			
		||||
     "creator": "http://mastodon.example.org/users/admin#main-key",
 | 
			
		||||
     "created": "2018-05-11T16:23:45Z"
 | 
			
		||||
   },
 | 
			
		||||
   "object": {
 | 
			
		||||
     "type": "Announce",
 | 
			
		||||
     "to": [
 | 
			
		||||
       "http://www.w3.org/ns/activitystreams#Public"
 | 
			
		||||
     ],
 | 
			
		||||
     "published": "2018-05-11T16:23:37Z",
 | 
			
		||||
     "object": "http://mastodon.example.org/@admin/99541947525187367",
 | 
			
		||||
     "id": "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity",
 | 
			
		||||
     "cc": [
 | 
			
		||||
       "http://mastodon.example.org/users/admin",
 | 
			
		||||
       "http://mastodon.example.org/users/admin/followers"
 | 
			
		||||
     ],
 | 
			
		||||
     "atomUri": "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity",
 | 
			
		||||
     "actor": "http://mastodon.example.org/users/admin"
 | 
			
		||||
   },
 | 
			
		||||
   "id": "http://mastodon.example.org/users/admin#announces/100011594053806179/undo",
 | 
			
		||||
   "actor": "http://mastodon.example.org/users/admin",
 | 
			
		||||
   "@context": [
 | 
			
		||||
     "http://www.w3.org/ns/activitystreams",
 | 
			
		||||
     "http://w3id.org/security/v1",
 | 
			
		||||
     {
 | 
			
		||||
       "toot": "http://joinmastodon.org/ns#",
 | 
			
		||||
       "sensitive": "as:sensitive",
 | 
			
		||||
       "ostatus": "http://ostatus.org#",
 | 
			
		||||
       "movedTo": "as:movedTo",
 | 
			
		||||
       "manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
 | 
			
		||||
       "inReplyToAtomUri": "ostatus:inReplyToAtomUri",
 | 
			
		||||
       "focalPoint": {
 | 
			
		||||
         "@id": "toot:focalPoint",
 | 
			
		||||
         "@container": "@list"
 | 
			
		||||
       },
 | 
			
		||||
       "featured": "toot:featured",
 | 
			
		||||
       "conversation": "ostatus:conversation",
 | 
			
		||||
       "atomUri": "ostatus:atomUri",
 | 
			
		||||
       "Hashtag": "as:Hashtag",
 | 
			
		||||
       "Emoji": "toot:Emoji"
 | 
			
		||||
     }
 | 
			
		||||
   ]
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -302,6 +302,38 @@ test "adds an announce activity to the db" do
 | 
			
		|||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "unannouncing an object" do
 | 
			
		||||
    test "unannouncing a previously announced object" do
 | 
			
		||||
      note_activity = insert(:note_activity)
 | 
			
		||||
      object = Object.get_by_ap_id(note_activity.data["object"]["id"])
 | 
			
		||||
      user = insert(:user)
 | 
			
		||||
 | 
			
		||||
      # Unannouncing an object that is not announced does nothing
 | 
			
		||||
      # {:ok, object} = ActivityPub.unannounce(user, object)
 | 
			
		||||
      # assert object.data["announcement_count"] == 0
 | 
			
		||||
 | 
			
		||||
      {:ok, announce_activity, object} = ActivityPub.announce(user, object)
 | 
			
		||||
      assert object.data["announcement_count"] == 1
 | 
			
		||||
 | 
			
		||||
      {:ok, unannounce_activity, activity, object} = ActivityPub.unannounce(user, object)
 | 
			
		||||
      assert object.data["announcement_count"] == 0
 | 
			
		||||
 | 
			
		||||
      assert activity == announce_activity
 | 
			
		||||
 | 
			
		||||
      assert unannounce_activity.data["to"] == [
 | 
			
		||||
               User.ap_followers(user),
 | 
			
		||||
               announce_activity.data["actor"]
 | 
			
		||||
             ]
 | 
			
		||||
 | 
			
		||||
      assert unannounce_activity.data["type"] == "Undo"
 | 
			
		||||
      assert unannounce_activity.data["object"] == announce_activity.data
 | 
			
		||||
      assert unannounce_activity.data["actor"] == user.ap_id
 | 
			
		||||
      assert unannounce_activity.data["context"] == announce_activity.data["context"]
 | 
			
		||||
 | 
			
		||||
      assert Repo.get(Activity, announce_activity.id) == nil
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "uploading files" do
 | 
			
		||||
    test "copies the file to the configured folder" do
 | 
			
		||||
      file = %Plug.Upload{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -232,6 +232,34 @@ test "it works for incoming deletes" do
 | 
			
		|||
 | 
			
		||||
      refute Repo.get(Activity, activity.id)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "it works for incoming unannounces with an existing notice" do
 | 
			
		||||
      user = insert(:user)
 | 
			
		||||
      {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
 | 
			
		||||
 | 
			
		||||
      announce_data =
 | 
			
		||||
        File.read!("test/fixtures/mastodon-announce.json")
 | 
			
		||||
        |> Poison.decode!()
 | 
			
		||||
        |> Map.put("object", activity.data["object"]["id"])
 | 
			
		||||
 | 
			
		||||
      {:ok, %Activity{data: announce_data, local: false}} =
 | 
			
		||||
        Transmogrifier.handle_incoming(announce_data)
 | 
			
		||||
 | 
			
		||||
      data =
 | 
			
		||||
        File.read!("test/fixtures/mastodon-undo-announce.json")
 | 
			
		||||
        |> Poison.decode!()
 | 
			
		||||
        |> Map.put("object", announce_data)
 | 
			
		||||
        |> Map.put("actor", announce_data["actor"])
 | 
			
		||||
 | 
			
		||||
      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
 | 
			
		||||
 | 
			
		||||
      assert data["type"] == "Undo"
 | 
			
		||||
      assert data["object"]["type"] == "Announce"
 | 
			
		||||
      assert data["object"]["object"] == activity.data["object"]["id"]
 | 
			
		||||
 | 
			
		||||
      assert data["object"]["id"] ==
 | 
			
		||||
               "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "prepare outgoing" do
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -298,6 +298,24 @@ test "reblogs and returns the reblogged status", %{conn: conn} do
 | 
			
		|||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "unreblogging" do
 | 
			
		||||
    test "unreblogs and returns the unreblogged status", %{conn: conn} do
 | 
			
		||||
      activity = insert(:note_activity)
 | 
			
		||||
      user = insert(:user)
 | 
			
		||||
 | 
			
		||||
      {:ok, _, _} = CommonAPI.repeat(activity.id, user)
 | 
			
		||||
 | 
			
		||||
      conn =
 | 
			
		||||
        conn
 | 
			
		||||
        |> assign(:user, user)
 | 
			
		||||
        |> post("/api/v1/statuses/#{activity.id}/unreblog")
 | 
			
		||||
 | 
			
		||||
      assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200)
 | 
			
		||||
 | 
			
		||||
      assert to_string(activity.id) == id
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "favoriting" do
 | 
			
		||||
    test "favs a status and returns it", %{conn: conn} do
 | 
			
		||||
      activity = insert(:note_activity)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue