1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Web.CommonAPITest do
6 use Oban.Testing, repo: Pleroma.Repo
7 use Pleroma.DataCase, async: false
11 alias Pleroma.Conversation.Participation
12 alias Pleroma.Notification
16 alias Pleroma.Web.ActivityPub.ActivityPub
17 alias Pleroma.Web.ActivityPub.Transmogrifier
18 alias Pleroma.Web.ActivityPub.Visibility
19 alias Pleroma.Web.AdminAPI.AccountView
20 alias Pleroma.Web.CommonAPI
21 alias Pleroma.Workers.PollWorker
23 import Pleroma.Factory
25 import Ecto.Query, only: [from: 2]
27 require Pleroma.Constants
30 Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
34 setup do: clear_config([:instance, :safe_dm_mentions])
35 setup do: clear_config([:instance, :limit])
36 setup do: clear_config([:instance, :max_pinned_statuses])
38 describe "posting polls" do
39 test "it posts a poll" do
43 CommonAPI.post(user, %{
44 status: "who is the best",
45 poll: %{expires_in: 600, options: ["reimu", "marisa"]}
48 object = Object.normalize(activity, fetch: false)
50 assert object.data["type"] == "Question"
51 assert object.data["oneOf"] |> length() == 2
55 args: %{op: "poll_end", activity_id: activity.id},
56 scheduled_at: NaiveDateTime.from_iso8601!(object.data["closed"])
61 describe "blocking" do
63 blocker = insert(:user)
64 blocked = insert(:user, local: false)
65 CommonAPI.follow(blocker, blocked)
66 CommonAPI.follow(blocked, blocker)
67 CommonAPI.accept_follow_request(blocker, blocked)
68 CommonAPI.accept_follow_request(blocked, blocked)
69 %{blocker: blocker, blocked: blocked}
72 test "it blocks and federates", %{blocker: blocker, blocked: blocked} do
73 clear_config([:instance, :federating], true)
75 with_mock Pleroma.Web.Federator,
76 publish: fn _ -> nil end do
77 assert User.get_follow_state(blocker, blocked) == :follow_accept
78 refute is_nil(Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(blocker, blocked))
80 assert {:ok, block} = CommonAPI.block(blocker, blocked)
83 assert User.blocks?(blocker, blocked)
84 refute User.following?(blocker, blocked)
85 refute User.following?(blocked, blocker)
87 refute User.get_follow_state(blocker, blocked)
89 assert %{data: %{"state" => "reject"}} =
90 Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(blocker, blocked)
92 assert called(Pleroma.Web.Federator.publish(block))
96 test "it blocks and does not federate if outgoing blocks are disabled", %{
100 clear_config([:instance, :federating], true)
101 clear_config([:activitypub, :outgoing_blocks], false)
103 with_mock Pleroma.Web.Federator,
104 publish: fn _ -> nil end do
105 assert {:ok, block} = CommonAPI.block(blocker, blocked)
108 assert User.blocks?(blocker, blocked)
109 refute User.following?(blocker, blocked)
110 refute User.following?(blocked, blocker)
112 refute called(Pleroma.Web.Federator.publish(block))
117 describe "posting chat messages" do
118 setup do: clear_config([:instance, :chat_limit])
120 test "it posts a self-chat" do
121 author = insert(:user)
125 CommonAPI.post_chat_message(
128 "remember to buy milk when milk truk arive"
131 assert activity.data["type"] == "Create"
134 test "it posts a chat message without content but with an attachment" do
135 author = insert(:user)
136 recipient = insert(:user)
139 content_type: "image/jpeg",
140 path: Path.absname("test/fixtures/image.jpg"),
141 filename: "an_image.jpg"
144 {:ok, upload} = ActivityPub.upload(file, actor: author.ap_id)
148 Pleroma.Web.Streamer,
160 send: fn _ -> nil end
165 CommonAPI.post_chat_message(
173 Notification.for_user_and_activity(recipient, activity)
174 |> Repo.preload(:activity)
176 assert called(Pleroma.Web.Push.send(notification))
177 assert called(Pleroma.Web.Streamer.stream(["user", "user:notification"], notification))
178 assert called(Pleroma.Web.Streamer.stream(["user", "user:pleroma_chat"], :_))
184 test "it adds html newlines" do
185 author = insert(:user)
186 recipient = insert(:user)
188 other_user = insert(:user)
191 CommonAPI.post_chat_message(
197 assert other_user.ap_id not in activity.recipients
199 object = Object.normalize(activity, fetch: false)
201 assert object.data["content"] == "uguu<br/>uguuu"
204 test "it linkifies" do
205 author = insert(:user)
206 recipient = insert(:user)
208 other_user = insert(:user)
211 CommonAPI.post_chat_message(
214 "https://example.org is the site of @#{other_user.nickname} #2hu"
217 assert other_user.ap_id not in activity.recipients
219 object = Object.normalize(activity, fetch: false)
221 assert object.data["content"] ==
222 "<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=\"#{other_user.id}\" 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>"
225 test "it posts a chat message" do
226 author = insert(:user)
227 recipient = insert(:user)
230 CommonAPI.post_chat_message(
233 "a test message <script>alert('uuu')</script> :firefox:"
236 assert activity.data["type"] == "Create"
237 assert activity.local
238 object = Object.normalize(activity, fetch: false)
240 assert object.data["type"] == "ChatMessage"
241 assert object.data["to"] == [recipient.ap_id]
243 assert object.data["content"] ==
244 "a test message <script>alert('uuu')</script> :firefox:"
246 assert object.data["emoji"] == %{
247 "firefox" => "http://localhost:4001/emoji/Firefox.gif"
250 assert Chat.get(author.id, recipient.ap_id)
251 assert Chat.get(recipient.id, author.ap_id)
253 assert :ok == Pleroma.Web.Federator.perform(:publish, activity)
256 test "it reject messages over the local limit" do
257 clear_config([:instance, :chat_limit], 2)
259 author = insert(:user)
260 recipient = insert(:user)
263 CommonAPI.post_chat_message(
269 assert message == :content_too_long
272 test "it reject messages via MRF" do
273 clear_config([:mrf_keyword, :reject], ["GNO"])
274 clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.KeywordPolicy])
276 author = insert(:user)
277 recipient = insert(:user)
279 assert {:reject, "[KeywordPolicy] Matches with rejected keyword"} ==
280 CommonAPI.post_chat_message(author, recipient, "GNO/Linux")
284 describe "unblocking" do
285 test "it works even without an existing block activity" do
286 blocked = insert(:user)
287 blocker = insert(:user)
288 User.block(blocker, blocked)
290 assert User.blocks?(blocker, blocked)
291 assert {:ok, :no_activity} == CommonAPI.unblock(blocker, blocked)
292 refute User.blocks?(blocker, blocked)
296 describe "deletion" do
297 test "it works with pruned objects" do
300 {:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
302 clear_config([:instance, :federating], true)
304 Object.normalize(post, fetch: false)
307 with_mock Pleroma.Web.Federator,
308 publish: fn _ -> nil end do
309 assert {:ok, delete} = CommonAPI.delete(post.id, user)
311 assert called(Pleroma.Web.Federator.publish(delete))
314 refute Activity.get_by_id(post.id)
317 test "it allows users to delete their posts" do
320 {:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
322 clear_config([:instance, :federating], true)
324 with_mock Pleroma.Web.Federator,
325 publish: fn _ -> nil end do
326 assert {:ok, delete} = CommonAPI.delete(post.id, user)
328 assert called(Pleroma.Web.Federator.publish(delete))
331 refute Activity.get_by_id(post.id)
334 test "it does not allow a user to delete posts from another user" do
336 other_user = insert(:user)
338 {:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
340 assert {:error, "Could not delete"} = CommonAPI.delete(post.id, other_user)
341 assert Activity.get_by_id(post.id)
344 test "it allows privileged users to delete other user's posts" do
345 clear_config([:instance, :moderator_privileges], [:messages_delete])
347 moderator = insert(:user, is_moderator: true)
349 {:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
351 assert {:ok, delete} = CommonAPI.delete(post.id, moderator)
354 refute Activity.get_by_id(post.id)
357 test "it doesn't allow unprivileged mods or admins to delete other user's posts" do
358 clear_config([:instance, :admin_privileges], [])
359 clear_config([:instance, :moderator_privileges], [])
361 moderator = insert(:user, is_moderator: true, is_admin: true)
363 {:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
365 assert {:error, "Could not delete"} = CommonAPI.delete(post.id, moderator)
366 assert Activity.get_by_id(post.id)
369 test "privileged users deleting non-local posts won't federate the delete" do
370 clear_config([:instance, :admin_privileges], [:messages_delete])
371 # This is the user of the ingested activity
375 ap_id: "http://mastodon.example.org/users/admin",
376 last_refreshed_at: NaiveDateTime.utc_now()
379 admin = insert(:user, is_admin: true)
382 File.read!("test/fixtures/mastodon-post-activity.json")
385 {:ok, post} = Transmogrifier.handle_incoming(data)
387 with_mock Pleroma.Web.Federator,
388 publish: fn _ -> nil end do
389 assert {:ok, delete} = CommonAPI.delete(post.id, admin)
391 refute called(Pleroma.Web.Federator.publish(:_))
394 refute Activity.get_by_id(post.id)
398 test "favoriting race condition" do
400 users_serial = insert_list(10, :user)
401 users = insert_list(10, :user)
403 {:ok, activity} = CommonAPI.post(user, %{status: "."})
406 |> Enum.map(fn user ->
407 CommonAPI.favorite(user, activity.id)
410 object = Object.get_by_ap_id(activity.data["object"])
411 assert object.data["like_count"] == 10
414 |> Enum.map(fn user ->
416 CommonAPI.favorite(user, activity.id)
419 |> Enum.map(&Task.await/1)
421 object = Object.get_by_ap_id(activity.data["object"])
422 assert object.data["like_count"] == 20
425 test "repeating race condition" do
427 users_serial = insert_list(10, :user)
428 users = insert_list(10, :user)
430 {:ok, activity} = CommonAPI.post(user, %{status: "."})
433 |> Enum.map(fn user ->
434 CommonAPI.repeat(activity.id, user)
437 object = Object.get_by_ap_id(activity.data["object"])
438 assert object.data["announcement_count"] == 10
441 |> Enum.map(fn user ->
443 CommonAPI.repeat(activity.id, user)
446 |> Enum.map(&Task.await/1)
448 object = Object.get_by_ap_id(activity.data["object"])
449 assert object.data["announcement_count"] == 20
452 test "when replying to a conversation / participation, it will set the correct context id even if no explicit reply_to is given" do
454 {:ok, activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"})
456 [participation] = Participation.for_user(user)
459 CommonAPI.post(user, %{status: ".", in_reply_to_conversation_id: participation.id})
461 assert Visibility.is_direct?(convo_reply)
463 assert activity.data["context"] == convo_reply.data["context"]
466 test "when replying to a conversation / participation, it only mentions the recipients explicitly declared in the participation" do
468 jafnhar = insert(:user)
469 tridi = insert(:user)
472 CommonAPI.post(har, %{
473 status: "@#{jafnhar.nickname} hey",
477 assert har.ap_id in activity.recipients
478 assert jafnhar.ap_id in activity.recipients
480 [participation] = Participation.for_user(har)
483 CommonAPI.post(har, %{
484 status: "I don't really like @#{tridi.nickname}",
485 visibility: "direct",
486 in_reply_to_status_id: activity.id,
487 in_reply_to_conversation_id: participation.id
490 assert har.ap_id in activity.recipients
491 assert jafnhar.ap_id in activity.recipients
492 refute tridi.ap_id in activity.recipients
495 test "with the safe_dm_mention option set, it does not mention people beyond the initial tags" do
497 jafnhar = insert(:user)
498 tridi = insert(:user)
500 clear_config([:instance, :safe_dm_mentions], true)
503 CommonAPI.post(har, %{
504 status: "@#{jafnhar.nickname} hey, i never want to see @#{tridi.nickname} again",
508 refute tridi.ap_id in activity.recipients
509 assert jafnhar.ap_id in activity.recipients
512 test "it de-duplicates tags" do
514 {:ok, activity} = CommonAPI.post(user, %{status: "#2hu #2HU"})
516 object = Object.normalize(activity, fetch: false)
518 assert Object.tags(object) == ["2hu"]
521 test "zwnj is treated as word character" do
523 {:ok, activity} = CommonAPI.post(user, %{status: "#ساٴينس"})
525 object = Object.normalize(activity, fetch: false)
527 assert Object.tags(object) == ["ساٴينس"]
530 test "double dot in link is allowed" do
532 text = "https://example.to/something..mp3"
533 {:ok, activity} = CommonAPI.post(user, %{status: text})
535 object = Object.normalize(activity, fetch: false)
537 assert object.data["content"] == "<a href=\"#{text}\" rel=\"ugc\">#{text}</a>"
540 test "it adds emoji in the object" do
542 {:ok, activity} = CommonAPI.post(user, %{status: ":firefox:"})
544 assert Object.normalize(activity, fetch: false).data["emoji"]["firefox"]
547 describe "posting" do
548 test "it adds an emoji on an external site" do
550 {:ok, activity} = CommonAPI.post(user, %{status: "hey :external_emoji:"})
552 assert %{"external_emoji" => url} = Object.normalize(activity).data["emoji"]
553 assert url == "https://example.com/emoji.png"
555 {:ok, activity} = CommonAPI.post(user, %{status: "hey :blank:"})
557 assert %{"blank" => url} = Object.normalize(activity).data["emoji"]
558 assert url == "#{Pleroma.Web.Endpoint.url()}/emoji/blank.png"
561 test "it copies emoji from the subject of the parent post" do
564 Object.normalize("https://patch.cx/objects/a399c28e-c821-4820-bc3e-4afeb044c16f",
568 activity = Activity.get_create_by_object_ap_id(object.data["id"])
571 {:ok, reply_activity} =
572 CommonAPI.post(user, %{
573 in_reply_to_id: activity.id,
574 status: ":joker_disapprove:",
575 spoiler_text: ":joker_smile:"
578 assert Object.normalize(reply_activity).data["emoji"]["joker_smile"]
579 refute Object.normalize(reply_activity).data["emoji"]["joker_disapprove"]
582 test "deactivated users can't post" do
583 user = insert(:user, is_active: false)
584 assert {:error, _} = CommonAPI.post(user, %{status: "ye"})
587 test "it supports explicit addressing" do
589 user_two = insert(:user)
590 user_three = insert(:user)
591 user_four = insert(:user)
594 CommonAPI.post(user, %{
596 "Hey, I think @#{user_three.nickname} is ugly. @#{user_four.nickname} is alright though.",
597 to: [user_two.nickname, user_four.nickname, "nonexistent"]
600 assert user.ap_id in activity.recipients
601 assert user_two.ap_id in activity.recipients
602 assert user_four.ap_id in activity.recipients
603 refute user_three.ap_id in activity.recipients
606 test "it filters out obviously bad tags when accepting a post as HTML" do
609 post = "<p><b>2hu</b></p><script>alert('xss')</script>"
612 CommonAPI.post(user, %{
614 content_type: "text/html"
617 object = Object.normalize(activity, fetch: false)
619 assert object.data["content"] == "<p><b>2hu</b></p>alert('xss')"
620 assert object.data["source"]["content"] == post
623 test "it filters out obviously bad tags when accepting a post as Markdown" do
626 post = "<p><b>2hu</b></p><script>alert('xss')</script>"
629 CommonAPI.post(user, %{
631 content_type: "text/markdown"
634 object = Object.normalize(activity, fetch: false)
636 assert object.data["content"] == "<p><b>2hu</b></p>"
637 assert object.data["source"]["content"] == post
640 test "it does not allow replies to direct messages that are not direct messages themselves" do
643 {:ok, activity} = CommonAPI.post(user, %{status: "suya..", visibility: "direct"})
646 CommonAPI.post(user, %{
648 visibility: "direct",
649 in_reply_to_status_id: activity.id
652 Enum.each(["public", "private", "unlisted"], fn visibility ->
653 assert {:error, "The message visibility must be direct"} =
654 CommonAPI.post(user, %{
656 visibility: visibility,
657 in_reply_to_status_id: activity.id
662 test "replying with a direct message will NOT auto-add the author of the reply to the recipient list" do
664 other_user = insert(:user)
665 third_user = insert(:user)
667 {:ok, post} = CommonAPI.post(user, %{status: "I'm stupid"})
670 CommonAPI.post(other_user, %{status: "No ur smart", in_reply_to_status_id: post.id})
672 # The OP is implicitly added
673 assert user.ap_id in open_answer.recipients
675 {:ok, secret_answer} =
676 CommonAPI.post(other_user, %{
677 status: "lol, that guy really is stupid, right, @#{third_user.nickname}?",
678 in_reply_to_status_id: post.id,
682 assert third_user.ap_id in secret_answer.recipients
684 # The OP is not added
685 refute user.ap_id in secret_answer.recipients
688 test "it allows to address a list" do
690 {:ok, list} = Pleroma.List.create("foo", user)
692 {:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})
694 assert activity.data["bcc"] == [list.ap_id]
695 assert activity.recipients == [list.ap_id, user.ap_id]
696 assert activity.data["listMessage"] == list.ap_id
699 test "it returns error when status is empty and no attachments" do
702 assert {:error, "Cannot post an empty status without attachments"} =
703 CommonAPI.post(user, %{status: ""})
706 test "it validates character limits are correctly enforced" do
707 clear_config([:instance, :limit], 5)
711 assert {:error, "The status is over the character limit"} =
712 CommonAPI.post(user, %{status: "foobar"})
714 assert {:ok, _activity} = CommonAPI.post(user, %{status: "12345"})
717 test "it validates media attachment limits are correctly enforced" do
718 clear_config([:instance, :max_media_attachments], 4)
723 content_type: "image/jpeg",
724 path: Path.absname("test/fixtures/image.jpg"),
725 filename: "an_image.jpg"
728 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
730 assert {:error, "Too many attachments"} =
731 CommonAPI.post(user, %{
733 media_ids: List.duplicate(upload.id, 5)
736 assert {:ok, _activity} =
737 CommonAPI.post(user, %{
739 media_ids: [upload.id]
743 test "it can handle activities that expire" do
746 expires_at = DateTime.add(DateTime.utc_now(), 1_000_000)
748 assert {:ok, activity} = CommonAPI.post(user, %{status: "chai", expires_in: 1_000_000})
751 worker: Pleroma.Workers.PurgeExpiredActivity,
752 args: %{activity_id: activity.id},
753 scheduled_at: expires_at
758 describe "reactions" do
759 test "reacting to a status with an emoji" do
761 other_user = insert(:user)
763 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
765 {:ok, reaction} = CommonAPI.react_with_emoji(activity.id, user, "👍")
767 assert reaction.data["actor"] == user.ap_id
768 assert reaction.data["content"] == "👍"
770 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
772 {:error, _} = CommonAPI.react_with_emoji(activity.id, user, ".")
775 test "unreacting to a status with an emoji" do
777 other_user = insert(:user)
779 clear_config([:instance, :federating], true)
781 with_mock Pleroma.Web.Federator,
782 publish: fn _ -> nil end do
783 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
784 {:ok, reaction} = CommonAPI.react_with_emoji(activity.id, user, "👍")
786 {:ok, unreaction} = CommonAPI.unreact_with_emoji(activity.id, user, "👍")
788 assert unreaction.data["type"] == "Undo"
789 assert unreaction.data["object"] == reaction.data["id"]
790 assert unreaction.local
792 # On federation, it contains the undone (and deleted) object
793 unreaction_with_object = %{
795 | data: Map.put(unreaction.data, "object", reaction.data)
798 assert called(Pleroma.Web.Federator.publish(unreaction_with_object))
802 test "repeating a status" do
804 other_user = insert(:user)
806 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
808 {:ok, %Activity{} = announce_activity} = CommonAPI.repeat(activity.id, user)
809 assert Visibility.is_public?(announce_activity)
812 test "can't repeat a repeat" do
814 other_user = insert(:user)
815 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
817 {:ok, %Activity{} = announce} = CommonAPI.repeat(activity.id, other_user)
819 refute match?({:ok, %Activity{}}, CommonAPI.repeat(announce.id, user))
822 test "repeating a status privately" do
824 other_user = insert(:user)
826 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
828 {:ok, %Activity{} = announce_activity} =
829 CommonAPI.repeat(activity.id, user, %{visibility: "private"})
831 assert Visibility.is_private?(announce_activity)
832 refute Visibility.visible_for_user?(announce_activity, nil)
835 test "author can repeat own private statuses" do
836 author = insert(:user)
837 follower = insert(:user)
838 CommonAPI.follow(follower, author)
840 {:ok, activity} = CommonAPI.post(author, %{status: "cofe", visibility: "private"})
842 {:ok, %Activity{} = announce_activity} = CommonAPI.repeat(activity.id, author)
844 assert Visibility.is_private?(announce_activity)
845 refute Visibility.visible_for_user?(announce_activity, nil)
847 assert Visibility.visible_for_user?(activity, follower)
848 assert {:error, :not_found} = CommonAPI.repeat(activity.id, follower)
851 test "favoriting a status" do
853 other_user = insert(:user)
855 {:ok, post_activity} = CommonAPI.post(other_user, %{status: "cofe"})
857 {:ok, %Activity{data: data}} = CommonAPI.favorite(user, post_activity.id)
858 assert data["type"] == "Like"
859 assert data["actor"] == user.ap_id
860 assert data["object"] == post_activity.data["object"]
863 test "retweeting a status twice returns the status" do
865 other_user = insert(:user)
867 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
868 {:ok, %Activity{} = announce} = CommonAPI.repeat(activity.id, user)
869 {:ok, ^announce} = CommonAPI.repeat(activity.id, user)
872 test "favoriting a status twice returns ok, but without the like activity" do
874 other_user = insert(:user)
876 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
877 {:ok, %Activity{}} = CommonAPI.favorite(user, activity.id)
878 assert {:ok, :already_liked} = CommonAPI.favorite(user, activity.id)
882 describe "pinned statuses" do
884 clear_config([:instance, :max_pinned_statuses], 1)
887 {:ok, activity} = CommonAPI.post(user, %{status: "HI!!!"})
889 [user: user, activity: activity]
892 test "activity not found error", %{user: user} do
893 assert {:error, :not_found} = CommonAPI.pin("id", user)
896 test "pin status", %{user: user, activity: activity} do
897 assert {:ok, ^activity} = CommonAPI.pin(activity.id, user)
899 %{data: %{"id" => object_id}} = Object.normalize(activity)
900 user = refresh_record(user)
902 assert user.pinned_objects |> Map.keys() == [object_id]
905 test "pin poll", %{user: user} do
907 CommonAPI.post(user, %{
908 status: "How is fediverse today?",
909 poll: %{options: ["Absolutely outstanding", "Not good"], expires_in: 20}
912 assert {:ok, ^activity} = CommonAPI.pin(activity.id, user)
914 %{data: %{"id" => object_id}} = Object.normalize(activity)
916 user = refresh_record(user)
918 assert user.pinned_objects |> Map.keys() == [object_id]
921 test "unlisted statuses can be pinned", %{user: user} do
922 {:ok, activity} = CommonAPI.post(user, %{status: "HI!!!", visibility: "unlisted"})
923 assert {:ok, ^activity} = CommonAPI.pin(activity.id, user)
926 test "only self-authored can be pinned", %{activity: activity} do
929 assert {:error, :ownership_error} = CommonAPI.pin(activity.id, user)
932 test "max pinned statuses", %{user: user, activity: activity_one} do
933 {:ok, activity_two} = CommonAPI.post(user, %{status: "HI!!!"})
935 assert {:ok, ^activity_one} = CommonAPI.pin(activity_one.id, user)
937 user = refresh_record(user)
939 assert {:error, :pinned_statuses_limit_reached} = CommonAPI.pin(activity_two.id, user)
942 test "only public can be pinned", %{user: user} do
943 {:ok, activity} = CommonAPI.post(user, %{status: "private status", visibility: "private"})
944 {:error, :visibility_error} = CommonAPI.pin(activity.id, user)
947 test "unpin status", %{user: user, activity: activity} do
948 {:ok, activity} = CommonAPI.pin(activity.id, user)
950 user = refresh_record(user)
954 assert match?({:ok, %{id: ^id}}, CommonAPI.unpin(activity.id, user))
956 user = refresh_record(user)
958 assert user.pinned_objects == %{}
961 test "should unpin when deleting a status", %{user: user, activity: activity} do
962 {:ok, activity} = CommonAPI.pin(activity.id, user)
964 user = refresh_record(user)
966 assert {:ok, _} = CommonAPI.delete(activity.id, user)
968 user = refresh_record(user)
970 assert user.pinned_objects == %{}
973 test "ephemeral activity won't be deleted if was pinned", %{user: user} do
974 {:ok, activity} = CommonAPI.post(user, %{status: "Hello!", expires_in: 601})
976 assert Pleroma.Workers.PurgeExpiredActivity.get_expiration(activity.id)
978 {:ok, _activity} = CommonAPI.pin(activity.id, user)
979 refute Pleroma.Workers.PurgeExpiredActivity.get_expiration(activity.id)
981 user = refresh_record(user)
982 {:ok, _} = CommonAPI.unpin(activity.id, user)
984 # recreates expiration job on unpin
985 assert Pleroma.Workers.PurgeExpiredActivity.get_expiration(activity.id)
988 test "ephemeral activity deletion job won't be deleted on pinning error", %{
992 clear_config([:instance, :max_pinned_statuses], 1)
994 {:ok, _activity} = CommonAPI.pin(activity.id, user)
996 {:ok, activity2} = CommonAPI.post(user, %{status: "another status", expires_in: 601})
998 assert Pleroma.Workers.PurgeExpiredActivity.get_expiration(activity2.id)
1000 user = refresh_record(user)
1001 {:error, :pinned_statuses_limit_reached} = CommonAPI.pin(activity2.id, user)
1003 assert Pleroma.Workers.PurgeExpiredActivity.get_expiration(activity2.id)
1007 describe "mute tests" do
1009 user = insert(:user)
1011 activity = insert(:note_activity)
1013 [user: user, activity: activity]
1016 test "marks notifications as read after mute" do
1017 author = insert(:user)
1018 activity = insert(:note_activity, user: author)
1020 friend1 = insert(:user)
1021 friend2 = insert(:user)
1023 {:ok, reply_activity} =
1027 status: "@#{author.nickname} @#{friend1.nickname} test reply",
1028 in_reply_to_status_id: activity.id
1032 {:ok, favorite_activity} = CommonAPI.favorite(friend2, activity.id)
1033 {:ok, repeat_activity} = CommonAPI.repeat(activity.id, friend1)
1035 assert Repo.aggregate(
1036 from(n in Notification, where: n.seen == false and n.user_id == ^friend1.id),
1040 unread_notifications =
1041 Repo.all(from(n in Notification, where: n.seen == false, where: n.user_id == ^author.id))
1043 assert Enum.any?(unread_notifications, fn n ->
1044 n.type == "favourite" && n.activity_id == favorite_activity.id
1047 assert Enum.any?(unread_notifications, fn n ->
1048 n.type == "reblog" && n.activity_id == repeat_activity.id
1051 assert Enum.any?(unread_notifications, fn n ->
1052 n.type == "mention" && n.activity_id == reply_activity.id
1055 {:ok, _} = CommonAPI.add_mute(author, activity)
1056 assert CommonAPI.thread_muted?(author, activity)
1058 assert Repo.aggregate(
1059 from(n in Notification, where: n.seen == false and n.user_id == ^friend1.id),
1063 read_notifications =
1064 Repo.all(from(n in Notification, where: n.seen == true, where: n.user_id == ^author.id))
1066 assert Enum.any?(read_notifications, fn n ->
1067 n.type == "favourite" && n.activity_id == favorite_activity.id
1070 assert Enum.any?(read_notifications, fn n ->
1071 n.type == "reblog" && n.activity_id == repeat_activity.id
1074 assert Enum.any?(read_notifications, fn n ->
1075 n.type == "mention" && n.activity_id == reply_activity.id
1079 test "add mute", %{user: user, activity: activity} do
1080 {:ok, _} = CommonAPI.add_mute(user, activity)
1081 assert CommonAPI.thread_muted?(user, activity)
1084 test "add expiring mute", %{user: user, activity: activity} do
1085 {:ok, _} = CommonAPI.add_mute(user, activity, %{expires_in: 60})
1086 assert CommonAPI.thread_muted?(user, activity)
1088 worker = Pleroma.Workers.MuteExpireWorker
1089 args = %{"op" => "unmute_conversation", "user_id" => user.id, "activity_id" => activity.id}
1096 assert :ok = perform_job(worker, args)
1097 refute CommonAPI.thread_muted?(user, activity)
1100 test "remove mute", %{user: user, activity: activity} do
1101 CommonAPI.add_mute(user, activity)
1102 {:ok, _} = CommonAPI.remove_mute(user, activity)
1103 refute CommonAPI.thread_muted?(user, activity)
1106 test "remove mute by ids", %{user: user, activity: activity} do
1107 CommonAPI.add_mute(user, activity)
1108 {:ok, _} = CommonAPI.remove_mute(user.id, activity.id)
1109 refute CommonAPI.thread_muted?(user, activity)
1112 test "check that mutes can't be duplicate", %{user: user, activity: activity} do
1113 CommonAPI.add_mute(user, activity)
1114 {:error, _} = CommonAPI.add_mute(user, activity)
1118 describe "reports" do
1119 test "creates a report" do
1120 reporter = insert(:user)
1121 target_user = insert(:user)
1123 {:ok, activity} = CommonAPI.post(target_user, %{status: "foobar"})
1124 activity = Activity.normalize(activity)
1126 reporter_ap_id = reporter.ap_id
1127 target_ap_id = target_user.ap_id
1128 reported_object_ap_id = activity.object.data["id"]
1132 account_id: target_user.id,
1134 status_ids: [activity.id]
1139 "id" => reported_object_ap_id,
1140 "content" => "foobar",
1141 "published" => activity.object.data["published"],
1142 "actor" => AccountView.render("show.json", %{user: target_user})
1145 assert {:ok, flag_activity} = CommonAPI.report(reporter, report_data)
1148 actor: ^reporter_ap_id,
1151 "content" => ^comment,
1152 "object" => [^target_ap_id, ^note_obj],
1158 test "updates report state" do
1159 [reporter, target_user] = insert_pair(:user)
1160 activity = insert(:note_activity, user: target_user)
1161 object = Object.normalize(activity)
1163 {:ok, %Activity{id: report_id}} =
1164 CommonAPI.report(reporter, %{
1165 account_id: target_user.id,
1166 comment: "I feel offended",
1167 status_ids: [activity.id]
1170 {:ok, report} = CommonAPI.update_report_state(report_id, "resolved")
1172 assert report.data["state"] == "resolved"
1174 [reported_user, object_id] = report.data["object"]
1176 assert reported_user == target_user.ap_id
1177 assert object_id == object.data["id"]
1180 test "updates report state, don't strip when report_strip_status is false" do
1181 clear_config([:instance, :report_strip_status], false)
1183 [reporter, target_user] = insert_pair(:user)
1184 activity = insert(:note_activity, user: target_user)
1186 {:ok, %Activity{id: report_id, data: report_data}} =
1187 CommonAPI.report(reporter, %{
1188 account_id: target_user.id,
1189 comment: "I feel offended",
1190 status_ids: [activity.id]
1193 {:ok, report} = CommonAPI.update_report_state(report_id, "resolved")
1195 assert report.data["state"] == "resolved"
1197 [reported_user, reported_activity] = report.data["object"]
1199 assert reported_user == target_user.ap_id
1200 assert is_map(reported_activity)
1202 assert reported_activity["content"] ==
1203 report_data["object"] |> Enum.at(1) |> Map.get("content")
1206 test "does not update report state when state is unsupported" do
1207 [reporter, target_user] = insert_pair(:user)
1208 activity = insert(:note_activity, user: target_user)
1210 {:ok, %Activity{id: report_id}} =
1211 CommonAPI.report(reporter, %{
1212 account_id: target_user.id,
1213 comment: "I feel offended",
1214 status_ids: [activity.id]
1217 assert CommonAPI.update_report_state(report_id, "test") == {:error, "Unsupported state"}
1220 test "updates state of multiple reports" do
1221 [reporter, target_user] = insert_pair(:user)
1222 activity = insert(:note_activity, user: target_user)
1224 {:ok, %Activity{id: first_report_id}} =
1225 CommonAPI.report(reporter, %{
1226 account_id: target_user.id,
1227 comment: "I feel offended",
1228 status_ids: [activity.id]
1231 {:ok, %Activity{id: second_report_id}} =
1232 CommonAPI.report(reporter, %{
1233 account_id: target_user.id,
1234 comment: "I feel very offended!",
1235 status_ids: [activity.id]
1239 CommonAPI.update_report_state([first_report_id, second_report_id], "resolved")
1241 first_report = Activity.get_by_id(first_report_id)
1242 second_report = Activity.get_by_id(second_report_id)
1244 assert report_ids -- [first_report_id, second_report_id] == []
1245 assert first_report.data["state"] == "resolved"
1246 assert second_report.data["state"] == "resolved"
1250 describe "reblog muting" do
1252 muter = insert(:user)
1254 muted = insert(:user)
1256 [muter: muter, muted: muted]
1259 test "add a reblog mute", %{muter: muter, muted: muted} do
1260 {:ok, _reblog_mute} = CommonAPI.hide_reblogs(muter, muted)
1262 assert User.showing_reblogs?(muter, muted) == false
1265 test "remove a reblog mute", %{muter: muter, muted: muted} do
1266 {:ok, _reblog_mute} = CommonAPI.hide_reblogs(muter, muted)
1267 {:ok, _reblog_mute} = CommonAPI.show_reblogs(muter, muted)
1269 assert User.showing_reblogs?(muter, muted) == true
1273 describe "follow/2" do
1274 test "directly follows a non-locked local user" do
1275 [follower, followed] = insert_pair(:user)
1276 {:ok, follower, followed, _} = CommonAPI.follow(follower, followed)
1278 assert User.following?(follower, followed)
1282 describe "unfollow/2" do
1283 test "also unsubscribes a user" do
1284 [follower, followed] = insert_pair(:user)
1285 {:ok, follower, followed, _} = CommonAPI.follow(follower, followed)
1286 {:ok, _subscription} = User.subscribe(follower, followed)
1288 assert User.subscribed_to?(follower, followed)
1290 {:ok, follower} = CommonAPI.unfollow(follower, followed)
1292 refute User.subscribed_to?(follower, followed)
1295 test "also unpins a user" do
1296 [follower, followed] = insert_pair(:user)
1297 {:ok, follower, followed, _} = CommonAPI.follow(follower, followed)
1298 {:ok, _endorsement} = User.endorse(follower, followed)
1300 assert User.endorses?(follower, followed)
1302 {:ok, follower} = CommonAPI.unfollow(follower, followed)
1304 refute User.endorses?(follower, followed)
1307 test "cancels a pending follow for a local user" do
1308 follower = insert(:user)
1309 followed = insert(:user, is_locked: true)
1311 assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} =
1312 CommonAPI.follow(follower, followed)
1314 assert User.get_follow_state(follower, followed) == :follow_pending
1315 assert {:ok, follower} = CommonAPI.unfollow(follower, followed)
1316 assert User.get_follow_state(follower, followed) == nil
1318 assert %{id: ^activity_id, data: %{"state" => "cancelled"}} =
1319 Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(follower, followed)
1324 "object" => %{"type" => "Follow", "state" => "cancelled"}
1326 } = Pleroma.Web.ActivityPub.Utils.fetch_latest_undo(follower)
1329 test "cancels a pending follow for a remote user" do
1330 follower = insert(:user)
1331 followed = insert(:user, is_locked: true, local: false, ap_enabled: true)
1333 assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} =
1334 CommonAPI.follow(follower, followed)
1336 assert User.get_follow_state(follower, followed) == :follow_pending
1337 assert {:ok, follower} = CommonAPI.unfollow(follower, followed)
1338 assert User.get_follow_state(follower, followed) == nil
1340 assert %{id: ^activity_id, data: %{"state" => "cancelled"}} =
1341 Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(follower, followed)
1346 "object" => %{"type" => "Follow", "state" => "cancelled"}
1348 } = Pleroma.Web.ActivityPub.Utils.fetch_latest_undo(follower)
1352 describe "accept_follow_request/2" do
1353 test "after acceptance, it sets all existing pending follow request states to 'accept'" do
1354 user = insert(:user, is_locked: true)
1355 follower = insert(:user)
1356 follower_two = insert(:user)
1358 {:ok, _, _, follow_activity} = CommonAPI.follow(follower, user)
1359 {:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user)
1360 {:ok, _, _, follow_activity_three} = CommonAPI.follow(follower_two, user)
1362 assert follow_activity.data["state"] == "pending"
1363 assert follow_activity_two.data["state"] == "pending"
1364 assert follow_activity_three.data["state"] == "pending"
1366 {:ok, _follower} = CommonAPI.accept_follow_request(follower, user)
1368 assert Repo.get(Activity, follow_activity.id).data["state"] == "accept"
1369 assert Repo.get(Activity, follow_activity_two.id).data["state"] == "accept"
1370 assert Repo.get(Activity, follow_activity_three.id).data["state"] == "pending"
1373 test "after rejection, it sets all existing pending follow request states to 'reject'" do
1374 user = insert(:user, is_locked: true)
1375 follower = insert(:user)
1376 follower_two = insert(:user)
1378 {:ok, _, _, follow_activity} = CommonAPI.follow(follower, user)
1379 {:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user)
1380 {:ok, _, _, follow_activity_three} = CommonAPI.follow(follower_two, user)
1382 assert follow_activity.data["state"] == "pending"
1383 assert follow_activity_two.data["state"] == "pending"
1384 assert follow_activity_three.data["state"] == "pending"
1386 {:ok, _follower} = CommonAPI.reject_follow_request(follower, user)
1388 assert Repo.get(Activity, follow_activity.id).data["state"] == "reject"
1389 assert Repo.get(Activity, follow_activity_two.id).data["state"] == "reject"
1390 assert Repo.get(Activity, follow_activity_three.id).data["state"] == "pending"
1393 test "doesn't create a following relationship if the corresponding follow request doesn't exist" do
1394 user = insert(:user, is_locked: true)
1395 not_follower = insert(:user)
1396 CommonAPI.accept_follow_request(not_follower, user)
1398 assert Pleroma.FollowingRelationship.following?(not_follower, user) == false
1402 describe "vote/3" do
1403 test "does not allow to vote twice" do
1404 user = insert(:user)
1405 other_user = insert(:user)
1408 CommonAPI.post(user, %{
1409 status: "Am I cute?",
1410 poll: %{options: ["Yes", "No"], expires_in: 20}
1413 object = Object.normalize(activity, fetch: false)
1415 {:ok, _, object} = CommonAPI.vote(other_user, object, [0])
1417 assert {:error, "Already voted"} == CommonAPI.vote(other_user, object, [1])
1421 describe "listen/2" do
1422 test "returns a valid activity" do
1423 user = insert(:user)
1426 CommonAPI.listen(user, %{
1427 title: "lain radio episode 1",
1428 album: "lain radio",
1433 object = Object.normalize(activity, fetch: false)
1435 assert object.data["title"] == "lain radio episode 1"
1437 assert Visibility.get_visibility(activity) == "public"
1440 test "respects visibility=private" do
1441 user = insert(:user)
1444 CommonAPI.listen(user, %{
1445 title: "lain radio episode 1",
1446 album: "lain radio",
1449 visibility: "private"
1452 object = Object.normalize(activity, fetch: false)
1454 assert object.data["title"] == "lain radio episode 1"
1456 assert Visibility.get_visibility(activity) == "private"
1460 describe "get_user/1" do
1461 test "gets user by ap_id" do
1462 user = insert(:user)
1463 assert CommonAPI.get_user(user.ap_id) == user
1466 test "gets user by guessed nickname" do
1467 user = insert(:user, ap_id: "", nickname: "mario@mushroom.kingdom")
1468 assert CommonAPI.get_user("https://mushroom.kingdom/users/mario") == user
1475 nickname: "erroruser@example.com"
1476 } = CommonAPI.get_user("")
1480 describe "with `local` visibility" do
1481 setup do: clear_config([:instance, :federating], true)
1484 user = insert(:user)
1486 with_mock Pleroma.Web.Federator, publish: fn _ -> :ok end do
1487 {:ok, activity} = CommonAPI.post(user, %{status: "#2hu #2HU", visibility: "local"})
1489 assert Visibility.is_local_public?(activity)
1490 assert_not_called(Pleroma.Web.Federator.publish(activity))
1495 user = insert(:user)
1497 {:ok, %Activity{id: activity_id}} =
1498 CommonAPI.post(user, %{status: "#2hu #2HU", visibility: "local"})
1500 with_mock Pleroma.Web.Federator, publish: fn _ -> :ok end do
1501 assert {:ok, %Activity{data: %{"deleted_activity_id" => ^activity_id}} = activity} =
1502 CommonAPI.delete(activity_id, user)
1504 assert Visibility.is_local_public?(activity)
1505 assert_not_called(Pleroma.Web.Federator.publish(activity))
1510 user = insert(:user)
1511 other_user = insert(:user)
1513 {:ok, %Activity{id: activity_id}} =
1514 CommonAPI.post(other_user, %{status: "cofe", visibility: "local"})
1516 with_mock Pleroma.Web.Federator, publish: fn _ -> :ok end do
1517 assert {:ok, %Activity{data: %{"type" => "Announce"}} = activity} =
1518 CommonAPI.repeat(activity_id, user)
1520 assert Visibility.is_local_public?(activity)
1521 refute called(Pleroma.Web.Federator.publish(activity))
1526 user = insert(:user)
1527 other_user = insert(:user)
1529 {:ok, %Activity{id: activity_id}} =
1530 CommonAPI.post(other_user, %{status: "cofe", visibility: "local"})
1532 assert {:ok, _} = CommonAPI.repeat(activity_id, user)
1534 with_mock Pleroma.Web.Federator, publish: fn _ -> :ok end do
1535 assert {:ok, %Activity{data: %{"type" => "Undo"}} = activity} =
1536 CommonAPI.unrepeat(activity_id, user)
1538 assert Visibility.is_local_public?(activity)
1539 refute called(Pleroma.Web.Federator.publish(activity))
1544 user = insert(:user)
1545 other_user = insert(:user)
1547 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe", visibility: "local"})
1549 with_mock Pleroma.Web.Federator, publish: fn _ -> :ok end do
1550 assert {:ok, %Activity{data: %{"type" => "Like"}} = activity} =
1551 CommonAPI.favorite(user, activity.id)
1553 assert Visibility.is_local_public?(activity)
1554 refute called(Pleroma.Web.Federator.publish(activity))
1558 test "unfavorite" do
1559 user = insert(:user)
1560 other_user = insert(:user)
1562 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe", visibility: "local"})
1564 {:ok, %Activity{}} = CommonAPI.favorite(user, activity.id)
1566 with_mock Pleroma.Web.Federator, publish: fn _ -> :ok end do
1567 assert {:ok, activity} = CommonAPI.unfavorite(activity.id, user)
1568 assert Visibility.is_local_public?(activity)
1569 refute called(Pleroma.Web.Federator.publish(activity))
1573 test "react_with_emoji" do
1574 user = insert(:user)
1575 other_user = insert(:user)
1576 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe", visibility: "local"})
1578 with_mock Pleroma.Web.Federator, publish: fn _ -> :ok end do
1579 assert {:ok, %Activity{data: %{"type" => "EmojiReact"}} = activity} =
1580 CommonAPI.react_with_emoji(activity.id, user, "👍")
1582 assert Visibility.is_local_public?(activity)
1583 refute called(Pleroma.Web.Federator.publish(activity))
1587 test "unreact_with_emoji" do
1588 user = insert(:user)
1589 other_user = insert(:user)
1590 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe", visibility: "local"})
1592 {:ok, _reaction} = CommonAPI.react_with_emoji(activity.id, user, "👍")
1594 with_mock Pleroma.Web.Federator, publish: fn _ -> :ok end do
1595 assert {:ok, %Activity{data: %{"type" => "Undo"}} = activity} =
1596 CommonAPI.unreact_with_emoji(activity.id, user, "👍")
1598 assert Visibility.is_local_public?(activity)
1599 refute called(Pleroma.Web.Federator.publish(activity))
1604 describe "update/3" do
1605 test "updates a post" do
1606 user = insert(:user)
1607 {:ok, activity} = CommonAPI.post(user, %{status: "foo1", spoiler_text: "title 1"})
1609 {:ok, updated} = CommonAPI.update(user, activity, %{status: "updated 2"})
1611 updated_object = Object.normalize(updated)
1612 assert updated_object.data["content"] == "updated 2"
1613 assert Map.get(updated_object.data, "summary", "") == ""
1614 assert Map.has_key?(updated_object.data, "updated")
1617 test "does not change visibility" do
1618 user = insert(:user)
1621 CommonAPI.post(user, %{status: "foo1", spoiler_text: "title 1", visibility: "private"})
1623 {:ok, updated} = CommonAPI.update(user, activity, %{status: "updated 2"})
1625 updated_object = Object.normalize(updated)
1626 assert updated_object.data["content"] == "updated 2"
1627 assert Map.get(updated_object.data, "summary", "") == ""
1628 assert Visibility.get_visibility(updated_object) == "private"
1629 assert Visibility.get_visibility(updated) == "private"
1632 test "updates a post with emoji" do
1633 [{emoji1, _}, {emoji2, _} | _] = Pleroma.Emoji.get_all()
1635 user = insert(:user)
1638 CommonAPI.post(user, %{status: "foo1", spoiler_text: "title 1 :#{emoji1}:"})
1640 {:ok, updated} = CommonAPI.update(user, activity, %{status: "updated 2 :#{emoji2}:"})
1642 updated_object = Object.normalize(updated)
1643 assert updated_object.data["content"] == "updated 2 :#{emoji2}:"
1644 assert %{^emoji2 => _} = updated_object.data["emoji"]
1647 test "updates a post with emoji and federate properly" do
1648 [{emoji1, _}, {emoji2, _} | _] = Pleroma.Emoji.get_all()
1650 user = insert(:user)
1653 CommonAPI.post(user, %{status: "foo1", spoiler_text: "title 1 :#{emoji1}:"})
1655 clear_config([:instance, :federating], true)
1657 with_mock Pleroma.Web.Federator,
1658 publish: fn _p -> nil end do
1659 {:ok, updated} = CommonAPI.update(user, activity, %{status: "updated 2 :#{emoji2}:"})
1661 assert updated.data["object"]["content"] == "updated 2 :#{emoji2}:"
1662 assert %{^emoji2 => _} = updated.data["object"]["emoji"]
1664 assert called(Pleroma.Web.Federator.publish(updated))
1668 test "editing a post that copied a remote title with remote emoji should keep that emoji" do
1669 remote_emoji_uri = "https://remote.org/emoji.png"
1675 "summary" => ":remoteemoji:",
1677 "remoteemoji" => remote_emoji_uri
1682 "name" => "remoteemoji",
1683 "icon" => %{"url" => remote_emoji_uri}
1689 note_activity = insert(:note_activity, note: note)
1691 user = insert(:user)
1694 CommonAPI.post(user, %{
1696 spoiler_text: ":remoteemoji:",
1697 in_reply_to_id: note_activity.id
1700 assert reply.object.data["emoji"]["remoteemoji"] == remote_emoji_uri
1703 CommonAPI.update(user, reply, %{status: "reply mew mew", spoiler_text: ":remoteemoji:"})
1705 edited_note = Pleroma.Object.normalize(edit)
1707 assert edited_note.data["emoji"]["remoteemoji"] == remote_emoji_uri
1710 test "respects MRF" do
1711 user = insert(:user)
1713 clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.KeywordPolicy])
1714 clear_config([:mrf_keyword, :replace], [{"updated", "mewmew"}])
1716 {:ok, activity} = CommonAPI.post(user, %{status: "foo1", spoiler_text: "updated 1"})
1717 assert Object.normalize(activity).data["summary"] == "mewmew 1"
1719 {:ok, updated} = CommonAPI.update(user, activity, %{status: "updated 2"})
1721 updated_object = Object.normalize(updated)
1722 assert updated_object.data["content"] == "mewmew 2"
1723 assert Map.get(updated_object.data, "summary", "") == ""
1724 assert Map.has_key?(updated_object.data, "updated")