Fork of Pleroma with site-specific changes and feature branches https://git.pleroma.social/pleroma/pleroma
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

287 lines
9.4KB

  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.UserSearchTest do
  5. alias Pleroma.Repo
  6. alias Pleroma.User
  7. use Pleroma.DataCase
  8. import Pleroma.Factory
  9. setup_all do
  10. Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
  11. :ok
  12. end
  13. describe "User.search" do
  14. setup do: clear_config([:instance, :limit_to_local_content])
  15. test "excluded invisible users from results" do
  16. user = insert(:user, %{nickname: "john t1000"})
  17. insert(:user, %{invisible: true, nickname: "john t800"})
  18. [found_user] = User.search("john")
  19. assert found_user.id == user.id
  20. end
  21. test "accepts limit parameter" do
  22. Enum.each(0..4, &insert(:user, %{nickname: "john#{&1}"}))
  23. assert length(User.search("john", limit: 3)) == 3
  24. assert length(User.search("john")) == 5
  25. end
  26. test "accepts offset parameter" do
  27. Enum.each(0..4, &insert(:user, %{nickname: "john#{&1}"}))
  28. assert length(User.search("john", limit: 3)) == 3
  29. assert length(User.search("john", limit: 3, offset: 3)) == 2
  30. end
  31. test "finds a user by full or partial nickname" do
  32. user = insert(:user, %{nickname: "john"})
  33. Enum.each(["john", "jo", "j"], fn query ->
  34. assert user ==
  35. User.search(query)
  36. |> List.first()
  37. |> Map.put(:search_rank, nil)
  38. |> Map.put(:search_type, nil)
  39. end)
  40. end
  41. test "finds a user by full or partial name" do
  42. user = insert(:user, %{name: "John Doe"})
  43. Enum.each(["John Doe", "JOHN", "doe", "j d", "j", "d"], fn query ->
  44. assert user ==
  45. User.search(query)
  46. |> List.first()
  47. |> Map.put(:search_rank, nil)
  48. |> Map.put(:search_type, nil)
  49. end)
  50. end
  51. test "finds users, considering density of matched tokens" do
  52. u1 = insert(:user, %{name: "Bar Bar plus Word Word"})
  53. u2 = insert(:user, %{name: "Word Word Bar Bar Bar"})
  54. assert [u2.id, u1.id] == Enum.map(User.search("bar word"), & &1.id)
  55. end
  56. test "finds users, boosting ranks of friends and followers" do
  57. u1 = insert(:user)
  58. u2 = insert(:user, %{name: "Doe"})
  59. follower = insert(:user, %{name: "Doe"})
  60. friend = insert(:user, %{name: "Doe"})
  61. {:ok, follower} = User.follow(follower, u1)
  62. {:ok, u1} = User.follow(u1, friend)
  63. assert [friend.id, follower.id, u2.id] --
  64. Enum.map(User.search("doe", resolve: false, for_user: u1), & &1.id) == []
  65. end
  66. test "finds followers of user by partial name" do
  67. u1 = insert(:user)
  68. u2 = insert(:user, %{name: "Jimi"})
  69. follower_jimi = insert(:user, %{name: "Jimi Hendrix"})
  70. follower_lizz = insert(:user, %{name: "Lizz Wright"})
  71. friend = insert(:user, %{name: "Jimi"})
  72. {:ok, follower_jimi} = User.follow(follower_jimi, u1)
  73. {:ok, _follower_lizz} = User.follow(follower_lizz, u2)
  74. {:ok, u1} = User.follow(u1, friend)
  75. assert Enum.map(User.search("jimi", following: true, for_user: u1), & &1.id) == [
  76. follower_jimi.id
  77. ]
  78. assert User.search("lizz", following: true, for_user: u1) == []
  79. end
  80. test "find local and remote users for authenticated users" do
  81. u1 = insert(:user, %{name: "lain"})
  82. u2 = insert(:user, %{name: "ebn", nickname: "lain@mastodon.social", local: false})
  83. u3 = insert(:user, %{nickname: "lain@pleroma.soykaf.com", local: false})
  84. results =
  85. "lain"
  86. |> User.search(for_user: u1)
  87. |> Enum.map(& &1.id)
  88. |> Enum.sort()
  89. assert [u1.id, u2.id, u3.id] == results
  90. end
  91. test "find only local users for unauthenticated users" do
  92. %{id: id} = insert(:user, %{name: "lain"})
  93. insert(:user, %{name: "ebn", nickname: "lain@mastodon.social", local: false})
  94. insert(:user, %{nickname: "lain@pleroma.soykaf.com", local: false})
  95. assert [%{id: ^id}] = User.search("lain")
  96. end
  97. test "find only local users for authenticated users when `limit_to_local_content` is `:all`" do
  98. Pleroma.Config.put([:instance, :limit_to_local_content], :all)
  99. %{id: id} = insert(:user, %{name: "lain"})
  100. insert(:user, %{name: "ebn", nickname: "lain@mastodon.social", local: false})
  101. insert(:user, %{nickname: "lain@pleroma.soykaf.com", local: false})
  102. assert [%{id: ^id}] = User.search("lain")
  103. end
  104. test "find all users for unauthenticated users when `limit_to_local_content` is `false`" do
  105. Pleroma.Config.put([:instance, :limit_to_local_content], false)
  106. u1 = insert(:user, %{name: "lain"})
  107. u2 = insert(:user, %{name: "ebn", nickname: "lain@mastodon.social", local: false})
  108. u3 = insert(:user, %{nickname: "lain@pleroma.soykaf.com", local: false})
  109. results =
  110. "lain"
  111. |> User.search()
  112. |> Enum.map(& &1.id)
  113. |> Enum.sort()
  114. assert [u1.id, u2.id, u3.id] == results
  115. end
  116. test "does not yield false-positive matches" do
  117. insert(:user, %{name: "John Doe"})
  118. Enum.each(["mary", "a", ""], fn query ->
  119. assert [] == User.search(query)
  120. end)
  121. end
  122. test "works with URIs" do
  123. user = insert(:user)
  124. results =
  125. User.search("http://mastodon.example.org/users/admin", resolve: true, for_user: user)
  126. result = results |> List.first()
  127. user = User.get_cached_by_ap_id("http://mastodon.example.org/users/admin")
  128. assert length(results) == 1
  129. expected =
  130. result
  131. |> Map.put(:search_rank, nil)
  132. |> Map.put(:search_type, nil)
  133. |> Map.put(:last_digest_emailed_at, nil)
  134. |> Map.put(:multi_factor_authentication_settings, nil)
  135. |> Map.put(:notification_settings, nil)
  136. assert user == expected
  137. end
  138. test "excludes a blocked users from search result" do
  139. user = insert(:user, %{nickname: "Bill"})
  140. [blocked_user | users] = Enum.map(0..3, &insert(:user, %{nickname: "john#{&1}"}))
  141. blocked_user2 =
  142. insert(
  143. :user,
  144. %{nickname: "john awful", ap_id: "https://awful-and-rude-instance.com/user/bully"}
  145. )
  146. User.block_domain(user, "awful-and-rude-instance.com")
  147. User.block(user, blocked_user)
  148. account_ids = User.search("john", for_user: refresh_record(user)) |> collect_ids
  149. assert account_ids == collect_ids(users)
  150. refute Enum.member?(account_ids, blocked_user.id)
  151. refute Enum.member?(account_ids, blocked_user2.id)
  152. assert length(account_ids) == 3
  153. end
  154. test "local user has the same search_rank as for users with the same nickname, but another domain" do
  155. user = insert(:user)
  156. insert(:user, nickname: "lain@mastodon.social")
  157. insert(:user, nickname: "lain")
  158. insert(:user, nickname: "lain@pleroma.social")
  159. assert User.search("lain@localhost", resolve: true, for_user: user)
  160. |> Enum.each(fn u -> u.search_rank == 0.5 end)
  161. end
  162. test "localhost is the part of the domain" do
  163. user = insert(:user)
  164. insert(:user, nickname: "another@somedomain")
  165. insert(:user, nickname: "lain")
  166. insert(:user, nickname: "lain@examplelocalhost")
  167. result = User.search("lain@examplelocalhost", resolve: true, for_user: user)
  168. assert Enum.each(result, fn u -> u.search_rank == 0.5 end)
  169. assert length(result) == 2
  170. end
  171. test "local user search with users" do
  172. user = insert(:user)
  173. local_user = insert(:user, nickname: "lain")
  174. insert(:user, nickname: "another@localhost.com")
  175. insert(:user, nickname: "localhost@localhost.com")
  176. [result] = User.search("lain@localhost", resolve: true, for_user: user)
  177. assert Map.put(result, :search_rank, nil) |> Map.put(:search_type, nil) == local_user
  178. end
  179. test "works with idna domains" do
  180. user = insert(:user, nickname: "lain@" <> to_string(:idna.encode("zetsubou.みんな")))
  181. results = User.search("lain@zetsubou.みんな", resolve: false, for_user: user)
  182. result = List.first(results)
  183. assert user == result |> Map.put(:search_rank, nil) |> Map.put(:search_type, nil)
  184. end
  185. test "works with idna domains converted input" do
  186. user = insert(:user, nickname: "lain@" <> to_string(:idna.encode("zetsubou.みんな")))
  187. results =
  188. User.search("lain@zetsubou." <> to_string(:idna.encode("zetsubou.みんな")),
  189. resolve: false,
  190. for_user: user
  191. )
  192. result = List.first(results)
  193. assert user == result |> Map.put(:search_rank, nil) |> Map.put(:search_type, nil)
  194. end
  195. test "works with idna domains and bad chars in domain" do
  196. user = insert(:user, nickname: "lain@" <> to_string(:idna.encode("zetsubou.みんな")))
  197. results =
  198. User.search("lain@zetsubou!@#$%^&*()+,-/:;<=>?[]'_{}|~`.みんな",
  199. resolve: false,
  200. for_user: user
  201. )
  202. result = List.first(results)
  203. assert user == result |> Map.put(:search_rank, nil) |> Map.put(:search_type, nil)
  204. end
  205. test "works with idna domains and query as link" do
  206. user = insert(:user, nickname: "lain@" <> to_string(:idna.encode("zetsubou.みんな")))
  207. results =
  208. User.search("https://zetsubou.みんな/users/lain",
  209. resolve: false,
  210. for_user: user
  211. )
  212. result = List.first(results)
  213. assert user == result |> Map.put(:search_rank, nil) |> Map.put(:search_type, nil)
  214. end
  215. end
  216. end