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