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,
253 assert status == expected
254 assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
257 test "tells if the message is muted for some reason" do
259 other_user = insert(:user)
261 {:ok, _user_relationships} = User.mute(user, other_user)
263 {:ok, activity} = CommonAPI.post(other_user, %{status: "test"})
265 relationships_opt = UserRelationship.view_relationships_option(user, [other_user])
267 opts = %{activity: activity}
268 status = StatusView.render("show.json", opts)
269 assert status.muted == false
270 assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
272 status = StatusView.render("show.json", Map.put(opts, :relationships, relationships_opt))
273 assert status.muted == false
275 for_opts = %{activity: activity, for: user}
276 status = StatusView.render("show.json", for_opts)
277 assert status.muted == true
279 status = StatusView.render("show.json", Map.put(for_opts, :relationships, relationships_opt))
280 assert status.muted == true
281 assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
284 test "tells if the message is thread muted" do
286 other_user = insert(:user)
288 {:ok, _user_relationships} = User.mute(user, other_user)
290 {:ok, activity} = CommonAPI.post(other_user, %{status: "test"})
291 status = StatusView.render("show.json", %{activity: activity, for: user})
293 assert status.pleroma.thread_muted == false
295 {:ok, activity} = CommonAPI.add_mute(user, activity)
297 status = StatusView.render("show.json", %{activity: activity, for: user})
299 assert status.pleroma.thread_muted == true
302 test "tells if the status is bookmarked" do
305 {:ok, activity} = CommonAPI.post(user, %{status: "Cute girls doing cute things"})
306 status = StatusView.render("show.json", %{activity: activity})
308 assert status.bookmarked == false
310 status = StatusView.render("show.json", %{activity: activity, for: user})
312 assert status.bookmarked == false
314 {:ok, _bookmark} = Bookmark.create(user.id, activity.id)
316 activity = Activity.get_by_id_with_object(activity.id)
318 status = StatusView.render("show.json", %{activity: activity, for: user})
320 assert status.bookmarked == true
324 note = insert(:note_activity)
327 {:ok, activity} = CommonAPI.post(user, %{status: "he", in_reply_to_status_id: note.id})
329 status = StatusView.render("show.json", %{activity: activity})
331 assert status.in_reply_to_id == to_string(note.id)
333 [status] = StatusView.render("index.json", %{activities: [activity], as: :activity})
335 assert status.in_reply_to_id == to_string(note.id)
338 test "contains mentions" do
340 mentioned = insert(:user)
342 {:ok, activity} = CommonAPI.post(user, %{status: "hi @#{mentioned.nickname}"})
344 status = StatusView.render("show.json", %{activity: activity})
346 assert status.mentions ==
347 Enum.map([mentioned], fn u -> AccountView.render("mention.json", %{user: u}) end)
349 assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
352 test "create mentions from the 'to' field" do
353 %User{ap_id: recipient_ap_id} = insert(:user)
354 cc = insert_pair(:user) |> Enum.map(& &1.ap_id)
359 "to" => [recipient_ap_id],
365 insert(:note_activity, %{
367 recipients: [recipient_ap_id | cc]
370 assert length(activity.recipients) == 3
372 %{mentions: [mention] = mentions} = StatusView.render("show.json", %{activity: activity})
374 assert length(mentions) == 1
375 assert mention.url == recipient_ap_id
378 test "create mentions from the 'tag' field" do
379 recipient = insert(:user)
380 cc = insert_pair(:user) |> Enum.map(& &1.ap_id)
388 "href" => recipient.ap_id,
389 "name" => recipient.nickname,
393 "href" => "https://example.com/search?tag=test",
402 insert(:note_activity, %{
404 recipients: [recipient.ap_id | cc]
407 assert length(activity.recipients) == 3
409 %{mentions: [mention] = mentions} = StatusView.render("show.json", %{activity: activity})
411 assert length(mentions) == 1
412 assert mention.url == recipient.ap_id
415 test "attachments" do
420 "mediaType" => "image/png",
431 remote_url: "someurl",
432 preview_url: "someurl",
435 pleroma: %{mime_type: "image/png"}
438 api_spec = Pleroma.Web.ApiSpec.spec()
440 assert expected == StatusView.render("attachment.json", %{attachment: object})
441 assert_schema(expected, "Attachment", api_spec)
443 # If theres a "id", use that instead of the generated one
444 object = Map.put(object, "id", 2)
445 result = StatusView.render("attachment.json", %{attachment: object})
447 assert %{id: "2"} = result
448 assert_schema(result, "Attachment", api_spec)
451 test "put the url advertised in the Activity in to the url attribute" do
452 id = "https://wedistribute.org/wp-json/pterotype/v1/object/85810"
453 [activity] = Activity.search(nil, id)
455 status = StatusView.render("show.json", %{activity: activity})
457 assert status.uri == id
458 assert status.url == "https://wedistribute.org/2019/07/mastodon-drops-ostatus/"
463 activity = insert(:note_activity)
465 {:ok, reblog} = CommonAPI.repeat(activity.id, user)
467 represented = StatusView.render("show.json", %{for: user, activity: reblog})
469 assert represented[:id] == to_string(reblog.id)
470 assert represented[:reblog][:id] == to_string(activity.id)
471 assert represented[:emojis] == []
472 assert_schema(represented, "Status", Pleroma.Web.ApiSpec.spec())
475 test "a peertube video" do
479 Pleroma.Object.Fetcher.fetch_object_from_id(
480 "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3"
483 %Activity{} = activity = Activity.get_create_by_object_ap_id(object.data["id"])
485 represented = StatusView.render("show.json", %{for: user, activity: activity})
487 assert represented[:id] == to_string(activity.id)
488 assert length(represented[:media_attachments]) == 1
489 assert_schema(represented, "Status", Pleroma.Web.ApiSpec.spec())
492 test "funkwhale audio" do
496 Pleroma.Object.Fetcher.fetch_object_from_id(
497 "https://channels.tests.funkwhale.audio/federation/music/uploads/42342395-0208-4fee-a38d-259a6dae0871"
500 %Activity{} = activity = Activity.get_create_by_object_ap_id(object.data["id"])
502 represented = StatusView.render("show.json", %{for: user, activity: activity})
504 assert represented[:id] == to_string(activity.id)
505 assert length(represented[:media_attachments]) == 1
508 test "a Mobilizon event" do
512 Pleroma.Object.Fetcher.fetch_object_from_id(
513 "https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39"
516 %Activity{} = activity = Activity.get_create_by_object_ap_id(object.data["id"])
518 represented = StatusView.render("show.json", %{for: user, activity: activity})
520 assert represented[:id] == to_string(activity.id)
522 assert represented[:url] ==
523 "https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39"
525 assert represented[:content] ==
526 "<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>"
529 describe "build_tags/1" do
530 test "it returns a a dictionary tags" do
536 "href" => "https://kawen.space/users/lain",
537 "name" => "@lain@kawen.space",
542 assert StatusView.build_tags(object_tags) == [
543 %{name: "fediverse", url: "/tag/fediverse"},
544 %{name: "mastodon", url: "/tag/mastodon"},
545 %{name: "nextcloud", url: "/tag/nextcloud"}
550 describe "rich media cards" do
551 test "a rich media card without a site name renders correctly" do
552 page_url = "http://example.com"
556 image: page_url <> "/example.jpg",
557 title: "Example website"
560 %{provider_name: "example.com"} =
561 StatusView.render("card.json", %{page_url: page_url, rich_media: card})
564 test "a rich media card without a site name or image renders correctly" do
565 page_url = "http://example.com"
569 title: "Example website"
572 %{provider_name: "example.com"} =
573 StatusView.render("card.json", %{page_url: page_url, rich_media: card})
576 test "a rich media card without an image renders correctly" do
577 page_url = "http://example.com"
581 site_name: "Example site name",
582 title: "Example website"
585 %{provider_name: "example.com"} =
586 StatusView.render("card.json", %{page_url: page_url, rich_media: card})
589 test "a rich media card with all relevant data renders correctly" do
590 page_url = "http://example.com"
594 site_name: "Example site name",
595 title: "Example website",
596 image: page_url <> "/example.jpg",
597 description: "Example description"
600 %{provider_name: "example.com"} =
601 StatusView.render("card.json", %{page_url: page_url, rich_media: card})
605 test "does not embed a relationship in the account" do
607 other_user = insert(:user)
610 CommonAPI.post(user, %{
611 status: "drink more water"
614 result = StatusView.render("show.json", %{activity: activity, for: other_user})
616 assert result[:account][:pleroma][:relationship] == %{}
617 assert_schema(result, "Status", Pleroma.Web.ApiSpec.spec())
620 test "does not embed a relationship in the account in reposts" do
622 other_user = insert(:user)
625 CommonAPI.post(user, %{
629 {:ok, activity} = CommonAPI.repeat(activity.id, other_user)
631 result = StatusView.render("show.json", %{activity: activity, for: user})
633 assert result[:account][:pleroma][:relationship] == %{}
634 assert result[:reblog][:account][:pleroma][:relationship] == %{}
635 assert_schema(result, "Status", Pleroma.Web.ApiSpec.spec())
638 test "visibility/list" do
641 {:ok, list} = Pleroma.List.create("foo", user)
643 {:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})
645 status = StatusView.render("show.json", activity: activity)
647 assert status.visibility == "list"
650 test "has a field for parent visibility" do
652 poster = insert(:user)
654 {:ok, invisible} = CommonAPI.post(poster, %{status: "hey", visibility: "private"})
657 CommonAPI.post(poster, %{status: "hey", visibility: "private", in_reply_to_id: invisible.id})
659 status = StatusView.render("show.json", activity: visible, for: user)
660 refute status.pleroma.parent_visible
662 status = StatusView.render("show.json", activity: visible, for: poster)
663 assert status.pleroma.parent_visible