# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Web.CommonAPITest do
  use Pleroma.DataCase
  alias Pleroma.Activity
  alias Pleroma.Conversation.Participation
  alias Pleroma.Object
  alias Pleroma.User
  alias Pleroma.Web.ActivityPub.ActivityPub
  alias Pleroma.Web.ActivityPub.Transmogrifier
  alias Pleroma.Web.ActivityPub.Visibility
  alias Pleroma.Web.AdminAPI.AccountView
  alias Pleroma.Web.CommonAPI

  import Pleroma.Factory
  import Mock

  require Pleroma.Constants

  setup do: clear_config([:instance, :safe_dm_mentions])
  setup do: clear_config([:instance, :limit])
  setup do: clear_config([:instance, :max_pinned_statuses])

  describe "unblocking" do
    test "it works even without an existing block activity" do
      blocked = insert(:user)
      blocker = insert(:user)
      User.block(blocker, blocked)

      assert User.blocks?(blocker, blocked)
      assert {:ok, :no_activity} == CommonAPI.unblock(blocker, blocked)
      refute User.blocks?(blocker, blocked)
    end
  end

  describe "deletion" do
    test "it works with pruned objects" do
      user = insert(:user)

      {:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})

      Object.normalize(post, false)
      |> Object.prune()

      with_mock Pleroma.Web.Federator,
        publish: fn _ -> nil end do
        assert {:ok, delete} = CommonAPI.delete(post.id, user)
        assert delete.local
        assert called(Pleroma.Web.Federator.publish(delete))
      end

      refute Activity.get_by_id(post.id)
    end

    test "it allows users to delete their posts" do
      user = insert(:user)

      {:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})

      with_mock Pleroma.Web.Federator,
        publish: fn _ -> nil end do
        assert {:ok, delete} = CommonAPI.delete(post.id, user)
        assert delete.local
        assert called(Pleroma.Web.Federator.publish(delete))
      end

      refute Activity.get_by_id(post.id)
    end

    test "it does not allow a user to delete their posts" do
      user = insert(:user)
      other_user = insert(:user)

      {:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})

      assert {:error, "Could not delete"} = CommonAPI.delete(post.id, other_user)
      assert Activity.get_by_id(post.id)
    end

    test "it allows moderators to delete other user's posts" do
      user = insert(:user)
      moderator = insert(:user, is_moderator: true)

      {:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})

      assert {:ok, delete} = CommonAPI.delete(post.id, moderator)
      assert delete.local

      refute Activity.get_by_id(post.id)
    end

    test "it allows admins to delete other user's posts" do
      user = insert(:user)
      moderator = insert(:user, is_admin: true)

      {:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})

      assert {:ok, delete} = CommonAPI.delete(post.id, moderator)
      assert delete.local

      refute Activity.get_by_id(post.id)
    end

    test "superusers deleting non-local posts won't federate the delete" do
      # This is the user of the ingested activity
      _user =
        insert(:user,
          local: false,
          ap_id: "http://mastodon.example.org/users/admin",
          last_refreshed_at: NaiveDateTime.utc_now()
        )

      moderator = insert(:user, is_admin: true)

      data =
        File.read!("test/fixtures/mastodon-post-activity.json")
        |> Jason.decode!()

      {:ok, post} = Transmogrifier.handle_incoming(data)

      with_mock Pleroma.Web.Federator,
        publish: fn _ -> nil end do
        assert {:ok, delete} = CommonAPI.delete(post.id, moderator)
        assert delete.local
        refute called(Pleroma.Web.Federator.publish(:_))
      end

      refute Activity.get_by_id(post.id)
    end
  end

  test "favoriting race condition" do
    user = insert(:user)
    users_serial = insert_list(10, :user)
    users = insert_list(10, :user)

    {:ok, activity} = CommonAPI.post(user, %{status: "."})

    users_serial
    |> Enum.map(fn user ->
      CommonAPI.favorite(user, activity.id)
    end)

    object = Object.get_by_ap_id(activity.data["object"])
    assert object.data["like_count"] == 10

    users
    |> Enum.map(fn user ->
      Task.async(fn ->
        CommonAPI.favorite(user, activity.id)
      end)
    end)
    |> Enum.map(&Task.await/1)

    object = Object.get_by_ap_id(activity.data["object"])
    assert object.data["like_count"] == 20
  end

  test "repeating race condition" do
    user = insert(:user)
    users_serial = insert_list(10, :user)
    users = insert_list(10, :user)

    {:ok, activity} = CommonAPI.post(user, %{status: "."})

    users_serial
    |> Enum.map(fn user ->
      CommonAPI.repeat(activity.id, user)
    end)

    object = Object.get_by_ap_id(activity.data["object"])
    assert object.data["announcement_count"] == 10

    users
    |> Enum.map(fn user ->
      Task.async(fn ->
        CommonAPI.repeat(activity.id, user)
      end)
    end)
    |> Enum.map(&Task.await/1)

    object = Object.get_by_ap_id(activity.data["object"])
    assert object.data["announcement_count"] == 20
  end

  test "when replying to a conversation / participation, it will set the correct context id even if no explicit reply_to is given" do
    user = insert(:user)
    {:ok, activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"})

    [participation] = Participation.for_user(user)

    {:ok, convo_reply} =
      CommonAPI.post(user, %{status: ".", in_reply_to_conversation_id: participation.id})

    assert Visibility.is_direct?(convo_reply)

    assert activity.data["context"] == convo_reply.data["context"]
  end

  test "when replying to a conversation / participation, it only mentions the recipients explicitly declared in the participation" do
    har = insert(:user)
    jafnhar = insert(:user)
    tridi = insert(:user)

    {:ok, activity} =
      CommonAPI.post(har, %{
        status: "@#{jafnhar.nickname} hey",
        visibility: "direct"
      })

    assert har.ap_id in activity.recipients
    assert jafnhar.ap_id in activity.recipients

    [participation] = Participation.for_user(har)

    {:ok, activity} =
      CommonAPI.post(har, %{
        status: "I don't really like @#{tridi.nickname}",
        visibility: "direct",
        in_reply_to_status_id: activity.id,
        in_reply_to_conversation_id: participation.id
      })

    assert har.ap_id in activity.recipients
    assert jafnhar.ap_id in activity.recipients
    refute tridi.ap_id in activity.recipients
  end

  test "with the safe_dm_mention option set, it does not mention people beyond the initial tags" do
    har = insert(:user)
    jafnhar = insert(:user)
    tridi = insert(:user)

    Pleroma.Config.put([:instance, :safe_dm_mentions], true)

    {:ok, activity} =
      CommonAPI.post(har, %{
        status: "@#{jafnhar.nickname} hey, i never want to see @#{tridi.nickname} again",
        visibility: "direct"
      })

    refute tridi.ap_id in activity.recipients
    assert jafnhar.ap_id in activity.recipients
  end

  test "it de-duplicates tags" do
    user = insert(:user)
    {:ok, activity} = CommonAPI.post(user, %{status: "#2hu #2HU"})

    object = Object.normalize(activity)

    assert object.data["tag"] == ["2hu"]
  end

  test "it adds emoji in the object" do
    user = insert(:user)
    {:ok, activity} = CommonAPI.post(user, %{status: ":firefox:"})

    assert Object.normalize(activity).data["emoji"]["firefox"]
  end

  describe "posting" do
    test "it supports explicit addressing" do
      user = insert(:user)
      user_two = insert(:user)
      user_three = insert(:user)
      user_four = insert(:user)

      {:ok, activity} =
        CommonAPI.post(user, %{
          status:
            "Hey, I think @#{user_three.nickname} is ugly. @#{user_four.nickname} is alright though.",
          to: [user_two.nickname, user_four.nickname, "nonexistent"]
        })

      assert user.ap_id in activity.recipients
      assert user_two.ap_id in activity.recipients
      assert user_four.ap_id in activity.recipients
      refute user_three.ap_id in activity.recipients
    end

    test "it filters out obviously bad tags when accepting a post as HTML" do
      user = insert(:user)

      post = "<p><b>2hu</b></p><script>alert('xss')</script>"

      {:ok, activity} =
        CommonAPI.post(user, %{
          status: post,
          content_type: "text/html"
        })

      object = Object.normalize(activity)

      assert object.data["content"] == "<p><b>2hu</b></p>alert(&#39;xss&#39;)"
    end

    test "it filters out obviously bad tags when accepting a post as Markdown" do
      user = insert(:user)

      post = "<p><b>2hu</b></p><script>alert('xss')</script>"

      {:ok, activity} =
        CommonAPI.post(user, %{
          status: post,
          content_type: "text/markdown"
        })

      object = Object.normalize(activity)

      assert object.data["content"] == "<p><b>2hu</b></p>alert(&#39;xss&#39;)"
    end

    test "it does not allow replies to direct messages that are not direct messages themselves" do
      user = insert(:user)

      {:ok, activity} = CommonAPI.post(user, %{status: "suya..", visibility: "direct"})

      assert {:ok, _} =
               CommonAPI.post(user, %{
                 status: "suya..",
                 visibility: "direct",
                 in_reply_to_status_id: activity.id
               })

      Enum.each(["public", "private", "unlisted"], fn visibility ->
        assert {:error, "The message visibility must be direct"} =
                 CommonAPI.post(user, %{
                   status: "suya..",
                   visibility: visibility,
                   in_reply_to_status_id: activity.id
                 })
      end)
    end

    test "it allows to address a list" do
      user = insert(:user)
      {:ok, list} = Pleroma.List.create("foo", user)

      {:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})

      assert activity.data["bcc"] == [list.ap_id]
      assert activity.recipients == [list.ap_id, user.ap_id]
      assert activity.data["listMessage"] == list.ap_id
    end

    test "it returns error when status is empty and no attachments" do
      user = insert(:user)

      assert {:error, "Cannot post an empty status without attachments"} =
               CommonAPI.post(user, %{status: ""})
    end

    test "it validates character limits are correctly enforced" do
      Pleroma.Config.put([:instance, :limit], 5)

      user = insert(:user)

      assert {:error, "The status is over the character limit"} =
               CommonAPI.post(user, %{status: "foobar"})

      assert {:ok, activity} = CommonAPI.post(user, %{status: "12345"})
    end

    test "it can handle activities that expire" do
      user = insert(:user)

      expires_at =
        NaiveDateTime.utc_now()
        |> NaiveDateTime.truncate(:second)
        |> NaiveDateTime.add(1_000_000, :second)

      assert {:ok, activity} = CommonAPI.post(user, %{status: "chai", expires_in: 1_000_000})

      assert expiration = Pleroma.ActivityExpiration.get_by_activity_id(activity.id)
      assert expiration.scheduled_at == expires_at
    end
  end

  describe "reactions" do
    test "reacting to a status with an emoji" do
      user = insert(:user)
      other_user = insert(:user)

      {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})

      {:ok, reaction} = CommonAPI.react_with_emoji(activity.id, user, "👍")

      assert reaction.data["actor"] == user.ap_id
      assert reaction.data["content"] == "👍"

      {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})

      {:error, _} = CommonAPI.react_with_emoji(activity.id, user, ".")
    end

    test "unreacting to a status with an emoji" do
      user = insert(:user)
      other_user = insert(:user)

      {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
      {:ok, reaction} = CommonAPI.react_with_emoji(activity.id, user, "👍")

      {:ok, unreaction} = CommonAPI.unreact_with_emoji(activity.id, user, "👍")

      assert unreaction.data["type"] == "Undo"
      assert unreaction.data["object"] == reaction.data["id"]
      assert unreaction.local
    end

    test "repeating a status" do
      user = insert(:user)
      other_user = insert(:user)

      {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})

      {:ok, %Activity{}, _} = CommonAPI.repeat(activity.id, user)
    end

    test "can't repeat a repeat" do
      user = insert(:user)
      other_user = insert(:user)
      {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})

      {:ok, %Activity{} = announce, _} = CommonAPI.repeat(activity.id, other_user)

      refute match?({:ok, %Activity{}, _}, CommonAPI.repeat(announce.id, user))
    end

    test "repeating a status privately" do
      user = insert(:user)
      other_user = insert(:user)

      {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})

      {:ok, %Activity{} = announce_activity, _} =
        CommonAPI.repeat(activity.id, user, %{visibility: "private"})

      assert Visibility.is_private?(announce_activity)
    end

    test "favoriting a status" do
      user = insert(:user)
      other_user = insert(:user)

      {:ok, post_activity} = CommonAPI.post(other_user, %{status: "cofe"})

      {: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

    test "retweeting a status twice returns the status" do
      user = insert(:user)
      other_user = insert(:user)

      {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
      {:ok, %Activity{} = announce, object} = CommonAPI.repeat(activity.id, user)
      {:ok, ^announce, ^object} = CommonAPI.repeat(activity.id, user)
    end

    test "favoriting a status twice returns ok, but without the like activity" do
      user = insert(:user)
      other_user = insert(:user)

      {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
      {:ok, %Activity{}} = CommonAPI.favorite(user, activity.id)
      assert {:ok, :already_liked} = CommonAPI.favorite(user, activity.id)
    end
  end

  describe "pinned statuses" do
    setup do
      Pleroma.Config.put([:instance, :max_pinned_statuses], 1)

      user = insert(:user)
      {:ok, activity} = CommonAPI.post(user, %{status: "HI!!!"})

      [user: user, activity: activity]
    end

    test "pin status", %{user: user, activity: activity} do
      assert {:ok, ^activity} = CommonAPI.pin(activity.id, user)

      id = activity.id
      user = refresh_record(user)

      assert %User{pinned_activities: [^id]} = user
    end

    test "pin poll", %{user: user} do
      {:ok, activity} =
        CommonAPI.post(user, %{
          status: "How is fediverse today?",
          poll: %{options: ["Absolutely outstanding", "Not good"], expires_in: 20}
        })

      assert {:ok, ^activity} = CommonAPI.pin(activity.id, user)

      id = activity.id
      user = refresh_record(user)

      assert %User{pinned_activities: [^id]} = user
    end

    test "unlisted statuses can be pinned", %{user: user} do
      {:ok, activity} = CommonAPI.post(user, %{status: "HI!!!", visibility: "unlisted"})
      assert {:ok, ^activity} = CommonAPI.pin(activity.id, user)
    end

    test "only self-authored can be pinned", %{activity: activity} do
      user = insert(:user)

      assert {:error, "Could not pin"} = CommonAPI.pin(activity.id, user)
    end

    test "max pinned statuses", %{user: user, activity: activity_one} do
      {:ok, activity_two} = CommonAPI.post(user, %{status: "HI!!!"})

      assert {:ok, ^activity_one} = CommonAPI.pin(activity_one.id, user)

      user = refresh_record(user)

      assert {:error, "You have already pinned the maximum number of statuses"} =
               CommonAPI.pin(activity_two.id, user)
    end

    test "unpin status", %{user: user, activity: activity} do
      {:ok, activity} = CommonAPI.pin(activity.id, user)

      user = refresh_record(user)

      id = activity.id

      assert match?({:ok, %{id: ^id}}, CommonAPI.unpin(activity.id, user))

      user = refresh_record(user)

      assert %User{pinned_activities: []} = user
    end

    test "should unpin when deleting a status", %{user: user, activity: activity} do
      {:ok, activity} = CommonAPI.pin(activity.id, user)

      user = refresh_record(user)

      assert {:ok, _} = CommonAPI.delete(activity.id, user)

      user = refresh_record(user)

      assert %User{pinned_activities: []} = user
    end
  end

  describe "mute tests" do
    setup do
      user = insert(:user)

      activity = insert(:note_activity)

      [user: user, activity: activity]
    end

    test "add mute", %{user: user, activity: activity} do
      {:ok, _} = CommonAPI.add_mute(user, activity)
      assert CommonAPI.thread_muted?(user, activity)
    end

    test "remove mute", %{user: user, activity: activity} do
      CommonAPI.add_mute(user, activity)
      {:ok, _} = CommonAPI.remove_mute(user, activity)
      refute CommonAPI.thread_muted?(user, activity)
    end

    test "check that mutes can't be duplicate", %{user: user, activity: activity} do
      CommonAPI.add_mute(user, activity)
      {:error, _} = CommonAPI.add_mute(user, activity)
    end
  end

  describe "reports" do
    test "creates a report" do
      reporter = insert(:user)
      target_user = insert(:user)

      {:ok, activity} = CommonAPI.post(target_user, %{status: "foobar"})

      reporter_ap_id = reporter.ap_id
      target_ap_id = target_user.ap_id
      activity_ap_id = activity.data["id"]
      comment = "foobar"

      report_data = %{
        account_id: target_user.id,
        comment: comment,
        status_ids: [activity.id]
      }

      note_obj = %{
        "type" => "Note",
        "id" => activity_ap_id,
        "content" => "foobar",
        "published" => activity.object.data["published"],
        "actor" => AccountView.render("show.json", %{user: target_user})
      }

      assert {:ok, flag_activity} = CommonAPI.report(reporter, report_data)

      assert %Activity{
               actor: ^reporter_ap_id,
               data: %{
                 "type" => "Flag",
                 "content" => ^comment,
                 "object" => [^target_ap_id, ^note_obj],
                 "state" => "open"
               }
             } = flag_activity
    end

    test "updates report state" do
      [reporter, target_user] = insert_pair(:user)
      activity = insert(:note_activity, user: target_user)

      {:ok, %Activity{id: report_id}} =
        CommonAPI.report(reporter, %{
          account_id: target_user.id,
          comment: "I feel offended",
          status_ids: [activity.id]
        })

      {:ok, report} = CommonAPI.update_report_state(report_id, "resolved")

      assert report.data["state"] == "resolved"

      [reported_user, activity_id] = report.data["object"]

      assert reported_user == target_user.ap_id
      assert activity_id == activity.data["id"]
    end

    test "does not update report state when state is unsupported" do
      [reporter, target_user] = insert_pair(:user)
      activity = insert(:note_activity, user: target_user)

      {:ok, %Activity{id: report_id}} =
        CommonAPI.report(reporter, %{
          account_id: target_user.id,
          comment: "I feel offended",
          status_ids: [activity.id]
        })

      assert CommonAPI.update_report_state(report_id, "test") == {:error, "Unsupported state"}
    end

    test "updates state of multiple reports" do
      [reporter, target_user] = insert_pair(:user)
      activity = insert(:note_activity, user: target_user)

      {:ok, %Activity{id: first_report_id}} =
        CommonAPI.report(reporter, %{
          account_id: target_user.id,
          comment: "I feel offended",
          status_ids: [activity.id]
        })

      {:ok, %Activity{id: second_report_id}} =
        CommonAPI.report(reporter, %{
          account_id: target_user.id,
          comment: "I feel very offended!",
          status_ids: [activity.id]
        })

      {:ok, report_ids} =
        CommonAPI.update_report_state([first_report_id, second_report_id], "resolved")

      first_report = Activity.get_by_id(first_report_id)
      second_report = Activity.get_by_id(second_report_id)

      assert report_ids -- [first_report_id, second_report_id] == []
      assert first_report.data["state"] == "resolved"
      assert second_report.data["state"] == "resolved"
    end
  end

  describe "reblog muting" do
    setup do
      muter = insert(:user)

      muted = insert(:user)

      [muter: muter, muted: muted]
    end

    test "add a reblog mute", %{muter: muter, muted: muted} do
      {:ok, _reblog_mute} = CommonAPI.hide_reblogs(muter, muted)

      assert User.showing_reblogs?(muter, muted) == false
    end

    test "remove a reblog mute", %{muter: muter, muted: muted} do
      {:ok, _reblog_mute} = CommonAPI.hide_reblogs(muter, muted)
      {:ok, _reblog_mute} = CommonAPI.show_reblogs(muter, muted)

      assert User.showing_reblogs?(muter, muted) == true
    end
  end

  describe "unfollow/2" do
    test "also unsubscribes a user" do
      [follower, followed] = insert_pair(:user)
      {:ok, follower, followed, _} = CommonAPI.follow(follower, followed)
      {:ok, _subscription} = User.subscribe(follower, followed)

      assert User.subscribed_to?(follower, followed)

      {:ok, follower} = CommonAPI.unfollow(follower, followed)

      refute User.subscribed_to?(follower, followed)
    end

    test "cancels a pending follow for a local user" do
      follower = insert(:user)
      followed = insert(:user, locked: true)

      assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} =
               CommonAPI.follow(follower, followed)

      assert User.get_follow_state(follower, followed) == :follow_pending
      assert {:ok, follower} = CommonAPI.unfollow(follower, followed)
      assert User.get_follow_state(follower, followed) == nil

      assert %{id: ^activity_id, data: %{"state" => "cancelled"}} =
               Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(follower, followed)

      assert %{
               data: %{
                 "type" => "Undo",
                 "object" => %{"type" => "Follow", "state" => "cancelled"}
               }
             } = Pleroma.Web.ActivityPub.Utils.fetch_latest_undo(follower)
    end

    test "cancels a pending follow for a remote user" do
      follower = insert(:user)
      followed = insert(:user, locked: true, local: false, ap_enabled: true)

      assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} =
               CommonAPI.follow(follower, followed)

      assert User.get_follow_state(follower, followed) == :follow_pending
      assert {:ok, follower} = CommonAPI.unfollow(follower, followed)
      assert User.get_follow_state(follower, followed) == nil

      assert %{id: ^activity_id, data: %{"state" => "cancelled"}} =
               Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(follower, followed)

      assert %{
               data: %{
                 "type" => "Undo",
                 "object" => %{"type" => "Follow", "state" => "cancelled"}
               }
             } = Pleroma.Web.ActivityPub.Utils.fetch_latest_undo(follower)
    end
  end

  describe "accept_follow_request/2" do
    test "after acceptance, it sets all existing pending follow request states to 'accept'" do
      user = insert(:user, locked: true)
      follower = insert(:user)
      follower_two = insert(:user)

      {:ok, follow_activity} = ActivityPub.follow(follower, user)
      {:ok, follow_activity_two} = ActivityPub.follow(follower, user)
      {:ok, follow_activity_three} = ActivityPub.follow(follower_two, user)

      assert follow_activity.data["state"] == "pending"
      assert follow_activity_two.data["state"] == "pending"
      assert follow_activity_three.data["state"] == "pending"

      {:ok, _follower} = CommonAPI.accept_follow_request(follower, user)

      assert Repo.get(Activity, follow_activity.id).data["state"] == "accept"
      assert Repo.get(Activity, follow_activity_two.id).data["state"] == "accept"
      assert Repo.get(Activity, follow_activity_three.id).data["state"] == "pending"
    end

    test "after rejection, it sets all existing pending follow request states to 'reject'" do
      user = insert(:user, locked: true)
      follower = insert(:user)
      follower_two = insert(:user)

      {:ok, follow_activity} = ActivityPub.follow(follower, user)
      {:ok, follow_activity_two} = ActivityPub.follow(follower, user)
      {:ok, follow_activity_three} = ActivityPub.follow(follower_two, user)

      assert follow_activity.data["state"] == "pending"
      assert follow_activity_two.data["state"] == "pending"
      assert follow_activity_three.data["state"] == "pending"

      {:ok, _follower} = CommonAPI.reject_follow_request(follower, user)

      assert Repo.get(Activity, follow_activity.id).data["state"] == "reject"
      assert Repo.get(Activity, follow_activity_two.id).data["state"] == "reject"
      assert Repo.get(Activity, follow_activity_three.id).data["state"] == "pending"
    end

    test "doesn't create a following relationship if the corresponding follow request doesn't exist" do
      user = insert(:user, locked: true)
      not_follower = insert(:user)
      CommonAPI.accept_follow_request(not_follower, user)

      assert Pleroma.FollowingRelationship.following?(not_follower, user) == false
    end
  end

  describe "vote/3" do
    test "does not allow to vote twice" do
      user = insert(:user)
      other_user = insert(:user)

      {:ok, activity} =
        CommonAPI.post(user, %{
          status: "Am I cute?",
          poll: %{options: ["Yes", "No"], expires_in: 20}
        })

      object = Object.normalize(activity)

      {:ok, _, object} = CommonAPI.vote(other_user, object, [0])

      assert {:error, "Already voted"} == CommonAPI.vote(other_user, object, [1])
    end
  end

  describe "listen/2" do
    test "returns a valid activity" do
      user = insert(:user)

      {:ok, activity} =
        CommonAPI.listen(user, %{
          title: "lain radio episode 1",
          album: "lain radio",
          artist: "lain",
          length: 180_000
        })

      object = Object.normalize(activity)

      assert object.data["title"] == "lain radio episode 1"

      assert Visibility.get_visibility(activity) == "public"
    end

    test "respects visibility=private" do
      user = insert(:user)

      {:ok, activity} =
        CommonAPI.listen(user, %{
          title: "lain radio episode 1",
          album: "lain radio",
          artist: "lain",
          length: 180_000,
          visibility: "private"
        })

      object = Object.normalize(activity)

      assert object.data["title"] == "lain radio episode 1"

      assert Visibility.get_visibility(activity) == "private"
    end
  end
end