logo

pleroma

My custom branche(s) on git.pleroma.social/pleroma/pleroma

status_view_test.exs (16479B)


      1 # Pleroma: A lightweight social networking server
      2 # Copyright © 2017-2018 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.Object
     11   alias Pleroma.Repo
     12   alias Pleroma.User
     13   alias Pleroma.Web.CommonAPI
     14   alias Pleroma.Web.CommonAPI.Utils
     15   alias Pleroma.Web.MastodonAPI.AccountView
     16   alias Pleroma.Web.MastodonAPI.StatusView
     17   alias Pleroma.Web.OStatus
     18   import Pleroma.Factory
     19   import Tesla.Mock
     20 
     21   setup do
     22     mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
     23     :ok
     24   end
     25 
     26   test "returns the direct conversation id when given the `with_conversation_id` option" do
     27     user = insert(:user)
     28 
     29     {:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!", "visibility" => "direct"})
     30 
     31     status =
     32       StatusView.render("status.json",
     33         activity: activity,
     34         with_direct_conversation_id: true,
     35         for: user
     36       )
     37 
     38     assert status[:pleroma][:direct_conversation_id]
     39   end
     40 
     41   test "returns a temporary ap_id based user for activities missing db users" do
     42     user = insert(:user)
     43 
     44     {:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!", "visibility" => "direct"})
     45 
     46     Repo.delete(user)
     47     Cachex.clear(:user_cache)
     48 
     49     %{account: ms_user} = StatusView.render("status.json", activity: activity)
     50 
     51     assert ms_user.acct == "erroruser@example.com"
     52   end
     53 
     54   test "tries to get a user by nickname if fetching by ap_id doesn't work" do
     55     user = insert(:user)
     56 
     57     {:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!", "visibility" => "direct"})
     58 
     59     {:ok, user} =
     60       user
     61       |> Ecto.Changeset.change(%{ap_id: "#{user.ap_id}/extension/#{user.nickname}"})
     62       |> Repo.update()
     63 
     64     Cachex.clear(:user_cache)
     65 
     66     result = StatusView.render("status.json", activity: activity)
     67 
     68     assert result[:account][:id] == to_string(user.id)
     69   end
     70 
     71   test "a note with null content" do
     72     note = insert(:note_activity)
     73     note_object = Object.normalize(note)
     74 
     75     data =
     76       note_object.data
     77       |> Map.put("content", nil)
     78 
     79     Object.change(note_object, %{data: data})
     80     |> Object.update_and_set_cache()
     81 
     82     User.get_cached_by_ap_id(note.data["actor"])
     83 
     84     status = StatusView.render("status.json", %{activity: note})
     85 
     86     assert status.content == ""
     87   end
     88 
     89   test "a note activity" do
     90     note = insert(:note_activity)
     91     object_data = Object.normalize(note).data
     92     user = User.get_cached_by_ap_id(note.data["actor"])
     93 
     94     convo_id = Utils.context_to_conversation_id(object_data["context"])
     95 
     96     status = StatusView.render("status.json", %{activity: note})
     97 
     98     created_at =
     99       (object_data["published"] || "")
    100       |> String.replace(~r/\.\d+Z/, ".000Z")
    101 
    102     expected = %{
    103       id: to_string(note.id),
    104       uri: object_data["id"],
    105       url: Pleroma.Web.Router.Helpers.o_status_url(Pleroma.Web.Endpoint, :notice, note),
    106       account: AccountView.render("account.json", %{user: user}),
    107       in_reply_to_id: nil,
    108       in_reply_to_account_id: nil,
    109       card: nil,
    110       reblog: nil,
    111       content: HtmlSanitizeEx.basic_html(object_data["content"]),
    112       created_at: created_at,
    113       reblogs_count: 0,
    114       replies_count: 0,
    115       favourites_count: 0,
    116       reblogged: false,
    117       bookmarked: false,
    118       favourited: false,
    119       muted: false,
    120       pinned: false,
    121       sensitive: false,
    122       poll: nil,
    123       spoiler_text: HtmlSanitizeEx.basic_html(object_data["summary"]),
    124       visibility: "public",
    125       media_attachments: [],
    126       mentions: [],
    127       tags: [
    128         %{
    129           name: "#{object_data["tag"]}",
    130           url: "/tag/#{object_data["tag"]}"
    131         }
    132       ],
    133       application: %{
    134         name: "Web",
    135         website: nil
    136       },
    137       language: nil,
    138       emojis: [
    139         %{
    140           shortcode: "2hu",
    141           url: "corndog.png",
    142           static_url: "corndog.png",
    143           visible_in_picker: false
    144         }
    145       ],
    146       pleroma: %{
    147         local: true,
    148         conversation_id: convo_id,
    149         in_reply_to_account_acct: nil,
    150         content: %{"text/plain" => HtmlSanitizeEx.strip_tags(object_data["content"])},
    151         spoiler_text: %{"text/plain" => HtmlSanitizeEx.strip_tags(object_data["summary"])},
    152         expires_at: nil,
    153         direct_conversation_id: nil
    154       }
    155     }
    156 
    157     assert status == expected
    158   end
    159 
    160   test "tells if the message is muted for some reason" do
    161     user = insert(:user)
    162     other_user = insert(:user)
    163 
    164     {:ok, user} = User.mute(user, other_user)
    165 
    166     {:ok, activity} = CommonAPI.post(other_user, %{"status" => "test"})
    167     status = StatusView.render("status.json", %{activity: activity})
    168 
    169     assert status.muted == false
    170 
    171     status = StatusView.render("status.json", %{activity: activity, for: user})
    172 
    173     assert status.muted == true
    174   end
    175 
    176   test "tells if the status is bookmarked" do
    177     user = insert(:user)
    178 
    179     {:ok, activity} = CommonAPI.post(user, %{"status" => "Cute girls doing cute things"})
    180     status = StatusView.render("status.json", %{activity: activity})
    181 
    182     assert status.bookmarked == false
    183 
    184     status = StatusView.render("status.json", %{activity: activity, for: user})
    185 
    186     assert status.bookmarked == false
    187 
    188     {:ok, _bookmark} = Bookmark.create(user.id, activity.id)
    189 
    190     activity = Activity.get_by_id_with_object(activity.id)
    191 
    192     status = StatusView.render("status.json", %{activity: activity, for: user})
    193 
    194     assert status.bookmarked == true
    195   end
    196 
    197   test "a reply" do
    198     note = insert(:note_activity)
    199     user = insert(:user)
    200 
    201     {:ok, activity} =
    202       CommonAPI.post(user, %{"status" => "he", "in_reply_to_status_id" => note.id})
    203 
    204     status = StatusView.render("status.json", %{activity: activity})
    205 
    206     assert status.in_reply_to_id == to_string(note.id)
    207 
    208     [status] = StatusView.render("index.json", %{activities: [activity], as: :activity})
    209 
    210     assert status.in_reply_to_id == to_string(note.id)
    211   end
    212 
    213   test "contains mentions" do
    214     incoming = File.read!("test/fixtures/incoming_reply_mastodon.xml")
    215     # a user with this ap id might be in the cache.
    216     recipient = "https://pleroma.soykaf.com/users/lain"
    217     user = insert(:user, %{ap_id: recipient})
    218 
    219     {:ok, [activity]} = OStatus.handle_incoming(incoming)
    220 
    221     status = StatusView.render("status.json", %{activity: activity})
    222 
    223     assert status.mentions ==
    224              Enum.map([user], fn u -> AccountView.render("mention.json", %{user: u}) end)
    225   end
    226 
    227   test "create mentions from the 'to' field" do
    228     %User{ap_id: recipient_ap_id} = insert(:user)
    229     cc = insert_pair(:user) |> Enum.map(& &1.ap_id)
    230 
    231     object =
    232       insert(:note, %{
    233         data: %{
    234           "to" => [recipient_ap_id],
    235           "cc" => cc
    236         }
    237       })
    238 
    239     activity =
    240       insert(:note_activity, %{
    241         note: object,
    242         recipients: [recipient_ap_id | cc]
    243       })
    244 
    245     assert length(activity.recipients) == 3
    246 
    247     %{mentions: [mention] = mentions} = StatusView.render("status.json", %{activity: activity})
    248 
    249     assert length(mentions) == 1
    250     assert mention.url == recipient_ap_id
    251   end
    252 
    253   test "create mentions from the 'tag' field" do
    254     recipient = insert(:user)
    255     cc = insert_pair(:user) |> Enum.map(& &1.ap_id)
    256 
    257     object =
    258       insert(:note, %{
    259         data: %{
    260           "cc" => cc,
    261           "tag" => [
    262             %{
    263               "href" => recipient.ap_id,
    264               "name" => recipient.nickname,
    265               "type" => "Mention"
    266             },
    267             %{
    268               "href" => "https://example.com/search?tag=test",
    269               "name" => "#test",
    270               "type" => "Hashtag"
    271             }
    272           ]
    273         }
    274       })
    275 
    276     activity =
    277       insert(:note_activity, %{
    278         note: object,
    279         recipients: [recipient.ap_id | cc]
    280       })
    281 
    282     assert length(activity.recipients) == 3
    283 
    284     %{mentions: [mention] = mentions} = StatusView.render("status.json", %{activity: activity})
    285 
    286     assert length(mentions) == 1
    287     assert mention.url == recipient.ap_id
    288   end
    289 
    290   test "attachments" do
    291     object = %{
    292       "type" => "Image",
    293       "url" => [
    294         %{
    295           "mediaType" => "image/png",
    296           "href" => "someurl"
    297         }
    298       ],
    299       "uuid" => 6
    300     }
    301 
    302     expected = %{
    303       id: "1638338801",
    304       type: "image",
    305       url: "someurl",
    306       remote_url: "someurl",
    307       preview_url: "someurl",
    308       text_url: "someurl",
    309       description: nil,
    310       pleroma: %{mime_type: "image/png"}
    311     }
    312 
    313     assert expected == StatusView.render("attachment.json", %{attachment: object})
    314 
    315     # If theres a "id", use that instead of the generated one
    316     object = Map.put(object, "id", 2)
    317     assert %{id: "2"} = StatusView.render("attachment.json", %{attachment: object})
    318   end
    319 
    320   test "put the url advertised in the Activity in to the url attribute" do
    321     id = "https://wedistribute.org/wp-json/pterotype/v1/object/85810"
    322     [activity] = Activity.search(nil, id)
    323 
    324     status = StatusView.render("status.json", %{activity: activity})
    325 
    326     assert status.uri == id
    327     assert status.url == "https://wedistribute.org/2019/07/mastodon-drops-ostatus/"
    328   end
    329 
    330   test "a reblog" do
    331     user = insert(:user)
    332     activity = insert(:note_activity)
    333 
    334     {:ok, reblog, _} = CommonAPI.repeat(activity.id, user)
    335 
    336     represented = StatusView.render("status.json", %{for: user, activity: reblog})
    337 
    338     assert represented[:id] == to_string(reblog.id)
    339     assert represented[:reblog][:id] == to_string(activity.id)
    340     assert represented[:emojis] == []
    341   end
    342 
    343   test "a peertube video" do
    344     user = insert(:user)
    345 
    346     {:ok, object} =
    347       Pleroma.Object.Fetcher.fetch_object_from_id(
    348         "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3"
    349       )
    350 
    351     %Activity{} = activity = Activity.get_create_by_object_ap_id(object.data["id"])
    352 
    353     represented = StatusView.render("status.json", %{for: user, activity: activity})
    354 
    355     assert represented[:id] == to_string(activity.id)
    356     assert length(represented[:media_attachments]) == 1
    357   end
    358 
    359   describe "build_tags/1" do
    360     test "it returns a a dictionary tags" do
    361       object_tags = [
    362         "fediverse",
    363         "mastodon",
    364         "nextcloud",
    365         %{
    366           "href" => "https://kawen.space/users/lain",
    367           "name" => "@lain@kawen.space",
    368           "type" => "Mention"
    369         }
    370       ]
    371 
    372       assert StatusView.build_tags(object_tags) == [
    373                %{name: "fediverse", url: "/tag/fediverse"},
    374                %{name: "mastodon", url: "/tag/mastodon"},
    375                %{name: "nextcloud", url: "/tag/nextcloud"}
    376              ]
    377     end
    378   end
    379 
    380   describe "rich media cards" do
    381     test "a rich media card without a site name renders correctly" do
    382       page_url = "http://example.com"
    383 
    384       card = %{
    385         url: page_url,
    386         image: page_url <> "/example.jpg",
    387         title: "Example website"
    388       }
    389 
    390       %{provider_name: "example.com"} =
    391         StatusView.render("card.json", %{page_url: page_url, rich_media: card})
    392     end
    393 
    394     test "a rich media card without a site name or image renders correctly" do
    395       page_url = "http://example.com"
    396 
    397       card = %{
    398         url: page_url,
    399         title: "Example website"
    400       }
    401 
    402       %{provider_name: "example.com"} =
    403         StatusView.render("card.json", %{page_url: page_url, rich_media: card})
    404     end
    405 
    406     test "a rich media card without an image renders correctly" do
    407       page_url = "http://example.com"
    408 
    409       card = %{
    410         url: page_url,
    411         site_name: "Example site name",
    412         title: "Example website"
    413       }
    414 
    415       %{provider_name: "Example site name"} =
    416         StatusView.render("card.json", %{page_url: page_url, rich_media: card})
    417     end
    418 
    419     test "a rich media card with all relevant data renders correctly" do
    420       page_url = "http://example.com"
    421 
    422       card = %{
    423         url: page_url,
    424         site_name: "Example site name",
    425         title: "Example website",
    426         image: page_url <> "/example.jpg",
    427         description: "Example description"
    428       }
    429 
    430       %{provider_name: "Example site name"} =
    431         StatusView.render("card.json", %{page_url: page_url, rich_media: card})
    432     end
    433   end
    434 
    435   describe "poll view" do
    436     test "renders a poll" do
    437       user = insert(:user)
    438 
    439       {:ok, activity} =
    440         CommonAPI.post(user, %{
    441           "status" => "Is Tenshi eating a corndog cute?",
    442           "poll" => %{
    443             "options" => ["absolutely!", "sure", "yes", "why are you even asking?"],
    444             "expires_in" => 20
    445           }
    446         })
    447 
    448       object = Object.normalize(activity)
    449 
    450       expected = %{
    451         emojis: [],
    452         expired: false,
    453         id: to_string(object.id),
    454         multiple: false,
    455         options: [
    456           %{title: "absolutely!", votes_count: 0},
    457           %{title: "sure", votes_count: 0},
    458           %{title: "yes", votes_count: 0},
    459           %{title: "why are you even asking?", votes_count: 0}
    460         ],
    461         voted: false,
    462         votes_count: 0
    463       }
    464 
    465       result = StatusView.render("poll.json", %{object: object})
    466       expires_at = result.expires_at
    467       result = Map.delete(result, :expires_at)
    468 
    469       assert result == expected
    470 
    471       expires_at = NaiveDateTime.from_iso8601!(expires_at)
    472       assert NaiveDateTime.diff(expires_at, NaiveDateTime.utc_now()) in 15..20
    473     end
    474 
    475     test "detects if it is multiple choice" do
    476       user = insert(:user)
    477 
    478       {:ok, activity} =
    479         CommonAPI.post(user, %{
    480           "status" => "Which Mastodon developer is your favourite?",
    481           "poll" => %{
    482             "options" => ["Gargron", "Eugen"],
    483             "expires_in" => 20,
    484             "multiple" => true
    485           }
    486         })
    487 
    488       object = Object.normalize(activity)
    489 
    490       assert %{multiple: true} = StatusView.render("poll.json", %{object: object})
    491     end
    492 
    493     test "detects emoji" do
    494       user = insert(:user)
    495 
    496       {:ok, activity} =
    497         CommonAPI.post(user, %{
    498           "status" => "What's with the smug face?",
    499           "poll" => %{
    500             "options" => [":blank: sip", ":blank::blank: sip", ":blank::blank::blank: sip"],
    501             "expires_in" => 20
    502           }
    503         })
    504 
    505       object = Object.normalize(activity)
    506 
    507       assert %{emojis: [%{shortcode: "blank"}]} =
    508                StatusView.render("poll.json", %{object: object})
    509     end
    510 
    511     test "detects vote status" do
    512       user = insert(:user)
    513       other_user = insert(:user)
    514 
    515       {:ok, activity} =
    516         CommonAPI.post(user, %{
    517           "status" => "Which input devices do you use?",
    518           "poll" => %{
    519             "options" => ["mouse", "trackball", "trackpoint"],
    520             "multiple" => true,
    521             "expires_in" => 20
    522           }
    523         })
    524 
    525       object = Object.normalize(activity)
    526 
    527       {:ok, _, object} = CommonAPI.vote(other_user, object, [1, 2])
    528 
    529       result = StatusView.render("poll.json", %{object: object, for: other_user})
    530 
    531       assert result[:voted] == true
    532       assert Enum.at(result[:options], 1)[:votes_count] == 1
    533       assert Enum.at(result[:options], 2)[:votes_count] == 1
    534     end
    535   end
    536 
    537   test "embeds a relationship in the account" do
    538     user = insert(:user)
    539     other_user = insert(:user)
    540 
    541     {:ok, activity} =
    542       CommonAPI.post(user, %{
    543         "status" => "drink more water"
    544       })
    545 
    546     result = StatusView.render("status.json", %{activity: activity, for: other_user})
    547 
    548     assert result[:account][:pleroma][:relationship] ==
    549              AccountView.render("relationship.json", %{user: other_user, target: user})
    550   end
    551 
    552   test "embeds a relationship in the account in reposts" do
    553     user = insert(:user)
    554     other_user = insert(:user)
    555 
    556     {:ok, activity} =
    557       CommonAPI.post(user, %{
    558         "status" => "˙˙ɐʎns"
    559       })
    560 
    561     {:ok, activity, _object} = CommonAPI.repeat(activity.id, other_user)
    562 
    563     result = StatusView.render("status.json", %{activity: activity, for: user})
    564 
    565     assert result[:account][:pleroma][:relationship] ==
    566              AccountView.render("relationship.json", %{user: user, target: other_user})
    567 
    568     assert result[:reblog][:account][:pleroma][:relationship] ==
    569              AccountView.render("relationship.json", %{user: user, target: user})
    570   end
    571 
    572   test "visibility/list" do
    573     user = insert(:user)
    574 
    575     {:ok, list} = Pleroma.List.create("foo", user)
    576 
    577     {:ok, activity} =
    578       CommonAPI.post(user, %{"status" => "foobar", "visibility" => "list:#{list.id}"})
    579 
    580     status = StatusView.render("status.json", activity: activity)
    581 
    582     assert status.visibility == "list"
    583   end
    584 end