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.

286 lines
9.4KB

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