1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
10 alias Pleroma.Conversation.Participation
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
21 import Pleroma.Factory
23 import OpenApiSpex.TestAssertions
26 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
30 test "has an emoji reaction list" do
32 other_user = insert(:user)
33 third_user = insert(:user)
34 {:ok, activity} = CommonAPI.post(user, %{status: "dae cofe??"})
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)
42 assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
44 assert status[:pleroma][:emoji_reactions] == [
45 %{name: "☕", count: 2, me: false},
46 %{name: "🍵", count: 1, me: false}
49 status = StatusView.render("show.json", activity: activity, for: user)
51 assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
53 assert status[:pleroma][:emoji_reactions] == [
54 %{name: "☕", count: 2, me: true},
55 %{name: "🍵", count: 1, me: false}
59 test "works correctly with badly formatted emojis" do
61 {:ok, activity} = CommonAPI.post(user, %{status: "yo"})
64 |> Object.normalize(false)
65 |> Object.update_data(%{"reactions" => %{"☕" => [user.ap_id], "x" => 1}})
67 activity = Activity.get_by_id(activity.id)
69 status = StatusView.render("show.json", activity: activity, for: user)
71 assert status[:pleroma][:emoji_reactions] == [
72 %{name: "☕", count: 1, me: true}
76 test "loads and returns the direct conversation id when given the `with_direct_conversation_id` option" do
79 {:ok, activity} = CommonAPI.post(user, %{status: "Hey @shp!", visibility: "direct"})
80 [participation] = Participation.for_user(user)
83 StatusView.render("show.json",
85 with_direct_conversation_id: true,
89 assert status[:pleroma][:direct_conversation_id] == participation.id
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())
96 test "returns the direct conversation id when given the `direct_conversation_id` option" do
99 {:ok, activity} = CommonAPI.post(user, %{status: "Hey @shp!", visibility: "direct"})
100 [participation] = Participation.for_user(user)
103 StatusView.render("show.json",
105 direct_conversation_id: participation.id,
109 assert status[:pleroma][:direct_conversation_id] == participation.id
110 assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
113 test "returns a temporary ap_id based user for activities missing db users" do
116 {:ok, activity} = CommonAPI.post(user, %{status: "Hey @shp!", visibility: "direct"})
119 Cachex.clear(:user_cache)
122 "https://localhost/.well-known/webfinger?resource=acct:#{user.nickname}@localhost"
124 Tesla.Mock.mock_global(fn
125 %{method: :get, url: "http://localhost/.well-known/host-meta"} ->
126 %Tesla.Env{status: 404, body: ""}
128 %{method: :get, url: "https://localhost/.well-known/host-meta"} ->
129 %Tesla.Env{status: 404, body: ""}
135 %Tesla.Env{status: 404, body: ""}
138 %{account: ms_user} = StatusView.render("show.json", activity: activity)
140 assert ms_user.acct == "erroruser@example.com"
143 test "tries to get a user by nickname if fetching by ap_id doesn't work" do
146 {:ok, activity} = CommonAPI.post(user, %{status: "Hey @shp!", visibility: "direct"})
150 |> Ecto.Changeset.change(%{ap_id: "#{user.ap_id}/extension/#{user.nickname}"})
153 Cachex.clear(:user_cache)
155 result = StatusView.render("show.json", activity: activity)
157 assert result[:account][:id] == to_string(user.id)
158 assert_schema(result, "Status", Pleroma.Web.ApiSpec.spec())
161 test "a note with null content" do
162 note = insert(:note_activity)
163 note_object = Object.normalize(note)
167 |> Map.put("content", nil)
169 Object.change(note_object, %{data: data})
170 |> Object.update_and_set_cache()
172 User.get_cached_by_ap_id(note.data["actor"])
174 status = StatusView.render("show.json", %{activity: note})
176 assert status.content == ""
177 assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
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"])
185 convo_id = Utils.context_to_conversation_id(object_data["context"])
187 status = StatusView.render("show.json", %{activity: note})
190 (object_data["published"] || "")
191 |> String.replace(~r/\.\d+Z/, ".000Z")
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}),
199 in_reply_to_account_id: nil,
202 content: HTML.filter_tags(object_data["content"]),
204 created_at: created_at,
215 spoiler_text: HTML.filter_tags(object_data["summary"]),
216 visibility: "public",
217 media_attachments: [],
221 name: "#{object_data["tag"]}",
222 url: "/tag/#{object_data["tag"]}"
234 static_url: "corndog.png",
235 visible_in_picker: false
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"])},
245 direct_conversation_id: nil,
248 parent_visible: false
252 assert status == expected
253 assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
256 test "tells if the message is muted for some reason" do
258 other_user = insert(:user)
260 {:ok, _user_relationships} = User.mute(user, other_user)
262 {:ok, activity} = CommonAPI.post(other_user, %{status: "test"})
264 relationships_opt = UserRelationship.view_relationships_option(user, [other_user])
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())
271 status = StatusView.render("show.json", Map.put(opts, :relationships, relationships_opt))
272 assert status.muted == false
274 for_opts = %{activity: activity, for: user}
275 status = StatusView.render("show.json", for_opts)
276 assert status.muted == true
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())
283 test "tells if the message is thread muted" do
285 other_user = insert(:user)
287 {:ok, _user_relationships} = User.mute(user, other_user)
289 {:ok, activity} = CommonAPI.post(other_user, %{status: "test"})
290 status = StatusView.render("show.json", %{activity: activity, for: user})
292 assert status.pleroma.thread_muted == false
294 {:ok, activity} = CommonAPI.add_mute(user, activity)
296 status = StatusView.render("show.json", %{activity: activity, for: user})
298 assert status.pleroma.thread_muted == true
301 test "tells if the status is bookmarked" do
304 {:ok, activity} = CommonAPI.post(user, %{status: "Cute girls doing cute things"})
305 status = StatusView.render("show.json", %{activity: activity})
307 assert status.bookmarked == false
309 status = StatusView.render("show.json", %{activity: activity, for: user})
311 assert status.bookmarked == false
313 {:ok, _bookmark} = Bookmark.create(user.id, activity.id)
315 activity = Activity.get_by_id_with_object(activity.id)
317 status = StatusView.render("show.json", %{activity: activity, for: user})
319 assert status.bookmarked == true
323 note = insert(:note_activity)
326 {:ok, activity} = CommonAPI.post(user, %{status: "he", in_reply_to_status_id: note.id})
328 status = StatusView.render("show.json", %{activity: activity})
330 assert status.in_reply_to_id == to_string(note.id)
332 [status] = StatusView.render("index.json", %{activities: [activity], as: :activity})
334 assert status.in_reply_to_id == to_string(note.id)
337 test "contains mentions" do
339 mentioned = insert(:user)
341 {:ok, activity} = CommonAPI.post(user, %{status: "hi @#{mentioned.nickname}"})
343 status = StatusView.render("show.json", %{activity: activity})
345 assert status.mentions ==
346 Enum.map([mentioned], fn u -> AccountView.render("mention.json", %{user: u}) end)
348 assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
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)
358 "to" => [recipient_ap_id],
364 insert(:note_activity, %{
366 recipients: [recipient_ap_id | cc]
369 assert length(activity.recipients) == 3
371 %{mentions: [mention] = mentions} = StatusView.render("show.json", %{activity: activity})
373 assert length(mentions) == 1
374 assert mention.url == recipient_ap_id
377 test "create mentions from the 'tag' field" do
378 recipient = insert(:user)
379 cc = insert_pair(:user) |> Enum.map(& &1.ap_id)
387 "href" => recipient.ap_id,
388 "name" => recipient.nickname,
392 "href" => "https://example.com/search?tag=test",
401 insert(:note_activity, %{
403 recipients: [recipient.ap_id | cc]
406 assert length(activity.recipients) == 3
408 %{mentions: [mention] = mentions} = StatusView.render("show.json", %{activity: activity})
410 assert length(mentions) == 1
411 assert mention.url == recipient.ap_id
414 test "attachments" do
419 "mediaType" => "image/png",
423 "blurhash" => "UJJ8X[xYW,%Jtq%NNFbXB5j]IVM|9GV=WHRn",
431 remote_url: "someurl",
432 preview_url: "someurl",
435 pleroma: %{mime_type: "image/png"},
436 blurhash: "UJJ8X[xYW,%Jtq%NNFbXB5j]IVM|9GV=WHRn"
439 api_spec = Pleroma.Web.ApiSpec.spec()
441 assert expected == StatusView.render("attachment.json", %{attachment: object})
442 assert_schema(expected, "Attachment", api_spec)
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})
448 assert %{id: "2"} = result
449 assert_schema(result, "Attachment", api_spec)
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)
456 status = StatusView.render("show.json", %{activity: activity})
458 assert status.uri == id
459 assert status.url == "https://wedistribute.org/2019/07/mastodon-drops-ostatus/"
464 activity = insert(:note_activity)
466 {:ok, reblog} = CommonAPI.repeat(activity.id, user)
468 represented = StatusView.render("show.json", %{for: user, activity: reblog})
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())
476 test "a peertube video" do
480 Pleroma.Object.Fetcher.fetch_object_from_id(
481 "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3"
484 %Activity{} = activity = Activity.get_create_by_object_ap_id(object.data["id"])
486 represented = StatusView.render("show.json", %{for: user, activity: activity})
488 assert represented[:id] == to_string(activity.id)
489 assert length(represented[:media_attachments]) == 1
490 assert_schema(represented, "Status", Pleroma.Web.ApiSpec.spec())
493 test "funkwhale audio" do
497 Pleroma.Object.Fetcher.fetch_object_from_id(
498 "https://channels.tests.funkwhale.audio/federation/music/uploads/42342395-0208-4fee-a38d-259a6dae0871"
501 %Activity{} = activity = Activity.get_create_by_object_ap_id(object.data["id"])
503 represented = StatusView.render("show.json", %{for: user, activity: activity})
505 assert represented[:id] == to_string(activity.id)
506 assert length(represented[:media_attachments]) == 1
509 test "a Mobilizon event" do
513 Pleroma.Object.Fetcher.fetch_object_from_id(
514 "https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39"
517 %Activity{} = activity = Activity.get_create_by_object_ap_id(object.data["id"])
519 represented = StatusView.render("show.json", %{for: user, activity: activity})
521 assert represented[:id] == to_string(activity.id)
523 assert represented[:url] ==
524 "https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39"
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's still <u>a work in progress</u>: if reports made from an instance on events and comments can be federated, you can'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't advise to do that for now, we have a little documentation but it's quite the early days and you'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>"
530 describe "build_tags/1" do
531 test "it returns a a dictionary tags" do
537 "href" => "https://kawen.space/users/lain",
538 "name" => "@lain@kawen.space",
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"}
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"
557 image: page_url <> "/example.jpg",
558 title: "Example website"
561 %{provider_name: "example.com"} =
562 StatusView.render("card.json", %{page_url: page_url, rich_media: card})
565 test "a rich media card without a site name or image renders correctly" do
566 page_url = "http://example.com"
570 title: "Example website"
573 %{provider_name: "example.com"} =
574 StatusView.render("card.json", %{page_url: page_url, rich_media: card})
577 test "a rich media card without an image renders correctly" do
578 page_url = "http://example.com"
582 site_name: "Example site name",
583 title: "Example website"
586 %{provider_name: "example.com"} =
587 StatusView.render("card.json", %{page_url: page_url, rich_media: card})
590 test "a rich media card with all relevant data renders correctly" do
591 page_url = "http://example.com"
595 site_name: "Example site name",
596 title: "Example website",
597 image: page_url <> "/example.jpg",
598 description: "Example description"
601 %{provider_name: "example.com"} =
602 StatusView.render("card.json", %{page_url: page_url, rich_media: card})
606 test "does not embed a relationship in the account" do
608 other_user = insert(:user)
611 CommonAPI.post(user, %{
612 status: "drink more water"
615 result = StatusView.render("show.json", %{activity: activity, for: other_user})
617 assert result[:account][:pleroma][:relationship] == %{}
618 assert_schema(result, "Status", Pleroma.Web.ApiSpec.spec())
621 test "does not embed a relationship in the account in reposts" do
623 other_user = insert(:user)
626 CommonAPI.post(user, %{
630 {:ok, activity} = CommonAPI.repeat(activity.id, other_user)
632 result = StatusView.render("show.json", %{activity: activity, for: user})
634 assert result[:account][:pleroma][:relationship] == %{}
635 assert result[:reblog][:account][:pleroma][:relationship] == %{}
636 assert_schema(result, "Status", Pleroma.Web.ApiSpec.spec())
639 test "visibility/list" do
642 {:ok, list} = Pleroma.List.create("foo", user)
644 {:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})
646 status = StatusView.render("show.json", activity: activity)
648 assert status.visibility == "list"
651 test "has a field for parent visibility" do
653 poster = insert(:user)
655 {:ok, invisible} = CommonAPI.post(poster, %{status: "hey", visibility: "private"})
658 CommonAPI.post(poster, %{status: "hey", visibility: "private", in_reply_to_id: invisible.id})
660 status = StatusView.render("show.json", activity: visible, for: user)
661 refute status.pleroma.parent_visible
663 status = StatusView.render("show.json", activity: visible, for: poster)
664 assert status.pleroma.parent_visible