diff options
| -rw-r--r-- | lib/pleroma/web/api_spec/operations/timeline_operation.ex | 199 | ||||
| -rw-r--r-- | lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex | 18 | ||||
| -rw-r--r-- | mix.exs | 2 | ||||
| -rw-r--r-- | mix.lock | 2 | ||||
| -rw-r--r-- | test/web/mastodon_api/controllers/timeline_controller_test.exs | 114 | 
5 files changed, 271 insertions, 64 deletions
diff --git a/lib/pleroma/web/api_spec/operations/timeline_operation.ex b/lib/pleroma/web/api_spec/operations/timeline_operation.ex new file mode 100644 index 000000000..1b89035d4 --- /dev/null +++ b/lib/pleroma/web/api_spec/operations/timeline_operation.ex @@ -0,0 +1,199 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.TimelineOperation do +  alias OpenApiSpex.Operation +  alias OpenApiSpex.Schema +  alias Pleroma.Web.ApiSpec.Schemas.ApiError +  alias Pleroma.Web.ApiSpec.Schemas.BooleanLike +  alias Pleroma.Web.ApiSpec.Schemas.Status +  alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope + +  import Pleroma.Web.ApiSpec.Helpers + +  def open_api_operation(action) do +    operation = String.to_existing_atom("#{action}_operation") +    apply(__MODULE__, operation, []) +  end + +  def home_operation do +    %Operation{ +      tags: ["Timelines"], +      summary: "Home timeline", +      description: "View statuses from followed users", +      security: [%{"oAuth" => ["read:statuses"]}], +      parameters: [ +        local_param(), +        with_muted_param(), +        exclude_visibilities_param(), +        reply_visibility_param(), +        with_relationships_param() | pagination_params() +      ], +      operationId: "TimelineController.home", +      responses: %{ +        200 => Operation.response("Array of Status", "application/json", array_of_statuses()) +      } +    } +  end + +  def direct_operation do +    %Operation{ +      tags: ["Timelines"], +      summary: "Direct timeline", +      description: +        "View statuses with a “direct” privacy, from your account or in your notifications", +      deprecated: true, +      parameters: pagination_params(), +      security: [%{"oAuth" => ["read:statuses"]}], +      operationId: "TimelineController.direct", +      responses: %{ +        200 => Operation.response("Array of Status", "application/json", array_of_statuses()) +      } +    } +  end + +  def public_operation do +    %Operation{ +      tags: ["Timelines"], +      summary: "Public timeline", +      security: [%{"oAuth" => ["read:statuses"]}], +      parameters: [ +        local_param(), +        only_media_param(), +        with_muted_param(), +        exclude_visibilities_param(), +        reply_visibility_param(), +        with_relationships_param() | pagination_params() +      ], +      operationId: "TimelineController.public", +      responses: %{ +        200 => Operation.response("Array of Status", "application/json", array_of_statuses()), +        401 => Operation.response("Error", "application/json", ApiError) +      } +    } +  end + +  def hashtag_operation do +    %Operation{ +      tags: ["Timelines"], +      summary: "Hashtag timeline", +      description: "View public statuses containing the given hashtag", +      security: [%{"oAuth" => ["read:statuses"]}], +      parameters: [ +        Operation.parameter( +          :tag, +          :path, +          %Schema{type: :string}, +          "Content of a #hashtag, not including # symbol.", +          required: true +        ), +        Operation.parameter( +          :any, +          :query, +          %Schema{type: :array, items: %Schema{type: :string}}, +          "Statuses that also includes any of these tags" +        ), +        Operation.parameter( +          :all, +          :query, +          %Schema{type: :array, items: %Schema{type: :string}}, +          "Statuses that also includes all of these tags" +        ), +        Operation.parameter( +          :none, +          :query, +          %Schema{type: :array, items: %Schema{type: :string}}, +          "Statuses that do not include these tags" +        ), +        local_param(), +        only_media_param(), +        with_muted_param(), +        exclude_visibilities_param(), +        with_relationships_param() | pagination_params() +      ], +      operationId: "TimelineController.hashtag", +      responses: %{ +        200 => Operation.response("Array of Status", "application/json", array_of_statuses()) +      } +    } +  end + +  def list_operation do +    %Operation{ +      tags: ["Timelines"], +      summary: "List timeline", +      description: "View statuses in the given list timeline", +      security: [%{"oAuth" => ["read:lists"]}], +      parameters: [ +        Operation.parameter( +          :list_id, +          :path, +          %Schema{type: :string}, +          "Local ID of the list in the database", +          required: true +        ), +        with_muted_param(), +        exclude_visibilities_param(), +        with_relationships_param() | pagination_params() +      ], +      operationId: "TimelineController.list", +      responses: %{ +        200 => Operation.response("Array of Status", "application/json", array_of_statuses()) +      } +    } +  end + +  defp array_of_statuses do +    %Schema{ +      title: "ArrayOfStatuses", +      type: :array, +      items: Status, +      example: [Status.schema().example] +    } +  end + +  defp with_relationships_param do +    Operation.parameter(:with_relationships, :query, BooleanLike, "Include relationships") +  end + +  defp local_param do +    Operation.parameter( +      :local, +      :query, +      %Schema{allOf: [BooleanLike], default: false}, +      "Show only local statuses?" +    ) +  end + +  defp with_muted_param do +    Operation.parameter(:with_muted, :query, BooleanLike, "Includeactivities by muted users") +  end + +  defp exclude_visibilities_param do +    Operation.parameter( +      :exclude_visibilities, +      :query, +      %Schema{type: :array, items: VisibilityScope}, +      "Exclude the statuses with the given visibilities" +    ) +  end + +  defp reply_visibility_param do +    Operation.parameter( +      :reply_visibility, +      :query, +      %Schema{type: :string, enum: ["following", "self"]}, +      "Filter replies. Possible values: without parameter (default) shows all replies, `following` - replies directed to you or users you follow, `self` - replies directed to you." +    ) +  end + +  defp only_media_param do +    Operation.parameter( +      :only_media, +      :query, +      %Schema{allOf: [BooleanLike], default: false}, +      "Show only statuses with media attached?" +    ) +  end +end diff --git a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex index 2d67e19da..bbd576ffd 100644 --- a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex @@ -6,7 +6,7 @@ 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, skip_relationships?: 1] +    only: [add_link_headers: 2, add_link_headers: 3, skip_relationships?: 1]    alias Pleroma.Pagination    alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug @@ -15,6 +15,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do    alias Pleroma.User    alias Pleroma.Web.ActivityPub.ActivityPub +  plug(Pleroma.Web.ApiSpec.CastAndValidate)    plug(:skip_plug, EnsurePublicOrAuthenticatedPlug when action in [:public, :hashtag])    # TODO: Replace with a macro when there is a Phoenix release with the following commit in it: @@ -37,10 +38,13 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do    plug(:put_view, Pleroma.Web.MastodonAPI.StatusView) +  defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.TimelineOperation +    # GET /api/v1/timelines/home    def home(%{assigns: %{user: user}} = conn, params) do      params =        params +      |> Map.new(fn {key, value} -> {to_string(key), value} end)        |> Map.put("type", ["Create", "Announce"])        |> Map.put("blocking_user", user)        |> Map.put("muting_user", user) @@ -68,6 +72,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do    def direct(%{assigns: %{user: user}} = conn, params) do      params =        params +      |> Map.new(fn {key, value} -> {to_string(key), value} end)        |> Map.put("type", "Create")        |> Map.put("blocking_user", user)        |> Map.put("user", user) @@ -90,7 +95,9 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do    # GET /api/v1/timelines/public    def public(%{assigns: %{user: user}} = conn, params) do -    local_only = truthy_param?(params["local"]) +    params = Map.new(params, fn {key, value} -> {to_string(key), value} end) + +    local_only = params["local"]      cfg_key =        if local_only do @@ -157,8 +164,8 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do    # GET /api/v1/timelines/tag/:tag    def hashtag(%{assigns: %{user: user}} = conn, params) do -    local_only = truthy_param?(params["local"]) - +    params = Map.new(params, fn {key, value} -> {to_string(key), value} end) +    local_only = params["local"]      activities = hashtag_fetching(params, user, local_only)      conn @@ -172,10 +179,11 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do    end    # GET /api/v1/timelines/list/:list_id -  def list(%{assigns: %{user: user}} = conn, %{"list_id" => id} = params) do +  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.new(fn {key, value} -> {to_string(key), value} end)          |> Map.put("type", "Create")          |> Map.put("blocking_user", user)          |> Map.put("user", user) @@ -198,7 +198,7 @@ defmodule Pleroma.Mixfile do        {:restarter, path: "./restarter"},        {:open_api_spex,         git: "https://git.pleroma.social/pleroma/elixir-libraries/open_api_spex.git", -       ref: "b862ebd78de0df95875cf46feb6e9607130dc2a8"} +       ref: "f296ac0924ba3cf79c7a588c4c252889df4c2edd"}      ] ++ oauth_deps()    end @@ -74,7 +74,7 @@    "nimble_parsec": {:hex, :nimble_parsec, "0.5.3", "def21c10a9ed70ce22754fdeea0810dafd53c2db3219a0cd54cf5526377af1c6", [:mix], [], "hexpm", "589b5af56f4afca65217a1f3eb3fee7e79b09c40c742fddc1c312b3ac0b3399f"},    "nodex": {:git, "https://git.pleroma.social/pleroma/nodex", "cb6730f943cfc6aad674c92161be23a8411f15d1", [ref: "cb6730f943cfc6aad674c92161be23a8411f15d1"]},    "oban": {:hex, :oban, "1.2.0", "7cca94d341be43d220571e28f69131c4afc21095b25257397f50973d3fc59b07", [:mix], [{:ecto_sql, "~> 3.1", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.14", [hex: :postgrex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ba5f8b3f7d76967b3e23cf8014f6a13e4ccb33431e4808f036709a7f822362ee"}, -  "open_api_spex": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/open_api_spex.git", "b862ebd78de0df95875cf46feb6e9607130dc2a8", [ref: "b862ebd78de0df95875cf46feb6e9607130dc2a8"]}, +  "open_api_spex": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/open_api_spex.git", "f296ac0924ba3cf79c7a588c4c252889df4c2edd", [ref: "f296ac0924ba3cf79c7a588c4c252889df4c2edd"]},    "parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm", "17ef63abde837ad30680ea7f857dd9e7ced9476cdd7b0394432af4bfc241b960"},    "pbkdf2_elixir": {:hex, :pbkdf2_elixir, "1.2.1", "9cbe354b58121075bd20eb83076900a3832324b7dd171a6895fab57b6bb2752c", [:mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}], "hexpm", "d3b40a4a4630f0b442f19eca891fcfeeee4c40871936fed2f68e1c4faa30481f"},    "phoenix": {:hex, :phoenix, "1.4.13", "67271ad69b51f3719354604f4a3f968f83aa61c19199343656c9caee057ff3b8", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.8.1 or ~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ab765a0feddb81fc62e2116c827b5f068df85159c162bee760745276ad7ddc1b"}, diff --git a/test/web/mastodon_api/controllers/timeline_controller_test.exs b/test/web/mastodon_api/controllers/timeline_controller_test.exs index 06efdc901..5e0d92f28 100644 --- a/test/web/mastodon_api/controllers/timeline_controller_test.exs +++ b/test/web/mastodon_api/controllers/timeline_controller_test.exs @@ -34,7 +34,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do          conn          |> assign(:user, user)          |> get("/api/v1/timelines/home") -        |> json_response(200) +        |> json_response_and_validate_schema(200)        assert Enum.all?(response, fn n ->                 get_in(n, ["account", "pleroma", "relationship"]) == %{} @@ -42,7 +42,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do      end      test "the home timeline", %{user: user, conn: conn} do -      uri = "/api/v1/timelines/home?with_relationships=true" +      uri = "/api/v1/timelines/home?with_relationships=1"        following = insert(:user, nickname: "followed")        third_user = insert(:user, nickname: "repeated") @@ -53,7 +53,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do        ret_conn = get(conn, uri) -      assert Enum.empty?(json_response(ret_conn, :ok)) +      assert Enum.empty?(json_response_and_validate_schema(ret_conn, :ok))        {:ok, _user} = User.follow(user, following) @@ -78,7 +78,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do                     "pleroma" => %{"relationship" => %{"following" => true}}                   }                 } -             ] = json_response(ret_conn, :ok) +             ] = json_response_and_validate_schema(ret_conn, :ok)        {:ok, _user} = User.follow(third_user, user) @@ -104,7 +104,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do                     "pleroma" => %{"relationship" => %{"following" => true}}                   }                 } -             ] = json_response(ret_conn, :ok) +             ] = json_response_and_validate_schema(ret_conn, :ok)      end      test "the home timeline when the direct messages are excluded", %{user: user, conn: conn} do @@ -117,9 +117,9 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do        {:ok, private_activity} =          CommonAPI.post(user, %{"status" => ".", "visibility" => "private"}) -      conn = get(conn, "/api/v1/timelines/home", %{"exclude_visibilities" => ["direct"]}) +      conn = get(conn, "/api/v1/timelines/home?exclude_visibilities[]=direct") -      assert status_ids = json_response(conn, :ok) |> Enum.map(& &1["id"]) +      assert status_ids = json_response_and_validate_schema(conn, :ok) |> Enum.map(& &1["id"])        assert public_activity.id in status_ids        assert unlisted_activity.id in status_ids        assert private_activity.id in status_ids @@ -136,17 +136,17 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do        _activity = insert(:note_activity, local: false) -      conn = get(conn, "/api/v1/timelines/public", %{"local" => "False"}) +      conn = get(conn, "/api/v1/timelines/public?local=False") -      assert length(json_response(conn, :ok)) == 2 +      assert length(json_response_and_validate_schema(conn, :ok)) == 2 -      conn = get(build_conn(), "/api/v1/timelines/public", %{"local" => "True"}) +      conn = get(build_conn(), "/api/v1/timelines/public?local=True") -      assert [%{"content" => "test"}] = json_response(conn, :ok) +      assert [%{"content" => "test"}] = json_response_and_validate_schema(conn, :ok) -      conn = get(build_conn(), "/api/v1/timelines/public", %{"local" => "1"}) +      conn = get(build_conn(), "/api/v1/timelines/public?local=1") -      assert [%{"content" => "test"}] = json_response(conn, :ok) +      assert [%{"content" => "test"}] = json_response_and_validate_schema(conn, :ok)      end      test "the public timeline includes only public statuses for an authenticated user" do @@ -158,7 +158,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do        {: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 +      assert length(json_response_and_validate_schema(res_conn, 200)) == 1      end    end @@ -176,15 +176,15 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do      setup do: clear_config([:restrict_unauthenticated, :timelines, :federated], true)      test "if user is unauthenticated", %{conn: conn} do -      res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "true"}) +      res_conn = get(conn, "/api/v1/timelines/public?local=true") -      assert json_response(res_conn, :unauthorized) == %{ +      assert json_response_and_validate_schema(res_conn, :unauthorized) == %{                 "error" => "authorization required for timeline view"               } -      res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "false"}) +      res_conn = get(conn, "/api/v1/timelines/public?local=false") -      assert json_response(res_conn, :unauthorized) == %{ +      assert json_response_and_validate_schema(res_conn, :unauthorized) == %{                 "error" => "authorization required for timeline view"               }      end @@ -192,11 +192,11 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do      test "if user is authenticated" do        %{conn: conn} = oauth_access(["read:statuses"]) -      res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "true"}) -      assert length(json_response(res_conn, 200)) == 1 +      res_conn = get(conn, "/api/v1/timelines/public?local=true") +      assert length(json_response_and_validate_schema(res_conn, 200)) == 1 -      res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "false"}) -      assert length(json_response(res_conn, 200)) == 2 +      res_conn = get(conn, "/api/v1/timelines/public?local=false") +      assert length(json_response_and_validate_schema(res_conn, 200)) == 2      end    end @@ -206,24 +206,24 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do      setup do: clear_config([:restrict_unauthenticated, :timelines, :local], true)      test "if user is unauthenticated", %{conn: conn} do -      res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "true"}) +      res_conn = get(conn, "/api/v1/timelines/public?local=true") -      assert json_response(res_conn, :unauthorized) == %{ +      assert json_response_and_validate_schema(res_conn, :unauthorized) == %{                 "error" => "authorization required for timeline view"               } -      res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "false"}) -      assert length(json_response(res_conn, 200)) == 2 +      res_conn = get(conn, "/api/v1/timelines/public?local=false") +      assert length(json_response_and_validate_schema(res_conn, 200)) == 2      end      test "if user is authenticated", %{conn: _conn} do        %{conn: conn} = oauth_access(["read:statuses"]) -      res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "true"}) -      assert length(json_response(res_conn, 200)) == 1 +      res_conn = get(conn, "/api/v1/timelines/public?local=true") +      assert length(json_response_and_validate_schema(res_conn, 200)) == 1 -      res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "false"}) -      assert length(json_response(res_conn, 200)) == 2 +      res_conn = get(conn, "/api/v1/timelines/public?local=false") +      assert length(json_response_and_validate_schema(res_conn, 200)) == 2      end    end @@ -233,12 +233,12 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do      setup do: clear_config([:restrict_unauthenticated, :timelines, :federated], true)      test "if user is unauthenticated", %{conn: conn} do -      res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "true"}) -      assert length(json_response(res_conn, 200)) == 1 +      res_conn = get(conn, "/api/v1/timelines/public?local=true") +      assert length(json_response_and_validate_schema(res_conn, 200)) == 1 -      res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "false"}) +      res_conn = get(conn, "/api/v1/timelines/public?local=false") -      assert json_response(res_conn, :unauthorized) == %{ +      assert json_response_and_validate_schema(res_conn, :unauthorized) == %{                 "error" => "authorization required for timeline view"               }      end @@ -246,11 +246,11 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do      test "if user is authenticated", %{conn: _conn} do        %{conn: conn} = oauth_access(["read:statuses"]) -      res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "true"}) -      assert length(json_response(res_conn, 200)) == 1 +      res_conn = get(conn, "/api/v1/timelines/public?local=true") +      assert length(json_response_and_validate_schema(res_conn, 200)) == 1 -      res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "false"}) -      assert length(json_response(res_conn, 200)) == 2 +      res_conn = get(conn, "/api/v1/timelines/public?local=false") +      assert length(json_response_and_validate_schema(res_conn, 200)) == 2      end    end @@ -281,7 +281,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do        # Only direct should be visible here        res_conn = get(conn_user_two, "api/v1/timelines/direct") -      [status] = json_response(res_conn, :ok) +      assert [status] = json_response_and_validate_schema(res_conn, :ok)        assert %{"visibility" => "direct"} = status        assert status["url"] != direct.data["id"] @@ -293,14 +293,14 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do          |> assign(:token, insert(:oauth_token, user: user_one, scopes: ["read:statuses"]))          |> get("api/v1/timelines/direct") -      [status] = json_response(res_conn, :ok) +      [status] = json_response_and_validate_schema(res_conn, :ok)        assert %{"visibility" => "direct"} = status        # Both should be visible here        res_conn = get(conn_user_two, "api/v1/timelines/home") -      [_s1, _s2] = json_response(res_conn, :ok) +      [_s1, _s2] = json_response_and_validate_schema(res_conn, :ok)        # Test pagination        Enum.each(1..20, fn _ -> @@ -313,13 +313,14 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do        res_conn = get(conn_user_two, "api/v1/timelines/direct") -      statuses = json_response(res_conn, :ok) +      statuses = json_response_and_validate_schema(res_conn, :ok)        assert length(statuses) == 20 -      res_conn = -        get(conn_user_two, "api/v1/timelines/direct", %{max_id: List.last(statuses)["id"]}) +      max_id = List.last(statuses)["id"] + +      res_conn = get(conn_user_two, "api/v1/timelines/direct?max_id=#{max_id}") -      [status] = json_response(res_conn, :ok) +      assert [status] = json_response_and_validate_schema(res_conn, :ok)        assert status["url"] != direct.data["id"]      end @@ -344,7 +345,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do        res_conn = get(conn, "api/v1/timelines/direct") -      [status] = json_response(res_conn, :ok) +      [status] = json_response_and_validate_schema(res_conn, :ok)        assert status["id"] == direct.id      end    end @@ -361,7 +362,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do        conn = get(conn, "/api/v1/timelines/list/#{list.id}") -      assert [%{"id" => id}] = json_response(conn, :ok) +      assert [%{"id" => id}] = json_response_and_validate_schema(conn, :ok)        assert id == to_string(activity_two.id)      end @@ -384,7 +385,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do        conn = get(conn, "/api/v1/timelines/list/#{list.id}") -      assert [%{"id" => id}] = json_response(conn, :ok) +      assert [%{"id" => id}] = json_response_and_validate_schema(conn, :ok)        assert id == to_string(activity_one.id)      end @@ -401,14 +402,14 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do        nconn = get(conn, "/api/v1/timelines/tag/2hu") -      assert [%{"id" => id}] = json_response(nconn, :ok) +      assert [%{"id" => id}] = json_response_and_validate_schema(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" => id}] = json_response_and_validate_schema(nconn, :ok)        assert id == to_string(activity.id)      end @@ -420,22 +421,21 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do        {: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"]}) +      any_test = get(conn, "/api/v1/timelines/tag/test?any[]=test1") -      [status_none, status_test1, status_test] = json_response(any_test, :ok) +      [status_none, status_test1, status_test] = json_response_and_validate_schema(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"]}) +      restricted_test = get(conn, "/api/v1/timelines/tag/test?all[]=test1&none[]=none") -      assert [status_test1] == json_response(restricted_test, :ok) +      assert [status_test1] == json_response_and_validate_schema(restricted_test, :ok) -      all_test = get(conn, "/api/v1/timelines/tag/test", %{"all" => ["none"]}) +      all_test = get(conn, "/api/v1/timelines/tag/test?all[]=none") -      assert [status_none] == json_response(all_test, :ok) +      assert [status_none] == json_response_and_validate_schema(all_test, :ok)      end    end  end  | 
