Merge remote-tracking branch 'remotes/origin/develop' into restricted-relations-embedding
[akkoma] / test / web / mastodon_api / views / status_view_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2020 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.User
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
20
21 import Pleroma.Factory
22 import Tesla.Mock
23 import OpenApiSpex.TestAssertions
24
25 setup do
26 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
27 :ok
28 end
29
30 test "has an emoji reaction list" do
31 user = insert(:user)
32 other_user = insert(:user)
33 third_user = insert(:user)
34 {:ok, activity} = CommonAPI.post(user, %{status: "dae cofe??"})
35
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)
41
42 assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
43
44 assert status[:pleroma][:emoji_reactions] == [
45 %{name: "☕", count: 2, me: false},
46 %{name: "🍵", count: 1, me: false}
47 ]
48
49 status = StatusView.render("show.json", activity: activity, for: user)
50
51 assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
52
53 assert status[:pleroma][:emoji_reactions] == [
54 %{name: "☕", count: 2, me: true},
55 %{name: "🍵", count: 1, me: false}
56 ]
57 end
58
59 test "loads and returns the direct conversation id when given the `with_direct_conversation_id` option" do
60 user = insert(:user)
61
62 {:ok, activity} = CommonAPI.post(user, %{status: "Hey @shp!", visibility: "direct"})
63 [participation] = Participation.for_user(user)
64
65 status =
66 StatusView.render("show.json",
67 activity: activity,
68 with_direct_conversation_id: true,
69 for: user
70 )
71
72 assert status[:pleroma][:direct_conversation_id] == participation.id
73
74 status = StatusView.render("show.json", activity: activity, for: user)
75 assert status[:pleroma][:direct_conversation_id] == nil
76 assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
77 end
78
79 test "returns the direct conversation id when given the `direct_conversation_id` option" do
80 user = insert(:user)
81
82 {:ok, activity} = CommonAPI.post(user, %{status: "Hey @shp!", visibility: "direct"})
83 [participation] = Participation.for_user(user)
84
85 status =
86 StatusView.render("show.json",
87 activity: activity,
88 direct_conversation_id: participation.id,
89 for: user
90 )
91
92 assert status[:pleroma][:direct_conversation_id] == participation.id
93 assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
94 end
95
96 test "returns a temporary ap_id based user for activities missing db users" do
97 user = insert(:user)
98
99 {:ok, activity} = CommonAPI.post(user, %{status: "Hey @shp!", visibility: "direct"})
100
101 Repo.delete(user)
102 Cachex.clear(:user_cache)
103
104 finger_url =
105 "https://localhost/.well-known/webfinger?resource=acct:#{user.nickname}@localhost"
106
107 Tesla.Mock.mock_global(fn
108 %{method: :get, url: "http://localhost/.well-known/host-meta"} ->
109 %Tesla.Env{status: 404, body: ""}
110
111 %{method: :get, url: "https://localhost/.well-known/host-meta"} ->
112 %Tesla.Env{status: 404, body: ""}
113
114 %{
115 method: :get,
116 url: ^finger_url
117 } ->
118 %Tesla.Env{status: 404, body: ""}
119 end)
120
121 %{account: ms_user} = StatusView.render("show.json", activity: activity)
122
123 assert ms_user.acct == "erroruser@example.com"
124 end
125
126 test "tries to get a user by nickname if fetching by ap_id doesn't work" do
127 user = insert(:user)
128
129 {:ok, activity} = CommonAPI.post(user, %{status: "Hey @shp!", visibility: "direct"})
130
131 {:ok, user} =
132 user
133 |> Ecto.Changeset.change(%{ap_id: "#{user.ap_id}/extension/#{user.nickname}"})
134 |> Repo.update()
135
136 Cachex.clear(:user_cache)
137
138 result = StatusView.render("show.json", activity: activity)
139
140 assert result[:account][:id] == to_string(user.id)
141 assert_schema(result, "Status", Pleroma.Web.ApiSpec.spec())
142 end
143
144 test "a note with null content" do
145 note = insert(:note_activity)
146 note_object = Object.normalize(note)
147
148 data =
149 note_object.data
150 |> Map.put("content", nil)
151
152 Object.change(note_object, %{data: data})
153 |> Object.update_and_set_cache()
154
155 User.get_cached_by_ap_id(note.data["actor"])
156
157 status = StatusView.render("show.json", %{activity: note})
158
159 assert status.content == ""
160 assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
161 end
162
163 test "a note activity" do
164 note = insert(:note_activity)
165 object_data = Object.normalize(note).data
166 user = User.get_cached_by_ap_id(note.data["actor"])
167
168 convo_id = Utils.context_to_conversation_id(object_data["context"])
169
170 status = StatusView.render("show.json", %{activity: note})
171
172 created_at =
173 (object_data["published"] || "")
174 |> String.replace(~r/\.\d+Z/, ".000Z")
175
176 expected = %{
177 id: to_string(note.id),
178 uri: object_data["id"],
179 url: Pleroma.Web.Router.Helpers.o_status_url(Pleroma.Web.Endpoint, :notice, note),
180 account: AccountView.render("show.json", %{user: user}),
181 in_reply_to_id: nil,
182 in_reply_to_account_id: nil,
183 card: nil,
184 reblog: nil,
185 content: HTML.filter_tags(object_data["content"]),
186 created_at: created_at,
187 reblogs_count: 0,
188 replies_count: 0,
189 favourites_count: 0,
190 reblogged: false,
191 bookmarked: false,
192 favourited: false,
193 muted: false,
194 pinned: false,
195 sensitive: false,
196 poll: nil,
197 spoiler_text: HTML.filter_tags(object_data["summary"]),
198 visibility: "public",
199 media_attachments: [],
200 mentions: [],
201 tags: [
202 %{
203 name: "#{object_data["tag"]}",
204 url: "/tag/#{object_data["tag"]}"
205 }
206 ],
207 application: %{
208 name: "Web",
209 website: nil
210 },
211 language: nil,
212 emojis: [
213 %{
214 shortcode: "2hu",
215 url: "corndog.png",
216 static_url: "corndog.png",
217 visible_in_picker: false
218 }
219 ],
220 pleroma: %{
221 local: true,
222 conversation_id: convo_id,
223 in_reply_to_account_acct: nil,
224 content: %{"text/plain" => HTML.strip_tags(object_data["content"])},
225 spoiler_text: %{"text/plain" => HTML.strip_tags(object_data["summary"])},
226 expires_at: nil,
227 direct_conversation_id: nil,
228 thread_muted: false,
229 emoji_reactions: []
230 }
231 }
232
233 assert status == expected
234 assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
235 end
236
237 test "tells if the message is muted for some reason" do
238 user = insert(:user)
239 other_user = insert(:user)
240
241 {:ok, _user_relationships} = User.mute(user, other_user)
242
243 {:ok, activity} = CommonAPI.post(other_user, %{status: "test"})
244
245 relationships_opt = UserRelationship.view_relationships_option(user, [other_user])
246
247 opts = %{activity: activity}
248 status = StatusView.render("show.json", opts)
249 assert status.muted == false
250 assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
251
252 status = StatusView.render("show.json", Map.put(opts, :relationships, relationships_opt))
253 assert status.muted == false
254
255 for_opts = %{activity: activity, for: user}
256 status = StatusView.render("show.json", for_opts)
257 assert status.muted == true
258
259 status = StatusView.render("show.json", Map.put(for_opts, :relationships, relationships_opt))
260 assert status.muted == true
261 assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
262 end
263
264 test "tells if the message is thread muted" do
265 user = insert(:user)
266 other_user = insert(:user)
267
268 {:ok, _user_relationships} = User.mute(user, other_user)
269
270 {:ok, activity} = CommonAPI.post(other_user, %{status: "test"})
271 status = StatusView.render("show.json", %{activity: activity, for: user})
272
273 assert status.pleroma.thread_muted == false
274
275 {:ok, activity} = CommonAPI.add_mute(user, activity)
276
277 status = StatusView.render("show.json", %{activity: activity, for: user})
278
279 assert status.pleroma.thread_muted == true
280 end
281
282 test "tells if the status is bookmarked" do
283 user = insert(:user)
284
285 {:ok, activity} = CommonAPI.post(user, %{status: "Cute girls doing cute things"})
286 status = StatusView.render("show.json", %{activity: activity})
287
288 assert status.bookmarked == false
289
290 status = StatusView.render("show.json", %{activity: activity, for: user})
291
292 assert status.bookmarked == false
293
294 {:ok, _bookmark} = Bookmark.create(user.id, activity.id)
295
296 activity = Activity.get_by_id_with_object(activity.id)
297
298 status = StatusView.render("show.json", %{activity: activity, for: user})
299
300 assert status.bookmarked == true
301 end
302
303 test "a reply" do
304 note = insert(:note_activity)
305 user = insert(:user)
306
307 {:ok, activity} = CommonAPI.post(user, %{status: "he", in_reply_to_status_id: note.id})
308
309 status = StatusView.render("show.json", %{activity: activity})
310
311 assert status.in_reply_to_id == to_string(note.id)
312
313 [status] = StatusView.render("index.json", %{activities: [activity], as: :activity})
314
315 assert status.in_reply_to_id == to_string(note.id)
316 end
317
318 test "contains mentions" do
319 user = insert(:user)
320 mentioned = insert(:user)
321
322 {:ok, activity} = CommonAPI.post(user, %{status: "hi @#{mentioned.nickname}"})
323
324 status = StatusView.render("show.json", %{activity: activity})
325
326 assert status.mentions ==
327 Enum.map([mentioned], fn u -> AccountView.render("mention.json", %{user: u}) end)
328
329 assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
330 end
331
332 test "create mentions from the 'to' field" do
333 %User{ap_id: recipient_ap_id} = insert(:user)
334 cc = insert_pair(:user) |> Enum.map(& &1.ap_id)
335
336 object =
337 insert(:note, %{
338 data: %{
339 "to" => [recipient_ap_id],
340 "cc" => cc
341 }
342 })
343
344 activity =
345 insert(:note_activity, %{
346 note: object,
347 recipients: [recipient_ap_id | cc]
348 })
349
350 assert length(activity.recipients) == 3
351
352 %{mentions: [mention] = mentions} = StatusView.render("show.json", %{activity: activity})
353
354 assert length(mentions) == 1
355 assert mention.url == recipient_ap_id
356 end
357
358 test "create mentions from the 'tag' field" do
359 recipient = insert(:user)
360 cc = insert_pair(:user) |> Enum.map(& &1.ap_id)
361
362 object =
363 insert(:note, %{
364 data: %{
365 "cc" => cc,
366 "tag" => [
367 %{
368 "href" => recipient.ap_id,
369 "name" => recipient.nickname,
370 "type" => "Mention"
371 },
372 %{
373 "href" => "https://example.com/search?tag=test",
374 "name" => "#test",
375 "type" => "Hashtag"
376 }
377 ]
378 }
379 })
380
381 activity =
382 insert(:note_activity, %{
383 note: object,
384 recipients: [recipient.ap_id | cc]
385 })
386
387 assert length(activity.recipients) == 3
388
389 %{mentions: [mention] = mentions} = StatusView.render("show.json", %{activity: activity})
390
391 assert length(mentions) == 1
392 assert mention.url == recipient.ap_id
393 end
394
395 test "attachments" do
396 object = %{
397 "type" => "Image",
398 "url" => [
399 %{
400 "mediaType" => "image/png",
401 "href" => "someurl"
402 }
403 ],
404 "uuid" => 6
405 }
406
407 expected = %{
408 id: "1638338801",
409 type: "image",
410 url: "someurl",
411 remote_url: "someurl",
412 preview_url: "someurl",
413 text_url: "someurl",
414 description: nil,
415 pleroma: %{mime_type: "image/png"}
416 }
417
418 api_spec = Pleroma.Web.ApiSpec.spec()
419
420 assert expected == StatusView.render("attachment.json", %{attachment: object})
421 assert_schema(expected, "Attachment", api_spec)
422
423 # If theres a "id", use that instead of the generated one
424 object = Map.put(object, "id", 2)
425 result = StatusView.render("attachment.json", %{attachment: object})
426
427 assert %{id: "2"} = result
428 assert_schema(result, "Attachment", api_spec)
429 end
430
431 test "put the url advertised in the Activity in to the url attribute" do
432 id = "https://wedistribute.org/wp-json/pterotype/v1/object/85810"
433 [activity] = Activity.search(nil, id)
434
435 status = StatusView.render("show.json", %{activity: activity})
436
437 assert status.uri == id
438 assert status.url == "https://wedistribute.org/2019/07/mastodon-drops-ostatus/"
439 end
440
441 test "a reblog" do
442 user = insert(:user)
443 activity = insert(:note_activity)
444
445 {:ok, reblog, _} = CommonAPI.repeat(activity.id, user)
446
447 represented = StatusView.render("show.json", %{for: user, activity: reblog})
448
449 assert represented[:id] == to_string(reblog.id)
450 assert represented[:reblog][:id] == to_string(activity.id)
451 assert represented[:emojis] == []
452 assert_schema(represented, "Status", Pleroma.Web.ApiSpec.spec())
453 end
454
455 test "a peertube video" do
456 user = insert(:user)
457
458 {:ok, object} =
459 Pleroma.Object.Fetcher.fetch_object_from_id(
460 "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3"
461 )
462
463 %Activity{} = activity = Activity.get_create_by_object_ap_id(object.data["id"])
464
465 represented = StatusView.render("show.json", %{for: user, activity: activity})
466
467 assert represented[:id] == to_string(activity.id)
468 assert length(represented[:media_attachments]) == 1
469 assert_schema(represented, "Status", Pleroma.Web.ApiSpec.spec())
470 end
471
472 test "funkwhale audio" do
473 user = insert(:user)
474
475 {:ok, object} =
476 Pleroma.Object.Fetcher.fetch_object_from_id(
477 "https://channels.tests.funkwhale.audio/federation/music/uploads/42342395-0208-4fee-a38d-259a6dae0871"
478 )
479
480 %Activity{} = activity = Activity.get_create_by_object_ap_id(object.data["id"])
481
482 represented = StatusView.render("show.json", %{for: user, activity: activity})
483
484 assert represented[:id] == to_string(activity.id)
485 assert length(represented[:media_attachments]) == 1
486 end
487
488 test "a Mobilizon event" do
489 user = insert(:user)
490
491 {:ok, object} =
492 Pleroma.Object.Fetcher.fetch_object_from_id(
493 "https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39"
494 )
495
496 %Activity{} = activity = Activity.get_create_by_object_ap_id(object.data["id"])
497
498 represented = StatusView.render("show.json", %{for: user, activity: activity})
499
500 assert represented[:id] == to_string(activity.id)
501 end
502
503 describe "build_tags/1" do
504 test "it returns a a dictionary tags" do
505 object_tags = [
506 "fediverse",
507 "mastodon",
508 "nextcloud",
509 %{
510 "href" => "https://kawen.space/users/lain",
511 "name" => "@lain@kawen.space",
512 "type" => "Mention"
513 }
514 ]
515
516 assert StatusView.build_tags(object_tags) == [
517 %{name: "fediverse", url: "/tag/fediverse"},
518 %{name: "mastodon", url: "/tag/mastodon"},
519 %{name: "nextcloud", url: "/tag/nextcloud"}
520 ]
521 end
522 end
523
524 describe "rich media cards" do
525 test "a rich media card without a site name renders correctly" do
526 page_url = "http://example.com"
527
528 card = %{
529 url: page_url,
530 image: page_url <> "/example.jpg",
531 title: "Example website"
532 }
533
534 %{provider_name: "example.com"} =
535 StatusView.render("card.json", %{page_url: page_url, rich_media: card})
536 end
537
538 test "a rich media card without a site name or image renders correctly" do
539 page_url = "http://example.com"
540
541 card = %{
542 url: page_url,
543 title: "Example website"
544 }
545
546 %{provider_name: "example.com"} =
547 StatusView.render("card.json", %{page_url: page_url, rich_media: card})
548 end
549
550 test "a rich media card without an image renders correctly" do
551 page_url = "http://example.com"
552
553 card = %{
554 url: page_url,
555 site_name: "Example site name",
556 title: "Example website"
557 }
558
559 %{provider_name: "example.com"} =
560 StatusView.render("card.json", %{page_url: page_url, rich_media: card})
561 end
562
563 test "a rich media card with all relevant data renders correctly" do
564 page_url = "http://example.com"
565
566 card = %{
567 url: page_url,
568 site_name: "Example site name",
569 title: "Example website",
570 image: page_url <> "/example.jpg",
571 description: "Example description"
572 }
573
574 %{provider_name: "example.com"} =
575 StatusView.render("card.json", %{page_url: page_url, rich_media: card})
576 end
577 end
578
579 test "does not embed a relationship in the account" do
580 user = insert(:user)
581 other_user = insert(:user)
582
583 {:ok, activity} =
584 CommonAPI.post(user, %{
585 status: "drink more water"
586 })
587
588 result = StatusView.render("show.json", %{activity: activity, for: other_user})
589
590 assert result[:account][:pleroma][:relationship] == %{}
591 assert_schema(result, "Status", Pleroma.Web.ApiSpec.spec())
592 end
593
594 test "does not embed a relationship in the account in reposts" do
595 user = insert(:user)
596 other_user = insert(:user)
597
598 {:ok, activity} =
599 CommonAPI.post(user, %{
600 status: "˙˙ɐʎns"
601 })
602
603 {:ok, activity, _object} = CommonAPI.repeat(activity.id, other_user)
604
605 result = StatusView.render("show.json", %{activity: activity, for: user})
606
607 assert result[:account][:pleroma][:relationship] == %{}
608 assert result[:reblog][:account][:pleroma][:relationship] == %{}
609 assert_schema(result, "Status", Pleroma.Web.ApiSpec.spec())
610 end
611
612 test "visibility/list" do
613 user = insert(:user)
614
615 {:ok, list} = Pleroma.List.create("foo", user)
616
617 {:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})
618
619 status = StatusView.render("show.json", activity: activity)
620
621 assert status.visibility == "list"
622 end
623
624 test "successfully renders a Listen activity (pleroma extension)" do
625 listen_activity = insert(:listen)
626
627 status = StatusView.render("listen.json", activity: listen_activity)
628
629 assert status.length == listen_activity.data["object"]["length"]
630 assert status.title == listen_activity.data["object"]["title"]
631 assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
632 end
633 end