CommonAPI: Newlines -> br for chat messages.
[akkoma] / test / web / common_api / common_api_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.CommonAPITest do
6 use Pleroma.DataCase
7 alias Pleroma.Activity
8 alias Pleroma.Chat
9 alias Pleroma.Conversation.Participation
10 alias Pleroma.Object
11 alias Pleroma.User
12 alias Pleroma.Web.ActivityPub.ActivityPub
13 alias Pleroma.Web.ActivityPub.Transmogrifier
14 alias Pleroma.Web.ActivityPub.Visibility
15 alias Pleroma.Web.AdminAPI.AccountView
16 alias Pleroma.Web.CommonAPI
17
18 import Pleroma.Factory
19 import Mock
20
21 require Pleroma.Constants
22
23 setup do: clear_config([:instance, :safe_dm_mentions])
24 setup do: clear_config([:instance, :limit])
25 setup do: clear_config([:instance, :max_pinned_statuses])
26
27 describe "posting chat messages" do
28 setup do: clear_config([:instance, :chat_limit])
29
30 test "it posts a chat message without content but with an attachment" do
31 author = insert(:user)
32 recipient = insert(:user)
33
34 file = %Plug.Upload{
35 content_type: "image/jpg",
36 path: Path.absname("test/fixtures/image.jpg"),
37 filename: "an_image.jpg"
38 }
39
40 {:ok, upload} = ActivityPub.upload(file, actor: author.ap_id)
41
42 {:ok, activity} =
43 CommonAPI.post_chat_message(
44 author,
45 recipient,
46 nil,
47 media_id: upload.id
48 )
49
50 assert activity
51 end
52
53 test "it adds html newlines" do
54 author = insert(:user)
55 recipient = insert(:user)
56
57 other_user = insert(:user)
58
59 {:ok, activity} =
60 CommonAPI.post_chat_message(
61 author,
62 recipient,
63 "uguu\nuguuu"
64 )
65
66 assert other_user.ap_id not in activity.recipients
67
68 object = Object.normalize(activity, false)
69
70 assert object.data["content"] == "uguu<br/>uguuu"
71 end
72
73 test "it linkifies" do
74 author = insert(:user)
75 recipient = insert(:user)
76
77 other_user = insert(:user)
78
79 {:ok, activity} =
80 CommonAPI.post_chat_message(
81 author,
82 recipient,
83 "https://example.org is the site of @#{other_user.nickname} #2hu"
84 )
85
86 assert other_user.ap_id not in activity.recipients
87
88 object = Object.normalize(activity, false)
89
90 assert object.data["content"] ==
91 "<a href=\"https://example.org\" rel=\"ugc\">https://example.org</a> is the site of <span class=\"h-card\"><a class=\"u-url mention\" data-user=\"#{
92 other_user.id
93 }\" href=\"#{other_user.ap_id}\" rel=\"ugc\">@<span>#{other_user.nickname}</span></a></span> <a class=\"hashtag\" data-tag=\"2hu\" href=\"http://localhost:4001/tag/2hu\">#2hu</a>"
94 end
95
96 test "it posts a chat message" do
97 author = insert(:user)
98 recipient = insert(:user)
99
100 {:ok, activity} =
101 CommonAPI.post_chat_message(
102 author,
103 recipient,
104 "a test message <script>alert('uuu')</script> :firefox:"
105 )
106
107 assert activity.data["type"] == "Create"
108 assert activity.local
109 object = Object.normalize(activity)
110
111 assert object.data["type"] == "ChatMessage"
112 assert object.data["to"] == [recipient.ap_id]
113
114 assert object.data["content"] ==
115 "a test message &lt;script&gt;alert(&#39;uuu&#39;)&lt;/script&gt; :firefox:"
116
117 assert object.data["emoji"] == %{
118 "firefox" => "http://localhost:4001/emoji/Firefox.gif"
119 }
120
121 assert Chat.get(author.id, recipient.ap_id)
122 assert Chat.get(recipient.id, author.ap_id)
123
124 assert :ok == Pleroma.Web.Federator.perform(:publish, activity)
125 end
126
127 test "it reject messages over the local limit" do
128 Pleroma.Config.put([:instance, :chat_limit], 2)
129
130 author = insert(:user)
131 recipient = insert(:user)
132
133 {:error, message} =
134 CommonAPI.post_chat_message(
135 author,
136 recipient,
137 "123"
138 )
139
140 assert message == :content_too_long
141 end
142 end
143
144 describe "unblocking" do
145 test "it works even without an existing block activity" do
146 blocked = insert(:user)
147 blocker = insert(:user)
148 User.block(blocker, blocked)
149
150 assert User.blocks?(blocker, blocked)
151 assert {:ok, :no_activity} == CommonAPI.unblock(blocker, blocked)
152 refute User.blocks?(blocker, blocked)
153 end
154 end
155
156 describe "deletion" do
157 test "it works with pruned objects" do
158 user = insert(:user)
159
160 {:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
161
162 clear_config([:instance, :federating], true)
163
164 Object.normalize(post, false)
165 |> Object.prune()
166
167 with_mock Pleroma.Web.Federator,
168 publish: fn _ -> nil end do
169 assert {:ok, delete} = CommonAPI.delete(post.id, user)
170 assert delete.local
171 assert called(Pleroma.Web.Federator.publish(delete))
172 end
173
174 refute Activity.get_by_id(post.id)
175 end
176
177 test "it allows users to delete their posts" do
178 user = insert(:user)
179
180 {:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
181
182 clear_config([:instance, :federating], true)
183
184 with_mock Pleroma.Web.Federator,
185 publish: fn _ -> nil end do
186 assert {:ok, delete} = CommonAPI.delete(post.id, user)
187 assert delete.local
188 assert called(Pleroma.Web.Federator.publish(delete))
189 end
190
191 refute Activity.get_by_id(post.id)
192 end
193
194 test "it does not allow a user to delete their posts" do
195 user = insert(:user)
196 other_user = insert(:user)
197
198 {:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
199
200 assert {:error, "Could not delete"} = CommonAPI.delete(post.id, other_user)
201 assert Activity.get_by_id(post.id)
202 end
203
204 test "it allows moderators to delete other user's posts" do
205 user = insert(:user)
206 moderator = insert(:user, is_moderator: true)
207
208 {:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
209
210 assert {:ok, delete} = CommonAPI.delete(post.id, moderator)
211 assert delete.local
212
213 refute Activity.get_by_id(post.id)
214 end
215
216 test "it allows admins to delete other user's posts" do
217 user = insert(:user)
218 moderator = insert(:user, is_admin: true)
219
220 {:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
221
222 assert {:ok, delete} = CommonAPI.delete(post.id, moderator)
223 assert delete.local
224
225 refute Activity.get_by_id(post.id)
226 end
227
228 test "superusers deleting non-local posts won't federate the delete" do
229 # This is the user of the ingested activity
230 _user =
231 insert(:user,
232 local: false,
233 ap_id: "http://mastodon.example.org/users/admin",
234 last_refreshed_at: NaiveDateTime.utc_now()
235 )
236
237 moderator = insert(:user, is_admin: true)
238
239 data =
240 File.read!("test/fixtures/mastodon-post-activity.json")
241 |> Jason.decode!()
242
243 {:ok, post} = Transmogrifier.handle_incoming(data)
244
245 with_mock Pleroma.Web.Federator,
246 publish: fn _ -> nil end do
247 assert {:ok, delete} = CommonAPI.delete(post.id, moderator)
248 assert delete.local
249 refute called(Pleroma.Web.Federator.publish(:_))
250 end
251
252 refute Activity.get_by_id(post.id)
253 end
254 end
255
256 test "favoriting race condition" do
257 user = insert(:user)
258 users_serial = insert_list(10, :user)
259 users = insert_list(10, :user)
260
261 {:ok, activity} = CommonAPI.post(user, %{status: "."})
262
263 users_serial
264 |> Enum.map(fn user ->
265 CommonAPI.favorite(user, activity.id)
266 end)
267
268 object = Object.get_by_ap_id(activity.data["object"])
269 assert object.data["like_count"] == 10
270
271 users
272 |> Enum.map(fn user ->
273 Task.async(fn ->
274 CommonAPI.favorite(user, activity.id)
275 end)
276 end)
277 |> Enum.map(&Task.await/1)
278
279 object = Object.get_by_ap_id(activity.data["object"])
280 assert object.data["like_count"] == 20
281 end
282
283 test "repeating race condition" do
284 user = insert(:user)
285 users_serial = insert_list(10, :user)
286 users = insert_list(10, :user)
287
288 {:ok, activity} = CommonAPI.post(user, %{status: "."})
289
290 users_serial
291 |> Enum.map(fn user ->
292 CommonAPI.repeat(activity.id, user)
293 end)
294
295 object = Object.get_by_ap_id(activity.data["object"])
296 assert object.data["announcement_count"] == 10
297
298 users
299 |> Enum.map(fn user ->
300 Task.async(fn ->
301 CommonAPI.repeat(activity.id, user)
302 end)
303 end)
304 |> Enum.map(&Task.await/1)
305
306 object = Object.get_by_ap_id(activity.data["object"])
307 assert object.data["announcement_count"] == 20
308 end
309
310 test "when replying to a conversation / participation, it will set the correct context id even if no explicit reply_to is given" do
311 user = insert(:user)
312 {:ok, activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"})
313
314 [participation] = Participation.for_user(user)
315
316 {:ok, convo_reply} =
317 CommonAPI.post(user, %{status: ".", in_reply_to_conversation_id: participation.id})
318
319 assert Visibility.is_direct?(convo_reply)
320
321 assert activity.data["context"] == convo_reply.data["context"]
322 end
323
324 test "when replying to a conversation / participation, it only mentions the recipients explicitly declared in the participation" do
325 har = insert(:user)
326 jafnhar = insert(:user)
327 tridi = insert(:user)
328
329 {:ok, activity} =
330 CommonAPI.post(har, %{
331 status: "@#{jafnhar.nickname} hey",
332 visibility: "direct"
333 })
334
335 assert har.ap_id in activity.recipients
336 assert jafnhar.ap_id in activity.recipients
337
338 [participation] = Participation.for_user(har)
339
340 {:ok, activity} =
341 CommonAPI.post(har, %{
342 status: "I don't really like @#{tridi.nickname}",
343 visibility: "direct",
344 in_reply_to_status_id: activity.id,
345 in_reply_to_conversation_id: participation.id
346 })
347
348 assert har.ap_id in activity.recipients
349 assert jafnhar.ap_id in activity.recipients
350 refute tridi.ap_id in activity.recipients
351 end
352
353 test "with the safe_dm_mention option set, it does not mention people beyond the initial tags" do
354 har = insert(:user)
355 jafnhar = insert(:user)
356 tridi = insert(:user)
357
358 Pleroma.Config.put([:instance, :safe_dm_mentions], true)
359
360 {:ok, activity} =
361 CommonAPI.post(har, %{
362 status: "@#{jafnhar.nickname} hey, i never want to see @#{tridi.nickname} again",
363 visibility: "direct"
364 })
365
366 refute tridi.ap_id in activity.recipients
367 assert jafnhar.ap_id in activity.recipients
368 end
369
370 test "it de-duplicates tags" do
371 user = insert(:user)
372 {:ok, activity} = CommonAPI.post(user, %{status: "#2hu #2HU"})
373
374 object = Object.normalize(activity)
375
376 assert object.data["tag"] == ["2hu"]
377 end
378
379 test "it adds emoji in the object" do
380 user = insert(:user)
381 {:ok, activity} = CommonAPI.post(user, %{status: ":firefox:"})
382
383 assert Object.normalize(activity).data["emoji"]["firefox"]
384 end
385
386 describe "posting" do
387 test "it supports explicit addressing" do
388 user = insert(:user)
389 user_two = insert(:user)
390 user_three = insert(:user)
391 user_four = insert(:user)
392
393 {:ok, activity} =
394 CommonAPI.post(user, %{
395 status:
396 "Hey, I think @#{user_three.nickname} is ugly. @#{user_four.nickname} is alright though.",
397 to: [user_two.nickname, user_four.nickname, "nonexistent"]
398 })
399
400 assert user.ap_id in activity.recipients
401 assert user_two.ap_id in activity.recipients
402 assert user_four.ap_id in activity.recipients
403 refute user_three.ap_id in activity.recipients
404 end
405
406 test "it filters out obviously bad tags when accepting a post as HTML" do
407 user = insert(:user)
408
409 post = "<p><b>2hu</b></p><script>alert('xss')</script>"
410
411 {:ok, activity} =
412 CommonAPI.post(user, %{
413 status: post,
414 content_type: "text/html"
415 })
416
417 object = Object.normalize(activity)
418
419 assert object.data["content"] == "<p><b>2hu</b></p>alert(&#39;xss&#39;)"
420 end
421
422 test "it filters out obviously bad tags when accepting a post as Markdown" do
423 user = insert(:user)
424
425 post = "<p><b>2hu</b></p><script>alert('xss')</script>"
426
427 {:ok, activity} =
428 CommonAPI.post(user, %{
429 status: post,
430 content_type: "text/markdown"
431 })
432
433 object = Object.normalize(activity)
434
435 assert object.data["content"] == "<p><b>2hu</b></p>alert(&#39;xss&#39;)"
436 end
437
438 test "it does not allow replies to direct messages that are not direct messages themselves" do
439 user = insert(:user)
440
441 {:ok, activity} = CommonAPI.post(user, %{status: "suya..", visibility: "direct"})
442
443 assert {:ok, _} =
444 CommonAPI.post(user, %{
445 status: "suya..",
446 visibility: "direct",
447 in_reply_to_status_id: activity.id
448 })
449
450 Enum.each(["public", "private", "unlisted"], fn visibility ->
451 assert {:error, "The message visibility must be direct"} =
452 CommonAPI.post(user, %{
453 status: "suya..",
454 visibility: visibility,
455 in_reply_to_status_id: activity.id
456 })
457 end)
458 end
459
460 test "replying with a direct message will NOT auto-add the author of the reply to the recipient list" do
461 user = insert(:user)
462 other_user = insert(:user)
463 third_user = insert(:user)
464
465 {:ok, post} = CommonAPI.post(user, %{status: "I'm stupid"})
466
467 {:ok, open_answer} =
468 CommonAPI.post(other_user, %{status: "No ur smart", in_reply_to_status_id: post.id})
469
470 # The OP is implicitly added
471 assert user.ap_id in open_answer.recipients
472
473 {:ok, secret_answer} =
474 CommonAPI.post(other_user, %{
475 status: "lol, that guy really is stupid, right, @#{third_user.nickname}?",
476 in_reply_to_status_id: post.id,
477 visibility: "direct"
478 })
479
480 assert third_user.ap_id in secret_answer.recipients
481
482 # The OP is not added
483 refute user.ap_id in secret_answer.recipients
484 end
485
486 test "it allows to address a list" do
487 user = insert(:user)
488 {:ok, list} = Pleroma.List.create("foo", user)
489
490 {:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})
491
492 assert activity.data["bcc"] == [list.ap_id]
493 assert activity.recipients == [list.ap_id, user.ap_id]
494 assert activity.data["listMessage"] == list.ap_id
495 end
496
497 test "it returns error when status is empty and no attachments" do
498 user = insert(:user)
499
500 assert {:error, "Cannot post an empty status without attachments"} =
501 CommonAPI.post(user, %{status: ""})
502 end
503
504 test "it validates character limits are correctly enforced" do
505 Pleroma.Config.put([:instance, :limit], 5)
506
507 user = insert(:user)
508
509 assert {:error, "The status is over the character limit"} =
510 CommonAPI.post(user, %{status: "foobar"})
511
512 assert {:ok, activity} = CommonAPI.post(user, %{status: "12345"})
513 end
514
515 test "it can handle activities that expire" do
516 user = insert(:user)
517
518 expires_at =
519 NaiveDateTime.utc_now()
520 |> NaiveDateTime.truncate(:second)
521 |> NaiveDateTime.add(1_000_000, :second)
522
523 assert {:ok, activity} = CommonAPI.post(user, %{status: "chai", expires_in: 1_000_000})
524
525 assert expiration = Pleroma.ActivityExpiration.get_by_activity_id(activity.id)
526 assert expiration.scheduled_at == expires_at
527 end
528 end
529
530 describe "reactions" do
531 test "reacting to a status with an emoji" do
532 user = insert(:user)
533 other_user = insert(:user)
534
535 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
536
537 {:ok, reaction} = CommonAPI.react_with_emoji(activity.id, user, "👍")
538
539 assert reaction.data["actor"] == user.ap_id
540 assert reaction.data["content"] == "👍"
541
542 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
543
544 {:error, _} = CommonAPI.react_with_emoji(activity.id, user, ".")
545 end
546
547 test "unreacting to a status with an emoji" do
548 user = insert(:user)
549 other_user = insert(:user)
550
551 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
552 {:ok, reaction} = CommonAPI.react_with_emoji(activity.id, user, "👍")
553
554 {:ok, unreaction} = CommonAPI.unreact_with_emoji(activity.id, user, "👍")
555
556 assert unreaction.data["type"] == "Undo"
557 assert unreaction.data["object"] == reaction.data["id"]
558 assert unreaction.local
559 end
560
561 test "repeating a status" do
562 user = insert(:user)
563 other_user = insert(:user)
564
565 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
566
567 {:ok, %Activity{} = announce_activity} = CommonAPI.repeat(activity.id, user)
568 assert Visibility.is_public?(announce_activity)
569 end
570
571 test "can't repeat a repeat" do
572 user = insert(:user)
573 other_user = insert(:user)
574 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
575
576 {:ok, %Activity{} = announce} = CommonAPI.repeat(activity.id, other_user)
577
578 refute match?({:ok, %Activity{}}, CommonAPI.repeat(announce.id, user))
579 end
580
581 test "repeating a status privately" do
582 user = insert(:user)
583 other_user = insert(:user)
584
585 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
586
587 {:ok, %Activity{} = announce_activity} =
588 CommonAPI.repeat(activity.id, user, %{visibility: "private"})
589
590 assert Visibility.is_private?(announce_activity)
591 refute Visibility.visible_for_user?(announce_activity, nil)
592 end
593
594 test "favoriting a status" do
595 user = insert(:user)
596 other_user = insert(:user)
597
598 {:ok, post_activity} = CommonAPI.post(other_user, %{status: "cofe"})
599
600 {:ok, %Activity{data: data}} = CommonAPI.favorite(user, post_activity.id)
601 assert data["type"] == "Like"
602 assert data["actor"] == user.ap_id
603 assert data["object"] == post_activity.data["object"]
604 end
605
606 test "retweeting a status twice returns the status" do
607 user = insert(:user)
608 other_user = insert(:user)
609
610 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
611 {:ok, %Activity{} = announce} = CommonAPI.repeat(activity.id, user)
612 {:ok, ^announce} = CommonAPI.repeat(activity.id, user)
613 end
614
615 test "favoriting a status twice returns ok, but without the like activity" do
616 user = insert(:user)
617 other_user = insert(:user)
618
619 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
620 {:ok, %Activity{}} = CommonAPI.favorite(user, activity.id)
621 assert {:ok, :already_liked} = CommonAPI.favorite(user, activity.id)
622 end
623 end
624
625 describe "pinned statuses" do
626 setup do
627 Pleroma.Config.put([:instance, :max_pinned_statuses], 1)
628
629 user = insert(:user)
630 {:ok, activity} = CommonAPI.post(user, %{status: "HI!!!"})
631
632 [user: user, activity: activity]
633 end
634
635 test "pin status", %{user: user, activity: activity} do
636 assert {:ok, ^activity} = CommonAPI.pin(activity.id, user)
637
638 id = activity.id
639 user = refresh_record(user)
640
641 assert %User{pinned_activities: [^id]} = user
642 end
643
644 test "pin poll", %{user: user} do
645 {:ok, activity} =
646 CommonAPI.post(user, %{
647 status: "How is fediverse today?",
648 poll: %{options: ["Absolutely outstanding", "Not good"], expires_in: 20}
649 })
650
651 assert {:ok, ^activity} = CommonAPI.pin(activity.id, user)
652
653 id = activity.id
654 user = refresh_record(user)
655
656 assert %User{pinned_activities: [^id]} = user
657 end
658
659 test "unlisted statuses can be pinned", %{user: user} do
660 {:ok, activity} = CommonAPI.post(user, %{status: "HI!!!", visibility: "unlisted"})
661 assert {:ok, ^activity} = CommonAPI.pin(activity.id, user)
662 end
663
664 test "only self-authored can be pinned", %{activity: activity} do
665 user = insert(:user)
666
667 assert {:error, "Could not pin"} = CommonAPI.pin(activity.id, user)
668 end
669
670 test "max pinned statuses", %{user: user, activity: activity_one} do
671 {:ok, activity_two} = CommonAPI.post(user, %{status: "HI!!!"})
672
673 assert {:ok, ^activity_one} = CommonAPI.pin(activity_one.id, user)
674
675 user = refresh_record(user)
676
677 assert {:error, "You have already pinned the maximum number of statuses"} =
678 CommonAPI.pin(activity_two.id, user)
679 end
680
681 test "unpin status", %{user: user, activity: activity} do
682 {:ok, activity} = CommonAPI.pin(activity.id, user)
683
684 user = refresh_record(user)
685
686 id = activity.id
687
688 assert match?({:ok, %{id: ^id}}, CommonAPI.unpin(activity.id, user))
689
690 user = refresh_record(user)
691
692 assert %User{pinned_activities: []} = user
693 end
694
695 test "should unpin when deleting a status", %{user: user, activity: activity} do
696 {:ok, activity} = CommonAPI.pin(activity.id, user)
697
698 user = refresh_record(user)
699
700 assert {:ok, _} = CommonAPI.delete(activity.id, user)
701
702 user = refresh_record(user)
703
704 assert %User{pinned_activities: []} = user
705 end
706 end
707
708 describe "mute tests" do
709 setup do
710 user = insert(:user)
711
712 activity = insert(:note_activity)
713
714 [user: user, activity: activity]
715 end
716
717 test "add mute", %{user: user, activity: activity} do
718 {:ok, _} = CommonAPI.add_mute(user, activity)
719 assert CommonAPI.thread_muted?(user, activity)
720 end
721
722 test "remove mute", %{user: user, activity: activity} do
723 CommonAPI.add_mute(user, activity)
724 {:ok, _} = CommonAPI.remove_mute(user, activity)
725 refute CommonAPI.thread_muted?(user, activity)
726 end
727
728 test "check that mutes can't be duplicate", %{user: user, activity: activity} do
729 CommonAPI.add_mute(user, activity)
730 {:error, _} = CommonAPI.add_mute(user, activity)
731 end
732 end
733
734 describe "reports" do
735 test "creates a report" do
736 reporter = insert(:user)
737 target_user = insert(:user)
738
739 {:ok, activity} = CommonAPI.post(target_user, %{status: "foobar"})
740
741 reporter_ap_id = reporter.ap_id
742 target_ap_id = target_user.ap_id
743 activity_ap_id = activity.data["id"]
744 comment = "foobar"
745
746 report_data = %{
747 account_id: target_user.id,
748 comment: comment,
749 status_ids: [activity.id]
750 }
751
752 note_obj = %{
753 "type" => "Note",
754 "id" => activity_ap_id,
755 "content" => "foobar",
756 "published" => activity.object.data["published"],
757 "actor" => AccountView.render("show.json", %{user: target_user})
758 }
759
760 assert {:ok, flag_activity} = CommonAPI.report(reporter, report_data)
761
762 assert %Activity{
763 actor: ^reporter_ap_id,
764 data: %{
765 "type" => "Flag",
766 "content" => ^comment,
767 "object" => [^target_ap_id, ^note_obj],
768 "state" => "open"
769 }
770 } = flag_activity
771 end
772
773 test "updates report state" do
774 [reporter, target_user] = insert_pair(:user)
775 activity = insert(:note_activity, user: target_user)
776
777 {:ok, %Activity{id: report_id}} =
778 CommonAPI.report(reporter, %{
779 account_id: target_user.id,
780 comment: "I feel offended",
781 status_ids: [activity.id]
782 })
783
784 {:ok, report} = CommonAPI.update_report_state(report_id, "resolved")
785
786 assert report.data["state"] == "resolved"
787
788 [reported_user, activity_id] = report.data["object"]
789
790 assert reported_user == target_user.ap_id
791 assert activity_id == activity.data["id"]
792 end
793
794 test "does not update report state when state is unsupported" do
795 [reporter, target_user] = insert_pair(:user)
796 activity = insert(:note_activity, user: target_user)
797
798 {:ok, %Activity{id: report_id}} =
799 CommonAPI.report(reporter, %{
800 account_id: target_user.id,
801 comment: "I feel offended",
802 status_ids: [activity.id]
803 })
804
805 assert CommonAPI.update_report_state(report_id, "test") == {:error, "Unsupported state"}
806 end
807
808 test "updates state of multiple reports" do
809 [reporter, target_user] = insert_pair(:user)
810 activity = insert(:note_activity, user: target_user)
811
812 {:ok, %Activity{id: first_report_id}} =
813 CommonAPI.report(reporter, %{
814 account_id: target_user.id,
815 comment: "I feel offended",
816 status_ids: [activity.id]
817 })
818
819 {:ok, %Activity{id: second_report_id}} =
820 CommonAPI.report(reporter, %{
821 account_id: target_user.id,
822 comment: "I feel very offended!",
823 status_ids: [activity.id]
824 })
825
826 {:ok, report_ids} =
827 CommonAPI.update_report_state([first_report_id, second_report_id], "resolved")
828
829 first_report = Activity.get_by_id(first_report_id)
830 second_report = Activity.get_by_id(second_report_id)
831
832 assert report_ids -- [first_report_id, second_report_id] == []
833 assert first_report.data["state"] == "resolved"
834 assert second_report.data["state"] == "resolved"
835 end
836 end
837
838 describe "reblog muting" do
839 setup do
840 muter = insert(:user)
841
842 muted = insert(:user)
843
844 [muter: muter, muted: muted]
845 end
846
847 test "add a reblog mute", %{muter: muter, muted: muted} do
848 {:ok, _reblog_mute} = CommonAPI.hide_reblogs(muter, muted)
849
850 assert User.showing_reblogs?(muter, muted) == false
851 end
852
853 test "remove a reblog mute", %{muter: muter, muted: muted} do
854 {:ok, _reblog_mute} = CommonAPI.hide_reblogs(muter, muted)
855 {:ok, _reblog_mute} = CommonAPI.show_reblogs(muter, muted)
856
857 assert User.showing_reblogs?(muter, muted) == true
858 end
859 end
860
861 describe "unfollow/2" do
862 test "also unsubscribes a user" do
863 [follower, followed] = insert_pair(:user)
864 {:ok, follower, followed, _} = CommonAPI.follow(follower, followed)
865 {:ok, _subscription} = User.subscribe(follower, followed)
866
867 assert User.subscribed_to?(follower, followed)
868
869 {:ok, follower} = CommonAPI.unfollow(follower, followed)
870
871 refute User.subscribed_to?(follower, followed)
872 end
873
874 test "cancels a pending follow for a local user" do
875 follower = insert(:user)
876 followed = insert(:user, locked: true)
877
878 assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} =
879 CommonAPI.follow(follower, followed)
880
881 assert User.get_follow_state(follower, followed) == :follow_pending
882 assert {:ok, follower} = CommonAPI.unfollow(follower, followed)
883 assert User.get_follow_state(follower, followed) == nil
884
885 assert %{id: ^activity_id, data: %{"state" => "cancelled"}} =
886 Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(follower, followed)
887
888 assert %{
889 data: %{
890 "type" => "Undo",
891 "object" => %{"type" => "Follow", "state" => "cancelled"}
892 }
893 } = Pleroma.Web.ActivityPub.Utils.fetch_latest_undo(follower)
894 end
895
896 test "cancels a pending follow for a remote user" do
897 follower = insert(:user)
898 followed = insert(:user, locked: true, local: false, ap_enabled: true)
899
900 assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} =
901 CommonAPI.follow(follower, followed)
902
903 assert User.get_follow_state(follower, followed) == :follow_pending
904 assert {:ok, follower} = CommonAPI.unfollow(follower, followed)
905 assert User.get_follow_state(follower, followed) == nil
906
907 assert %{id: ^activity_id, data: %{"state" => "cancelled"}} =
908 Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(follower, followed)
909
910 assert %{
911 data: %{
912 "type" => "Undo",
913 "object" => %{"type" => "Follow", "state" => "cancelled"}
914 }
915 } = Pleroma.Web.ActivityPub.Utils.fetch_latest_undo(follower)
916 end
917 end
918
919 describe "accept_follow_request/2" do
920 test "after acceptance, it sets all existing pending follow request states to 'accept'" do
921 user = insert(:user, locked: true)
922 follower = insert(:user)
923 follower_two = insert(:user)
924
925 {:ok, follow_activity} = ActivityPub.follow(follower, user)
926 {:ok, follow_activity_two} = ActivityPub.follow(follower, user)
927 {:ok, follow_activity_three} = ActivityPub.follow(follower_two, user)
928
929 assert follow_activity.data["state"] == "pending"
930 assert follow_activity_two.data["state"] == "pending"
931 assert follow_activity_three.data["state"] == "pending"
932
933 {:ok, _follower} = CommonAPI.accept_follow_request(follower, user)
934
935 assert Repo.get(Activity, follow_activity.id).data["state"] == "accept"
936 assert Repo.get(Activity, follow_activity_two.id).data["state"] == "accept"
937 assert Repo.get(Activity, follow_activity_three.id).data["state"] == "pending"
938 end
939
940 test "after rejection, it sets all existing pending follow request states to 'reject'" do
941 user = insert(:user, locked: true)
942 follower = insert(:user)
943 follower_two = insert(:user)
944
945 {:ok, follow_activity} = ActivityPub.follow(follower, user)
946 {:ok, follow_activity_two} = ActivityPub.follow(follower, user)
947 {:ok, follow_activity_three} = ActivityPub.follow(follower_two, user)
948
949 assert follow_activity.data["state"] == "pending"
950 assert follow_activity_two.data["state"] == "pending"
951 assert follow_activity_three.data["state"] == "pending"
952
953 {:ok, _follower} = CommonAPI.reject_follow_request(follower, user)
954
955 assert Repo.get(Activity, follow_activity.id).data["state"] == "reject"
956 assert Repo.get(Activity, follow_activity_two.id).data["state"] == "reject"
957 assert Repo.get(Activity, follow_activity_three.id).data["state"] == "pending"
958 end
959
960 test "doesn't create a following relationship if the corresponding follow request doesn't exist" do
961 user = insert(:user, locked: true)
962 not_follower = insert(:user)
963 CommonAPI.accept_follow_request(not_follower, user)
964
965 assert Pleroma.FollowingRelationship.following?(not_follower, user) == false
966 end
967 end
968
969 describe "vote/3" do
970 test "does not allow to vote twice" do
971 user = insert(:user)
972 other_user = insert(:user)
973
974 {:ok, activity} =
975 CommonAPI.post(user, %{
976 status: "Am I cute?",
977 poll: %{options: ["Yes", "No"], expires_in: 20}
978 })
979
980 object = Object.normalize(activity)
981
982 {:ok, _, object} = CommonAPI.vote(other_user, object, [0])
983
984 assert {:error, "Already voted"} == CommonAPI.vote(other_user, object, [1])
985 end
986 end
987
988 describe "listen/2" do
989 test "returns a valid activity" do
990 user = insert(:user)
991
992 {:ok, activity} =
993 CommonAPI.listen(user, %{
994 title: "lain radio episode 1",
995 album: "lain radio",
996 artist: "lain",
997 length: 180_000
998 })
999
1000 object = Object.normalize(activity)
1001
1002 assert object.data["title"] == "lain radio episode 1"
1003
1004 assert Visibility.get_visibility(activity) == "public"
1005 end
1006
1007 test "respects visibility=private" do
1008 user = insert(:user)
1009
1010 {:ok, activity} =
1011 CommonAPI.listen(user, %{
1012 title: "lain radio episode 1",
1013 album: "lain radio",
1014 artist: "lain",
1015 length: 180_000,
1016 visibility: "private"
1017 })
1018
1019 object = Object.normalize(activity)
1020
1021 assert object.data["title"] == "lain radio episode 1"
1022
1023 assert Visibility.get_visibility(activity) == "private"
1024 end
1025 end
1026 end