diff --git a/lib/pleroma/web/mastodon_api/search_controller.ex b/lib/pleroma/web/mastodon_api/search_controller.ex index efa9cc788..939f7f6cb 100644 --- a/lib/pleroma/web/mastodon_api/search_controller.ex +++ b/lib/pleroma/web/mastodon_api/search_controller.ex @@ -4,29 +4,27 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do use Pleroma.Web, :controller + alias Pleroma.Activity + alias Pleroma.Plugs.RateLimiter alias Pleroma.User alias Pleroma.Web + alias Pleroma.Web.ControllerHelper alias Pleroma.Web.MastodonAPI.AccountView alias Pleroma.Web.MastodonAPI.StatusView - alias Pleroma.Web.ControllerHelper - require Logger - - plug(Pleroma.Plugs.RateLimiter, :search when action in [:search, :search2, :account_search]) + plug(RateLimiter, :search when action in [:search, :search2, :account_search]) def search2(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do accounts = with_fallback(fn -> User.search(query, search_options(params, user)) end, []) statuses = with_fallback(fn -> Activity.search(user, query) end, []) + tags_path = Web.base_url() <> "/tag/" tags = query - |> String.split() - |> Enum.uniq() - |> Enum.filter(fn tag -> String.starts_with?(tag, "#") end) - |> Enum.map(fn tag -> String.slice(tag, 1..-1) end) + |> prepare_tags |> Enum.map(fn tag -> %{name: tag, url: tags_path <> tag} end) res = %{ @@ -40,15 +38,10 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do end def search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do - accounts = with_fallback(fn -> User.search(query, search_options(params, user)) end, []) - statuses = with_fallback(fn -> Activity.search(user, query) end, []) + accounts = with_fallback(fn -> User.search(query, search_options(params, user)) end) + statuses = with_fallback(fn -> Activity.search(user, query) end) - tags = - query - |> String.split() - |> Enum.uniq() - |> Enum.filter(fn tag -> String.starts_with?(tag, "#") end) - |> Enum.map(fn tag -> String.slice(tag, 1..-1) end) + tags = prepare_tags(query) res = %{ "accounts" => AccountView.render("accounts.json", users: accounts, for: user, as: :user), @@ -67,6 +60,14 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do json(conn, res) end + defp prepare_tags(query) do + query + |> String.split() + |> Enum.uniq() + |> Enum.filter(fn tag -> String.starts_with?(tag, "#") end) + |> Enum.map(fn tag -> String.slice(tag, 1..-1) end) + end + defp search_options(params, user) do [ resolve: params["resolve"] == "true", @@ -77,7 +78,7 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do ] end - defp with_fallback(f, fallback) do + defp with_fallback(f, fallback \\ []) do try do f.() rescue diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index ec582b919..a070bc942 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -19,6 +19,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do import Pleroma.Web.ActivityPub.Visibility, only: [get_visibility: 1] # TODO: Add cached version. + defp get_replied_to_activities([]), do: %{} + defp get_replied_to_activities(activities) do activities |> Enum.map(fn diff --git a/test/web/mastodon_api/search_controller_test.exs b/test/web/mastodon_api/search_controller_test.exs index c3f531590..ea534b393 100644 --- a/test/web/mastodon_api/search_controller_test.exs +++ b/test/web/mastodon_api/search_controller_test.exs @@ -6,123 +6,194 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do use Pleroma.Web.ConnCase alias Pleroma.Object + alias Pleroma.Web alias Pleroma.Web.CommonAPI import Pleroma.Factory import ExUnit.CaptureLog import Tesla.Mock + import Mock setup do - mock(fn env -> apply(HttpRequestMock, :request, [env]) end) + mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) :ok end - test "account search", %{conn: conn} do - user = insert(:user) - user_two = insert(:user, %{nickname: "shp@shitposter.club"}) - user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"}) + describe ".search2" do + test "it returns empty result if user or status search return undefined error", %{conn: conn} do + with_mocks [ + {Pleroma.User, [], [search: fn _q, _o -> raise "Oops" end]}, + {Pleroma.Activity, [], [search: fn _u, _q -> raise "Oops" end]} + ] do + conn = get(conn, "/api/v2/search", %{"q" => "2hu"}) - results = - conn - |> assign(:user, user) - |> get("/api/v1/accounts/search", %{"q" => "shp"}) - |> json_response(200) + assert results = json_response(conn, 200) - result_ids = for result <- results, do: result["acct"] + assert results["accounts"] == [] + assert results["statuses"] == [] + end + end - assert user_two.nickname in result_ids - assert user_three.nickname in result_ids + test "search", %{conn: conn} do + user = insert(:user) + user_two = insert(:user, %{nickname: "shp@shitposter.club"}) + user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"}) - results = - conn - |> assign(:user, user) - |> get("/api/v1/accounts/search", %{"q" => "2hu"}) - |> json_response(200) + {:ok, activity} = CommonAPI.post(user, %{"status" => "This is about 2hu private"}) - result_ids = for result <- results, do: result["acct"] + {:ok, _activity} = + CommonAPI.post(user, %{ + "status" => "This is about 2hu, but private", + "visibility" => "private" + }) - assert user_three.nickname in result_ids - end + {:ok, _} = CommonAPI.post(user_two, %{"status" => "This isn't"}) - test "search", %{conn: conn} do - user = insert(:user) - user_two = insert(:user, %{nickname: "shp@shitposter.club"}) - user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"}) - - {:ok, activity} = CommonAPI.post(user, %{"status" => "This is about 2hu"}) - - {:ok, _activity} = - CommonAPI.post(user, %{ - "status" => "This is about 2hu, but private", - "visibility" => "private" - }) - - {:ok, _} = CommonAPI.post(user_two, %{"status" => "This isn't"}) - - conn = - conn - |> get("/api/v1/search", %{"q" => "2hu"}) - - assert results = json_response(conn, 200) - - [account | _] = results["accounts"] - assert account["id"] == to_string(user_three.id) - - assert results["hashtags"] == [] - - [status] = results["statuses"] - assert status["id"] == to_string(activity.id) - end - - test "search fetches remote statuses", %{conn: conn} do - capture_log(fn -> - conn = - conn - |> get("/api/v1/search", %{"q" => "https://shitposter.club/notice/2827873"}) + conn = get(conn, "/api/v2/search", %{"q" => "2hu #private"}) assert results = json_response(conn, 200) + # IO.inspect results + + [account | _] = results["accounts"] + assert account["id"] == to_string(user_three.id) + + assert results["hashtags"] == [ + %{"name" => "private", "url" => "#{Web.base_url()}/tag/private"} + ] [status] = results["statuses"] - assert status["uri"] == "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment" - end) + assert status["id"] == to_string(activity.id) + end end - test "search doesn't show statuses that it shouldn't", %{conn: conn} do - {:ok, activity} = - CommonAPI.post(insert(:user), %{ - "status" => "This is about 2hu, but private", - "visibility" => "private" - }) + describe ".account_search" do + test "account search", %{conn: conn} do + user = insert(:user) + user_two = insert(:user, %{nickname: "shp@shitposter.club"}) + user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"}) + + results = + conn + |> assign(:user, user) + |> get("/api/v1/accounts/search", %{"q" => "shp"}) + |> json_response(200) + + result_ids = for result <- results, do: result["acct"] + + assert user_two.nickname in result_ids + assert user_three.nickname in result_ids + + results = + conn + |> assign(:user, user) + |> get("/api/v1/accounts/search", %{"q" => "2hu"}) + |> json_response(200) + + result_ids = for result <- results, do: result["acct"] + + assert user_three.nickname in result_ids + end + end + + describe ".search" do + test "it returns empty result if user or status search return undefined error", %{conn: conn} do + with_mocks [ + {Pleroma.User, [], [search: fn _q, _o -> raise "Oops" end]}, + {Pleroma.Activity, [], [search: fn _u, _q -> raise "Oops" end]} + ] do + conn = + conn + |> get("/api/v1/search", %{"q" => "2hu"}) + + assert results = json_response(conn, 200) + + assert results["accounts"] == [] + assert results["statuses"] == [] + end + end + + test "search", %{conn: conn} do + user = insert(:user) + user_two = insert(:user, %{nickname: "shp@shitposter.club"}) + user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"}) + + {:ok, activity} = CommonAPI.post(user, %{"status" => "This is about 2hu"}) + + {:ok, _activity} = + CommonAPI.post(user, %{ + "status" => "This is about 2hu, but private", + "visibility" => "private" + }) + + {:ok, _} = CommonAPI.post(user_two, %{"status" => "This isn't"}) - capture_log(fn -> conn = conn - |> get("/api/v1/search", %{"q" => Object.normalize(activity).data["id"]}) + |> get("/api/v1/search", %{"q" => "2hu"}) assert results = json_response(conn, 200) - [] = results["statuses"] - end) - end + [account | _] = results["accounts"] + assert account["id"] == to_string(user_three.id) - test "search fetches remote accounts", %{conn: conn} do - user = insert(:user) + assert results["hashtags"] == [] - conn = - conn - |> assign(:user, user) - |> get("/api/v1/search", %{"q" => "shp@social.heldscal.la", "resolve" => "true"}) + [status] = results["statuses"] + assert status["id"] == to_string(activity.id) + end - assert results = json_response(conn, 200) - [account] = results["accounts"] - assert account["acct"] == "shp@social.heldscal.la" - end + test "search fetches remote statuses", %{conn: conn} do + capture_log(fn -> + conn = + conn + |> get("/api/v1/search", %{"q" => "https://shitposter.club/notice/2827873"}) - test "search doesn't fetch remote accounts if resolve is false", %{conn: conn} do - conn = - conn - |> get("/api/v1/search", %{"q" => "shp@social.heldscal.la", "resolve" => "false"}) + assert results = json_response(conn, 200) - assert results = json_response(conn, 200) - assert [] == results["accounts"] + [status] = results["statuses"] + + assert status["uri"] == + "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment" + end) + end + + test "search doesn't show statuses that it shouldn't", %{conn: conn} do + {:ok, activity} = + CommonAPI.post(insert(:user), %{ + "status" => "This is about 2hu, but private", + "visibility" => "private" + }) + + capture_log(fn -> + conn = + conn + |> get("/api/v1/search", %{"q" => Object.normalize(activity).data["id"]}) + + assert results = json_response(conn, 200) + + [] = results["statuses"] + end) + end + + test "search fetches remote accounts", %{conn: conn} do + user = insert(:user) + + conn = + conn + |> assign(:user, user) + |> get("/api/v1/search", %{"q" => "shp@social.heldscal.la", "resolve" => "true"}) + + assert results = json_response(conn, 200) + [account] = results["accounts"] + assert account["acct"] == "shp@social.heldscal.la" + end + + test "search doesn't fetch remote accounts if resolve is false", %{conn: conn} do + conn = + conn + |> get("/api/v1/search", %{"q" => "shp@social.heldscal.la", "resolve" => "false"}) + + assert results = json_response(conn, 200) + assert [] == results["accounts"] + end end end