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")
283 test "it reject messages with attachments not belonging to user" do
284 author = insert(:user)
285 not_author = insert(:user)
288 attachment = insert(:attachment, %{user: not_author})
291 CommonAPI.post_chat_message(
295 media_id: attachment.id
298 assert message == :forbidden
302 describe "unblocking" do
303 test "it works even without an existing block activity" do
304 blocked = insert(:user)
305 blocker = insert(:user)
306 User.block(blocker, blocked)
308 assert User.blocks?(blocker, blocked)
309 assert {:ok, :no_activity} == CommonAPI.unblock(blocker, blocked)
310 refute User.blocks?(blocker, blocked)
314 describe "deletion" do
315 test "it works with pruned objects" do
318 {:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
320 clear_config([:instance, :federating], true)
322 Object.normalize(post, fetch: false)
325 with_mock Pleroma.Web.Federator,
326 publish: fn _ -> nil end do
327 assert {:ok, delete} = CommonAPI.delete(post.id, user)
329 assert called(Pleroma.Web.Federator.publish(delete))
332 refute Activity.get_by_id(post.id)
335 test "it allows users to delete their posts" do
338 {:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
340 clear_config([:instance, :federating], true)
342 with_mock Pleroma.Web.Federator,
343 publish: fn _ -> nil end do
344 assert {:ok, delete} = CommonAPI.delete(post.id, user)
346 assert called(Pleroma.Web.Federator.publish(delete))
349 refute Activity.get_by_id(post.id)
352 test "it does not allow a user to delete posts from another user" do
354 other_user = insert(:user)
356 {:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
358 assert {:error, "Could not delete"} = CommonAPI.delete(post.id, other_user)
359 assert Activity.get_by_id(post.id)
362 test "it allows privileged users to delete other user's posts" do
363 clear_config([:instance, :moderator_privileges], [:messages_delete])
365 moderator = insert(:user, is_moderator: true)
367 {:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
369 assert {:ok, delete} = CommonAPI.delete(post.id, moderator)
372 refute Activity.get_by_id(post.id)
375 test "it doesn't allow unprivileged mods or admins to delete other user's posts" do
376 clear_config([:instance, :admin_privileges], [])
377 clear_config([:instance, :moderator_privileges], [])
379 moderator = insert(:user, is_moderator: true, is_admin: true)
381 {:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
383 assert {:error, "Could not delete"} = CommonAPI.delete(post.id, moderator)
384 assert Activity.get_by_id(post.id)
387 test "privileged users deleting non-local posts won't federate the delete" do
388 clear_config([:instance, :admin_privileges], [:messages_delete])
389 # This is the user of the ingested activity
393 ap_id: "http://mastodon.example.org/users/admin",
394 last_refreshed_at: NaiveDateTime.utc_now()
397 admin = insert(:user, is_admin: true)
400 File.read!("test/fixtures/mastodon-post-activity.json")
403 {:ok, post} = Transmogrifier.handle_incoming(data)
405 with_mock Pleroma.Web.Federator,
406 publish: fn _ -> nil end do
407 assert {:ok, delete} = CommonAPI.delete(post.id, admin)
409 refute called(Pleroma.Web.Federator.publish(:_))
412 refute Activity.get_by_id(post.id)
416 test "favoriting race condition" do
418 users_serial = insert_list(10, :user)
419 users = insert_list(10, :user)
421 {:ok, activity} = CommonAPI.post(user, %{status: "."})
424 |> Enum.map(fn user ->
425 CommonAPI.favorite(user, activity.id)
428 object = Object.get_by_ap_id(activity.data["object"])
429 assert object.data["like_count"] == 10
432 |> Enum.map(fn user ->
434 CommonAPI.favorite(user, activity.id)
437 |> Enum.map(&Task.await/1)
439 object = Object.get_by_ap_id(activity.data["object"])
440 assert object.data["like_count"] == 20
443 test "repeating race condition" do
445 users_serial = insert_list(10, :user)
446 users = insert_list(10, :user)
448 {:ok, activity} = CommonAPI.post(user, %{status: "."})
451 |> Enum.map(fn user ->
452 CommonAPI.repeat(activity.id, user)
455 object = Object.get_by_ap_id(activity.data["object"])
456 assert object.data["announcement_count"] == 10
459 |> Enum.map(fn user ->
461 CommonAPI.repeat(activity.id, user)
464 |> Enum.map(&Task.await/1)
466 object = Object.get_by_ap_id(activity.data["object"])
467 assert object.data["announcement_count"] == 20
470 test "when replying to a conversation / participation, it will set the correct context id even if no explicit reply_to is given" do
472 {:ok, activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"})
474 [participation] = Participation.for_user(user)
477 CommonAPI.post(user, %{status: ".", in_reply_to_conversation_id: participation.id})
479 assert Visibility.is_direct?(convo_reply)
481 assert activity.data["context"] == convo_reply.data["context"]
484 test "when replying to a conversation / participation, it only mentions the recipients explicitly declared in the participation" do
486 jafnhar = insert(:user)
487 tridi = insert(:user)
490 CommonAPI.post(har, %{
491 status: "@#{jafnhar.nickname} hey",
495 assert har.ap_id in activity.recipients
496 assert jafnhar.ap_id in activity.recipients
498 [participation] = Participation.for_user(har)
501 CommonAPI.post(har, %{
502 status: "I don't really like @#{tridi.nickname}",
503 visibility: "direct",
504 in_reply_to_status_id: activity.id,
505 in_reply_to_conversation_id: participation.id
508 assert har.ap_id in activity.recipients
509 assert jafnhar.ap_id in activity.recipients
510 refute tridi.ap_id in activity.recipients
513 test "with the safe_dm_mention option set, it does not mention people beyond the initial tags" do
515 jafnhar = insert(:user)
516 tridi = insert(:user)
518 clear_config([:instance, :safe_dm_mentions], true)
521 CommonAPI.post(har, %{
522 status: "@#{jafnhar.nickname} hey, i never want to see @#{tridi.nickname} again",
526 refute tridi.ap_id in activity.recipients
527 assert jafnhar.ap_id in activity.recipients
530 test "it de-duplicates tags" do
532 {:ok, activity} = CommonAPI.post(user, %{status: "#2hu #2HU"})
534 object = Object.normalize(activity, fetch: false)
536 assert Object.tags(object) == ["2hu"]
539 test "zwnj is treated as word character" do
541 {:ok, activity} = CommonAPI.post(user, %{status: "#ساٴينس"})
543 object = Object.normalize(activity, fetch: false)
545 assert Object.tags(object) == ["ساٴينس"]
548 test "double dot in link is allowed" do
550 text = "https://example.to/something..mp3"
551 {:ok, activity} = CommonAPI.post(user, %{status: text})
553 object = Object.normalize(activity, fetch: false)
555 assert object.data["content"] == "<a href=\"#{text}\" rel=\"ugc\">#{text}</a>"
558 test "it adds emoji in the object" do
560 {:ok, activity} = CommonAPI.post(user, %{status: ":firefox:"})
562 assert Object.normalize(activity, fetch: false).data["emoji"]["firefox"]
565 describe "posting" do
566 test "it adds an emoji on an external site" do
568 {:ok, activity} = CommonAPI.post(user, %{status: "hey :external_emoji:"})
570 assert %{"external_emoji" => url} = Object.normalize(activity).data["emoji"]
571 assert url == "https://example.com/emoji.png"
573 {:ok, activity} = CommonAPI.post(user, %{status: "hey :blank:"})
575 assert %{"blank" => url} = Object.normalize(activity).data["emoji"]
576 assert url == "#{Pleroma.Web.Endpoint.url()}/emoji/blank.png"
579 test "it copies emoji from the subject of the parent post" do
582 Object.normalize("https://patch.cx/objects/a399c28e-c821-4820-bc3e-4afeb044c16f",
586 activity = Activity.get_create_by_object_ap_id(object.data["id"])
589 {:ok, reply_activity} =
590 CommonAPI.post(user, %{
591 in_reply_to_id: activity.id,
592 status: ":joker_disapprove:",
593 spoiler_text: ":joker_smile:"
596 assert Object.normalize(reply_activity).data["emoji"]["joker_smile"]
597 refute Object.normalize(reply_activity).data["emoji"]["joker_disapprove"]
600 test "deactivated users can't post" do
601 user = insert(:user, is_active: false)
602 assert {:error, _} = CommonAPI.post(user, %{status: "ye"})
605 test "it supports explicit addressing" do
607 user_two = insert(:user)
608 user_three = insert(:user)
609 user_four = insert(:user)
612 CommonAPI.post(user, %{
614 "Hey, I think @#{user_three.nickname} is ugly. @#{user_four.nickname} is alright though.",
615 to: [user_two.nickname, user_four.nickname, "nonexistent"]
618 assert user.ap_id in activity.recipients
619 assert user_two.ap_id in activity.recipients
620 assert user_four.ap_id in activity.recipients
621 refute user_three.ap_id in activity.recipients
624 test "it filters out obviously bad tags when accepting a post as HTML" do
627 post = "<p><b>2hu</b></p><script>alert('xss')</script>"
630 CommonAPI.post(user, %{
632 content_type: "text/html"
635 object = Object.normalize(activity, fetch: false)
637 assert object.data["content"] == "<p><b>2hu</b></p>alert('xss')"
638 assert object.data["source"]["content"] == post
641 test "it filters out obviously bad tags when accepting a post as Markdown" do
644 post = "<p><b>2hu</b></p><script>alert('xss')</script>"
647 CommonAPI.post(user, %{
649 content_type: "text/markdown"
652 object = Object.normalize(activity, fetch: false)
654 assert object.data["content"] == "<p><b>2hu</b></p>"
655 assert object.data["source"]["content"] == post
658 test "it does not allow replies to direct messages that are not direct messages themselves" do
661 {:ok, activity} = CommonAPI.post(user, %{status: "suya..", visibility: "direct"})
664 CommonAPI.post(user, %{
666 visibility: "direct",
667 in_reply_to_status_id: activity.id
670 Enum.each(["public", "private", "unlisted"], fn visibility ->
671 assert {:error, "The message visibility must be direct"} =
672 CommonAPI.post(user, %{
674 visibility: visibility,
675 in_reply_to_status_id: activity.id
680 test "replying with a direct message will NOT auto-add the author of the reply to the recipient list" do
682 other_user = insert(:user)
683 third_user = insert(:user)
685 {:ok, post} = CommonAPI.post(user, %{status: "I'm stupid"})
688 CommonAPI.post(other_user, %{status: "No ur smart", in_reply_to_status_id: post.id})
690 # The OP is implicitly added
691 assert user.ap_id in open_answer.recipients
693 {:ok, secret_answer} =
694 CommonAPI.post(other_user, %{
695 status: "lol, that guy really is stupid, right, @#{third_user.nickname}?",
696 in_reply_to_status_id: post.id,
700 assert third_user.ap_id in secret_answer.recipients
702 # The OP is not added
703 refute user.ap_id in secret_answer.recipients
706 test "it allows to address a list" do
708 {:ok, list} = Pleroma.List.create("foo", user)
710 {:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})
712 assert activity.data["bcc"] == [list.ap_id]
713 assert activity.recipients == [list.ap_id, user.ap_id]
714 assert activity.data["listMessage"] == list.ap_id
717 test "it returns error when status is empty and no attachments" do
720 assert {:error, "Cannot post an empty status without attachments"} =
721 CommonAPI.post(user, %{status: ""})
724 test "it validates character limits are correctly enforced" do
725 clear_config([:instance, :limit], 5)
729 assert {:error, "The status is over the character limit"} =
730 CommonAPI.post(user, %{status: "foobar"})
732 assert {:ok, _activity} = CommonAPI.post(user, %{status: "12345"})
735 test "it validates media attachment limits are correctly enforced" do
736 clear_config([:instance, :max_media_attachments], 4)
741 content_type: "image/jpeg",
742 path: Path.absname("test/fixtures/image.jpg"),
743 filename: "an_image.jpg"
746 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
748 assert {:error, "Too many attachments"} =
749 CommonAPI.post(user, %{
751 media_ids: List.duplicate(upload.id, 5)
754 assert {:ok, _activity} =
755 CommonAPI.post(user, %{
757 media_ids: [upload.id]
761 test "it can handle activities that expire" do
764 expires_at = DateTime.add(DateTime.utc_now(), 1_000_000)
766 assert {:ok, activity} = CommonAPI.post(user, %{status: "chai", expires_in: 1_000_000})
769 worker: Pleroma.Workers.PurgeExpiredActivity,
770 args: %{activity_id: activity.id},
771 scheduled_at: expires_at
776 describe "reactions" do
777 test "reacting to a status with an emoji" do
779 other_user = insert(:user)
781 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
783 {:ok, reaction} = CommonAPI.react_with_emoji(activity.id, user, "👍")
785 assert reaction.data["actor"] == user.ap_id
786 assert reaction.data["content"] == "👍"
788 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
790 {:error, _} = CommonAPI.react_with_emoji(activity.id, user, ".")
793 test "unreacting to a status with an emoji" do
795 other_user = insert(:user)
797 clear_config([:instance, :federating], true)
799 with_mock Pleroma.Web.Federator,
800 publish: fn _ -> nil end do
801 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
802 {:ok, reaction} = CommonAPI.react_with_emoji(activity.id, user, "👍")
804 {:ok, unreaction} = CommonAPI.unreact_with_emoji(activity.id, user, "👍")
806 assert unreaction.data["type"] == "Undo"
807 assert unreaction.data["object"] == reaction.data["id"]
808 assert unreaction.local
810 # On federation, it contains the undone (and deleted) object
811 unreaction_with_object = %{
813 | data: Map.put(unreaction.data, "object", reaction.data)
816 assert called(Pleroma.Web.Federator.publish(unreaction_with_object))
820 test "repeating a status" do
822 other_user = insert(:user)
824 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
826 {:ok, %Activity{} = announce_activity} = CommonAPI.repeat(activity.id, user)
827 assert Visibility.is_public?(announce_activity)
830 test "can't repeat a repeat" do
832 other_user = insert(:user)
833 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
835 {:ok, %Activity{} = announce} = CommonAPI.repeat(activity.id, other_user)
837 refute match?({:ok, %Activity{}}, CommonAPI.repeat(announce.id, user))
840 test "repeating a status privately" do
842 other_user = insert(:user)
844 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
846 {:ok, %Activity{} = announce_activity} =
847 CommonAPI.repeat(activity.id, user, %{visibility: "private"})
849 assert Visibility.is_private?(announce_activity)
850 refute Visibility.visible_for_user?(announce_activity, nil)
853 test "author can repeat own private statuses" do
854 author = insert(:user)
855 follower = insert(:user)
856 CommonAPI.follow(follower, author)
858 {:ok, activity} = CommonAPI.post(author, %{status: "cofe", visibility: "private"})
860 {:ok, %Activity{} = announce_activity} = CommonAPI.repeat(activity.id, author)
862 assert Visibility.is_private?(announce_activity)
863 refute Visibility.visible_for_user?(announce_activity, nil)
865 assert Visibility.visible_for_user?(activity, follower)
866 assert {:error, :not_found} = CommonAPI.repeat(activity.id, follower)
869 test "favoriting a status" do
871 other_user = insert(:user)
873 {:ok, post_activity} = CommonAPI.post(other_user, %{status: "cofe"})
875 {:ok, %Activity{data: data}} = CommonAPI.favorite(user, post_activity.id)
876 assert data["type"] == "Like"
877 assert data["actor"] == user.ap_id
878 assert data["object"] == post_activity.data["object"]
881 test "retweeting a status twice returns the status" do
883 other_user = insert(:user)
885 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
886 {:ok, %Activity{} = announce} = CommonAPI.repeat(activity.id, user)
887 {:ok, ^announce} = CommonAPI.repeat(activity.id, user)
890 test "favoriting a status twice returns ok, but without the like activity" do
892 other_user = insert(:user)
894 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
895 {:ok, %Activity{}} = CommonAPI.favorite(user, activity.id)
896 assert {:ok, :already_liked} = CommonAPI.favorite(user, activity.id)
900 describe "pinned statuses" do
902 clear_config([:instance, :max_pinned_statuses], 1)
905 {:ok, activity} = CommonAPI.post(user, %{status: "HI!!!"})
907 [user: user, activity: activity]
910 test "activity not found error", %{user: user} do
911 assert {:error, :not_found} = CommonAPI.pin("id", user)
914 test "pin status", %{user: user, activity: activity} do
915 assert {:ok, ^activity} = CommonAPI.pin(activity.id, user)
917 %{data: %{"id" => object_id}} = Object.normalize(activity)
918 user = refresh_record(user)
920 assert user.pinned_objects |> Map.keys() == [object_id]
923 test "pin poll", %{user: user} do
925 CommonAPI.post(user, %{
926 status: "How is fediverse today?",
927 poll: %{options: ["Absolutely outstanding", "Not good"], expires_in: 20}
930 assert {:ok, ^activity} = CommonAPI.pin(activity.id, user)
932 %{data: %{"id" => object_id}} = Object.normalize(activity)
934 user = refresh_record(user)
936 assert user.pinned_objects |> Map.keys() == [object_id]
939 test "unlisted statuses can be pinned", %{user: user} do
940 {:ok, activity} = CommonAPI.post(user, %{status: "HI!!!", visibility: "unlisted"})
941 assert {:ok, ^activity} = CommonAPI.pin(activity.id, user)
944 test "only self-authored can be pinned", %{activity: activity} do
947 assert {:error, :ownership_error} = CommonAPI.pin(activity.id, user)
950 test "max pinned statuses", %{user: user, activity: activity_one} do
951 {:ok, activity_two} = CommonAPI.post(user, %{status: "HI!!!"})
953 assert {:ok, ^activity_one} = CommonAPI.pin(activity_one.id, user)
955 user = refresh_record(user)
957 assert {:error, :pinned_statuses_limit_reached} = CommonAPI.pin(activity_two.id, user)
960 test "only public can be pinned", %{user: user} do
961 {:ok, activity} = CommonAPI.post(user, %{status: "private status", visibility: "private"})
962 {:error, :visibility_error} = CommonAPI.pin(activity.id, user)
965 test "unpin status", %{user: user, activity: activity} do
966 {:ok, activity} = CommonAPI.pin(activity.id, user)
968 user = refresh_record(user)
972 assert match?({:ok, %{id: ^id}}, CommonAPI.unpin(activity.id, user))
974 user = refresh_record(user)
976 assert user.pinned_objects == %{}
979 test "should unpin when deleting a status", %{user: user, activity: activity} do
980 {:ok, activity} = CommonAPI.pin(activity.id, user)
982 user = refresh_record(user)
984 assert {:ok, _} = CommonAPI.delete(activity.id, user)
986 user = refresh_record(user)
988 assert user.pinned_objects == %{}
991 test "ephemeral activity won't be deleted if was pinned", %{user: user} do
992 {:ok, activity} = CommonAPI.post(user, %{status: "Hello!", expires_in: 601})
994 assert Pleroma.Workers.PurgeExpiredActivity.get_expiration(activity.id)
996 {:ok, _activity} = CommonAPI.pin(activity.id, user)
997 refute Pleroma.Workers.PurgeExpiredActivity.get_expiration(activity.id)
999 user = refresh_record(user)
1000 {:ok, _} = CommonAPI.unpin(activity.id, user)
1002 # recreates expiration job on unpin
1003 assert Pleroma.Workers.PurgeExpiredActivity.get_expiration(activity.id)
1006 test "ephemeral activity deletion job won't be deleted on pinning error", %{
1010 clear_config([:instance, :max_pinned_statuses], 1)
1012 {:ok, _activity} = CommonAPI.pin(activity.id, user)
1014 {:ok, activity2} = CommonAPI.post(user, %{status: "another status", expires_in: 601})
1016 assert Pleroma.Workers.PurgeExpiredActivity.get_expiration(activity2.id)
1018 user = refresh_record(user)
1019 {:error, :pinned_statuses_limit_reached} = CommonAPI.pin(activity2.id, user)
1021 assert Pleroma.Workers.PurgeExpiredActivity.get_expiration(activity2.id)
1025 describe "mute tests" do
1027 user = insert(:user)
1029 activity = insert(:note_activity)
1031 [user: user, activity: activity]
1034 test "marks notifications as read after mute" do
1035 author = insert(:user)
1036 activity = insert(:note_activity, user: author)
1038 friend1 = insert(:user)
1039 friend2 = insert(:user)
1041 {:ok, reply_activity} =
1045 status: "@#{author.nickname} @#{friend1.nickname} test reply",
1046 in_reply_to_status_id: activity.id
1050 {:ok, favorite_activity} = CommonAPI.favorite(friend2, activity.id)
1051 {:ok, repeat_activity} = CommonAPI.repeat(activity.id, friend1)
1053 assert Repo.aggregate(
1054 from(n in Notification, where: n.seen == false and n.user_id == ^friend1.id),
1058 unread_notifications =
1059 Repo.all(from(n in Notification, where: n.seen == false, where: n.user_id == ^author.id))
1061 assert Enum.any?(unread_notifications, fn n ->
1062 n.type == "favourite" && n.activity_id == favorite_activity.id
1065 assert Enum.any?(unread_notifications, fn n ->
1066 n.type == "reblog" && n.activity_id == repeat_activity.id
1069 assert Enum.any?(unread_notifications, fn n ->
1070 n.type == "mention" && n.activity_id == reply_activity.id
1073 {:ok, _} = CommonAPI.add_mute(author, activity)
1074 assert CommonAPI.thread_muted?(author, activity)
1076 assert Repo.aggregate(
1077 from(n in Notification, where: n.seen == false and n.user_id == ^friend1.id),
1081 read_notifications =
1082 Repo.all(from(n in Notification, where: n.seen == true, where: n.user_id == ^author.id))
1084 assert Enum.any?(read_notifications, fn n ->
1085 n.type == "favourite" && n.activity_id == favorite_activity.id
1088 assert Enum.any?(read_notifications, fn n ->
1089 n.type == "reblog" && n.activity_id == repeat_activity.id
1092 assert Enum.any?(read_notifications, fn n ->
1093 n.type == "mention" && n.activity_id == reply_activity.id
1097 test "add mute", %{user: user, activity: activity} do
1098 {:ok, _} = CommonAPI.add_mute(user, activity)
1099 assert CommonAPI.thread_muted?(user, activity)
1102 test "add expiring mute", %{user: user, activity: activity} do
1103 {:ok, _} = CommonAPI.add_mute(user, activity, %{expires_in: 60})
1104 assert CommonAPI.thread_muted?(user, activity)
1106 worker = Pleroma.Workers.MuteExpireWorker
1107 args = %{"op" => "unmute_conversation", "user_id" => user.id, "activity_id" => activity.id}
1114 assert :ok = perform_job(worker, args)
1115 refute CommonAPI.thread_muted?(user, activity)
1118 test "remove mute", %{user: user, activity: activity} do
1119 CommonAPI.add_mute(user, activity)
1120 {:ok, _} = CommonAPI.remove_mute(user, activity)
1121 refute CommonAPI.thread_muted?(user, activity)
1124 test "remove mute by ids", %{user: user, activity: activity} do
1125 CommonAPI.add_mute(user, activity)
1126 {:ok, _} = CommonAPI.remove_mute(user.id, activity.id)
1127 refute CommonAPI.thread_muted?(user, activity)
1130 test "check that mutes can't be duplicate", %{user: user, activity: activity} do
1131 CommonAPI.add_mute(user, activity)
1132 {:error, _} = CommonAPI.add_mute(user, activity)
1136 describe "reports" do
1137 test "creates a report" do
1138 reporter = insert(:user)
1139 target_user = insert(:user)
1141 {:ok, activity} = CommonAPI.post(target_user, %{status: "foobar"})
1142 activity = Activity.normalize(activity)
1144 reporter_ap_id = reporter.ap_id
1145 target_ap_id = target_user.ap_id
1146 reported_object_ap_id = activity.object.data["id"]
1150 account_id: target_user.id,
1152 status_ids: [activity.id]
1157 "id" => reported_object_ap_id,
1158 "content" => "foobar",
1159 "published" => activity.object.data["published"],
1160 "actor" => AccountView.render("show.json", %{user: target_user})
1163 assert {:ok, flag_activity} = CommonAPI.report(reporter, report_data)
1166 actor: ^reporter_ap_id,
1169 "content" => ^comment,
1170 "object" => [^target_ap_id, ^note_obj],
1176 test "updates report state" do
1177 [reporter, target_user] = insert_pair(:user)
1178 activity = insert(:note_activity, user: target_user)
1179 object = Object.normalize(activity)
1181 {:ok, %Activity{id: report_id}} =
1182 CommonAPI.report(reporter, %{
1183 account_id: target_user.id,
1184 comment: "I feel offended",
1185 status_ids: [activity.id]
1188 {:ok, report} = CommonAPI.update_report_state(report_id, "resolved")
1190 assert report.data["state"] == "resolved"
1192 [reported_user, object_id] = report.data["object"]
1194 assert reported_user == target_user.ap_id
1195 assert object_id == object.data["id"]
1198 test "updates report state, don't strip when report_strip_status is false" do
1199 clear_config([:instance, :report_strip_status], false)
1201 [reporter, target_user] = insert_pair(:user)
1202 activity = insert(:note_activity, user: target_user)
1204 {:ok, %Activity{id: report_id, data: report_data}} =
1205 CommonAPI.report(reporter, %{
1206 account_id: target_user.id,
1207 comment: "I feel offended",
1208 status_ids: [activity.id]
1211 {:ok, report} = CommonAPI.update_report_state(report_id, "resolved")
1213 assert report.data["state"] == "resolved"
1215 [reported_user, reported_activity] = report.data["object"]
1217 assert reported_user == target_user.ap_id
1218 assert is_map(reported_activity)
1220 assert reported_activity["content"] ==
1221 report_data["object"] |> Enum.at(1) |> Map.get("content")
1224 test "does not update report state when state is unsupported" do
1225 [reporter, target_user] = insert_pair(:user)
1226 activity = insert(:note_activity, user: target_user)
1228 {:ok, %Activity{id: report_id}} =
1229 CommonAPI.report(reporter, %{
1230 account_id: target_user.id,
1231 comment: "I feel offended",
1232 status_ids: [activity.id]
1235 assert CommonAPI.update_report_state(report_id, "test") == {:error, "Unsupported state"}
1238 test "updates state of multiple reports" do
1239 [reporter, target_user] = insert_pair(:user)
1240 activity = insert(:note_activity, user: target_user)
1242 {:ok, %Activity{id: first_report_id}} =
1243 CommonAPI.report(reporter, %{
1244 account_id: target_user.id,
1245 comment: "I feel offended",
1246 status_ids: [activity.id]
1249 {:ok, %Activity{id: second_report_id}} =
1250 CommonAPI.report(reporter, %{
1251 account_id: target_user.id,
1252 comment: "I feel very offended!",
1253 status_ids: [activity.id]
1257 CommonAPI.update_report_state([first_report_id, second_report_id], "resolved")
1259 first_report = Activity.get_by_id(first_report_id)
1260 second_report = Activity.get_by_id(second_report_id)
1262 assert report_ids -- [first_report_id, second_report_id] == []
1263 assert first_report.data["state"] == "resolved"
1264 assert second_report.data["state"] == "resolved"
1268 describe "reblog muting" do
1270 muter = insert(:user)
1272 muted = insert(:user)
1274 [muter: muter, muted: muted]
1277 test "add a reblog mute", %{muter: muter, muted: muted} do
1278 {:ok, _reblog_mute} = CommonAPI.hide_reblogs(muter, muted)
1280 assert User.showing_reblogs?(muter, muted) == false
1283 test "remove a reblog mute", %{muter: muter, muted: muted} do
1284 {:ok, _reblog_mute} = CommonAPI.hide_reblogs(muter, muted)
1285 {:ok, _reblog_mute} = CommonAPI.show_reblogs(muter, muted)
1287 assert User.showing_reblogs?(muter, muted) == true
1291 describe "follow/2" do
1292 test "directly follows a non-locked local user" do
1293 [follower, followed] = insert_pair(:user)
1294 {:ok, follower, followed, _} = CommonAPI.follow(follower, followed)
1296 assert User.following?(follower, followed)
1300 describe "unfollow/2" do
1301 test "also unsubscribes a user" do
1302 [follower, followed] = insert_pair(:user)
1303 {:ok, follower, followed, _} = CommonAPI.follow(follower, followed)
1304 {:ok, _subscription} = User.subscribe(follower, followed)
1306 assert User.subscribed_to?(follower, followed)
1308 {:ok, follower} = CommonAPI.unfollow(follower, followed)
1310 refute User.subscribed_to?(follower, followed)
1313 test "also unpins a user" do
1314 [follower, followed] = insert_pair(:user)
1315 {:ok, follower, followed, _} = CommonAPI.follow(follower, followed)
1316 {:ok, _endorsement} = User.endorse(follower, followed)
1318 assert User.endorses?(follower, followed)
1320 {:ok, follower} = CommonAPI.unfollow(follower, followed)
1322 refute User.endorses?(follower, followed)
1325 test "cancels a pending follow for a local user" do
1326 follower = insert(:user)
1327 followed = insert(:user, is_locked: true)
1329 assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} =
1330 CommonAPI.follow(follower, followed)
1332 assert User.get_follow_state(follower, followed) == :follow_pending
1333 assert {:ok, follower} = CommonAPI.unfollow(follower, followed)
1334 assert User.get_follow_state(follower, followed) == nil
1336 assert %{id: ^activity_id, data: %{"state" => "cancelled"}} =
1337 Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(follower, followed)
1342 "object" => %{"type" => "Follow", "state" => "cancelled"}
1344 } = Pleroma.Web.ActivityPub.Utils.fetch_latest_undo(follower)
1347 test "cancels a pending follow for a remote user" do
1348 follower = insert(:user)
1349 followed = insert(:user, is_locked: true, local: false, ap_enabled: true)
1351 assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} =
1352 CommonAPI.follow(follower, followed)
1354 assert User.get_follow_state(follower, followed) == :follow_pending
1355 assert {:ok, follower} = CommonAPI.unfollow(follower, followed)
1356 assert User.get_follow_state(follower, followed) == nil
1358 assert %{id: ^activity_id, data: %{"state" => "cancelled"}} =
1359 Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(follower, followed)
1364 "object" => %{"type" => "Follow", "state" => "cancelled"}
1366 } = Pleroma.Web.ActivityPub.Utils.fetch_latest_undo(follower)
1370 describe "accept_follow_request/2" do
1371 test "after acceptance, it sets all existing pending follow request states to 'accept'" do
1372 user = insert(:user, is_locked: true)
1373 follower = insert(:user)
1374 follower_two = insert(:user)
1376 {:ok, _, _, follow_activity} = CommonAPI.follow(follower, user)
1377 {:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user)
1378 {:ok, _, _, follow_activity_three} = CommonAPI.follow(follower_two, user)
1380 assert follow_activity.data["state"] == "pending"
1381 assert follow_activity_two.data["state"] == "pending"
1382 assert follow_activity_three.data["state"] == "pending"
1384 {:ok, _follower} = CommonAPI.accept_follow_request(follower, user)
1386 assert Repo.get(Activity, follow_activity.id).data["state"] == "accept"
1387 assert Repo.get(Activity, follow_activity_two.id).data["state"] == "accept"
1388 assert Repo.get(Activity, follow_activity_three.id).data["state"] == "pending"
1391 test "after rejection, it sets all existing pending follow request states to 'reject'" do
1392 user = insert(:user, is_locked: true)
1393 follower = insert(:user)
1394 follower_two = insert(:user)
1396 {:ok, _, _, follow_activity} = CommonAPI.follow(follower, user)
1397 {:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user)
1398 {:ok, _, _, follow_activity_three} = CommonAPI.follow(follower_two, user)
1400 assert follow_activity.data["state"] == "pending"
1401 assert follow_activity_two.data["state"] == "pending"
1402 assert follow_activity_three.data["state"] == "pending"
1404 {:ok, _follower} = CommonAPI.reject_follow_request(follower, user)
1406 assert Repo.get(Activity, follow_activity.id).data["state"] == "reject"
1407 assert Repo.get(Activity, follow_activity_two.id).data["state"] == "reject"
1408 assert Repo.get(Activity, follow_activity_three.id).data["state"] == "pending"
1411 test "doesn't create a following relationship if the corresponding follow request doesn't exist" do
1412 user = insert(:user, is_locked: true)
1413 not_follower = insert(:user)
1414 CommonAPI.accept_follow_request(not_follower, user)
1416 assert Pleroma.FollowingRelationship.following?(not_follower, user) == false
1420 describe "vote/3" do
1421 test "does not allow to vote twice" do
1422 user = insert(:user)
1423 other_user = insert(:user)
1426 CommonAPI.post(user, %{
1427 status: "Am I cute?",
1428 poll: %{options: ["Yes", "No"], expires_in: 20}
1431 object = Object.normalize(activity, fetch: false)
1433 {:ok, _, object} = CommonAPI.vote(other_user, object, [0])
1435 assert {:error, "Already voted"} == CommonAPI.vote(other_user, object, [1])
1439 describe "listen/2" do
1440 test "returns a valid activity" do
1441 user = insert(:user)
1444 CommonAPI.listen(user, %{
1445 title: "lain radio episode 1",
1446 album: "lain radio",
1451 object = Object.normalize(activity, fetch: false)
1453 assert object.data["title"] == "lain radio episode 1"
1455 assert Visibility.get_visibility(activity) == "public"
1458 test "respects visibility=private" do
1459 user = insert(:user)
1462 CommonAPI.listen(user, %{
1463 title: "lain radio episode 1",
1464 album: "lain radio",
1467 visibility: "private"
1470 object = Object.normalize(activity, fetch: false)
1472 assert object.data["title"] == "lain radio episode 1"
1474 assert Visibility.get_visibility(activity) == "private"
1478 describe "get_user/1" do
1479 test "gets user by ap_id" do
1480 user = insert(:user)
1481 assert CommonAPI.get_user(user.ap_id) == user
1484 test "gets user by guessed nickname" do
1485 user = insert(:user, ap_id: "", nickname: "mario@mushroom.kingdom")
1486 assert CommonAPI.get_user("https://mushroom.kingdom/users/mario") == user
1493 nickname: "erroruser@example.com"
1494 } = CommonAPI.get_user("")
1498 describe "with `local` visibility" do
1499 setup do: clear_config([:instance, :federating], true)
1502 user = insert(:user)
1504 with_mock Pleroma.Web.Federator, publish: fn _ -> :ok end do
1505 {:ok, activity} = CommonAPI.post(user, %{status: "#2hu #2HU", visibility: "local"})
1507 assert Visibility.is_local_public?(activity)
1508 assert_not_called(Pleroma.Web.Federator.publish(activity))
1513 user = insert(:user)
1515 {:ok, %Activity{id: activity_id}} =
1516 CommonAPI.post(user, %{status: "#2hu #2HU", visibility: "local"})
1518 with_mock Pleroma.Web.Federator, publish: fn _ -> :ok end do
1519 assert {:ok, %Activity{data: %{"deleted_activity_id" => ^activity_id}} = activity} =
1520 CommonAPI.delete(activity_id, user)
1522 assert Visibility.is_local_public?(activity)
1523 assert_not_called(Pleroma.Web.Federator.publish(activity))
1528 user = insert(:user)
1529 other_user = insert(:user)
1531 {:ok, %Activity{id: activity_id}} =
1532 CommonAPI.post(other_user, %{status: "cofe", visibility: "local"})
1534 with_mock Pleroma.Web.Federator, publish: fn _ -> :ok end do
1535 assert {:ok, %Activity{data: %{"type" => "Announce"}} = activity} =
1536 CommonAPI.repeat(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{id: activity_id}} =
1548 CommonAPI.post(other_user, %{status: "cofe", visibility: "local"})
1550 assert {:ok, _} = CommonAPI.repeat(activity_id, user)
1552 with_mock Pleroma.Web.Federator, publish: fn _ -> :ok end do
1553 assert {:ok, %Activity{data: %{"type" => "Undo"}} = activity} =
1554 CommonAPI.unrepeat(activity_id, user)
1556 assert Visibility.is_local_public?(activity)
1557 refute called(Pleroma.Web.Federator.publish(activity))
1562 user = insert(:user)
1563 other_user = insert(:user)
1565 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe", visibility: "local"})
1567 with_mock Pleroma.Web.Federator, publish: fn _ -> :ok end do
1568 assert {:ok, %Activity{data: %{"type" => "Like"}} = activity} =
1569 CommonAPI.favorite(user, activity.id)
1571 assert Visibility.is_local_public?(activity)
1572 refute called(Pleroma.Web.Federator.publish(activity))
1576 test "unfavorite" do
1577 user = insert(:user)
1578 other_user = insert(:user)
1580 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe", visibility: "local"})
1582 {:ok, %Activity{}} = CommonAPI.favorite(user, activity.id)
1584 with_mock Pleroma.Web.Federator, publish: fn _ -> :ok end do
1585 assert {:ok, activity} = CommonAPI.unfavorite(activity.id, user)
1586 assert Visibility.is_local_public?(activity)
1587 refute called(Pleroma.Web.Federator.publish(activity))
1591 test "react_with_emoji" do
1592 user = insert(:user)
1593 other_user = insert(:user)
1594 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe", visibility: "local"})
1596 with_mock Pleroma.Web.Federator, publish: fn _ -> :ok end do
1597 assert {:ok, %Activity{data: %{"type" => "EmojiReact"}} = activity} =
1598 CommonAPI.react_with_emoji(activity.id, user, "👍")
1600 assert Visibility.is_local_public?(activity)
1601 refute called(Pleroma.Web.Federator.publish(activity))
1605 test "unreact_with_emoji" do
1606 user = insert(:user)
1607 other_user = insert(:user)
1608 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe", visibility: "local"})
1610 {:ok, _reaction} = CommonAPI.react_with_emoji(activity.id, user, "👍")
1612 with_mock Pleroma.Web.Federator, publish: fn _ -> :ok end do
1613 assert {:ok, %Activity{data: %{"type" => "Undo"}} = activity} =
1614 CommonAPI.unreact_with_emoji(activity.id, user, "👍")
1616 assert Visibility.is_local_public?(activity)
1617 refute called(Pleroma.Web.Federator.publish(activity))
1622 describe "update/3" do
1623 test "updates a post" do
1624 user = insert(:user)
1625 {:ok, activity} = CommonAPI.post(user, %{status: "foo1", spoiler_text: "title 1"})
1627 {:ok, updated} = CommonAPI.update(user, activity, %{status: "updated 2"})
1629 updated_object = Object.normalize(updated)
1630 assert updated_object.data["content"] == "updated 2"
1631 assert Map.get(updated_object.data, "summary", "") == ""
1632 assert Map.has_key?(updated_object.data, "updated")
1635 test "does not change visibility" do
1636 user = insert(:user)
1639 CommonAPI.post(user, %{status: "foo1", spoiler_text: "title 1", visibility: "private"})
1641 {:ok, updated} = CommonAPI.update(user, activity, %{status: "updated 2"})
1643 updated_object = Object.normalize(updated)
1644 assert updated_object.data["content"] == "updated 2"
1645 assert Map.get(updated_object.data, "summary", "") == ""
1646 assert Visibility.get_visibility(updated_object) == "private"
1647 assert Visibility.get_visibility(updated) == "private"
1650 test "updates a post with emoji" do
1651 [{emoji1, _}, {emoji2, _} | _] = Pleroma.Emoji.get_all()
1653 user = insert(:user)
1656 CommonAPI.post(user, %{status: "foo1", spoiler_text: "title 1 :#{emoji1}:"})
1658 {:ok, updated} = CommonAPI.update(user, activity, %{status: "updated 2 :#{emoji2}:"})
1660 updated_object = Object.normalize(updated)
1661 assert updated_object.data["content"] == "updated 2 :#{emoji2}:"
1662 assert %{^emoji2 => _} = updated_object.data["emoji"]
1665 test "updates a post with emoji and federate properly" do
1666 [{emoji1, _}, {emoji2, _} | _] = Pleroma.Emoji.get_all()
1668 user = insert(:user)
1671 CommonAPI.post(user, %{status: "foo1", spoiler_text: "title 1 :#{emoji1}:"})
1673 clear_config([:instance, :federating], true)
1675 with_mock Pleroma.Web.Federator,
1676 publish: fn _p -> nil end do
1677 {:ok, updated} = CommonAPI.update(user, activity, %{status: "updated 2 :#{emoji2}:"})
1679 assert updated.data["object"]["content"] == "updated 2 :#{emoji2}:"
1680 assert %{^emoji2 => _} = updated.data["object"]["emoji"]
1682 assert called(Pleroma.Web.Federator.publish(updated))
1686 test "editing a post that copied a remote title with remote emoji should keep that emoji" do
1687 remote_emoji_uri = "https://remote.org/emoji.png"
1693 "summary" => ":remoteemoji:",
1695 "remoteemoji" => remote_emoji_uri
1700 "name" => "remoteemoji",
1701 "icon" => %{"url" => remote_emoji_uri}
1707 note_activity = insert(:note_activity, note: note)
1709 user = insert(:user)
1712 CommonAPI.post(user, %{
1714 spoiler_text: ":remoteemoji:",
1715 in_reply_to_id: note_activity.id
1718 assert reply.object.data["emoji"]["remoteemoji"] == remote_emoji_uri
1721 CommonAPI.update(user, reply, %{status: "reply mew mew", spoiler_text: ":remoteemoji:"})
1723 edited_note = Pleroma.Object.normalize(edit)
1725 assert edited_note.data["emoji"]["remoteemoji"] == remote_emoji_uri
1728 test "respects MRF" do
1729 user = insert(:user)
1731 clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.KeywordPolicy])
1732 clear_config([:mrf_keyword, :replace], [{"updated", "mewmew"}])
1734 {:ok, activity} = CommonAPI.post(user, %{status: "foo1", spoiler_text: "updated 1"})
1735 assert Object.normalize(activity).data["summary"] == "mewmew 1"
1737 {:ok, updated} = CommonAPI.update(user, activity, %{status: "updated 2"})
1739 updated_object = Object.normalize(updated)
1740 assert updated_object.data["content"] == "mewmew 2"
1741 assert Map.get(updated_object.data, "summary", "") == ""
1742 assert Map.has_key?(updated_object.data, "updated")