logo

pleroma

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

search_controller_test.exs (15888B)


  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. @tag capture_log: true
  104. test "constructs hashtags from search query", %{conn: conn} do
  105. results =
  106. conn
  107. |> get("/api/v2/search?#{URI.encode_query(%{q: "some text with #explicit #hashtags"})}")
  108. |> json_response_and_validate_schema(200)
  109. assert results["hashtags"] == [
  110. %{"name" => "explicit", "url" => "#{Endpoint.url()}/tag/explicit"},
  111. %{"name" => "hashtags", "url" => "#{Endpoint.url()}/tag/hashtags"}
  112. ]
  113. results =
  114. conn
  115. |> get("/api/v2/search?#{URI.encode_query(%{q: "john doe JOHN DOE"})}")
  116. |> json_response_and_validate_schema(200)
  117. assert results["hashtags"] == [
  118. %{"name" => "john", "url" => "#{Endpoint.url()}/tag/john"},
  119. %{"name" => "doe", "url" => "#{Endpoint.url()}/tag/doe"},
  120. %{"name" => "JohnDoe", "url" => "#{Endpoint.url()}/tag/JohnDoe"}
  121. ]
  122. results =
  123. conn
  124. |> get("/api/v2/search?#{URI.encode_query(%{q: "accident-prone"})}")
  125. |> json_response_and_validate_schema(200)
  126. assert results["hashtags"] == [
  127. %{"name" => "accident", "url" => "#{Endpoint.url()}/tag/accident"},
  128. %{"name" => "prone", "url" => "#{Endpoint.url()}/tag/prone"},
  129. %{"name" => "AccidentProne", "url" => "#{Endpoint.url()}/tag/AccidentProne"}
  130. ]
  131. results =
  132. conn
  133. |> get("/api/v2/search?#{URI.encode_query(%{q: "https://shpposter.club/users/shpuld"})}")
  134. |> json_response_and_validate_schema(200)
  135. assert results["hashtags"] == [
  136. %{"name" => "shpuld", "url" => "#{Endpoint.url()}/tag/shpuld"}
  137. ]
  138. results =
  139. conn
  140. |> get(
  141. "/api/v2/search?#{URI.encode_query(%{q: "https://www.washingtonpost.com/sports/2020/06/10/" <> "nascar-ban-display-confederate-flag-all-events-properties/"})}"
  142. )
  143. |> json_response_and_validate_schema(200)
  144. assert results["hashtags"] == [
  145. %{"name" => "nascar", "url" => "#{Endpoint.url()}/tag/nascar"},
  146. %{"name" => "ban", "url" => "#{Endpoint.url()}/tag/ban"},
  147. %{"name" => "display", "url" => "#{Endpoint.url()}/tag/display"},
  148. %{"name" => "confederate", "url" => "#{Endpoint.url()}/tag/confederate"},
  149. %{"name" => "flag", "url" => "#{Endpoint.url()}/tag/flag"},
  150. %{"name" => "all", "url" => "#{Endpoint.url()}/tag/all"},
  151. %{"name" => "events", "url" => "#{Endpoint.url()}/tag/events"},
  152. %{"name" => "properties", "url" => "#{Endpoint.url()}/tag/properties"},
  153. %{
  154. "name" => "NascarBanDisplayConfederateFlagAllEventsProperties",
  155. "url" =>
  156. "#{Endpoint.url()}/tag/NascarBanDisplayConfederateFlagAllEventsProperties"
  157. }
  158. ]
  159. end
  160. test "supports pagination of hashtags search results", %{conn: conn} do
  161. results =
  162. conn
  163. |> get(
  164. "/api/v2/search?#{URI.encode_query(%{q: "#some #text #with #hashtags", limit: 2, offset: 1})}"
  165. )
  166. |> json_response_and_validate_schema(200)
  167. assert results["hashtags"] == [
  168. %{"name" => "text", "url" => "#{Endpoint.url()}/tag/text"},
  169. %{"name" => "with", "url" => "#{Endpoint.url()}/tag/with"}
  170. ]
  171. end
  172. test "excludes a blocked users from search results", %{conn: conn} do
  173. user = insert(:user)
  174. user_smith = insert(:user, %{nickname: "Agent", name: "I love 2hu"})
  175. user_neo = insert(:user, %{nickname: "Agent Neo", name: "Agent"})
  176. {:ok, act1} = CommonAPI.post(user, %{status: "This is about 2hu private 天子"})
  177. {:ok, act2} = CommonAPI.post(user_smith, %{status: "Agent Smith"})
  178. {:ok, act3} = CommonAPI.post(user_neo, %{status: "Agent Smith"})
  179. Pleroma.User.block(user, user_smith)
  180. results =
  181. conn
  182. |> assign(:user, user)
  183. |> assign(:token, insert(:oauth_token, user: user, scopes: ["read"]))
  184. |> get("/api/v2/search?q=Agent")
  185. |> json_response_and_validate_schema(200)
  186. status_ids = Enum.map(results["statuses"], fn g -> g["id"] end)
  187. assert act3.id in status_ids
  188. refute act2.id in status_ids
  189. refute act1.id in status_ids
  190. end
  191. end
  192. describe ".account_search" do
  193. test "account search", %{conn: conn} do
  194. user_two = insert(:user, %{nickname: "shp@shitposter.club"})
  195. user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
  196. results =
  197. conn
  198. |> get("/api/v1/accounts/search?q=shp")
  199. |> json_response_and_validate_schema(200)
  200. result_ids = for result <- results, do: result["acct"]
  201. assert user_two.nickname in result_ids
  202. assert user_three.nickname in result_ids
  203. results =
  204. conn
  205. |> get("/api/v1/accounts/search?q=2hu")
  206. |> json_response_and_validate_schema(200)
  207. result_ids = for result <- results, do: result["acct"]
  208. assert user_three.nickname in result_ids
  209. end
  210. test "returns account if query contains a space", %{conn: conn} do
  211. insert(:user, %{nickname: "shp@shitposter.club"})
  212. results =
  213. conn
  214. |> get("/api/v1/accounts/search?q=shp@shitposter.club xxx")
  215. |> json_response_and_validate_schema(200)
  216. assert length(results) == 1
  217. end
  218. end
  219. describe ".search" do
  220. test "it returns empty result if user or status search return undefined error", %{conn: conn} do
  221. with_mocks [
  222. {Pleroma.User, [], [search: fn _q, _o -> raise "Oops" end]},
  223. {Pleroma.Activity, [], [search: fn _u, _q, _o -> raise "Oops" end]}
  224. ] do
  225. capture_log(fn ->
  226. results =
  227. conn
  228. |> get("/api/v1/search?q=2hu")
  229. |> json_response_and_validate_schema(200)
  230. assert results["accounts"] == []
  231. assert results["statuses"] == []
  232. end) =~
  233. "[error] Elixir.Pleroma.Web.MastodonAPI.SearchController search error: %RuntimeError{message: \"Oops\"}"
  234. end
  235. end
  236. test "search", %{conn: conn} do
  237. user = insert(:user)
  238. user_two = insert(:user, %{nickname: "shp@shitposter.club"})
  239. user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
  240. {:ok, activity} = CommonAPI.post(user, %{status: "This is about 2hu"})
  241. {:ok, _activity} =
  242. CommonAPI.post(user, %{
  243. status: "This is about 2hu, but private",
  244. visibility: "private"
  245. })
  246. {:ok, _} = CommonAPI.post(user_two, %{status: "This isn't"})
  247. results =
  248. conn
  249. |> get("/api/v1/search?q=2hu")
  250. |> json_response_and_validate_schema(200)
  251. [account | _] = results["accounts"]
  252. assert account["id"] == to_string(user_three.id)
  253. assert results["hashtags"] == ["2hu"]
  254. [status] = results["statuses"]
  255. assert status["id"] == to_string(activity.id)
  256. end
  257. test "search fetches remote statuses and prefers them over other results", %{conn: conn} do
  258. old_version = :persistent_term.get({Pleroma.Repo, :postgres_version})
  259. :persistent_term.put({Pleroma.Repo, :postgres_version}, 10.0)
  260. on_exit(fn -> :persistent_term.put({Pleroma.Repo, :postgres_version}, old_version) end)
  261. capture_log(fn ->
  262. {:ok, %{id: activity_id}} =
  263. CommonAPI.post(insert(:user), %{
  264. status: "check out http://mastodon.example.org/@admin/99541947525187367"
  265. })
  266. results =
  267. conn
  268. |> get("/api/v1/search?q=http://mastodon.example.org/@admin/99541947525187367")
  269. |> json_response_and_validate_schema(200)
  270. assert [
  271. %{"url" => "http://mastodon.example.org/@admin/99541947525187367"},
  272. %{"id" => ^activity_id}
  273. ] = results["statuses"]
  274. end)
  275. end
  276. test "search doesn't show statuses that it shouldn't", %{conn: conn} do
  277. {:ok, activity} =
  278. CommonAPI.post(insert(:user), %{
  279. status: "This is about 2hu, but private",
  280. visibility: "private"
  281. })
  282. capture_log(fn ->
  283. q = Object.normalize(activity, fetch: false).data["id"]
  284. results =
  285. conn
  286. |> get("/api/v1/search?q=#{q}")
  287. |> json_response_and_validate_schema(200)
  288. [] = results["statuses"]
  289. end)
  290. end
  291. test "search fetches remote accounts", %{conn: conn} do
  292. user = insert(:user)
  293. query = URI.encode_query(%{q: " mike@osada.macgirvin.com ", resolve: true})
  294. results =
  295. conn
  296. |> assign(:user, user)
  297. |> assign(:token, insert(:oauth_token, user: user, scopes: ["read"]))
  298. |> get("/api/v1/search?#{query}")
  299. |> json_response_and_validate_schema(200)
  300. [account] = results["accounts"]
  301. assert account["acct"] == "mike@osada.macgirvin.com"
  302. end
  303. test "search doesn't fetch remote accounts if resolve is false", %{conn: conn} do
  304. results =
  305. conn
  306. |> get("/api/v1/search?q=mike@osada.macgirvin.com&resolve=false")
  307. |> json_response_and_validate_schema(200)
  308. assert [] == results["accounts"]
  309. end
  310. test "search with limit and offset", %{conn: conn} do
  311. user = insert(:user)
  312. _user_two = insert(:user, %{nickname: "shp@shitposter.club"})
  313. _user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
  314. {:ok, _activity1} = CommonAPI.post(user, %{status: "This is about 2hu"})
  315. {:ok, _activity2} = CommonAPI.post(user, %{status: "This is also about 2hu"})
  316. result =
  317. conn
  318. |> get("/api/v1/search?q=2hu&limit=1")
  319. assert results = json_response_and_validate_schema(result, 200)
  320. assert [%{"id" => activity_id1}] = results["statuses"]
  321. assert [_] = results["accounts"]
  322. results =
  323. conn
  324. |> get("/api/v1/search?q=2hu&limit=1&offset=1")
  325. |> json_response_and_validate_schema(200)
  326. assert [%{"id" => activity_id2}] = results["statuses"]
  327. assert [] = results["accounts"]
  328. assert activity_id1 != activity_id2
  329. end
  330. test "search returns results only for the given type", %{conn: conn} do
  331. user = insert(:user)
  332. _user_two = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
  333. {:ok, _activity} = CommonAPI.post(user, %{status: "This is about 2hu"})
  334. assert %{"statuses" => [_activity], "accounts" => [], "hashtags" => []} =
  335. conn
  336. |> get("/api/v1/search?q=2hu&type=statuses")
  337. |> json_response_and_validate_schema(200)
  338. assert %{"statuses" => [], "accounts" => [_user_two], "hashtags" => []} =
  339. conn
  340. |> get("/api/v1/search?q=2hu&type=accounts")
  341. |> json_response_and_validate_schema(200)
  342. end
  343. test "search uses account_id to filter statuses by the author", %{conn: conn} do
  344. user = insert(:user, %{nickname: "shp@shitposter.club"})
  345. user_two = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
  346. {:ok, activity1} = CommonAPI.post(user, %{status: "This is about 2hu"})
  347. {:ok, activity2} = CommonAPI.post(user_two, %{status: "This is also about 2hu"})
  348. results =
  349. conn
  350. |> get("/api/v1/search?q=2hu&account_id=#{user.id}")
  351. |> json_response_and_validate_schema(200)
  352. assert [%{"id" => activity_id1}] = results["statuses"]
  353. assert activity_id1 == activity1.id
  354. assert [_] = results["accounts"]
  355. results =
  356. conn
  357. |> get("/api/v1/search?q=2hu&account_id=#{user_two.id}")
  358. |> json_response_and_validate_schema(200)
  359. assert [%{"id" => activity_id2}] = results["statuses"]
  360. assert activity_id2 == activity2.id
  361. end
  362. end
  363. end