logo

pleroma

My custom branche(s) on git.pleroma.social/pleroma/pleroma

search_controller_test.exs (14254B)


  1. # Pleroma: A lightweight social networking server
  2. # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
  3. # SPDX-License-Identifier: AGPL-3.0-only
  4. defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do
  5. use Pleroma.Web.ConnCase
  6. alias Pleroma.Object
  7. alias Pleroma.Web
  8. alias Pleroma.Web.CommonAPI
  9. import Pleroma.Factory
  10. import ExUnit.CaptureLog
  11. import Tesla.Mock
  12. import Mock
  13. setup_all do
  14. mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
  15. :ok
  16. end
  17. describe ".search2" do
  18. test "it returns empty result if user or status search return undefined error", %{conn: conn} do
  19. with_mocks [
  20. {Pleroma.User, [], [search: fn _q, _o -> raise "Oops" end]},
  21. {Pleroma.Activity, [], [search: fn _u, _q, _o -> raise "Oops" end]}
  22. ] do
  23. capture_log(fn ->
  24. results =
  25. conn
  26. |> get("/api/v2/search?q=2hu")
  27. |> json_response_and_validate_schema(200)
  28. assert results["accounts"] == []
  29. assert results["statuses"] == []
  30. end) =~
  31. "[error] Elixir.Pleroma.Web.MastodonAPI.SearchController search error: %RuntimeError{message: \"Oops\"}"
  32. end
  33. end
  34. test "search", %{conn: conn} do
  35. user = insert(:user)
  36. user_two = insert(:user, %{nickname: "shp@shitposter.club"})
  37. user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
  38. {:ok, activity} = CommonAPI.post(user, %{status: "This is about 2hu private 天子"})
  39. {:ok, _activity} =
  40. CommonAPI.post(user, %{
  41. status: "This is about 2hu, but private",
  42. visibility: "private"
  43. })
  44. {:ok, _} = CommonAPI.post(user_two, %{status: "This isn't"})
  45. results =
  46. conn
  47. |> get("/api/v2/search?#{URI.encode_query(%{q: "2hu #private"})}")
  48. |> json_response_and_validate_schema(200)
  49. [account | _] = results["accounts"]
  50. assert account["id"] == to_string(user_three.id)
  51. assert results["hashtags"] == [
  52. %{"name" => "private", "url" => "#{Web.base_url()}/tag/private"}
  53. ]
  54. [status] = results["statuses"]
  55. assert status["id"] == to_string(activity.id)
  56. results =
  57. get(conn, "/api/v2/search?q=天子")
  58. |> json_response_and_validate_schema(200)
  59. assert results["hashtags"] == [
  60. %{"name" => "天子", "url" => "#{Web.base_url()}/tag/天子"}
  61. ]
  62. [status] = results["statuses"]
  63. assert status["id"] == to_string(activity.id)
  64. end
  65. @tag capture_log: true
  66. test "constructs hashtags from search query", %{conn: conn} do
  67. results =
  68. conn
  69. |> get("/api/v2/search?#{URI.encode_query(%{q: "some text with #explicit #hashtags"})}")
  70. |> json_response_and_validate_schema(200)
  71. assert results["hashtags"] == [
  72. %{"name" => "explicit", "url" => "#{Web.base_url()}/tag/explicit"},
  73. %{"name" => "hashtags", "url" => "#{Web.base_url()}/tag/hashtags"}
  74. ]
  75. results =
  76. conn
  77. |> get("/api/v2/search?#{URI.encode_query(%{q: "john doe JOHN DOE"})}")
  78. |> json_response_and_validate_schema(200)
  79. assert results["hashtags"] == [
  80. %{"name" => "john", "url" => "#{Web.base_url()}/tag/john"},
  81. %{"name" => "doe", "url" => "#{Web.base_url()}/tag/doe"},
  82. %{"name" => "JohnDoe", "url" => "#{Web.base_url()}/tag/JohnDoe"}
  83. ]
  84. results =
  85. conn
  86. |> get("/api/v2/search?#{URI.encode_query(%{q: "accident-prone"})}")
  87. |> json_response_and_validate_schema(200)
  88. assert results["hashtags"] == [
  89. %{"name" => "accident", "url" => "#{Web.base_url()}/tag/accident"},
  90. %{"name" => "prone", "url" => "#{Web.base_url()}/tag/prone"},
  91. %{"name" => "AccidentProne", "url" => "#{Web.base_url()}/tag/AccidentProne"}
  92. ]
  93. results =
  94. conn
  95. |> get("/api/v2/search?#{URI.encode_query(%{q: "https://shpposter.club/users/shpuld"})}")
  96. |> json_response_and_validate_schema(200)
  97. assert results["hashtags"] == [
  98. %{"name" => "shpuld", "url" => "#{Web.base_url()}/tag/shpuld"}
  99. ]
  100. results =
  101. conn
  102. |> get(
  103. "/api/v2/search?#{
  104. URI.encode_query(%{
  105. q:
  106. "https://www.washingtonpost.com/sports/2020/06/10/" <>
  107. "nascar-ban-display-confederate-flag-all-events-properties/"
  108. })
  109. }"
  110. )
  111. |> json_response_and_validate_schema(200)
  112. assert results["hashtags"] == [
  113. %{"name" => "nascar", "url" => "#{Web.base_url()}/tag/nascar"},
  114. %{"name" => "ban", "url" => "#{Web.base_url()}/tag/ban"},
  115. %{"name" => "display", "url" => "#{Web.base_url()}/tag/display"},
  116. %{"name" => "confederate", "url" => "#{Web.base_url()}/tag/confederate"},
  117. %{"name" => "flag", "url" => "#{Web.base_url()}/tag/flag"},
  118. %{"name" => "all", "url" => "#{Web.base_url()}/tag/all"},
  119. %{"name" => "events", "url" => "#{Web.base_url()}/tag/events"},
  120. %{"name" => "properties", "url" => "#{Web.base_url()}/tag/properties"},
  121. %{
  122. "name" => "NascarBanDisplayConfederateFlagAllEventsProperties",
  123. "url" =>
  124. "#{Web.base_url()}/tag/NascarBanDisplayConfederateFlagAllEventsProperties"
  125. }
  126. ]
  127. end
  128. test "supports pagination of hashtags search results", %{conn: conn} do
  129. results =
  130. conn
  131. |> get(
  132. "/api/v2/search?#{
  133. URI.encode_query(%{q: "#some #text #with #hashtags", limit: 2, offset: 1})
  134. }"
  135. )
  136. |> json_response_and_validate_schema(200)
  137. assert results["hashtags"] == [
  138. %{"name" => "text", "url" => "#{Web.base_url()}/tag/text"},
  139. %{"name" => "with", "url" => "#{Web.base_url()}/tag/with"}
  140. ]
  141. end
  142. test "excludes a blocked users from search results", %{conn: conn} do
  143. user = insert(:user)
  144. user_smith = insert(:user, %{nickname: "Agent", name: "I love 2hu"})
  145. user_neo = insert(:user, %{nickname: "Agent Neo", name: "Agent"})
  146. {:ok, act1} = CommonAPI.post(user, %{status: "This is about 2hu private 天子"})
  147. {:ok, act2} = CommonAPI.post(user_smith, %{status: "Agent Smith"})
  148. {:ok, act3} = CommonAPI.post(user_neo, %{status: "Agent Smith"})
  149. Pleroma.User.block(user, user_smith)
  150. results =
  151. conn
  152. |> assign(:user, user)
  153. |> assign(:token, insert(:oauth_token, user: user, scopes: ["read"]))
  154. |> get("/api/v2/search?q=Agent")
  155. |> json_response_and_validate_schema(200)
  156. status_ids = Enum.map(results["statuses"], fn g -> g["id"] end)
  157. assert act3.id in status_ids
  158. refute act2.id in status_ids
  159. refute act1.id in status_ids
  160. end
  161. end
  162. describe ".account_search" do
  163. test "account search", %{conn: conn} do
  164. user_two = insert(:user, %{nickname: "shp@shitposter.club"})
  165. user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
  166. results =
  167. conn
  168. |> get("/api/v1/accounts/search?q=shp")
  169. |> json_response_and_validate_schema(200)
  170. result_ids = for result <- results, do: result["acct"]
  171. assert user_two.nickname in result_ids
  172. assert user_three.nickname in result_ids
  173. results =
  174. conn
  175. |> get("/api/v1/accounts/search?q=2hu")
  176. |> json_response_and_validate_schema(200)
  177. result_ids = for result <- results, do: result["acct"]
  178. assert user_three.nickname in result_ids
  179. end
  180. test "returns account if query contains a space", %{conn: conn} do
  181. insert(:user, %{nickname: "shp@shitposter.club"})
  182. results =
  183. conn
  184. |> get("/api/v1/accounts/search?q=shp@shitposter.club xxx")
  185. |> json_response_and_validate_schema(200)
  186. assert length(results) == 1
  187. end
  188. end
  189. describe ".search" do
  190. test "it returns empty result if user or status search return undefined error", %{conn: conn} do
  191. with_mocks [
  192. {Pleroma.User, [], [search: fn _q, _o -> raise "Oops" end]},
  193. {Pleroma.Activity, [], [search: fn _u, _q, _o -> raise "Oops" end]}
  194. ] do
  195. capture_log(fn ->
  196. results =
  197. conn
  198. |> get("/api/v1/search?q=2hu")
  199. |> json_response_and_validate_schema(200)
  200. assert results["accounts"] == []
  201. assert results["statuses"] == []
  202. end) =~
  203. "[error] Elixir.Pleroma.Web.MastodonAPI.SearchController search error: %RuntimeError{message: \"Oops\"}"
  204. end
  205. end
  206. test "search", %{conn: conn} do
  207. user = insert(:user)
  208. user_two = insert(:user, %{nickname: "shp@shitposter.club"})
  209. user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
  210. {:ok, activity} = CommonAPI.post(user, %{status: "This is about 2hu"})
  211. {:ok, _activity} =
  212. CommonAPI.post(user, %{
  213. status: "This is about 2hu, but private",
  214. visibility: "private"
  215. })
  216. {:ok, _} = CommonAPI.post(user_two, %{status: "This isn't"})
  217. results =
  218. conn
  219. |> get("/api/v1/search?q=2hu")
  220. |> json_response_and_validate_schema(200)
  221. [account | _] = results["accounts"]
  222. assert account["id"] == to_string(user_three.id)
  223. assert results["hashtags"] == ["2hu"]
  224. [status] = results["statuses"]
  225. assert status["id"] == to_string(activity.id)
  226. end
  227. test "search fetches remote statuses and prefers them over other results", %{conn: conn} do
  228. capture_log(fn ->
  229. {:ok, %{id: activity_id}} =
  230. CommonAPI.post(insert(:user), %{
  231. status: "check out http://mastodon.example.org/@admin/99541947525187367"
  232. })
  233. results =
  234. conn
  235. |> get("/api/v1/search?q=http://mastodon.example.org/@admin/99541947525187367")
  236. |> json_response_and_validate_schema(200)
  237. assert [
  238. %{"url" => "http://mastodon.example.org/@admin/99541947525187367"},
  239. %{"id" => ^activity_id}
  240. ] = results["statuses"]
  241. end)
  242. end
  243. test "search doesn't show statuses that it shouldn't", %{conn: conn} do
  244. {:ok, activity} =
  245. CommonAPI.post(insert(:user), %{
  246. status: "This is about 2hu, but private",
  247. visibility: "private"
  248. })
  249. capture_log(fn ->
  250. q = Object.normalize(activity).data["id"]
  251. results =
  252. conn
  253. |> get("/api/v1/search?q=#{q}")
  254. |> json_response_and_validate_schema(200)
  255. [] = results["statuses"]
  256. end)
  257. end
  258. test "search fetches remote accounts", %{conn: conn} do
  259. user = insert(:user)
  260. query = URI.encode_query(%{q: " mike@osada.macgirvin.com ", resolve: true})
  261. results =
  262. conn
  263. |> assign(:user, user)
  264. |> assign(:token, insert(:oauth_token, user: user, scopes: ["read"]))
  265. |> get("/api/v1/search?#{query}")
  266. |> json_response_and_validate_schema(200)
  267. [account] = results["accounts"]
  268. assert account["acct"] == "mike@osada.macgirvin.com"
  269. end
  270. test "search doesn't fetch remote accounts if resolve is false", %{conn: conn} do
  271. results =
  272. conn
  273. |> get("/api/v1/search?q=mike@osada.macgirvin.com&resolve=false")
  274. |> json_response_and_validate_schema(200)
  275. assert [] == results["accounts"]
  276. end
  277. test "search with limit and offset", %{conn: conn} do
  278. user = insert(:user)
  279. _user_two = insert(:user, %{nickname: "shp@shitposter.club"})
  280. _user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
  281. {:ok, _activity1} = CommonAPI.post(user, %{status: "This is about 2hu"})
  282. {:ok, _activity2} = CommonAPI.post(user, %{status: "This is also about 2hu"})
  283. result =
  284. conn
  285. |> get("/api/v1/search?q=2hu&limit=1")
  286. assert results = json_response_and_validate_schema(result, 200)
  287. assert [%{"id" => activity_id1}] = results["statuses"]
  288. assert [_] = results["accounts"]
  289. results =
  290. conn
  291. |> get("/api/v1/search?q=2hu&limit=1&offset=1")
  292. |> json_response_and_validate_schema(200)
  293. assert [%{"id" => activity_id2}] = results["statuses"]
  294. assert [] = results["accounts"]
  295. assert activity_id1 != activity_id2
  296. end
  297. test "search returns results only for the given type", %{conn: conn} do
  298. user = insert(:user)
  299. _user_two = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
  300. {:ok, _activity} = CommonAPI.post(user, %{status: "This is about 2hu"})
  301. assert %{"statuses" => [_activity], "accounts" => [], "hashtags" => []} =
  302. conn
  303. |> get("/api/v1/search?q=2hu&type=statuses")
  304. |> json_response_and_validate_schema(200)
  305. assert %{"statuses" => [], "accounts" => [_user_two], "hashtags" => []} =
  306. conn
  307. |> get("/api/v1/search?q=2hu&type=accounts")
  308. |> json_response_and_validate_schema(200)
  309. end
  310. test "search uses account_id to filter statuses by the author", %{conn: conn} do
  311. user = insert(:user, %{nickname: "shp@shitposter.club"})
  312. user_two = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
  313. {:ok, activity1} = CommonAPI.post(user, %{status: "This is about 2hu"})
  314. {:ok, activity2} = CommonAPI.post(user_two, %{status: "This is also about 2hu"})
  315. results =
  316. conn
  317. |> get("/api/v1/search?q=2hu&account_id=#{user.id}")
  318. |> json_response_and_validate_schema(200)
  319. assert [%{"id" => activity_id1}] = results["statuses"]
  320. assert activity_id1 == activity1.id
  321. assert [_] = results["accounts"]
  322. results =
  323. conn
  324. |> get("/api/v1/search?q=2hu&account_id=#{user_two.id}")
  325. |> json_response_and_validate_schema(200)
  326. assert [%{"id" => activity_id2}] = results["statuses"]
  327. assert activity_id2 == activity2.id
  328. end
  329. end
  330. end