total rebase
[anni] / test / pleroma / web / mastodon_api / views / status_view_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2022 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.UnstubbedConfigMock, as: ConfigMock
15   alias Pleroma.User
16   alias Pleroma.UserRelationship
17   alias Pleroma.Web.CommonAPI
18   alias Pleroma.Web.MastodonAPI.AccountView
19   alias Pleroma.Web.MastodonAPI.StatusView
20   alias Pleroma.Web.RichMedia.Card
21
22   require Bitwise
23
24   import Mox
25   import OpenApiSpex.TestAssertions
26   import Pleroma.Factory
27   import Tesla.Mock
28
29   setup do
30     mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
31     :ok
32   end
33
34   test "has an emoji reaction list" do
35     user = insert(:user)
36     other_user = insert(:user)
37     third_user = insert(:user)
38     {:ok, activity} = CommonAPI.post(user, %{status: "dae cofe??"})
39
40     {:ok, _} = CommonAPI.react_with_emoji(activity.id, user, "☕")
41     {:ok, _} = CommonAPI.react_with_emoji(activity.id, user, ":dinosaur:")
42     {:ok, _} = CommonAPI.react_with_emoji(activity.id, third_user, "🍵")
43     {:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "☕")
44     {:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, ":dinosaur:")
45
46     activity = Repo.get(Activity, activity.id)
47     status = StatusView.render("show.json", activity: activity)
48
49     assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
50
51     assert status[:pleroma][:emoji_reactions] == [
52              %{name: "☕", count: 2, me: false, url: nil, account_ids: [other_user.id, user.id]},
53              %{
54                count: 2,
55                me: false,
56                name: "dinosaur",
57                url: "http://localhost:4001/emoji/dino walking.gif",
58                account_ids: [other_user.id, user.id]
59              },
60              %{name: "🍵", count: 1, me: false, url: nil, account_ids: [third_user.id]}
61            ]
62
63     status = StatusView.render("show.json", activity: activity, for: user)
64
65     assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
66
67     assert status[:pleroma][:emoji_reactions] == [
68              %{name: "☕", count: 2, me: true, url: nil, account_ids: [other_user.id, user.id]},
69              %{
70                count: 2,
71                me: true,
72                name: "dinosaur",
73                url: "http://localhost:4001/emoji/dino walking.gif",
74                account_ids: [other_user.id, user.id]
75              },
76              %{name: "🍵", count: 1, me: false, url: nil, account_ids: [third_user.id]}
77            ]
78   end
79
80   test "works with legacy-formatted reactions" do
81     user = insert(:user)
82     other_user = insert(:user)
83
84     note =
85       insert(:note,
86         user: user,
87         data: %{
88           "reactions" => [["😿", [other_user.ap_id]]]
89         }
90       )
91
92     activity = insert(:note_activity, user: user, note: note)
93
94     status = StatusView.render("show.json", activity: activity, for: user)
95
96     assert status[:pleroma][:emoji_reactions] == [
97              %{name: "😿", count: 1, me: false, url: nil, account_ids: [other_user.id]}
98            ]
99   end
100
101   test "works correctly with badly formatted emojis" do
102     user = insert(:user)
103     {:ok, activity} = CommonAPI.post(user, %{status: "yo"})
104
105     activity
106     |> Object.normalize(fetch: false)
107     |> Object.update_data(%{"reactions" => %{"☕" => [user.ap_id], "x" => 1}})
108
109     activity = Activity.get_by_id(activity.id)
110     status = StatusView.render("show.json", activity: activity, for: user)
111
112     assert status[:pleroma][:emoji_reactions] == [
113              %{name: "☕", count: 1, me: true, url: nil, account_ids: [user.id]}
114            ]
115   end
116
117   test "doesn't show reactions from muted and blocked users" do
118     user = insert(:user)
119     other_user = insert(:user)
120     third_user = insert(:user)
121
122     {:ok, activity} = CommonAPI.post(user, %{status: "dae cofe??"})
123
124     {:ok, _} = User.mute(user, other_user)
125     {:ok, _} = User.block(other_user, third_user)
126
127     {:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "☕")
128
129     activity = Repo.get(Activity, activity.id)
130     status = StatusView.render("show.json", activity: activity)
131
132     assert status[:pleroma][:emoji_reactions] == [
133              %{name: "☕", count: 1, me: false, url: nil, account_ids: [other_user.id]}
134            ]
135
136     status = StatusView.render("show.json", activity: activity, for: user)
137
138     assert status[:pleroma][:emoji_reactions] == []
139
140     {:ok, _} = CommonAPI.react_with_emoji(activity.id, third_user, "☕")
141
142     status = StatusView.render("show.json", activity: activity)
143
144     assert status[:pleroma][:emoji_reactions] == [
145              %{
146                name: "☕",
147                count: 2,
148                me: false,
149                url: nil,
150                account_ids: [third_user.id, other_user.id]
151              }
152            ]
153
154     status = StatusView.render("show.json", activity: activity, for: user)
155
156     assert status[:pleroma][:emoji_reactions] == [
157              %{name: "☕", count: 1, me: false, url: nil, account_ids: [third_user.id]}
158            ]
159
160     status = StatusView.render("show.json", activity: activity, for: other_user)
161
162     assert status[:pleroma][:emoji_reactions] == [
163              %{name: "☕", count: 1, me: true, url: nil, account_ids: [other_user.id]}
164            ]
165   end
166
167   test "loads and returns the direct conversation id when given the `with_direct_conversation_id` option" do
168     user = insert(:user)
169
170     {:ok, activity} = CommonAPI.post(user, %{status: "Hey @shp!", visibility: "direct"})
171     [participation] = Participation.for_user(user)
172
173     status =
174       StatusView.render("show.json",
175         activity: activity,
176         with_direct_conversation_id: true,
177         for: user
178       )
179
180     assert status[:pleroma][:direct_conversation_id] == participation.id
181
182     status = StatusView.render("show.json", activity: activity, for: user)
183     assert status[:pleroma][:direct_conversation_id] == nil
184     assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
185   end
186
187   test "returns the direct conversation id when given the `direct_conversation_id` option" do
188     user = insert(:user)
189
190     {:ok, activity} = CommonAPI.post(user, %{status: "Hey @shp!", visibility: "direct"})
191     [participation] = Participation.for_user(user)
192
193     status =
194       StatusView.render("show.json",
195         activity: activity,
196         direct_conversation_id: participation.id,
197         for: user
198       )
199
200     assert status[:pleroma][:direct_conversation_id] == participation.id
201     assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
202   end
203
204   @tag capture_log: true
205   test "returns a temporary ap_id based user for activities missing db users" do
206     user = insert(:user)
207
208     {:ok, activity} = CommonAPI.post(user, %{status: "Hey @shp!", visibility: "direct"})
209
210     Repo.delete(user)
211     User.invalidate_cache(user)
212
213     finger_url =
214       "https://localhost/.well-known/webfinger?resource=acct:#{user.nickname}@localhost"
215
216     Tesla.Mock.mock_global(fn
217       %{method: :get, url: "http://localhost/.well-known/host-meta"} ->
218         %Tesla.Env{status: 404, body: ""}
219
220       %{method: :get, url: "https://localhost/.well-known/host-meta"} ->
221         %Tesla.Env{status: 404, body: ""}
222
223       %{
224         method: :get,
225         url: ^finger_url
226       } ->
227         %Tesla.Env{status: 404, body: ""}
228     end)
229
230     %{account: ms_user} = StatusView.render("show.json", activity: activity)
231
232     assert ms_user.acct == "erroruser@example.com"
233   end
234
235   test "tries to get a user by nickname if fetching by ap_id doesn't work" do
236     user = insert(:user)
237
238     {:ok, activity} = CommonAPI.post(user, %{status: "Hey @shp!", visibility: "direct"})
239
240     {:ok, user} =
241       user
242       |> Ecto.Changeset.change(%{ap_id: "#{user.ap_id}/extension/#{user.nickname}"})
243       |> Repo.update()
244
245     User.invalidate_cache(user)
246
247     result = StatusView.render("show.json", activity: activity)
248
249     assert result[:account][:id] == to_string(user.id)
250     assert_schema(result, "Status", Pleroma.Web.ApiSpec.spec())
251   end
252
253   test "a note with null content" do
254     note = insert(:note_activity)
255     note_object = Object.normalize(note, fetch: false)
256
257     data =
258       note_object.data
259       |> Map.put("content", nil)
260
261     Object.change(note_object, %{data: data})
262     |> Object.update_and_set_cache()
263
264     User.get_cached_by_ap_id(note.data["actor"])
265
266     status = StatusView.render("show.json", %{activity: note})
267
268     assert status.content == ""
269     assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
270   end
271
272   test "a note activity" do
273     note = insert(:note_activity)
274     object_data = Object.normalize(note, fetch: false).data
275     user = User.get_cached_by_ap_id(note.data["actor"])
276
277     convo_id = :erlang.crc32(object_data["context"]) |> Bitwise.band(Bitwise.bnot(0x8000_0000))
278
279     status = StatusView.render("show.json", %{activity: note})
280
281     created_at =
282       (object_data["published"] || "")
283       |> String.replace(~r/\.\d+Z/, ".000Z")
284
285     expected = %{
286       id: to_string(note.id),
287       uri: object_data["id"],
288       url: Pleroma.Web.Router.Helpers.o_status_url(Pleroma.Web.Endpoint, :notice, note),
289       account: AccountView.render("show.json", %{user: user, skip_visibility_check: true}),
290       in_reply_to_id: nil,
291       in_reply_to_account_id: nil,
292       card: nil,
293       reblog: nil,
294       content: HTML.filter_tags(object_data["content"]),
295       text: nil,
296       created_at: created_at,
297       edited_at: nil,
298       reblogs_count: 0,
299       replies_count: 0,
300       favourites_count: 0,
301       reblogged: false,
302       bookmarked: false,
303       favourited: false,
304       muted: false,
305       pinned: false,
306       sensitive: false,
307       poll: nil,
308       spoiler_text: HTML.filter_tags(object_data["summary"]),
309       visibility: "public",
310       media_attachments: [],
311       mentions: [],
312       tags: [
313         %{
314           name: "#{hd(object_data["tag"])}",
315           url: "http://localhost:4001/tag/#{hd(object_data["tag"])}"
316         }
317       ],
318       application: nil,
319       language: nil,
320       emojis: [
321         %{
322           shortcode: "2hu",
323           url: "corndog.png",
324           static_url: "corndog.png",
325           visible_in_picker: false
326         }
327       ],
328       pleroma: %{
329         local: true,
330         conversation_id: convo_id,
331         context: object_data["context"],
332         in_reply_to_account_acct: nil,
333         quote: nil,
334         quote_id: nil,
335         quote_url: nil,
336         quote_visible: false,
337         content: %{"text/plain" => HTML.strip_tags(object_data["content"])},
338         spoiler_text: %{"text/plain" => HTML.strip_tags(object_data["summary"])},
339         expires_at: nil,
340         direct_conversation_id: nil,
341         thread_muted: false,
342         emoji_reactions: [],
343         parent_visible: false,
344         pinned_at: nil,
345         quotes_count: 0,
346         bookmark_folder: nil
347       }
348     }
349
350     assert status == expected
351     assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
352   end
353
354   test "tells if the message is muted for some reason" do
355     user = insert(:user)
356     other_user = insert(:user)
357
358     {:ok, _user_relationships} = User.mute(user, other_user)
359
360     {:ok, activity} = CommonAPI.post(other_user, %{status: "test"})
361
362     relationships_opt = UserRelationship.view_relationships_option(user, [other_user])
363
364     opts = %{activity: activity}
365     status = StatusView.render("show.json", opts)
366     assert status.muted == false
367     assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
368
369     status = StatusView.render("show.json", Map.put(opts, :relationships, relationships_opt))
370     assert status.muted == false
371
372     for_opts = %{activity: activity, for: user}
373     status = StatusView.render("show.json", for_opts)
374     assert status.muted == true
375
376     status = StatusView.render("show.json", Map.put(for_opts, :relationships, relationships_opt))
377     assert status.muted == true
378     assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
379   end
380
381   test "tells if the message is thread muted" do
382     user = insert(:user)
383     other_user = insert(:user)
384
385     {:ok, _user_relationships} = User.mute(user, other_user)
386
387     {:ok, activity} = CommonAPI.post(other_user, %{status: "test"})
388     status = StatusView.render("show.json", %{activity: activity, for: user})
389
390     assert status.pleroma.thread_muted == false
391
392     {:ok, activity} = CommonAPI.add_mute(user, activity)
393
394     status = StatusView.render("show.json", %{activity: activity, for: user})
395
396     assert status.pleroma.thread_muted == true
397   end
398
399   test "tells if the status is bookmarked" do
400     user = insert(:user)
401
402     {:ok, activity} = CommonAPI.post(user, %{status: "Cute girls doing cute things"})
403     status = StatusView.render("show.json", %{activity: activity})
404
405     assert status.bookmarked == false
406
407     status = StatusView.render("show.json", %{activity: activity, for: user})
408
409     assert status.bookmarked == false
410
411     {:ok, _bookmark} = Bookmark.create(user.id, activity.id)
412
413     activity = Activity.get_by_id_with_object(activity.id)
414
415     status = StatusView.render("show.json", %{activity: activity, for: user})
416
417     assert status.bookmarked == true
418   end
419
420   test "a reply" do
421     note = insert(:note_activity)
422     user = insert(:user)
423
424     {:ok, activity} = CommonAPI.post(user, %{status: "he", in_reply_to_status_id: note.id})
425
426     status = StatusView.render("show.json", %{activity: activity})
427
428     assert status.in_reply_to_id == to_string(note.id)
429
430     [status] = StatusView.render("index.json", %{activities: [activity], as: :activity})
431
432     assert status.in_reply_to_id == to_string(note.id)
433   end
434
435   test "a quote post" do
436     post = insert(:note_activity)
437     user = insert(:user)
438
439     {:ok, quote_post} = CommonAPI.post(user, %{status: "he", quote_id: post.id})
440     {:ok, quoted_quote_post} = CommonAPI.post(user, %{status: "yo", quote_id: quote_post.id})
441
442     status = StatusView.render("show.json", %{activity: quoted_quote_post})
443
444     assert status.pleroma.quote.id == to_string(quote_post.id)
445     assert status.pleroma.quote_id == to_string(quote_post.id)
446     assert status.pleroma.quote_url == Object.normalize(quote_post).data["id"]
447     assert status.pleroma.quote_visible
448
449     # Quotes don't go more than one level deep
450     refute status.pleroma.quote.pleroma.quote
451     assert status.pleroma.quote.pleroma.quote_id == to_string(post.id)
452     assert status.pleroma.quote.pleroma.quote_url == Object.normalize(post).data["id"]
453     assert status.pleroma.quote.pleroma.quote_visible
454
455     # In an index
456     [status] = StatusView.render("index.json", %{activities: [quoted_quote_post], as: :activity})
457
458     assert status.pleroma.quote.id == to_string(quote_post.id)
459   end
460
461   test "quoted private post" do
462     user = insert(:user)
463
464     # Insert a private post
465     private = insert(:followers_only_note_activity, user: user)
466     private_object = Object.normalize(private)
467
468     # Create a public post quoting the private post
469     quote_private =
470       insert(:note_activity, note: insert(:note, data: %{"quoteUrl" => private_object.data["id"]}))
471
472     status = StatusView.render("show.json", %{activity: quote_private})
473
474     # The quote isn't rendered
475     refute status.pleroma.quote
476     assert status.pleroma.quote_url == private_object.data["id"]
477     refute status.pleroma.quote_visible
478
479     # After following the user, the quote is rendered
480     follower = insert(:user)
481     CommonAPI.follow(follower, user)
482
483     status = StatusView.render("show.json", %{activity: quote_private, for: follower})
484     assert status.pleroma.quote.id == to_string(private.id)
485     assert status.pleroma.quote_visible
486   end
487
488   test "quoted direct message" do
489     # Insert a direct message
490     direct = insert(:direct_note_activity)
491     direct_object = Object.normalize(direct)
492
493     # Create a public post quoting the direct message
494     quote_direct =
495       insert(:note_activity, note: insert(:note, data: %{"quoteUrl" => direct_object.data["id"]}))
496
497     status = StatusView.render("show.json", %{activity: quote_direct})
498
499     # The quote isn't rendered
500     refute status.pleroma.quote
501     assert status.pleroma.quote_url == direct_object.data["id"]
502     refute status.pleroma.quote_visible
503   end
504
505   test "repost of quote post" do
506     post = insert(:note_activity)
507     user = insert(:user)
508
509     {:ok, quote_post} = CommonAPI.post(user, %{status: "he", quote_id: post.id})
510     {:ok, repost} = CommonAPI.repeat(quote_post.id, user)
511
512     [status] = StatusView.render("index.json", %{activities: [repost], as: :activity})
513
514     assert status.reblog.pleroma.quote.id == to_string(post.id)
515   end
516
517   test "contains mentions" do
518     user = insert(:user)
519     mentioned = insert(:user)
520
521     {:ok, activity} = CommonAPI.post(user, %{status: "hi @#{mentioned.nickname}"})
522
523     status = StatusView.render("show.json", %{activity: activity})
524
525     assert status.mentions ==
526              Enum.map([mentioned], fn u -> AccountView.render("mention.json", %{user: u}) end)
527
528     assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
529   end
530
531   test "create mentions from the 'to' field" do
532     %User{ap_id: recipient_ap_id} = insert(:user)
533     cc = insert_pair(:user) |> Enum.map(& &1.ap_id)
534
535     object =
536       insert(:note, %{
537         data: %{
538           "to" => [recipient_ap_id],
539           "cc" => cc
540         }
541       })
542
543     activity =
544       insert(:note_activity, %{
545         note: object,
546         recipients: [recipient_ap_id | cc]
547       })
548
549     assert length(activity.recipients) == 3
550
551     %{mentions: [mention] = mentions} = StatusView.render("show.json", %{activity: activity})
552
553     assert length(mentions) == 1
554     assert mention.url == recipient_ap_id
555   end
556
557   test "create mentions from the 'tag' field" do
558     recipient = insert(:user)
559     cc = insert_pair(:user) |> Enum.map(& &1.ap_id)
560
561     object =
562       insert(:note, %{
563         data: %{
564           "cc" => cc,
565           "tag" => [
566             %{
567               "href" => recipient.ap_id,
568               "name" => recipient.nickname,
569               "type" => "Mention"
570             },
571             %{
572               "href" => "https://example.com/search?tag=test",
573               "name" => "#test",
574               "type" => "Hashtag"
575             }
576           ]
577         }
578       })
579
580     activity =
581       insert(:note_activity, %{
582         note: object,
583         recipients: [recipient.ap_id | cc]
584       })
585
586     assert length(activity.recipients) == 3
587
588     %{mentions: [mention] = mentions} = StatusView.render("show.json", %{activity: activity})
589
590     assert length(mentions) == 1
591     assert mention.url == recipient.ap_id
592   end
593
594   test "attachments" do
595     object = %{
596       "type" => "Image",
597       "url" => [
598         %{
599           "mediaType" => "image/png",
600           "href" => "someurl",
601           "width" => 200,
602           "height" => 100
603         }
604       ],
605       "blurhash" => "UJJ8X[xYW,%Jtq%NNFbXB5j]IVM|9GV=WHRn",
606       "uuid" => 6
607     }
608
609     expected = %{
610       id: "1638338801",
611       type: "image",
612       url: "someurl",
613       remote_url: "someurl",
614       preview_url: "someurl",
615       text_url: "someurl",
616       description: nil,
617       pleroma: %{mime_type: "image/png"},
618       meta: %{original: %{width: 200, height: 100, aspect: 2}},
619       blurhash: "UJJ8X[xYW,%Jtq%NNFbXB5j]IVM|9GV=WHRn"
620     }
621
622     api_spec = Pleroma.Web.ApiSpec.spec()
623
624     assert expected == StatusView.render("attachment.json", %{attachment: object})
625     assert_schema(expected, "Attachment", api_spec)
626
627     # If theres a "id", use that instead of the generated one
628     object = Map.put(object, "id", 2)
629     result = StatusView.render("attachment.json", %{attachment: object})
630
631     assert %{id: "2"} = result
632     assert_schema(result, "Attachment", api_spec)
633   end
634
635   test "put the url advertised in the Activity in to the url attribute" do
636     id = "https://wedistribute.org/wp-json/pterotype/v1/object/85810"
637     [activity] = Activity.search(nil, id)
638
639     status = StatusView.render("show.json", %{activity: activity})
640
641     assert status.uri == id
642     assert status.url == "https://wedistribute.org/2019/07/mastodon-drops-ostatus/"
643   end
644
645   test "a reblog" do
646     user = insert(:user)
647     activity = insert(:note_activity)
648
649     {:ok, reblog} = CommonAPI.repeat(activity.id, user)
650
651     represented = StatusView.render("show.json", %{for: user, activity: reblog})
652
653     assert represented[:id] == to_string(reblog.id)
654     assert represented[:reblog][:id] == to_string(activity.id)
655     assert represented[:emojis] == []
656     assert_schema(represented, "Status", Pleroma.Web.ApiSpec.spec())
657   end
658
659   test "a peertube video" do
660     user = insert(:user)
661
662     {:ok, object} =
663       Pleroma.Object.Fetcher.fetch_object_from_id(
664         "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3"
665       )
666
667     %Activity{} = activity = Activity.get_create_by_object_ap_id(object.data["id"])
668
669     represented = StatusView.render("show.json", %{for: user, activity: activity})
670
671     assert represented[:id] == to_string(activity.id)
672     assert length(represented[:media_attachments]) == 1
673     assert_schema(represented, "Status", Pleroma.Web.ApiSpec.spec())
674   end
675
676   test "funkwhale audio" do
677     user = insert(:user)
678
679     {:ok, object} =
680       Pleroma.Object.Fetcher.fetch_object_from_id(
681         "https://channels.tests.funkwhale.audio/federation/music/uploads/42342395-0208-4fee-a38d-259a6dae0871"
682       )
683
684     %Activity{} = activity = Activity.get_create_by_object_ap_id(object.data["id"])
685
686     represented = StatusView.render("show.json", %{for: user, activity: activity})
687
688     assert represented[:id] == to_string(activity.id)
689     assert length(represented[:media_attachments]) == 1
690   end
691
692   test "a Mobilizon event" do
693     user = insert(:user)
694
695     {:ok, object} =
696       Pleroma.Object.Fetcher.fetch_object_from_id(
697         "https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39"
698       )
699
700     %Activity{} = activity = Activity.get_create_by_object_ap_id(object.data["id"])
701
702     represented = StatusView.render("show.json", %{for: user, activity: activity})
703
704     assert represented[:id] == to_string(activity.id)
705
706     assert represented[:url] ==
707              "https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39"
708
709     assert represented[:content] ==
710              "<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>"
711   end
712
713   describe "build_tags/1" do
714     test "it returns a a dictionary tags" do
715       object_tags = [
716         "fediverse",
717         "mastodon",
718         "nextcloud",
719         %{
720           "href" => "https://kawen.space/users/lain",
721           "name" => "@lain@kawen.space",
722           "type" => "Mention"
723         }
724       ]
725
726       assert StatusView.build_tags(object_tags) == [
727                %{name: "fediverse", url: "http://localhost:4001/tag/fediverse"},
728                %{name: "mastodon", url: "http://localhost:4001/tag/mastodon"},
729                %{name: "nextcloud", url: "http://localhost:4001/tag/nextcloud"}
730              ]
731     end
732   end
733
734   describe "rich media cards" do
735     test "a rich media card without a site name renders correctly" do
736       page_url = "https://example.com"
737
738       {:ok, card} =
739         Card.create(page_url, %{image: page_url <> "/example.jpg", title: "Example website"})
740
741       assert match?(%{provider_name: "example.com"}, StatusView.render("card.json", card))
742     end
743
744     test "a rich media card without a site name or image renders correctly" do
745       page_url = "https://example.com"
746
747       fields = %{
748         "url" => page_url,
749         "title" => "Example website"
750       }
751
752       {:ok, card} = Card.create(page_url, fields)
753
754       assert match?(%{provider_name: "example.com"}, StatusView.render("card.json", card))
755     end
756
757     test "a rich media card without an image renders correctly" do
758       page_url = "https://example.com"
759
760       fields = %{
761         "url" => page_url,
762         "site_name" => "Example site name",
763         "title" => "Example website"
764       }
765
766       {:ok, card} = Card.create(page_url, fields)
767
768       assert match?(%{provider_name: "example.com"}, StatusView.render("card.json", card))
769     end
770
771     test "a rich media card without descriptions returns the fields with empty strings" do
772       page_url = "https://example.com"
773
774       fields = %{
775         "url" => page_url,
776         "site_name" => "Example site name",
777         "title" => "Example website"
778       }
779
780       {:ok, card} = Card.create(page_url, fields)
781
782       assert match?(
783                %{description: "", image_description: ""},
784                StatusView.render("card.json", card)
785              )
786     end
787
788     test "a rich media card with all relevant data renders correctly" do
789       page_url = "https://example.com"
790
791       fields = %{
792         "url" => page_url,
793         "site_name" => "Example site name",
794         "title" => "Example website",
795         "image" => page_url <> "/example.jpg",
796         "description" => "Example description"
797       }
798
799       {:ok, card} = Card.create(page_url, fields)
800
801       assert match?(%{provider_name: "example.com"}, StatusView.render("card.json", card))
802     end
803
804     test "a rich media card has all media proxied" do
805       clear_config([:media_proxy, :enabled], true)
806       clear_config([:media_preview_proxy, :enabled])
807
808       ConfigMock
809       |> stub_with(Pleroma.Test.StaticConfig)
810
811       page_url = "https://example.com"
812
813       fields = %{
814         "url" => page_url,
815         "site_name" => "Example site name",
816         "title" => "Example website",
817         "image" => page_url <> "/example.jpg",
818         "audio" => page_url <> "/example.ogg",
819         "video" => page_url <> "/example.mp4",
820         "description" => "Example description"
821       }
822
823       {:ok, card} = Card.create(page_url, fields)
824
825       %{
826         provider_name: "example.com",
827         image: image,
828         pleroma: %{opengraph: og}
829       } = StatusView.render("card.json", card)
830
831       assert String.match?(image, ~r/\/proxy\//)
832       assert String.match?(og["image"], ~r/\/proxy\//)
833       assert String.match?(og["audio"], ~r/\/proxy\//)
834       assert String.match?(og["video"], ~r/\/proxy\//)
835     end
836   end
837
838   test "does not embed a relationship in the account" do
839     user = insert(:user)
840     other_user = insert(:user)
841
842     {:ok, activity} =
843       CommonAPI.post(user, %{
844         status: "drink more water"
845       })
846
847     result = StatusView.render("show.json", %{activity: activity, for: other_user})
848
849     assert result[:account][:pleroma][:relationship] == %{}
850     assert_schema(result, "Status", Pleroma.Web.ApiSpec.spec())
851   end
852
853   test "does not embed a relationship in the account in reposts" do
854     user = insert(:user)
855     other_user = insert(:user)
856
857     {:ok, activity} =
858       CommonAPI.post(user, %{
859         status: "˙˙ɐʎns"
860       })
861
862     {:ok, activity} = CommonAPI.repeat(activity.id, other_user)
863
864     result = StatusView.render("show.json", %{activity: activity, for: user})
865
866     assert result[:account][:pleroma][:relationship] == %{}
867     assert result[:reblog][:account][:pleroma][:relationship] == %{}
868     assert_schema(result, "Status", Pleroma.Web.ApiSpec.spec())
869   end
870
871   test "visibility/list" do
872     user = insert(:user)
873
874     {:ok, list} = Pleroma.List.create("foo", user)
875
876     {:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})
877
878     status = StatusView.render("show.json", activity: activity)
879
880     assert status.visibility == "list"
881   end
882
883   test "has a field for parent visibility" do
884     user = insert(:user)
885     poster = insert(:user)
886
887     {:ok, invisible} = CommonAPI.post(poster, %{status: "hey", visibility: "private"})
888
889     {:ok, visible} =
890       CommonAPI.post(poster, %{status: "hey", visibility: "private", in_reply_to_id: invisible.id})
891
892     status = StatusView.render("show.json", activity: visible, for: user)
893     refute status.pleroma.parent_visible
894
895     status = StatusView.render("show.json", activity: visible, for: poster)
896     assert status.pleroma.parent_visible
897   end
898
899   test "it shows edited_at" do
900     poster = insert(:user)
901
902     {:ok, post} = CommonAPI.post(poster, %{status: "hey"})
903
904     status = StatusView.render("show.json", activity: post)
905     refute status.edited_at
906
907     {:ok, _} = CommonAPI.update(poster, post, %{status: "mew mew"})
908     edited = Pleroma.Activity.normalize(post)
909
910     status = StatusView.render("show.json", activity: edited)
911     assert status.edited_at
912   end
913
914   test "with a source object" do
915     note =
916       insert(:note,
917         data: %{"source" => %{"content" => "object source", "mediaType" => "text/markdown"}}
918       )
919
920     activity = insert(:note_activity, note: note)
921
922     status = StatusView.render("show.json", activity: activity, with_source: true)
923     assert status.text == "object source"
924   end
925
926   describe "source.json" do
927     test "with a source object, renders both source and content type" do
928       note =
929         insert(:note,
930           data: %{"source" => %{"content" => "object source", "mediaType" => "text/markdown"}}
931         )
932
933       activity = insert(:note_activity, note: note)
934
935       status = StatusView.render("source.json", activity: activity)
936       assert status.text == "object source"
937       assert status.content_type == "text/markdown"
938     end
939
940     test "with a source string, renders source and put text/plain as the content type" do
941       note = insert(:note, data: %{"source" => "string source"})
942       activity = insert(:note_activity, note: note)
943
944       status = StatusView.render("source.json", activity: activity)
945       assert status.text == "string source"
946       assert status.content_type == "text/plain"
947     end
948   end
949 end