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.

524 lines
17KB

  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.AccountViewTest do
  5. use Pleroma.DataCase
  6. alias Pleroma.User
  7. alias Pleroma.UserRelationship
  8. alias Pleroma.Web.CommonAPI
  9. alias Pleroma.Web.MastodonAPI.AccountView
  10. import Pleroma.Factory
  11. import Tesla.Mock
  12. setup do
  13. mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
  14. :ok
  15. end
  16. test "Represent a user account" do
  17. background_image = %{
  18. "url" => [%{"href" => "https://example.com/images/asuka_hospital.png"}]
  19. }
  20. user =
  21. insert(:user, %{
  22. follower_count: 3,
  23. note_count: 5,
  24. background: background_image,
  25. nickname: "shp@shitposter.club",
  26. name: ":karjalanpiirakka: shp",
  27. bio:
  28. "<script src=\"invalid-html\"></script><span>valid html</span>. a<br>b<br/>c<br >d<br />f '&<>\"",
  29. inserted_at: ~N[2017-08-15 15:47:06.597036],
  30. emoji: %{"karjalanpiirakka" => "/file.png"}
  31. })
  32. expected = %{
  33. id: to_string(user.id),
  34. username: "shp",
  35. acct: user.nickname,
  36. display_name: user.name,
  37. locked: false,
  38. created_at: "2017-08-15T15:47:06.000Z",
  39. followers_count: 3,
  40. following_count: 0,
  41. statuses_count: 5,
  42. note: "<span>valid html</span>. a<br/>b<br/>c<br/>d<br/>f &#39;&amp;&lt;&gt;&quot;",
  43. url: user.ap_id,
  44. avatar: "http://localhost:4001/images/avi.png",
  45. avatar_static: "http://localhost:4001/images/avi.png",
  46. header: "http://localhost:4001/images/banner.png",
  47. header_static: "http://localhost:4001/images/banner.png",
  48. emojis: [
  49. %{
  50. static_url: "/file.png",
  51. url: "/file.png",
  52. shortcode: "karjalanpiirakka",
  53. visible_in_picker: false
  54. }
  55. ],
  56. fields: [],
  57. bot: false,
  58. source: %{
  59. note: "valid html. a\nb\nc\nd\nf '&<>\"",
  60. sensitive: false,
  61. pleroma: %{
  62. actor_type: "Person",
  63. discoverable: false
  64. },
  65. fields: []
  66. },
  67. pleroma: %{
  68. ap_id: user.ap_id,
  69. background_image: "https://example.com/images/asuka_hospital.png",
  70. confirmation_pending: false,
  71. tags: [],
  72. is_admin: false,
  73. is_moderator: false,
  74. hide_favorites: true,
  75. hide_followers: false,
  76. hide_follows: false,
  77. hide_followers_count: false,
  78. hide_follows_count: false,
  79. relationship: %{},
  80. skip_thread_containment: false
  81. }
  82. }
  83. assert expected == AccountView.render("show.json", %{user: user})
  84. end
  85. test "Represent the user account for the account owner" do
  86. user = insert(:user)
  87. notification_settings = %{
  88. followers: true,
  89. follows: true,
  90. non_followers: true,
  91. non_follows: true,
  92. privacy_option: false
  93. }
  94. privacy = user.default_scope
  95. assert %{
  96. pleroma: %{notification_settings: ^notification_settings, allow_following_move: true},
  97. source: %{privacy: ^privacy}
  98. } = AccountView.render("show.json", %{user: user, for: user})
  99. end
  100. test "Represent a Service(bot) account" do
  101. user =
  102. insert(:user, %{
  103. follower_count: 3,
  104. note_count: 5,
  105. actor_type: "Service",
  106. nickname: "shp@shitposter.club",
  107. inserted_at: ~N[2017-08-15 15:47:06.597036]
  108. })
  109. expected = %{
  110. id: to_string(user.id),
  111. username: "shp",
  112. acct: user.nickname,
  113. display_name: user.name,
  114. locked: false,
  115. created_at: "2017-08-15T15:47:06.000Z",
  116. followers_count: 3,
  117. following_count: 0,
  118. statuses_count: 5,
  119. note: user.bio,
  120. url: user.ap_id,
  121. avatar: "http://localhost:4001/images/avi.png",
  122. avatar_static: "http://localhost:4001/images/avi.png",
  123. header: "http://localhost:4001/images/banner.png",
  124. header_static: "http://localhost:4001/images/banner.png",
  125. emojis: [],
  126. fields: [],
  127. bot: true,
  128. source: %{
  129. note: user.bio,
  130. sensitive: false,
  131. pleroma: %{
  132. actor_type: "Service",
  133. discoverable: false
  134. },
  135. fields: []
  136. },
  137. pleroma: %{
  138. ap_id: user.ap_id,
  139. background_image: nil,
  140. confirmation_pending: false,
  141. tags: [],
  142. is_admin: false,
  143. is_moderator: false,
  144. hide_favorites: true,
  145. hide_followers: false,
  146. hide_follows: false,
  147. hide_followers_count: false,
  148. hide_follows_count: false,
  149. relationship: %{},
  150. skip_thread_containment: false
  151. }
  152. }
  153. assert expected == AccountView.render("show.json", %{user: user})
  154. end
  155. test "Represent a Funkwhale channel" do
  156. {:ok, user} =
  157. User.get_or_fetch_by_ap_id(
  158. "https://channels.tests.funkwhale.audio/federation/actors/compositions"
  159. )
  160. assert represented = AccountView.render("show.json", %{user: user})
  161. assert represented.acct == "compositions@channels.tests.funkwhale.audio"
  162. assert represented.url == "https://channels.tests.funkwhale.audio/channels/compositions"
  163. end
  164. test "Represent a deactivated user for an admin" do
  165. admin = insert(:user, is_admin: true)
  166. deactivated_user = insert(:user, deactivated: true)
  167. represented = AccountView.render("show.json", %{user: deactivated_user, for: admin})
  168. assert represented[:pleroma][:deactivated] == true
  169. end
  170. test "Represent a smaller mention" do
  171. user = insert(:user)
  172. expected = %{
  173. id: to_string(user.id),
  174. acct: user.nickname,
  175. username: user.nickname,
  176. url: user.ap_id
  177. }
  178. assert expected == AccountView.render("mention.json", %{user: user})
  179. end
  180. describe "relationship" do
  181. defp test_relationship_rendering(user, other_user, expected_result) do
  182. opts = %{user: user, target: other_user, relationships: nil}
  183. assert expected_result == AccountView.render("relationship.json", opts)
  184. relationships_opt = UserRelationship.view_relationships_option(user, [other_user])
  185. opts = Map.put(opts, :relationships, relationships_opt)
  186. assert expected_result == AccountView.render("relationship.json", opts)
  187. assert [expected_result] ==
  188. AccountView.render("relationships.json", %{user: user, targets: [other_user]})
  189. end
  190. @blank_response %{
  191. following: false,
  192. followed_by: false,
  193. blocking: false,
  194. blocked_by: false,
  195. muting: false,
  196. muting_notifications: false,
  197. subscribing: false,
  198. requested: false,
  199. domain_blocking: false,
  200. showing_reblogs: true,
  201. endorsed: false
  202. }
  203. test "represent a relationship for the following and followed user" do
  204. user = insert(:user)
  205. other_user = insert(:user)
  206. {:ok, user} = User.follow(user, other_user)
  207. {:ok, other_user} = User.follow(other_user, user)
  208. {:ok, _subscription} = User.subscribe(user, other_user)
  209. {:ok, _user_relationships} = User.mute(user, other_user, true)
  210. {:ok, _reblog_mute} = CommonAPI.hide_reblogs(user, other_user)
  211. expected =
  212. Map.merge(
  213. @blank_response,
  214. %{
  215. following: true,
  216. followed_by: true,
  217. muting: true,
  218. muting_notifications: true,
  219. subscribing: true,
  220. showing_reblogs: false,
  221. id: to_string(other_user.id)
  222. }
  223. )
  224. test_relationship_rendering(user, other_user, expected)
  225. end
  226. test "represent a relationship for the blocking and blocked user" do
  227. user = insert(:user)
  228. other_user = insert(:user)
  229. {:ok, user} = User.follow(user, other_user)
  230. {:ok, _subscription} = User.subscribe(user, other_user)
  231. {:ok, _user_relationship} = User.block(user, other_user)
  232. {:ok, _user_relationship} = User.block(other_user, user)
  233. expected =
  234. Map.merge(
  235. @blank_response,
  236. %{following: false, blocking: true, blocked_by: true, id: to_string(other_user.id)}
  237. )
  238. test_relationship_rendering(user, other_user, expected)
  239. end
  240. test "represent a relationship for the user blocking a domain" do
  241. user = insert(:user)
  242. other_user = insert(:user, ap_id: "https://bad.site/users/other_user")
  243. {:ok, user} = User.block_domain(user, "bad.site")
  244. expected =
  245. Map.merge(
  246. @blank_response,
  247. %{domain_blocking: true, blocking: false, id: to_string(other_user.id)}
  248. )
  249. test_relationship_rendering(user, other_user, expected)
  250. end
  251. test "represent a relationship for the user with a pending follow request" do
  252. user = insert(:user)
  253. other_user = insert(:user, locked: true)
  254. {:ok, user, other_user, _} = CommonAPI.follow(user, other_user)
  255. user = User.get_cached_by_id(user.id)
  256. other_user = User.get_cached_by_id(other_user.id)
  257. expected =
  258. Map.merge(
  259. @blank_response,
  260. %{requested: true, following: false, id: to_string(other_user.id)}
  261. )
  262. test_relationship_rendering(user, other_user, expected)
  263. end
  264. end
  265. test "returns the settings store if the requesting user is the represented user and it's requested specifically" do
  266. user = insert(:user, pleroma_settings_store: %{fe: "test"})
  267. result =
  268. AccountView.render("show.json", %{user: user, for: user, with_pleroma_settings: true})
  269. assert result.pleroma.settings_store == %{:fe => "test"}
  270. result = AccountView.render("show.json", %{user: user, with_pleroma_settings: true})
  271. assert result.pleroma[:settings_store] == nil
  272. result = AccountView.render("show.json", %{user: user, for: user})
  273. assert result.pleroma[:settings_store] == nil
  274. end
  275. test "doesn't sanitize display names" do
  276. user = insert(:user, name: "<marquee> username </marquee>")
  277. result = AccountView.render("show.json", %{user: user})
  278. assert result.display_name == "<marquee> username </marquee>"
  279. end
  280. test "never display nil user follow counts" do
  281. user = insert(:user, following_count: 0, follower_count: 0)
  282. result = AccountView.render("show.json", %{user: user})
  283. assert result.following_count == 0
  284. assert result.followers_count == 0
  285. end
  286. describe "hiding follows/following" do
  287. test "shows when follows/followers stats are hidden and sets follow/follower count to 0" do
  288. user =
  289. insert(:user, %{
  290. hide_followers: true,
  291. hide_followers_count: true,
  292. hide_follows: true,
  293. hide_follows_count: true
  294. })
  295. other_user = insert(:user)
  296. {:ok, user, other_user, _activity} = CommonAPI.follow(user, other_user)
  297. {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user)
  298. assert %{
  299. followers_count: 0,
  300. following_count: 0,
  301. pleroma: %{hide_follows_count: true, hide_followers_count: true}
  302. } = AccountView.render("show.json", %{user: user})
  303. end
  304. test "shows when follows/followers are hidden" do
  305. user = insert(:user, hide_followers: true, hide_follows: true)
  306. other_user = insert(:user)
  307. {:ok, user, other_user, _activity} = CommonAPI.follow(user, other_user)
  308. {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user)
  309. assert %{
  310. followers_count: 1,
  311. following_count: 1,
  312. pleroma: %{hide_follows: true, hide_followers: true}
  313. } = AccountView.render("show.json", %{user: user})
  314. end
  315. test "shows actual follower/following count to the account owner" do
  316. user = insert(:user, hide_followers: true, hide_follows: true)
  317. other_user = insert(:user)
  318. {:ok, user, other_user, _activity} = CommonAPI.follow(user, other_user)
  319. {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user)
  320. assert %{
  321. followers_count: 1,
  322. following_count: 1
  323. } = AccountView.render("show.json", %{user: user, for: user})
  324. end
  325. test "shows unread_conversation_count only to the account owner" do
  326. user = insert(:user)
  327. other_user = insert(:user)
  328. {:ok, _activity} =
  329. CommonAPI.post(other_user, %{
  330. status: "Hey @#{user.nickname}.",
  331. visibility: "direct"
  332. })
  333. user = User.get_cached_by_ap_id(user.ap_id)
  334. assert AccountView.render("show.json", %{user: user, for: other_user})[:pleroma][
  335. :unread_conversation_count
  336. ] == nil
  337. assert AccountView.render("show.json", %{user: user, for: user})[:pleroma][
  338. :unread_conversation_count
  339. ] == 1
  340. end
  341. test "shows unread_count only to the account owner" do
  342. user = insert(:user)
  343. insert_list(7, :notification, user: user)
  344. other_user = insert(:user)
  345. user = User.get_cached_by_ap_id(user.ap_id)
  346. assert AccountView.render(
  347. "show.json",
  348. %{user: user, for: other_user}
  349. )[:pleroma][:unread_notifications_count] == nil
  350. assert AccountView.render(
  351. "show.json",
  352. %{user: user, for: user}
  353. )[:pleroma][:unread_notifications_count] == 7
  354. end
  355. end
  356. describe "follow requests counter" do
  357. test "shows zero when no follow requests are pending" do
  358. user = insert(:user)
  359. assert %{follow_requests_count: 0} =
  360. AccountView.render("show.json", %{user: user, for: user})
  361. other_user = insert(:user)
  362. {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user)
  363. assert %{follow_requests_count: 0} =
  364. AccountView.render("show.json", %{user: user, for: user})
  365. end
  366. test "shows non-zero when follow requests are pending" do
  367. user = insert(:user, locked: true)
  368. assert %{locked: true} = AccountView.render("show.json", %{user: user, for: user})
  369. other_user = insert(:user)
  370. {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user)
  371. assert %{locked: true, follow_requests_count: 1} =
  372. AccountView.render("show.json", %{user: user, for: user})
  373. end
  374. test "decreases when accepting a follow request" do
  375. user = insert(:user, locked: true)
  376. assert %{locked: true} = AccountView.render("show.json", %{user: user, for: user})
  377. other_user = insert(:user)
  378. {:ok, other_user, user, _activity} = CommonAPI.follow(other_user, user)
  379. assert %{locked: true, follow_requests_count: 1} =
  380. AccountView.render("show.json", %{user: user, for: user})
  381. {:ok, _other_user} = CommonAPI.accept_follow_request(other_user, user)
  382. assert %{locked: true, follow_requests_count: 0} =
  383. AccountView.render("show.json", %{user: user, for: user})
  384. end
  385. test "decreases when rejecting a follow request" do
  386. user = insert(:user, locked: true)
  387. assert %{locked: true} = AccountView.render("show.json", %{user: user, for: user})
  388. other_user = insert(:user)
  389. {:ok, other_user, user, _activity} = CommonAPI.follow(other_user, user)
  390. assert %{locked: true, follow_requests_count: 1} =
  391. AccountView.render("show.json", %{user: user, for: user})
  392. {:ok, _other_user} = CommonAPI.reject_follow_request(other_user, user)
  393. assert %{locked: true, follow_requests_count: 0} =
  394. AccountView.render("show.json", %{user: user, for: user})
  395. end
  396. test "shows non-zero when historical unapproved requests are present" do
  397. user = insert(:user, locked: true)
  398. assert %{locked: true} = AccountView.render("show.json", %{user: user, for: user})
  399. other_user = insert(:user)
  400. {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user)
  401. {:ok, user} = User.update_and_set_cache(user, %{locked: false})
  402. assert %{locked: false, follow_requests_count: 1} =
  403. AccountView.render("show.json", %{user: user, for: user})
  404. end
  405. end
  406. test "uses mediaproxy urls when it's enabled" do
  407. clear_config([:media_proxy, :enabled], true)
  408. user =
  409. insert(:user,
  410. avatar: %{"url" => [%{"href" => "https://evil.website/avatar.png"}]},
  411. banner: %{"url" => [%{"href" => "https://evil.website/banner.png"}]},
  412. emoji: %{"joker_smile" => "https://evil.website/society.png"}
  413. )
  414. AccountView.render("show.json", %{user: user})
  415. |> Enum.all?(fn
  416. {key, url} when key in [:avatar, :avatar_static, :header, :header_static] ->
  417. String.starts_with?(url, Pleroma.Web.base_url())
  418. {:emojis, emojis} ->
  419. Enum.all?(emojis, fn %{url: url, static_url: static_url} ->
  420. String.starts_with?(url, Pleroma.Web.base_url()) &&
  421. String.starts_with?(static_url, Pleroma.Web.base_url())
  422. end)
  423. _ ->
  424. true
  425. end)
  426. |> assert()
  427. end
  428. end