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",
430 remote_url: "someurl",
431 preview_url: "someurl",
434 pleroma: %{mime_type: "image/png"}
437 api_spec = Pleroma.Web.ApiSpec.spec()
439 assert expected == StatusView.render("attachment.json", %{attachment: object})
440 assert_schema(expected, "Attachment", api_spec)
442 # If theres a "id", use that instead of the generated one
443 object = Map.put(object, "id", 2)
444 result = StatusView.render("attachment.json", %{attachment: object})
446 assert %{id: "2"} = result
447 assert_schema(result, "Attachment", api_spec)
450 test "put the url advertised in the Activity in to the url attribute" do
451 id = "https://wedistribute.org/wp-json/pterotype/v1/object/85810"
452 [activity] = Activity.search(nil, id)
454 status = StatusView.render("show.json", %{activity: activity})
456 assert status.uri == id
457 assert status.url == "https://wedistribute.org/2019/07/mastodon-drops-ostatus/"
462 activity = insert(:note_activity)
464 {:ok, reblog} = CommonAPI.repeat(activity.id, user)
466 represented = StatusView.render("show.json", %{for: user, activity: reblog})
468 assert represented[:id] == to_string(reblog.id)
469 assert represented[:reblog][:id] == to_string(activity.id)
470 assert represented[:emojis] == []
471 assert_schema(represented, "Status", Pleroma.Web.ApiSpec.spec())
474 test "a peertube video" do
478 Pleroma.Object.Fetcher.fetch_object_from_id(
479 "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3"
482 %Activity{} = activity = Activity.get_create_by_object_ap_id(object.data["id"])
484 represented = StatusView.render("show.json", %{for: user, activity: activity})
486 assert represented[:id] == to_string(activity.id)
487 assert length(represented[:media_attachments]) == 1
488 assert_schema(represented, "Status", Pleroma.Web.ApiSpec.spec())
491 test "funkwhale audio" do
495 Pleroma.Object.Fetcher.fetch_object_from_id(
496 "https://channels.tests.funkwhale.audio/federation/music/uploads/42342395-0208-4fee-a38d-259a6dae0871"
499 %Activity{} = activity = Activity.get_create_by_object_ap_id(object.data["id"])
501 represented = StatusView.render("show.json", %{for: user, activity: activity})
503 assert represented[:id] == to_string(activity.id)
504 assert length(represented[:media_attachments]) == 1
507 test "a Mobilizon event" do
511 Pleroma.Object.Fetcher.fetch_object_from_id(
512 "https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39"
515 %Activity{} = activity = Activity.get_create_by_object_ap_id(object.data["id"])
517 represented = StatusView.render("show.json", %{for: user, activity: activity})
519 assert represented[:id] == to_string(activity.id)
521 assert represented[:url] ==
522 "https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39"
524 assert represented[:content] ==
525 "<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>"
528 describe "build_tags/1" do
529 test "it returns a a dictionary tags" do
535 "href" => "https://kawen.space/users/lain",
536 "name" => "@lain@kawen.space",
541 assert StatusView.build_tags(object_tags) == [
542 %{name: "fediverse", url: "/tag/fediverse"},
543 %{name: "mastodon", url: "/tag/mastodon"},
544 %{name: "nextcloud", url: "/tag/nextcloud"}
549 describe "rich media cards" do
550 test "a rich media card without a site name renders correctly" do
551 page_url = "http://example.com"
555 image: page_url <> "/example.jpg",
556 title: "Example website"
559 %{provider_name: "example.com"} =
560 StatusView.render("card.json", %{page_url: page_url, rich_media: card})
563 test "a rich media card without a site name or image renders correctly" do
564 page_url = "http://example.com"
568 title: "Example website"
571 %{provider_name: "example.com"} =
572 StatusView.render("card.json", %{page_url: page_url, rich_media: card})
575 test "a rich media card without an image renders correctly" do
576 page_url = "http://example.com"
580 site_name: "Example site name",
581 title: "Example website"
584 %{provider_name: "example.com"} =
585 StatusView.render("card.json", %{page_url: page_url, rich_media: card})
588 test "a rich media card with all relevant data renders correctly" do
589 page_url = "http://example.com"
593 site_name: "Example site name",
594 title: "Example website",
595 image: page_url <> "/example.jpg",
596 description: "Example description"
599 %{provider_name: "example.com"} =
600 StatusView.render("card.json", %{page_url: page_url, rich_media: card})
604 test "does not embed a relationship in the account" do
606 other_user = insert(:user)
609 CommonAPI.post(user, %{
610 status: "drink more water"
613 result = StatusView.render("show.json", %{activity: activity, for: other_user})
615 assert result[:account][:pleroma][:relationship] == %{}
616 assert_schema(result, "Status", Pleroma.Web.ApiSpec.spec())
619 test "does not embed a relationship in the account in reposts" do
621 other_user = insert(:user)
624 CommonAPI.post(user, %{
628 {:ok, activity} = CommonAPI.repeat(activity.id, other_user)
630 result = StatusView.render("show.json", %{activity: activity, for: user})
632 assert result[:account][:pleroma][:relationship] == %{}
633 assert result[:reblog][:account][:pleroma][:relationship] == %{}
634 assert_schema(result, "Status", Pleroma.Web.ApiSpec.spec())
637 test "visibility/list" do
640 {:ok, list} = Pleroma.List.create("foo", user)
642 {:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})
644 status = StatusView.render("show.json", activity: activity)
646 assert status.visibility == "list"
649 test "has a field for parent visibility" do
651 poster = insert(:user)
653 {:ok, invisible} = CommonAPI.post(poster, %{status: "hey", visibility: "private"})
656 CommonAPI.post(poster, %{status: "hey", visibility: "private", in_reply_to_id: invisible.id})
658 status = StatusView.render("show.json", activity: visible, for: user)
659 refute status.pleroma.parent_visible
661 status = StatusView.render("show.json", activity: visible, for: poster)
662 assert status.pleroma.parent_visible