logo

pleroma

My custom branche(s) on git.pleroma.social/pleroma/pleroma git clone https://anongit.hacktivis.me/git/pleroma.git/

search_controller_test.exs (15612B)


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