Remove `httpoison` from dependencies
[akkoma] / test / web / mastodon_api / status_view_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2018 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.Object
11 alias Pleroma.Repo
12 alias Pleroma.User
13 alias Pleroma.Web.CommonAPI
14 alias Pleroma.Web.CommonAPI.Utils
15 alias Pleroma.Web.MastodonAPI.AccountView
16 alias Pleroma.Web.MastodonAPI.StatusView
17 alias Pleroma.Web.OStatus
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 a temporary ap_id based user for activities missing db users" do
27 user = insert(:user)
28
29 {:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!", "visibility" => "direct"})
30
31 Repo.delete(user)
32 Cachex.clear(:user_cache)
33
34 %{account: ms_user} = StatusView.render("status.json", activity: activity)
35
36 assert ms_user.acct == "erroruser@example.com"
37 end
38
39 test "tries to get a user by nickname if fetching by ap_id doesn't work" do
40 user = insert(:user)
41
42 {:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!", "visibility" => "direct"})
43
44 {:ok, user} =
45 user
46 |> Ecto.Changeset.change(%{ap_id: "#{user.ap_id}/extension/#{user.nickname}"})
47 |> Repo.update()
48
49 Cachex.clear(:user_cache)
50
51 result = StatusView.render("status.json", activity: activity)
52
53 assert result[:account][:id] == to_string(user.id)
54 end
55
56 test "a note with null content" do
57 note = insert(:note_activity)
58 note_object = Object.normalize(note.data["object"])
59
60 data =
61 note_object.data
62 |> Map.put("content", nil)
63
64 Object.change(note_object, %{data: data})
65 |> Object.update_and_set_cache()
66
67 User.get_cached_by_ap_id(note.data["actor"])
68
69 status = StatusView.render("status.json", %{activity: note})
70
71 assert status.content == ""
72 end
73
74 test "a note activity" do
75 note = insert(:note_activity)
76 user = User.get_cached_by_ap_id(note.data["actor"])
77
78 convo_id = Utils.context_to_conversation_id(note.data["object"]["context"])
79
80 status = StatusView.render("status.json", %{activity: note})
81
82 created_at =
83 (note.data["object"]["published"] || "")
84 |> String.replace(~r/\.\d+Z/, ".000Z")
85
86 expected = %{
87 id: to_string(note.id),
88 uri: note.data["object"]["id"],
89 url: Pleroma.Web.Router.Helpers.o_status_url(Pleroma.Web.Endpoint, :notice, note),
90 account: AccountView.render("account.json", %{user: user}),
91 in_reply_to_id: nil,
92 in_reply_to_account_id: nil,
93 card: nil,
94 reblog: nil,
95 content: HtmlSanitizeEx.basic_html(note.data["object"]["content"]),
96 created_at: created_at,
97 reblogs_count: 0,
98 replies_count: 0,
99 favourites_count: 0,
100 reblogged: false,
101 bookmarked: false,
102 favourited: false,
103 muted: false,
104 pinned: false,
105 sensitive: false,
106 poll: nil,
107 spoiler_text: HtmlSanitizeEx.basic_html(note.data["object"]["summary"]),
108 visibility: "public",
109 media_attachments: [],
110 mentions: [],
111 tags: [
112 %{
113 name: "#{note.data["object"]["tag"]}",
114 url: "/tag/#{note.data["object"]["tag"]}"
115 }
116 ],
117 application: %{
118 name: "Web",
119 website: nil
120 },
121 language: nil,
122 emojis: [
123 %{
124 shortcode: "2hu",
125 url: "corndog.png",
126 static_url: "corndog.png",
127 visible_in_picker: false
128 }
129 ],
130 pleroma: %{
131 local: true,
132 conversation_id: convo_id,
133 in_reply_to_account_acct: nil,
134 content: %{"text/plain" => HtmlSanitizeEx.strip_tags(note.data["object"]["content"])},
135 spoiler_text: %{"text/plain" => HtmlSanitizeEx.strip_tags(note.data["object"]["summary"])}
136 }
137 }
138
139 assert status == expected
140 end
141
142 test "tells if the message is muted for some reason" do
143 user = insert(:user)
144 other_user = insert(:user)
145
146 {:ok, user} = User.mute(user, other_user)
147
148 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "test"})
149 status = StatusView.render("status.json", %{activity: activity})
150
151 assert status.muted == false
152
153 status = StatusView.render("status.json", %{activity: activity, for: user})
154
155 assert status.muted == true
156 end
157
158 test "tells if the status is bookmarked" do
159 user = insert(:user)
160
161 {:ok, activity} = CommonAPI.post(user, %{"status" => "Cute girls doing cute things"})
162 status = StatusView.render("status.json", %{activity: activity})
163
164 assert status.bookmarked == false
165
166 status = StatusView.render("status.json", %{activity: activity, for: user})
167
168 assert status.bookmarked == false
169
170 {:ok, _bookmark} = Bookmark.create(user.id, activity.id)
171
172 activity = Activity.get_by_id_with_object(activity.id)
173
174 status = StatusView.render("status.json", %{activity: activity, for: user})
175
176 assert status.bookmarked == true
177 end
178
179 test "a reply" do
180 note = insert(:note_activity)
181 user = insert(:user)
182
183 {:ok, activity} =
184 CommonAPI.post(user, %{"status" => "he", "in_reply_to_status_id" => note.id})
185
186 status = StatusView.render("status.json", %{activity: activity})
187
188 assert status.in_reply_to_id == to_string(note.id)
189
190 [status] = StatusView.render("index.json", %{activities: [activity], as: :activity})
191
192 assert status.in_reply_to_id == to_string(note.id)
193 end
194
195 test "contains mentions" do
196 incoming = File.read!("test/fixtures/incoming_reply_mastodon.xml")
197 # a user with this ap id might be in the cache.
198 recipient = "https://pleroma.soykaf.com/users/lain"
199 user = insert(:user, %{ap_id: recipient})
200
201 {:ok, [activity]} = OStatus.handle_incoming(incoming)
202
203 status = StatusView.render("status.json", %{activity: activity})
204
205 actor = User.get_cached_by_ap_id(activity.actor)
206
207 assert status.mentions ==
208 Enum.map([user, actor], fn u -> AccountView.render("mention.json", %{user: u}) end)
209 end
210
211 test "attachments" do
212 object = %{
213 "type" => "Image",
214 "url" => [
215 %{
216 "mediaType" => "image/png",
217 "href" => "someurl"
218 }
219 ],
220 "uuid" => 6
221 }
222
223 expected = %{
224 id: "1638338801",
225 type: "image",
226 url: "someurl",
227 remote_url: "someurl",
228 preview_url: "someurl",
229 text_url: "someurl",
230 description: nil,
231 pleroma: %{mime_type: "image/png"}
232 }
233
234 assert expected == StatusView.render("attachment.json", %{attachment: object})
235
236 # If theres a "id", use that instead of the generated one
237 object = Map.put(object, "id", 2)
238 assert %{id: "2"} = StatusView.render("attachment.json", %{attachment: object})
239 end
240
241 test "a reblog" do
242 user = insert(:user)
243 activity = insert(:note_activity)
244
245 {:ok, reblog, _} = CommonAPI.repeat(activity.id, user)
246
247 represented = StatusView.render("status.json", %{for: user, activity: reblog})
248
249 assert represented[:id] == to_string(reblog.id)
250 assert represented[:reblog][:id] == to_string(activity.id)
251 assert represented[:emojis] == []
252 end
253
254 test "a peertube video" do
255 user = insert(:user)
256
257 {:ok, object} =
258 Pleroma.Object.Fetcher.fetch_object_from_id(
259 "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3"
260 )
261
262 %Activity{} = activity = Activity.get_create_by_object_ap_id(object.data["id"])
263
264 represented = StatusView.render("status.json", %{for: user, activity: activity})
265
266 assert represented[:id] == to_string(activity.id)
267 assert length(represented[:media_attachments]) == 1
268 end
269
270 describe "build_tags/1" do
271 test "it returns a a dictionary tags" do
272 object_tags = [
273 "fediverse",
274 "mastodon",
275 "nextcloud",
276 %{
277 "href" => "https://kawen.space/users/lain",
278 "name" => "@lain@kawen.space",
279 "type" => "Mention"
280 }
281 ]
282
283 assert StatusView.build_tags(object_tags) == [
284 %{name: "fediverse", url: "/tag/fediverse"},
285 %{name: "mastodon", url: "/tag/mastodon"},
286 %{name: "nextcloud", url: "/tag/nextcloud"}
287 ]
288 end
289 end
290
291 describe "rich media cards" do
292 test "a rich media card without a site name renders correctly" do
293 page_url = "http://example.com"
294
295 card = %{
296 url: page_url,
297 image: page_url <> "/example.jpg",
298 title: "Example website"
299 }
300
301 %{provider_name: "example.com"} =
302 StatusView.render("card.json", %{page_url: page_url, rich_media: card})
303 end
304
305 test "a rich media card without a site name or image renders correctly" do
306 page_url = "http://example.com"
307
308 card = %{
309 url: page_url,
310 title: "Example website"
311 }
312
313 %{provider_name: "example.com"} =
314 StatusView.render("card.json", %{page_url: page_url, rich_media: card})
315 end
316
317 test "a rich media card without an image renders correctly" do
318 page_url = "http://example.com"
319
320 card = %{
321 url: page_url,
322 site_name: "Example site name",
323 title: "Example website"
324 }
325
326 %{provider_name: "Example site name"} =
327 StatusView.render("card.json", %{page_url: page_url, rich_media: card})
328 end
329
330 test "a rich media card with all relevant data renders correctly" do
331 page_url = "http://example.com"
332
333 card = %{
334 url: page_url,
335 site_name: "Example site name",
336 title: "Example website",
337 image: page_url <> "/example.jpg",
338 description: "Example description"
339 }
340
341 %{provider_name: "Example site name"} =
342 StatusView.render("card.json", %{page_url: page_url, rich_media: card})
343 end
344 end
345
346 describe "poll view" do
347 test "renders a poll" do
348 user = insert(:user)
349
350 {:ok, activity} =
351 CommonAPI.post(user, %{
352 "status" => "Is Tenshi eating a corndog cute?",
353 "poll" => %{
354 "options" => ["absolutely!", "sure", "yes", "why are you even asking?"],
355 "expires_in" => 20
356 }
357 })
358
359 object = Object.normalize(activity)
360
361 expected = %{
362 emojis: [],
363 expired: false,
364 id: object.id,
365 multiple: false,
366 options: [
367 %{title: "absolutely!", votes_count: 0},
368 %{title: "sure", votes_count: 0},
369 %{title: "yes", votes_count: 0},
370 %{title: "why are you even asking?", votes_count: 0}
371 ],
372 voted: false,
373 votes_count: 0
374 }
375
376 result = StatusView.render("poll.json", %{object: object})
377 expires_at = result.expires_at
378 result = Map.delete(result, :expires_at)
379
380 assert result == expected
381
382 expires_at = NaiveDateTime.from_iso8601!(expires_at)
383 assert NaiveDateTime.diff(expires_at, NaiveDateTime.utc_now()) in 15..20
384 end
385
386 test "detects if it is multiple choice" do
387 user = insert(:user)
388
389 {:ok, activity} =
390 CommonAPI.post(user, %{
391 "status" => "Which Mastodon developer is your favourite?",
392 "poll" => %{
393 "options" => ["Gargron", "Eugen"],
394 "expires_in" => 20,
395 "multiple" => true
396 }
397 })
398
399 object = Object.normalize(activity)
400
401 assert %{multiple: true} = StatusView.render("poll.json", %{object: object})
402 end
403
404 test "detects emoji" do
405 user = insert(:user)
406
407 {:ok, activity} =
408 CommonAPI.post(user, %{
409 "status" => "What's with the smug face?",
410 "poll" => %{
411 "options" => [":blank: sip", ":blank::blank: sip", ":blank::blank::blank: sip"],
412 "expires_in" => 20
413 }
414 })
415
416 object = Object.normalize(activity)
417
418 assert %{emojis: [%{shortcode: "blank"}]} =
419 StatusView.render("poll.json", %{object: object})
420 end
421
422 test "detects vote status" do
423 user = insert(:user)
424 other_user = insert(:user)
425
426 {:ok, activity} =
427 CommonAPI.post(user, %{
428 "status" => "Which input devices do you use?",
429 "poll" => %{
430 "options" => ["mouse", "trackball", "trackpoint"],
431 "multiple" => true,
432 "expires_in" => 20
433 }
434 })
435
436 object = Object.normalize(activity)
437
438 {:ok, _, object} = CommonAPI.vote(other_user, object, [1, 2])
439
440 result = StatusView.render("poll.json", %{object: object, for: other_user})
441
442 assert result[:voted] == true
443 assert Enum.at(result[:options], 1)[:votes_count] == 1
444 assert Enum.at(result[:options], 2)[:votes_count] == 1
445 end
446 end
447 end