logo

pleroma

My custom branche(s) on git.pleroma.social/pleroma/pleroma

account_view_test.exs (19140B)


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