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