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