bdd87a79e6296339530f3a73b62430f10c3a03a2
[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 describe "build_tags/1" do
398 test "it returns a a dictionary tags" do
399 object_tags = [
400 "fediverse",
401 "mastodon",
402 "nextcloud",
403 %{
404 "href" => "https://kawen.space/users/lain",
405 "name" => "@lain@kawen.space",
406 "type" => "Mention"
407 }
408 ]
409
410 assert StatusView.build_tags(object_tags) == [
411 %{name: "fediverse", url: "/tag/fediverse"},
412 %{name: "mastodon", url: "/tag/mastodon"},
413 %{name: "nextcloud", url: "/tag/nextcloud"}
414 ]
415 end
416 end
417
418 describe "rich media cards" do
419 test "a rich media card without a site name renders correctly" do
420 page_url = "http://example.com"
421
422 card = %{
423 url: page_url,
424 image: page_url <> "/example.jpg",
425 title: "Example website"
426 }
427
428 %{provider_name: "example.com"} =
429 StatusView.render("card.json", %{page_url: page_url, rich_media: card})
430 end
431
432 test "a rich media card without a site name or image renders correctly" do
433 page_url = "http://example.com"
434
435 card = %{
436 url: page_url,
437 title: "Example website"
438 }
439
440 %{provider_name: "example.com"} =
441 StatusView.render("card.json", %{page_url: page_url, rich_media: card})
442 end
443
444 test "a rich media card without an image renders correctly" do
445 page_url = "http://example.com"
446
447 card = %{
448 url: page_url,
449 site_name: "Example site name",
450 title: "Example website"
451 }
452
453 %{provider_name: "Example site name"} =
454 StatusView.render("card.json", %{page_url: page_url, rich_media: card})
455 end
456
457 test "a rich media card with all relevant data renders correctly" do
458 page_url = "http://example.com"
459
460 card = %{
461 url: page_url,
462 site_name: "Example site name",
463 title: "Example website",
464 image: page_url <> "/example.jpg",
465 description: "Example description"
466 }
467
468 %{provider_name: "Example site name"} =
469 StatusView.render("card.json", %{page_url: page_url, rich_media: card})
470 end
471 end
472
473 test "embeds a relationship in the account" do
474 user = insert(:user)
475 other_user = insert(:user)
476
477 {:ok, activity} =
478 CommonAPI.post(user, %{
479 "status" => "drink more water"
480 })
481
482 result = StatusView.render("show.json", %{activity: activity, for: other_user})
483
484 assert result[:account][:pleroma][:relationship] ==
485 AccountView.render("relationship.json", %{user: other_user, target: user})
486 end
487
488 test "embeds a relationship in the account in reposts" do
489 user = insert(:user)
490 other_user = insert(:user)
491
492 {:ok, activity} =
493 CommonAPI.post(user, %{
494 "status" => "˙˙ɐʎns"
495 })
496
497 {:ok, activity, _object} = CommonAPI.repeat(activity.id, other_user)
498
499 result = StatusView.render("show.json", %{activity: activity, for: user})
500
501 assert result[:account][:pleroma][:relationship] ==
502 AccountView.render("relationship.json", %{user: user, target: other_user})
503
504 assert result[:reblog][:account][:pleroma][:relationship] ==
505 AccountView.render("relationship.json", %{user: user, target: user})
506 end
507
508 test "visibility/list" do
509 user = insert(:user)
510
511 {:ok, list} = Pleroma.List.create("foo", user)
512
513 {:ok, activity} =
514 CommonAPI.post(user, %{"status" => "foobar", "visibility" => "list:#{list.id}"})
515
516 status = StatusView.render("show.json", activity: activity)
517
518 assert status.visibility == "list"
519 end
520
521 test "successfully renders a Listen activity (pleroma extension)" do
522 listen_activity = insert(:listen)
523
524 status = StatusView.render("listen.json", activity: listen_activity)
525
526 assert status.length == listen_activity.data["object"]["length"]
527 assert status.title == listen_activity.data["object"]["title"]
528 end
529 end