diff options
| -rw-r--r-- | lib/pleroma/web/controller_helper.ex | 2 | ||||
| -rw-r--r-- | lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex | 125 | ||||
| -rw-r--r-- | lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex | 136 | ||||
| -rw-r--r-- | lib/pleroma/web/router.ex | 10 | ||||
| -rw-r--r-- | test/web/mastodon_api/controllers/timeline_controller_test.exs | 291 | ||||
| -rw-r--r-- | test/web/mastodon_api/mastodon_api_controller_test.exs | 283 | 
6 files changed, 438 insertions, 409 deletions
diff --git a/lib/pleroma/web/controller_helper.ex b/lib/pleroma/web/controller_helper.ex index b53a01955..e90bf842e 100644 --- a/lib/pleroma/web/controller_helper.ex +++ b/lib/pleroma/web/controller_helper.ex @@ -6,7 +6,7 @@ defmodule Pleroma.Web.ControllerHelper do    use Pleroma.Web, :controller    # As in MastoAPI, per https://api.rubyonrails.org/classes/ActiveModel/Type/Boolean.html -  @falsy_param_values [false, 0, "0", "f", "F", "false", "FALSE", "off", "OFF"] +  @falsy_param_values [false, 0, "0", "f", "F", "false", "False", "FALSE", "off", "OFF"]    def truthy_param?(blank_value) when blank_value in [nil, ""], do: nil    def truthy_param?(value), do: value not in @falsy_param_values diff --git a/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex index 1e88ff7fe..74a8b5055 100644 --- a/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex @@ -6,7 +6,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do    use Pleroma.Web, :controller    import Pleroma.Web.ControllerHelper, -    only: [json_response: 3, add_link_headers: 2, add_link_headers: 3] +    only: [json_response: 3, add_link_headers: 2, truthy_param?: 1]    alias Ecto.Changeset    alias Pleroma.Activity @@ -44,7 +44,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do    alias Pleroma.Web.OAuth.Token    alias Pleroma.Web.TwitterAPI.TwitterAPI -  alias Pleroma.Web.ControllerHelper    import Ecto.Query    require Logger @@ -156,7 +155,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do        ]        |> Enum.reduce(%{}, fn key, acc ->          add_if_present(acc, params, to_string(key), key, fn value -> -          {:ok, ControllerHelper.truthy_param?(value)} +          {:ok, truthy_param?(value)}          end)        end)        |> add_if_present(params, "default_scope", :default_scope) @@ -344,43 +343,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do      json(conn, mastodon_emoji)    end -  def home_timeline(%{assigns: %{user: user}} = conn, params) do -    params = -      params -      |> Map.put("type", ["Create", "Announce"]) -      |> Map.put("blocking_user", user) -      |> Map.put("muting_user", user) -      |> Map.put("user", user) - -    activities = -      [user.ap_id | user.following] -      |> ActivityPub.fetch_activities(params) -      |> Enum.reverse() - -    conn -    |> add_link_headers(activities) -    |> put_view(StatusView) -    |> render("index.json", %{activities: activities, for: user, as: :activity}) -  end - -  def public_timeline(%{assigns: %{user: user}} = conn, params) do -    local_only = params["local"] in [true, "True", "true", "1"] - -    activities = -      params -      |> Map.put("type", ["Create", "Announce"]) -      |> Map.put("local_only", local_only) -      |> Map.put("blocking_user", user) -      |> Map.put("muting_user", user) -      |> ActivityPub.fetch_public_activities() -      |> Enum.reverse() - -    conn -    |> add_link_headers(activities, %{"local" => local_only}) -    |> put_view(StatusView) -    |> render("index.json", %{activities: activities, for: user, as: :activity}) -  end -    def user_statuses(%{assigns: %{user: reading_user}} = conn, params) do      with %User{} = user <- User.get_cached_by_nickname_or_id(params["id"], for: reading_user) do        params = @@ -400,25 +362,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do      end    end -  def dm_timeline(%{assigns: %{user: user}} = conn, params) do -    params = -      params -      |> Map.put("type", "Create") -      |> Map.put("blocking_user", user) -      |> Map.put("user", user) -      |> Map.put(:visibility, "direct") - -    activities = -      [user.ap_id] -      |> ActivityPub.fetch_activities_query(params) -      |> Pagination.fetch_paginated(params) - -    conn -    |> add_link_headers(activities) -    |> put_view(StatusView) -    |> render("index.json", %{activities: activities, for: user, as: :activity}) -  end -    def get_statuses(%{assigns: %{user: user}} = conn, %{"ids" => ids}) do      limit = 100 @@ -822,45 +765,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do      end    end -  def hashtag_timeline(%{assigns: %{user: user}} = conn, params) do -    local_only = params["local"] in [true, "True", "true", "1"] - -    tags = -      [params["tag"], params["any"]] -      |> List.flatten() -      |> Enum.uniq() -      |> Enum.filter(& &1) -      |> Enum.map(&String.downcase(&1)) - -    tag_all = -      params["all"] || -        [] -        |> Enum.map(&String.downcase(&1)) - -    tag_reject = -      params["none"] || -        [] -        |> Enum.map(&String.downcase(&1)) - -    activities = -      params -      |> Map.put("type", "Create") -      |> Map.put("local_only", local_only) -      |> Map.put("blocking_user", user) -      |> Map.put("muting_user", user) -      |> Map.put("user", user) -      |> Map.put("tag", tags) -      |> Map.put("tag_all", tag_all) -      |> Map.put("tag_reject", tag_reject) -      |> ActivityPub.fetch_public_activities() -      |> Enum.reverse() - -    conn -    |> add_link_headers(activities, %{"local" => local_only}) -    |> put_view(StatusView) -    |> render("index.json", %{activities: activities, for: user, as: :activity}) -  end -    def followers(%{assigns: %{user: for_user}} = conn, %{"id" => id} = params) do      with %User{} = user <- User.get_cached_by_id(id),           followers <- MastodonAPI.get_followers(user, params) do @@ -1173,31 +1077,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do      json(conn, res)    end -  def list_timeline(%{assigns: %{user: user}} = conn, %{"list_id" => id} = params) do -    with %Pleroma.List{title: _title, following: following} <- Pleroma.List.get(id, user) do -      params = -        params -        |> Map.put("type", "Create") -        |> Map.put("blocking_user", user) -        |> Map.put("user", user) -        |> Map.put("muting_user", user) - -      # we must filter the following list for the user to avoid leaking statuses the user -      # does not actually have permission to see (for more info, peruse security issue #270). -      activities = -        following -        |> Enum.filter(fn x -> x in user.following end) -        |> ActivityPub.fetch_activities_bounded(following, params) -        |> Enum.reverse() - -      conn -      |> put_view(StatusView) -      |> render("index.json", %{activities: activities, for: user, as: :activity}) -    else -      _e -> render_error(conn, :forbidden, "Error.") -    end -  end -    def index(%{assigns: %{user: user}} = conn, _params) do      token = get_session(conn, :oauth_token) diff --git a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex new file mode 100644 index 000000000..bb8b0eb32 --- /dev/null +++ b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex @@ -0,0 +1,136 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.TimelineController do +  use Pleroma.Web, :controller + +  import Pleroma.Web.ControllerHelper, +    only: [add_link_headers: 2, add_link_headers: 3, truthy_param?: 1] + +  alias Pleroma.Pagination +  alias Pleroma.Web.ActivityPub.ActivityPub + +  plug(:put_view, Pleroma.Web.MastodonAPI.StatusView) + +  # GET /api/v1/timelines/home +  def home(%{assigns: %{user: user}} = conn, params) do +    params = +      params +      |> Map.put("type", ["Create", "Announce"]) +      |> Map.put("blocking_user", user) +      |> Map.put("muting_user", user) +      |> Map.put("user", user) + +    recipients = [user.ap_id | user.following] + +    activities = +      recipients +      |> ActivityPub.fetch_activities(params) +      |> Enum.reverse() + +    conn +    |> add_link_headers(activities) +    |> render("index.json", activities: activities, for: user, as: :activity) +  end + +  # GET /api/v1/timelines/direct +  def direct(%{assigns: %{user: user}} = conn, params) do +    params = +      params +      |> Map.put("type", "Create") +      |> Map.put("blocking_user", user) +      |> Map.put("user", user) +      |> Map.put(:visibility, "direct") + +    activities = +      [user.ap_id] +      |> ActivityPub.fetch_activities_query(params) +      |> Pagination.fetch_paginated(params) + +    conn +    |> add_link_headers(activities) +    |> render("index.json", activities: activities, for: user, as: :activity) +  end + +  # GET /api/v1/timelines/public +  def public(%{assigns: %{user: user}} = conn, params) do +    local_only = truthy_param?(params["local"]) + +    activities = +      params +      |> Map.put("type", ["Create", "Announce"]) +      |> Map.put("local_only", local_only) +      |> Map.put("blocking_user", user) +      |> Map.put("muting_user", user) +      |> ActivityPub.fetch_public_activities() +      |> Enum.reverse() + +    conn +    |> add_link_headers(activities, %{"local" => local_only}) +    |> render("index.json", activities: activities, for: user, as: :activity) +  end + +  # GET /api/v1/timelines/tag/:tag +  def hashtag(%{assigns: %{user: user}} = conn, params) do +    local_only = truthy_param?(params["local"]) + +    tags = +      [params["tag"], params["any"]] +      |> List.flatten() +      |> Enum.uniq() +      |> Enum.filter(& &1) +      |> Enum.map(&String.downcase(&1)) + +    tag_all = +      params +      |> Map.get("all", []) +      |> Enum.map(&String.downcase(&1)) + +    tag_reject = +      params +      |> Map.get("none", []) +      |> Enum.map(&String.downcase(&1)) + +    activities = +      params +      |> Map.put("type", "Create") +      |> Map.put("local_only", local_only) +      |> Map.put("blocking_user", user) +      |> Map.put("muting_user", user) +      |> Map.put("user", user) +      |> Map.put("tag", tags) +      |> Map.put("tag_all", tag_all) +      |> Map.put("tag_reject", tag_reject) +      |> ActivityPub.fetch_public_activities() +      |> Enum.reverse() + +    conn +    |> add_link_headers(activities, %{"local" => local_only}) +    |> render("index.json", activities: activities, for: user, as: :activity) +  end + +  # GET /api/v1/timelines/list/:list_id +  def list(%{assigns: %{user: user}} = conn, %{"list_id" => id} = params) do +    with %Pleroma.List{title: _title, following: following} <- Pleroma.List.get(id, user) do +      params = +        params +        |> Map.put("type", "Create") +        |> Map.put("blocking_user", user) +        |> Map.put("user", user) +        |> Map.put("muting_user", user) + +      # we must filter the following list for the user to avoid leaking statuses the user +      # does not actually have permission to see (for more info, peruse security issue #270). +      activities = +        following +        |> Enum.filter(fn x -> x in user.following end) +        |> ActivityPub.fetch_activities_bounded(following, params) +        |> Enum.reverse() + +      render(conn, "index.json", activities: activities, for: user, as: :activity) +    else +      _e -> render_error(conn, :forbidden, "Error.") +    end +  end +end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 316c895ee..2575481ff 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -319,8 +319,8 @@ defmodule Pleroma.Web.Router do        get("/blocks", MastodonAPIController, :blocks)        get("/mutes", MastodonAPIController, :mutes) -      get("/timelines/home", MastodonAPIController, :home_timeline) -      get("/timelines/direct", MastodonAPIController, :dm_timeline) +      get("/timelines/home", TimelineController, :home) +      get("/timelines/direct", TimelineController, :direct)        get("/favourites", MastodonAPIController, :favourites)        get("/bookmarks", MastodonAPIController, :bookmarks) @@ -466,9 +466,9 @@ defmodule Pleroma.Web.Router do      scope [] do        pipe_through(:oauth_read_or_public) -      get("/timelines/public", MastodonAPIController, :public_timeline) -      get("/timelines/tag/:tag", MastodonAPIController, :hashtag_timeline) -      get("/timelines/list/:list_id", MastodonAPIController, :list_timeline) +      get("/timelines/public", TimelineController, :public) +      get("/timelines/tag/:tag", TimelineController, :hashtag) +      get("/timelines/list/:list_id", TimelineController, :list)        get("/statuses", MastodonAPIController, :get_statuses)        get("/statuses/:id", MastodonAPIController, :get_status) diff --git a/test/web/mastodon_api/controllers/timeline_controller_test.exs b/test/web/mastodon_api/controllers/timeline_controller_test.exs new file mode 100644 index 000000000..d3652d964 --- /dev/null +++ b/test/web/mastodon_api/controllers/timeline_controller_test.exs @@ -0,0 +1,291 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do +  use Pleroma.Web.ConnCase + +  import Pleroma.Factory +  import Tesla.Mock + +  alias Pleroma.Config +  alias Pleroma.User +  alias Pleroma.Web.CommonAPI +  alias Pleroma.Web.OStatus + +  clear_config([:instance, :public]) + +  setup do +    mock(fn env -> apply(HttpRequestMock, :request, [env]) end) +    :ok +  end + +  test "the home timeline", %{conn: conn} do +    user = insert(:user) +    following = insert(:user) + +    {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"}) + +    conn = +      conn +      |> assign(:user, user) +      |> get("/api/v1/timelines/home") + +    assert Enum.empty?(json_response(conn, :ok)) + +    {:ok, user} = User.follow(user, following) + +    conn = +      build_conn() +      |> assign(:user, user) +      |> get("/api/v1/timelines/home") + +    assert [%{"content" => "test"}] = json_response(conn, :ok) +  end + +  describe "public" do +    @tag capture_log: true +    test "the public timeline", %{conn: conn} do +      following = insert(:user) + +      {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"}) + +      {:ok, [_activity]} = +        OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873") + +      conn = get(conn, "/api/v1/timelines/public", %{"local" => "False"}) + +      assert length(json_response(conn, :ok)) == 2 + +      conn = get(build_conn(), "/api/v1/timelines/public", %{"local" => "True"}) + +      assert [%{"content" => "test"}] = json_response(conn, :ok) + +      conn = get(build_conn(), "/api/v1/timelines/public", %{"local" => "1"}) + +      assert [%{"content" => "test"}] = json_response(conn, :ok) +    end + +    test "the public timeline when public is set to false", %{conn: conn} do +      Config.put([:instance, :public], false) + +      assert %{"error" => "This resource requires authentication."} == +               conn +               |> get("/api/v1/timelines/public", %{"local" => "False"}) +               |> json_response(:forbidden) +    end + +    test "the public timeline includes only public statuses for an authenticated user" do +      user = insert(:user) + +      conn = +        build_conn() +        |> assign(:user, user) + +      {:ok, _activity} = CommonAPI.post(user, %{"status" => "test"}) +      {:ok, _activity} = CommonAPI.post(user, %{"status" => "test", "visibility" => "private"}) +      {:ok, _activity} = CommonAPI.post(user, %{"status" => "test", "visibility" => "unlisted"}) +      {:ok, _activity} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"}) + +      res_conn = get(conn, "/api/v1/timelines/public") +      assert length(json_response(res_conn, 200)) == 1 +    end +  end + +  describe "direct" do +    test "direct timeline", %{conn: conn} do +      user_one = insert(:user) +      user_two = insert(:user) + +      {:ok, user_two} = User.follow(user_two, user_one) + +      {:ok, direct} = +        CommonAPI.post(user_one, %{ +          "status" => "Hi @#{user_two.nickname}!", +          "visibility" => "direct" +        }) + +      {:ok, _follower_only} = +        CommonAPI.post(user_one, %{ +          "status" => "Hi @#{user_two.nickname}!", +          "visibility" => "private" +        }) + +      # Only direct should be visible here +      res_conn = +        conn +        |> assign(:user, user_two) +        |> get("api/v1/timelines/direct") + +      [status] = json_response(res_conn, :ok) + +      assert %{"visibility" => "direct"} = status +      assert status["url"] != direct.data["id"] + +      # User should be able to see their own direct message +      res_conn = +        build_conn() +        |> assign(:user, user_one) +        |> get("api/v1/timelines/direct") + +      [status] = json_response(res_conn, :ok) + +      assert %{"visibility" => "direct"} = status + +      # Both should be visible here +      res_conn = +        conn +        |> assign(:user, user_two) +        |> get("api/v1/timelines/home") + +      [_s1, _s2] = json_response(res_conn, :ok) + +      # Test pagination +      Enum.each(1..20, fn _ -> +        {:ok, _} = +          CommonAPI.post(user_one, %{ +            "status" => "Hi @#{user_two.nickname}!", +            "visibility" => "direct" +          }) +      end) + +      res_conn = +        conn +        |> assign(:user, user_two) +        |> get("api/v1/timelines/direct") + +      statuses = json_response(res_conn, :ok) +      assert length(statuses) == 20 + +      res_conn = +        conn +        |> assign(:user, user_two) +        |> get("api/v1/timelines/direct", %{max_id: List.last(statuses)["id"]}) + +      [status] = json_response(res_conn, :ok) + +      assert status["url"] != direct.data["id"] +    end + +    test "doesn't include DMs from blocked users", %{conn: conn} do +      blocker = insert(:user) +      blocked = insert(:user) +      user = insert(:user) +      {:ok, blocker} = User.block(blocker, blocked) + +      {:ok, _blocked_direct} = +        CommonAPI.post(blocked, %{ +          "status" => "Hi @#{blocker.nickname}!", +          "visibility" => "direct" +        }) + +      {:ok, direct} = +        CommonAPI.post(user, %{ +          "status" => "Hi @#{blocker.nickname}!", +          "visibility" => "direct" +        }) + +      res_conn = +        conn +        |> assign(:user, user) +        |> get("api/v1/timelines/direct") + +      [status] = json_response(res_conn, :ok) +      assert status["id"] == direct.id +    end +  end + +  describe "list" do +    test "list timeline", %{conn: conn} do +      user = insert(:user) +      other_user = insert(:user) +      {:ok, _activity_one} = CommonAPI.post(user, %{"status" => "Marisa is cute."}) +      {:ok, activity_two} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."}) +      {:ok, list} = Pleroma.List.create("name", user) +      {:ok, list} = Pleroma.List.follow(list, other_user) + +      conn = +        conn +        |> assign(:user, user) +        |> get("/api/v1/timelines/list/#{list.id}") + +      assert [%{"id" => id}] = json_response(conn, :ok) + +      assert id == to_string(activity_two.id) +    end + +    test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do +      user = insert(:user) +      other_user = insert(:user) +      {:ok, activity_one} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."}) + +      {:ok, _activity_two} = +        CommonAPI.post(other_user, %{ +          "status" => "Marisa is cute.", +          "visibility" => "private" +        }) + +      {:ok, list} = Pleroma.List.create("name", user) +      {:ok, list} = Pleroma.List.follow(list, other_user) + +      conn = +        conn +        |> assign(:user, user) +        |> get("/api/v1/timelines/list/#{list.id}") + +      assert [%{"id" => id}] = json_response(conn, :ok) + +      assert id == to_string(activity_one.id) +    end +  end + +  describe "hashtag" do +    @tag capture_log: true +    test "hashtag timeline", %{conn: conn} do +      following = insert(:user) + +      {:ok, activity} = CommonAPI.post(following, %{"status" => "test #2hu"}) + +      {:ok, [_activity]} = +        OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873") + +      nconn = get(conn, "/api/v1/timelines/tag/2hu") + +      assert [%{"id" => id}] = json_response(nconn, :ok) + +      assert id == to_string(activity.id) + +      # works for different capitalization too +      nconn = get(conn, "/api/v1/timelines/tag/2HU") + +      assert [%{"id" => id}] = json_response(nconn, :ok) + +      assert id == to_string(activity.id) +    end + +    test "multi-hashtag timeline", %{conn: conn} do +      user = insert(:user) + +      {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"}) +      {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"}) +      {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"}) + +      any_test = get(conn, "/api/v1/timelines/tag/test", %{"any" => ["test1"]}) + +      [status_none, status_test1, status_test] = json_response(any_test, :ok) + +      assert to_string(activity_test.id) == status_test["id"] +      assert to_string(activity_test1.id) == status_test1["id"] +      assert to_string(activity_none.id) == status_none["id"] + +      restricted_test = +        get(conn, "/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]}) + +      assert [status_test1] == json_response(restricted_test, :ok) + +      all_test = get(conn, "/api/v1/timelines/tag/test", %{"all" => ["none"]}) + +      assert [status_none] == json_response(all_test, :ok) +    end +  end +end diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs index cd672132b..7f7a89516 100644 --- a/test/web/mastodon_api/mastodon_api_controller_test.exs +++ b/test/web/mastodon_api/mastodon_api_controller_test.exs @@ -20,12 +20,12 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do    alias Pleroma.Web.MastodonAPI.FilterView    alias Pleroma.Web.OAuth.App    alias Pleroma.Web.OAuth.Token -  alias Pleroma.Web.OStatus    alias Pleroma.Web.Push -  import Pleroma.Factory +    import ExUnit.CaptureLog -  import Tesla.Mock +  import Pleroma.Factory    import Swoosh.TestAssertions +  import Tesla.Mock    @image "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7" @@ -37,82 +37,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do    clear_config([:instance, :public])    clear_config([:rich_media, :enabled]) -  test "the home timeline", %{conn: conn} do -    user = insert(:user) -    following = insert(:user) - -    {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"}) - -    conn = -      conn -      |> assign(:user, user) -      |> get("/api/v1/timelines/home") - -    assert Enum.empty?(json_response(conn, 200)) - -    {:ok, user} = User.follow(user, following) - -    conn = -      build_conn() -      |> assign(:user, user) -      |> get("/api/v1/timelines/home") - -    assert [%{"content" => "test"}] = json_response(conn, 200) -  end - -  test "the public timeline", %{conn: conn} do -    following = insert(:user) - -    capture_log(fn -> -      {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"}) - -      {:ok, [_activity]} = -        OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873") - -      conn = -        conn -        |> get("/api/v1/timelines/public", %{"local" => "False"}) - -      assert length(json_response(conn, 200)) == 2 - -      conn = -        build_conn() -        |> get("/api/v1/timelines/public", %{"local" => "True"}) - -      assert [%{"content" => "test"}] = json_response(conn, 200) - -      conn = -        build_conn() -        |> get("/api/v1/timelines/public", %{"local" => "1"}) - -      assert [%{"content" => "test"}] = json_response(conn, 200) -    end) -  end - -  test "the public timeline when public is set to false", %{conn: conn} do -    Config.put([:instance, :public], false) - -    assert conn -           |> get("/api/v1/timelines/public", %{"local" => "False"}) -           |> json_response(403) == %{"error" => "This resource requires authentication."} -  end - -  test "the public timeline includes only public statuses for an authenticated user" do -    user = insert(:user) - -    conn = -      build_conn() -      |> assign(:user, user) - -    {:ok, _activity} = CommonAPI.post(user, %{"status" => "test"}) -    {:ok, _activity} = CommonAPI.post(user, %{"status" => "test", "visibility" => "private"}) -    {:ok, _activity} = CommonAPI.post(user, %{"status" => "test", "visibility" => "unlisted"}) -    {:ok, _activity} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"}) - -    res_conn = get(conn, "/api/v1/timelines/public") -    assert length(json_response(res_conn, 200)) == 1 -  end -    describe "posting statuses" do      setup do        user = insert(:user) @@ -419,80 +343,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do      end    end -  test "direct timeline", %{conn: conn} do -    user_one = insert(:user) -    user_two = insert(:user) - -    {:ok, user_two} = User.follow(user_two, user_one) - -    {:ok, direct} = -      CommonAPI.post(user_one, %{ -        "status" => "Hi @#{user_two.nickname}!", -        "visibility" => "direct" -      }) - -    {:ok, _follower_only} = -      CommonAPI.post(user_one, %{ -        "status" => "Hi @#{user_two.nickname}!", -        "visibility" => "private" -      }) - -    # Only direct should be visible here -    res_conn = -      conn -      |> assign(:user, user_two) -      |> get("api/v1/timelines/direct") - -    [status] = json_response(res_conn, 200) - -    assert %{"visibility" => "direct"} = status -    assert status["url"] != direct.data["id"] - -    # User should be able to see their own direct message -    res_conn = -      build_conn() -      |> assign(:user, user_one) -      |> get("api/v1/timelines/direct") - -    [status] = json_response(res_conn, 200) - -    assert %{"visibility" => "direct"} = status - -    # Both should be visible here -    res_conn = -      conn -      |> assign(:user, user_two) -      |> get("api/v1/timelines/home") - -    [_s1, _s2] = json_response(res_conn, 200) - -    # Test pagination -    Enum.each(1..20, fn _ -> -      {:ok, _} = -        CommonAPI.post(user_one, %{ -          "status" => "Hi @#{user_two.nickname}!", -          "visibility" => "direct" -        }) -    end) - -    res_conn = -      conn -      |> assign(:user, user_two) -      |> get("api/v1/timelines/direct") - -    statuses = json_response(res_conn, 200) -    assert length(statuses) == 20 - -    res_conn = -      conn -      |> assign(:user, user_two) -      |> get("api/v1/timelines/direct", %{max_id: List.last(statuses)["id"]}) - -    [status] = json_response(res_conn, 200) - -    assert status["url"] != direct.data["id"] -  end -    test "Conversations", %{conn: conn} do      user_one = insert(:user)      user_two = insert(:user) @@ -556,33 +406,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do      assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200)    end -  test "doesn't include DMs from blocked users", %{conn: conn} do -    blocker = insert(:user) -    blocked = insert(:user) -    user = insert(:user) -    {:ok, blocker} = User.block(blocker, blocked) - -    {:ok, _blocked_direct} = -      CommonAPI.post(blocked, %{ -        "status" => "Hi @#{blocker.nickname}!", -        "visibility" => "direct" -      }) - -    {:ok, direct} = -      CommonAPI.post(user, %{ -        "status" => "Hi @#{blocker.nickname}!", -        "visibility" => "direct" -      }) - -    res_conn = -      conn -      |> assign(:user, user) -      |> get("api/v1/timelines/direct") - -    [status] = json_response(res_conn, 200) -    assert status["id"] == direct.id -  end -    test "verify_credentials", %{conn: conn} do      user = insert(:user) @@ -955,50 +778,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do      end    end -  describe "list timelines" do -    test "list timeline", %{conn: conn} do -      user = insert(:user) -      other_user = insert(:user) -      {:ok, _activity_one} = CommonAPI.post(user, %{"status" => "Marisa is cute."}) -      {:ok, activity_two} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."}) -      {:ok, list} = Pleroma.List.create("name", user) -      {:ok, list} = Pleroma.List.follow(list, other_user) - -      conn = -        conn -        |> assign(:user, user) -        |> get("/api/v1/timelines/list/#{list.id}") - -      assert [%{"id" => id}] = json_response(conn, 200) - -      assert id == to_string(activity_two.id) -    end - -    test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do -      user = insert(:user) -      other_user = insert(:user) -      {:ok, activity_one} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."}) - -      {:ok, _activity_two} = -        CommonAPI.post(other_user, %{ -          "status" => "Marisa is cute.", -          "visibility" => "private" -        }) - -      {:ok, list} = Pleroma.List.create("name", user) -      {:ok, list} = Pleroma.List.follow(list, other_user) - -      conn = -        conn -        |> assign(:user, user) -        |> get("/api/v1/timelines/list/#{list.id}") - -      assert [%{"id" => id}] = json_response(conn, 200) - -      assert id == to_string(activity_one.id) -    end -  end -    describe "reblogging" do      test "reblogs and returns the reblogged status", %{conn: conn} do        activity = insert(:note_activity) @@ -1554,62 +1333,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do      assert url =~ "an_image"    end -  test "hashtag timeline", %{conn: conn} do -    following = insert(:user) - -    capture_log(fn -> -      {:ok, activity} = CommonAPI.post(following, %{"status" => "test #2hu"}) - -      {:ok, [_activity]} = -        OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873") - -      nconn = -        conn -        |> get("/api/v1/timelines/tag/2hu") - -      assert [%{"id" => id}] = json_response(nconn, 200) - -      assert id == to_string(activity.id) - -      # works for different capitalization too -      nconn = -        conn -        |> get("/api/v1/timelines/tag/2HU") - -      assert [%{"id" => id}] = json_response(nconn, 200) - -      assert id == to_string(activity.id) -    end) -  end - -  test "multi-hashtag timeline", %{conn: conn} do -    user = insert(:user) - -    {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"}) -    {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"}) -    {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"}) - -    any_test = -      conn -      |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]}) - -    [status_none, status_test1, status_test] = json_response(any_test, 200) - -    assert to_string(activity_test.id) == status_test["id"] -    assert to_string(activity_test1.id) == status_test1["id"] -    assert to_string(activity_none.id) == status_none["id"] - -    restricted_test = -      conn -      |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]}) - -    assert [status_test1] == json_response(restricted_test, 200) - -    all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]}) - -    assert [status_none] == json_response(all_test, 200) -  end -    test "getting followers", %{conn: conn} do      user = insert(:user)      other_user = insert(:user)  | 
