4d6c9ea2610786def850afffaecec5bef223ecf2
[akkoma] / test / pleroma / web / common_api / utils_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.CommonAPI.UtilsTest do
6 alias Pleroma.Builders.UserBuilder
7 alias Pleroma.Object
8 alias Pleroma.Web.CommonAPI
9 alias Pleroma.Web.CommonAPI.ActivityDraft
10 alias Pleroma.Web.CommonAPI.Utils
11 use Pleroma.DataCase
12
13 import ExUnit.CaptureLog
14 import Pleroma.Factory
15
16 @public_address "https://www.w3.org/ns/activitystreams#Public"
17
18 describe "add_attachments/2" do
19 setup do
20 name =
21 "Sakura Mana – Turned on by a Senior OL with a Temptating Tight Skirt-s Full Hipline and Panty Shot- Beautiful Thick Thighs- and Erotic Ass- -2015- -- Oppaitime 8-28-2017 6-50-33 PM.png"
22
23 attachment = %{
24 "url" => [%{"href" => URI.encode(name)}]
25 }
26
27 %{name: name, attachment: attachment}
28 end
29
30 test "it adds attachment links to a given text and attachment set", %{
31 name: name,
32 attachment: attachment
33 } do
34 len = 10
35 clear_config([Pleroma.Upload, :filename_display_max_length], len)
36
37 expected =
38 "<br><a href=\"#{URI.encode(name)}\" class='attachment'>#{String.slice(name, 0..len)}…</a>"
39
40 assert Utils.add_attachments("", [attachment]) == expected
41 end
42
43 test "doesn't truncate file name if config for truncate is set to 0", %{
44 name: name,
45 attachment: attachment
46 } do
47 clear_config([Pleroma.Upload, :filename_display_max_length], 0)
48
49 expected = "<br><a href=\"#{URI.encode(name)}\" class='attachment'>#{name}</a>"
50
51 assert Utils.add_attachments("", [attachment]) == expected
52 end
53 end
54
55 describe "it confirms the password given is the current users password" do
56 test "incorrect password given" do
57 {:ok, user} = UserBuilder.insert()
58
59 assert Utils.confirm_current_password(user, "") == {:error, "Invalid password."}
60 end
61
62 test "correct password given" do
63 {:ok, user} = UserBuilder.insert()
64 assert Utils.confirm_current_password(user, "test") == {:ok, user}
65 end
66 end
67
68 describe "format_input/3" do
69 test "works for bare text/plain" do
70 text = "hello world!"
71 expected = "hello world!"
72
73 {output, [], []} = Utils.format_input(text, "text/plain")
74
75 assert output == expected
76
77 text = "hello world!\n\nsecond paragraph!"
78 expected = "hello world!<br><br>second paragraph!"
79
80 {output, [], []} = Utils.format_input(text, "text/plain")
81
82 assert output == expected
83 end
84
85 test "works for bare text/html" do
86 text = "<p>hello world!</p>"
87 expected = "<p>hello world!</p>"
88
89 {output, [], []} = Utils.format_input(text, "text/html")
90
91 assert output == expected
92
93 text = "<p>hello world!</p><br/>\n<p>second paragraph</p>"
94 expected = "<p>hello world!</p><br/>\n<p>second paragraph</p>"
95
96 {output, [], []} = Utils.format_input(text, "text/html")
97
98 assert output == expected
99 end
100
101 test "works for bare text/markdown" do
102 text = "**hello world**"
103 expected = "<p><strong>hello world</strong></p>"
104
105 {output, [], []} = Utils.format_input(text, "text/markdown")
106
107 assert output == expected
108
109 text = "**hello world**\n\n*another paragraph*"
110 expected = "<p><strong>hello world</strong></p><p><em>another paragraph</em></p>"
111
112 {output, [], []} = Utils.format_input(text, "text/markdown")
113
114 assert output == expected
115
116 text = """
117 > cool quote
118
119 by someone
120 """
121
122 expected = "<blockquote><p>cool quote</p></blockquote><p>by someone</p>"
123
124 {output, [], []} = Utils.format_input(text, "text/markdown")
125
126 assert output == expected
127 end
128
129 test "works for bare text/bbcode" do
130 text = "[b]hello world[/b]"
131 expected = "<strong>hello world</strong>"
132
133 {output, [], []} = Utils.format_input(text, "text/bbcode")
134
135 assert output == expected
136
137 text = "[b]hello world![/b]\n\nsecond paragraph!"
138 expected = "<strong>hello world!</strong><br><br>second paragraph!"
139
140 {output, [], []} = Utils.format_input(text, "text/bbcode")
141
142 assert output == expected
143
144 text = "[b]hello world![/b]\n\n<strong>second paragraph!</strong>"
145
146 expected =
147 "<strong>hello world!</strong><br><br>&lt;strong&gt;second paragraph!&lt;/strong&gt;"
148
149 {output, [], []} = Utils.format_input(text, "text/bbcode")
150
151 assert output == expected
152 end
153
154 test "works for text/markdown with mentions" do
155 {:ok, user} =
156 UserBuilder.insert(%{nickname: "user__test", ap_id: "http://foo.com/user__test"})
157
158 text = "**hello world**\n\n*another @user__test and @user__test google.com paragraph*"
159
160 {output, _, _} = Utils.format_input(text, "text/markdown")
161
162 assert output ==
163 ~s(<p><strong>hello world</strong></p><p><em>another <span class="h-card"><a class="u-url mention" data-user="#{
164 user.id
165 }" href="http://foo.com/user__test" rel="ugc">@<span>user__test</span></a></span> and <span class="h-card"><a class="u-url mention" data-user="#{
166 user.id
167 }" href="http://foo.com/user__test" rel="ugc">@<span>user__test</span></a></span> <a href="http://google.com" rel="ugc">google.com</a> paragraph</em></p>)
168 end
169 end
170
171 describe "context_to_conversation_id" do
172 test "creates a mapping object" do
173 conversation_id = Utils.context_to_conversation_id("random context")
174 object = Object.get_by_ap_id("random context")
175
176 assert conversation_id == object.id
177 end
178
179 test "returns an existing mapping for an existing object" do
180 {:ok, object} = Object.context_mapping("random context") |> Repo.insert()
181 conversation_id = Utils.context_to_conversation_id("random context")
182
183 assert conversation_id == object.id
184 end
185 end
186
187 describe "formats date to asctime" do
188 test "when date is in ISO 8601 format" do
189 date = DateTime.utc_now() |> DateTime.to_iso8601()
190
191 expected =
192 date
193 |> DateTime.from_iso8601()
194 |> elem(1)
195 |> Calendar.Strftime.strftime!("%a %b %d %H:%M:%S %z %Y")
196
197 assert Utils.date_to_asctime(date) == expected
198 end
199
200 test "when date is a binary in wrong format" do
201 date = DateTime.utc_now()
202
203 expected = ""
204
205 assert capture_log(fn ->
206 assert Utils.date_to_asctime(date) == expected
207 end) =~ "[warn] Date #{date} in wrong format, must be ISO 8601"
208 end
209
210 test "when date is a Unix timestamp" do
211 date = DateTime.utc_now() |> DateTime.to_unix()
212
213 expected = ""
214
215 assert capture_log(fn ->
216 assert Utils.date_to_asctime(date) == expected
217 end) =~ "[warn] Date #{date} in wrong format, must be ISO 8601"
218 end
219
220 test "when date is nil" do
221 expected = ""
222
223 assert capture_log(fn ->
224 assert Utils.date_to_asctime(nil) == expected
225 end) =~ "[warn] Date in wrong format, must be ISO 8601"
226 end
227
228 test "when date is a random string" do
229 assert capture_log(fn ->
230 assert Utils.date_to_asctime("foo") == ""
231 end) =~ "[warn] Date foo in wrong format, must be ISO 8601"
232 end
233 end
234
235 describe "get_to_and_cc" do
236 test "for public posts, not a reply" do
237 user = insert(:user)
238 mentioned_user = insert(:user)
239 draft = %ActivityDraft{user: user, mentions: [mentioned_user.ap_id], visibility: "public"}
240
241 {to, cc} = Utils.get_to_and_cc(draft)
242
243 assert length(to) == 2
244 assert length(cc) == 1
245
246 assert @public_address in to
247 assert mentioned_user.ap_id in to
248 assert user.follower_address in cc
249 end
250
251 test "for public posts, a reply" do
252 user = insert(:user)
253 mentioned_user = insert(:user)
254 third_user = insert(:user)
255 {:ok, activity} = CommonAPI.post(third_user, %{status: "uguu"})
256
257 draft = %ActivityDraft{
258 user: user,
259 mentions: [mentioned_user.ap_id],
260 visibility: "public",
261 in_reply_to: activity
262 }
263
264 {to, cc} = Utils.get_to_and_cc(draft)
265
266 assert length(to) == 3
267 assert length(cc) == 1
268
269 assert @public_address in to
270 assert mentioned_user.ap_id in to
271 assert third_user.ap_id in to
272 assert user.follower_address in cc
273 end
274
275 test "for unlisted posts, not a reply" do
276 user = insert(:user)
277 mentioned_user = insert(:user)
278 draft = %ActivityDraft{user: user, mentions: [mentioned_user.ap_id], visibility: "unlisted"}
279
280 {to, cc} = Utils.get_to_and_cc(draft)
281
282 assert length(to) == 2
283 assert length(cc) == 1
284
285 assert @public_address in cc
286 assert mentioned_user.ap_id in to
287 assert user.follower_address in to
288 end
289
290 test "for unlisted posts, a reply" do
291 user = insert(:user)
292 mentioned_user = insert(:user)
293 third_user = insert(:user)
294 {:ok, activity} = CommonAPI.post(third_user, %{status: "uguu"})
295
296 draft = %ActivityDraft{
297 user: user,
298 mentions: [mentioned_user.ap_id],
299 visibility: "unlisted",
300 in_reply_to: activity
301 }
302
303 {to, cc} = Utils.get_to_and_cc(draft)
304
305 assert length(to) == 3
306 assert length(cc) == 1
307
308 assert @public_address in cc
309 assert mentioned_user.ap_id in to
310 assert third_user.ap_id in to
311 assert user.follower_address in to
312 end
313
314 test "for private posts, not a reply" do
315 user = insert(:user)
316 mentioned_user = insert(:user)
317 draft = %ActivityDraft{user: user, mentions: [mentioned_user.ap_id], visibility: "private"}
318
319 {to, cc} = Utils.get_to_and_cc(draft)
320 assert length(to) == 2
321 assert Enum.empty?(cc)
322
323 assert mentioned_user.ap_id in to
324 assert user.follower_address in to
325 end
326
327 test "for private posts, a reply" do
328 user = insert(:user)
329 mentioned_user = insert(:user)
330 third_user = insert(:user)
331 {:ok, activity} = CommonAPI.post(third_user, %{status: "uguu"})
332
333 draft = %ActivityDraft{
334 user: user,
335 mentions: [mentioned_user.ap_id],
336 visibility: "private",
337 in_reply_to: activity
338 }
339
340 {to, cc} = Utils.get_to_and_cc(draft)
341
342 assert length(to) == 2
343 assert Enum.empty?(cc)
344
345 assert mentioned_user.ap_id in to
346 assert user.follower_address in to
347 end
348
349 test "for direct posts, not a reply" do
350 user = insert(:user)
351 mentioned_user = insert(:user)
352 draft = %ActivityDraft{user: user, mentions: [mentioned_user.ap_id], visibility: "direct"}
353
354 {to, cc} = Utils.get_to_and_cc(draft)
355
356 assert length(to) == 1
357 assert Enum.empty?(cc)
358
359 assert mentioned_user.ap_id in to
360 end
361
362 test "for direct posts, a reply" do
363 user = insert(:user)
364 mentioned_user = insert(:user)
365 third_user = insert(:user)
366 {:ok, activity} = CommonAPI.post(third_user, %{status: "uguu"})
367
368 draft = %ActivityDraft{
369 user: user,
370 mentions: [mentioned_user.ap_id],
371 visibility: "direct",
372 in_reply_to: activity
373 }
374
375 {to, cc} = Utils.get_to_and_cc(draft)
376
377 assert length(to) == 1
378 assert Enum.empty?(cc)
379
380 assert mentioned_user.ap_id in to
381
382 {:ok, direct_activity} = CommonAPI.post(third_user, %{status: "uguu", visibility: "direct"})
383
384 draft = %ActivityDraft{
385 user: user,
386 mentions: [mentioned_user.ap_id],
387 visibility: "direct",
388 in_reply_to: direct_activity
389 }
390
391 {to, cc} = Utils.get_to_and_cc(draft)
392
393 assert length(to) == 2
394 assert Enum.empty?(cc)
395
396 assert mentioned_user.ap_id in to
397 assert third_user.ap_id in to
398 end
399 end
400
401 describe "to_master_date/1" do
402 test "removes microseconds from date (NaiveDateTime)" do
403 assert Utils.to_masto_date(~N[2015-01-23 23:50:07.123]) == "2015-01-23T23:50:07.000Z"
404 end
405
406 test "removes microseconds from date (String)" do
407 assert Utils.to_masto_date("2015-01-23T23:50:07.123Z") == "2015-01-23T23:50:07.000Z"
408 end
409
410 test "returns empty string when date invalid" do
411 assert Utils.to_masto_date("2015-01?23T23:50:07.123Z") == ""
412 end
413 end
414
415 describe "conversation_id_to_context/1" do
416 test "returns id" do
417 object = insert(:note)
418 assert Utils.conversation_id_to_context(object.id) == object.data["id"]
419 end
420
421 test "returns error if object not found" do
422 assert Utils.conversation_id_to_context("123") == {:error, "No such conversation"}
423 end
424 end
425
426 describe "maybe_notify_mentioned_recipients/2" do
427 test "returns recipients when activity is not `Create`" do
428 activity = insert(:like_activity)
429 assert Utils.maybe_notify_mentioned_recipients(["test"], activity) == ["test"]
430 end
431
432 test "returns recipients from tag" do
433 user = insert(:user)
434
435 object =
436 insert(:note,
437 user: user,
438 data: %{
439 "tag" => [
440 %{"type" => "Hashtag"},
441 "",
442 %{"type" => "Mention", "href" => "https://testing.pleroma.lol/users/lain"},
443 %{"type" => "Mention", "href" => "https://shitposter.club/user/5381"},
444 %{"type" => "Mention", "href" => "https://shitposter.club/user/5381"}
445 ]
446 }
447 )
448
449 activity = insert(:note_activity, user: user, note: object)
450
451 assert Utils.maybe_notify_mentioned_recipients(["test"], activity) == [
452 "test",
453 "https://testing.pleroma.lol/users/lain",
454 "https://shitposter.club/user/5381"
455 ]
456 end
457
458 test "returns recipients when object is map" do
459 user = insert(:user)
460 object = insert(:note, user: user)
461
462 activity =
463 insert(:note_activity,
464 user: user,
465 note: object,
466 data_attrs: %{
467 "object" => %{
468 "tag" => [
469 %{"type" => "Hashtag"},
470 "",
471 %{"type" => "Mention", "href" => "https://testing.pleroma.lol/users/lain"},
472 %{"type" => "Mention", "href" => "https://shitposter.club/user/5381"},
473 %{"type" => "Mention", "href" => "https://shitposter.club/user/5381"}
474 ]
475 }
476 }
477 )
478
479 Pleroma.Repo.delete(object)
480
481 assert Utils.maybe_notify_mentioned_recipients(["test"], activity) == [
482 "test",
483 "https://testing.pleroma.lol/users/lain",
484 "https://shitposter.club/user/5381"
485 ]
486 end
487
488 test "returns recipients when object not found" do
489 user = insert(:user)
490 object = insert(:note, user: user)
491
492 activity = insert(:note_activity, user: user, note: object)
493 Pleroma.Repo.delete(object)
494
495 obj_url = activity.data["object"]
496
497 Tesla.Mock.mock(fn
498 %{method: :get, url: ^obj_url} ->
499 %Tesla.Env{status: 404, body: ""}
500 end)
501
502 assert Utils.maybe_notify_mentioned_recipients(["test-test"], activity) == [
503 "test-test"
504 ]
505 end
506 end
507
508 describe "attachments_from_ids_descs/2" do
509 test "returns [] when attachment ids is empty" do
510 assert Utils.attachments_from_ids_descs([], "{}") == []
511 end
512
513 test "returns list attachments with desc" do
514 object = insert(:note)
515 desc = Jason.encode!(%{object.id => "test-desc"})
516
517 assert Utils.attachments_from_ids_descs(["#{object.id}", "34"], desc) == [
518 Map.merge(object.data, %{"name" => "test-desc"})
519 ]
520 end
521 end
522
523 describe "attachments_from_ids/1" do
524 test "returns attachments with descs" do
525 object = insert(:note)
526 desc = Jason.encode!(%{object.id => "test-desc"})
527
528 assert Utils.attachments_from_ids(%{
529 media_ids: ["#{object.id}"],
530 descriptions: desc
531 }) == [
532 Map.merge(object.data, %{"name" => "test-desc"})
533 ]
534 end
535
536 test "returns attachments without descs" do
537 object = insert(:note)
538 assert Utils.attachments_from_ids(%{media_ids: ["#{object.id}"]}) == [object.data]
539 end
540
541 test "returns [] when not pass media_ids" do
542 assert Utils.attachments_from_ids(%{}) == []
543 end
544 end
545
546 describe "maybe_add_list_data/3" do
547 test "adds list params when found user list" do
548 user = insert(:user)
549 {:ok, %Pleroma.List{} = list} = Pleroma.List.create("title", user)
550
551 assert Utils.maybe_add_list_data(%{additional: %{}, object: %{}}, user, {:list, list.id}) ==
552 %{
553 additional: %{"bcc" => [list.ap_id], "listMessage" => list.ap_id},
554 object: %{"listMessage" => list.ap_id}
555 }
556 end
557
558 test "returns original params when list not found" do
559 user = insert(:user)
560 {:ok, %Pleroma.List{} = list} = Pleroma.List.create("title", insert(:user))
561
562 assert Utils.maybe_add_list_data(%{additional: %{}, object: %{}}, user, {:list, list.id}) ==
563 %{additional: %{}, object: %{}}
564 end
565 end
566
567 describe "make_note_data/1" do
568 test "returns note data" do
569 user = insert(:user)
570 note = insert(:note)
571 user2 = insert(:user)
572 user3 = insert(:user)
573
574 draft = %ActivityDraft{
575 user: user,
576 to: [user2.ap_id],
577 context: "2hu",
578 content_html: "<h1>This is :moominmamma: note</h1>",
579 in_reply_to: note.id,
580 tags: [name: "jimm"],
581 summary: "test summary",
582 cc: [user3.ap_id],
583 extra: %{"custom_tag" => "test"}
584 }
585
586 assert Utils.make_note_data(draft) == %{
587 "actor" => user.ap_id,
588 "attachment" => [],
589 "cc" => [user3.ap_id],
590 "content" => "<h1>This is :moominmamma: note</h1>",
591 "context" => "2hu",
592 "sensitive" => false,
593 "summary" => "test summary",
594 "tag" => ["jimm"],
595 "to" => [user2.ap_id],
596 "type" => "Note",
597 "custom_tag" => "test"
598 }
599 end
600 end
601
602 describe "maybe_add_attachments/3" do
603 test "returns parsed results when attachment_links is false" do
604 assert Utils.maybe_add_attachments(
605 {"test", [], ["tags"]},
606 [],
607 false
608 ) == {"test", [], ["tags"]}
609 end
610
611 test "adds attachments to parsed results" do
612 attachment = %{"url" => [%{"href" => "SakuraPM.png"}]}
613
614 assert Utils.maybe_add_attachments(
615 {"test", [], ["tags"]},
616 [attachment],
617 true
618 ) == {
619 "test<br><a href=\"SakuraPM.png\" class='attachment'>SakuraPM.png</a>",
620 [],
621 ["tags"]
622 }
623 end
624 end
625 end