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