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.

580 lines
19KB

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