Merge branch 'fixes_2034_reports_should_send_a_notification_to_admins' into 'develop'
[akkoma] / test / pleroma / web / mastodon_api / views / status_view_test.exs
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
5 defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
6 use Pleroma.DataCase
7
8 alias Pleroma.Activity
9 alias Pleroma.Bookmark
10 alias Pleroma.Conversation.Participation
11 alias Pleroma.HTML
12 alias Pleroma.Object
13 alias Pleroma.Repo
14 alias Pleroma.User
15 alias Pleroma.UserRelationship
16 alias Pleroma.Web.CommonAPI
17 alias Pleroma.Web.CommonAPI.Utils
18 alias Pleroma.Web.MastodonAPI.AccountView
19 alias Pleroma.Web.MastodonAPI.StatusView
20
21 import Pleroma.Factory
22 import Tesla.Mock
23 import OpenApiSpex.TestAssertions
24
25 setup do
26 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
27 :ok
28 end
29
30 test "has an emoji reaction list" do
31 user = insert(:user)
32 other_user = insert(:user)
33 third_user = insert(:user)
34 {:ok, activity} = CommonAPI.post(user, %{status: "dae cofe??"})
35
36 {:ok, _} = CommonAPI.react_with_emoji(activity.id, user, "☕")
37 {:ok, _} = CommonAPI.react_with_emoji(activity.id, third_user, "🍵")
38 {:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "☕")
39 activity = Repo.get(Activity, activity.id)
40 status = StatusView.render("show.json", activity: activity)
41
42 assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
43
44 assert status[:pleroma][:emoji_reactions] == [
45 %{name: "☕", count: 2, me: false},
46 %{name: "🍵", count: 1, me: false}
47 ]
48
49 status = StatusView.render("show.json", activity: activity, for: user)
50
51 assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
52
53 assert status[:pleroma][:emoji_reactions] == [
54 %{name: "☕", count: 2, me: true},
55 %{name: "🍵", count: 1, me: false}
56 ]
57 end
58
59 test "works correctly with badly formatted emojis" do
60 user = insert(:user)
61 {:ok, activity} = CommonAPI.post(user, %{status: "yo"})
62
63 activity
64 |> Object.normalize(false)
65 |> Object.update_data(%{"reactions" => %{"☕" => [user.ap_id], "x" => 1}})
66
67 activity = Activity.get_by_id(activity.id)
68
69 status = StatusView.render("show.json", activity: activity, for: user)
70
71 assert status[:pleroma][:emoji_reactions] == [
72 %{name: "☕", count: 1, me: true}
73 ]
74 end
75
76 test "loads and returns the direct conversation id when given the `with_direct_conversation_id` option" do
77 user = insert(:user)
78
79 {:ok, activity} = CommonAPI.post(user, %{status: "Hey @shp!", visibility: "direct"})
80 [participation] = Participation.for_user(user)
81
82 status =
83 StatusView.render("show.json",
84 activity: activity,
85 with_direct_conversation_id: true,
86 for: user
87 )
88
89 assert status[:pleroma][:direct_conversation_id] == participation.id
90
91 status = StatusView.render("show.json", activity: activity, for: user)
92 assert status[:pleroma][:direct_conversation_id] == nil
93 assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
94 end
95
96 test "returns the direct conversation id when given the `direct_conversation_id` option" do
97 user = insert(:user)
98
99 {:ok, activity} = CommonAPI.post(user, %{status: "Hey @shp!", visibility: "direct"})
100 [participation] = Participation.for_user(user)
101
102 status =
103 StatusView.render("show.json",
104 activity: activity,
105 direct_conversation_id: participation.id,
106 for: user
107 )
108
109 assert status[:pleroma][:direct_conversation_id] == participation.id
110 assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
111 end
112
113 test "returns a temporary ap_id based user for activities missing db users" do
114 user = insert(:user)
115
116 {:ok, activity} = CommonAPI.post(user, %{status: "Hey @shp!", visibility: "direct"})
117
118 Repo.delete(user)
119 Cachex.clear(:user_cache)
120
121 finger_url =
122 "https://localhost/.well-known/webfinger?resource=acct:#{user.nickname}@localhost"
123
124 Tesla.Mock.mock_global(fn
125 %{method: :get, url: "http://localhost/.well-known/host-meta"} ->
126 %Tesla.Env{status: 404, body: ""}
127
128 %{method: :get, url: "https://localhost/.well-known/host-meta"} ->
129 %Tesla.Env{status: 404, body: ""}
130
131 %{
132 method: :get,
133 url: ^finger_url
134 } ->
135 %Tesla.Env{status: 404, body: ""}
136 end)
137
138 %{account: ms_user} = StatusView.render("show.json", activity: activity)
139
140 assert ms_user.acct == "erroruser@example.com"
141 end
142
143 test "tries to get a user by nickname if fetching by ap_id doesn't work" do
144 user = insert(:user)
145
146 {:ok, activity} = CommonAPI.post(user, %{status: "Hey @shp!", visibility: "direct"})
147
148 {:ok, user} =
149 user
150 |> Ecto.Changeset.change(%{ap_id: "#{user.ap_id}/extension/#{user.nickname}"})
151 |> Repo.update()
152
153 Cachex.clear(:user_cache)
154
155 result = StatusView.render("show.json", activity: activity)
156
157 assert result[:account][:id] == to_string(user.id)
158 assert_schema(result, "Status", Pleroma.Web.ApiSpec.spec())
159 end
160
161 test "a note with null content" do
162 note = insert(:note_activity)
163 note_object = Object.normalize(note)
164
165 data =
166 note_object.data
167 |> Map.put("content", nil)
168
169 Object.change(note_object, %{data: data})
170 |> Object.update_and_set_cache()
171
172 User.get_cached_by_ap_id(note.data["actor"])
173
174 status = StatusView.render("show.json", %{activity: note})
175
176 assert status.content == ""
177 assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
178 end
179
180 test "a note activity" do
181 note = insert(:note_activity)
182 object_data = Object.normalize(note).data
183 user = User.get_cached_by_ap_id(note.data["actor"])
184
185 convo_id = Utils.context_to_conversation_id(object_data["context"])
186
187 status = StatusView.render("show.json", %{activity: note})
188
189 created_at =
190 (object_data["published"] || "")
191 |> String.replace(~r/\.\d+Z/, ".000Z")
192
193 expected = %{
194 id: to_string(note.id),
195 uri: object_data["id"],
196 url: Pleroma.Web.Router.Helpers.o_status_url(Pleroma.Web.Endpoint, :notice, note),
197 account: AccountView.render("show.json", %{user: user, skip_visibility_check: true}),
198 in_reply_to_id: nil,
199 in_reply_to_account_id: nil,
200 card: nil,
201 reblog: nil,
202 content: HTML.filter_tags(object_data["content"]),
203 text: nil,
204 created_at: created_at,
205 reblogs_count: 0,
206 replies_count: 0,
207 favourites_count: 0,
208 reblogged: false,
209 bookmarked: false,
210 favourited: false,
211 muted: false,
212 pinned: false,
213 sensitive: false,
214 poll: nil,
215 spoiler_text: HTML.filter_tags(object_data["summary"]),
216 visibility: "public",
217 media_attachments: [],
218 mentions: [],
219 tags: [
220 %{
221 name: "#{object_data["tag"]}",
222 url: "/tag/#{object_data["tag"]}"
223 }
224 ],
225 application: %{
226 name: "Web",
227 website: nil
228 },
229 language: nil,
230 emojis: [
231 %{
232 shortcode: "2hu",
233 url: "corndog.png",
234 static_url: "corndog.png",
235 visible_in_picker: false
236 }
237 ],
238 pleroma: %{
239 local: true,
240 conversation_id: convo_id,
241 in_reply_to_account_acct: nil,
242 content: %{"text/plain" => HTML.strip_tags(object_data["content"])},
243 spoiler_text: %{"text/plain" => HTML.strip_tags(object_data["summary"])},
244 expires_at: nil,
245 direct_conversation_id: nil,
246 thread_muted: false,
247 emoji_reactions: [],
248 parent_visible: false
249 }
250 }
251
252 assert status == expected
253 assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
254 end
255
256 test "tells if the message is muted for some reason" do
257 user = insert(:user)
258 other_user = insert(:user)
259
260 {:ok, _user_relationships} = User.mute(user, other_user)
261
262 {:ok, activity} = CommonAPI.post(other_user, %{status: "test"})
263
264 relationships_opt = UserRelationship.view_relationships_option(user, [other_user])
265
266 opts = %{activity: activity}
267 status = StatusView.render("show.json", opts)
268 assert status.muted == false
269 assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
270
271 status = StatusView.render("show.json", Map.put(opts, :relationships, relationships_opt))
272 assert status.muted == false
273
274 for_opts = %{activity: activity, for: user}
275 status = StatusView.render("show.json", for_opts)
276 assert status.muted == true
277
278 status = StatusView.render("show.json", Map.put(for_opts, :relationships, relationships_opt))
279 assert status.muted == true
280 assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
281 end
282
283 test "tells if the message is thread muted" do
284 user = insert(:user)
285 other_user = insert(:user)
286
287 {:ok, _user_relationships} = User.mute(user, other_user)
288
289 {:ok, activity} = CommonAPI.post(other_user, %{status: "test"})
290 status = StatusView.render("show.json", %{activity: activity, for: user})
291
292 assert status.pleroma.thread_muted == false
293
294 {:ok, activity} = CommonAPI.add_mute(user, activity)
295
296 status = StatusView.render("show.json", %{activity: activity, for: user})
297
298 assert status.pleroma.thread_muted == true
299 end
300
301 test "tells if the status is bookmarked" do
302 user = insert(:user)
303
304 {:ok, activity} = CommonAPI.post(user, %{status: "Cute girls doing cute things"})
305 status = StatusView.render("show.json", %{activity: activity})
306
307 assert status.bookmarked == false
308
309 status = StatusView.render("show.json", %{activity: activity, for: user})
310
311 assert status.bookmarked == false
312
313 {:ok, _bookmark} = Bookmark.create(user.id, activity.id)
314
315 activity = Activity.get_by_id_with_object(activity.id)
316
317 status = StatusView.render("show.json", %{activity: activity, for: user})
318
319 assert status.bookmarked == true
320 end
321
322 test "a reply" do
323 note = insert(:note_activity)
324 user = insert(:user)
325
326 {:ok, activity} = CommonAPI.post(user, %{status: "he", in_reply_to_status_id: note.id})
327
328 status = StatusView.render("show.json", %{activity: activity})
329
330 assert status.in_reply_to_id == to_string(note.id)
331
332 [status] = StatusView.render("index.json", %{activities: [activity], as: :activity})
333
334 assert status.in_reply_to_id == to_string(note.id)
335 end
336
337 test "contains mentions" do
338 user = insert(:user)
339 mentioned = insert(:user)
340
341 {:ok, activity} = CommonAPI.post(user, %{status: "hi @#{mentioned.nickname}"})
342
343 status = StatusView.render("show.json", %{activity: activity})
344
345 assert status.mentions ==
346 Enum.map([mentioned], fn u -> AccountView.render("mention.json", %{user: u}) end)
347
348 assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
349 end
350
351 test "create mentions from the 'to' field" do
352 %User{ap_id: recipient_ap_id} = insert(:user)
353 cc = insert_pair(:user) |> Enum.map(& &1.ap_id)
354
355 object =
356 insert(:note, %{
357 data: %{
358 "to" => [recipient_ap_id],
359 "cc" => cc
360 }
361 })
362
363 activity =
364 insert(:note_activity, %{
365 note: object,
366 recipients: [recipient_ap_id | cc]
367 })
368
369 assert length(activity.recipients) == 3
370
371 %{mentions: [mention] = mentions} = StatusView.render("show.json", %{activity: activity})
372
373 assert length(mentions) == 1
374 assert mention.url == recipient_ap_id
375 end
376
377 test "create mentions from the 'tag' field" do
378 recipient = insert(:user)
379 cc = insert_pair(:user) |> Enum.map(& &1.ap_id)
380
381 object =
382 insert(:note, %{
383 data: %{
384 "cc" => cc,
385 "tag" => [
386 %{
387 "href" => recipient.ap_id,
388 "name" => recipient.nickname,
389 "type" => "Mention"
390 },
391 %{
392 "href" => "https://example.com/search?tag=test",
393 "name" => "#test",
394 "type" => "Hashtag"
395 }
396 ]
397 }
398 })
399
400 activity =
401 insert(:note_activity, %{
402 note: object,
403 recipients: [recipient.ap_id | cc]
404 })
405
406 assert length(activity.recipients) == 3
407
408 %{mentions: [mention] = mentions} = StatusView.render("show.json", %{activity: activity})
409
410 assert length(mentions) == 1
411 assert mention.url == recipient.ap_id
412 end
413
414 test "attachments" do
415 object = %{
416 "type" => "Image",
417 "url" => [
418 %{
419 "mediaType" => "image/png",
420 "href" => "someurl"
421 }
422 ],
423 "blurhash" => "UJJ8X[xYW,%Jtq%NNFbXB5j]IVM|9GV=WHRn",
424 "uuid" => 6
425 }
426
427 expected = %{
428 id: "1638338801",
429 type: "image",
430 url: "someurl",
431 remote_url: "someurl",
432 preview_url: "someurl",
433 text_url: "someurl",
434 description: nil,
435 pleroma: %{mime_type: "image/png"},
436 blurhash: "UJJ8X[xYW,%Jtq%NNFbXB5j]IVM|9GV=WHRn"
437 }
438
439 api_spec = Pleroma.Web.ApiSpec.spec()
440
441 assert expected == StatusView.render("attachment.json", %{attachment: object})
442 assert_schema(expected, "Attachment", api_spec)
443
444 # If theres a "id", use that instead of the generated one
445 object = Map.put(object, "id", 2)
446 result = StatusView.render("attachment.json", %{attachment: object})
447
448 assert %{id: "2"} = result
449 assert_schema(result, "Attachment", api_spec)
450 end
451
452 test "put the url advertised in the Activity in to the url attribute" do
453 id = "https://wedistribute.org/wp-json/pterotype/v1/object/85810"
454 [activity] = Activity.search(nil, id)
455
456 status = StatusView.render("show.json", %{activity: activity})
457
458 assert status.uri == id
459 assert status.url == "https://wedistribute.org/2019/07/mastodon-drops-ostatus/"
460 end
461
462 test "a reblog" do
463 user = insert(:user)
464 activity = insert(:note_activity)
465
466 {:ok, reblog} = CommonAPI.repeat(activity.id, user)
467
468 represented = StatusView.render("show.json", %{for: user, activity: reblog})
469
470 assert represented[:id] == to_string(reblog.id)
471 assert represented[:reblog][:id] == to_string(activity.id)
472 assert represented[:emojis] == []
473 assert_schema(represented, "Status", Pleroma.Web.ApiSpec.spec())
474 end
475
476 test "a peertube video" do
477 user = insert(:user)
478
479 {:ok, object} =
480 Pleroma.Object.Fetcher.fetch_object_from_id(
481 "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3"
482 )
483
484 %Activity{} = activity = Activity.get_create_by_object_ap_id(object.data["id"])
485
486 represented = StatusView.render("show.json", %{for: user, activity: activity})
487
488 assert represented[:id] == to_string(activity.id)
489 assert length(represented[:media_attachments]) == 1
490 assert_schema(represented, "Status", Pleroma.Web.ApiSpec.spec())
491 end
492
493 test "funkwhale audio" do
494 user = insert(:user)
495
496 {:ok, object} =
497 Pleroma.Object.Fetcher.fetch_object_from_id(
498 "https://channels.tests.funkwhale.audio/federation/music/uploads/42342395-0208-4fee-a38d-259a6dae0871"
499 )
500
501 %Activity{} = activity = Activity.get_create_by_object_ap_id(object.data["id"])
502
503 represented = StatusView.render("show.json", %{for: user, activity: activity})
504
505 assert represented[:id] == to_string(activity.id)
506 assert length(represented[:media_attachments]) == 1
507 end
508
509 test "a Mobilizon event" do
510 user = insert(:user)
511
512 {:ok, object} =
513 Pleroma.Object.Fetcher.fetch_object_from_id(
514 "https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39"
515 )
516
517 %Activity{} = activity = Activity.get_create_by_object_ap_id(object.data["id"])
518
519 represented = StatusView.render("show.json", %{for: user, activity: activity})
520
521 assert represented[:id] == to_string(activity.id)
522
523 assert represented[:url] ==
524 "https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39"
525
526 assert represented[:content] ==
527 "<p><a href=\"https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39\">Mobilizon Launching Party</a></p><p>Mobilizon is now federated! 🎉</p><p></p><p>You can view this event from other instances if they are subscribed to mobilizon.org, and soon directly from Mastodon and Pleroma. It is possible that you may see some comments from other instances, including Mastodon ones, just below.</p><p></p><p>With a Mobilizon account on an instance, you may <strong>participate</strong> at events from other instances and <strong>add comments</strong> on events.</p><p></p><p>Of course, it&#39;s still <u>a work in progress</u>: if reports made from an instance on events and comments can be federated, you can&#39;t block people right now, and moderators actions are rather limited, but this <strong>will definitely get fixed over time</strong> until first stable version next year.</p><p></p><p>Anyway, if you want to come up with some feedback, head over to our forum or - if you feel you have technical skills and are familiar with it - on our Gitlab repository.</p><p></p><p>Also, to people that want to set Mobilizon themselves even though we really don&#39;t advise to do that for now, we have a little documentation but it&#39;s quite the early days and you&#39;ll probably need some help. No worries, you can chat with us on our Forum or though our Matrix channel.</p><p></p><p>Check our website for more informations and follow us on Twitter or Mastodon.</p>"
528 end
529
530 describe "build_tags/1" do
531 test "it returns a a dictionary tags" do
532 object_tags = [
533 "fediverse",
534 "mastodon",
535 "nextcloud",
536 %{
537 "href" => "https://kawen.space/users/lain",
538 "name" => "@lain@kawen.space",
539 "type" => "Mention"
540 }
541 ]
542
543 assert StatusView.build_tags(object_tags) == [
544 %{name: "fediverse", url: "/tag/fediverse"},
545 %{name: "mastodon", url: "/tag/mastodon"},
546 %{name: "nextcloud", url: "/tag/nextcloud"}
547 ]
548 end
549 end
550
551 describe "rich media cards" do
552 test "a rich media card without a site name renders correctly" do
553 page_url = "http://example.com"
554
555 card = %{
556 url: page_url,
557 image: page_url <> "/example.jpg",
558 title: "Example website"
559 }
560
561 %{provider_name: "example.com"} =
562 StatusView.render("card.json", %{page_url: page_url, rich_media: card})
563 end
564
565 test "a rich media card without a site name or image renders correctly" do
566 page_url = "http://example.com"
567
568 card = %{
569 url: page_url,
570 title: "Example website"
571 }
572
573 %{provider_name: "example.com"} =
574 StatusView.render("card.json", %{page_url: page_url, rich_media: card})
575 end
576
577 test "a rich media card without an image renders correctly" do
578 page_url = "http://example.com"
579
580 card = %{
581 url: page_url,
582 site_name: "Example site name",
583 title: "Example website"
584 }
585
586 %{provider_name: "example.com"} =
587 StatusView.render("card.json", %{page_url: page_url, rich_media: card})
588 end
589
590 test "a rich media card with all relevant data renders correctly" do
591 page_url = "http://example.com"
592
593 card = %{
594 url: page_url,
595 site_name: "Example site name",
596 title: "Example website",
597 image: page_url <> "/example.jpg",
598 description: "Example description"
599 }
600
601 %{provider_name: "example.com"} =
602 StatusView.render("card.json", %{page_url: page_url, rich_media: card})
603 end
604 end
605
606 test "does not embed a relationship in the account" do
607 user = insert(:user)
608 other_user = insert(:user)
609
610 {:ok, activity} =
611 CommonAPI.post(user, %{
612 status: "drink more water"
613 })
614
615 result = StatusView.render("show.json", %{activity: activity, for: other_user})
616
617 assert result[:account][:pleroma][:relationship] == %{}
618 assert_schema(result, "Status", Pleroma.Web.ApiSpec.spec())
619 end
620
621 test "does not embed a relationship in the account in reposts" do
622 user = insert(:user)
623 other_user = insert(:user)
624
625 {:ok, activity} =
626 CommonAPI.post(user, %{
627 status: "˙˙ɐʎns"
628 })
629
630 {:ok, activity} = CommonAPI.repeat(activity.id, other_user)
631
632 result = StatusView.render("show.json", %{activity: activity, for: user})
633
634 assert result[:account][:pleroma][:relationship] == %{}
635 assert result[:reblog][:account][:pleroma][:relationship] == %{}
636 assert_schema(result, "Status", Pleroma.Web.ApiSpec.spec())
637 end
638
639 test "visibility/list" do
640 user = insert(:user)
641
642 {:ok, list} = Pleroma.List.create("foo", user)
643
644 {:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})
645
646 status = StatusView.render("show.json", activity: activity)
647
648 assert status.visibility == "list"
649 end
650
651 test "has a field for parent visibility" do
652 user = insert(:user)
653 poster = insert(:user)
654
655 {:ok, invisible} = CommonAPI.post(poster, %{status: "hey", visibility: "private"})
656
657 {:ok, visible} =
658 CommonAPI.post(poster, %{status: "hey", visibility: "private", in_reply_to_id: invisible.id})
659
660 status = StatusView.render("show.json", activity: visible, for: user)
661 refute status.pleroma.parent_visible
662
663 status = StatusView.render("show.json", activity: visible, for: poster)
664 assert status.pleroma.parent_visible
665 end
666 end