diff options
Diffstat (limited to 'test')
178 files changed, 10978 insertions, 4430 deletions
diff --git a/test/activity/ir/topics_test.exs b/test/activity/ir/topics_test.exs index 44aec1e19..14a6e6b71 100644 --- a/test/activity/ir/topics_test.exs +++ b/test/activity/ir/topics_test.exs @@ -83,7 +83,7 @@ defmodule Pleroma.Activity.Ir.TopicsTest do assert Enum.member?(topics, "hashtag:bar") end - test "only converts strinngs to hash tags", %{ + test "only converts strings to hash tags", %{ activity: %{object: %{data: data} = object} = activity } do tagged_data = Map.put(data, "tag", [2]) diff --git a/test/activity_test.exs b/test/activity_test.exs index 0c19f481b..2a92327d1 100644 --- a/test/activity_test.exs +++ b/test/activity_test.exs @@ -11,6 +11,11 @@ defmodule Pleroma.ActivityTest do alias Pleroma.ThreadMute import Pleroma.Factory + setup_all do + Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + test "returns an activity by it's AP id" do activity = insert(:note_activity) found_activity = Activity.get_by_ap_id(activity.data["id"]) @@ -107,8 +112,6 @@ defmodule Pleroma.ActivityTest do describe "search" do setup do - Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) - user = insert(:user) params = %{ @@ -125,8 +128,8 @@ defmodule Pleroma.ActivityTest do "to" => ["https://www.w3.org/ns/activitystreams#Public"] } - {:ok, local_activity} = Pleroma.Web.CommonAPI.post(user, %{"status" => "find me!"}) - {:ok, japanese_activity} = Pleroma.Web.CommonAPI.post(user, %{"status" => "更新情報"}) + {:ok, local_activity} = Pleroma.Web.CommonAPI.post(user, %{status: "find me!"}) + {:ok, japanese_activity} = Pleroma.Web.CommonAPI.post(user, %{status: "更新情報"}) {:ok, job} = Pleroma.Web.Federator.incoming_ap_doc(params) {:ok, remote_activity} = ObanHelpers.perform(job) @@ -225,8 +228,8 @@ defmodule Pleroma.ActivityTest do test "all_by_actor_and_id/2" do user = insert(:user) - {:ok, %{id: id1}} = Pleroma.Web.CommonAPI.post(user, %{"status" => "cofe"}) - {:ok, %{id: id2}} = Pleroma.Web.CommonAPI.post(user, %{"status" => "cofefe"}) + {:ok, %{id: id1}} = Pleroma.Web.CommonAPI.post(user, %{status: "cofe"}) + {:ok, %{id: id2}} = Pleroma.Web.CommonAPI.post(user, %{status: "cofefe"}) assert [] == Activity.all_by_actor_and_id(user, []) diff --git a/test/bbs/handler_test.exs b/test/bbs/handler_test.exs index 74982547b..eb716486e 100644 --- a/test/bbs/handler_test.exs +++ b/test/bbs/handler_test.exs @@ -21,8 +21,8 @@ defmodule Pleroma.BBS.HandlerTest do {:ok, user} = User.follow(user, followed) - {:ok, _first} = CommonAPI.post(user, %{"status" => "hey"}) - {:ok, _second} = CommonAPI.post(followed, %{"status" => "hello"}) + {:ok, _first} = CommonAPI.post(user, %{status: "hey"}) + {:ok, _second} = CommonAPI.post(followed, %{status: "hello"}) output = capture_io(fn -> @@ -62,7 +62,7 @@ defmodule Pleroma.BBS.HandlerTest do user = insert(:user) another_user = insert(:user) - {:ok, activity} = CommonAPI.post(another_user, %{"status" => "this is a test post"}) + {:ok, activity} = CommonAPI.post(another_user, %{status: "this is a test post"}) activity_object = Object.normalize(activity) output = diff --git a/test/bookmark_test.exs b/test/bookmark_test.exs index 021f79322..2726fe7cd 100644 --- a/test/bookmark_test.exs +++ b/test/bookmark_test.exs @@ -11,7 +11,7 @@ defmodule Pleroma.BookmarkTest do describe "create/2" do test "with valid params" do user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "Some cool information"}) + {:ok, activity} = CommonAPI.post(user, %{status: "Some cool information"}) {:ok, bookmark} = Bookmark.create(user.id, activity.id) assert bookmark.user_id == user.id assert bookmark.activity_id == activity.id @@ -32,7 +32,7 @@ defmodule Pleroma.BookmarkTest do test "with valid params" do user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "Some cool information"}) + {:ok, activity} = CommonAPI.post(user, %{status: "Some cool information"}) {:ok, _bookmark} = Bookmark.create(user.id, activity.id) {:ok, _deleted_bookmark} = Bookmark.destroy(user.id, activity.id) @@ -45,7 +45,7 @@ defmodule Pleroma.BookmarkTest do {:ok, activity} = CommonAPI.post(user, %{ - "status" => + status: "Scientists Discover The Secret Behind Tenshi Eating A Corndog Being So Cute – Science Daily" }) diff --git a/test/captcha_test.exs b/test/captcha_test.exs index ac1d846e8..1ab9019ab 100644 --- a/test/captcha_test.exs +++ b/test/captcha_test.exs @@ -61,7 +61,7 @@ defmodule Pleroma.CaptchaTest do assert is_binary(answer) assert :ok = Native.validate(token, answer, answer) - assert {:error, "Invalid CAPTCHA"} == Native.validate(token, answer, answer <> "foobar") + assert {:error, :invalid} == Native.validate(token, answer, answer <> "foobar") end end @@ -78,6 +78,7 @@ defmodule Pleroma.CaptchaTest do assert is_binary(answer) assert :ok = Captcha.validate(token, "63615261b77f5354fb8c4e4986477555", answer) + Cachex.del(:used_captcha_cache, token) end test "doesn't validate invalid answer" do @@ -92,7 +93,7 @@ defmodule Pleroma.CaptchaTest do assert is_binary(answer) - assert {:error, "Invalid answer data"} = + assert {:error, :invalid_answer_data} = Captcha.validate(token, "63615261b77f5354fb8c4e4986477555", answer <> "foobar") end @@ -108,7 +109,7 @@ defmodule Pleroma.CaptchaTest do assert is_binary(answer) - assert {:error, "Invalid answer data"} = + assert {:error, :invalid_answer_data} = Captcha.validate(token, "63615261b77f5354fb8c4e4986477555", nil) end end diff --git a/test/config/config_db_test.exs b/test/config/config_db_test.exs index ac3dde681..336de7359 100644 --- a/test/config/config_db_test.exs +++ b/test/config/config_db_test.exs @@ -43,11 +43,9 @@ defmodule Pleroma.ConfigDBTest do {ConfigDB.from_string(saved.key), ConfigDB.from_binary(saved.value)} ] - assert config[:quack] == [ - level: :info, - meta: [:none], - webhook_url: "https://hooks.slack.com/services/KEY/some_val" - ] + assert config[:quack][:level] == :info + assert config[:quack][:meta] == [:none] + assert config[:quack][:webhook_url] == "https://hooks.slack.com/services/KEY/some_val" end describe "update_or_create/1" do diff --git a/test/config/transfer_task_test.exs b/test/config/transfer_task_test.exs index 0265a6156..473899d1d 100644 --- a/test/config/transfer_task_test.exs +++ b/test/config/transfer_task_test.exs @@ -16,6 +16,8 @@ defmodule Pleroma.Config.TransferTaskTest do refute Application.get_env(:pleroma, :test_key) refute Application.get_env(:idna, :test_key) refute Application.get_env(:quack, :test_key) + refute Application.get_env(:postgrex, :test_key) + initial = Application.get_env(:logger, :level) ConfigDB.create(%{ group: ":pleroma", @@ -35,16 +37,28 @@ defmodule Pleroma.Config.TransferTaskTest do value: [:test_value1, :test_value2] }) + ConfigDB.create(%{ + group: ":postgrex", + key: ":test_key", + value: :value + }) + + ConfigDB.create(%{group: ":logger", key: ":level", value: :debug}) + TransferTask.start_link([]) assert Application.get_env(:pleroma, :test_key) == [live: 2, com: 3] assert Application.get_env(:idna, :test_key) == [live: 15, com: 35] assert Application.get_env(:quack, :test_key) == [:test_value1, :test_value2] + assert Application.get_env(:logger, :level) == :debug + assert Application.get_env(:postgrex, :test_key) == :value on_exit(fn -> Application.delete_env(:pleroma, :test_key) Application.delete_env(:idna, :test_key) Application.delete_env(:quack, :test_key) + Application.delete_env(:postgrex, :test_key) + Application.put_env(:logger, :level, initial) end) end @@ -78,8 +92,8 @@ defmodule Pleroma.Config.TransferTaskTest do end test "transfer config values with full subkey update" do - emoji = Application.get_env(:pleroma, :emoji) - assets = Application.get_env(:pleroma, :assets) + clear_config(:emoji) + clear_config(:assets) ConfigDB.create(%{ group: ":pleroma", @@ -99,11 +113,6 @@ defmodule Pleroma.Config.TransferTaskTest do assert emoji_env[:groups] == [a: 1, b: 2] assets_env = Application.get_env(:pleroma, :assets) assert assets_env[:mascots] == [a: 1, b: 2] - - on_exit(fn -> - Application.put_env(:pleroma, :emoji, emoji) - Application.put_env(:pleroma, :assets, assets) - end) end describe "pleroma restart" do @@ -112,8 +121,7 @@ defmodule Pleroma.Config.TransferTaskTest do end test "don't restart if no reboot time settings were changed" do - emoji = Application.get_env(:pleroma, :emoji) - on_exit(fn -> Application.put_env(:pleroma, :emoji, emoji) end) + clear_config(:emoji) ConfigDB.create(%{ group: ":pleroma", @@ -128,8 +136,7 @@ defmodule Pleroma.Config.TransferTaskTest do end test "on reboot time key" do - chat = Application.get_env(:pleroma, :chat) - on_exit(fn -> Application.put_env(:pleroma, :chat, chat) end) + clear_config(:chat) ConfigDB.create(%{ group: ":pleroma", @@ -141,8 +148,7 @@ defmodule Pleroma.Config.TransferTaskTest do end test "on reboot time subkey" do - captcha = Application.get_env(:pleroma, Pleroma.Captcha) - on_exit(fn -> Application.put_env(:pleroma, Pleroma.Captcha, captcha) end) + clear_config(Pleroma.Captcha) ConfigDB.create(%{ group: ":pleroma", @@ -154,13 +160,8 @@ defmodule Pleroma.Config.TransferTaskTest do end test "don't restart pleroma on reboot time key and subkey if there is false flag" do - chat = Application.get_env(:pleroma, :chat) - captcha = Application.get_env(:pleroma, Pleroma.Captcha) - - on_exit(fn -> - Application.put_env(:pleroma, :chat, chat) - Application.put_env(:pleroma, Pleroma.Captcha, captcha) - end) + clear_config(:chat) + clear_config(Pleroma.Captcha) ConfigDB.create(%{ group: ":pleroma", diff --git a/test/conversation/participation_test.exs b/test/conversation/participation_test.exs index 3536842e8..59a1b6492 100644 --- a/test/conversation/participation_test.exs +++ b/test/conversation/participation_test.exs @@ -16,7 +16,7 @@ defmodule Pleroma.Conversation.ParticipationTest do other_user = insert(:user) {:ok, _activity} = - CommonAPI.post(user, %{"status" => "Hey @#{other_user.nickname}.", "visibility" => "direct"}) + CommonAPI.post(user, %{status: "Hey @#{other_user.nickname}.", visibility: "direct"}) [participation] = Participation.for_user(user) @@ -30,7 +30,7 @@ defmodule Pleroma.Conversation.ParticipationTest do other_user = insert(:user) {:ok, _} = - CommonAPI.post(user, %{"status" => "Hey @#{other_user.nickname}.", "visibility" => "direct"}) + CommonAPI.post(user, %{status: "Hey @#{other_user.nickname}.", visibility: "direct"}) user = User.get_cached_by_id(user.id) other_user = User.get_cached_by_id(other_user.id) @@ -43,9 +43,9 @@ defmodule Pleroma.Conversation.ParticipationTest do {:ok, _} = CommonAPI.post(other_user, %{ - "status" => "Hey @#{user.nickname}.", - "visibility" => "direct", - "in_reply_to_conversation_id" => participation.id + status: "Hey @#{user.nickname}.", + visibility: "direct", + in_reply_to_conversation_id: participation.id }) user = User.get_cached_by_id(user.id) @@ -64,7 +64,7 @@ defmodule Pleroma.Conversation.ParticipationTest do third_user = insert(:user) {:ok, activity} = - CommonAPI.post(user, %{"status" => "Hey @#{other_user.nickname}.", "visibility" => "direct"}) + CommonAPI.post(user, %{status: "Hey @#{other_user.nickname}.", visibility: "direct"}) user = User.get_cached_by_id(user.id) other_user = User.get_cached_by_id(other_user.id) @@ -79,9 +79,9 @@ defmodule Pleroma.Conversation.ParticipationTest do {:ok, _activity} = CommonAPI.post(user, %{ - "in_reply_to_status_id" => activity.id, - "status" => "Hey @#{third_user.nickname}.", - "visibility" => "direct" + in_reply_to_status_id: activity.id, + status: "Hey @#{third_user.nickname}.", + visibility: "direct" }) [participation] = Participation.for_user(user) @@ -154,14 +154,14 @@ defmodule Pleroma.Conversation.ParticipationTest do test "gets all the participations for a user, ordered by updated at descending" do user = insert(:user) - {:ok, activity_one} = CommonAPI.post(user, %{"status" => "x", "visibility" => "direct"}) - {:ok, activity_two} = CommonAPI.post(user, %{"status" => "x", "visibility" => "direct"}) + {:ok, activity_one} = CommonAPI.post(user, %{status: "x", visibility: "direct"}) + {:ok, activity_two} = CommonAPI.post(user, %{status: "x", visibility: "direct"}) {:ok, activity_three} = CommonAPI.post(user, %{ - "status" => "x", - "visibility" => "direct", - "in_reply_to_status_id" => activity_one.id + status: "x", + visibility: "direct", + in_reply_to_status_id: activity_one.id }) # Offset participations because the accuracy of updated_at is down to a second @@ -201,7 +201,7 @@ defmodule Pleroma.Conversation.ParticipationTest do test "Doesn't die when the conversation gets empty" do user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"}) + {:ok, activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"}) [participation] = Participation.for_user_with_last_activity_id(user) assert participation.last_activity_id == activity.id @@ -215,7 +215,7 @@ defmodule Pleroma.Conversation.ParticipationTest do user = insert(:user) other_user = insert(:user) - {:ok, _activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"}) + {:ok, _activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"}) [participation] = Participation.for_user_with_last_activity_id(user) participation = Repo.preload(participation, :recipients) @@ -239,26 +239,26 @@ defmodule Pleroma.Conversation.ParticipationTest do {:ok, _direct1} = CommonAPI.post(third_user, %{ - "status" => "Hi @#{blocker.nickname}", - "visibility" => "direct" + status: "Hi @#{blocker.nickname}", + visibility: "direct" }) {:ok, _direct2} = CommonAPI.post(third_user, %{ - "status" => "Hi @#{blocker.nickname}, @#{blocked.nickname}", - "visibility" => "direct" + status: "Hi @#{blocker.nickname}, @#{blocked.nickname}", + visibility: "direct" }) {:ok, _direct3} = CommonAPI.post(blocked, %{ - "status" => "Hi @#{blocker.nickname}", - "visibility" => "direct" + status: "Hi @#{blocker.nickname}", + visibility: "direct" }) {:ok, _direct4} = CommonAPI.post(blocked, %{ - "status" => "Hi @#{blocker.nickname}, @#{third_user.nickname}", - "visibility" => "direct" + status: "Hi @#{blocker.nickname}, @#{third_user.nickname}", + visibility: "direct" }) assert [%{read: false}, %{read: false}, %{read: false}, %{read: false}] = @@ -293,8 +293,8 @@ defmodule Pleroma.Conversation.ParticipationTest do # When the blocked user is the author {:ok, _direct1} = CommonAPI.post(blocked, %{ - "status" => "Hi @#{blocker.nickname}", - "visibility" => "direct" + status: "Hi @#{blocker.nickname}", + visibility: "direct" }) assert [%{read: true}] = Participation.for_user(blocker) @@ -303,8 +303,8 @@ defmodule Pleroma.Conversation.ParticipationTest do # When the blocked user is a recipient {:ok, _direct2} = CommonAPI.post(third_user, %{ - "status" => "Hi @#{blocker.nickname}, @#{blocked.nickname}", - "visibility" => "direct" + status: "Hi @#{blocker.nickname}, @#{blocked.nickname}", + visibility: "direct" }) assert [%{read: true}, %{read: true}] = Participation.for_user(blocker) @@ -321,8 +321,8 @@ defmodule Pleroma.Conversation.ParticipationTest do {:ok, _direct1} = CommonAPI.post(blocker, %{ - "status" => "Hi @#{third_user.nickname}, @#{blocked.nickname}", - "visibility" => "direct" + status: "Hi @#{third_user.nickname}, @#{blocked.nickname}", + visibility: "direct" }) {:ok, _user_relationship} = User.block(blocker, blocked) @@ -334,9 +334,9 @@ defmodule Pleroma.Conversation.ParticipationTest do # When it's a reply from the blocked user {:ok, _direct2} = CommonAPI.post(blocked, %{ - "status" => "reply", - "visibility" => "direct", - "in_reply_to_conversation_id" => blocked_participation.id + status: "reply", + visibility: "direct", + in_reply_to_conversation_id: blocked_participation.id }) assert [%{read: true}] = Participation.for_user(blocker) @@ -347,9 +347,9 @@ defmodule Pleroma.Conversation.ParticipationTest do # When it's a reply from the third user {:ok, _direct3} = CommonAPI.post(third_user, %{ - "status" => "reply", - "visibility" => "direct", - "in_reply_to_conversation_id" => third_user_participation.id + status: "reply", + visibility: "direct", + in_reply_to_conversation_id: third_user_participation.id }) assert [%{read: true}] = Participation.for_user(blocker) diff --git a/test/conversation_test.exs b/test/conversation_test.exs index 056a0e920..359aa6840 100644 --- a/test/conversation_test.exs +++ b/test/conversation_test.exs @@ -18,7 +18,7 @@ defmodule Pleroma.ConversationTest do other_user = insert(:user) {:ok, _activity} = - CommonAPI.post(user, %{"visibility" => "direct", "status" => "hey @#{other_user.nickname}"}) + CommonAPI.post(user, %{visibility: "direct", status: "hey @#{other_user.nickname}"}) Pleroma.Tests.ObanHelpers.perform_all() @@ -46,7 +46,7 @@ defmodule Pleroma.ConversationTest do test "public posts don't create conversations" do user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "Hey"}) + {:ok, activity} = CommonAPI.post(user, %{status: "Hey"}) object = Pleroma.Object.normalize(activity) context = object.data["context"] @@ -62,7 +62,7 @@ defmodule Pleroma.ConversationTest do tridi = insert(:user) {:ok, activity} = - CommonAPI.post(har, %{"status" => "Hey @#{jafnhar.nickname}", "visibility" => "direct"}) + CommonAPI.post(har, %{status: "Hey @#{jafnhar.nickname}", visibility: "direct"}) object = Pleroma.Object.normalize(activity) context = object.data["context"] @@ -81,9 +81,9 @@ defmodule Pleroma.ConversationTest do {:ok, activity} = CommonAPI.post(jafnhar, %{ - "status" => "Hey @#{har.nickname}", - "visibility" => "direct", - "in_reply_to_status_id" => activity.id + status: "Hey @#{har.nickname}", + visibility: "direct", + in_reply_to_status_id: activity.id }) object = Pleroma.Object.normalize(activity) @@ -105,9 +105,9 @@ defmodule Pleroma.ConversationTest do {:ok, activity} = CommonAPI.post(tridi, %{ - "status" => "Hey @#{har.nickname}", - "visibility" => "direct", - "in_reply_to_status_id" => activity.id + status: "Hey @#{har.nickname}", + visibility: "direct", + in_reply_to_status_id: activity.id }) object = Pleroma.Object.normalize(activity) @@ -149,14 +149,14 @@ defmodule Pleroma.ConversationTest do jafnhar = insert(:user, local: false) {:ok, activity} = - CommonAPI.post(har, %{"status" => "Hey @#{jafnhar.nickname}", "visibility" => "direct"}) + CommonAPI.post(har, %{status: "Hey @#{jafnhar.nickname}", visibility: "direct"}) {:ok, conversation} = Conversation.create_or_bump_for(activity) assert length(conversation.participations) == 2 {:ok, activity} = - CommonAPI.post(har, %{"status" => "Hey @#{jafnhar.nickname}", "visibility" => "public"}) + CommonAPI.post(har, %{status: "Hey @#{jafnhar.nickname}", visibility: "public"}) assert {:error, _} = Conversation.create_or_bump_for(activity) end diff --git a/test/emoji/formatter_test.exs b/test/emoji/formatter_test.exs index 3bfee9420..12af6cd8b 100644 --- a/test/emoji/formatter_test.exs +++ b/test/emoji/formatter_test.exs @@ -3,7 +3,6 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Emoji.FormatterTest do - alias Pleroma.Emoji alias Pleroma.Emoji.Formatter use Pleroma.DataCase @@ -32,30 +31,19 @@ defmodule Pleroma.Emoji.FormatterTest do end end - describe "get_emoji" do + describe "get_emoji_map" do test "it returns the emoji used in the text" do - text = "I love :firefox:" - - assert Formatter.get_emoji(text) == [ - {"firefox", - %Emoji{ - code: "firefox", - file: "/emoji/Firefox.gif", - tags: ["Gif", "Fun"], - safe_code: "firefox", - safe_file: "/emoji/Firefox.gif" - }} - ] + assert Formatter.get_emoji_map("I love :firefox:") == %{ + "firefox" => "http://localhost:4001/emoji/Firefox.gif" + } end test "it returns a nice empty result when no emojis are present" do - text = "I love moominamma" - assert Formatter.get_emoji(text) == [] + assert Formatter.get_emoji_map("I love moominamma") == %{} end test "it doesn't die when text is absent" do - text = nil - assert Formatter.get_emoji(text) == [] + assert Formatter.get_emoji_map(nil) == %{} end end end diff --git a/test/filter_test.exs b/test/filter_test.exs index b2a8330ee..63a30c736 100644 --- a/test/filter_test.exs +++ b/test/filter_test.exs @@ -141,17 +141,15 @@ defmodule Pleroma.FilterTest do context: ["home"] } - query_two = %Pleroma.Filter{ - user_id: user.id, - filter_id: 1, + changes = %{ phrase: "who", context: ["home", "timeline"] } {:ok, filter_one} = Pleroma.Filter.create(query_one) - {:ok, filter_two} = Pleroma.Filter.update(query_two) + {:ok, filter_two} = Pleroma.Filter.update(filter_one, changes) assert filter_one != filter_two - assert filter_two.phrase == query_two.phrase - assert filter_two.context == query_two.context + assert filter_two.phrase == changes.phrase + assert filter_two.context == changes.context end end diff --git a/test/fixtures/config/temp.secret.exs b/test/fixtures/config/temp.secret.exs index f4686c101..dc950ca30 100644 --- a/test/fixtures/config/temp.secret.exs +++ b/test/fixtures/config/temp.secret.exs @@ -7,3 +7,5 @@ config :pleroma, :second_setting, key: "value2", key2: ["Activity"] config :quack, level: :info config :pleroma, Pleroma.Repo, pool: Ecto.Adapters.SQL.Sandbox + +config :postgrex, :json_library, Poison diff --git a/test/fixtures/emoji/packs/blank.png.zip b/test/fixtures/emoji/packs/blank.png.zip Binary files differnew file mode 100644 index 000000000..651daf127 --- /dev/null +++ b/test/fixtures/emoji/packs/blank.png.zip diff --git a/test/fixtures/emoji/packs/default-manifest.json b/test/fixtures/emoji/packs/default-manifest.json new file mode 100644 index 000000000..c8433808d --- /dev/null +++ b/test/fixtures/emoji/packs/default-manifest.json @@ -0,0 +1,10 @@ +{ + "finmoji": { + "license": "CC BY-NC-ND 4.0", + "homepage": "https://finland.fi/emoji/", + "description": "Finland is the first country in the world to publish its own set of country themed emojis. The Finland emoji collection contains 56 tongue-in-cheek emotions, which were created to explain some hard-to-describe Finnish emotions, Finnish words and customs.", + "src": "https://finland.fi/wp-content/uploads/2017/06/finland-emojis.zip", + "src_sha256": "384025A1AC6314473863A11AC7AB38A12C01B851A3F82359B89B4D4211D3291D", + "files": "finmoji.json" + } +}
\ No newline at end of file diff --git a/test/fixtures/emoji/packs/finmoji.json b/test/fixtures/emoji/packs/finmoji.json new file mode 100644 index 000000000..279770998 --- /dev/null +++ b/test/fixtures/emoji/packs/finmoji.json @@ -0,0 +1,3 @@ +{ + "blank": "blank.png" +}
\ No newline at end of file diff --git a/test/fixtures/emoji/packs/manifest.json b/test/fixtures/emoji/packs/manifest.json new file mode 100644 index 000000000..2d51a459b --- /dev/null +++ b/test/fixtures/emoji/packs/manifest.json @@ -0,0 +1,10 @@ +{ + "blobs.gg": { + "src_sha256": "3a12f3a181678d5b3584a62095411b0d60a335118135910d879920f8ade5a57f", + "src": "https://git.pleroma.social/pleroma/emoji-index/raw/master/packs/blobs_gg.zip", + "license": "Apache 2.0", + "homepage": "https://blobs.gg", + "files": "blobs_gg.json", + "description": "Blob Emoji from blobs.gg repacked as apng" + } +}
\ No newline at end of file diff --git a/test/fixtures/tesla_mock/craigmaloney.json b/test/fixtures/tesla_mock/craigmaloney.json new file mode 100644 index 000000000..56ea9c7c3 --- /dev/null +++ b/test/fixtures/tesla_mock/craigmaloney.json @@ -0,0 +1,112 @@ +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1", + { + "CacheFile": "pt:CacheFile", + "Hashtag": "as:Hashtag", + "Infohash": "pt:Infohash", + "RsaSignature2017": "https://w3id.org/security#RsaSignature2017", + "category": "sc:category", + "commentsEnabled": { + "@id": "pt:commentsEnabled", + "@type": "sc:Boolean" + }, + "downloadEnabled": { + "@id": "pt:downloadEnabled", + "@type": "sc:Boolean" + }, + "expires": "sc:expires", + "fps": { + "@id": "pt:fps", + "@type": "sc:Number" + }, + "language": "sc:inLanguage", + "licence": "sc:license", + "originallyPublishedAt": "sc:datePublished", + "position": { + "@id": "pt:position", + "@type": "sc:Number" + }, + "pt": "https://joinpeertube.org/ns#", + "sc": "http://schema.org#", + "sensitive": "as:sensitive", + "size": { + "@id": "pt:size", + "@type": "sc:Number" + }, + "startTimestamp": { + "@id": "pt:startTimestamp", + "@type": "sc:Number" + }, + "state": { + "@id": "pt:state", + "@type": "sc:Number" + }, + "stopTimestamp": { + "@id": "pt:stopTimestamp", + "@type": "sc:Number" + }, + "subtitleLanguage": "sc:subtitleLanguage", + "support": { + "@id": "pt:support", + "@type": "sc:Text" + }, + "uuid": "sc:identifier", + "views": { + "@id": "pt:views", + "@type": "sc:Number" + }, + "waitTranscoding": { + "@id": "pt:waitTranscoding", + "@type": "sc:Boolean" + } + }, + { + "comments": { + "@id": "as:comments", + "@type": "@id" + }, + "dislikes": { + "@id": "as:dislikes", + "@type": "@id" + }, + "likes": { + "@id": "as:likes", + "@type": "@id" + }, + "playlists": { + "@id": "pt:playlists", + "@type": "@id" + }, + "shares": { + "@id": "as:shares", + "@type": "@id" + } + } + ], + "endpoints": { + "sharedInbox": "https://peertube.social/inbox" + }, + "followers": "https://peertube.social/accounts/craigmaloney/followers", + "following": "https://peertube.social/accounts/craigmaloney/following", + "icon": { + "mediaType": "image/png", + "type": "Image", + "url": "https://peertube.social/lazy-static/avatars/87bd694b-95bc-4066-83f4-bddfcd2b9caa.png" + }, + "id": "https://peertube.social/accounts/craigmaloney", + "inbox": "https://peertube.social/accounts/craigmaloney/inbox", + "name": "Craig Maloney", + "outbox": "https://peertube.social/accounts/craigmaloney/outbox", + "playlists": "https://peertube.social/accounts/craigmaloney/playlists", + "preferredUsername": "craigmaloney", + "publicKey": { + "id": "https://peertube.social/accounts/craigmaloney#main-key", + "owner": "https://peertube.social/accounts/craigmaloney", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA9qvGIYUW01yc8CCsrwxK\n5OXlV5s7EbNWY8tJr/p1oGuELZwAnG2XKxtdbvgcCT+YxL5uRXIdCFIIIKrzRFr/\nHfS0mOgNT9u3gu+SstCNgtatciT0RVP77yiC3b2NHq1NRRvvVhzQb4cpIWObIxqh\nb2ypDClTc7XaKtgmQCbwZlGyZMT+EKz/vustD6BlpGsglRkm7iES6s1PPGb1BU+n\nS94KhbS2DOFiLcXCVWt0QarokIIuKznp4+xP1axKyP+SkT5AHx08Nd5TYFb2C1Jl\nz0WD/1q0mAN62m7QrA3SQPUgB+wWD+S3Nzf7FwNPiP4srbBgxVEUnji/r9mQ6BXC\nrQIDAQAB\n-----END PUBLIC KEY-----" + }, + "summary": null, + "type": "Person", + "url": "https://peertube.social/accounts/craigmaloney" +} diff --git a/test/fixtures/tesla_mock/funkwhale_audio.json b/test/fixtures/tesla_mock/funkwhale_audio.json new file mode 100644 index 000000000..15736b1f8 --- /dev/null +++ b/test/fixtures/tesla_mock/funkwhale_audio.json @@ -0,0 +1,44 @@ +{ + "id": "https://channels.tests.funkwhale.audio/federation/music/uploads/42342395-0208-4fee-a38d-259a6dae0871", + "type": "Audio", + "name": "Compositions - Test Audio for Pleroma", + "attributedTo": "https://channels.tests.funkwhale.audio/federation/actors/compositions", + "published": "2020-03-11T10:01:52.714918+00:00", + "to": "https://www.w3.org/ns/activitystreams#Public", + "url": [ + { + "type": "Link", + "mimeType": "audio/ogg", + "href": "https://channels.tests.funkwhale.audio/api/v1/listen/3901e5d8-0445-49d5-9711-e096cf32e515/?upload=42342395-0208-4fee-a38d-259a6dae0871&download=false" + }, + { + "type": "Link", + "mimeType": "text/html", + "href": "https://channels.tests.funkwhale.audio/library/tracks/74" + } + ], + "content": "<p>This is a test Audio for Pleroma.</p>", + "mediaType": "text/html", + "tag": [ + { + "type": "Hashtag", + "name": "#funkwhale" + }, + { + "type": "Hashtag", + "name": "#test" + }, + { + "type": "Hashtag", + "name": "#tests" + } + ], + "summary": "#funkwhale #test #tests", + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1", + { + "manuallyApprovesFollowers": "as:manuallyApprovesFollowers" + } + ] +} diff --git a/test/fixtures/tesla_mock/funkwhale_channel.json b/test/fixtures/tesla_mock/funkwhale_channel.json new file mode 100644 index 000000000..cf9ee8151 --- /dev/null +++ b/test/fixtures/tesla_mock/funkwhale_channel.json @@ -0,0 +1,44 @@ +{ + "id": "https://channels.tests.funkwhale.audio/federation/actors/compositions", + "outbox": "https://channels.tests.funkwhale.audio/federation/actors/compositions/outbox", + "inbox": "https://channels.tests.funkwhale.audio/federation/actors/compositions/inbox", + "preferredUsername": "compositions", + "type": "Person", + "name": "Compositions", + "followers": "https://channels.tests.funkwhale.audio/federation/actors/compositions/followers", + "following": "https://channels.tests.funkwhale.audio/federation/actors/compositions/following", + "manuallyApprovesFollowers": false, + "url": [ + { + "type": "Link", + "href": "https://channels.tests.funkwhale.audio/channels/compositions", + "mediaType": "text/html" + }, + { + "type": "Link", + "href": "https://channels.tests.funkwhale.audio/api/v1/channels/compositions/rss", + "mediaType": "application/rss+xml" + } + ], + "icon": { + "type": "Image", + "url": "https://channels.tests.funkwhale.audio/media/attachments/75/b4/f1/nosmile.jpeg", + "mediaType": "image/jpeg" + }, + "summary": "<p>I'm testing federation with the fediverse :)</p>", + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1", + { + "manuallyApprovesFollowers": "as:manuallyApprovesFollowers" + } + ], + "publicKey": { + "owner": "https://channels.tests.funkwhale.audio/federation/actors/compositions", + "publicKeyPem": "-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAv25u57oZfVLV3KltS+HcsdSx9Op4MmzIes1J8Wu8s0KbdXf2zEwS\nsVqyHgs/XCbnzsR3FqyJTo46D2BVnvZcuU5srNcR2I2HMaqQ0oVdnATE4K6KdcgV\nN+98pMWo56B8LTgE1VpvqbsrXLi9jCTzjrkebVMOP+ZVu+64v1qdgddseblYMnBZ\nct0s7ONbHnqrWlTGf5wES1uIZTVdn5r4MduZG+Uenfi1opBS0lUUxfWdW9r0oF2b\nyneZUyaUCbEroeKbqsweXCWVgnMarUOsgqC42KM4cf95lySSwTSaUtZYIbTw7s9W\n2jveU/rVg8BYZu5JK5obgBoxtlUeUoSswwIDAQAB\n-----END RSA PUBLIC KEY-----\n", + "id": "https://channels.tests.funkwhale.audio/federation/actors/compositions#main-key" + }, + "endpoints": { + "sharedInbox": "https://channels.tests.funkwhale.audio/federation/shared/inbox" + } +} diff --git a/test/fixtures/tesla_mock/peertube-social.json b/test/fixtures/tesla_mock/peertube-social.json new file mode 100644 index 000000000..0e996ba35 --- /dev/null +++ b/test/fixtures/tesla_mock/peertube-social.json @@ -0,0 +1,234 @@ +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1", + { + "CacheFile": "pt:CacheFile", + "Hashtag": "as:Hashtag", + "Infohash": "pt:Infohash", + "RsaSignature2017": "https://w3id.org/security#RsaSignature2017", + "category": "sc:category", + "commentsEnabled": { + "@id": "pt:commentsEnabled", + "@type": "sc:Boolean" + }, + "downloadEnabled": { + "@id": "pt:downloadEnabled", + "@type": "sc:Boolean" + }, + "expires": "sc:expires", + "fps": { + "@id": "pt:fps", + "@type": "sc:Number" + }, + "language": "sc:inLanguage", + "licence": "sc:license", + "originallyPublishedAt": "sc:datePublished", + "position": { + "@id": "pt:position", + "@type": "sc:Number" + }, + "pt": "https://joinpeertube.org/ns#", + "sc": "http://schema.org#", + "sensitive": "as:sensitive", + "size": { + "@id": "pt:size", + "@type": "sc:Number" + }, + "startTimestamp": { + "@id": "pt:startTimestamp", + "@type": "sc:Number" + }, + "state": { + "@id": "pt:state", + "@type": "sc:Number" + }, + "stopTimestamp": { + "@id": "pt:stopTimestamp", + "@type": "sc:Number" + }, + "subtitleLanguage": "sc:subtitleLanguage", + "support": { + "@id": "pt:support", + "@type": "sc:Text" + }, + "uuid": "sc:identifier", + "views": { + "@id": "pt:views", + "@type": "sc:Number" + }, + "waitTranscoding": { + "@id": "pt:waitTranscoding", + "@type": "sc:Boolean" + } + }, + { + "comments": { + "@id": "as:comments", + "@type": "@id" + }, + "dislikes": { + "@id": "as:dislikes", + "@type": "@id" + }, + "likes": { + "@id": "as:likes", + "@type": "@id" + }, + "playlists": { + "@id": "pt:playlists", + "@type": "@id" + }, + "shares": { + "@id": "as:shares", + "@type": "@id" + } + } + ], + "attributedTo": [ + { + "id": "https://peertube.social/accounts/craigmaloney", + "type": "Person" + }, + { + "id": "https://peertube.social/video-channels/9909c7d9-6b5b-4aae-9164-c1af7229c91c", + "type": "Group" + } + ], + "category": { + "identifier": "15", + "name": "Science & Technology" + }, + "cc": [ + "https://peertube.social/accounts/craigmaloney/followers" + ], + "comments": "https://peertube.social/videos/watch/278d2b7c-0f38-4aaa-afe6-9ecc0c4a34fe/comments", + "commentsEnabled": true, + "content": "Support this and our other Michigan!/usr/group videos and meetings. Learn more at http://mug.org/membership\n\nTwenty Years in Jail: FreeBSD's Jails, Then and Now\n\nJails started as a limited virtualization system, but over the last two years they've...", + "dislikes": "https://peertube.social/videos/watch/278d2b7c-0f38-4aaa-afe6-9ecc0c4a34fe/dislikes", + "downloadEnabled": true, + "duration": "PT5151S", + "icon": { + "height": 122, + "mediaType": "image/jpeg", + "type": "Image", + "url": "https://peertube.social/static/thumbnails/278d2b7c-0f38-4aaa-afe6-9ecc0c4a34fe.jpg", + "width": 223 + }, + "id": "https://peertube.social/videos/watch/278d2b7c-0f38-4aaa-afe6-9ecc0c4a34fe", + "language": { + "identifier": "en", + "name": "English" + }, + "licence": { + "identifier": "1", + "name": "Attribution" + }, + "likes": "https://peertube.social/videos/watch/278d2b7c-0f38-4aaa-afe6-9ecc0c4a34fe/likes", + "mediaType": "text/markdown", + "name": "Twenty Years in Jail: FreeBSD's Jails, Then and Now", + "originallyPublishedAt": "2019-08-13T00:00:00.000Z", + "published": "2020-02-12T01:06:08.054Z", + "sensitive": false, + "shares": "https://peertube.social/videos/watch/278d2b7c-0f38-4aaa-afe6-9ecc0c4a34fe/announces", + "state": 1, + "subtitleLanguage": [], + "support": "Learn more at http://mug.org", + "tag": [ + { + "name": "linux", + "type": "Hashtag" + }, + { + "name": "mug.org", + "type": "Hashtag" + }, + { + "name": "open", + "type": "Hashtag" + }, + { + "name": "oss", + "type": "Hashtag" + }, + { + "name": "source", + "type": "Hashtag" + } + ], + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "type": "Video", + "updated": "2020-02-15T15:01:09.474Z", + "url": [ + { + "href": "https://peertube.social/videos/watch/278d2b7c-0f38-4aaa-afe6-9ecc0c4a34fe", + "mediaType": "text/html", + "type": "Link" + }, + { + "fps": 30, + "height": 240, + "href": "https://peertube.social/static/webseed/278d2b7c-0f38-4aaa-afe6-9ecc0c4a34fe-240.mp4", + "mediaType": "video/mp4", + "size": 119465800, + "type": "Link" + }, + { + "height": 240, + "href": "https://peertube.social/static/torrents/278d2b7c-0f38-4aaa-afe6-9ecc0c4a34fe-240.torrent", + "mediaType": "application/x-bittorrent", + "type": "Link" + }, + { + "height": 240, + "href": "magnet:?xs=https%3A%2F%2Fpeertube.social%2Fstatic%2Ftorrents%2F278d2b7c-0f38-4aaa-afe6-9ecc0c4a34fe-240.torrent&xt=urn:btih:b3365331a8543bf48d09add56d7fe4b1cbbb5659&dn=Twenty+Years+in+Jail%3A+FreeBSD's+Jails%2C+Then+and+Now&tr=wss%3A%2F%2Fpeertube.social%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fpeertube.social%2Ftracker%2Fannounce&ws=https%3A%2F%2Fpeertube.social%2Fstatic%2Fwebseed%2F278d2b7c-0f38-4aaa-afe6-9ecc0c4a34fe-240.mp4", + "mediaType": "application/x-bittorrent;x-scheme-handler/magnet", + "type": "Link" + }, + { + "fps": 30, + "height": 360, + "href": "https://peertube.social/static/webseed/278d2b7c-0f38-4aaa-afe6-9ecc0c4a34fe-360.mp4", + "mediaType": "video/mp4", + "size": 143930318, + "type": "Link" + }, + { + "height": 360, + "href": "https://peertube.social/static/torrents/278d2b7c-0f38-4aaa-afe6-9ecc0c4a34fe-360.torrent", + "mediaType": "application/x-bittorrent", + "type": "Link" + }, + { + "height": 360, + "href": "magnet:?xs=https%3A%2F%2Fpeertube.social%2Fstatic%2Ftorrents%2F278d2b7c-0f38-4aaa-afe6-9ecc0c4a34fe-360.torrent&xt=urn:btih:0d37b23c98cb0d89e28b5dc8f49b3c97a041e569&dn=Twenty+Years+in+Jail%3A+FreeBSD's+Jails%2C+Then+and+Now&tr=wss%3A%2F%2Fpeertube.social%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fpeertube.social%2Ftracker%2Fannounce&ws=https%3A%2F%2Fpeertube.social%2Fstatic%2Fwebseed%2F278d2b7c-0f38-4aaa-afe6-9ecc0c4a34fe-360.mp4", + "mediaType": "application/x-bittorrent;x-scheme-handler/magnet", + "type": "Link" + }, + { + "fps": 30, + "height": 480, + "href": "https://peertube.social/static/webseed/278d2b7c-0f38-4aaa-afe6-9ecc0c4a34fe-480.mp4", + "mediaType": "video/mp4", + "size": 130530754, + "type": "Link" + }, + { + "height": 480, + "href": "https://peertube.social/static/torrents/278d2b7c-0f38-4aaa-afe6-9ecc0c4a34fe-480.torrent", + "mediaType": "application/x-bittorrent", + "type": "Link" + }, + { + "height": 480, + "href": "magnet:?xs=https%3A%2F%2Fpeertube.social%2Fstatic%2Ftorrents%2F278d2b7c-0f38-4aaa-afe6-9ecc0c4a34fe-480.torrent&xt=urn:btih:3a13ff822ad9494165eff6167183ddaaabc1372a&dn=Twenty+Years+in+Jail%3A+FreeBSD's+Jails%2C+Then+and+Now&tr=wss%3A%2F%2Fpeertube.social%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fpeertube.social%2Ftracker%2Fannounce&ws=https%3A%2F%2Fpeertube.social%2Fstatic%2Fwebseed%2F278d2b7c-0f38-4aaa-afe6-9ecc0c4a34fe-480.mp4", + "mediaType": "application/x-bittorrent;x-scheme-handler/magnet", + "type": "Link" + } + ], + "uuid": "278d2b7c-0f38-4aaa-afe6-9ecc0c4a34fe", + "views": 2, + "waitTranscoding": false +} diff --git a/test/fixtures/users_mock/localhost.json b/test/fixtures/users_mock/localhost.json new file mode 100644 index 000000000..a49935db1 --- /dev/null +++ b/test/fixtures/users_mock/localhost.json @@ -0,0 +1,41 @@ +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + "http://localhost:4001/schemas/litepub-0.1.jsonld", + { + "@language": "und" + } + ], + "attachment": [], + "endpoints": { + "oauthAuthorizationEndpoint": "http://localhost:4001/oauth/authorize", + "oauthRegistrationEndpoint": "http://localhost:4001/api/v1/apps", + "oauthTokenEndpoint": "http://localhost:4001/oauth/token", + "sharedInbox": "http://localhost:4001/inbox" + }, + "followers": "http://localhost:4001/users/{{nickname}}/followers", + "following": "http://localhost:4001/users/{{nickname}}/following", + "icon": { + "type": "Image", + "url": "http://localhost:4001/media/4e914f5b84e4a259a3f6c2d2edc9ab642f2ab05f3e3d9c52c81fc2d984b3d51e.jpg" + }, + "id": "http://localhost:4001/users/{{nickname}}", + "image": { + "type": "Image", + "url": "http://localhost:4001/media/f739efddefeee49c6e67e947c4811fdc911785c16ae43da4c3684051fbf8da6a.jpg?name=f739efddefeee49c6e67e947c4811fdc911785c16ae43da4c3684051fbf8da6a.jpg" + }, + "inbox": "http://localhost:4001/users/{{nickname}}/inbox", + "manuallyApprovesFollowers": false, + "name": "{{nickname}}", + "outbox": "http://localhost:4001/users/{{nickname}}/outbox", + "preferredUsername": "{{nickname}}", + "publicKey": { + "id": "http://localhost:4001/users/{{nickname}}#main-key", + "owner": "http://localhost:4001/users/{{nickname}}", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5DLtwGXNZElJyxFGfcVc\nXANhaMadj/iYYQwZjOJTV9QsbtiNBeIK54PJrYuU0/0YIdrvS1iqheX5IwXRhcwa\nhm3ZyLz7XeN9st7FBni4BmZMBtMpxAuYuu5p/jbWy13qAiYOhPreCx0wrWgm/lBD\n9mkgaxIxPooBE0S4ZWEJIDIV1Vft3AWcRUyWW1vIBK0uZzs6GYshbQZB952S0yo4\nFzI1hABGHncH8UvuFauh4EZ8tY7/X5I0pGRnDOcRN1dAht5w5yTA+6r5kebiFQjP\nIzN/eCO/a9Flrj9YGW7HDNtjSOH0A31PLRGlJtJO3yK57dnf5ppyCZGfL4emShQo\ncQIDAQAB\n-----END PUBLIC KEY-----\n\n" + }, + "summary": "your friendly neighborhood pleroma developer<br>I like cute things and distributed systems, and really hate delete and redrafts", + "tag": [], + "type": "Person", + "url": "http://localhost:4001/users/{{nickname}}" +}
\ No newline at end of file diff --git a/test/fixtures/warnings/otp_version/21.1 b/test/fixtures/warnings/otp_version/21.1 new file mode 100644 index 000000000..90cd64c4f --- /dev/null +++ b/test/fixtures/warnings/otp_version/21.1 @@ -0,0 +1 @@ +21.1
\ No newline at end of file diff --git a/test/fixtures/warnings/otp_version/22.1 b/test/fixtures/warnings/otp_version/22.1 new file mode 100644 index 000000000..d9b314368 --- /dev/null +++ b/test/fixtures/warnings/otp_version/22.1 @@ -0,0 +1 @@ +22.1
\ No newline at end of file diff --git a/test/fixtures/warnings/otp_version/22.4 b/test/fixtures/warnings/otp_version/22.4 new file mode 100644 index 000000000..1da8ccd28 --- /dev/null +++ b/test/fixtures/warnings/otp_version/22.4 @@ -0,0 +1 @@ +22.4
\ No newline at end of file diff --git a/test/fixtures/warnings/otp_version/23.0 b/test/fixtures/warnings/otp_version/23.0 new file mode 100644 index 000000000..4266d8634 --- /dev/null +++ b/test/fixtures/warnings/otp_version/23.0 @@ -0,0 +1 @@ +23.0
\ No newline at end of file diff --git a/test/following_relationship_test.exs b/test/following_relationship_test.exs index 865bb3838..17a468abb 100644 --- a/test/following_relationship_test.exs +++ b/test/following_relationship_test.exs @@ -15,28 +15,28 @@ defmodule Pleroma.FollowingRelationshipTest do test "returns following addresses without internal.fetch" do user = insert(:user) fetch_actor = InternalFetchActor.get_actor() - FollowingRelationship.follow(fetch_actor, user, "accept") + FollowingRelationship.follow(fetch_actor, user, :follow_accept) assert FollowingRelationship.following(fetch_actor) == [user.follower_address] end test "returns following addresses without relay" do user = insert(:user) relay_actor = Relay.get_actor() - FollowingRelationship.follow(relay_actor, user, "accept") + FollowingRelationship.follow(relay_actor, user, :follow_accept) assert FollowingRelationship.following(relay_actor) == [user.follower_address] end test "returns following addresses without remote user" do user = insert(:user) actor = insert(:user, local: false) - FollowingRelationship.follow(actor, user, "accept") + FollowingRelationship.follow(actor, user, :follow_accept) assert FollowingRelationship.following(actor) == [user.follower_address] end test "returns following addresses with local user" do user = insert(:user) actor = insert(:user, local: true) - FollowingRelationship.follow(actor, user, "accept") + FollowingRelationship.follow(actor, user, :follow_accept) assert FollowingRelationship.following(actor) == [ actor.follower_address, diff --git a/test/formatter_test.exs b/test/formatter_test.exs index cf8441cf6..bef5a2c28 100644 --- a/test/formatter_test.exs +++ b/test/formatter_test.exs @@ -140,7 +140,7 @@ defmodule Pleroma.FormatterTest do archaeme = insert(:user, nickname: "archa_eme_", - source_data: %{"url" => "https://archeme/@archa_eme_"} + uri: "https://archeme/@archa_eme_" ) archaeme_remote = insert(:user, %{nickname: "archaeme@archae.me"}) @@ -150,13 +150,13 @@ defmodule Pleroma.FormatterTest do assert length(mentions) == 3 expected_text = - ~s(<span class="h-card"><a data-user="#{gsimg.id}" class="u-url mention" href="#{ + ~s(<span class="h-card"><a class="u-url mention" data-user="#{gsimg.id}" href="#{ gsimg.ap_id - }" rel="ugc">@<span>gsimg</span></a></span> According to <span class="h-card"><a data-user="#{ + }" rel="ugc">@<span>gsimg</span></a></span> According to <span class="h-card"><a class="u-url mention" data-user="#{ archaeme.id - }" class="u-url mention" href="#{"https://archeme/@archa_eme_"}" rel="ugc">@<span>archa_eme_</span></a></span>, that is @daggsy. Also hello <span class="h-card"><a data-user="#{ + }" href="#{"https://archeme/@archa_eme_"}" rel="ugc">@<span>archa_eme_</span></a></span>, that is @daggsy. Also hello <span class="h-card"><a class="u-url mention" data-user="#{ archaeme_remote.id - }" class="u-url mention" href="#{archaeme_remote.ap_id}" rel="ugc">@<span>archaeme</span></a></span>) + }" href="#{archaeme_remote.ap_id}" rel="ugc">@<span>archaeme</span></a></span>) assert expected_text == text end @@ -171,7 +171,7 @@ defmodule Pleroma.FormatterTest do assert length(mentions) == 1 expected_text = - ~s(<span class="h-card"><a data-user="#{mike.id}" class="u-url mention" href="#{ + ~s(<span class="h-card"><a class="u-url mention" data-user="#{mike.id}" href="#{ mike.ap_id }" rel="ugc">@<span>mike</span></a></span> test) @@ -187,7 +187,7 @@ defmodule Pleroma.FormatterTest do assert length(mentions) == 1 expected_text = - ~s(<span class="h-card"><a data-user="#{o.id}" class="u-url mention" href="#{o.ap_id}" rel="ugc">@<span>o</span></a></span> hi) + ~s(<span class="h-card"><a class="u-url mention" data-user="#{o.id}" href="#{o.ap_id}" rel="ugc">@<span>o</span></a></span> hi) assert expected_text == text end @@ -209,17 +209,13 @@ defmodule Pleroma.FormatterTest do assert mentions == [{"@#{user.nickname}", user}, {"@#{other_user.nickname}", other_user}] assert expected_text == - ~s(<span class="h-card"><a data-user="#{user.id}" class="u-url mention" href="#{ + ~s(<span class="h-card"><a class="u-url mention" data-user="#{user.id}" href="#{ user.ap_id - }" rel="ugc">@<span>#{user.nickname}</span></a></span> <span class="h-card"><a data-user="#{ + }" rel="ugc">@<span>#{user.nickname}</span></a></span> <span class="h-card"><a class="u-url mention" data-user="#{ other_user.id - }" class="u-url mention" href="#{other_user.ap_id}" rel="ugc">@<span>#{ - other_user.nickname - }</span></a></span> hey dudes i hate <span class="h-card"><a data-user="#{ + }" href="#{other_user.ap_id}" rel="ugc">@<span>#{other_user.nickname}</span></a></span> hey dudes i hate <span class="h-card"><a class="u-url mention" data-user="#{ third_user.id - }" class="u-url mention" href="#{third_user.ap_id}" rel="ugc">@<span>#{ - third_user.nickname - }</span></a></span>) + }" href="#{third_user.ap_id}" rel="ugc">@<span>#{third_user.nickname}</span></a></span>) end test "given the 'safe_mention' option, it will still work without any mention" do diff --git a/test/html_test.exs b/test/html_test.exs index a006fd492..0a4b4ebbc 100644 --- a/test/html_test.exs +++ b/test/html_test.exs @@ -171,7 +171,7 @@ defmodule Pleroma.HTMLTest do {:ok, activity} = CommonAPI.post(user, %{ - "status" => + status: "I think I just found the best github repo https://github.com/komeiji-satori/Dress" }) @@ -186,7 +186,7 @@ defmodule Pleroma.HTMLTest do {:ok, activity} = CommonAPI.post(user, %{ - "status" => + status: "@#{other_user.nickname} install misskey! https://github.com/syuilo/misskey/blob/develop/docs/setup.en.md" }) @@ -203,8 +203,7 @@ defmodule Pleroma.HTMLTest do {:ok, activity} = CommonAPI.post(user, %{ - "status" => - "#cofe https://www.pixiv.net/member_illust.php?mode=medium&illust_id=72255140" + status: "#cofe https://www.pixiv.net/member_illust.php?mode=medium&illust_id=72255140" }) object = Object.normalize(activity) @@ -218,9 +217,9 @@ defmodule Pleroma.HTMLTest do {:ok, activity} = CommonAPI.post(user, %{ - "status" => + status: "<a href=\"https://pleroma.gov/tags/cofe\" rel=\"tag\">#cofe</a> https://www.pixiv.net/member_illust.php?mode=medium&illust_id=72255140", - "content_type" => "text/html" + content_type: "text/html" }) object = Object.normalize(activity) @@ -232,8 +231,7 @@ defmodule Pleroma.HTMLTest do test "does not crash when there is an HTML entity in a link" do user = insert(:user) - {:ok, activity} = - CommonAPI.post(user, %{"status" => "\"http://cofe.com/?boomer=ok&foo=bar\""}) + {:ok, activity} = CommonAPI.post(user, %{status: "\"http://cofe.com/?boomer=ok&foo=bar\""}) object = Object.normalize(activity) diff --git a/test/http/adapter_helper/gun_test.exs b/test/http/adapter_helper/gun_test.exs new file mode 100644 index 000000000..2e961826e --- /dev/null +++ b/test/http/adapter_helper/gun_test.exs @@ -0,0 +1,258 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.HTTP.AdapterHelper.GunTest do + use ExUnit.Case, async: true + use Pleroma.Tests.Helpers + + import Mox + + alias Pleroma.Config + alias Pleroma.Gun.Conn + alias Pleroma.HTTP.AdapterHelper.Gun + alias Pleroma.Pool.Connections + + setup :verify_on_exit! + + defp gun_mock(_) do + gun_mock() + :ok + end + + defp gun_mock do + Pleroma.GunMock + |> stub(:open, fn _, _, _ -> Task.start_link(fn -> Process.sleep(1000) end) end) + |> stub(:await_up, fn _, _ -> {:ok, :http} end) + |> stub(:set_owner, fn _, _ -> :ok end) + end + + describe "options/1" do + setup do: clear_config([:http, :adapter], a: 1, b: 2) + + test "https url with default port" do + uri = URI.parse("https://example.com") + + opts = Gun.options([receive_conn: false], uri) + assert opts[:certificates_verification] + assert opts[:tls_opts][:log_level] == :warning + end + + test "https ipv4 with default port" do + uri = URI.parse("https://127.0.0.1") + + opts = Gun.options([receive_conn: false], uri) + assert opts[:certificates_verification] + assert opts[:tls_opts][:log_level] == :warning + end + + test "https ipv6 with default port" do + uri = URI.parse("https://[2a03:2880:f10c:83:face:b00c:0:25de]") + + opts = Gun.options([receive_conn: false], uri) + assert opts[:certificates_verification] + assert opts[:tls_opts][:log_level] == :warning + end + + test "https url with non standart port" do + uri = URI.parse("https://example.com:115") + + opts = Gun.options([receive_conn: false], uri) + + assert opts[:certificates_verification] + end + + test "get conn on next request" do + gun_mock() + level = Application.get_env(:logger, :level) + Logger.configure(level: :debug) + on_exit(fn -> Logger.configure(level: level) end) + uri = URI.parse("http://some-domain2.com") + + opts = Gun.options(uri) + + assert opts[:conn] == nil + assert opts[:close_conn] == nil + + Process.sleep(50) + opts = Gun.options(uri) + + assert is_pid(opts[:conn]) + assert opts[:close_conn] == false + end + + test "merges with defaul http adapter config" do + defaults = Gun.options([receive_conn: false], URI.parse("https://example.com")) + assert Keyword.has_key?(defaults, :a) + assert Keyword.has_key?(defaults, :b) + end + + test "default ssl adapter opts with connection" do + gun_mock() + uri = URI.parse("https://some-domain.com") + + :ok = Conn.open(uri, :gun_connections) + + opts = Gun.options(uri) + + assert opts[:certificates_verification] + refute opts[:tls_opts] == [] + + assert opts[:close_conn] == false + assert is_pid(opts[:conn]) + end + + test "parses string proxy host & port" do + proxy = Config.get([:http, :proxy_url]) + Config.put([:http, :proxy_url], "localhost:8123") + on_exit(fn -> Config.put([:http, :proxy_url], proxy) end) + + uri = URI.parse("https://some-domain.com") + opts = Gun.options([receive_conn: false], uri) + assert opts[:proxy] == {'localhost', 8123} + end + + test "parses tuple proxy scheme host and port" do + proxy = Config.get([:http, :proxy_url]) + Config.put([:http, :proxy_url], {:socks, 'localhost', 1234}) + on_exit(fn -> Config.put([:http, :proxy_url], proxy) end) + + uri = URI.parse("https://some-domain.com") + opts = Gun.options([receive_conn: false], uri) + assert opts[:proxy] == {:socks, 'localhost', 1234} + end + + test "passed opts have more weight than defaults" do + proxy = Config.get([:http, :proxy_url]) + Config.put([:http, :proxy_url], {:socks5, 'localhost', 1234}) + on_exit(fn -> Config.put([:http, :proxy_url], proxy) end) + uri = URI.parse("https://some-domain.com") + opts = Gun.options([receive_conn: false, proxy: {'example.com', 4321}], uri) + + assert opts[:proxy] == {'example.com', 4321} + end + end + + describe "options/1 with receive_conn parameter" do + setup :gun_mock + + test "receive conn by default" do + uri = URI.parse("http://another-domain.com") + :ok = Conn.open(uri, :gun_connections) + + received_opts = Gun.options(uri) + assert received_opts[:close_conn] == false + assert is_pid(received_opts[:conn]) + end + + test "don't receive conn if receive_conn is false" do + uri = URI.parse("http://another-domain.com") + :ok = Conn.open(uri, :gun_connections) + + opts = [receive_conn: false] + received_opts = Gun.options(opts, uri) + assert received_opts[:close_conn] == nil + assert received_opts[:conn] == nil + end + end + + describe "after_request/1" do + setup :gun_mock + + test "body_as not chunks" do + uri = URI.parse("http://some-domain.com") + :ok = Conn.open(uri, :gun_connections) + opts = Gun.options(uri) + :ok = Gun.after_request(opts) + conn = opts[:conn] + + assert %Connections{ + conns: %{ + "http:some-domain.com:80" => %Pleroma.Gun.Conn{ + conn: ^conn, + conn_state: :idle, + used_by: [] + } + } + } = Connections.get_state(:gun_connections) + end + + test "body_as chunks" do + uri = URI.parse("http://some-domain.com") + :ok = Conn.open(uri, :gun_connections) + opts = Gun.options([body_as: :chunks], uri) + :ok = Gun.after_request(opts) + conn = opts[:conn] + self = self() + + assert %Connections{ + conns: %{ + "http:some-domain.com:80" => %Pleroma.Gun.Conn{ + conn: ^conn, + conn_state: :active, + used_by: [{^self, _}] + } + } + } = Connections.get_state(:gun_connections) + end + + test "with no connection" do + uri = URI.parse("http://uniq-domain.com") + + :ok = Conn.open(uri, :gun_connections) + + opts = Gun.options([body_as: :chunks], uri) + conn = opts[:conn] + opts = Keyword.delete(opts, :conn) + self = self() + + :ok = Gun.after_request(opts) + + assert %Connections{ + conns: %{ + "http:uniq-domain.com:80" => %Pleroma.Gun.Conn{ + conn: ^conn, + conn_state: :active, + used_by: [{^self, _}] + } + } + } = Connections.get_state(:gun_connections) + end + + test "with ipv4" do + uri = URI.parse("http://127.0.0.1") + :ok = Conn.open(uri, :gun_connections) + opts = Gun.options(uri) + :ok = Gun.after_request(opts) + conn = opts[:conn] + + assert %Connections{ + conns: %{ + "http:127.0.0.1:80" => %Pleroma.Gun.Conn{ + conn: ^conn, + conn_state: :idle, + used_by: [] + } + } + } = Connections.get_state(:gun_connections) + end + + test "with ipv6" do + uri = URI.parse("http://[2a03:2880:f10c:83:face:b00c:0:25de]") + :ok = Conn.open(uri, :gun_connections) + opts = Gun.options(uri) + :ok = Gun.after_request(opts) + conn = opts[:conn] + + assert %Connections{ + conns: %{ + "http:2a03:2880:f10c:83:face:b00c:0:25de:80" => %Pleroma.Gun.Conn{ + conn: ^conn, + conn_state: :idle, + used_by: [] + } + } + } = Connections.get_state(:gun_connections) + end + end +end diff --git a/test/http/adapter_helper/hackney_test.exs b/test/http/adapter_helper/hackney_test.exs new file mode 100644 index 000000000..3f7e708e0 --- /dev/null +++ b/test/http/adapter_helper/hackney_test.exs @@ -0,0 +1,47 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.HTTP.AdapterHelper.HackneyTest do + use ExUnit.Case, async: true + use Pleroma.Tests.Helpers + + alias Pleroma.HTTP.AdapterHelper.Hackney + + setup_all do + uri = URI.parse("http://domain.com") + {:ok, uri: uri} + end + + describe "options/2" do + setup do: clear_config([:http, :adapter], a: 1, b: 2) + + test "add proxy and opts from config", %{uri: uri} do + opts = Hackney.options([proxy: "localhost:8123"], uri) + + assert opts[:a] == 1 + assert opts[:b] == 2 + assert opts[:proxy] == "localhost:8123" + end + + test "respect connection opts and no proxy", %{uri: uri} do + opts = Hackney.options([a: 2, b: 1], uri) + + assert opts[:a] == 2 + assert opts[:b] == 1 + refute Keyword.has_key?(opts, :proxy) + end + + test "add opts for https" do + uri = URI.parse("https://domain.com") + + opts = Hackney.options(uri) + + assert opts[:ssl_options] == [ + partial_chain: &:hackney_connect.partial_chain/1, + versions: [:tlsv1, :"tlsv1.1", :"tlsv1.2"], + server_name_indication: 'domain.com' + ] + end + end +end diff --git a/test/http/adapter_helper_test.exs b/test/http/adapter_helper_test.exs new file mode 100644 index 000000000..24d501ad5 --- /dev/null +++ b/test/http/adapter_helper_test.exs @@ -0,0 +1,28 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.HTTP.AdapterHelperTest do + use ExUnit.Case, async: true + + alias Pleroma.HTTP.AdapterHelper + + describe "format_proxy/1" do + test "with nil" do + assert AdapterHelper.format_proxy(nil) == nil + end + + test "with string" do + assert AdapterHelper.format_proxy("127.0.0.1:8123") == {{127, 0, 0, 1}, 8123} + end + + test "localhost with port" do + assert AdapterHelper.format_proxy("localhost:8123") == {'localhost', 8123} + end + + test "tuple" do + assert AdapterHelper.format_proxy({:socks4, :localhost, 9050}) == + {:socks4, 'localhost', 9050} + end + end +end diff --git a/test/http/connection_test.exs b/test/http/connection_test.exs new file mode 100644 index 000000000..7c94a50b2 --- /dev/null +++ b/test/http/connection_test.exs @@ -0,0 +1,135 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.HTTP.ConnectionTest do + use ExUnit.Case + use Pleroma.Tests.Helpers + + import ExUnit.CaptureLog + + alias Pleroma.Config + alias Pleroma.HTTP.Connection + + describe "parse_host/1" do + test "as atom to charlist" do + assert Connection.parse_host(:localhost) == 'localhost' + end + + test "as string to charlist" do + assert Connection.parse_host("localhost.com") == 'localhost.com' + end + + test "as string ip to tuple" do + assert Connection.parse_host("127.0.0.1") == {127, 0, 0, 1} + end + end + + describe "parse_proxy/1" do + test "ip with port" do + assert Connection.parse_proxy("127.0.0.1:8123") == {:ok, {127, 0, 0, 1}, 8123} + end + + test "host with port" do + assert Connection.parse_proxy("localhost:8123") == {:ok, 'localhost', 8123} + end + + test "as tuple" do + assert Connection.parse_proxy({:socks4, :localhost, 9050}) == + {:ok, :socks4, 'localhost', 9050} + end + + test "as tuple with string host" do + assert Connection.parse_proxy({:socks5, "localhost", 9050}) == + {:ok, :socks5, 'localhost', 9050} + end + end + + describe "parse_proxy/1 errors" do + test "ip without port" do + capture_log(fn -> + assert Connection.parse_proxy("127.0.0.1") == {:error, :invalid_proxy} + end) =~ "parsing proxy fail \"127.0.0.1\"" + end + + test "host without port" do + capture_log(fn -> + assert Connection.parse_proxy("localhost") == {:error, :invalid_proxy} + end) =~ "parsing proxy fail \"localhost\"" + end + + test "host with bad port" do + capture_log(fn -> + assert Connection.parse_proxy("localhost:port") == {:error, :invalid_proxy_port} + end) =~ "parsing port in proxy fail \"localhost:port\"" + end + + test "ip with bad port" do + capture_log(fn -> + assert Connection.parse_proxy("127.0.0.1:15.9") == {:error, :invalid_proxy_port} + end) =~ "parsing port in proxy fail \"127.0.0.1:15.9\"" + end + + test "as tuple without port" do + capture_log(fn -> + assert Connection.parse_proxy({:socks5, :localhost}) == {:error, :invalid_proxy} + end) =~ "parsing proxy fail {:socks5, :localhost}" + end + + test "with nil" do + assert Connection.parse_proxy(nil) == nil + end + end + + describe "options/3" do + setup do: clear_config([:http, :proxy_url]) + + test "without proxy_url in config" do + Config.delete([:http, :proxy_url]) + + opts = Connection.options(%URI{}) + refute Keyword.has_key?(opts, :proxy) + end + + test "parses string proxy host & port" do + Config.put([:http, :proxy_url], "localhost:8123") + + opts = Connection.options(%URI{}) + assert opts[:proxy] == {'localhost', 8123} + end + + test "parses tuple proxy scheme host and port" do + Config.put([:http, :proxy_url], {:socks, 'localhost', 1234}) + + opts = Connection.options(%URI{}) + assert opts[:proxy] == {:socks, 'localhost', 1234} + end + + test "passed opts have more weight than defaults" do + Config.put([:http, :proxy_url], {:socks5, 'localhost', 1234}) + + opts = Connection.options(%URI{}, proxy: {'example.com', 4321}) + + assert opts[:proxy] == {'example.com', 4321} + end + end + + describe "format_host/1" do + test "with domain" do + assert Connection.format_host("example.com") == 'example.com' + end + + test "with idna domain" do + assert Connection.format_host("ですexample.com") == 'xn--example-183fne.com' + end + + test "with ipv4" do + assert Connection.format_host("127.0.0.1") == '127.0.0.1' + end + + test "with ipv6" do + assert Connection.format_host("2a03:2880:f10c:83:face:b00c:0:25de") == + '2a03:2880:f10c:83:face:b00c:0:25de' + end + end +end diff --git a/test/http/request_builder_test.exs b/test/http/request_builder_test.exs index bf3a15ebe..fab909905 100644 --- a/test/http/request_builder_test.exs +++ b/test/http/request_builder_test.exs @@ -3,57 +3,38 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.HTTP.RequestBuilderTest do - use ExUnit.Case, async: true + use ExUnit.Case use Pleroma.Tests.Helpers + alias Pleroma.HTTP.Request alias Pleroma.HTTP.RequestBuilder describe "headers/2" do - setup do: clear_config([:http, :send_user_agent]) - setup do: clear_config([:http, :user_agent]) - test "don't send pleroma user agent" do - assert RequestBuilder.headers(%{}, []) == %{headers: []} + assert RequestBuilder.headers(%Request{}, []) == %Request{headers: []} end test "send pleroma user agent" do - Pleroma.Config.put([:http, :send_user_agent], true) - Pleroma.Config.put([:http, :user_agent], :default) + clear_config([:http, :send_user_agent], true) + clear_config([:http, :user_agent], :default) - assert RequestBuilder.headers(%{}, []) == %{ - headers: [{"User-Agent", Pleroma.Application.user_agent()}] + assert RequestBuilder.headers(%Request{}, []) == %Request{ + headers: [{"user-agent", Pleroma.Application.user_agent()}] } end test "send custom user agent" do - Pleroma.Config.put([:http, :send_user_agent], true) - Pleroma.Config.put([:http, :user_agent], "totally-not-pleroma") + clear_config([:http, :send_user_agent], true) + clear_config([:http, :user_agent], "totally-not-pleroma") - assert RequestBuilder.headers(%{}, []) == %{ - headers: [{"User-Agent", "totally-not-pleroma"}] + assert RequestBuilder.headers(%Request{}, []) == %Request{ + headers: [{"user-agent", "totally-not-pleroma"}] } end end - describe "add_optional_params/3" do - test "don't add if keyword is empty" do - assert RequestBuilder.add_optional_params(%{}, %{}, []) == %{} - end - - test "add query parameter" do - assert RequestBuilder.add_optional_params( - %{}, - %{query: :query, body: :body, another: :val}, - [ - {:query, "param1=val1¶m2=val2"}, - {:body, "some body"} - ] - ) == %{query: "param1=val1¶m2=val2", body: "some body"} - end - end - describe "add_param/4" do test "add file parameter" do - %{ + %Request{ body: %Tesla.Multipart{ boundary: _, content_type_params: [], @@ -70,7 +51,7 @@ defmodule Pleroma.HTTP.RequestBuilderTest do } ] } - } = RequestBuilder.add_param(%{}, :file, "filename.png", "some-path/filename.png") + } = RequestBuilder.add_param(%Request{}, :file, "filename.png", "some-path/filename.png") end test "add key to body" do @@ -82,7 +63,7 @@ defmodule Pleroma.HTTP.RequestBuilderTest do %Tesla.Multipart.Part{ body: "\"someval\"", dispositions: [name: "somekey"], - headers: ["Content-Type": "application/json"] + headers: [{"content-type", "application/json"}] } ] } diff --git a/test/http_test.exs b/test/http_test.exs index 3edb0de36..618485b55 100644 --- a/test/http_test.exs +++ b/test/http_test.exs @@ -3,8 +3,10 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.HTTPTest do - use Pleroma.DataCase + use ExUnit.Case, async: true + use Pleroma.Tests.Helpers import Tesla.Mock + alias Pleroma.HTTP setup do mock(fn @@ -27,7 +29,7 @@ defmodule Pleroma.HTTPTest do describe "get/1" do test "returns successfully result" do - assert Pleroma.HTTP.get("http://example.com/hello") == { + assert HTTP.get("http://example.com/hello") == { :ok, %Tesla.Env{status: 200, body: "hello"} } @@ -36,7 +38,7 @@ defmodule Pleroma.HTTPTest do describe "get/2 (with headers)" do test "returns successfully result for json content-type" do - assert Pleroma.HTTP.get("http://example.com/hello", [{"content-type", "application/json"}]) == + assert HTTP.get("http://example.com/hello", [{"content-type", "application/json"}]) == { :ok, %Tesla.Env{ @@ -50,7 +52,7 @@ defmodule Pleroma.HTTPTest do describe "post/2" do test "returns successfully result" do - assert Pleroma.HTTP.post("http://example.com/world", "") == { + assert HTTP.post("http://example.com/world", "") == { :ok, %Tesla.Env{status: 200, body: "world"} } diff --git a/test/instance_static/add/shortcode.png b/test/instance_static/add/shortcode.png Binary files differnew file mode 100644 index 000000000..8f50fa023 --- /dev/null +++ b/test/instance_static/add/shortcode.png diff --git a/test/instance_static/emoji/pack_bad_sha/blank.png b/test/instance_static/emoji/pack_bad_sha/blank.png Binary files differnew file mode 100644 index 000000000..8f50fa023 --- /dev/null +++ b/test/instance_static/emoji/pack_bad_sha/blank.png diff --git a/test/instance_static/emoji/pack_bad_sha/pack.json b/test/instance_static/emoji/pack_bad_sha/pack.json new file mode 100644 index 000000000..35caf4298 --- /dev/null +++ b/test/instance_static/emoji/pack_bad_sha/pack.json @@ -0,0 +1,13 @@ +{ + "pack": { + "license": "Test license", + "homepage": "https://pleroma.social", + "description": "Test description", + "can-download": true, + "share-files": true, + "download-sha256": "57482F30674FD3DE821FF48C81C00DA4D4AF1F300209253684ABA7075E5FC238" + }, + "files": { + "blank": "blank.png" + } +}
\ No newline at end of file diff --git a/test/instance_static/emoji/pack_bad_sha/pack_bad_sha.zip b/test/instance_static/emoji/pack_bad_sha/pack_bad_sha.zip Binary files differnew file mode 100644 index 000000000..148446c64 --- /dev/null +++ b/test/instance_static/emoji/pack_bad_sha/pack_bad_sha.zip diff --git a/test/instance_static/emoji/test_pack/pack.json b/test/instance_static/emoji/test_pack/pack.json index 5a8ee75f9..481891b08 100644 --- a/test/instance_static/emoji/test_pack/pack.json +++ b/test/instance_static/emoji/test_pack/pack.json @@ -1,13 +1,11 @@ { + "files": { + "blank": "blank.png" + }, "pack": { - "license": "Test license", - "homepage": "https://pleroma.social", "description": "Test description", - + "homepage": "https://pleroma.social", + "license": "Test license", "share-files": true - }, - - "files": { - "blank": "blank.png" } -} +}
\ No newline at end of file diff --git a/test/instance_static/emoji/test_pack_nonshared/pack.json b/test/instance_static/emoji/test_pack_nonshared/pack.json index b96781f81..93d643a5f 100644 --- a/test/instance_static/emoji/test_pack_nonshared/pack.json +++ b/test/instance_static/emoji/test_pack_nonshared/pack.json @@ -3,14 +3,11 @@ "license": "Test license", "homepage": "https://pleroma.social", "description": "Test description", - "fallback-src": "https://nonshared-pack", "fallback-src-sha256": "74409E2674DAA06C072729C6C8426C4CB3B7E0B85ED77792DB7A436E11D76DAF", - "share-files": false }, - "files": { "blank": "blank.png" } -} +}
\ No newline at end of file diff --git a/test/integration/mastodon_websocket_test.exs b/test/integration/mastodon_websocket_test.exs index bd229c55f..ea17e9feb 100644 --- a/test/integration/mastodon_websocket_test.exs +++ b/test/integration/mastodon_websocket_test.exs @@ -12,17 +12,14 @@ defmodule Pleroma.Integration.MastodonWebsocketTest do alias Pleroma.Web.CommonAPI alias Pleroma.Web.OAuth + @moduletag needs_streamer: true, capture_log: true + @path Pleroma.Web.Endpoint.url() |> URI.parse() |> Map.put(:scheme, "ws") |> Map.put(:path, "/api/v1/streaming") |> URI.to_string() - setup_all do - start_supervised(Pleroma.Web.Streamer.supervisor()) - :ok - end - def start_socket(qs \\ nil, headers \\ []) do path = case qs do @@ -35,7 +32,7 @@ defmodule Pleroma.Integration.MastodonWebsocketTest do test "refuses invalid requests" do capture_log(fn -> - assert {:error, {400, _}} = start_socket() + assert {:error, {404, _}} = start_socket() assert {:error, {404, _}} = start_socket("?stream=ncjdk") Process.sleep(30) end) @@ -43,8 +40,8 @@ defmodule Pleroma.Integration.MastodonWebsocketTest do test "requires authentication and a valid token for protected streams" do capture_log(fn -> - assert {:error, {403, _}} = start_socket("?stream=user&access_token=aaaaaaaaaaaa") - assert {:error, {403, _}} = start_socket("?stream=user") + assert {:error, {401, _}} = start_socket("?stream=user&access_token=aaaaaaaaaaaa") + assert {:error, {401, _}} = start_socket("?stream=user") Process.sleep(30) end) end @@ -58,7 +55,7 @@ defmodule Pleroma.Integration.MastodonWebsocketTest do test "receives well formatted events" do user = insert(:user) {:ok, _} = start_socket("?stream=public") - {:ok, activity} = CommonAPI.post(user, %{"status" => "nice echo chamber"}) + {:ok, activity} = CommonAPI.post(user, %{status: "nice echo chamber"}) assert_receive {:text, raw_json}, 1_000 assert {:ok, json} = Jason.decode(raw_json) @@ -103,7 +100,7 @@ defmodule Pleroma.Integration.MastodonWebsocketTest do assert {:ok, _} = start_socket("?stream=user&access_token=#{token.token}") assert capture_log(fn -> - assert {:error, {403, "Forbidden"}} = start_socket("?stream=user") + assert {:error, {401, _}} = start_socket("?stream=user") Process.sleep(30) end) =~ ":badarg" end @@ -112,7 +109,7 @@ defmodule Pleroma.Integration.MastodonWebsocketTest do assert {:ok, _} = start_socket("?stream=user:notification&access_token=#{token.token}") assert capture_log(fn -> - assert {:error, {403, "Forbidden"}} = start_socket("?stream=user:notification") + assert {:error, {401, _}} = start_socket("?stream=user:notification") Process.sleep(30) end) =~ ":badarg" end @@ -121,7 +118,7 @@ defmodule Pleroma.Integration.MastodonWebsocketTest do assert {:ok, _} = start_socket("?stream=user", [{"Sec-WebSocket-Protocol", token.token}]) assert capture_log(fn -> - assert {:error, {403, "Forbidden"}} = + assert {:error, {401, _}} = start_socket("?stream=user", [{"Sec-WebSocket-Protocol", "I am a friend"}]) Process.sleep(30) diff --git a/test/marker_test.exs b/test/marker_test.exs index c80ae16b6..5b6d0b4a4 100644 --- a/test/marker_test.exs +++ b/test/marker_test.exs @@ -8,12 +8,39 @@ defmodule Pleroma.MarkerTest do import Pleroma.Factory + describe "multi_set_unread_count/3" do + test "returns multi" do + user = insert(:user) + + assert %Ecto.Multi{ + operations: [marker: {:run, _}, counters: {:run, _}] + } = + Marker.multi_set_last_read_id( + Ecto.Multi.new(), + user, + "notifications" + ) + end + + test "return empty multi" do + user = insert(:user) + multi = Ecto.Multi.new() + assert Marker.multi_set_last_read_id(multi, user, "home") == multi + end + end + describe "get_markers/2" do test "returns user markers" do user = insert(:user) marker = insert(:marker, user: user) + insert(:notification, user: user) + insert(:notification, user: user) insert(:marker, timeline: "home", user: user) - assert Marker.get_markers(user, ["notifications"]) == [refresh_record(marker)] + + assert Marker.get_markers( + user, + ["notifications"] + ) == [%Marker{refresh_record(marker) | unread_count: 2}] end end diff --git a/test/mfa/backup_codes_test.exs b/test/mfa/backup_codes_test.exs new file mode 100644 index 000000000..7bc01b36b --- /dev/null +++ b/test/mfa/backup_codes_test.exs @@ -0,0 +1,11 @@ +defmodule Pleroma.MFA.BackupCodesTest do + use Pleroma.DataCase + + alias Pleroma.MFA.BackupCodes + + test "generate backup codes" do + codes = BackupCodes.generate(number_of_codes: 2, length: 4) + + assert [<<_::bytes-size(4)>>, <<_::bytes-size(4)>>] = codes + end +end diff --git a/test/mfa/totp_test.exs b/test/mfa/totp_test.exs new file mode 100644 index 000000000..50153d208 --- /dev/null +++ b/test/mfa/totp_test.exs @@ -0,0 +1,17 @@ +defmodule Pleroma.MFA.TOTPTest do + use Pleroma.DataCase + + alias Pleroma.MFA.TOTP + + test "create provisioning_uri to generate qrcode" do + uri = + TOTP.provisioning_uri("test-secrcet", "test@example.com", + issuer: "Plerome-42", + digits: 8, + period: 60 + ) + + assert uri == + "otpauth://totp/test@example.com?digits=8&issuer=Plerome-42&period=60&secret=test-secrcet" + end +end diff --git a/test/mfa_test.exs b/test/mfa_test.exs new file mode 100644 index 000000000..8875cefd9 --- /dev/null +++ b/test/mfa_test.exs @@ -0,0 +1,52 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.MFATest do + use Pleroma.DataCase + + import Pleroma.Factory + alias Pleroma.MFA + + describe "mfa_settings" do + test "returns settings user's" do + user = + insert(:user, + multi_factor_authentication_settings: %MFA.Settings{ + enabled: true, + totp: %MFA.Settings.TOTP{secret: "xx", confirmed: true} + } + ) + + settings = MFA.mfa_settings(user) + assert match?(^settings, %{enabled: true, totp: true}) + end + end + + describe "generate backup codes" do + test "returns backup codes" do + user = insert(:user) + + {:ok, [code1, code2]} = MFA.generate_backup_codes(user) + updated_user = refresh_record(user) + [hash1, hash2] = updated_user.multi_factor_authentication_settings.backup_codes + assert Pbkdf2.verify_pass(code1, hash1) + assert Pbkdf2.verify_pass(code2, hash2) + end + end + + describe "invalidate_backup_code" do + test "invalid used code" do + user = insert(:user) + + {:ok, _} = MFA.generate_backup_codes(user) + user = refresh_record(user) + assert length(user.multi_factor_authentication_settings.backup_codes) == 2 + [hash_code | _] = user.multi_factor_authentication_settings.backup_codes + + {:ok, user} = MFA.invalidate_backup_code(user, hash_code) + + assert length(user.multi_factor_authentication_settings.backup_codes) == 1 + end + end +end diff --git a/test/notification_test.exs b/test/notification_test.exs index d240ede94..111ff09f4 100644 --- a/test/notification_test.exs +++ b/test/notification_test.exs @@ -6,12 +6,18 @@ defmodule Pleroma.NotificationTest do use Pleroma.DataCase import Pleroma.Factory + import Mock + alias Pleroma.FollowingRelationship alias Pleroma.Notification alias Pleroma.Tests.ObanHelpers alias Pleroma.User + alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.ActivityPub.Builder alias Pleroma.Web.ActivityPub.Transmogrifier alias Pleroma.Web.CommonAPI + alias Pleroma.Web.MastodonAPI.NotificationView + alias Pleroma.Web.Push alias Pleroma.Web.Streamer describe "create_notifications" do @@ -19,8 +25,8 @@ defmodule Pleroma.NotificationTest do user = insert(:user) other_user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "yeah"}) - {:ok, activity, _object} = CommonAPI.react_with_emoji(activity.id, other_user, "☕") + {:ok, activity} = CommonAPI.post(user, %{status: "yeah"}) + {:ok, activity} = CommonAPI.react_with_emoji(activity.id, other_user, "☕") {:ok, [notification]} = Notification.create_notifications(activity) @@ -34,7 +40,7 @@ defmodule Pleroma.NotificationTest do {:ok, activity} = CommonAPI.post(user, %{ - "status" => "hey @#{other_user.nickname} and @#{third_user.nickname}" + status: "hey @#{other_user.nickname} and @#{third_user.nickname}" }) {:ok, [notification, other_notification]} = Notification.create_notifications(activity) @@ -43,6 +49,9 @@ defmodule Pleroma.NotificationTest do assert notified_ids == [other_user.id, third_user.id] assert notification.activity_id == activity.id assert other_notification.activity_id == activity.id + + assert [%Pleroma.Marker{unread_count: 2}] = + Pleroma.Marker.get_markers(other_user, ["notifications"]) end test "it creates a notification for subscribed users" do @@ -51,7 +60,7 @@ defmodule Pleroma.NotificationTest do User.subscribe(subscriber, user) - {:ok, status} = CommonAPI.post(user, %{"status" => "Akariiiin"}) + {:ok, status} = CommonAPI.post(user, %{status: "Akariiiin"}) {:ok, [notification]} = Notification.create_notifications(status) assert notification.user_id == subscriber.id @@ -64,12 +73,12 @@ defmodule Pleroma.NotificationTest do User.subscribe(subscriber, other_user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "test post"}) + {:ok, activity} = CommonAPI.post(user, %{status: "test post"}) {:ok, _reply_activity} = CommonAPI.post(other_user, %{ - "status" => "test reply", - "in_reply_to_status_id" => activity.id + status: "test reply", + in_reply_to_status_id: activity.id }) user_notifications = Notification.for_user(user) @@ -80,18 +89,96 @@ defmodule Pleroma.NotificationTest do end end + describe "CommonApi.post/2 notification-related functionality" do + test_with_mock "creates but does NOT send notification to blocker user", + Push, + [:passthrough], + [] do + user = insert(:user) + blocker = insert(:user) + {:ok, _user_relationship} = User.block(blocker, user) + + {:ok, _activity} = CommonAPI.post(user, %{status: "hey @#{blocker.nickname}!"}) + + blocker_id = blocker.id + assert [%Notification{user_id: ^blocker_id}] = Repo.all(Notification) + refute called(Push.send(:_)) + end + + test_with_mock "creates but does NOT send notification to notification-muter user", + Push, + [:passthrough], + [] do + user = insert(:user) + muter = insert(:user) + {:ok, _user_relationships} = User.mute(muter, user) + + {:ok, _activity} = CommonAPI.post(user, %{status: "hey @#{muter.nickname}!"}) + + muter_id = muter.id + assert [%Notification{user_id: ^muter_id}] = Repo.all(Notification) + refute called(Push.send(:_)) + end + + test_with_mock "creates but does NOT send notification to thread-muter user", + Push, + [:passthrough], + [] do + user = insert(:user) + thread_muter = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{thread_muter.nickname}!"}) + + {:ok, _} = CommonAPI.add_mute(thread_muter, activity) + + {:ok, _same_context_activity} = + CommonAPI.post(user, %{ + status: "hey-hey-hey @#{thread_muter.nickname}!", + in_reply_to_status_id: activity.id + }) + + [pre_mute_notification, post_mute_notification] = + Repo.all(from(n in Notification, where: n.user_id == ^thread_muter.id, order_by: n.id)) + + pre_mute_notification_id = pre_mute_notification.id + post_mute_notification_id = post_mute_notification.id + + assert called( + Push.send( + :meck.is(fn + %Notification{id: ^pre_mute_notification_id} -> true + _ -> false + end) + ) + ) + + refute called( + Push.send( + :meck.is(fn + %Notification{id: ^post_mute_notification_id} -> true + _ -> false + end) + ) + ) + end + end + describe "create_notification" do @tag needs_streamer: true test "it creates a notification for user and send to the 'user' and the 'user:notification' stream" do user = insert(:user) - task = Task.async(fn -> assert_receive {:text, _}, 4_000 end) - task_user_notification = Task.async(fn -> assert_receive {:text, _}, 4_000 end) - Streamer.add_socket("user", %{transport_pid: task.pid, assigns: %{user: user}}) - Streamer.add_socket( - "user:notification", - %{transport_pid: task_user_notification.pid, assigns: %{user: user}} - ) + task = + Task.async(fn -> + Streamer.get_topic_and_add_socket("user", user) + assert_receive {:render_with_user, _, _, _}, 4_000 + end) + + task_user_notification = + Task.async(fn -> + Streamer.get_topic_and_add_socket("user:notification", user) + assert_receive {:render_with_user, _, _, _}, 4_000 + end) activity = insert(:note_activity) @@ -115,7 +202,7 @@ defmodule Pleroma.NotificationTest do muted = insert(:user) {:ok, _} = User.mute(muter, muted) muter = Repo.get(User, muter.id) - {:ok, activity} = CommonAPI.post(muted, %{"status" => "Hi @#{muter.nickname}"}) + {:ok, activity} = CommonAPI.post(muted, %{status: "Hi @#{muter.nickname}"}) assert Notification.create_notification(activity, muter) end @@ -126,7 +213,7 @@ defmodule Pleroma.NotificationTest do {:ok, _user_relationships} = User.mute(muter, muted, false) - {:ok, activity} = CommonAPI.post(muted, %{"status" => "Hi @#{muter.nickname}"}) + {:ok, activity} = CommonAPI.post(muted, %{status: "Hi @#{muter.nickname}"}) assert Notification.create_notification(activity, muter) end @@ -134,13 +221,13 @@ defmodule Pleroma.NotificationTest do test "it creates a notification for an activity from a muted thread" do muter = insert(:user) other_user = insert(:user) - {:ok, activity} = CommonAPI.post(muter, %{"status" => "hey"}) + {:ok, activity} = CommonAPI.post(muter, %{status: "hey"}) CommonAPI.add_mute(muter, activity) {:ok, activity} = CommonAPI.post(other_user, %{ - "status" => "Hi @#{muter.nickname}", - "in_reply_to_status_id" => activity.id + status: "Hi @#{muter.nickname}", + in_reply_to_status_id: activity.id }) assert Notification.create_notification(activity, muter) @@ -153,7 +240,7 @@ defmodule Pleroma.NotificationTest do insert(:user, notification_settings: %Pleroma.User.NotificationSetting{followers: false}) User.follow(follower, followed) - {:ok, activity} = CommonAPI.post(follower, %{"status" => "hey @#{followed.nickname}"}) + {:ok, activity} = CommonAPI.post(follower, %{status: "hey @#{followed.nickname}"}) refute Notification.create_notification(activity, followed) end @@ -165,7 +252,7 @@ defmodule Pleroma.NotificationTest do notification_settings: %Pleroma.User.NotificationSetting{non_followers: false} ) - {:ok, activity} = CommonAPI.post(follower, %{"status" => "hey @#{followed.nickname}"}) + {:ok, activity} = CommonAPI.post(follower, %{status: "hey @#{followed.nickname}"}) refute Notification.create_notification(activity, followed) end @@ -176,7 +263,7 @@ defmodule Pleroma.NotificationTest do followed = insert(:user) User.follow(follower, followed) follower = Repo.get(User, follower.id) - {:ok, activity} = CommonAPI.post(followed, %{"status" => "hey @#{follower.nickname}"}) + {:ok, activity} = CommonAPI.post(followed, %{status: "hey @#{follower.nickname}"}) refute Notification.create_notification(activity, follower) end @@ -185,7 +272,7 @@ defmodule Pleroma.NotificationTest do insert(:user, notification_settings: %Pleroma.User.NotificationSetting{non_follows: false}) followed = insert(:user) - {:ok, activity} = CommonAPI.post(followed, %{"status" => "hey @#{follower.nickname}"}) + {:ok, activity} = CommonAPI.post(followed, %{status: "hey @#{follower.nickname}"}) refute Notification.create_notification(activity, follower) end @@ -196,23 +283,13 @@ defmodule Pleroma.NotificationTest do refute Notification.create_notification(activity, author) end - test "it doesn't create a notification for follow-unfollow-follow chains" do - user = insert(:user) - followed_user = insert(:user) - {:ok, _, _, activity} = CommonAPI.follow(user, followed_user) - Notification.create_notification(activity, followed_user) - CommonAPI.unfollow(user, followed_user) - {:ok, _, _, activity_dupe} = CommonAPI.follow(user, followed_user) - refute Notification.create_notification(activity_dupe, followed_user) - end - test "it doesn't create duplicate notifications for follow+subscribed users" do user = insert(:user) subscriber = insert(:user) {:ok, _, _, _} = CommonAPI.follow(subscriber, user) User.subscribe(subscriber, user) - {:ok, status} = CommonAPI.post(user, %{"status" => "Akariiiin"}) + {:ok, status} = CommonAPI.post(user, %{status: "Akariiiin"}) {:ok, [_notif]} = Notification.create_notifications(status) end @@ -222,18 +299,78 @@ defmodule Pleroma.NotificationTest do User.subscribe(subscriber, user) - {:ok, status} = CommonAPI.post(user, %{"status" => "inwisible", "visibility" => "direct"}) + {:ok, status} = CommonAPI.post(user, %{status: "inwisible", visibility: "direct"}) assert {:ok, []} == Notification.create_notifications(status) end end + describe "follow / follow_request notifications" do + test "it creates `follow` notification for approved Follow activity" do + user = insert(:user) + followed_user = insert(:user, locked: false) + + {:ok, _, _, _activity} = CommonAPI.follow(user, followed_user) + assert FollowingRelationship.following?(user, followed_user) + assert [notification] = Notification.for_user(followed_user) + + assert %{type: "follow"} = + NotificationView.render("show.json", %{ + notification: notification, + for: followed_user + }) + end + + test "it creates `follow_request` notification for pending Follow activity" do + user = insert(:user) + followed_user = insert(:user, locked: true) + + {:ok, _, _, _activity} = CommonAPI.follow(user, followed_user) + refute FollowingRelationship.following?(user, followed_user) + assert [notification] = Notification.for_user(followed_user) + + render_opts = %{notification: notification, for: followed_user} + assert %{type: "follow_request"} = NotificationView.render("show.json", render_opts) + + # After request is accepted, the same notification is rendered with type "follow": + assert {:ok, _} = CommonAPI.accept_follow_request(user, followed_user) + + notification_id = notification.id + assert [%{id: ^notification_id}] = Notification.for_user(followed_user) + assert %{type: "follow"} = NotificationView.render("show.json", render_opts) + end + + test "it doesn't create a notification for follow-unfollow-follow chains" do + user = insert(:user) + followed_user = insert(:user, locked: false) + + {:ok, _, _, _activity} = CommonAPI.follow(user, followed_user) + assert FollowingRelationship.following?(user, followed_user) + assert [notification] = Notification.for_user(followed_user) + + CommonAPI.unfollow(user, followed_user) + {:ok, _, _, _activity_dupe} = CommonAPI.follow(user, followed_user) + + notification_id = notification.id + assert [%{id: ^notification_id}] = Notification.for_user(followed_user) + end + + test "dismisses the notification on follow request rejection" do + user = insert(:user, locked: true) + follower = insert(:user) + {:ok, _, _, _follow_activity} = CommonAPI.follow(follower, user) + assert [notification] = Notification.for_user(user) + {:ok, _follower} = CommonAPI.reject_follow_request(follower, user) + assert [] = Notification.for_user(user) + end + end + describe "get notification" do test "it gets a notification that belongs to the user" do user = insert(:user) other_user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "hey @#{other_user.nickname}"}) + {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}"}) {:ok, [notification]} = Notification.create_notifications(activity) {:ok, notification} = Notification.get(other_user, notification.id) @@ -245,7 +382,7 @@ defmodule Pleroma.NotificationTest do user = insert(:user) other_user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "hey @#{other_user.nickname}"}) + {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}"}) {:ok, [notification]} = Notification.create_notifications(activity) {:error, _notification} = Notification.get(user, notification.id) @@ -257,7 +394,7 @@ defmodule Pleroma.NotificationTest do user = insert(:user) other_user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "hey @#{other_user.nickname}"}) + {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}"}) {:ok, [notification]} = Notification.create_notifications(activity) {:ok, notification} = Notification.dismiss(other_user, notification.id) @@ -269,7 +406,7 @@ defmodule Pleroma.NotificationTest do user = insert(:user) other_user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "hey @#{other_user.nickname}"}) + {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}"}) {:ok, [notification]} = Notification.create_notifications(activity) {:error, _notification} = Notification.dismiss(user, notification.id) @@ -284,14 +421,14 @@ defmodule Pleroma.NotificationTest do {:ok, activity} = CommonAPI.post(user, %{ - "status" => "hey @#{other_user.nickname} and @#{third_user.nickname} !" + status: "hey @#{other_user.nickname} and @#{third_user.nickname} !" }) {:ok, _notifs} = Notification.create_notifications(activity) {:ok, activity} = CommonAPI.post(user, %{ - "status" => "hey again @#{other_user.nickname} and @#{third_user.nickname} !" + status: "hey again @#{other_user.nickname} and @#{third_user.nickname} !" }) {:ok, _notifs} = Notification.create_notifications(activity) @@ -309,12 +446,12 @@ defmodule Pleroma.NotificationTest do {:ok, _activity} = CommonAPI.post(user, %{ - "status" => "hey @#{other_user.nickname}!" + status: "hey @#{other_user.nickname}!" }) {:ok, _activity} = CommonAPI.post(user, %{ - "status" => "hey again @#{other_user.nickname}!" + status: "hey again @#{other_user.nickname}!" }) [n2, n1] = notifs = Notification.for_user(other_user) @@ -324,7 +461,7 @@ defmodule Pleroma.NotificationTest do {:ok, _activity} = CommonAPI.post(user, %{ - "status" => "hey yet again @#{other_user.nickname}!" + status: "hey yet again @#{other_user.nickname}!" }) Notification.set_read_up_to(other_user, n2.id) @@ -334,6 +471,16 @@ defmodule Pleroma.NotificationTest do assert n1.seen == true assert n2.seen == true assert n3.seen == false + + assert %Pleroma.Marker{} = + m = + Pleroma.Repo.get_by( + Pleroma.Marker, + user_id: other_user.id, + timeline: "notifications" + ) + + assert m.last_read_id == to_string(n2.id) end end @@ -353,7 +500,7 @@ defmodule Pleroma.NotificationTest do Enum.each(0..10, fn i -> {:ok, _activity} = CommonAPI.post(user1, %{ - "status" => "hey ##{i} @#{user2.nickname}!" + status: "hey ##{i} @#{user2.nickname}!" }) end) @@ -382,17 +529,19 @@ defmodule Pleroma.NotificationTest do end end - describe "notification target determination" do + describe "notification target determination / get_notified_from_activity/2" do test "it sends notifications to addressed users in new messages" do user = insert(:user) other_user = insert(:user) {:ok, activity} = CommonAPI.post(user, %{ - "status" => "hey @#{other_user.nickname}!" + status: "hey @#{other_user.nickname}!" }) - assert other_user in Notification.get_notified_from_activity(activity) + {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity) + + assert other_user in enabled_receivers end test "it sends notifications to mentioned users in new messages" do @@ -420,7 +569,9 @@ defmodule Pleroma.NotificationTest do {:ok, activity} = Transmogrifier.handle_incoming(create_activity) - assert other_user in Notification.get_notified_from_activity(activity) + {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity) + + assert other_user in enabled_receivers end test "it does not send notifications to users who are only cc in new messages" do @@ -442,7 +593,9 @@ defmodule Pleroma.NotificationTest do {:ok, activity} = Transmogrifier.handle_incoming(create_activity) - assert other_user not in Notification.get_notified_from_activity(activity) + {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity) + + assert other_user not in enabled_receivers end test "it does not send notification to mentioned users in likes" do @@ -452,12 +605,37 @@ defmodule Pleroma.NotificationTest do {:ok, activity_one} = CommonAPI.post(user, %{ - "status" => "hey @#{other_user.nickname}!" + status: "hey @#{other_user.nickname}!" + }) + + {:ok, activity_two} = CommonAPI.favorite(third_user, activity_one.id) + + {enabled_receivers, _disabled_receivers} = + Notification.get_notified_from_activity(activity_two) + + assert other_user not in enabled_receivers + end + + test "it only notifies the post's author in likes" do + user = insert(:user) + other_user = insert(:user) + third_user = insert(:user) + + {:ok, activity_one} = + CommonAPI.post(user, %{ + status: "hey @#{other_user.nickname}!" }) - {:ok, activity_two, _} = CommonAPI.favorite(activity_one.id, third_user) + {:ok, like_data, _} = Builder.like(third_user, activity_one.object) + + {:ok, like, _} = + like_data + |> Map.put("to", [other_user.ap_id | like_data["to"]]) + |> ActivityPub.persist(local: true) + + {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(like) - assert other_user not in Notification.get_notified_from_activity(activity_two) + assert other_user not in enabled_receivers end test "it does not send notification to mentioned users in announces" do @@ -467,12 +645,93 @@ defmodule Pleroma.NotificationTest do {:ok, activity_one} = CommonAPI.post(user, %{ - "status" => "hey @#{other_user.nickname}!" + status: "hey @#{other_user.nickname}!" }) {:ok, activity_two, _} = CommonAPI.repeat(activity_one.id, third_user) - assert other_user not in Notification.get_notified_from_activity(activity_two) + {enabled_receivers, _disabled_receivers} = + Notification.get_notified_from_activity(activity_two) + + assert other_user not in enabled_receivers + end + + test "it returns blocking recipient in disabled recipients list" do + user = insert(:user) + other_user = insert(:user) + {:ok, _user_relationship} = User.block(other_user, user) + + {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"}) + + {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity) + + assert [] == enabled_receivers + assert [other_user] == disabled_receivers + end + + test "it returns notification-muting recipient in disabled recipients list" do + user = insert(:user) + other_user = insert(:user) + {:ok, _user_relationships} = User.mute(other_user, user) + + {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"}) + + {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity) + + assert [] == enabled_receivers + assert [other_user] == disabled_receivers + end + + test "it returns thread-muting recipient in disabled recipients list" do + user = insert(:user) + other_user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"}) + + {:ok, _} = CommonAPI.add_mute(other_user, activity) + + {:ok, same_context_activity} = + CommonAPI.post(user, %{ + status: "hey-hey-hey @#{other_user.nickname}!", + in_reply_to_status_id: activity.id + }) + + {enabled_receivers, disabled_receivers} = + Notification.get_notified_from_activity(same_context_activity) + + assert [other_user] == disabled_receivers + refute other_user in enabled_receivers + end + + test "it returns non-following domain-blocking recipient in disabled recipients list" do + blocked_domain = "blocked.domain" + user = insert(:user, %{ap_id: "https://#{blocked_domain}/@actor"}) + other_user = insert(:user) + + {:ok, other_user} = User.block_domain(other_user, blocked_domain) + + {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"}) + + {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity) + + assert [] == enabled_receivers + assert [other_user] == disabled_receivers + end + + test "it returns following domain-blocking recipient in enabled recipients list" do + blocked_domain = "blocked.domain" + user = insert(:user, %{ap_id: "https://#{blocked_domain}/@actor"}) + other_user = insert(:user) + + {:ok, other_user} = User.block_domain(other_user, blocked_domain) + {:ok, other_user} = User.follow(other_user, user) + + {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"}) + + {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity) + + assert [other_user] == enabled_receivers + assert [] == disabled_receivers end end @@ -481,11 +740,11 @@ defmodule Pleroma.NotificationTest do user = insert(:user) other_user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "test post"}) + {:ok, activity} = CommonAPI.post(user, %{status: "test post"}) assert Enum.empty?(Notification.for_user(user)) - {:ok, _, _} = CommonAPI.favorite(activity.id, other_user) + {:ok, _} = CommonAPI.favorite(other_user, activity.id) assert length(Notification.for_user(user)) == 1 @@ -498,15 +757,15 @@ defmodule Pleroma.NotificationTest do user = insert(:user) other_user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "test post"}) + {:ok, activity} = CommonAPI.post(user, %{status: "test post"}) assert Enum.empty?(Notification.for_user(user)) - {:ok, _, _} = CommonAPI.favorite(activity.id, other_user) + {:ok, _} = CommonAPI.favorite(other_user, activity.id) assert length(Notification.for_user(user)) == 1 - {:ok, _, _, _} = CommonAPI.unfavorite(activity.id, other_user) + {:ok, _} = CommonAPI.unfavorite(activity.id, other_user) assert Enum.empty?(Notification.for_user(user)) end @@ -515,7 +774,7 @@ defmodule Pleroma.NotificationTest do user = insert(:user) other_user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "test post"}) + {:ok, activity} = CommonAPI.post(user, %{status: "test post"}) assert Enum.empty?(Notification.for_user(user)) @@ -532,7 +791,7 @@ defmodule Pleroma.NotificationTest do user = insert(:user) other_user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "test post"}) + {:ok, activity} = CommonAPI.post(user, %{status: "test post"}) assert Enum.empty?(Notification.for_user(user)) @@ -540,7 +799,7 @@ defmodule Pleroma.NotificationTest do assert length(Notification.for_user(user)) == 1 - {:ok, _, _} = CommonAPI.unrepeat(activity.id, other_user) + {:ok, _} = CommonAPI.unrepeat(activity.id, other_user) assert Enum.empty?(Notification.for_user(user)) end @@ -549,7 +808,7 @@ defmodule Pleroma.NotificationTest do user = insert(:user) other_user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "test post"}) + {:ok, activity} = CommonAPI.post(user, %{status: "test post"}) assert Enum.empty?(Notification.for_user(user)) @@ -557,7 +816,7 @@ defmodule Pleroma.NotificationTest do assert Enum.empty?(Notification.for_user(user)) - {:error, _} = CommonAPI.favorite(activity.id, other_user) + {:error, :not_found} = CommonAPI.favorite(other_user, activity.id) assert Enum.empty?(Notification.for_user(user)) end @@ -566,7 +825,7 @@ defmodule Pleroma.NotificationTest do user = insert(:user) other_user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "test post"}) + {:ok, activity} = CommonAPI.post(user, %{status: "test post"}) assert Enum.empty?(Notification.for_user(user)) @@ -583,13 +842,13 @@ defmodule Pleroma.NotificationTest do user = insert(:user) other_user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "test post"}) + {:ok, activity} = CommonAPI.post(user, %{status: "test post"}) {:ok, _deletion_activity} = CommonAPI.delete(activity.id, user) {:ok, _reply_activity} = CommonAPI.post(other_user, %{ - "status" => "test reply", - "in_reply_to_status_id" => activity.id + status: "test reply", + in_reply_to_status_id: activity.id }) assert Enum.empty?(Notification.for_user(user)) @@ -600,7 +859,7 @@ defmodule Pleroma.NotificationTest do other_user = insert(:user) {:ok, _activity} = - CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}", "visibility" => "direct"}) + CommonAPI.post(user, %{status: "hi @#{other_user.nickname}", visibility: "direct"}) refute Enum.empty?(Notification.for_user(other_user)) @@ -649,12 +908,20 @@ defmodule Pleroma.NotificationTest do "object" => remote_user.ap_id } + remote_user_url = remote_user.ap_id + + Tesla.Mock.mock(fn + %{method: :get, url: ^remote_user_url} -> + %Tesla.Env{status: 404, body: ""} + end) + {:ok, _delete_activity} = Transmogrifier.handle_incoming(delete_user_message) ObanHelpers.perform_all() assert Enum.empty?(Notification.for_user(local_user)) end + @tag capture_log: true test "move activity generates a notification" do %{ap_id: old_ap_id} = old_user = insert(:user) %{ap_id: new_ap_id} = new_user = insert(:user, also_known_as: [old_ap_id]) @@ -664,6 +931,18 @@ defmodule Pleroma.NotificationTest do User.follow(follower, old_user) User.follow(other_follower, old_user) + old_user_url = old_user.ap_id + + body = + File.read!("test/fixtures/users_mock/localhost.json") + |> String.replace("{{nickname}}", old_user.nickname) + |> Jason.encode!() + + Tesla.Mock.mock(fn + %{method: :get, url: ^old_user_url} -> + %Tesla.Env{status: 200, body: body} + end) + Pleroma.Web.ActivityPub.ActivityPub.move(old_user, new_user) ObanHelpers.perform_all() @@ -691,7 +970,7 @@ defmodule Pleroma.NotificationTest do muted = insert(:user) {:ok, _user_relationships} = User.mute(user, muted, false) - {:ok, _activity} = CommonAPI.post(muted, %{"status" => "hey @#{user.nickname}"}) + {:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"}) assert length(Notification.for_user(user)) == 1 end @@ -701,7 +980,7 @@ defmodule Pleroma.NotificationTest do muted = insert(:user) {:ok, _user_relationships} = User.mute(user, muted) - {:ok, _activity} = CommonAPI.post(muted, %{"status" => "hey @#{user.nickname}"}) + {:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"}) assert Notification.for_user(user) == [] end @@ -711,26 +990,38 @@ defmodule Pleroma.NotificationTest do blocked = insert(:user) {:ok, _user_relationship} = User.block(user, blocked) - {:ok, _activity} = CommonAPI.post(blocked, %{"status" => "hey @#{user.nickname}"}) + {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"}) assert Notification.for_user(user) == [] end - test "it doesn't return notificatitons for blocked domain" do + test "it doesn't return notifications for domain-blocked non-followed user" do user = insert(:user) blocked = insert(:user, ap_id: "http://some-domain.com") {:ok, user} = User.block_domain(user, "some-domain.com") - {:ok, _activity} = CommonAPI.post(blocked, %{"status" => "hey @#{user.nickname}"}) + {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"}) assert Notification.for_user(user) == [] end + test "it returns notifications for domain-blocked but followed user" do + user = insert(:user) + blocked = insert(:user, ap_id: "http://some-domain.com") + + {:ok, user} = User.block_domain(user, "some-domain.com") + {:ok, _} = User.follow(user, blocked) + + {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"}) + + assert length(Notification.for_user(user)) == 1 + end + test "it doesn't return notifications for muted thread" do user = insert(:user) another_user = insert(:user) - {:ok, activity} = CommonAPI.post(another_user, %{"status" => "hey @#{user.nickname}"}) + {:ok, activity} = CommonAPI.post(another_user, %{status: "hey @#{user.nickname}"}) {:ok, _} = Pleroma.ThreadMute.add_mute(user.id, activity.data["context"]) assert Notification.for_user(user) == [] @@ -741,7 +1032,7 @@ defmodule Pleroma.NotificationTest do muted = insert(:user) {:ok, _user_relationships} = User.mute(user, muted) - {:ok, _activity} = CommonAPI.post(muted, %{"status" => "hey @#{user.nickname}"}) + {:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"}) assert length(Notification.for_user(user, %{with_muted: true})) == 1 end @@ -751,17 +1042,18 @@ defmodule Pleroma.NotificationTest do blocked = insert(:user) {:ok, _user_relationship} = User.block(user, blocked) - {:ok, _activity} = CommonAPI.post(blocked, %{"status" => "hey @#{user.nickname}"}) + {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"}) assert Enum.empty?(Notification.for_user(user, %{with_muted: true})) end - test "it doesn't return notifications from a domain-blocked user when with_muted is set" do + test "when with_muted is set, " <> + "it doesn't return notifications from a domain-blocked non-followed user" do user = insert(:user) blocked = insert(:user, ap_id: "http://some-domain.com") {:ok, user} = User.block_domain(user, "some-domain.com") - {:ok, _activity} = CommonAPI.post(blocked, %{"status" => "hey @#{user.nickname}"}) + {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"}) assert Enum.empty?(Notification.for_user(user, %{with_muted: true})) end @@ -770,7 +1062,7 @@ defmodule Pleroma.NotificationTest do user = insert(:user) another_user = insert(:user) - {:ok, activity} = CommonAPI.post(another_user, %{"status" => "hey @#{user.nickname}"}) + {:ok, activity} = CommonAPI.post(another_user, %{status: "hey @#{user.nickname}"}) {:ok, _} = Pleroma.ThreadMute.add_mute(user.id, activity.data["context"]) assert length(Notification.for_user(user, %{with_muted: true})) == 1 diff --git a/test/object_test.exs b/test/object_test.exs index fe583decd..198d3b1cf 100644 --- a/test/object_test.exs +++ b/test/object_test.exs @@ -380,7 +380,8 @@ defmodule Pleroma.ObjectTest do user = insert(:user) activity = Activity.get_create_by_object_ap_id(object.data["id"]) - {:ok, _activity, object} = CommonAPI.favorite(activity.id, user) + {:ok, activity} = CommonAPI.favorite(user, activity.id) + object = Object.get_by_ap_id(activity.data["object"]) assert object.data["like_count"] == 1 diff --git a/test/otp_version_test.exs b/test/otp_version_test.exs new file mode 100644 index 000000000..7d2538ec8 --- /dev/null +++ b/test/otp_version_test.exs @@ -0,0 +1,42 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.OTPVersionTest do + use ExUnit.Case, async: true + + alias Pleroma.OTPVersion + + describe "check/1" do + test "22.4" do + assert OTPVersion.get_version_from_files(["test/fixtures/warnings/otp_version/22.4"]) == + "22.4" + end + + test "22.1" do + assert OTPVersion.get_version_from_files(["test/fixtures/warnings/otp_version/22.1"]) == + "22.1" + end + + test "21.1" do + assert OTPVersion.get_version_from_files(["test/fixtures/warnings/otp_version/21.1"]) == + "21.1" + end + + test "23.0" do + assert OTPVersion.get_version_from_files(["test/fixtures/warnings/otp_version/23.0"]) == + "23.0" + end + + test "with non existance file" do + assert OTPVersion.get_version_from_files([ + "test/fixtures/warnings/otp_version/non-exising", + "test/fixtures/warnings/otp_version/22.4" + ]) == "22.4" + end + + test "empty paths" do + assert OTPVersion.get_version_from_files([]) == nil + end + end +end diff --git a/test/plugs/authentication_plug_test.exs b/test/plugs/authentication_plug_test.exs index ae2f3f8ec..c8ede71c0 100644 --- a/test/plugs/authentication_plug_test.exs +++ b/test/plugs/authentication_plug_test.exs @@ -6,6 +6,8 @@ defmodule Pleroma.Plugs.AuthenticationPlugTest do use Pleroma.Web.ConnCase, async: true alias Pleroma.Plugs.AuthenticationPlug + alias Pleroma.Plugs.OAuthScopesPlug + alias Pleroma.Plugs.PlugHelper alias Pleroma.User import ExUnit.CaptureLog @@ -14,7 +16,7 @@ defmodule Pleroma.Plugs.AuthenticationPlugTest do user = %User{ id: 1, name: "dude", - password_hash: Comeonin.Pbkdf2.hashpwsalt("guy") + password_hash: Pbkdf2.hash_pwd_salt("guy") } conn = @@ -36,13 +38,16 @@ defmodule Pleroma.Plugs.AuthenticationPlugTest do assert ret_conn == conn end - test "with a correct password in the credentials, it assigns the auth_user", %{conn: conn} do + test "with a correct password in the credentials, " <> + "it assigns the auth_user and marks OAuthScopesPlug as skipped", + %{conn: conn} do conn = conn |> assign(:auth_credentials, %{password: "guy"}) |> AuthenticationPlug.call(%{}) assert conn.assigns.user == conn.assigns.auth_user + assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug) end test "with a wrong password in the credentials, it does nothing", %{conn: conn} do @@ -74,6 +79,13 @@ defmodule Pleroma.Plugs.AuthenticationPlugTest do assert AuthenticationPlug.checkpw("password", hash) end + test "check bcrypt hash" do + hash = "$2a$10$uyhC/R/zoE1ndwwCtMusK.TLVzkQ/Ugsbqp3uXI.CTTz0gBw.24jS" + + assert AuthenticationPlug.checkpw("password", hash) + refute AuthenticationPlug.checkpw("password1", hash) + end + test "it returns false when hash invalid" do hash = "psBWV8gxkGOZWBz$PmfCycChoxeJ3GgGzwvhlgacb9mUoZ.KUXNCssekER4SJ7bOK53uXrHNb2e4i8yPFgSKyzaW9CcmrDXWIEMtD1" diff --git a/test/plugs/ensure_authenticated_plug_test.exs b/test/plugs/ensure_authenticated_plug_test.exs index 7f3559b83..a0667c5e0 100644 --- a/test/plugs/ensure_authenticated_plug_test.exs +++ b/test/plugs/ensure_authenticated_plug_test.exs @@ -20,34 +20,61 @@ defmodule Pleroma.Plugs.EnsureAuthenticatedPlugTest do conn = assign(conn, :user, %User{}) ret_conn = EnsureAuthenticatedPlug.call(conn, %{}) - assert ret_conn == conn + refute ret_conn.halted end end + test "it halts if user is assigned and MFA enabled", %{conn: conn} do + conn = + conn + |> assign(:user, %User{multi_factor_authentication_settings: %{enabled: true}}) + |> assign(:auth_credentials, %{password: "xd-42"}) + |> EnsureAuthenticatedPlug.call(%{}) + + assert conn.status == 403 + assert conn.halted == true + + assert conn.resp_body == + "{\"error\":\"Two-factor authentication enabled, you must use a access token.\"}" + end + + test "it continues if user is assigned and MFA disabled", %{conn: conn} do + conn = + conn + |> assign(:user, %User{multi_factor_authentication_settings: %{enabled: false}}) + |> assign(:auth_credentials, %{password: "xd-42"}) + |> EnsureAuthenticatedPlug.call(%{}) + + refute conn.status == 403 + refute conn.halted + end + describe "with :if_func / :unless_func options" do setup do %{ - true_fn: fn -> true end, - false_fn: fn -> false end + true_fn: fn _conn -> true end, + false_fn: fn _conn -> false end } end test "it continues if a user is assigned", %{conn: conn, true_fn: true_fn, false_fn: false_fn} do conn = assign(conn, :user, %User{}) - assert EnsureAuthenticatedPlug.call(conn, if_func: true_fn) == conn - assert EnsureAuthenticatedPlug.call(conn, if_func: false_fn) == conn - assert EnsureAuthenticatedPlug.call(conn, unless_func: true_fn) == conn - assert EnsureAuthenticatedPlug.call(conn, unless_func: false_fn) == conn + refute EnsureAuthenticatedPlug.call(conn, if_func: true_fn).halted + refute EnsureAuthenticatedPlug.call(conn, if_func: false_fn).halted + refute EnsureAuthenticatedPlug.call(conn, unless_func: true_fn).halted + refute EnsureAuthenticatedPlug.call(conn, unless_func: false_fn).halted end test "it continues if a user is NOT assigned but :if_func evaluates to `false`", %{conn: conn, false_fn: false_fn} do - assert EnsureAuthenticatedPlug.call(conn, if_func: false_fn) == conn + ret_conn = EnsureAuthenticatedPlug.call(conn, if_func: false_fn) + refute ret_conn.halted end test "it continues if a user is NOT assigned but :unless_func evaluates to `true`", %{conn: conn, true_fn: true_fn} do - assert EnsureAuthenticatedPlug.call(conn, unless_func: true_fn) == conn + ret_conn = EnsureAuthenticatedPlug.call(conn, unless_func: true_fn) + refute ret_conn.halted end test "it halts if a user is NOT assigned and :if_func evaluates to `true`", diff --git a/test/plugs/ensure_public_or_authenticated_plug_test.exs b/test/plugs/ensure_public_or_authenticated_plug_test.exs index 411252274..fc2934369 100644 --- a/test/plugs/ensure_public_or_authenticated_plug_test.exs +++ b/test/plugs/ensure_public_or_authenticated_plug_test.exs @@ -29,7 +29,7 @@ defmodule Pleroma.Plugs.EnsurePublicOrAuthenticatedPlugTest do conn |> EnsurePublicOrAuthenticatedPlug.call(%{}) - assert ret_conn == conn + refute ret_conn.halted end test "it continues if a user is assigned, even if not public", %{conn: conn} do @@ -43,6 +43,6 @@ defmodule Pleroma.Plugs.EnsurePublicOrAuthenticatedPlugTest do conn |> EnsurePublicOrAuthenticatedPlug.call(%{}) - assert ret_conn == conn + refute ret_conn.halted end end diff --git a/test/plugs/legacy_authentication_plug_test.exs b/test/plugs/legacy_authentication_plug_test.exs index 7559de7d3..3b8c07627 100644 --- a/test/plugs/legacy_authentication_plug_test.exs +++ b/test/plugs/legacy_authentication_plug_test.exs @@ -8,6 +8,8 @@ defmodule Pleroma.Plugs.LegacyAuthenticationPlugTest do import Pleroma.Factory alias Pleroma.Plugs.LegacyAuthenticationPlug + alias Pleroma.Plugs.OAuthScopesPlug + alias Pleroma.Plugs.PlugHelper alias Pleroma.User setup do @@ -36,7 +38,8 @@ defmodule Pleroma.Plugs.LegacyAuthenticationPlugTest do end @tag :skip_on_mac - test "it authenticates the auth_user if present and password is correct and resets the password", + test "if `auth_user` is present and password is correct, " <> + "it authenticates the user, resets the password, marks OAuthScopesPlug as skipped", %{ conn: conn, user: user @@ -49,6 +52,7 @@ defmodule Pleroma.Plugs.LegacyAuthenticationPlugTest do conn = LegacyAuthenticationPlug.call(conn, %{}) assert conn.assigns.user.id == user.id + assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug) end @tag :skip_on_mac diff --git a/test/plugs/oauth_scopes_plug_test.exs b/test/plugs/oauth_scopes_plug_test.exs index e79ecf263..884de7b4d 100644 --- a/test/plugs/oauth_scopes_plug_test.exs +++ b/test/plugs/oauth_scopes_plug_test.exs @@ -5,15 +5,22 @@ defmodule Pleroma.Plugs.OAuthScopesPlugTest do use Pleroma.Web.ConnCase, async: true - alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Repo import Mock import Pleroma.Factory - setup_with_mocks([{EnsurePublicOrAuthenticatedPlug, [], [call: fn conn, _ -> conn end]}]) do - :ok + test "is not performed if marked as skipped", %{conn: conn} do + with_mock OAuthScopesPlug, [:passthrough], perform: &passthrough([&1, &2]) do + conn = + conn + |> OAuthScopesPlug.skip_plug() + |> OAuthScopesPlug.call(%{scopes: ["random_scope"]}) + + refute called(OAuthScopesPlug.perform(:_, :_)) + refute conn.halted + end end test "if `token.scopes` fulfills specified 'any of' conditions, " <> @@ -48,7 +55,7 @@ defmodule Pleroma.Plugs.OAuthScopesPlugTest do describe "with `fallback: :proceed_unauthenticated` option, " do test "if `token.scopes` doesn't fulfill specified conditions, " <> - "clears :user and :token assigns and calls EnsurePublicOrAuthenticatedPlug", + "clears :user and :token assigns", %{conn: conn} do user = insert(:user) token1 = insert(:oauth_token, scopes: ["read", "write"], user: user) @@ -67,35 +74,6 @@ defmodule Pleroma.Plugs.OAuthScopesPlugTest do refute ret_conn.halted refute ret_conn.assigns[:user] refute ret_conn.assigns[:token] - - assert called(EnsurePublicOrAuthenticatedPlug.call(ret_conn, :_)) - end - end - - test "with :skip_instance_privacy_check option, " <> - "if `token.scopes` doesn't fulfill specified conditions, " <> - "clears :user and :token assigns and does NOT call EnsurePublicOrAuthenticatedPlug", - %{conn: conn} do - user = insert(:user) - token1 = insert(:oauth_token, scopes: ["read:statuses", "write"], user: user) - - for token <- [token1, nil], op <- [:|, :&] do - ret_conn = - conn - |> assign(:user, user) - |> assign(:token, token) - |> OAuthScopesPlug.call(%{ - scopes: ["read"], - op: op, - fallback: :proceed_unauthenticated, - skip_instance_privacy_check: true - }) - - refute ret_conn.halted - refute ret_conn.assigns[:user] - refute ret_conn.assigns[:token] - - refute called(EnsurePublicOrAuthenticatedPlug.call(ret_conn, :_)) end end end diff --git a/test/plugs/rate_limiter_test.exs b/test/plugs/rate_limiter_test.exs index 0ce9f3a0a..4d3d694f4 100644 --- a/test/plugs/rate_limiter_test.exs +++ b/test/plugs/rate_limiter_test.exs @@ -5,8 +5,10 @@ defmodule Pleroma.Plugs.RateLimiterTest do use Pleroma.Web.ConnCase + alias Phoenix.ConnTest alias Pleroma.Config alias Pleroma.Plugs.RateLimiter + alias Plug.Conn import Pleroma.Factory import Pleroma.Tests.Helpers, only: [clear_config: 1, clear_config: 2] @@ -36,8 +38,15 @@ defmodule Pleroma.Plugs.RateLimiterTest do end test "it is disabled if it remote ip plug is enabled but no remote ip is found" do - Config.put([Pleroma.Web.Endpoint, :http, :ip], {127, 0, 0, 1}) - assert RateLimiter.disabled?(Plug.Conn.assign(build_conn(), :remote_ip_found, false)) + assert RateLimiter.disabled?(Conn.assign(build_conn(), :remote_ip_found, false)) + end + + test "it is enabled if remote ip found" do + refute RateLimiter.disabled?(Conn.assign(build_conn(), :remote_ip_found, true)) + end + + test "it is enabled if remote_ip_found flag doesn't exist" do + refute RateLimiter.disabled?(build_conn()) end test "it restricts based on config values" do @@ -58,7 +67,7 @@ defmodule Pleroma.Plugs.RateLimiterTest do end conn = RateLimiter.call(conn, plug_opts) - assert %{"error" => "Throttled"} = Phoenix.ConnTest.json_response(conn, :too_many_requests) + assert %{"error" => "Throttled"} = ConnTest.json_response(conn, :too_many_requests) assert conn.halted Process.sleep(50) @@ -68,7 +77,7 @@ defmodule Pleroma.Plugs.RateLimiterTest do conn = RateLimiter.call(conn, plug_opts) assert {1, 4} = RateLimiter.inspect_bucket(conn, limiter_name, plug_opts) - refute conn.status == Plug.Conn.Status.code(:too_many_requests) + refute conn.status == Conn.Status.code(:too_many_requests) refute conn.resp_body refute conn.halted end @@ -98,7 +107,7 @@ defmodule Pleroma.Plugs.RateLimiterTest do plug_opts = RateLimiter.init(name: limiter_name, params: ["id"]) conn = build_conn(:get, "/?id=1") - conn = Plug.Conn.fetch_query_params(conn) + conn = Conn.fetch_query_params(conn) conn_2 = build_conn(:get, "/?id=2") RateLimiter.call(conn, plug_opts) @@ -119,7 +128,7 @@ defmodule Pleroma.Plugs.RateLimiterTest do id = "100" conn = build_conn(:get, "/?id=#{id}") - conn = Plug.Conn.fetch_query_params(conn) + conn = Conn.fetch_query_params(conn) conn_2 = build_conn(:get, "/?id=#{101}") RateLimiter.call(conn, plug_opts) @@ -147,13 +156,13 @@ defmodule Pleroma.Plugs.RateLimiterTest do conn = RateLimiter.call(conn, plug_opts) - assert %{"error" => "Throttled"} = Phoenix.ConnTest.json_response(conn, :too_many_requests) + assert %{"error" => "Throttled"} = ConnTest.json_response(conn, :too_many_requests) assert conn.halted conn_2 = RateLimiter.call(conn_2, plug_opts) assert {1, 4} = RateLimiter.inspect_bucket(conn_2, limiter_name, plug_opts) - refute conn_2.status == Plug.Conn.Status.code(:too_many_requests) + refute conn_2.status == Conn.Status.code(:too_many_requests) refute conn_2.resp_body refute conn_2.halted end @@ -187,7 +196,7 @@ defmodule Pleroma.Plugs.RateLimiterTest do conn = RateLimiter.call(conn, plug_opts) - assert %{"error" => "Throttled"} = Phoenix.ConnTest.json_response(conn, :too_many_requests) + assert %{"error" => "Throttled"} = ConnTest.json_response(conn, :too_many_requests) assert conn.halted end @@ -210,12 +219,12 @@ defmodule Pleroma.Plugs.RateLimiterTest do end conn = RateLimiter.call(conn, plug_opts) - assert %{"error" => "Throttled"} = Phoenix.ConnTest.json_response(conn, :too_many_requests) + assert %{"error" => "Throttled"} = ConnTest.json_response(conn, :too_many_requests) assert conn.halted conn_2 = RateLimiter.call(conn_2, plug_opts) assert {1, 4} = RateLimiter.inspect_bucket(conn_2, limiter_name, plug_opts) - refute conn_2.status == Plug.Conn.Status.code(:too_many_requests) + refute conn_2.status == Conn.Status.code(:too_many_requests) refute conn_2.resp_body refute conn_2.halted end diff --git a/test/pool/connections_test.exs b/test/pool/connections_test.exs new file mode 100644 index 000000000..aeda54875 --- /dev/null +++ b/test/pool/connections_test.exs @@ -0,0 +1,760 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Pool.ConnectionsTest do + use ExUnit.Case, async: true + use Pleroma.Tests.Helpers + + import ExUnit.CaptureLog + import Mox + + alias Pleroma.Gun.Conn + alias Pleroma.GunMock + alias Pleroma.Pool.Connections + + setup :verify_on_exit! + + setup_all do + name = :test_connections + {:ok, pid} = Connections.start_link({name, [checkin_timeout: 150]}) + {:ok, _} = Registry.start_link(keys: :unique, name: Pleroma.GunMock) + + on_exit(fn -> + if Process.alive?(pid), do: GenServer.stop(name) + end) + + {:ok, name: name} + end + + defp open_mock(num \\ 1) do + GunMock + |> expect(:open, num, &start_and_register(&1, &2, &3)) + |> expect(:await_up, num, fn _, _ -> {:ok, :http} end) + |> expect(:set_owner, num, fn _, _ -> :ok end) + end + + defp connect_mock(mock) do + mock + |> expect(:connect, &connect(&1, &2)) + |> expect(:await, &await(&1, &2)) + end + + defp info_mock(mock), do: expect(mock, :info, &info(&1)) + + defp start_and_register('gun-not-up.com', _, _), do: {:error, :timeout} + + defp start_and_register(host, port, _) do + {:ok, pid} = Task.start_link(fn -> Process.sleep(1000) end) + + scheme = + case port do + 443 -> "https" + _ -> "http" + end + + Registry.register(GunMock, pid, %{ + origin_scheme: scheme, + origin_host: host, + origin_port: port + }) + + {:ok, pid} + end + + defp info(pid) do + [{_, info}] = Registry.lookup(GunMock, pid) + info + end + + defp connect(pid, _) do + ref = make_ref() + Registry.register(GunMock, ref, pid) + ref + end + + defp await(pid, ref) do + [{_, ^pid}] = Registry.lookup(GunMock, ref) + {:response, :fin, 200, []} + end + + defp now, do: :os.system_time(:second) + + describe "alive?/2" do + test "is alive", %{name: name} do + assert Connections.alive?(name) + end + + test "returns false if not started" do + refute Connections.alive?(:some_random_name) + end + end + + test "opens connection and reuse it on next request", %{name: name} do + open_mock() + url = "http://some-domain.com" + key = "http:some-domain.com:80" + refute Connections.checkin(url, name) + :ok = Conn.open(url, name) + + conn = Connections.checkin(url, name) + assert is_pid(conn) + assert Process.alive?(conn) + + self = self() + + %Connections{ + conns: %{ + ^key => %Conn{ + conn: ^conn, + gun_state: :up, + used_by: [{^self, _}], + conn_state: :active + } + } + } = Connections.get_state(name) + + reused_conn = Connections.checkin(url, name) + + assert conn == reused_conn + + %Connections{ + conns: %{ + ^key => %Conn{ + conn: ^conn, + gun_state: :up, + used_by: [{^self, _}, {^self, _}], + conn_state: :active + } + } + } = Connections.get_state(name) + + :ok = Connections.checkout(conn, self, name) + + %Connections{ + conns: %{ + ^key => %Conn{ + conn: ^conn, + gun_state: :up, + used_by: [{^self, _}], + conn_state: :active + } + } + } = Connections.get_state(name) + + :ok = Connections.checkout(conn, self, name) + + %Connections{ + conns: %{ + ^key => %Conn{ + conn: ^conn, + gun_state: :up, + used_by: [], + conn_state: :idle + } + } + } = Connections.get_state(name) + end + + test "reuse connection for idna domains", %{name: name} do + open_mock() + url = "http://ですsome-domain.com" + refute Connections.checkin(url, name) + + :ok = Conn.open(url, name) + + conn = Connections.checkin(url, name) + assert is_pid(conn) + assert Process.alive?(conn) + + self = self() + + %Connections{ + conns: %{ + "http:ですsome-domain.com:80" => %Conn{ + conn: ^conn, + gun_state: :up, + used_by: [{^self, _}], + conn_state: :active + } + } + } = Connections.get_state(name) + + reused_conn = Connections.checkin(url, name) + + assert conn == reused_conn + end + + test "reuse for ipv4", %{name: name} do + open_mock() + url = "http://127.0.0.1" + + refute Connections.checkin(url, name) + + :ok = Conn.open(url, name) + + conn = Connections.checkin(url, name) + assert is_pid(conn) + assert Process.alive?(conn) + + self = self() + + %Connections{ + conns: %{ + "http:127.0.0.1:80" => %Conn{ + conn: ^conn, + gun_state: :up, + used_by: [{^self, _}], + conn_state: :active + } + } + } = Connections.get_state(name) + + reused_conn = Connections.checkin(url, name) + + assert conn == reused_conn + + :ok = Connections.checkout(conn, self, name) + :ok = Connections.checkout(reused_conn, self, name) + + %Connections{ + conns: %{ + "http:127.0.0.1:80" => %Conn{ + conn: ^conn, + gun_state: :up, + used_by: [], + conn_state: :idle + } + } + } = Connections.get_state(name) + end + + test "reuse for ipv6", %{name: name} do + open_mock() + url = "http://[2a03:2880:f10c:83:face:b00c:0:25de]" + + refute Connections.checkin(url, name) + + :ok = Conn.open(url, name) + + conn = Connections.checkin(url, name) + assert is_pid(conn) + assert Process.alive?(conn) + + self = self() + + %Connections{ + conns: %{ + "http:2a03:2880:f10c:83:face:b00c:0:25de:80" => %Conn{ + conn: ^conn, + gun_state: :up, + used_by: [{^self, _}], + conn_state: :active + } + } + } = Connections.get_state(name) + + reused_conn = Connections.checkin(url, name) + + assert conn == reused_conn + end + + test "up and down ipv4", %{name: name} do + open_mock() + |> info_mock() + |> allow(self(), name) + + self = self() + url = "http://127.0.0.1" + :ok = Conn.open(url, name) + conn = Connections.checkin(url, name) + send(name, {:gun_down, conn, nil, nil, nil}) + send(name, {:gun_up, conn, nil}) + + %Connections{ + conns: %{ + "http:127.0.0.1:80" => %Conn{ + conn: ^conn, + gun_state: :up, + used_by: [{^self, _}], + conn_state: :active + } + } + } = Connections.get_state(name) + end + + test "up and down ipv6", %{name: name} do + self = self() + + open_mock() + |> info_mock() + |> allow(self, name) + + url = "http://[2a03:2880:f10c:83:face:b00c:0:25de]" + :ok = Conn.open(url, name) + conn = Connections.checkin(url, name) + send(name, {:gun_down, conn, nil, nil, nil}) + send(name, {:gun_up, conn, nil}) + + %Connections{ + conns: %{ + "http:2a03:2880:f10c:83:face:b00c:0:25de:80" => %Conn{ + conn: ^conn, + gun_state: :up, + used_by: [{^self, _}], + conn_state: :active + } + } + } = Connections.get_state(name) + end + + test "reuses connection based on protocol", %{name: name} do + open_mock(2) + http_url = "http://some-domain.com" + http_key = "http:some-domain.com:80" + https_url = "https://some-domain.com" + https_key = "https:some-domain.com:443" + + refute Connections.checkin(http_url, name) + :ok = Conn.open(http_url, name) + conn = Connections.checkin(http_url, name) + assert is_pid(conn) + assert Process.alive?(conn) + + refute Connections.checkin(https_url, name) + :ok = Conn.open(https_url, name) + https_conn = Connections.checkin(https_url, name) + + refute conn == https_conn + + reused_https = Connections.checkin(https_url, name) + + refute conn == reused_https + + assert reused_https == https_conn + + %Connections{ + conns: %{ + ^http_key => %Conn{ + conn: ^conn, + gun_state: :up + }, + ^https_key => %Conn{ + conn: ^https_conn, + gun_state: :up + } + } + } = Connections.get_state(name) + end + + test "connection can't get up", %{name: name} do + expect(GunMock, :open, &start_and_register(&1, &2, &3)) + url = "http://gun-not-up.com" + + assert capture_log(fn -> + refute Conn.open(url, name) + refute Connections.checkin(url, name) + end) =~ + "Opening connection to http://gun-not-up.com failed with error {:error, :timeout}" + end + + test "process gun_down message and then gun_up", %{name: name} do + self = self() + + open_mock() + |> info_mock() + |> allow(self, name) + + url = "http://gun-down-and-up.com" + key = "http:gun-down-and-up.com:80" + :ok = Conn.open(url, name) + conn = Connections.checkin(url, name) + + assert is_pid(conn) + assert Process.alive?(conn) + + %Connections{ + conns: %{ + ^key => %Conn{ + conn: ^conn, + gun_state: :up, + used_by: [{^self, _}] + } + } + } = Connections.get_state(name) + + send(name, {:gun_down, conn, :http, nil, nil}) + + %Connections{ + conns: %{ + ^key => %Conn{ + conn: ^conn, + gun_state: :down, + used_by: [{^self, _}] + } + } + } = Connections.get_state(name) + + send(name, {:gun_up, conn, :http}) + + conn2 = Connections.checkin(url, name) + assert conn == conn2 + + assert is_pid(conn2) + assert Process.alive?(conn2) + + %Connections{ + conns: %{ + ^key => %Conn{ + conn: _, + gun_state: :up, + used_by: [{^self, _}, {^self, _}] + } + } + } = Connections.get_state(name) + end + + test "async processes get same conn for same domain", %{name: name} do + open_mock() + url = "http://some-domain.com" + :ok = Conn.open(url, name) + + tasks = + for _ <- 1..5 do + Task.async(fn -> + Connections.checkin(url, name) + end) + end + + tasks_with_results = Task.yield_many(tasks) + + results = + Enum.map(tasks_with_results, fn {task, res} -> + res || Task.shutdown(task, :brutal_kill) + end) + + conns = for {:ok, value} <- results, do: value + + %Connections{ + conns: %{ + "http:some-domain.com:80" => %Conn{ + conn: conn, + gun_state: :up + } + } + } = Connections.get_state(name) + + assert Enum.all?(conns, fn res -> res == conn end) + end + + test "remove frequently used and idle", %{name: name} do + open_mock(3) + self = self() + http_url = "http://some-domain.com" + https_url = "https://some-domain.com" + :ok = Conn.open(https_url, name) + :ok = Conn.open(http_url, name) + + conn1 = Connections.checkin(https_url, name) + + [conn2 | _conns] = + for _ <- 1..4 do + Connections.checkin(http_url, name) + end + + http_key = "http:some-domain.com:80" + + %Connections{ + conns: %{ + ^http_key => %Conn{ + conn: ^conn2, + gun_state: :up, + conn_state: :active, + used_by: [{^self, _}, {^self, _}, {^self, _}, {^self, _}] + }, + "https:some-domain.com:443" => %Conn{ + conn: ^conn1, + gun_state: :up, + conn_state: :active, + used_by: [{^self, _}] + } + } + } = Connections.get_state(name) + + :ok = Connections.checkout(conn1, self, name) + + another_url = "http://another-domain.com" + :ok = Conn.open(another_url, name) + conn = Connections.checkin(another_url, name) + + %Connections{ + conns: %{ + "http:another-domain.com:80" => %Conn{ + conn: ^conn, + gun_state: :up + }, + ^http_key => %Conn{ + conn: _, + gun_state: :up + } + } + } = Connections.get_state(name) + end + + describe "with proxy" do + test "as ip", %{name: name} do + open_mock() + |> connect_mock() + + url = "http://proxy-string.com" + key = "http:proxy-string.com:80" + :ok = Conn.open(url, name, proxy: {{127, 0, 0, 1}, 8123}) + + conn = Connections.checkin(url, name) + + %Connections{ + conns: %{ + ^key => %Conn{ + conn: ^conn, + gun_state: :up + } + } + } = Connections.get_state(name) + + reused_conn = Connections.checkin(url, name) + + assert reused_conn == conn + end + + test "as host", %{name: name} do + open_mock() + |> connect_mock() + + url = "http://proxy-tuple-atom.com" + :ok = Conn.open(url, name, proxy: {'localhost', 9050}) + conn = Connections.checkin(url, name) + + %Connections{ + conns: %{ + "http:proxy-tuple-atom.com:80" => %Conn{ + conn: ^conn, + gun_state: :up + } + } + } = Connections.get_state(name) + + reused_conn = Connections.checkin(url, name) + + assert reused_conn == conn + end + + test "as ip and ssl", %{name: name} do + open_mock() + |> connect_mock() + + url = "https://proxy-string.com" + + :ok = Conn.open(url, name, proxy: {{127, 0, 0, 1}, 8123}) + conn = Connections.checkin(url, name) + + %Connections{ + conns: %{ + "https:proxy-string.com:443" => %Conn{ + conn: ^conn, + gun_state: :up + } + } + } = Connections.get_state(name) + + reused_conn = Connections.checkin(url, name) + + assert reused_conn == conn + end + + test "as host and ssl", %{name: name} do + open_mock() + |> connect_mock() + + url = "https://proxy-tuple-atom.com" + :ok = Conn.open(url, name, proxy: {'localhost', 9050}) + conn = Connections.checkin(url, name) + + %Connections{ + conns: %{ + "https:proxy-tuple-atom.com:443" => %Conn{ + conn: ^conn, + gun_state: :up + } + } + } = Connections.get_state(name) + + reused_conn = Connections.checkin(url, name) + + assert reused_conn == conn + end + + test "with socks type", %{name: name} do + open_mock() + + url = "http://proxy-socks.com" + + :ok = Conn.open(url, name, proxy: {:socks5, 'localhost', 1234}) + + conn = Connections.checkin(url, name) + + %Connections{ + conns: %{ + "http:proxy-socks.com:80" => %Conn{ + conn: ^conn, + gun_state: :up + } + } + } = Connections.get_state(name) + + reused_conn = Connections.checkin(url, name) + + assert reused_conn == conn + end + + test "with socks4 type and ssl", %{name: name} do + open_mock() + url = "https://proxy-socks.com" + + :ok = Conn.open(url, name, proxy: {:socks4, 'localhost', 1234}) + + conn = Connections.checkin(url, name) + + %Connections{ + conns: %{ + "https:proxy-socks.com:443" => %Conn{ + conn: ^conn, + gun_state: :up + } + } + } = Connections.get_state(name) + + reused_conn = Connections.checkin(url, name) + + assert reused_conn == conn + end + end + + describe "crf/3" do + setup do + crf = Connections.crf(1, 10, 1) + {:ok, crf: crf} + end + + test "more used will have crf higher", %{crf: crf} do + # used 3 times + crf1 = Connections.crf(1, 10, crf) + crf1 = Connections.crf(1, 10, crf1) + + # used 2 times + crf2 = Connections.crf(1, 10, crf) + + assert crf1 > crf2 + end + + test "recently used will have crf higher on equal references", %{crf: crf} do + # used 3 sec ago + crf1 = Connections.crf(3, 10, crf) + + # used 4 sec ago + crf2 = Connections.crf(4, 10, crf) + + assert crf1 > crf2 + end + + test "equal crf on equal reference and time", %{crf: crf} do + # used 2 times + crf1 = Connections.crf(1, 10, crf) + + # used 2 times + crf2 = Connections.crf(1, 10, crf) + + assert crf1 == crf2 + end + + test "recently used will have higher crf", %{crf: crf} do + crf1 = Connections.crf(2, 10, crf) + crf1 = Connections.crf(1, 10, crf1) + + crf2 = Connections.crf(3, 10, crf) + crf2 = Connections.crf(4, 10, crf2) + assert crf1 > crf2 + end + end + + describe "get_unused_conns/1" do + test "crf is equalent, sorting by reference", %{name: name} do + Connections.add_conn(name, "1", %Conn{ + conn_state: :idle, + last_reference: now() - 1 + }) + + Connections.add_conn(name, "2", %Conn{ + conn_state: :idle, + last_reference: now() + }) + + assert [{"1", _unused_conn} | _others] = Connections.get_unused_conns(name) + end + + test "reference is equalent, sorting by crf", %{name: name} do + Connections.add_conn(name, "1", %Conn{ + conn_state: :idle, + crf: 1.999 + }) + + Connections.add_conn(name, "2", %Conn{ + conn_state: :idle, + crf: 2 + }) + + assert [{"1", _unused_conn} | _others] = Connections.get_unused_conns(name) + end + + test "higher crf and lower reference", %{name: name} do + Connections.add_conn(name, "1", %Conn{ + conn_state: :idle, + crf: 3, + last_reference: now() - 1 + }) + + Connections.add_conn(name, "2", %Conn{ + conn_state: :idle, + crf: 2, + last_reference: now() + }) + + assert [{"2", _unused_conn} | _others] = Connections.get_unused_conns(name) + end + + test "lower crf and lower reference", %{name: name} do + Connections.add_conn(name, "1", %Conn{ + conn_state: :idle, + crf: 1.99, + last_reference: now() - 1 + }) + + Connections.add_conn(name, "2", %Conn{ + conn_state: :idle, + crf: 2, + last_reference: now() + }) + + assert [{"1", _unused_conn} | _others] = Connections.get_unused_conns(name) + end + end + + test "count/1" do + name = :test_count + {:ok, _} = Connections.start_link({name, [checkin_timeout: 150]}) + assert Connections.count(name) == 0 + Connections.add_conn(name, "1", %Conn{conn: self()}) + assert Connections.count(name) == 1 + Connections.remove_conn(name, "1") + assert Connections.count(name) == 0 + end +end diff --git a/test/reverse_proxy_test.exs b/test/reverse_proxy/reverse_proxy_test.exs index 87c6aca4e..c677066b3 100644 --- a/test/reverse_proxy_test.exs +++ b/test/reverse_proxy/reverse_proxy_test.exs @@ -4,13 +4,16 @@ defmodule Pleroma.ReverseProxyTest do use Pleroma.Web.ConnCase, async: true + import ExUnit.CaptureLog import Mox + alias Pleroma.ReverseProxy alias Pleroma.ReverseProxy.ClientMock + alias Plug.Conn setup_all do - {:ok, _} = Registry.start_link(keys: :unique, name: Pleroma.ReverseProxy.ClientMock) + {:ok, _} = Registry.start_link(keys: :unique, name: ClientMock) :ok end @@ -21,7 +24,7 @@ defmodule Pleroma.ReverseProxyTest do ClientMock |> expect(:request, fn :get, url, _, _, _ -> - Registry.register(Pleroma.ReverseProxy.ClientMock, url, 0) + Registry.register(ClientMock, url, 0) {:ok, 200, [ @@ -29,14 +32,14 @@ defmodule Pleroma.ReverseProxyTest do {"content-length", byte_size(json) |> to_string()} ], %{url: url}} end) - |> expect(:stream_body, invokes, fn %{url: url} -> - case Registry.lookup(Pleroma.ReverseProxy.ClientMock, url) do + |> expect(:stream_body, invokes, fn %{url: url} = client -> + case Registry.lookup(ClientMock, url) do [{_, 0}] -> - Registry.update_value(Pleroma.ReverseProxy.ClientMock, url, &(&1 + 1)) - {:ok, json} + Registry.update_value(ClientMock, url, &(&1 + 1)) + {:ok, json, client} [{_, 1}] -> - Registry.unregister(Pleroma.ReverseProxy.ClientMock, url) + Registry.unregister(ClientMock, url) :done end end) @@ -78,7 +81,39 @@ defmodule Pleroma.ReverseProxyTest do assert conn.halted end - describe "max_body " do + defp stream_mock(invokes, with_close? \\ false) do + ClientMock + |> expect(:request, fn :get, "/stream-bytes/" <> length, _, _, _ -> + Registry.register(ClientMock, "/stream-bytes/" <> length, 0) + + {:ok, 200, [{"content-type", "application/octet-stream"}], + %{url: "/stream-bytes/" <> length}} + end) + |> expect(:stream_body, invokes, fn %{url: "/stream-bytes/" <> length} = client -> + max = String.to_integer(length) + + case Registry.lookup(ClientMock, "/stream-bytes/" <> length) do + [{_, current}] when current < max -> + Registry.update_value( + ClientMock, + "/stream-bytes/" <> length, + &(&1 + 10) + ) + + {:ok, "0123456789", client} + + [{_, ^max}] -> + Registry.unregister(ClientMock, "/stream-bytes/" <> length) + :done + end + end) + + if with_close? do + expect(ClientMock, :close, fn _ -> :ok end) + end + end + + describe "max_body" do test "length returns error if content-length more than option", %{conn: conn} do user_agent_mock("hackney/1.15.1", 0) @@ -94,38 +129,6 @@ defmodule Pleroma.ReverseProxyTest do end) == "" end - defp stream_mock(invokes, with_close? \\ false) do - ClientMock - |> expect(:request, fn :get, "/stream-bytes/" <> length, _, _, _ -> - Registry.register(Pleroma.ReverseProxy.ClientMock, "/stream-bytes/" <> length, 0) - - {:ok, 200, [{"content-type", "application/octet-stream"}], - %{url: "/stream-bytes/" <> length}} - end) - |> expect(:stream_body, invokes, fn %{url: "/stream-bytes/" <> length} -> - max = String.to_integer(length) - - case Registry.lookup(Pleroma.ReverseProxy.ClientMock, "/stream-bytes/" <> length) do - [{_, current}] when current < max -> - Registry.update_value( - Pleroma.ReverseProxy.ClientMock, - "/stream-bytes/" <> length, - &(&1 + 10) - ) - - {:ok, "0123456789"} - - [{_, ^max}] -> - Registry.unregister(Pleroma.ReverseProxy.ClientMock, "/stream-bytes/" <> length) - :done - end - end) - - if with_close? do - expect(ClientMock, :close, fn _ -> :ok end) - end - end - test "max_body_length returns error if streaming body more than that option", %{conn: conn} do stream_mock(3, true) @@ -214,24 +217,24 @@ defmodule Pleroma.ReverseProxyTest do conn = ReverseProxy.call(conn, "/stream-bytes/200") assert conn.state == :chunked assert byte_size(conn.resp_body) == 200 - assert Plug.Conn.get_resp_header(conn, "content-type") == ["application/octet-stream"] + assert Conn.get_resp_header(conn, "content-type") == ["application/octet-stream"] end defp headers_mock(_) do ClientMock |> expect(:request, fn :get, "/headers", headers, _, _ -> - Registry.register(Pleroma.ReverseProxy.ClientMock, "/headers", 0) + Registry.register(ClientMock, "/headers", 0) {:ok, 200, [{"content-type", "application/json"}], %{url: "/headers", headers: headers}} end) - |> expect(:stream_body, 2, fn %{url: url, headers: headers} -> - case Registry.lookup(Pleroma.ReverseProxy.ClientMock, url) do + |> expect(:stream_body, 2, fn %{url: url, headers: headers} = client -> + case Registry.lookup(ClientMock, url) do [{_, 0}] -> - Registry.update_value(Pleroma.ReverseProxy.ClientMock, url, &(&1 + 1)) + Registry.update_value(ClientMock, url, &(&1 + 1)) headers = for {k, v} <- headers, into: %{}, do: {String.capitalize(k), v} - {:ok, Jason.encode!(%{headers: headers})} + {:ok, Jason.encode!(%{headers: headers}), client} [{_, 1}] -> - Registry.unregister(Pleroma.ReverseProxy.ClientMock, url) + Registry.unregister(ClientMock, url) :done end end) @@ -244,7 +247,7 @@ defmodule Pleroma.ReverseProxyTest do test "header passes", %{conn: conn} do conn = - Plug.Conn.put_req_header( + Conn.put_req_header( conn, "accept", "text/html" @@ -257,7 +260,7 @@ defmodule Pleroma.ReverseProxyTest do test "header is filtered", %{conn: conn} do conn = - Plug.Conn.put_req_header( + Conn.put_req_header( conn, "accept-language", "en-US" @@ -290,18 +293,18 @@ defmodule Pleroma.ReverseProxyTest do defp disposition_headers_mock(headers) do ClientMock |> expect(:request, fn :get, "/disposition", _, _, _ -> - Registry.register(Pleroma.ReverseProxy.ClientMock, "/disposition", 0) + Registry.register(ClientMock, "/disposition", 0) {:ok, 200, headers, %{url: "/disposition"}} end) - |> expect(:stream_body, 2, fn %{url: "/disposition"} -> - case Registry.lookup(Pleroma.ReverseProxy.ClientMock, "/disposition") do + |> expect(:stream_body, 2, fn %{url: "/disposition"} = client -> + case Registry.lookup(ClientMock, "/disposition") do [{_, 0}] -> - Registry.update_value(Pleroma.ReverseProxy.ClientMock, "/disposition", &(&1 + 1)) - {:ok, ""} + Registry.update_value(ClientMock, "/disposition", &(&1 + 1)) + {:ok, "", client} [{_, 1}] -> - Registry.unregister(Pleroma.ReverseProxy.ClientMock, "/disposition") + Registry.unregister(ClientMock, "/disposition") :done end end) diff --git a/test/signature_test.exs b/test/signature_test.exs index 04736d8b9..a7a75aa4d 100644 --- a/test/signature_test.exs +++ b/test/signature_test.exs @@ -19,12 +19,7 @@ defmodule Pleroma.SignatureTest do @private_key "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEA48qb4v6kqigZutO9Ot0wkp27GIF2LiVaADgxQORZozZR63jH\nTaoOrS3Xhngbgc8SSOhfXET3omzeCLqaLNfXnZ8OXmuhJfJSU6mPUvmZ9QdT332j\nfN/g3iWGhYMf/M9ftCKh96nvFVO/tMruzS9xx7tkrfJjehdxh/3LlJMMImPtwcD7\nkFXwyt1qZTAU6Si4oQAJxRDQXHp1ttLl3Ob829VM7IKkrVmY8TD+JSlV0jtVJPj6\n1J19ytKTx/7UaucYvb9HIiBpkuiy5n/irDqKLVf5QEdZoNCdojOZlKJmTLqHhzKP\n3E9TxsUjhrf4/EqegNc/j982RvOxeu4i40zMQwIDAQABAoIBAQDH5DXjfh21i7b4\ncXJuw0cqget617CDUhemdakTDs9yH+rHPZd3mbGDWuT0hVVuFe4vuGpmJ8c+61X0\nRvugOlBlavxK8xvYlsqTzAmPgKUPljyNtEzQ+gz0I+3mH2jkin2rL3D+SksZZgKm\nfiYMPIQWB2WUF04gB46DDb2mRVuymGHyBOQjIx3WC0KW2mzfoFUFRlZEF+Nt8Ilw\nT+g/u0aZ1IWoszbsVFOEdghgZET0HEarum0B2Je/ozcPYtwmU10iBANGMKdLqaP/\nj954BPunrUf6gmlnLZKIKklJj0advx0NA+cL79+zeVB3zexRYSA5o9q0WPhiuTwR\n/aedWHnBAoGBAP0sDWBAM1Y4TRAf8ZI9PcztwLyHPzfEIqzbObJJnx1icUMt7BWi\n+/RMOnhrlPGE1kMhOqSxvXYN3u+eSmWTqai2sSH5Hdw2EqnrISSTnwNUPINX7fHH\njEkgmXQ6ixE48SuBZnb4w1EjdB/BA6/sjL+FNhggOc87tizLTkMXmMtTAoGBAOZV\n+wPuAMBDBXmbmxCuDIjoVmgSlgeRunB1SA8RCPAFAiUo3+/zEgzW2Oz8kgI+xVwM\n33XkLKrWG1Orhpp6Hm57MjIc5MG+zF4/YRDpE/KNG9qU1tiz0UD5hOpIU9pP4bR/\ngxgPxZzvbk4h5BfHWLpjlk8UUpgk6uxqfti48c1RAoGBALBOKDZ6HwYRCSGMjUcg\n3NPEUi84JD8qmFc2B7Tv7h2he2ykIz9iFAGpwCIyETQsJKX1Ewi0OlNnD3RhEEAy\nl7jFGQ+mkzPSeCbadmcpYlgIJmf1KN/x7fDTAepeBpCEzfZVE80QKbxsaybd3Dp8\nCfwpwWUFtBxr4c7J+gNhAGe/AoGAPn8ZyqkrPv9wXtyfqFjxQbx4pWhVmNwrkBPi\nZ2Qh3q4dNOPwTvTO8vjghvzIyR8rAZzkjOJKVFgftgYWUZfM5gE7T2mTkBYq8W+U\n8LetF+S9qAM2gDnaDx0kuUTCq7t87DKk6URuQ/SbI0wCzYjjRD99KxvChVGPBHKo\n1DjqMuECgYEAgJGNm7/lJCS2wk81whfy/ttKGsEIkyhPFYQmdGzSYC5aDc2gp1R3\nxtOkYEvdjfaLfDGEa4UX8CHHF+w3t9u8hBtcdhMH6GYb9iv6z0VBTt4A/11HUR49\n3Z7TQ18Iyh3jAUCzFV9IJlLIExq5Y7P4B3ojWFBN607sDCt8BMPbDYs=\n-----END RSA PRIVATE KEY-----" - @public_key %{ - "id" => "https://mastodon.social/users/lambadalambda#main-key", - "owner" => "https://mastodon.social/users/lambadalambda", - "publicKeyPem" => - "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw0P/Tq4gb4G/QVuMGbJo\nC/AfMNcv+m7NfrlOwkVzcU47jgESuYI4UtJayissCdBycHUnfVUd9qol+eznSODz\nCJhfJloqEIC+aSnuEPGA0POtWad6DU0E6/Ho5zQn5WAWUwbRQqowbrsm/GHo2+3v\neR5jGenwA6sYhINg/c3QQbksyV0uJ20Umyx88w8+TJuv53twOfmyDWuYNoQ3y5cc\nHKOZcLHxYOhvwg3PFaGfFHMFiNmF40dTXt9K96r7sbzc44iLD+VphbMPJEjkMuf8\nPGEFOBzy8pm3wJZw2v32RNW2VESwMYyqDzwHXGSq1a73cS7hEnc79gXlELsK04L9\nQQIDAQAB\n-----END PUBLIC KEY-----\n" - } + @public_key "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw0P/Tq4gb4G/QVuMGbJo\nC/AfMNcv+m7NfrlOwkVzcU47jgESuYI4UtJayissCdBycHUnfVUd9qol+eznSODz\nCJhfJloqEIC+aSnuEPGA0POtWad6DU0E6/Ho5zQn5WAWUwbRQqowbrsm/GHo2+3v\neR5jGenwA6sYhINg/c3QQbksyV0uJ20Umyx88w8+TJuv53twOfmyDWuYNoQ3y5cc\nHKOZcLHxYOhvwg3PFaGfFHMFiNmF40dTXt9K96r7sbzc44iLD+VphbMPJEjkMuf8\nPGEFOBzy8pm3wJZw2v32RNW2VESwMYyqDzwHXGSq1a73cS7hEnc79gXlELsK04L9\nQQIDAQAB\n-----END PUBLIC KEY-----\n" @rsa_public_key { :RSAPublicKey, @@ -42,19 +37,20 @@ defmodule Pleroma.SignatureTest do test "it returns key" do expected_result = {:ok, @rsa_public_key} - user = insert(:user, source_data: %{"publicKey" => @public_key}) + user = insert(:user, public_key: @public_key) assert Signature.fetch_public_key(make_fake_conn(user.ap_id)) == expected_result end test "it returns error when not found user" do assert capture_log(fn -> - assert Signature.fetch_public_key(make_fake_conn("test-ap_id")) == {:error, :error} + assert Signature.fetch_public_key(make_fake_conn("https://test-ap-id")) == + {:error, :error} end) =~ "[error] Could not decode user" end - test "it returns error if public key is empty" do - user = insert(:user, source_data: %{"publicKey" => %{}}) + test "it returns error if public key is nil" do + user = insert(:user, public_key: nil) assert Signature.fetch_public_key(make_fake_conn(user.ap_id)) == {:error, :error} end @@ -69,7 +65,7 @@ defmodule Pleroma.SignatureTest do test "it returns error when not found user" do assert capture_log(fn -> - {:error, _} = Signature.refetch_public_key(make_fake_conn("test-ap_id")) + {:error, _} = Signature.refetch_public_key(make_fake_conn("https://test-ap_id")) end) =~ "[error] Could not decode user" end end @@ -105,12 +101,21 @@ defmodule Pleroma.SignatureTest do describe "key_id_to_actor_id/1" do test "it properly deduces the actor id for misskey" do assert Signature.key_id_to_actor_id("https://example.com/users/1234/publickey") == - "https://example.com/users/1234" + {:ok, "https://example.com/users/1234"} end test "it properly deduces the actor id for mastodon and pleroma" do assert Signature.key_id_to_actor_id("https://example.com/users/1234#main-key") == - "https://example.com/users/1234" + {:ok, "https://example.com/users/1234"} + end + + test "it calls webfinger for 'acct:' accounts" do + with_mock(Pleroma.Web.WebFinger, + finger: fn _ -> %{"ap_id" => "https://gensokyo.2hu/users/raymoo"} end + ) do + assert Signature.key_id_to_actor_id("acct:raymoo@gensokyo.2hu") == + {:ok, "https://gensokyo.2hu/users/raymoo"} + end end end diff --git a/test/stat_test.exs b/test/stats_test.exs index 33b77e7e7..4b76e2e78 100644 --- a/test/stat_test.exs +++ b/test/stats_test.exs @@ -2,36 +2,46 @@ # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.StateTest do +defmodule Pleroma.StatsTest do use Pleroma.DataCase import Pleroma.Factory alias Pleroma.Web.CommonAPI + describe "user count" do + test "it ignores internal users" do + _user = insert(:user, local: true) + _internal = insert(:user, local: true, nickname: nil) + _internal = Pleroma.Web.ActivityPub.Relay.get_actor() + + assert match?(%{stats: %{user_count: 1}}, Pleroma.Stats.calculate_stat_data()) + end + end + describe "status visibility count" do test "on new status" do user = insert(:user) other_user = insert(:user) - CommonAPI.post(user, %{"visibility" => "public", "status" => "hey"}) + CommonAPI.post(user, %{visibility: "public", status: "hey"}) Enum.each(0..1, fn _ -> CommonAPI.post(user, %{ - "visibility" => "unlisted", - "status" => "hey" + visibility: "unlisted", + status: "hey" }) end) Enum.each(0..2, fn _ -> CommonAPI.post(user, %{ - "visibility" => "direct", - "status" => "hey @#{other_user.nickname}" + visibility: "direct", + status: "hey @#{other_user.nickname}" }) end) Enum.each(0..3, fn _ -> CommonAPI.post(user, %{ - "visibility" => "private", - "status" => "hey" + visibility: "private", + status: "hey" }) end) @@ -41,7 +51,7 @@ defmodule Pleroma.StateTest do test "on status delete" do user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"visibility" => "public", "status" => "hey"}) + {:ok, activity} = CommonAPI.post(user, %{visibility: "public", status: "hey"}) assert %{public: 1} = Pleroma.Stats.get_status_visibility_count() CommonAPI.delete(activity.id, user) assert %{public: 0} = Pleroma.Stats.get_status_visibility_count() @@ -49,18 +59,18 @@ defmodule Pleroma.StateTest do test "on status visibility update" do user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"visibility" => "public", "status" => "hey"}) + {:ok, activity} = CommonAPI.post(user, %{visibility: "public", status: "hey"}) assert %{public: 1, private: 0} = Pleroma.Stats.get_status_visibility_count() - {:ok, _} = CommonAPI.update_activity_scope(activity.id, %{"visibility" => "private"}) + {:ok, _} = CommonAPI.update_activity_scope(activity.id, %{visibility: "private"}) assert %{public: 0, private: 1} = Pleroma.Stats.get_status_visibility_count() end test "doesn't count unrelated activities" do user = insert(:user) other_user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"visibility" => "public", "status" => "hey"}) + {:ok, activity} = CommonAPI.post(user, %{visibility: "public", status: "hey"}) _ = CommonAPI.follow(user, other_user) - CommonAPI.favorite(activity.id, other_user) + CommonAPI.favorite(other_user, activity.id) CommonAPI.repeat(activity.id, other_user) assert %{direct: 0, private: 0, public: 1, unlisted: 0} = diff --git a/test/support/api_spec_helpers.ex b/test/support/api_spec_helpers.ex new file mode 100644 index 000000000..80c69c788 --- /dev/null +++ b/test/support/api_spec_helpers.ex @@ -0,0 +1,57 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Tests.ApiSpecHelpers do + @moduledoc """ + OpenAPI spec test helpers + """ + + import ExUnit.Assertions + + alias OpenApiSpex.Cast.Error + alias OpenApiSpex.Reference + alias OpenApiSpex.Schema + + def assert_schema(value, schema) do + api_spec = Pleroma.Web.ApiSpec.spec() + + case OpenApiSpex.cast_value(value, schema, api_spec) do + {:ok, data} -> + data + + {:error, errors} -> + errors = + Enum.map(errors, fn error -> + message = Error.message(error) + path = Error.path_to_string(error) + "#{message} at #{path}" + end) + + flunk( + "Value does not conform to schema #{schema.title}: #{Enum.join(errors, "\n")}\n#{ + inspect(value) + }" + ) + end + end + + def resolve_schema(%Schema{} = schema), do: schema + + def resolve_schema(%Reference{} = ref) do + schemas = Pleroma.Web.ApiSpec.spec().components.schemas + Reference.resolve_schema(ref, schemas) + end + + def api_operations do + paths = Pleroma.Web.ApiSpec.spec().paths + + Enum.flat_map(paths, fn {_, path_item} -> + path_item + |> Map.take([:delete, :get, :head, :options, :patch, :post, :put, :trace]) + |> Map.values() + |> Enum.reject(&is_nil/1) + |> Enum.uniq() + end) + end +end diff --git a/test/support/builders/activity_builder.ex b/test/support/builders/activity_builder.ex index 6e5a8e059..7c4950bfa 100644 --- a/test/support/builders/activity_builder.ex +++ b/test/support/builders/activity_builder.ex @@ -21,7 +21,15 @@ defmodule Pleroma.Builders.ActivityBuilder do def insert(data \\ %{}, opts \\ %{}) do activity = build(data, opts) - ActivityPub.insert(activity) + + case ActivityPub.insert(activity) do + ok = {:ok, activity} -> + ActivityPub.notify_and_stream(activity) + ok + + error -> + error + end end def insert_list(times, data \\ %{}, opts \\ %{}) do diff --git a/test/support/builders/user_builder.ex b/test/support/builders/user_builder.ex index fcfea666f..0c687c029 100644 --- a/test/support/builders/user_builder.ex +++ b/test/support/builders/user_builder.ex @@ -7,10 +7,11 @@ defmodule Pleroma.Builders.UserBuilder do email: "test@example.org", name: "Test Name", nickname: "testname", - password_hash: Comeonin.Pbkdf2.hashpwsalt("test"), + password_hash: Pbkdf2.hash_pwd_salt("test"), bio: "A tester.", ap_id: "some id", last_digest_emailed_at: NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second), + multi_factor_authentication_settings: %Pleroma.MFA.Settings{}, notification_settings: %Pleroma.User.NotificationSetting{} } diff --git a/test/support/captcha_mock.ex b/test/support/captcha_mock.ex index 6dae94edf..7b0c1d5af 100644 --- a/test/support/captcha_mock.ex +++ b/test/support/captcha_mock.ex @@ -6,12 +6,16 @@ defmodule Pleroma.Captcha.Mock do alias Pleroma.Captcha.Service @behaviour Service + @solution "63615261b77f5354fb8c4e4986477555" + + def solution, do: @solution + @impl Service def new, do: %{ type: :mock, token: "afa1815e14e29355e6c8f6b143a39fa2", - answer_data: "63615261b77f5354fb8c4e4986477555", + answer_data: @solution, url: "https://example.org/captcha.png" } diff --git a/test/support/conn_case.ex b/test/support/conn_case.ex index 064874201..b23918dd1 100644 --- a/test/support/conn_case.ex +++ b/test/support/conn_case.ex @@ -51,6 +51,60 @@ defmodule Pleroma.Web.ConnCase do %{user: user, token: token, conn: conn} end + defp request_content_type(%{conn: conn}) do + conn = put_req_header(conn, "content-type", "multipart/form-data") + [conn: conn] + end + + defp json_response_and_validate_schema( + %{ + private: %{ + open_api_spex: %{operation_id: op_id, operation_lookup: lookup, spec: spec} + } + } = conn, + status + ) do + content_type = + conn + |> Plug.Conn.get_resp_header("content-type") + |> List.first() + |> String.split(";") + |> List.first() + + status = Plug.Conn.Status.code(status) + + unless lookup[op_id].responses[status] do + err = "Response schema not found for #{status} #{conn.method} #{conn.request_path}" + flunk(err) + end + + schema = lookup[op_id].responses[status].content[content_type].schema + json = json_response(conn, status) + + case OpenApiSpex.cast_value(json, schema, spec) do + {:ok, _data} -> + json + + {:error, errors} -> + errors = + Enum.map(errors, fn error -> + message = OpenApiSpex.Cast.Error.message(error) + path = OpenApiSpex.Cast.Error.path_to_string(error) + "#{message} at #{path}" + end) + + flunk( + "Response does not conform to schema of #{op_id} operation: #{ + Enum.join(errors, "\n") + }\n#{inspect(json)}" + ) + end + end + + defp json_response_and_validate_schema(conn, _status) do + flunk("Response schema not found for #{conn.method} #{conn.request_path} #{conn.status}") + end + defp ensure_federating_or_authenticated(conn, url, user) do initial_setting = Config.get([:instance, :federating]) on_exit(fn -> Config.put([:instance, :federating], initial_setting) end) @@ -85,7 +139,11 @@ defmodule Pleroma.Web.ConnCase do end if tags[:needs_streamer] do - start_supervised(Pleroma.Web.Streamer.supervisor()) + start_supervised(%{ + id: Pleroma.Web.Streamer.registry(), + start: + {Registry, :start_link, [[keys: :duplicate, name: Pleroma.Web.Streamer.registry()]]} + }) end {:ok, conn: Phoenix.ConnTest.build_conn()} diff --git a/test/support/data_case.ex b/test/support/data_case.ex index 1669f2520..ba8848952 100644 --- a/test/support/data_case.ex +++ b/test/support/data_case.ex @@ -40,7 +40,11 @@ defmodule Pleroma.DataCase do end if tags[:needs_streamer] do - start_supervised(Pleroma.Web.Streamer.supervisor()) + start_supervised(%{ + id: Pleroma.Web.Streamer.registry(), + start: + {Registry, :start_link, [[keys: :duplicate, name: Pleroma.Web.Streamer.registry()]]} + }) end :ok diff --git a/test/support/factory.ex b/test/support/factory.ex index af639b6cd..d4284831c 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -29,10 +29,12 @@ defmodule Pleroma.Factory do name: sequence(:name, &"Test テスト User #{&1}"), email: sequence(:email, &"user#{&1}@example.com"), nickname: sequence(:nickname, &"nick#{&1}"), - password_hash: Comeonin.Pbkdf2.hashpwsalt("test"), + password_hash: Pbkdf2.hash_pwd_salt("test"), bio: sequence(:bio, &"Tester Number #{&1}"), last_digest_emailed_at: NaiveDateTime.utc_now(), - notification_settings: %Pleroma.User.NotificationSetting{} + last_refreshed_at: NaiveDateTime.utc_now(), + notification_settings: %Pleroma.User.NotificationSetting{}, + multi_factor_authentication_settings: %Pleroma.MFA.Settings{} } %{ @@ -294,7 +296,7 @@ defmodule Pleroma.Factory do def oauth_app_factory do %Pleroma.Web.OAuth.App{ - client_name: "Some client", + client_name: sequence(:client_name, &"Some client #{&1}"), redirect_uris: "https://example.com/callback", scopes: ["read", "write", "follow", "push", "admin"], website: "https://example.com", @@ -421,4 +423,13 @@ defmodule Pleroma.Factory do last_read_id: "1" } end + + def mfa_token_factory do + %Pleroma.MFA.Token{ + token: :crypto.strong_rand_bytes(32) |> Base.url_encode64(padding: false), + authorization: build(:oauth_authorization), + valid_until: NaiveDateTime.add(NaiveDateTime.utc_now(), 60 * 10), + user: build(:user) + } + end end diff --git a/test/support/helpers.ex b/test/support/helpers.ex index e68e9bfd2..26281b45e 100644 --- a/test/support/helpers.ex +++ b/test/support/helpers.ex @@ -40,12 +40,18 @@ defmodule Pleroma.Tests.Helpers do clear_config: 2 ] - def to_datetime(naive_datetime) do + def to_datetime(%NaiveDateTime{} = naive_datetime) do naive_datetime |> DateTime.from_naive!("Etc/UTC") |> DateTime.truncate(:second) end + def to_datetime(datetime) when is_binary(datetime) do + datetime + |> NaiveDateTime.from_iso8601!() + |> to_datetime() + end + def collect_ids(collection) do collection |> Enum.map(& &1.id) diff --git a/test/support/http_request_mock.ex b/test/support/http_request_mock.ex index e72638814..3a95e92da 100644 --- a/test/support/http_request_mock.ex +++ b/test/support/http_request_mock.ex @@ -107,7 +107,7 @@ defmodule HttpRequestMock do "https://osada.macgirvin.com/.well-known/webfinger?resource=acct:mike@osada.macgirvin.com", _, _, - Accept: "application/xrd+xml,application/jrd+json" + [{"accept", "application/xrd+xml,application/jrd+json"}] ) do {:ok, %Tesla.Env{ @@ -120,7 +120,7 @@ defmodule HttpRequestMock do "https://social.heldscal.la/.well-known/webfinger?resource=https://social.heldscal.la/user/29191", _, _, - Accept: "application/xrd+xml,application/jrd+json" + [{"accept", "application/xrd+xml,application/jrd+json"}] ) do {:ok, %Tesla.Env{ @@ -141,7 +141,7 @@ defmodule HttpRequestMock do "https://pawoo.net/.well-known/webfinger?resource=acct:https://pawoo.net/users/pekorino", _, _, - Accept: "application/xrd+xml,application/jrd+json" + [{"accept", "application/xrd+xml,application/jrd+json"}] ) do {:ok, %Tesla.Env{ @@ -167,7 +167,7 @@ defmodule HttpRequestMock do "https://social.stopwatchingus-heidelberg.de/.well-known/webfinger?resource=acct:https://social.stopwatchingus-heidelberg.de/user/18330", _, _, - Accept: "application/xrd+xml,application/jrd+json" + [{"accept", "application/xrd+xml,application/jrd+json"}] ) do {:ok, %Tesla.Env{ @@ -188,7 +188,7 @@ defmodule HttpRequestMock do "https://mamot.fr/.well-known/webfinger?resource=acct:https://mamot.fr/users/Skruyb", _, _, - Accept: "application/xrd+xml,application/jrd+json" + [{"accept", "application/xrd+xml,application/jrd+json"}] ) do {:ok, %Tesla.Env{ @@ -201,7 +201,7 @@ defmodule HttpRequestMock do "https://social.heldscal.la/.well-known/webfinger?resource=nonexistant@social.heldscal.la", _, _, - Accept: "application/xrd+xml,application/jrd+json" + [{"accept", "application/xrd+xml,application/jrd+json"}] ) do {:ok, %Tesla.Env{ @@ -211,10 +211,10 @@ defmodule HttpRequestMock do end def get( - "https://squeet.me/xrd/?uri=lain@squeet.me", + "https://squeet.me/xrd/?uri=acct:lain@squeet.me", _, _, - Accept: "application/xrd+xml,application/jrd+json" + [{"accept", "application/xrd+xml,application/jrd+json"}] ) do {:ok, %Tesla.Env{ @@ -227,7 +227,7 @@ defmodule HttpRequestMock do "https://mst3k.interlinked.me/users/luciferMysticus", _, _, - Accept: "application/activity+json" + [{"accept", "application/activity+json"}] ) do {:ok, %Tesla.Env{ @@ -248,7 +248,7 @@ defmodule HttpRequestMock do "https://hubzilla.example.org/channel/kaniini", _, _, - Accept: "application/activity+json" + [{"accept", "application/activity+json"}] ) do {:ok, %Tesla.Env{ @@ -257,7 +257,7 @@ defmodule HttpRequestMock do }} end - def get("https://niu.moe/users/rye", _, _, Accept: "application/activity+json") do + def get("https://niu.moe/users/rye", _, _, [{"accept", "application/activity+json"}]) do {:ok, %Tesla.Env{ status: 200, @@ -265,7 +265,7 @@ defmodule HttpRequestMock do }} end - def get("https://n1u.moe/users/rye", _, _, Accept: "application/activity+json") do + def get("https://n1u.moe/users/rye", _, _, [{"accept", "application/activity+json"}]) do {:ok, %Tesla.Env{ status: 200, @@ -284,7 +284,7 @@ defmodule HttpRequestMock do }} end - def get("https://puckipedia.com/", _, _, Accept: "application/activity+json") do + def get("https://puckipedia.com/", _, _, [{"accept", "application/activity+json"}]) do {:ok, %Tesla.Env{ status: 200, @@ -308,9 +308,25 @@ defmodule HttpRequestMock do }} end - def get("https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39", _, _, - Accept: "application/activity+json" - ) do + def get("https://peertube.social/accounts/craigmaloney", _, _, _) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/craigmaloney.json") + }} + end + + def get("https://peertube.social/videos/watch/278d2b7c-0f38-4aaa-afe6-9ecc0c4a34fe", _, _, _) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/peertube-social.json") + }} + end + + def get("https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39", _, _, [ + {"accept", "application/activity+json"} + ]) do {:ok, %Tesla.Env{ status: 200, @@ -318,7 +334,7 @@ defmodule HttpRequestMock do }} end - def get("https://mobilizon.org/@tcit", _, _, Accept: "application/activity+json") do + def get("https://mobilizon.org/@tcit", _, _, [{"accept", "application/activity+json"}]) do {:ok, %Tesla.Env{ status: 200, @@ -358,7 +374,7 @@ defmodule HttpRequestMock do }} end - def get("http://mastodon.example.org/users/admin", _, _, Accept: "application/activity+json") do + def get("http://mastodon.example.org/users/admin", _, _, _) do {:ok, %Tesla.Env{ status: 200, @@ -366,7 +382,9 @@ defmodule HttpRequestMock do }} end - def get("http://mastodon.example.org/users/relay", _, _, Accept: "application/activity+json") do + def get("http://mastodon.example.org/users/relay", _, _, [ + {"accept", "application/activity+json"} + ]) do {:ok, %Tesla.Env{ status: 200, @@ -374,7 +392,9 @@ defmodule HttpRequestMock do }} end - def get("http://mastodon.example.org/users/gargron", _, _, Accept: "application/activity+json") do + def get("http://mastodon.example.org/users/gargron", _, _, [ + {"accept", "application/activity+json"} + ]) do {:error, :nxdomain} end @@ -557,7 +577,7 @@ defmodule HttpRequestMock do "http://mastodon.example.org/@admin/99541947525187367", _, _, - Accept: "application/activity+json" + _ ) do {:ok, %Tesla.Env{ @@ -582,7 +602,7 @@ defmodule HttpRequestMock do }} end - def get("https://mstdn.io/users/mayuutann", _, _, Accept: "application/activity+json") do + def get("https://mstdn.io/users/mayuutann", _, _, [{"accept", "application/activity+json"}]) do {:ok, %Tesla.Env{ status: 200, @@ -594,7 +614,7 @@ defmodule HttpRequestMock do "https://mstdn.io/users/mayuutann/statuses/99568293732299394", _, _, - Accept: "application/activity+json" + [{"accept", "application/activity+json"}] ) do {:ok, %Tesla.Env{ @@ -614,7 +634,7 @@ defmodule HttpRequestMock do }} end - def get(url, _, _, Accept: "application/xrd+xml,application/jrd+json") + def get(url, _, _, [{"accept", "application/xrd+xml,application/jrd+json"}]) when url in [ "https://pleroma.soykaf.com/.well-known/webfinger?resource=acct:https://pleroma.soykaf.com/users/lain", "https://pleroma.soykaf.com/.well-known/webfinger?resource=https://pleroma.soykaf.com/users/lain" @@ -641,7 +661,7 @@ defmodule HttpRequestMock do "https://shitposter.club/.well-known/webfinger?resource=https://shitposter.club/user/1", _, _, - Accept: "application/xrd+xml,application/jrd+json" + [{"accept", "application/xrd+xml,application/jrd+json"}] ) do {:ok, %Tesla.Env{ @@ -685,7 +705,7 @@ defmodule HttpRequestMock do "https://shitposter.club/.well-known/webfinger?resource=https://shitposter.club/user/5381", _, _, - Accept: "application/xrd+xml,application/jrd+json" + [{"accept", "application/xrd+xml,application/jrd+json"}] ) do {:ok, %Tesla.Env{ @@ -738,7 +758,7 @@ defmodule HttpRequestMock do "https://social.sakamoto.gq/.well-known/webfinger?resource=https://social.sakamoto.gq/users/eal", _, _, - Accept: "application/xrd+xml,application/jrd+json" + [{"accept", "application/xrd+xml,application/jrd+json"}] ) do {:ok, %Tesla.Env{ @@ -751,7 +771,7 @@ defmodule HttpRequestMock do "https://social.sakamoto.gq/objects/0ccc1a2c-66b0-4305-b23a-7f7f2b040056", _, _, - Accept: "application/atom+xml" + [{"accept", "application/atom+xml"}] ) do {:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/sakamoto.atom")}} end @@ -768,7 +788,7 @@ defmodule HttpRequestMock do "https://mastodon.social/.well-known/webfinger?resource=https://mastodon.social/users/lambadalambda", _, _, - Accept: "application/xrd+xml,application/jrd+json" + [{"accept", "application/xrd+xml,application/jrd+json"}] ) do {:ok, %Tesla.Env{ @@ -790,7 +810,7 @@ defmodule HttpRequestMock do "http://gs.example.org/.well-known/webfinger?resource=http://gs.example.org:4040/index.php/user/1", _, _, - Accept: "application/xrd+xml,application/jrd+json" + [{"accept", "application/xrd+xml,application/jrd+json"}] ) do {:ok, %Tesla.Env{ @@ -804,7 +824,7 @@ defmodule HttpRequestMock do "http://gs.example.org:4040/index.php/user/1", _, _, - Accept: "application/activity+json" + [{"accept", "application/activity+json"}] ) do {:ok, %Tesla.Env{status: 406, body: ""}} end @@ -840,7 +860,7 @@ defmodule HttpRequestMock do "https://squeet.me/xrd?uri=lain@squeet.me", _, _, - Accept: "application/xrd+xml,application/jrd+json" + [{"accept", "application/xrd+xml,application/jrd+json"}] ) do {:ok, %Tesla.Env{ @@ -850,10 +870,10 @@ defmodule HttpRequestMock do end def get( - "https://social.heldscal.la/.well-known/webfinger?resource=shp@social.heldscal.la", + "https://social.heldscal.la/.well-known/webfinger?resource=acct:shp@social.heldscal.la", _, _, - Accept: "application/xrd+xml,application/jrd+json" + [{"accept", "application/xrd+xml,application/jrd+json"}] ) do {:ok, %Tesla.Env{ @@ -863,10 +883,10 @@ defmodule HttpRequestMock do end def get( - "https://social.heldscal.la/.well-known/webfinger?resource=invalid_content@social.heldscal.la", + "https://social.heldscal.la/.well-known/webfinger?resource=acct:invalid_content@social.heldscal.la", _, _, - Accept: "application/xrd+xml,application/jrd+json" + [{"accept", "application/xrd+xml,application/jrd+json"}] ) do {:ok, %Tesla.Env{status: 200, body: ""}} end @@ -880,10 +900,10 @@ defmodule HttpRequestMock do end def get( - "http://framatube.org/main/xrd?uri=framasoft@framatube.org", + "http://framatube.org/main/xrd?uri=acct:framasoft@framatube.org", _, _, - Accept: "application/xrd+xml,application/jrd+json" + [{"accept", "application/xrd+xml,application/jrd+json"}] ) do {:ok, %Tesla.Env{ @@ -905,7 +925,7 @@ defmodule HttpRequestMock do "http://gnusocial.de/main/xrd?uri=winterdienst@gnusocial.de", _, _, - Accept: "application/xrd+xml,application/jrd+json" + [{"accept", "application/xrd+xml,application/jrd+json"}] ) do {:ok, %Tesla.Env{ @@ -939,10 +959,10 @@ defmodule HttpRequestMock do end def get( - "https://gerzilla.de/xrd/?uri=kaniini@gerzilla.de", + "https://gerzilla.de/xrd/?uri=acct:kaniini@gerzilla.de", _, _, - Accept: "application/xrd+xml,application/jrd+json" + [{"accept", "application/xrd+xml,application/jrd+json"}] ) do {:ok, %Tesla.Env{ @@ -1005,7 +1025,7 @@ defmodule HttpRequestMock do %Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/osada-user-indio.json")}} end - def get("https://social.heldscal.la/user/23211", _, _, Accept: "application/activity+json") do + def get("https://social.heldscal.la/user/23211", _, _, [{"accept", "application/activity+json"}]) do {:ok, Tesla.Mock.json(%{"id" => "https://social.heldscal.la/user/23211"}, status: 200)} end @@ -1135,10 +1155,10 @@ defmodule HttpRequestMock do end def get( - "https://zetsubou.xn--q9jyb4c/.well-known/webfinger?resource=lain@zetsubou.xn--q9jyb4c", + "https://zetsubou.xn--q9jyb4c/.well-known/webfinger?resource=acct:lain@zetsubou.xn--q9jyb4c", _, _, - Accept: "application/xrd+xml,application/jrd+json" + [{"accept", "application/xrd+xml,application/jrd+json"}] ) do {:ok, %Tesla.Env{ @@ -1148,10 +1168,10 @@ defmodule HttpRequestMock do end def get( - "https://zetsubou.xn--q9jyb4c/.well-known/webfinger?resource=https://zetsubou.xn--q9jyb4c/users/lain", + "https://zetsubou.xn--q9jyb4c/.well-known/webfinger?resource=acct:https://zetsubou.xn--q9jyb4c/users/lain", _, _, - Accept: "application/xrd+xml,application/jrd+json" + [{"accept", "application/xrd+xml,application/jrd+json"}] ) do {:ok, %Tesla.Env{ @@ -1173,7 +1193,9 @@ defmodule HttpRequestMock do }} end - def get("https://info.pleroma.site/activity.json", _, _, Accept: "application/activity+json") do + def get("https://info.pleroma.site/activity.json", _, _, [ + {"accept", "application/activity+json"} + ]) do {:ok, %Tesla.Env{ status: 200, @@ -1185,7 +1207,9 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{status: 404, body: ""}} end - def get("https://info.pleroma.site/activity2.json", _, _, Accept: "application/activity+json") do + def get("https://info.pleroma.site/activity2.json", _, _, [ + {"accept", "application/activity+json"} + ]) do {:ok, %Tesla.Env{ status: 200, @@ -1197,7 +1221,9 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{status: 404, body: ""}} end - def get("https://info.pleroma.site/activity3.json", _, _, Accept: "application/activity+json") do + def get("https://info.pleroma.site/activity3.json", _, _, [ + {"accept", "application/activity+json"} + ]) do {:ok, %Tesla.Env{ status: 200, @@ -1273,6 +1299,21 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/rin.json")}} end + def get( + "https://channels.tests.funkwhale.audio/federation/music/uploads/42342395-0208-4fee-a38d-259a6dae0871", + _, + _, + _ + ) do + {:ok, + %Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/funkwhale_audio.json")}} + end + + def get("https://channels.tests.funkwhale.audio/federation/actors/compositions", _, _, _) do + {:ok, + %Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/funkwhale_channel.json")}} + end + def get("http://example.com/rel_me/error", _, _, _) do {:ok, %Tesla.Env{status: 404, body: ""}} end diff --git a/test/tasks/app_test.exs b/test/tasks/app_test.exs new file mode 100644 index 000000000..b8f03566d --- /dev/null +++ b/test/tasks/app_test.exs @@ -0,0 +1,65 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Mix.Tasks.Pleroma.AppTest do + use Pleroma.DataCase, async: true + + setup_all do + Mix.shell(Mix.Shell.Process) + + on_exit(fn -> + Mix.shell(Mix.Shell.IO) + end) + end + + describe "creates new app" do + test "with default scopes" do + name = "Some name" + redirect = "https://example.com" + Mix.Tasks.Pleroma.App.run(["create", "-n", name, "-r", redirect]) + + assert_app(name, redirect, ["read", "write", "follow", "push"]) + end + + test "with custom scopes" do + name = "Another name" + redirect = "https://example.com" + + Mix.Tasks.Pleroma.App.run([ + "create", + "-n", + name, + "-r", + redirect, + "-s", + "read,write,follow,push,admin" + ]) + + assert_app(name, redirect, ["read", "write", "follow", "push", "admin"]) + end + end + + test "with errors" do + Mix.Tasks.Pleroma.App.run(["create"]) + {:mix_shell, :error, ["Creating failed:"]} + {:mix_shell, :error, ["name: can't be blank"]} + {:mix_shell, :error, ["redirect_uris: can't be blank"]} + end + + defp assert_app(name, redirect, scopes) do + app = Repo.get_by(Pleroma.Web.OAuth.App, client_name: name) + + assert_received {:mix_shell, :info, [message]} + assert message == "#{name} successfully created:" + + assert_received {:mix_shell, :info, [message]} + assert message == "App client_id: #{app.client_id}" + + assert_received {:mix_shell, :info, [message]} + assert message == "App client_secret: #{app.client_secret}" + + assert app.scopes == scopes + assert app.redirect_uris == redirect + end +end diff --git a/test/tasks/config_test.exs b/test/tasks/config_test.exs index 3dee4f082..04bc947a9 100644 --- a/test/tasks/config_test.exs +++ b/test/tasks/config_test.exs @@ -38,7 +38,7 @@ defmodule Mix.Tasks.Pleroma.ConfigTest do on_exit(fn -> Application.put_env(:quack, :level, initial) end) end - test "settings are migrated to db" do + test "filtered settings are migrated to db" do assert Repo.all(ConfigDB) == [] Mix.Tasks.Pleroma.Config.migrate_to_db("test/fixtures/config/temp.secret.exs") @@ -47,6 +47,7 @@ defmodule Mix.Tasks.Pleroma.ConfigTest do config2 = ConfigDB.get_by_params(%{group: ":pleroma", key: ":second_setting"}) config3 = ConfigDB.get_by_params(%{group: ":quack", key: ":level"}) refute ConfigDB.get_by_params(%{group: ":pleroma", key: "Pleroma.Repo"}) + refute ConfigDB.get_by_params(%{group: ":postgrex", key: ":json_library"}) assert ConfigDB.from_binary(config1.value) == [key: "value", key2: [Repo]] assert ConfigDB.from_binary(config2.value) == [key: "value2", key2: ["Activity"]] diff --git a/test/tasks/count_statuses_test.exs b/test/tasks/count_statuses_test.exs index 73c2ea690..c5cd16960 100644 --- a/test/tasks/count_statuses_test.exs +++ b/test/tasks/count_statuses_test.exs @@ -13,11 +13,11 @@ defmodule Mix.Tasks.Pleroma.CountStatusesTest do test "counts statuses" do user = insert(:user) - {:ok, _} = CommonAPI.post(user, %{"status" => "test"}) - {:ok, _} = CommonAPI.post(user, %{"status" => "test2"}) + {:ok, _} = CommonAPI.post(user, %{status: "test"}) + {:ok, _} = CommonAPI.post(user, %{status: "test2"}) user2 = insert(:user) - {:ok, _} = CommonAPI.post(user2, %{"status" => "test3"}) + {:ok, _} = CommonAPI.post(user2, %{status: "test3"}) user = refresh_record(user) user2 = refresh_record(user2) diff --git a/test/tasks/database_test.exs b/test/tasks/database_test.exs index ed1c31d9c..883828d77 100644 --- a/test/tasks/database_test.exs +++ b/test/tasks/database_test.exs @@ -26,7 +26,7 @@ defmodule Mix.Tasks.Pleroma.DatabaseTest do describe "running remove_embedded_objects" do test "it replaces objects with references" do user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "test"}) + {:ok, activity} = CommonAPI.post(user, %{status: "test"}) new_data = Map.put(activity.data, "object", activity.object.data) {:ok, activity} = @@ -99,10 +99,10 @@ defmodule Mix.Tasks.Pleroma.DatabaseTest do test "it turns OrderedCollection likes into empty arrays" do [user, user2] = insert_pair(:user) - {:ok, %{id: id, object: object}} = CommonAPI.post(user, %{"status" => "test"}) - {:ok, %{object: object2}} = CommonAPI.post(user, %{"status" => "test test"}) + {:ok, %{id: id, object: object}} = CommonAPI.post(user, %{status: "test"}) + {:ok, %{object: object2}} = CommonAPI.post(user, %{status: "test test"}) - CommonAPI.favorite(id, user2) + CommonAPI.favorite(user2, id) likes = %{ "first" => diff --git a/test/tasks/digest_test.exs b/test/tasks/digest_test.exs index 96d762685..eefbc8936 100644 --- a/test/tasks/digest_test.exs +++ b/test/tasks/digest_test.exs @@ -25,7 +25,7 @@ defmodule Mix.Tasks.Pleroma.DigestTest do Enum.each(0..10, fn i -> {:ok, _activity} = CommonAPI.post(user1, %{ - "status" => "hey ##{i} @#{user2.nickname}!" + status: "hey ##{i} @#{user2.nickname}!" }) end) diff --git a/test/tasks/emoji_test.exs b/test/tasks/emoji_test.exs new file mode 100644 index 000000000..f5de3ef0e --- /dev/null +++ b/test/tasks/emoji_test.exs @@ -0,0 +1,226 @@ +defmodule Mix.Tasks.Pleroma.EmojiTest do + use ExUnit.Case, async: true + + import ExUnit.CaptureIO + import Tesla.Mock + + alias Mix.Tasks.Pleroma.Emoji + + describe "ls-packs" do + test "with default manifest as url" do + mock(fn + %{ + method: :get, + url: "https://git.pleroma.social/pleroma/emoji-index/raw/master/index.json" + } -> + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/emoji/packs/default-manifest.json") + } + end) + + capture_io(fn -> Emoji.run(["ls-packs"]) end) =~ + "https://finland.fi/wp-content/uploads/2017/06/finland-emojis.zip" + end + + test "with passed manifest as file" do + capture_io(fn -> + Emoji.run(["ls-packs", "-m", "test/fixtures/emoji/packs/manifest.json"]) + end) =~ "https://git.pleroma.social/pleroma/emoji-index/raw/master/packs/blobs_gg.zip" + end + end + + describe "get-packs" do + test "download pack from default manifest" do + mock(fn + %{ + method: :get, + url: "https://git.pleroma.social/pleroma/emoji-index/raw/master/index.json" + } -> + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/emoji/packs/default-manifest.json") + } + + %{ + method: :get, + url: "https://finland.fi/wp-content/uploads/2017/06/finland-emojis.zip" + } -> + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/emoji/packs/blank.png.zip") + } + + %{ + method: :get, + url: "https://git.pleroma.social/pleroma/emoji-index/raw/master/finmoji.json" + } -> + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/emoji/packs/finmoji.json") + } + end) + + assert capture_io(fn -> Emoji.run(["get-packs", "finmoji"]) end) =~ "Writing pack.json for" + + emoji_path = + Path.join( + Pleroma.Config.get!([:instance, :static_dir]), + "emoji" + ) + + assert File.exists?(Path.join([emoji_path, "finmoji", "pack.json"])) + on_exit(fn -> File.rm_rf!("test/instance_static/emoji/finmoji") end) + end + + test "pack not found" do + mock(fn + %{ + method: :get, + url: "https://git.pleroma.social/pleroma/emoji-index/raw/master/index.json" + } -> + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/emoji/packs/default-manifest.json") + } + end) + + assert capture_io(fn -> Emoji.run(["get-packs", "not_found"]) end) =~ + "No pack named \"not_found\" found" + end + + test "raise on bad sha256" do + mock(fn + %{ + method: :get, + url: "https://git.pleroma.social/pleroma/emoji-index/raw/master/packs/blobs_gg.zip" + } -> + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/emoji/packs/blank.png.zip") + } + end) + + assert_raise RuntimeError, ~r/^Bad SHA256 for blobs.gg/, fn -> + capture_io(fn -> + Emoji.run(["get-packs", "blobs.gg", "-m", "test/fixtures/emoji/packs/manifest.json"]) + end) + end + end + end + + describe "gen-pack" do + setup do + url = "https://finland.fi/wp-content/uploads/2017/06/finland-emojis.zip" + + mock(fn %{ + method: :get, + url: ^url + } -> + %Tesla.Env{status: 200, body: File.read!("test/fixtures/emoji/packs/blank.png.zip")} + end) + + {:ok, url: url} + end + + test "with default extensions", %{url: url} do + name = "pack1" + pack_json = "#{name}.json" + files_json = "#{name}_file.json" + refute File.exists?(pack_json) + refute File.exists?(files_json) + + captured = + capture_io(fn -> + Emoji.run([ + "gen-pack", + url, + "--name", + name, + "--license", + "license", + "--homepage", + "homepage", + "--description", + "description", + "--files", + files_json, + "--extensions", + ".png .gif" + ]) + end) + + assert captured =~ "#{pack_json} has been created with the pack1 pack" + assert captured =~ "Using .png .gif extensions" + + assert File.exists?(pack_json) + assert File.exists?(files_json) + + on_exit(fn -> + File.rm!(pack_json) + File.rm!(files_json) + end) + end + + test "with custom extensions and update existing files", %{url: url} do + name = "pack2" + pack_json = "#{name}.json" + files_json = "#{name}_file.json" + refute File.exists?(pack_json) + refute File.exists?(files_json) + + captured = + capture_io(fn -> + Emoji.run([ + "gen-pack", + url, + "--name", + name, + "--license", + "license", + "--homepage", + "homepage", + "--description", + "description", + "--files", + files_json, + "--extensions", + " .png .gif .jpeg " + ]) + end) + + assert captured =~ "#{pack_json} has been created with the pack2 pack" + assert captured =~ "Using .png .gif .jpeg extensions" + + assert File.exists?(pack_json) + assert File.exists?(files_json) + + captured = + capture_io(fn -> + Emoji.run([ + "gen-pack", + url, + "--name", + name, + "--license", + "license", + "--homepage", + "homepage", + "--description", + "description", + "--files", + files_json, + "--extensions", + " .png .gif .jpeg " + ]) + end) + + assert captured =~ "#{pack_json} has been updated with the pack2 pack" + + on_exit(fn -> + File.rm!(pack_json) + File.rm!(files_json) + end) + end + end +end diff --git a/test/tasks/refresh_counter_cache_test.exs b/test/tasks/refresh_counter_cache_test.exs index b63f44c08..851971a77 100644 --- a/test/tasks/refresh_counter_cache_test.exs +++ b/test/tasks/refresh_counter_cache_test.exs @@ -12,26 +12,26 @@ defmodule Mix.Tasks.Pleroma.RefreshCounterCacheTest do user = insert(:user) other_user = insert(:user) - CommonAPI.post(user, %{"visibility" => "public", "status" => "hey"}) + CommonAPI.post(user, %{visibility: "public", status: "hey"}) Enum.each(0..1, fn _ -> CommonAPI.post(user, %{ - "visibility" => "unlisted", - "status" => "hey" + visibility: "unlisted", + status: "hey" }) end) Enum.each(0..2, fn _ -> CommonAPI.post(user, %{ - "visibility" => "direct", - "status" => "hey @#{other_user.nickname}" + visibility: "direct", + status: "hey @#{other_user.nickname}" }) end) Enum.each(0..3, fn _ -> CommonAPI.post(user, %{ - "visibility" => "private", - "status" => "hey" + visibility: "private", + status: "hey" }) end) diff --git a/test/tasks/user_test.exs b/test/tasks/user_test.exs index b45f37263..4aa873f0b 100644 --- a/test/tasks/user_test.exs +++ b/test/tasks/user_test.exs @@ -3,15 +3,21 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.UserTest do + alias Pleroma.Activity + alias Pleroma.Object alias Pleroma.Repo + alias Pleroma.Tests.ObanHelpers alias Pleroma.User + alias Pleroma.Web.CommonAPI alias Pleroma.Web.OAuth.Authorization alias Pleroma.Web.OAuth.Token use Pleroma.DataCase + use Oban.Testing, repo: Pleroma.Repo - import Pleroma.Factory import ExUnit.CaptureIO + import Mock + import Pleroma.Factory setup_all do Mix.shell(Mix.Shell.Process) @@ -87,12 +93,39 @@ defmodule Mix.Tasks.Pleroma.UserTest do test "user is deleted" do user = insert(:user) - Mix.Tasks.Pleroma.User.run(["rm", user.nickname]) + with_mock Pleroma.Web.Federator, + publish: fn _ -> nil end do + Mix.Tasks.Pleroma.User.run(["rm", user.nickname]) + ObanHelpers.perform_all() - assert_received {:mix_shell, :info, [message]} - assert message =~ " deleted" + assert_received {:mix_shell, :info, [message]} + assert message =~ " deleted" + assert %{deactivated: true} = User.get_by_nickname(user.nickname) + + assert called(Pleroma.Web.Federator.publish(:_)) + end + end + + test "a remote user's create activity is deleted when the object has been pruned" do + user = insert(:user) + + {:ok, post} = CommonAPI.post(user, %{status: "uguu"}) + object = Object.normalize(post) + Object.prune(object) + + with_mock Pleroma.Web.Federator, + publish: fn _ -> nil end do + Mix.Tasks.Pleroma.User.run(["rm", user.nickname]) + ObanHelpers.perform_all() + + assert_received {:mix_shell, :info, [message]} + assert message =~ " deleted" + assert %{deactivated: true} = User.get_by_nickname(user.nickname) + + assert called(Pleroma.Web.Federator.publish(:_)) + end - refute User.get_by_nickname(user.nickname) + refute Activity.get_by_id(post.id) end test "no user to delete" do @@ -140,7 +173,7 @@ defmodule Mix.Tasks.Pleroma.UserTest do test "user is unsubscribed" do followed = insert(:user) user = insert(:user) - User.follow(user, followed, "accept") + User.follow(user, followed, :follow_accept) Mix.Tasks.Pleroma.User.run(["unsubscribe", user.nickname]) diff --git a/test/test_helper.exs b/test/test_helper.exs index 6b91d2b46..ee880e226 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -6,7 +6,10 @@ os_exclude = if :os.type() == {:unix, :darwin}, do: [skip_on_mac: true], else: [ ExUnit.start(exclude: [:federated | os_exclude]) Ecto.Adapters.SQL.Sandbox.mode(Pleroma.Repo, :manual) + Mox.defmock(Pleroma.ReverseProxy.ClientMock, for: Pleroma.ReverseProxy.Client) +Mox.defmock(Pleroma.GunMock, for: Pleroma.Gun) + {:ok, _} = Application.ensure_all_started(:ex_machina) ExUnit.after_suite(fn _results -> diff --git a/test/uploaders/s3_test.exs b/test/uploaders/s3_test.exs index 6950ccb25..d949c90a5 100644 --- a/test/uploaders/s3_test.exs +++ b/test/uploaders/s3_test.exs @@ -58,7 +58,7 @@ defmodule Pleroma.Uploaders.S3Test do name: "image-tet.jpg", content_type: "image/jpg", path: "test_folder/image-tet.jpg", - tempfile: Path.absname("test/fixtures/image_tmp.jpg") + tempfile: Path.absname("test/instance_static/add/shortcode.png") } [file_upload: file_upload] diff --git a/test/user_invite_token_test.exs b/test/user_invite_token_test.exs index 4f70ef337..63f18f13c 100644 --- a/test/user_invite_token_test.exs +++ b/test/user_invite_token_test.exs @@ -4,7 +4,6 @@ defmodule Pleroma.UserInviteTokenTest do use ExUnit.Case, async: true - use Pleroma.DataCase alias Pleroma.UserInviteToken describe "valid_invite?/1 one time invites" do @@ -64,7 +63,6 @@ defmodule Pleroma.UserInviteTokenTest do test "expires yesterday returns false", %{invite: invite} do invite = %{invite | expires_at: Date.add(Date.utc_today(), -1)} - invite = Repo.insert!(invite) refute UserInviteToken.valid_invite?(invite) end end @@ -82,7 +80,6 @@ defmodule Pleroma.UserInviteTokenTest do test "overdue date and less uses returns false", %{invite: invite} do invite = %{invite | expires_at: Date.add(Date.utc_today(), -1)} - invite = Repo.insert!(invite) refute UserInviteToken.valid_invite?(invite) end @@ -93,7 +90,6 @@ defmodule Pleroma.UserInviteTokenTest do test "overdue date with more uses returns false", %{invite: invite} do invite = %{invite | expires_at: Date.add(Date.utc_today(), -1), uses: 5} - invite = Repo.insert!(invite) refute UserInviteToken.valid_invite?(invite) end end diff --git a/test/user_search_test.exs b/test/user_search_test.exs index cb847b516..17c63322a 100644 --- a/test/user_search_test.exs +++ b/test/user_search_test.exs @@ -172,6 +172,7 @@ defmodule Pleroma.UserSearchTest do |> Map.put(:search_rank, nil) |> Map.put(:search_type, nil) |> Map.put(:last_digest_emailed_at, nil) + |> Map.put(:multi_factor_authentication_settings, nil) |> Map.put(:notification_settings, nil) assert user == expected diff --git a/test/user_test.exs b/test/user_test.exs index 119a36ec1..6b9df60a4 100644 --- a/test/user_test.exs +++ b/test/user_test.exs @@ -15,7 +15,6 @@ defmodule Pleroma.UserTest do use Pleroma.DataCase use Oban.Testing, repo: Pleroma.Repo - import Mock import Pleroma.Factory import ExUnit.CaptureLog @@ -86,7 +85,7 @@ defmodule Pleroma.UserTest do {:ok, user: insert(:user)} end - test "outgoing_relations_ap_ids/1", %{user: user} do + test "outgoing_relationships_ap_ids/1", %{user: user} do rel_types = [:block, :mute, :notification_mute, :reblog_mute, :inverse_subscription] ap_ids_by_rel = @@ -124,10 +123,10 @@ defmodule Pleroma.UserTest do assert ap_ids_by_rel[:inverse_subscription] == Enum.sort(Enum.map(User.subscriber_users(user), & &1.ap_id)) - outgoing_relations_ap_ids = User.outgoing_relations_ap_ids(user, rel_types) + outgoing_relationships_ap_ids = User.outgoing_relationships_ap_ids(user, rel_types) assert ap_ids_by_rel == - Enum.into(outgoing_relations_ap_ids, %{}, fn {k, v} -> {k, Enum.sort(v)} end) + Enum.into(outgoing_relationships_ap_ids, %{}, fn {k, v} -> {k, Enum.sort(v)} end) end end @@ -194,7 +193,8 @@ defmodule Pleroma.UserTest do CommonAPI.follow(pending_follower, locked) CommonAPI.follow(pending_follower, locked) CommonAPI.follow(accepted_follower, locked) - Pleroma.FollowingRelationship.update(accepted_follower, locked, "accept") + + Pleroma.FollowingRelationship.update(accepted_follower, locked, :follow_accept) assert [^pending_follower] = User.get_follow_requests(locked) end @@ -319,7 +319,7 @@ defmodule Pleroma.UserTest do following_address: "http://localhost:4001/users/fuser2/following" }) - {:ok, user} = User.follow(user, followed, "accept") + {:ok, user} = User.follow(user, followed, :follow_accept) {:ok, user, _activity} = User.unfollow(user, followed) @@ -332,7 +332,7 @@ defmodule Pleroma.UserTest do followed = insert(:user) user = insert(:user) - {:ok, user} = User.follow(user, followed, "accept") + {:ok, user} = User.follow(user, followed, :follow_accept) assert User.following(user) == [user.follower_address, followed.follower_address] @@ -353,7 +353,7 @@ defmodule Pleroma.UserTest do test "test if a user is following another user" do followed = insert(:user) user = insert(:user) - User.follow(user, followed, "accept") + User.follow(user, followed, :follow_accept) assert User.following?(user, followed) refute User.following?(followed, user) @@ -581,7 +581,7 @@ defmodule Pleroma.UserTest do {:ok, user} = User.get_or_fetch_by_ap_id("http://mastodon.example.org/users/admin") - assert user.source_data["endpoints"] + assert user.inbox refute user.last_refreshed_at == orig_user.last_refreshed_at end @@ -609,7 +609,7 @@ defmodule Pleroma.UserTest do ) <> "/followers" end - describe "remote user creation changeset" do + describe "remote user changeset" do @valid_remote %{ bio: "hello", name: "Someone", @@ -621,28 +621,28 @@ defmodule Pleroma.UserTest do setup do: clear_config([:instance, :user_name_length]) test "it confirms validity" do - cs = User.remote_user_creation(@valid_remote) + cs = User.remote_user_changeset(@valid_remote) assert cs.valid? end test "it sets the follower_adress" do - cs = User.remote_user_creation(@valid_remote) + cs = User.remote_user_changeset(@valid_remote) # remote users get a fake local follower address assert cs.changes.follower_address == User.ap_followers(%User{nickname: @valid_remote[:nickname]}) end test "it enforces the fqn format for nicknames" do - cs = User.remote_user_creation(%{@valid_remote | nickname: "bla"}) + cs = User.remote_user_changeset(%{@valid_remote | nickname: "bla"}) assert Ecto.Changeset.get_field(cs, :local) == false assert cs.changes.avatar refute cs.valid? end test "it has required fields" do - [:name, :ap_id] + [:ap_id] |> Enum.each(fn field -> - cs = User.remote_user_creation(Map.delete(@valid_remote, field)) + cs = User.remote_user_changeset(Map.delete(@valid_remote, field)) refute cs.valid? end) end @@ -755,8 +755,8 @@ defmodule Pleroma.UserTest do ] {:ok, job} = User.follow_import(user1, identifiers) - result = ObanHelpers.perform(job) + assert {:ok, result} = ObanHelpers.perform(job) assert is_list(result) assert result == [user2, user3] end @@ -978,14 +978,26 @@ defmodule Pleroma.UserTest do ] {:ok, job} = User.blocks_import(user1, identifiers) - result = ObanHelpers.perform(job) + assert {:ok, result} = ObanHelpers.perform(job) assert is_list(result) assert result == [user2, user3] end end describe "get_recipients_from_activity" do + test "works for announces" do + actor = insert(:user) + user = insert(:user, local: true) + + {:ok, activity} = CommonAPI.post(actor, %{status: "hello"}) + {:ok, announce, _} = CommonAPI.repeat(activity.id, user) + + recipients = User.get_recipients_from_activity(announce) + + assert user in recipients + end + test "get recipients" do actor = insert(:user) user = insert(:user, local: true) @@ -995,7 +1007,7 @@ defmodule Pleroma.UserTest do {:ok, activity} = CommonAPI.post(actor, %{ - "status" => "hey @#{addressed.nickname} @#{addressed_remote.nickname}" + status: "hey @#{addressed.nickname} @#{addressed_remote.nickname}" }) assert Enum.map([actor, addressed], & &1.ap_id) -- @@ -1017,7 +1029,7 @@ defmodule Pleroma.UserTest do {:ok, activity} = CommonAPI.post(actor, %{ - "status" => "hey @#{addressed.nickname}" + status: "hey @#{addressed.nickname}" }) assert Enum.map([actor, addressed], & &1.ap_id) -- @@ -1078,7 +1090,7 @@ defmodule Pleroma.UserTest do {:ok, user2} = User.follow(user2, user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "hey @#{user2.nickname}"}) + {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{user2.nickname}"}) activity = Repo.preload(activity, :bookmark) @@ -1114,24 +1126,15 @@ defmodule Pleroma.UserTest do setup do: clear_config([:instance, :federating]) test ".delete_user_activities deletes all create activities", %{user: user} do - {:ok, activity} = CommonAPI.post(user, %{"status" => "2hu"}) + {:ok, activity} = CommonAPI.post(user, %{status: "2hu"}) User.delete_user_activities(user) - # TODO: Remove favorites, repeats, delete activities. + # TODO: Test removal favorites, repeats, delete activities. refute Activity.get_by_id(activity.id) end - test "it deletes deactivated user" do - {:ok, user} = insert(:user, deactivated: true) |> User.set_cache() - - {:ok, job} = User.delete(user) - {:ok, _user} = ObanHelpers.perform(job) - - refute User.get_by_id(user.id) - end - - test "it deletes a user, all follow relationships and all activities", %{user: user} do + test "it deactivates a user, all follow relationships and all activities", %{user: user} do follower = insert(:user) {:ok, follower} = User.follow(follower, user) @@ -1141,8 +1144,8 @@ defmodule Pleroma.UserTest do object_two = insert(:note, user: follower) activity_two = insert(:note_activity, user: follower, note: object_two) - {:ok, like, _} = CommonAPI.favorite(activity_two.id, user) - {:ok, like_two, _} = CommonAPI.favorite(activity.id, follower) + {:ok, like} = CommonAPI.favorite(user, activity_two.id) + {:ok, like_two} = CommonAPI.favorite(follower, activity.id) {:ok, repeat, _} = CommonAPI.repeat(activity_two.id, user) {:ok, job} = User.delete(user) @@ -1151,8 +1154,7 @@ defmodule Pleroma.UserTest do follower = User.get_cached_by_id(follower.id) refute User.following?(follower, user) - refute User.get_by_id(user.id) - assert {:ok, nil} == Cachex.get(:user_cache, "ap_id:#{user.ap_id}") + assert %{deactivated: true} = User.get_by_id(user.id) user_activities = user.ap_id @@ -1167,89 +1169,12 @@ defmodule Pleroma.UserTest do refute Activity.get_by_id(like_two.id) refute Activity.get_by_id(repeat.id) end - - test_with_mock "it sends out User Delete activity", - %{user: user}, - Pleroma.Web.ActivityPub.Publisher, - [:passthrough], - [] do - Pleroma.Config.put([:instance, :federating], true) - - {:ok, follower} = User.get_or_fetch_by_ap_id("http://mastodon.example.org/users/admin") - {:ok, _} = User.follow(follower, user) - - {:ok, job} = User.delete(user) - {:ok, _user} = ObanHelpers.perform(job) - - assert ObanHelpers.member?( - %{ - "op" => "publish_one", - "params" => %{ - "inbox" => "http://mastodon.example.org/inbox", - "id" => "pleroma:fakeid" - } - }, - all_enqueued(worker: Pleroma.Workers.PublisherWorker) - ) - end end test "get_public_key_for_ap_id fetches a user that's not in the db" do assert {:ok, _key} = User.get_public_key_for_ap_id("http://mastodon.example.org/users/admin") end - describe "insert or update a user from given data" do - test "with normal data" do - user = insert(:user, %{nickname: "nick@name.de"}) - data = %{ap_id: user.ap_id <> "xxx", name: user.name, nickname: user.nickname} - - assert {:ok, %User{}} = User.insert_or_update_user(data) - end - - test "with overly long fields" do - current_max_length = Pleroma.Config.get([:instance, :account_field_value_length], 255) - user = insert(:user, nickname: "nickname@supergood.domain") - - data = %{ - ap_id: user.ap_id, - name: user.name, - nickname: user.nickname, - fields: [ - %{"name" => "myfield", "value" => String.duplicate("h", current_max_length + 1)} - ] - } - - assert {:ok, %User{}} = User.insert_or_update_user(data) - end - - test "with an overly long bio" do - current_max_length = Pleroma.Config.get([:instance, :user_bio_length], 5000) - user = insert(:user, nickname: "nickname@supergood.domain") - - data = %{ - ap_id: user.ap_id, - name: user.name, - nickname: user.nickname, - bio: String.duplicate("h", current_max_length + 1) - } - - assert {:ok, %User{}} = User.insert_or_update_user(data) - end - - test "with an overly long display name" do - current_max_length = Pleroma.Config.get([:instance, :user_name_length], 100) - user = insert(:user, nickname: "nickname@supergood.domain") - - data = %{ - ap_id: user.ap_id, - name: String.duplicate("h", current_max_length + 1), - nickname: user.nickname - } - - assert {:ok, %User{}} = User.insert_or_update_user(data) - end - end - describe "per-user rich-text filtering" do test "html_filter_policy returns default policies, when rich-text is enabled" do user = insert(:user) @@ -1404,7 +1329,7 @@ defmodule Pleroma.UserTest do bio = "A.k.a. @nick@domain.com" expected_text = - ~s(A.k.a. <span class="h-card"><a data-user="#{remote_user.id}" class="u-url mention" href="#{ + ~s(A.k.a. <span class="h-card"><a class="u-url mention" data-user="#{remote_user.id}" href="#{ remote_user.ap_id }" rel="ugc">@<span>nick@domain.com</span></a></span>) @@ -1486,7 +1411,7 @@ defmodule Pleroma.UserTest do {:ok, _} = CommonAPI.post(user, %{ - "status" => "hey @#{to.nickname}" + status: "hey @#{to.nickname}" }) end) @@ -1518,12 +1443,12 @@ defmodule Pleroma.UserTest do Enum.each(recipients, fn to -> {:ok, _} = CommonAPI.post(sender, %{ - "status" => "hey @#{to.nickname}" + status: "hey @#{to.nickname}" }) {:ok, _} = CommonAPI.post(sender, %{ - "status" => "hey again @#{to.nickname}" + status: "hey again @#{to.nickname}" }) end) diff --git a/test/web/activity_pub/activity_pub_controller_test.exs b/test/web/activity_pub/activity_pub_controller_test.exs index 573853afa..c432c90e3 100644 --- a/test/web/activity_pub/activity_pub_controller_test.exs +++ b/test/web/activity_pub/activity_pub_controller_test.exs @@ -341,7 +341,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do test "cached purged after activity deletion", %{conn: conn} do user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "cofe"}) + {:ok, activity} = CommonAPI.post(user, %{status: "cofe"}) uuid = String.split(activity.data["id"], "/") |> List.last() @@ -765,51 +765,110 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do end end - describe "POST /users/:nickname/outbox" do - test "it rejects posts from other users / unauuthenticated users", %{conn: conn} do - data = File.read!("test/fixtures/activitypub-client-post-activity.json") |> Poison.decode!() + describe "POST /users/:nickname/outbox (C2S)" do + setup do + [ + activity: %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "type" => "Create", + "object" => %{"type" => "Note", "content" => "AP C2S test"}, + "to" => "https://www.w3.org/ns/activitystreams#Public", + "cc" => [] + } + ] + end + + test "it rejects posts from other users / unauthenticated users", %{ + conn: conn, + activity: activity + } do user = insert(:user) other_user = insert(:user) conn = put_req_header(conn, "content-type", "application/activity+json") conn - |> post("/users/#{user.nickname}/outbox", data) + |> post("/users/#{user.nickname}/outbox", activity) |> json_response(403) conn |> assign(:user, other_user) - |> post("/users/#{user.nickname}/outbox", data) + |> post("/users/#{user.nickname}/outbox", activity) |> json_response(403) end - test "it inserts an incoming create activity into the database", %{conn: conn} do - data = File.read!("test/fixtures/activitypub-client-post-activity.json") |> Poison.decode!() + test "it inserts an incoming create activity into the database", %{ + conn: conn, + activity: activity + } do user = insert(:user) - conn = + result = conn |> assign(:user, user) |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{user.nickname}/outbox", data) - - result = json_response(conn, 201) + |> post("/users/#{user.nickname}/outbox", activity) + |> json_response(201) assert Activity.get_by_ap_id(result["id"]) + assert result["object"] + assert %Object{data: object} = Object.normalize(result["object"]) + assert object["content"] == activity["object"]["content"] end - test "it rejects an incoming activity with bogus type", %{conn: conn} do - data = File.read!("test/fixtures/activitypub-client-post-activity.json") |> Poison.decode!() + test "it rejects anything beyond 'Note' creations", %{conn: conn, activity: activity} do user = insert(:user) - data = - data - |> Map.put("type", "BadType") + activity = + activity + |> put_in(["object", "type"], "Benis") + + _result = + conn + |> assign(:user, user) + |> put_req_header("content-type", "application/activity+json") + |> post("/users/#{user.nickname}/outbox", activity) + |> json_response(400) + end + + test "it inserts an incoming sensitive activity into the database", %{ + conn: conn, + activity: activity + } do + user = insert(:user) + conn = assign(conn, :user, user) + object = Map.put(activity["object"], "sensitive", true) + activity = Map.put(activity, "object", object) + + response = + conn + |> put_req_header("content-type", "application/activity+json") + |> post("/users/#{user.nickname}/outbox", activity) + |> json_response(201) + + assert Activity.get_by_ap_id(response["id"]) + assert response["object"] + assert %Object{data: response_object} = Object.normalize(response["object"]) + assert response_object["sensitive"] == true + assert response_object["content"] == activity["object"]["content"] + + representation = + conn + |> put_req_header("accept", "application/activity+json") + |> get(response["id"]) + |> json_response(200) + + assert representation["object"]["sensitive"] == true + end + + test "it rejects an incoming activity with bogus type", %{conn: conn, activity: activity} do + user = insert(:user) + activity = Map.put(activity, "type", "BadType") conn = conn |> assign(:user, user) |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{user.nickname}/outbox", data) + |> post("/users/#{user.nickname}/outbox", activity) assert json_response(conn, 400) end @@ -1019,12 +1078,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do assert result["totalItems"] == 15 end - test "returns 403 if requester is not logged in", %{conn: conn} do + test "does not require authentication", %{conn: conn} do user = insert(:user) conn |> get("/users/#{user.nickname}/followers") - |> json_response(403) + |> json_response(200) end end @@ -1116,12 +1175,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do assert result["totalItems"] == 15 end - test "returns 403 if requester is not logged in", %{conn: conn} do + test "does not require authentication", %{conn: conn} do user = insert(:user) conn |> get("/users/#{user.nickname}/following") - |> json_response(403) + |> json_response(200) end end @@ -1239,16 +1298,56 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do filename: "an_image.jpg" } - conn = + object = conn |> assign(:user, user) |> post("/api/ap/upload_media", %{"file" => image, "description" => desc}) + |> json_response(:created) - assert object = json_response(conn, :created) assert object["name"] == desc assert object["type"] == "Document" assert object["actor"] == user.ap_id + assert [%{"href" => object_href, "mediaType" => object_mediatype}] = object["url"] + assert is_binary(object_href) + assert object_mediatype == "image/jpeg" + + activity_request = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "type" => "Create", + "object" => %{ + "type" => "Note", + "content" => "AP C2S test, attachment", + "attachment" => [object] + }, + "to" => "https://www.w3.org/ns/activitystreams#Public", + "cc" => [] + } + + activity_response = + conn + |> assign(:user, user) + |> post("/users/#{user.nickname}/outbox", activity_request) + |> json_response(:created) + + assert activity_response["id"] + assert activity_response["object"] + assert activity_response["actor"] == user.ap_id + + assert %Object{data: %{"attachment" => [attachment]}} = + Object.normalize(activity_response["object"]) + + assert attachment["type"] == "Document" + assert attachment["name"] == desc + + assert [ + %{ + "href" => ^object_href, + "type" => "Link", + "mediaType" => ^object_mediatype + } + ] = attachment["url"] + # Fails if unauthenticated conn |> post("/api/ap/upload_media", %{"file" => image, "description" => desc}) |> json_response(403) diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs index a43dd34f0..77bd07edf 100644 --- a/test/web/activity_pub/activity_pub_test.exs +++ b/test/web/activity_pub/activity_pub_test.exs @@ -16,11 +16,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.AdminAPI.AccountView alias Pleroma.Web.CommonAPI - alias Pleroma.Web.Federator + import ExUnit.CaptureLog + import Mock import Pleroma.Factory import Tesla.Mock - import Mock setup do mock(fn env -> apply(HttpRequestMock, :request, [env]) end) @@ -32,7 +32,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do describe "streaming out participations" do test "it streams them out" do user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"}) + {:ok, activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"}) {:ok, conversation} = Pleroma.Conversation.create_or_bump_for(activity) @@ -56,8 +56,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do stream: fn _, _ -> nil end do {:ok, activity} = CommonAPI.post(user_one, %{ - "status" => "@#{user_two.nickname}", - "visibility" => "direct" + status: "@#{user_two.nickname}", + visibility: "direct" }) conversation = @@ -74,15 +74,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do test "it restricts by the appropriate visibility" do user = insert(:user) - {:ok, public_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"}) + {:ok, public_activity} = CommonAPI.post(user, %{status: ".", visibility: "public"}) - {:ok, direct_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"}) + {:ok, direct_activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"}) - {:ok, unlisted_activity} = - CommonAPI.post(user, %{"status" => ".", "visibility" => "unlisted"}) + {:ok, unlisted_activity} = CommonAPI.post(user, %{status: ".", visibility: "unlisted"}) - {:ok, private_activity} = - CommonAPI.post(user, %{"status" => ".", "visibility" => "private"}) + {:ok, private_activity} = CommonAPI.post(user, %{status: ".", visibility: "private"}) activities = ActivityPub.fetch_activities([], %{:visibility => "direct", "actor_id" => user.ap_id}) @@ -118,15 +116,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do test "it excludes by the appropriate visibility" do user = insert(:user) - {:ok, public_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"}) + {:ok, public_activity} = CommonAPI.post(user, %{status: ".", visibility: "public"}) - {:ok, direct_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"}) + {:ok, direct_activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"}) - {:ok, unlisted_activity} = - CommonAPI.post(user, %{"status" => ".", "visibility" => "unlisted"}) + {:ok, unlisted_activity} = CommonAPI.post(user, %{status: ".", visibility: "unlisted"}) - {:ok, private_activity} = - CommonAPI.post(user, %{"status" => ".", "visibility" => "private"}) + {:ok, private_activity} = CommonAPI.post(user, %{status: ".", visibility: "private"}) activities = ActivityPub.fetch_activities([], %{ @@ -180,7 +176,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do {:ok, user} = ActivityPub.make_user_from_ap_id(user_id) assert user.ap_id == user_id assert user.nickname == "admin@mastodon.example.org" - assert user.source_data assert user.ap_enabled assert user.follower_address == "http://mastodon.example.org/users/admin/followers" end @@ -194,9 +189,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do test "it fetches the appropriate tag-restricted posts" do user = insert(:user) - {:ok, status_one} = CommonAPI.post(user, %{"status" => ". #test"}) - {:ok, status_two} = CommonAPI.post(user, %{"status" => ". #essais"}) - {:ok, status_three} = CommonAPI.post(user, %{"status" => ". #test #reject"}) + {:ok, status_one} = CommonAPI.post(user, %{status: ". #test"}) + {:ok, status_two} = CommonAPI.post(user, %{status: ". #essais"}) + {:ok, status_three} = CommonAPI.post(user, %{status: ". #test #reject"}) fetch_one = ActivityPub.fetch_activities([], %{"type" => "Create", "tag" => "test"}) @@ -433,26 +428,26 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do {:ok, _} = CommonAPI.post(User.get_cached_by_id(user.id), %{ - "status" => "1", - "visibility" => "public" + status: "1", + visibility: "public" }) {:ok, _} = CommonAPI.post(User.get_cached_by_id(user.id), %{ - "status" => "2", - "visibility" => "unlisted" + status: "2", + visibility: "unlisted" }) {:ok, _} = CommonAPI.post(User.get_cached_by_id(user.id), %{ - "status" => "2", - "visibility" => "private" + status: "2", + visibility: "private" }) {:ok, _} = CommonAPI.post(User.get_cached_by_id(user.id), %{ - "status" => "3", - "visibility" => "direct" + status: "3", + visibility: "direct" }) user = User.get_cached_by_id(user.id) @@ -463,27 +458,27 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do user = insert(:user) user2 = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "1", "visibility" => "public"}) + {:ok, activity} = CommonAPI.post(user, %{status: "1", visibility: "public"}) ap_id = activity.data["id"] - reply_data = %{"status" => "1", "in_reply_to_status_id" => activity.id} + reply_data = %{status: "1", in_reply_to_status_id: activity.id} # public - {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "public")) + {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, :visibility, "public")) assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id) assert object.data["repliesCount"] == 1 # unlisted - {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "unlisted")) + {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, :visibility, "unlisted")) assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id) assert object.data["repliesCount"] == 2 # private - {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "private")) + {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, :visibility, "private")) assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id) assert object.data["repliesCount"] == 2 # direct - {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "direct")) + {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, :visibility, "direct")) assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id) assert object.data["repliesCount"] == 2 end @@ -570,13 +565,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do {:ok, _user_relationship} = User.block(blocker, blockee) - {:ok, activity_one} = CommonAPI.post(friend, %{"status" => "hey!"}) + {:ok, activity_one} = CommonAPI.post(friend, %{status: "hey!"}) - {:ok, activity_two} = CommonAPI.post(friend, %{"status" => "hey! @#{blockee.nickname}"}) + {:ok, activity_two} = CommonAPI.post(friend, %{status: "hey! @#{blockee.nickname}"}) - {:ok, activity_three} = CommonAPI.post(blockee, %{"status" => "hey! @#{friend.nickname}"}) + {:ok, activity_three} = CommonAPI.post(blockee, %{status: "hey! @#{friend.nickname}"}) - {:ok, activity_four} = CommonAPI.post(blockee, %{"status" => "hey! @#{blocker.nickname}"}) + {:ok, activity_four} = CommonAPI.post(blockee, %{status: "hey! @#{blocker.nickname}"}) activities = ActivityPub.fetch_activities([], %{"blocking_user" => blocker}) @@ -593,9 +588,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do {:ok, _user_relationship} = User.block(blocker, blockee) - {:ok, activity_one} = CommonAPI.post(friend, %{"status" => "hey!"}) + {:ok, activity_one} = CommonAPI.post(friend, %{status: "hey!"}) - {:ok, activity_two} = CommonAPI.post(blockee, %{"status" => "hey! @#{friend.nickname}"}) + {:ok, activity_two} = CommonAPI.post(blockee, %{status: "hey! @#{friend.nickname}"}) {:ok, activity_three, _} = CommonAPI.repeat(activity_two.id, friend) @@ -775,10 +770,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do test "doesn't retrieve unlisted activities" do user = insert(:user) - {:ok, _unlisted_activity} = - CommonAPI.post(user, %{"status" => "yeah", "visibility" => "unlisted"}) + {:ok, _unlisted_activity} = CommonAPI.post(user, %{status: "yeah", visibility: "unlisted"}) - {:ok, listed_activity} = CommonAPI.post(user, %{"status" => "yeah"}) + {:ok, listed_activity} = CommonAPI.post(user, %{status: "yeah"}) [activity] = ActivityPub.fetch_public_activities() @@ -874,250 +868,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do end end - describe "react to an object" do - test_with_mock "sends an activity to federation", Federator, [:passthrough], [] do - Config.put([:instance, :federating], true) - user = insert(:user) - reactor = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "YASSSS queen slay"}) - assert object = Object.normalize(activity) - - {:ok, reaction_activity, _object} = ActivityPub.react_with_emoji(reactor, object, "🔥") - - assert called(Federator.publish(reaction_activity)) - end - - test "adds an emoji reaction activity to the db" do - user = insert(:user) - reactor = insert(:user) - third_user = insert(:user) - fourth_user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "YASSSS queen slay"}) - assert object = Object.normalize(activity) - - {:ok, reaction_activity, object} = ActivityPub.react_with_emoji(reactor, object, "🔥") - - assert reaction_activity - - assert reaction_activity.data["actor"] == reactor.ap_id - assert reaction_activity.data["type"] == "EmojiReact" - assert reaction_activity.data["content"] == "🔥" - assert reaction_activity.data["object"] == object.data["id"] - assert reaction_activity.data["to"] == [User.ap_followers(reactor), activity.data["actor"]] - assert reaction_activity.data["context"] == object.data["context"] - assert object.data["reaction_count"] == 1 - assert object.data["reactions"] == [["🔥", [reactor.ap_id]]] - - {:ok, _reaction_activity, object} = ActivityPub.react_with_emoji(third_user, object, "☕") - - assert object.data["reaction_count"] == 2 - assert object.data["reactions"] == [["🔥", [reactor.ap_id]], ["☕", [third_user.ap_id]]] - - {:ok, _reaction_activity, object} = ActivityPub.react_with_emoji(fourth_user, object, "🔥") - - assert object.data["reaction_count"] == 3 - - assert object.data["reactions"] == [ - ["🔥", [fourth_user.ap_id, reactor.ap_id]], - ["☕", [third_user.ap_id]] - ] - end - - test "reverts emoji reaction on error" do - [user, reactor] = insert_list(2, :user) - - {:ok, activity} = CommonAPI.post(user, %{"status" => "Status"}) - object = Object.normalize(activity) - - with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do - assert {:error, :reverted} = ActivityPub.react_with_emoji(reactor, object, "😀") - end - - object = Object.get_by_ap_id(object.data["id"]) - refute object.data["reaction_count"] - refute object.data["reactions"] - end - end - - describe "unreacting to an object" do - test_with_mock "sends an activity to federation", Federator, [:passthrough], [] do - Config.put([:instance, :federating], true) - user = insert(:user) - reactor = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "YASSSS queen slay"}) - assert object = Object.normalize(activity) - - {:ok, reaction_activity, _object} = ActivityPub.react_with_emoji(reactor, object, "🔥") - - assert called(Federator.publish(reaction_activity)) - - {:ok, unreaction_activity, _object} = - ActivityPub.unreact_with_emoji(reactor, reaction_activity.data["id"]) - - assert called(Federator.publish(unreaction_activity)) - end - - test "adds an undo activity to the db" do - user = insert(:user) - reactor = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "YASSSS queen slay"}) - assert object = Object.normalize(activity) - - {:ok, reaction_activity, _object} = ActivityPub.react_with_emoji(reactor, object, "🔥") - - {:ok, unreaction_activity, _object} = - ActivityPub.unreact_with_emoji(reactor, reaction_activity.data["id"]) - - assert unreaction_activity.actor == reactor.ap_id - assert unreaction_activity.data["object"] == reaction_activity.data["id"] - - object = Object.get_by_ap_id(object.data["id"]) - assert object.data["reaction_count"] == 0 - assert object.data["reactions"] == [] - end - - test "reverts emoji unreact on error" do - [user, reactor] = insert_list(2, :user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "Status"}) - object = Object.normalize(activity) - - {:ok, reaction_activity, _object} = ActivityPub.react_with_emoji(reactor, object, "😀") - - with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do - assert {:error, :reverted} = - ActivityPub.unreact_with_emoji(reactor, reaction_activity.data["id"]) - end - - object = Object.get_by_ap_id(object.data["id"]) - - assert object.data["reaction_count"] == 1 - assert object.data["reactions"] == [["😀", [reactor.ap_id]]] - end - end - - describe "like an object" do - test_with_mock "sends an activity to federation", Federator, [:passthrough], [] do - Config.put([:instance, :federating], true) - note_activity = insert(:note_activity) - assert object_activity = Object.normalize(note_activity) - - user = insert(:user) - - {:ok, like_activity, _object} = ActivityPub.like(user, object_activity) - assert called(Federator.publish(like_activity)) - end - - test "returns exist activity if object already liked" do - note_activity = insert(:note_activity) - assert object_activity = Object.normalize(note_activity) - - user = insert(:user) - - {:ok, like_activity, _object} = ActivityPub.like(user, object_activity) - - {:ok, like_activity_exist, _object} = ActivityPub.like(user, object_activity) - assert like_activity == like_activity_exist - end - - test "reverts like activity on error" do - note_activity = insert(:note_activity) - object = Object.normalize(note_activity) - user = insert(:user) - - with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do - assert {:error, :reverted} = ActivityPub.like(user, object) - end - - assert Repo.aggregate(Activity, :count, :id) == 1 - assert Repo.get(Object, object.id) == object - end - - test "adds a like activity to the db" do - note_activity = insert(:note_activity) - assert object = Object.normalize(note_activity) - - user = insert(:user) - user_two = insert(:user) - - {:ok, like_activity, object} = ActivityPub.like(user, object) - - assert like_activity.data["actor"] == user.ap_id - assert like_activity.data["type"] == "Like" - assert like_activity.data["object"] == object.data["id"] - assert like_activity.data["to"] == [User.ap_followers(user), note_activity.data["actor"]] - assert like_activity.data["context"] == object.data["context"] - assert object.data["like_count"] == 1 - assert object.data["likes"] == [user.ap_id] - - # Just return the original activity if the user already liked it. - {:ok, same_like_activity, object} = ActivityPub.like(user, object) - - assert like_activity == same_like_activity - assert object.data["likes"] == [user.ap_id] - assert object.data["like_count"] == 1 - - {:ok, _like_activity, object} = ActivityPub.like(user_two, object) - assert object.data["like_count"] == 2 - end - end - - describe "unliking" do - test_with_mock "sends an activity to federation", Federator, [:passthrough], [] do - Config.put([:instance, :federating], true) - - note_activity = insert(:note_activity) - object = Object.normalize(note_activity) - user = insert(:user) - - {:ok, object} = ActivityPub.unlike(user, object) - refute called(Federator.publish()) - - {:ok, _like_activity, object} = ActivityPub.like(user, object) - assert object.data["like_count"] == 1 - - {:ok, unlike_activity, _, object} = ActivityPub.unlike(user, object) - assert object.data["like_count"] == 0 - - assert called(Federator.publish(unlike_activity)) - end - - test "reverts unliking on error" do - note_activity = insert(:note_activity) - object = Object.normalize(note_activity) - user = insert(:user) - - {:ok, like_activity, object} = ActivityPub.like(user, object) - assert object.data["like_count"] == 1 - - with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do - assert {:error, :reverted} = ActivityPub.unlike(user, object) - end - - assert Object.get_by_ap_id(object.data["id"]) == object - assert object.data["like_count"] == 1 - assert Activity.get_by_id(like_activity.id) - end - - test "unliking a previously liked object" do - note_activity = insert(:note_activity) - object = Object.normalize(note_activity) - user = insert(:user) - - # Unliking something that hasn't been liked does nothing - {:ok, object} = ActivityPub.unlike(user, object) - assert object.data["like_count"] == 0 - - {:ok, like_activity, object} = ActivityPub.like(user, object) - assert object.data["like_count"] == 1 - - {:ok, unlike_activity, _, object} = ActivityPub.unlike(user, object) - assert object.data["like_count"] == 0 - - assert Activity.get_by_id(like_activity.id) == nil - assert note_activity.actor in unlike_activity.recipients - end - end - describe "announcing an object" do test "adds an announce activity to the db" do note_activity = insert(:note_activity) @@ -1157,7 +907,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do describe "announcing a private object" do test "adds an announce activity to the db if the audience is not widened" do user = insert(:user) - {:ok, note_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "private"}) + {:ok, note_activity} = CommonAPI.post(user, %{status: ".", visibility: "private"}) object = Object.normalize(note_activity) {:ok, announce_activity, object} = ActivityPub.announce(user, object, nil, true, false) @@ -1171,7 +921,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do test "does not add an announce activity to the db if the audience is widened" do user = insert(:user) - {:ok, note_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "private"}) + {:ok, note_activity} = CommonAPI.post(user, %{status: ".", visibility: "private"}) object = Object.normalize(note_activity) assert {:error, _} = ActivityPub.announce(user, object, nil, true, true) @@ -1180,59 +930,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do test "does not add an announce activity to the db if the announcer is not the author" do user = insert(:user) announcer = insert(:user) - {:ok, note_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "private"}) + {:ok, note_activity} = CommonAPI.post(user, %{status: ".", visibility: "private"}) object = Object.normalize(note_activity) assert {:error, _} = ActivityPub.announce(announcer, object, nil, true, false) end end - describe "unannouncing an object" do - test "unannouncing a previously announced object" do - note_activity = insert(:note_activity) - object = Object.normalize(note_activity) - user = insert(:user) - - # Unannouncing an object that is not announced does nothing - {:ok, object} = ActivityPub.unannounce(user, object) - refute object.data["announcement_count"] - - {:ok, announce_activity, object} = ActivityPub.announce(user, object) - assert object.data["announcement_count"] == 1 - - {:ok, unannounce_activity, object} = ActivityPub.unannounce(user, object) - assert object.data["announcement_count"] == 0 - - assert unannounce_activity.data["to"] == [ - User.ap_followers(user), - object.data["actor"] - ] - - assert unannounce_activity.data["type"] == "Undo" - assert unannounce_activity.data["object"] == announce_activity.data - assert unannounce_activity.data["actor"] == user.ap_id - assert unannounce_activity.data["context"] == announce_activity.data["context"] - - assert Activity.get_by_id(announce_activity.id) == nil - end - - test "reverts unannouncing on error" do - note_activity = insert(:note_activity) - object = Object.normalize(note_activity) - user = insert(:user) - - {:ok, _announce_activity, object} = ActivityPub.announce(user, object) - assert object.data["announcement_count"] == 1 - - with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do - assert {:error, :reverted} = ActivityPub.unannounce(user, object) - end - - object = Object.get_by_ap_id(object.data["id"]) - assert object.data["announcement_count"] == 1 - end - end - describe "uploading files" do test "copies the file to the configured folder" do file = %Plug.Upload{ @@ -1247,7 +951,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do test "works with base64 encoded images" do file = %{ - "img" => data_uri() + img: data_uri() } {:ok, %Object{}} = ActivityPub.upload(file) @@ -1339,7 +1043,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do end end - describe "blocking / unblocking" do + describe "blocking" do test "reverts block activity on error" do [blocker, blocked] = insert_list(2, :user) @@ -1352,177 +1056,38 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do end test "creates a block activity" do + clear_config([:instance, :federating], true) blocker = insert(:user) blocked = insert(:user) - {:ok, activity} = ActivityPub.block(blocker, blocked) - - assert activity.data["type"] == "Block" - assert activity.data["actor"] == blocker.ap_id - assert activity.data["object"] == blocked.ap_id - end + with_mock Pleroma.Web.Federator, + publish: fn _ -> nil end do + {:ok, activity} = ActivityPub.block(blocker, blocked) - test "reverts unblock activity on error" do - [blocker, blocked] = insert_list(2, :user) - {:ok, block_activity} = ActivityPub.block(blocker, blocked) + assert activity.data["type"] == "Block" + assert activity.data["actor"] == blocker.ap_id + assert activity.data["object"] == blocked.ap_id - with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do - assert {:error, :reverted} = ActivityPub.unblock(blocker, blocked) + assert called(Pleroma.Web.Federator.publish(activity)) end - - assert block_activity.data["type"] == "Block" - assert block_activity.data["actor"] == blocker.ap_id - - assert Repo.aggregate(Activity, :count, :id) == 1 - assert Repo.aggregate(Object, :count, :id) == 1 end - test "creates an undo activity for the last block" do + test "works with outgoing blocks disabled, but doesn't federate" do + clear_config([:instance, :federating], true) + clear_config([:activitypub, :outgoing_blocks], false) blocker = insert(:user) blocked = insert(:user) - {:ok, block_activity} = ActivityPub.block(blocker, blocked) - {:ok, activity} = ActivityPub.unblock(blocker, blocked) - - assert activity.data["type"] == "Undo" - assert activity.data["actor"] == blocker.ap_id + with_mock Pleroma.Web.Federator, + publish: fn _ -> nil end do + {:ok, activity} = ActivityPub.block(blocker, blocked) - embedded_object = activity.data["object"] - assert is_map(embedded_object) - assert embedded_object["type"] == "Block" - assert embedded_object["object"] == blocked.ap_id - assert embedded_object["id"] == block_activity.data["id"] - end - end + assert activity.data["type"] == "Block" + assert activity.data["actor"] == blocker.ap_id + assert activity.data["object"] == blocked.ap_id - describe "deletion" do - setup do: clear_config([:instance, :rewrite_policy]) - - test "it reverts deletion on error" do - note = insert(:note_activity) - object = Object.normalize(note) - - with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do - assert {:error, :reverted} = ActivityPub.delete(object) + refute called(Pleroma.Web.Federator.publish(:_)) end - - assert Repo.aggregate(Activity, :count, :id) == 1 - assert Repo.get(Object, object.id) == object - assert Activity.get_by_id(note.id) == note - end - - test "it creates a delete activity and deletes the original object" do - note = insert(:note_activity) - object = Object.normalize(note) - {:ok, delete} = ActivityPub.delete(object) - - assert delete.data["type"] == "Delete" - assert delete.data["actor"] == note.data["actor"] - assert delete.data["object"] == object.data["id"] - - assert Activity.get_by_id(delete.id) != nil - - assert Repo.get(Object, object.id).data["type"] == "Tombstone" - end - - test "decrements user note count only for public activities" do - user = insert(:user, note_count: 10) - - {:ok, a1} = - CommonAPI.post(User.get_cached_by_id(user.id), %{ - "status" => "yeah", - "visibility" => "public" - }) - - {:ok, a2} = - CommonAPI.post(User.get_cached_by_id(user.id), %{ - "status" => "yeah", - "visibility" => "unlisted" - }) - - {:ok, a3} = - CommonAPI.post(User.get_cached_by_id(user.id), %{ - "status" => "yeah", - "visibility" => "private" - }) - - {:ok, a4} = - CommonAPI.post(User.get_cached_by_id(user.id), %{ - "status" => "yeah", - "visibility" => "direct" - }) - - {:ok, _} = Object.normalize(a1) |> ActivityPub.delete() - {:ok, _} = Object.normalize(a2) |> ActivityPub.delete() - {:ok, _} = Object.normalize(a3) |> ActivityPub.delete() - {:ok, _} = Object.normalize(a4) |> ActivityPub.delete() - - user = User.get_cached_by_id(user.id) - assert user.note_count == 10 - end - - test "it creates a delete activity and checks that it is also sent to users mentioned by the deleted object" do - user = insert(:user) - note = insert(:note_activity) - object = Object.normalize(note) - - {:ok, object} = - object - |> Object.change(%{ - data: %{ - "actor" => object.data["actor"], - "id" => object.data["id"], - "to" => [user.ap_id], - "type" => "Note" - } - }) - |> Object.update_and_set_cache() - - {:ok, delete} = ActivityPub.delete(object) - - assert user.ap_id in delete.data["to"] - end - - test "decreases reply count" do - user = insert(:user) - user2 = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{"status" => "1", "visibility" => "public"}) - reply_data = %{"status" => "1", "in_reply_to_status_id" => activity.id} - ap_id = activity.data["id"] - - {:ok, public_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "public")) - {:ok, unlisted_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "unlisted")) - {:ok, private_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "private")) - {:ok, direct_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "direct")) - - _ = CommonAPI.delete(direct_reply.id, user2) - assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id) - assert object.data["repliesCount"] == 2 - - _ = CommonAPI.delete(private_reply.id, user2) - assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id) - assert object.data["repliesCount"] == 2 - - _ = CommonAPI.delete(public_reply.id, user2) - assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id) - assert object.data["repliesCount"] == 1 - - _ = CommonAPI.delete(unlisted_reply.id, user2) - assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id) - assert object.data["repliesCount"] == 0 - end - - test "it passes delete activity through MRF before deleting the object" do - Pleroma.Config.put([:instance, :rewrite_policy], Pleroma.Web.ActivityPub.MRF.DropPolicy) - - note = insert(:note_activity) - object = Object.normalize(note) - - {:error, {:reject, _}} = ActivityPub.delete(object) - - assert Activity.get_by_id(note.id) - assert Repo.get(Object, object.id).data["type"] == object.data["type"] end end @@ -1541,23 +1106,22 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do {:ok, user3} = User.follow(user3, user2) assert User.following?(user3, user2) - {:ok, public_activity} = CommonAPI.post(user3, %{"status" => "hi 1"}) + {:ok, public_activity} = CommonAPI.post(user3, %{status: "hi 1"}) - {:ok, private_activity_1} = - CommonAPI.post(user3, %{"status" => "hi 2", "visibility" => "private"}) + {:ok, private_activity_1} = CommonAPI.post(user3, %{status: "hi 2", visibility: "private"}) {:ok, private_activity_2} = CommonAPI.post(user2, %{ - "status" => "hi 3", - "visibility" => "private", - "in_reply_to_status_id" => private_activity_1.id + status: "hi 3", + visibility: "private", + in_reply_to_status_id: private_activity_1.id }) {:ok, private_activity_3} = CommonAPI.post(user3, %{ - "status" => "hi 4", - "visibility" => "private", - "in_reply_to_status_id" => private_activity_2.id + status: "hi 4", + visibility: "private", + in_reply_to_status_id: private_activity_2.id }) activities = @@ -1607,9 +1171,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do Config.put([:instance, :max_pinned_statuses], 3) user = insert(:user) - {:ok, activity_one} = CommonAPI.post(user, %{"status" => "HI!!!"}) - {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"}) - {:ok, activity_three} = CommonAPI.post(user, %{"status" => "HI!!!"}) + {:ok, activity_one} = CommonAPI.post(user, %{status: "HI!!!"}) + {:ok, activity_two} = CommonAPI.post(user, %{status: "HI!!!"}) + {:ok, activity_three} = CommonAPI.post(user, %{status: "HI!!!"}) CommonAPI.pin(activity_one.id, user) user = refresh_record(user) @@ -1630,7 +1194,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do reporter = insert(:user) target_account = insert(:user) content = "foobar" - {:ok, activity} = CommonAPI.post(target_account, %{"status" => content}) + {:ok, activity} = CommonAPI.post(target_account, %{status: content}) context = Utils.generate_context_id() reporter_ap_id = reporter.ap_id @@ -1726,8 +1290,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do {:ok, list} = Pleroma.List.create("foo", user) {:ok, list} = Pleroma.List.follow(list, member) - {:ok, activity} = - CommonAPI.post(user, %{"status" => "foobar", "visibility" => "list:#{list.id}"}) + {:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"}) activity = Repo.preload(activity, :bookmark) activity = %Activity{activity | thread_muted?: !!activity.thread_muted?} @@ -1745,8 +1308,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do {:ok, activity} = CommonAPI.post(user, %{ - "status" => "thought I looked cute might delete later :3", - "visibility" => "private" + status: "thought I looked cute might delete later :3", + visibility: "private" }) [result] = ActivityPub.fetch_activities_bounded([user.follower_address], []) @@ -1755,12 +1318,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do test "fetches only public posts for other users" do user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "#cofe", "visibility" => "public"}) + {:ok, activity} = CommonAPI.post(user, %{status: "#cofe", visibility: "public"}) {:ok, _private_activity} = CommonAPI.post(user, %{ - "status" => "why is tenshi eating a corndog so cute?", - "visibility" => "private" + status: "why is tenshi eating a corndog so cute?", + visibility: "private" }) [result] = ActivityPub.fetch_activities_bounded([], [user.follower_address]) @@ -1888,20 +1451,20 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do other_user = insert(:user) user1 = insert(:user) user2 = insert(:user) - {:ok, a1} = CommonAPI.post(user1, %{"status" => "bla"}) - {:ok, _a2} = CommonAPI.post(user2, %{"status" => "traps are happy"}) - {:ok, a3} = CommonAPI.post(user2, %{"status" => "Trees Are "}) - {:ok, a4} = CommonAPI.post(user2, %{"status" => "Agent Smith "}) - {:ok, a5} = CommonAPI.post(user1, %{"status" => "Red or Blue "}) - - {:ok, _, _} = CommonAPI.favorite(a4.id, user) - {:ok, _, _} = CommonAPI.favorite(a3.id, other_user) - {:ok, _, _} = CommonAPI.favorite(a3.id, user) - {:ok, _, _} = CommonAPI.favorite(a5.id, other_user) - {:ok, _, _} = CommonAPI.favorite(a5.id, user) - {:ok, _, _} = CommonAPI.favorite(a4.id, other_user) - {:ok, _, _} = CommonAPI.favorite(a1.id, user) - {:ok, _, _} = CommonAPI.favorite(a1.id, other_user) + {:ok, a1} = CommonAPI.post(user1, %{status: "bla"}) + {:ok, _a2} = CommonAPI.post(user2, %{status: "traps are happy"}) + {:ok, a3} = CommonAPI.post(user2, %{status: "Trees Are "}) + {:ok, a4} = CommonAPI.post(user2, %{status: "Agent Smith "}) + {:ok, a5} = CommonAPI.post(user1, %{status: "Red or Blue "}) + + {:ok, _} = CommonAPI.favorite(user, a4.id) + {:ok, _} = CommonAPI.favorite(other_user, a3.id) + {:ok, _} = CommonAPI.favorite(user, a3.id) + {:ok, _} = CommonAPI.favorite(other_user, a5.id) + {:ok, _} = CommonAPI.favorite(user, a5.id) + {:ok, _} = CommonAPI.favorite(other_user, a4.id) + {:ok, _} = CommonAPI.favorite(user, a1.id) + {:ok, _} = CommonAPI.favorite(other_user, a1.id) result = ActivityPub.fetch_favourites(user) assert Enum.map(result, & &1.id) == [a1.id, a5.id, a3.id, a4.id] @@ -1968,4 +1531,543 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do ActivityPub.move(old_user, new_user) end end + + test "doesn't retrieve replies activities with exclude_replies" do + user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "yeah"}) + + {:ok, _reply} = CommonAPI.post(user, %{status: "yeah", in_reply_to_status_id: activity.id}) + + [result] = ActivityPub.fetch_public_activities(%{"exclude_replies" => "true"}) + + assert result.id == activity.id + + assert length(ActivityPub.fetch_public_activities()) == 2 + end + + describe "replies filtering with public messages" do + setup :public_messages + + test "public timeline", %{users: %{u1: user}} do + activities_ids = + %{} + |> Map.put("type", ["Create", "Announce"]) + |> Map.put("local_only", false) + |> Map.put("blocking_user", user) + |> Map.put("muting_user", user) + |> Map.put("reply_filtering_user", user) + |> ActivityPub.fetch_public_activities() + |> Enum.map(& &1.id) + + assert length(activities_ids) == 16 + end + + test "public timeline with reply_visibility `following`", %{ + users: %{u1: user}, + u1: u1, + u2: u2, + u3: u3, + u4: u4, + activities: activities + } do + activities_ids = + %{} + |> Map.put("type", ["Create", "Announce"]) + |> Map.put("local_only", false) + |> Map.put("blocking_user", user) + |> Map.put("muting_user", user) + |> Map.put("reply_visibility", "following") + |> Map.put("reply_filtering_user", user) + |> ActivityPub.fetch_public_activities() + |> Enum.map(& &1.id) + + assert length(activities_ids) == 14 + + visible_ids = + Map.values(u1) ++ Map.values(u2) ++ Map.values(u4) ++ Map.values(activities) ++ [u3[:r1]] + + assert Enum.all?(visible_ids, &(&1 in activities_ids)) + end + + test "public timeline with reply_visibility `self`", %{ + users: %{u1: user}, + u1: u1, + u2: u2, + u3: u3, + u4: u4, + activities: activities + } do + activities_ids = + %{} + |> Map.put("type", ["Create", "Announce"]) + |> Map.put("local_only", false) + |> Map.put("blocking_user", user) + |> Map.put("muting_user", user) + |> Map.put("reply_visibility", "self") + |> Map.put("reply_filtering_user", user) + |> ActivityPub.fetch_public_activities() + |> Enum.map(& &1.id) + + assert length(activities_ids) == 10 + visible_ids = Map.values(u1) ++ [u2[:r1], u3[:r1], u4[:r1]] ++ Map.values(activities) + assert Enum.all?(visible_ids, &(&1 in activities_ids)) + end + + test "home timeline", %{ + users: %{u1: user}, + activities: activities, + u1: u1, + u2: u2, + u3: u3, + u4: u4 + } do + params = + %{} + |> Map.put("type", ["Create", "Announce"]) + |> Map.put("blocking_user", user) + |> Map.put("muting_user", user) + |> Map.put("user", user) + |> Map.put("reply_filtering_user", user) + + activities_ids = + ActivityPub.fetch_activities([user.ap_id | User.following(user)], params) + |> Enum.map(& &1.id) + + assert length(activities_ids) == 13 + + visible_ids = + Map.values(u1) ++ + Map.values(u3) ++ + [ + activities[:a1], + activities[:a2], + activities[:a4], + u2[:r1], + u2[:r3], + u4[:r1], + u4[:r2] + ] + + assert Enum.all?(visible_ids, &(&1 in activities_ids)) + end + + test "home timeline with reply_visibility `following`", %{ + users: %{u1: user}, + activities: activities, + u1: u1, + u2: u2, + u3: u3, + u4: u4 + } do + params = + %{} + |> Map.put("type", ["Create", "Announce"]) + |> Map.put("blocking_user", user) + |> Map.put("muting_user", user) + |> Map.put("user", user) + |> Map.put("reply_visibility", "following") + |> Map.put("reply_filtering_user", user) + + activities_ids = + ActivityPub.fetch_activities([user.ap_id | User.following(user)], params) + |> Enum.map(& &1.id) + + assert length(activities_ids) == 11 + + visible_ids = + Map.values(u1) ++ + [ + activities[:a1], + activities[:a2], + activities[:a4], + u2[:r1], + u2[:r3], + u3[:r1], + u4[:r1], + u4[:r2] + ] + + assert Enum.all?(visible_ids, &(&1 in activities_ids)) + end + + test "home timeline with reply_visibility `self`", %{ + users: %{u1: user}, + activities: activities, + u1: u1, + u2: u2, + u3: u3, + u4: u4 + } do + params = + %{} + |> Map.put("type", ["Create", "Announce"]) + |> Map.put("blocking_user", user) + |> Map.put("muting_user", user) + |> Map.put("user", user) + |> Map.put("reply_visibility", "self") + |> Map.put("reply_filtering_user", user) + + activities_ids = + ActivityPub.fetch_activities([user.ap_id | User.following(user)], params) + |> Enum.map(& &1.id) + + assert length(activities_ids) == 9 + + visible_ids = + Map.values(u1) ++ + [ + activities[:a1], + activities[:a2], + activities[:a4], + u2[:r1], + u3[:r1], + u4[:r1] + ] + + assert Enum.all?(visible_ids, &(&1 in activities_ids)) + end + end + + describe "replies filtering with private messages" do + setup :private_messages + + test "public timeline", %{users: %{u1: user}} do + activities_ids = + %{} + |> Map.put("type", ["Create", "Announce"]) + |> Map.put("local_only", false) + |> Map.put("blocking_user", user) + |> Map.put("muting_user", user) + |> Map.put("user", user) + |> ActivityPub.fetch_public_activities() + |> Enum.map(& &1.id) + + assert activities_ids == [] + end + + test "public timeline with default reply_visibility `following`", %{users: %{u1: user}} do + activities_ids = + %{} + |> Map.put("type", ["Create", "Announce"]) + |> Map.put("local_only", false) + |> Map.put("blocking_user", user) + |> Map.put("muting_user", user) + |> Map.put("reply_visibility", "following") + |> Map.put("reply_filtering_user", user) + |> Map.put("user", user) + |> ActivityPub.fetch_public_activities() + |> Enum.map(& &1.id) + + assert activities_ids == [] + end + + test "public timeline with default reply_visibility `self`", %{users: %{u1: user}} do + activities_ids = + %{} + |> Map.put("type", ["Create", "Announce"]) + |> Map.put("local_only", false) + |> Map.put("blocking_user", user) + |> Map.put("muting_user", user) + |> Map.put("reply_visibility", "self") + |> Map.put("reply_filtering_user", user) + |> Map.put("user", user) + |> ActivityPub.fetch_public_activities() + |> Enum.map(& &1.id) + + assert activities_ids == [] + end + + test "home timeline", %{users: %{u1: user}} do + params = + %{} + |> Map.put("type", ["Create", "Announce"]) + |> Map.put("blocking_user", user) + |> Map.put("muting_user", user) + |> Map.put("user", user) + + activities_ids = + ActivityPub.fetch_activities([user.ap_id | User.following(user)], params) + |> Enum.map(& &1.id) + + assert length(activities_ids) == 12 + end + + test "home timeline with default reply_visibility `following`", %{users: %{u1: user}} do + params = + %{} + |> Map.put("type", ["Create", "Announce"]) + |> Map.put("blocking_user", user) + |> Map.put("muting_user", user) + |> Map.put("user", user) + |> Map.put("reply_visibility", "following") + |> Map.put("reply_filtering_user", user) + + activities_ids = + ActivityPub.fetch_activities([user.ap_id | User.following(user)], params) + |> Enum.map(& &1.id) + + assert length(activities_ids) == 12 + end + + test "home timeline with default reply_visibility `self`", %{ + users: %{u1: user}, + activities: activities, + u1: u1, + u2: u2, + u3: u3, + u4: u4 + } do + params = + %{} + |> Map.put("type", ["Create", "Announce"]) + |> Map.put("blocking_user", user) + |> Map.put("muting_user", user) + |> Map.put("user", user) + |> Map.put("reply_visibility", "self") + |> Map.put("reply_filtering_user", user) + + activities_ids = + ActivityPub.fetch_activities([user.ap_id | User.following(user)], params) + |> Enum.map(& &1.id) + + assert length(activities_ids) == 10 + + visible_ids = + Map.values(u1) ++ Map.values(u4) ++ [u2[:r1], u3[:r1]] ++ Map.values(activities) + + assert Enum.all?(visible_ids, &(&1 in activities_ids)) + end + end + + defp public_messages(_) do + [u1, u2, u3, u4] = insert_list(4, :user) + {:ok, u1} = User.follow(u1, u2) + {:ok, u2} = User.follow(u2, u1) + {:ok, u1} = User.follow(u1, u4) + {:ok, u4} = User.follow(u4, u1) + + {:ok, u2} = User.follow(u2, u3) + {:ok, u3} = User.follow(u3, u2) + + {:ok, a1} = CommonAPI.post(u1, %{status: "Status"}) + + {:ok, r1_1} = + CommonAPI.post(u2, %{ + status: "@#{u1.nickname} reply from u2 to u1", + in_reply_to_status_id: a1.id + }) + + {:ok, r1_2} = + CommonAPI.post(u3, %{ + status: "@#{u1.nickname} reply from u3 to u1", + in_reply_to_status_id: a1.id + }) + + {:ok, r1_3} = + CommonAPI.post(u4, %{ + status: "@#{u1.nickname} reply from u4 to u1", + in_reply_to_status_id: a1.id + }) + + {:ok, a2} = CommonAPI.post(u2, %{status: "Status"}) + + {:ok, r2_1} = + CommonAPI.post(u1, %{ + status: "@#{u2.nickname} reply from u1 to u2", + in_reply_to_status_id: a2.id + }) + + {:ok, r2_2} = + CommonAPI.post(u3, %{ + status: "@#{u2.nickname} reply from u3 to u2", + in_reply_to_status_id: a2.id + }) + + {:ok, r2_3} = + CommonAPI.post(u4, %{ + status: "@#{u2.nickname} reply from u4 to u2", + in_reply_to_status_id: a2.id + }) + + {:ok, a3} = CommonAPI.post(u3, %{status: "Status"}) + + {:ok, r3_1} = + CommonAPI.post(u1, %{ + status: "@#{u3.nickname} reply from u1 to u3", + in_reply_to_status_id: a3.id + }) + + {:ok, r3_2} = + CommonAPI.post(u2, %{ + status: "@#{u3.nickname} reply from u2 to u3", + in_reply_to_status_id: a3.id + }) + + {:ok, r3_3} = + CommonAPI.post(u4, %{ + status: "@#{u3.nickname} reply from u4 to u3", + in_reply_to_status_id: a3.id + }) + + {:ok, a4} = CommonAPI.post(u4, %{status: "Status"}) + + {:ok, r4_1} = + CommonAPI.post(u1, %{ + status: "@#{u4.nickname} reply from u1 to u4", + in_reply_to_status_id: a4.id + }) + + {:ok, r4_2} = + CommonAPI.post(u2, %{ + status: "@#{u4.nickname} reply from u2 to u4", + in_reply_to_status_id: a4.id + }) + + {:ok, r4_3} = + CommonAPI.post(u3, %{ + status: "@#{u4.nickname} reply from u3 to u4", + in_reply_to_status_id: a4.id + }) + + {:ok, + users: %{u1: u1, u2: u2, u3: u3, u4: u4}, + activities: %{a1: a1.id, a2: a2.id, a3: a3.id, a4: a4.id}, + u1: %{r1: r1_1.id, r2: r1_2.id, r3: r1_3.id}, + u2: %{r1: r2_1.id, r2: r2_2.id, r3: r2_3.id}, + u3: %{r1: r3_1.id, r2: r3_2.id, r3: r3_3.id}, + u4: %{r1: r4_1.id, r2: r4_2.id, r3: r4_3.id}} + end + + defp private_messages(_) do + [u1, u2, u3, u4] = insert_list(4, :user) + {:ok, u1} = User.follow(u1, u2) + {:ok, u2} = User.follow(u2, u1) + {:ok, u1} = User.follow(u1, u3) + {:ok, u3} = User.follow(u3, u1) + {:ok, u1} = User.follow(u1, u4) + {:ok, u4} = User.follow(u4, u1) + + {:ok, u2} = User.follow(u2, u3) + {:ok, u3} = User.follow(u3, u2) + + {:ok, a1} = CommonAPI.post(u1, %{status: "Status", visibility: "private"}) + + {:ok, r1_1} = + CommonAPI.post(u2, %{ + status: "@#{u1.nickname} reply from u2 to u1", + in_reply_to_status_id: a1.id, + visibility: "private" + }) + + {:ok, r1_2} = + CommonAPI.post(u3, %{ + status: "@#{u1.nickname} reply from u3 to u1", + in_reply_to_status_id: a1.id, + visibility: "private" + }) + + {:ok, r1_3} = + CommonAPI.post(u4, %{ + status: "@#{u1.nickname} reply from u4 to u1", + in_reply_to_status_id: a1.id, + visibility: "private" + }) + + {:ok, a2} = CommonAPI.post(u2, %{status: "Status", visibility: "private"}) + + {:ok, r2_1} = + CommonAPI.post(u1, %{ + status: "@#{u2.nickname} reply from u1 to u2", + in_reply_to_status_id: a2.id, + visibility: "private" + }) + + {:ok, r2_2} = + CommonAPI.post(u3, %{ + status: "@#{u2.nickname} reply from u3 to u2", + in_reply_to_status_id: a2.id, + visibility: "private" + }) + + {:ok, a3} = CommonAPI.post(u3, %{status: "Status", visibility: "private"}) + + {:ok, r3_1} = + CommonAPI.post(u1, %{ + status: "@#{u3.nickname} reply from u1 to u3", + in_reply_to_status_id: a3.id, + visibility: "private" + }) + + {:ok, r3_2} = + CommonAPI.post(u2, %{ + status: "@#{u3.nickname} reply from u2 to u3", + in_reply_to_status_id: a3.id, + visibility: "private" + }) + + {:ok, a4} = CommonAPI.post(u4, %{status: "Status", visibility: "private"}) + + {:ok, r4_1} = + CommonAPI.post(u1, %{ + status: "@#{u4.nickname} reply from u1 to u4", + in_reply_to_status_id: a4.id, + visibility: "private" + }) + + {:ok, + users: %{u1: u1, u2: u2, u3: u3, u4: u4}, + activities: %{a1: a1.id, a2: a2.id, a3: a3.id, a4: a4.id}, + u1: %{r1: r1_1.id, r2: r1_2.id, r3: r1_3.id}, + u2: %{r1: r2_1.id, r2: r2_2.id}, + u3: %{r1: r3_1.id, r2: r3_2.id}, + u4: %{r1: r4_1.id}} + end + + describe "maybe_update_follow_information/1" do + setup do + clear_config([:instance, :external_user_synchronization], true) + + user = %{ + local: false, + ap_id: "https://gensokyo.2hu/users/raymoo", + following_address: "https://gensokyo.2hu/users/following", + follower_address: "https://gensokyo.2hu/users/followers", + type: "Person" + } + + %{user: user} + end + + test "logs an error when it can't fetch the info", %{user: user} do + assert capture_log(fn -> + ActivityPub.maybe_update_follow_information(user) + end) =~ "Follower/Following counter update for #{user.ap_id} failed" + end + + test "just returns the input if the user type is Application", %{ + user: user + } do + user = + user + |> Map.put(:type, "Application") + + refute capture_log(fn -> + assert ^user = ActivityPub.maybe_update_follow_information(user) + end) =~ "Follower/Following counter update for #{user.ap_id} failed" + end + + test "it just returns the input if the user has no following/follower addresses", %{ + user: user + } do + user = + user + |> Map.put(:following_address, nil) + |> Map.put(:follower_address, nil) + + refute capture_log(fn -> + assert ^user = ActivityPub.maybe_update_follow_information(user) + end) =~ "Follower/Following counter update for #{user.ap_id} failed" + end + end end diff --git a/test/web/activity_pub/mrf/anti_followbot_policy_test.exs b/test/web/activity_pub/mrf/anti_followbot_policy_test.exs index 37a7bfcf7..fca0de7c6 100644 --- a/test/web/activity_pub/mrf/anti_followbot_policy_test.exs +++ b/test/web/activity_pub/mrf/anti_followbot_policy_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicyTest do diff --git a/test/web/activity_pub/mrf/anti_link_spam_policy_test.exs b/test/web/activity_pub/mrf/anti_link_spam_policy_test.exs index b524fdd23..1a13699be 100644 --- a/test/web/activity_pub/mrf/anti_link_spam_policy_test.exs +++ b/test/web/activity_pub/mrf/anti_link_spam_policy_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicyTest do @@ -110,6 +110,15 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicyTest do end describe "with unknown actors" do + setup do + Tesla.Mock.mock(fn + %{method: :get, url: "http://invalid.actor"} -> + %Tesla.Env{status: 500, body: ""} + end) + + :ok + end + test "it rejects posts without links" do message = @linkless_message diff --git a/test/web/activity_pub/mrf/ensure_re_prepended_test.exs b/test/web/activity_pub/mrf/ensure_re_prepended_test.exs index dbc8b9e80..38ddec5bb 100644 --- a/test/web/activity_pub/mrf/ensure_re_prepended_test.exs +++ b/test/web/activity_pub/mrf/ensure_re_prepended_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrependedTest do diff --git a/test/web/activity_pub/mrf/no_placeholder_text_policy_test.exs b/test/web/activity_pub/mrf/no_placeholder_text_policy_test.exs index 63ed71129..64ea61dd4 100644 --- a/test/web/activity_pub/mrf/no_placeholder_text_policy_test.exs +++ b/test/web/activity_pub/mrf/no_placeholder_text_policy_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.NoPlaceholderTextPolicyTest do diff --git a/test/web/activity_pub/mrf/normalize_markup_test.exs b/test/web/activity_pub/mrf/normalize_markup_test.exs index 0207be56b..9b39c45bd 100644 --- a/test/web/activity_pub/mrf/normalize_markup_test.exs +++ b/test/web/activity_pub/mrf/normalize_markup_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.NormalizeMarkupTest do diff --git a/test/web/activity_pub/mrf/object_age_policy_test.exs b/test/web/activity_pub/mrf/object_age_policy_test.exs index 0fbc5f57a..b0fb753bd 100644 --- a/test/web/activity_pub/mrf/object_age_policy_test.exs +++ b/test/web/activity_pub/mrf/object_age_policy_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicyTest do @@ -20,26 +20,38 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicyTest do :ok end + defp get_old_message do + File.read!("test/fixtures/mastodon-post-activity.json") + |> Poison.decode!() + end + + defp get_new_message do + old_message = get_old_message() + + new_object = + old_message + |> Map.get("object") + |> Map.put("published", DateTime.utc_now() |> DateTime.to_iso8601()) + + old_message + |> Map.put("object", new_object) + end + describe "with reject action" do test "it rejects an old post" do Config.put([:mrf_object_age, :actions], [:reject]) - data = - File.read!("test/fixtures/mastodon-post-activity.json") - |> Poison.decode!() + data = get_old_message() - {:reject, _} = ObjectAgePolicy.filter(data) + assert match?({:reject, _}, ObjectAgePolicy.filter(data)) end test "it allows a new post" do Config.put([:mrf_object_age, :actions], [:reject]) - data = - File.read!("test/fixtures/mastodon-post-activity.json") - |> Poison.decode!() - |> Map.put("published", DateTime.utc_now() |> DateTime.to_iso8601()) + data = get_new_message() - {:ok, _} = ObjectAgePolicy.filter(data) + assert match?({:ok, _}, ObjectAgePolicy.filter(data)) end end @@ -47,9 +59,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicyTest do test "it delists an old post" do Config.put([:mrf_object_age, :actions], [:delist]) - data = - File.read!("test/fixtures/mastodon-post-activity.json") - |> Poison.decode!() + data = get_old_message() {:ok, _u} = User.get_or_fetch_by_ap_id(data["actor"]) @@ -61,14 +71,11 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicyTest do test "it allows a new post" do Config.put([:mrf_object_age, :actions], [:delist]) - data = - File.read!("test/fixtures/mastodon-post-activity.json") - |> Poison.decode!() - |> Map.put("published", DateTime.utc_now() |> DateTime.to_iso8601()) + data = get_new_message() {:ok, _user} = User.get_or_fetch_by_ap_id(data["actor"]) - {:ok, ^data} = ObjectAgePolicy.filter(data) + assert match?({:ok, ^data}, ObjectAgePolicy.filter(data)) end end @@ -76,9 +83,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicyTest do test "it strips followers collections from an old post" do Config.put([:mrf_object_age, :actions], [:strip_followers]) - data = - File.read!("test/fixtures/mastodon-post-activity.json") - |> Poison.decode!() + data = get_old_message() {:ok, user} = User.get_or_fetch_by_ap_id(data["actor"]) @@ -91,14 +96,11 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicyTest do test "it allows a new post" do Config.put([:mrf_object_age, :actions], [:strip_followers]) - data = - File.read!("test/fixtures/mastodon-post-activity.json") - |> Poison.decode!() - |> Map.put("published", DateTime.utc_now() |> DateTime.to_iso8601()) + data = get_new_message() {:ok, _u} = User.get_or_fetch_by_ap_id(data["actor"]) - {:ok, ^data} = ObjectAgePolicy.filter(data) + assert match?({:ok, ^data}, ObjectAgePolicy.filter(data)) end end end diff --git a/test/web/activity_pub/mrf/reject_non_public_test.exs b/test/web/activity_pub/mrf/reject_non_public_test.exs index abfd32df8..f36299b86 100644 --- a/test/web/activity_pub/mrf/reject_non_public_test.exs +++ b/test/web/activity_pub/mrf/reject_non_public_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublicTest do diff --git a/test/web/activity_pub/mrf/simple_policy_test.exs b/test/web/activity_pub/mrf/simple_policy_test.exs index 5aebbc675..b7b9bc6a2 100644 --- a/test/web/activity_pub/mrf/simple_policy_test.exs +++ b/test/web/activity_pub/mrf/simple_policy_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do @@ -17,7 +17,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do reject: [], accept: [], avatar_removal: [], - banner_removal: [] + banner_removal: [], + reject_deletes: [] ) describe "when :media_removal" do @@ -382,6 +383,66 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do end end + describe "when :reject_deletes is empty" do + setup do: Config.put([:mrf_simple, :reject_deletes], []) + + test "it accepts deletions even from rejected servers" do + Config.put([:mrf_simple, :reject], ["remote.instance"]) + + deletion_message = build_remote_deletion_message() + + assert SimplePolicy.filter(deletion_message) == {:ok, deletion_message} + end + + test "it accepts deletions even from non-whitelisted servers" do + Config.put([:mrf_simple, :accept], ["non.matching.remote"]) + + deletion_message = build_remote_deletion_message() + + assert SimplePolicy.filter(deletion_message) == {:ok, deletion_message} + end + end + + describe "when :reject_deletes is not empty but it doesn't have a matching host" do + setup do: Config.put([:mrf_simple, :reject_deletes], ["non.matching.remote"]) + + test "it accepts deletions even from rejected servers" do + Config.put([:mrf_simple, :reject], ["remote.instance"]) + + deletion_message = build_remote_deletion_message() + + assert SimplePolicy.filter(deletion_message) == {:ok, deletion_message} + end + + test "it accepts deletions even from non-whitelisted servers" do + Config.put([:mrf_simple, :accept], ["non.matching.remote"]) + + deletion_message = build_remote_deletion_message() + + assert SimplePolicy.filter(deletion_message) == {:ok, deletion_message} + end + end + + describe "when :reject_deletes has a matching host" do + setup do: Config.put([:mrf_simple, :reject_deletes], ["remote.instance"]) + + test "it rejects the deletion" do + deletion_message = build_remote_deletion_message() + + assert SimplePolicy.filter(deletion_message) == {:reject, nil} + end + end + + describe "when :reject_deletes match with wildcard domain" do + setup do: Config.put([:mrf_simple, :reject_deletes], ["*.remote.instance"]) + + test "it rejects the deletion" do + deletion_message = build_remote_deletion_message() + + assert SimplePolicy.filter(deletion_message) == {:reject, nil} + end + end + defp build_local_message do %{ "actor" => "#{Pleroma.Web.base_url()}/users/alice", @@ -408,4 +469,11 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do "type" => "Person" } end + + defp build_remote_deletion_message do + %{ + "type" => "Delete", + "actor" => "https://remote.instance/users/bob" + } + end end diff --git a/test/web/activity_pub/object_validator_test.exs b/test/web/activity_pub/object_validator_test.exs new file mode 100644 index 000000000..96eff1c30 --- /dev/null +++ b/test/web/activity_pub/object_validator_test.exs @@ -0,0 +1,283 @@ +defmodule Pleroma.Web.ActivityPub.ObjectValidatorTest do + use Pleroma.DataCase + + alias Pleroma.Object + alias Pleroma.Web.ActivityPub.Builder + alias Pleroma.Web.ActivityPub.ObjectValidator + alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator + alias Pleroma.Web.ActivityPub.Utils + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + describe "EmojiReacts" do + setup do + user = insert(:user) + {:ok, post_activity} = CommonAPI.post(user, %{status: "uguu"}) + + object = Pleroma.Object.get_by_ap_id(post_activity.data["object"]) + + {:ok, valid_emoji_react, []} = Builder.emoji_react(user, object, "👌") + + %{user: user, post_activity: post_activity, valid_emoji_react: valid_emoji_react} + end + + test "it validates a valid EmojiReact", %{valid_emoji_react: valid_emoji_react} do + assert {:ok, _, _} = ObjectValidator.validate(valid_emoji_react, []) + end + + test "it is not valid without a 'content' field", %{valid_emoji_react: valid_emoji_react} do + without_content = + valid_emoji_react + |> Map.delete("content") + + {:error, cng} = ObjectValidator.validate(without_content, []) + + refute cng.valid? + assert {:content, {"can't be blank", [validation: :required]}} in cng.errors + end + + test "it is not valid with a non-emoji content field", %{valid_emoji_react: valid_emoji_react} do + without_emoji_content = + valid_emoji_react + |> Map.put("content", "x") + + {:error, cng} = ObjectValidator.validate(without_emoji_content, []) + + refute cng.valid? + + assert {:content, {"must be a single character emoji", []}} in cng.errors + end + end + + describe "Undos" do + setup do + user = insert(:user) + {:ok, post_activity} = CommonAPI.post(user, %{status: "uguu"}) + {:ok, like} = CommonAPI.favorite(user, post_activity.id) + {:ok, valid_like_undo, []} = Builder.undo(user, like) + + %{user: user, like: like, valid_like_undo: valid_like_undo} + end + + test "it validates a basic like undo", %{valid_like_undo: valid_like_undo} do + assert {:ok, _, _} = ObjectValidator.validate(valid_like_undo, []) + end + + test "it does not validate if the actor of the undo is not the actor of the object", %{ + valid_like_undo: valid_like_undo + } do + other_user = insert(:user, ap_id: "https://gensokyo.2hu/users/raymoo") + + bad_actor = + valid_like_undo + |> Map.put("actor", other_user.ap_id) + + {:error, cng} = ObjectValidator.validate(bad_actor, []) + + assert {:actor, {"not the same as object actor", []}} in cng.errors + end + + test "it does not validate if the object is missing", %{valid_like_undo: valid_like_undo} do + missing_object = + valid_like_undo + |> Map.put("object", "https://gensokyo.2hu/objects/1") + + {:error, cng} = ObjectValidator.validate(missing_object, []) + + assert {:object, {"can't find object", []}} in cng.errors + assert length(cng.errors) == 1 + end + end + + describe "deletes" do + setup do + user = insert(:user) + {:ok, post_activity} = CommonAPI.post(user, %{status: "cancel me daddy"}) + + {:ok, valid_post_delete, _} = Builder.delete(user, post_activity.data["object"]) + {:ok, valid_user_delete, _} = Builder.delete(user, user.ap_id) + + %{user: user, valid_post_delete: valid_post_delete, valid_user_delete: valid_user_delete} + end + + test "it is valid for a post deletion", %{valid_post_delete: valid_post_delete} do + {:ok, valid_post_delete, _} = ObjectValidator.validate(valid_post_delete, []) + + assert valid_post_delete["deleted_activity_id"] + end + + test "it is invalid if the object isn't in a list of certain types", %{ + valid_post_delete: valid_post_delete + } do + object = Object.get_by_ap_id(valid_post_delete["object"]) + + data = + object.data + |> Map.put("type", "Like") + + {:ok, _object} = + object + |> Ecto.Changeset.change(%{data: data}) + |> Object.update_and_set_cache() + + {:error, cng} = ObjectValidator.validate(valid_post_delete, []) + assert {:object, {"object not in allowed types", []}} in cng.errors + end + + test "it is valid for a user deletion", %{valid_user_delete: valid_user_delete} do + assert match?({:ok, _, _}, ObjectValidator.validate(valid_user_delete, [])) + end + + test "it's invalid if the id is missing", %{valid_post_delete: valid_post_delete} do + no_id = + valid_post_delete + |> Map.delete("id") + + {:error, cng} = ObjectValidator.validate(no_id, []) + + assert {:id, {"can't be blank", [validation: :required]}} in cng.errors + end + + test "it's invalid if the object doesn't exist", %{valid_post_delete: valid_post_delete} do + missing_object = + valid_post_delete + |> Map.put("object", "http://does.not/exist") + + {:error, cng} = ObjectValidator.validate(missing_object, []) + + assert {:object, {"can't find object", []}} in cng.errors + end + + test "it's invalid if the actor of the object and the actor of delete are from different domains", + %{valid_post_delete: valid_post_delete} do + valid_user = insert(:user) + + valid_other_actor = + valid_post_delete + |> Map.put("actor", valid_user.ap_id) + + assert match?({:ok, _, _}, ObjectValidator.validate(valid_other_actor, [])) + + invalid_other_actor = + valid_post_delete + |> Map.put("actor", "https://gensokyo.2hu/users/raymoo") + + {:error, cng} = ObjectValidator.validate(invalid_other_actor, []) + + assert {:actor, {"is not allowed to delete object", []}} in cng.errors + end + + test "it's valid if the actor of the object is a local superuser", + %{valid_post_delete: valid_post_delete} do + user = + insert(:user, local: true, is_moderator: true, ap_id: "https://gensokyo.2hu/users/raymoo") + + valid_other_actor = + valid_post_delete + |> Map.put("actor", user.ap_id) + + {:ok, _, meta} = ObjectValidator.validate(valid_other_actor, []) + assert meta[:do_not_federate] + end + end + + describe "likes" do + setup do + user = insert(:user) + {:ok, post_activity} = CommonAPI.post(user, %{status: "uguu"}) + + valid_like = %{ + "to" => [user.ap_id], + "cc" => [], + "type" => "Like", + "id" => Utils.generate_activity_id(), + "object" => post_activity.data["object"], + "actor" => user.ap_id, + "context" => "a context" + } + + %{valid_like: valid_like, user: user, post_activity: post_activity} + end + + test "returns ok when called in the ObjectValidator", %{valid_like: valid_like} do + {:ok, object, _meta} = ObjectValidator.validate(valid_like, []) + + assert "id" in Map.keys(object) + end + + test "is valid for a valid object", %{valid_like: valid_like} do + assert LikeValidator.cast_and_validate(valid_like).valid? + end + + test "sets the 'to' field to the object actor if no recipients are given", %{ + valid_like: valid_like, + user: user + } do + without_recipients = + valid_like + |> Map.delete("to") + + {:ok, object, _meta} = ObjectValidator.validate(without_recipients, []) + + assert object["to"] == [user.ap_id] + end + + test "sets the context field to the context of the object if no context is given", %{ + valid_like: valid_like, + post_activity: post_activity + } do + without_context = + valid_like + |> Map.delete("context") + + {:ok, object, _meta} = ObjectValidator.validate(without_context, []) + + assert object["context"] == post_activity.data["context"] + end + + test "it errors when the actor is missing or not known", %{valid_like: valid_like} do + without_actor = Map.delete(valid_like, "actor") + + refute LikeValidator.cast_and_validate(without_actor).valid? + + with_invalid_actor = Map.put(valid_like, "actor", "invalidactor") + + refute LikeValidator.cast_and_validate(with_invalid_actor).valid? + end + + test "it errors when the object is missing or not known", %{valid_like: valid_like} do + without_object = Map.delete(valid_like, "object") + + refute LikeValidator.cast_and_validate(without_object).valid? + + with_invalid_object = Map.put(valid_like, "object", "invalidobject") + + refute LikeValidator.cast_and_validate(with_invalid_object).valid? + end + + test "it errors when the actor has already like the object", %{ + valid_like: valid_like, + user: user, + post_activity: post_activity + } do + _like = CommonAPI.favorite(user, post_activity.id) + + refute LikeValidator.cast_and_validate(valid_like).valid? + end + + test "it works when actor or object are wrapped in maps", %{valid_like: valid_like} do + wrapped_like = + valid_like + |> Map.put("actor", %{"id" => valid_like["actor"]}) + |> Map.put("object", %{"id" => valid_like["object"]}) + + validated = LikeValidator.cast_and_validate(wrapped_like) + + assert validated.valid? + + assert {:actor, valid_like["actor"]} in validated.changes + assert {:object, valid_like["object"]} in validated.changes + end + end +end diff --git a/test/web/activity_pub/object_validators/note_validator_test.exs b/test/web/activity_pub/object_validators/note_validator_test.exs new file mode 100644 index 000000000..30c481ffb --- /dev/null +++ b/test/web/activity_pub/object_validators/note_validator_test.exs @@ -0,0 +1,35 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.ObjectValidators.NoteValidatorTest do + use Pleroma.DataCase + + alias Pleroma.Web.ActivityPub.ObjectValidators.NoteValidator + alias Pleroma.Web.ActivityPub.Utils + + import Pleroma.Factory + + describe "Notes" do + setup do + user = insert(:user) + + note = %{ + "id" => Utils.generate_activity_id(), + "type" => "Note", + "actor" => user.ap_id, + "to" => [user.follower_address], + "cc" => [], + "content" => "Hellow this is content.", + "context" => "xxx", + "summary" => "a post" + } + + %{user: user, note: note} + end + + test "a basic note validates", %{note: note} do + %{valid?: true} = NoteValidator.cast_and_validate(note) + end + end +end diff --git a/test/web/activity_pub/object_validators/types/date_time_test.exs b/test/web/activity_pub/object_validators/types/date_time_test.exs new file mode 100644 index 000000000..3e17a9497 --- /dev/null +++ b/test/web/activity_pub/object_validators/types/date_time_test.exs @@ -0,0 +1,32 @@ +defmodule Pleroma.Web.ActivityPub.ObjectValidators.Types.DateTimeTest do + alias Pleroma.Web.ActivityPub.ObjectValidators.Types.DateTime + use Pleroma.DataCase + + test "it validates an xsd:Datetime" do + valid_strings = [ + "2004-04-12T13:20:00", + "2004-04-12T13:20:15.5", + "2004-04-12T13:20:00-05:00", + "2004-04-12T13:20:00Z" + ] + + invalid_strings = [ + "2004-04-12T13:00", + "2004-04-1213:20:00", + "99-04-12T13:00", + "2004-04-12" + ] + + assert {:ok, "2004-04-01T12:00:00Z"} == DateTime.cast("2004-04-01T12:00:00Z") + + Enum.each(valid_strings, fn date_time -> + result = DateTime.cast(date_time) + assert {:ok, _} = result + end) + + Enum.each(invalid_strings, fn date_time -> + result = DateTime.cast(date_time) + assert :error == result + end) + end +end diff --git a/test/web/activity_pub/object_validators/types/object_id_test.exs b/test/web/activity_pub/object_validators/types/object_id_test.exs new file mode 100644 index 000000000..834213182 --- /dev/null +++ b/test/web/activity_pub/object_validators/types/object_id_test.exs @@ -0,0 +1,37 @@ +defmodule Pleroma.Web.ObjectValidators.Types.ObjectIDTest do + alias Pleroma.Web.ActivityPub.ObjectValidators.Types.ObjectID + use Pleroma.DataCase + + @uris [ + "http://lain.com/users/lain", + "http://lain.com", + "https://lain.com/object/1" + ] + + @non_uris [ + "https://", + "rin", + 1, + :x, + %{"1" => 2} + ] + + test "it accepts http uris" do + Enum.each(@uris, fn uri -> + assert {:ok, uri} == ObjectID.cast(uri) + end) + end + + test "it accepts an object with a nested uri id" do + Enum.each(@uris, fn uri -> + assert {:ok, uri} == ObjectID.cast(%{"id" => uri}) + end) + end + + test "it rejects non-uri strings" do + Enum.each(@non_uris, fn non_uri -> + assert :error == ObjectID.cast(non_uri) + assert :error == ObjectID.cast(%{"id" => non_uri}) + end) + end +end diff --git a/test/web/activity_pub/object_validators/types/recipients_test.exs b/test/web/activity_pub/object_validators/types/recipients_test.exs new file mode 100644 index 000000000..f278f039b --- /dev/null +++ b/test/web/activity_pub/object_validators/types/recipients_test.exs @@ -0,0 +1,27 @@ +defmodule Pleroma.Web.ObjectValidators.Types.RecipientsTest do + alias Pleroma.Web.ActivityPub.ObjectValidators.Types.Recipients + use Pleroma.DataCase + + test "it asserts that all elements of the list are object ids" do + list = ["https://lain.com/users/lain", "invalid"] + + assert :error == Recipients.cast(list) + end + + test "it works with a list" do + list = ["https://lain.com/users/lain"] + assert {:ok, list} == Recipients.cast(list) + end + + test "it works with a list with whole objects" do + list = ["https://lain.com/users/lain", %{"id" => "https://gensokyo.2hu/users/raymoo"}] + resulting_list = ["https://gensokyo.2hu/users/raymoo", "https://lain.com/users/lain"] + assert {:ok, resulting_list} == Recipients.cast(list) + end + + test "it turns a single string into a list" do + recipient = "https://lain.com/users/lain" + + assert {:ok, [recipient]} == Recipients.cast(recipient) + end +end diff --git a/test/web/activity_pub/pipeline_test.exs b/test/web/activity_pub/pipeline_test.exs new file mode 100644 index 000000000..f3c437498 --- /dev/null +++ b/test/web/activity_pub/pipeline_test.exs @@ -0,0 +1,87 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.PipelineTest do + use Pleroma.DataCase + + import Mock + import Pleroma.Factory + + describe "common_pipeline/2" do + test "it goes through validation, filtering, persisting, side effects and federation for local activities" do + activity = insert(:note_activity) + meta = [local: true] + + with_mocks([ + {Pleroma.Web.ActivityPub.ObjectValidator, [], [validate: fn o, m -> {:ok, o, m} end]}, + { + Pleroma.Web.ActivityPub.MRF, + [], + [filter: fn o -> {:ok, o} end] + }, + { + Pleroma.Web.ActivityPub.ActivityPub, + [], + [persist: fn o, m -> {:ok, o, m} end] + }, + { + Pleroma.Web.ActivityPub.SideEffects, + [], + [handle: fn o, m -> {:ok, o, m} end] + }, + { + Pleroma.Web.Federator, + [], + [publish: fn _o -> :ok end] + } + ]) do + assert {:ok, ^activity, ^meta} = + Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity, meta) + + assert_called(Pleroma.Web.ActivityPub.ObjectValidator.validate(activity, meta)) + assert_called(Pleroma.Web.ActivityPub.MRF.filter(activity)) + assert_called(Pleroma.Web.ActivityPub.ActivityPub.persist(activity, meta)) + assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta)) + assert_called(Pleroma.Web.Federator.publish(activity)) + end + end + + test "it goes through validation, filtering, persisting, side effects without federation for remote activities" do + activity = insert(:note_activity) + meta = [local: false] + + with_mocks([ + {Pleroma.Web.ActivityPub.ObjectValidator, [], [validate: fn o, m -> {:ok, o, m} end]}, + { + Pleroma.Web.ActivityPub.MRF, + [], + [filter: fn o -> {:ok, o} end] + }, + { + Pleroma.Web.ActivityPub.ActivityPub, + [], + [persist: fn o, m -> {:ok, o, m} end] + }, + { + Pleroma.Web.ActivityPub.SideEffects, + [], + [handle: fn o, m -> {:ok, o, m} end] + }, + { + Pleroma.Web.Federator, + [], + [] + } + ]) do + assert {:ok, ^activity, ^meta} = + Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity, meta) + + assert_called(Pleroma.Web.ActivityPub.ObjectValidator.validate(activity, meta)) + assert_called(Pleroma.Web.ActivityPub.MRF.filter(activity)) + assert_called(Pleroma.Web.ActivityPub.ActivityPub.persist(activity, meta)) + assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta)) + end + end + end +end diff --git a/test/web/activity_pub/publisher_test.exs b/test/web/activity_pub/publisher_test.exs index 801da03c1..c2bc38d52 100644 --- a/test/web/activity_pub/publisher_test.exs +++ b/test/web/activity_pub/publisher_test.exs @@ -48,10 +48,7 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do describe "determine_inbox/2" do test "it returns sharedInbox for messages involving as:Public in to" do - user = - insert(:user, %{ - source_data: %{"endpoints" => %{"sharedInbox" => "http://example.com/inbox"}} - }) + user = insert(:user, %{shared_inbox: "http://example.com/inbox"}) activity = %Activity{ data: %{"to" => [@as_public], "cc" => [user.follower_address]} @@ -61,10 +58,7 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do end test "it returns sharedInbox for messages involving as:Public in cc" do - user = - insert(:user, %{ - source_data: %{"endpoints" => %{"sharedInbox" => "http://example.com/inbox"}} - }) + user = insert(:user, %{shared_inbox: "http://example.com/inbox"}) activity = %Activity{ data: %{"cc" => [@as_public], "to" => [user.follower_address]} @@ -74,11 +68,7 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do end test "it returns sharedInbox for messages involving multiple recipients in to" do - user = - insert(:user, %{ - source_data: %{"endpoints" => %{"sharedInbox" => "http://example.com/inbox"}} - }) - + user = insert(:user, %{shared_inbox: "http://example.com/inbox"}) user_two = insert(:user) user_three = insert(:user) @@ -90,11 +80,7 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do end test "it returns sharedInbox for messages involving multiple recipients in cc" do - user = - insert(:user, %{ - source_data: %{"endpoints" => %{"sharedInbox" => "http://example.com/inbox"}} - }) - + user = insert(:user, %{shared_inbox: "http://example.com/inbox"}) user_two = insert(:user) user_three = insert(:user) @@ -107,12 +93,10 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do test "it returns sharedInbox for messages involving multiple recipients in total" do user = - insert(:user, - source_data: %{ - "inbox" => "http://example.com/personal-inbox", - "endpoints" => %{"sharedInbox" => "http://example.com/inbox"} - } - ) + insert(:user, %{ + shared_inbox: "http://example.com/inbox", + inbox: "http://example.com/personal-inbox" + }) user_two = insert(:user) @@ -125,12 +109,10 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do test "it returns inbox for messages involving single recipients in total" do user = - insert(:user, - source_data: %{ - "inbox" => "http://example.com/personal-inbox", - "endpoints" => %{"sharedInbox" => "http://example.com/inbox"} - } - ) + insert(:user, %{ + shared_inbox: "http://example.com/inbox", + inbox: "http://example.com/personal-inbox" + }) activity = %Activity{ data: %{"to" => [user.ap_id], "cc" => []} @@ -258,11 +240,11 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do [:passthrough], [] do follower = - insert(:user, + insert(:user, %{ local: false, - source_data: %{"inbox" => "https://domain.com/users/nick1/inbox"}, + inbox: "https://domain.com/users/nick1/inbox", ap_enabled: true - ) + }) actor = insert(:user, follower_address: follower.ap_id) user = insert(:user) @@ -295,14 +277,14 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do fetcher = insert(:user, local: false, - source_data: %{"inbox" => "https://domain.com/users/nick1/inbox"}, + inbox: "https://domain.com/users/nick1/inbox", ap_enabled: true ) another_fetcher = insert(:user, local: false, - source_data: %{"inbox" => "https://domain2.com/users/nick1/inbox"}, + inbox: "https://domain2.com/users/nick1/inbox", ap_enabled: true ) diff --git a/test/web/activity_pub/relay_test.exs b/test/web/activity_pub/relay_test.exs index 040625e4d..9e16e39c4 100644 --- a/test/web/activity_pub/relay_test.exs +++ b/test/web/activity_pub/relay_test.exs @@ -89,6 +89,11 @@ defmodule Pleroma.Web.ActivityPub.RelayTest do } ) + Tesla.Mock.mock(fn + %{method: :get, url: "http://mastodon.example.org/eee/99541947525187367"} -> + %Tesla.Env{status: 500, body: ""} + end) + assert capture_log(fn -> assert Relay.publish(activity) == {:error, nil} end) =~ "[error] error: nil" diff --git a/test/web/activity_pub/side_effects_test.exs b/test/web/activity_pub/side_effects_test.exs new file mode 100644 index 000000000..797f00d08 --- /dev/null +++ b/test/web/activity_pub/side_effects_test.exs @@ -0,0 +1,267 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.SideEffectsTest do + use Oban.Testing, repo: Pleroma.Repo + use Pleroma.DataCase + + alias Pleroma.Activity + alias Pleroma.Notification + alias Pleroma.Object + alias Pleroma.Repo + alias Pleroma.Tests.ObanHelpers + alias Pleroma.User + alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.ActivityPub.Builder + alias Pleroma.Web.ActivityPub.SideEffects + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + import Mock + + describe "delete objects" do + setup do + user = insert(:user) + other_user = insert(:user) + + {:ok, op} = CommonAPI.post(other_user, %{status: "big oof"}) + {:ok, post} = CommonAPI.post(user, %{status: "hey", in_reply_to_id: op}) + {:ok, favorite} = CommonAPI.favorite(user, post.id) + object = Object.normalize(post) + {:ok, delete_data, _meta} = Builder.delete(user, object.data["id"]) + {:ok, delete_user_data, _meta} = Builder.delete(user, user.ap_id) + {:ok, delete, _meta} = ActivityPub.persist(delete_data, local: true) + {:ok, delete_user, _meta} = ActivityPub.persist(delete_user_data, local: true) + + %{ + user: user, + delete: delete, + post: post, + object: object, + delete_user: delete_user, + op: op, + favorite: favorite + } + end + + test "it handles object deletions", %{ + delete: delete, + post: post, + object: object, + user: user, + op: op, + favorite: favorite + } do + with_mock Pleroma.Web.ActivityPub.ActivityPub, [:passthrough], + stream_out: fn _ -> nil end, + stream_out_participations: fn _, _ -> nil end do + {:ok, delete, _} = SideEffects.handle(delete) + user = User.get_cached_by_ap_id(object.data["actor"]) + + assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out(delete)) + assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out_participations(object, user)) + end + + object = Object.get_by_id(object.id) + assert object.data["type"] == "Tombstone" + refute Activity.get_by_id(post.id) + refute Activity.get_by_id(favorite.id) + + user = User.get_by_id(user.id) + assert user.note_count == 0 + + object = Object.normalize(op.data["object"], false) + + assert object.data["repliesCount"] == 0 + end + + test "it handles object deletions when the object itself has been pruned", %{ + delete: delete, + post: post, + object: object, + user: user, + op: op + } do + with_mock Pleroma.Web.ActivityPub.ActivityPub, [:passthrough], + stream_out: fn _ -> nil end, + stream_out_participations: fn _, _ -> nil end do + {:ok, delete, _} = SideEffects.handle(delete) + user = User.get_cached_by_ap_id(object.data["actor"]) + + assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out(delete)) + assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out_participations(object, user)) + end + + object = Object.get_by_id(object.id) + assert object.data["type"] == "Tombstone" + refute Activity.get_by_id(post.id) + + user = User.get_by_id(user.id) + assert user.note_count == 0 + + object = Object.normalize(op.data["object"], false) + + assert object.data["repliesCount"] == 0 + end + + test "it handles user deletions", %{delete_user: delete, user: user} do + {:ok, _delete, _} = SideEffects.handle(delete) + ObanHelpers.perform_all() + + assert User.get_cached_by_ap_id(user.ap_id).deactivated + end + end + + describe "EmojiReact objects" do + setup do + poster = insert(:user) + user = insert(:user) + + {:ok, post} = CommonAPI.post(poster, %{status: "hey"}) + + {:ok, emoji_react_data, []} = Builder.emoji_react(user, post.object, "👌") + {:ok, emoji_react, _meta} = ActivityPub.persist(emoji_react_data, local: true) + + %{emoji_react: emoji_react, user: user, poster: poster} + end + + test "adds the reaction to the object", %{emoji_react: emoji_react, user: user} do + {:ok, emoji_react, _} = SideEffects.handle(emoji_react) + object = Object.get_by_ap_id(emoji_react.data["object"]) + + assert object.data["reaction_count"] == 1 + assert ["👌", [user.ap_id]] in object.data["reactions"] + end + + test "creates a notification", %{emoji_react: emoji_react, poster: poster} do + {:ok, emoji_react, _} = SideEffects.handle(emoji_react) + assert Repo.get_by(Notification, user_id: poster.id, activity_id: emoji_react.id) + end + end + + describe "Undo objects" do + setup do + poster = insert(:user) + user = insert(:user) + {:ok, post} = CommonAPI.post(poster, %{status: "hey"}) + {:ok, like} = CommonAPI.favorite(user, post.id) + {:ok, reaction} = CommonAPI.react_with_emoji(post.id, user, "👍") + {:ok, announce, _} = CommonAPI.repeat(post.id, user) + {:ok, block} = ActivityPub.block(user, poster) + User.block(user, poster) + + {:ok, undo_data, _meta} = Builder.undo(user, like) + {:ok, like_undo, _meta} = ActivityPub.persist(undo_data, local: true) + + {:ok, undo_data, _meta} = Builder.undo(user, reaction) + {:ok, reaction_undo, _meta} = ActivityPub.persist(undo_data, local: true) + + {:ok, undo_data, _meta} = Builder.undo(user, announce) + {:ok, announce_undo, _meta} = ActivityPub.persist(undo_data, local: true) + + {:ok, undo_data, _meta} = Builder.undo(user, block) + {:ok, block_undo, _meta} = ActivityPub.persist(undo_data, local: true) + + %{ + like_undo: like_undo, + post: post, + like: like, + reaction_undo: reaction_undo, + reaction: reaction, + announce_undo: announce_undo, + announce: announce, + block_undo: block_undo, + block: block, + poster: poster, + user: user + } + end + + test "deletes the original block", %{block_undo: block_undo, block: block} do + {:ok, _block_undo, _} = SideEffects.handle(block_undo) + refute Activity.get_by_id(block.id) + end + + test "unblocks the blocked user", %{block_undo: block_undo, block: block} do + blocker = User.get_by_ap_id(block.data["actor"]) + blocked = User.get_by_ap_id(block.data["object"]) + + {:ok, _block_undo, _} = SideEffects.handle(block_undo) + refute User.blocks?(blocker, blocked) + end + + test "an announce undo removes the announce from the object", %{ + announce_undo: announce_undo, + post: post + } do + {:ok, _announce_undo, _} = SideEffects.handle(announce_undo) + + object = Object.get_by_ap_id(post.data["object"]) + + assert object.data["announcement_count"] == 0 + assert object.data["announcements"] == [] + end + + test "deletes the original announce", %{announce_undo: announce_undo, announce: announce} do + {:ok, _announce_undo, _} = SideEffects.handle(announce_undo) + refute Activity.get_by_id(announce.id) + end + + test "a reaction undo removes the reaction from the object", %{ + reaction_undo: reaction_undo, + post: post + } do + {:ok, _reaction_undo, _} = SideEffects.handle(reaction_undo) + + object = Object.get_by_ap_id(post.data["object"]) + + assert object.data["reaction_count"] == 0 + assert object.data["reactions"] == [] + end + + test "deletes the original reaction", %{reaction_undo: reaction_undo, reaction: reaction} do + {:ok, _reaction_undo, _} = SideEffects.handle(reaction_undo) + refute Activity.get_by_id(reaction.id) + end + + test "a like undo removes the like from the object", %{like_undo: like_undo, post: post} do + {:ok, _like_undo, _} = SideEffects.handle(like_undo) + + object = Object.get_by_ap_id(post.data["object"]) + + assert object.data["like_count"] == 0 + assert object.data["likes"] == [] + end + + test "deletes the original like", %{like_undo: like_undo, like: like} do + {:ok, _like_undo, _} = SideEffects.handle(like_undo) + refute Activity.get_by_id(like.id) + end + end + + describe "like objects" do + setup do + poster = insert(:user) + user = insert(:user) + {:ok, post} = CommonAPI.post(poster, %{status: "hey"}) + + {:ok, like_data, _meta} = Builder.like(user, post.object) + {:ok, like, _meta} = ActivityPub.persist(like_data, local: true) + + %{like: like, user: user, poster: poster} + end + + test "add the like to the original object", %{like: like, user: user} do + {:ok, like, _} = SideEffects.handle(like) + object = Object.get_by_ap_id(like.data["object"]) + assert object.data["like_count"] == 1 + assert user.ap_id in object.data["likes"] + end + + test "creates a notification", %{like: like, poster: poster} do + {:ok, like, _} = SideEffects.handle(like) + assert Repo.get_by(Notification, user_id: poster.id, activity_id: like.id) + end + end +end diff --git a/test/web/activity_pub/transmogrifier/delete_handling_test.exs b/test/web/activity_pub/transmogrifier/delete_handling_test.exs new file mode 100644 index 000000000..c9a53918c --- /dev/null +++ b/test/web/activity_pub/transmogrifier/delete_handling_test.exs @@ -0,0 +1,114 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.Transmogrifier.DeleteHandlingTest do + use Oban.Testing, repo: Pleroma.Repo + use Pleroma.DataCase + + alias Pleroma.Activity + alias Pleroma.Object + alias Pleroma.Tests.ObanHelpers + alias Pleroma.User + alias Pleroma.Web.ActivityPub.Transmogrifier + + import Pleroma.Factory + + setup_all do + Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + + test "it works for incoming deletes" do + activity = insert(:note_activity) + deleting_user = insert(:user) + + data = + File.read!("test/fixtures/mastodon-delete.json") + |> Poison.decode!() + |> Map.put("actor", deleting_user.ap_id) + |> put_in(["object", "id"], activity.data["object"]) + + {:ok, %Activity{actor: actor, local: false, data: %{"id" => id}}} = + Transmogrifier.handle_incoming(data) + + assert id == data["id"] + + # We delete the Create activity because we base our timelines on it. + # This should be changed after we unify objects and activities + refute Activity.get_by_id(activity.id) + assert actor == deleting_user.ap_id + + # Objects are replaced by a tombstone object. + object = Object.normalize(activity.data["object"]) + assert object.data["type"] == "Tombstone" + end + + test "it works for incoming when the object has been pruned" do + activity = insert(:note_activity) + + {:ok, object} = + Object.normalize(activity.data["object"]) + |> Repo.delete() + + Cachex.del(:object_cache, "object:#{object.data["id"]}") + + deleting_user = insert(:user) + + data = + File.read!("test/fixtures/mastodon-delete.json") + |> Poison.decode!() + |> Map.put("actor", deleting_user.ap_id) + |> put_in(["object", "id"], activity.data["object"]) + + {:ok, %Activity{actor: actor, local: false, data: %{"id" => id}}} = + Transmogrifier.handle_incoming(data) + + assert id == data["id"] + + # We delete the Create activity because we base our timelines on it. + # This should be changed after we unify objects and activities + refute Activity.get_by_id(activity.id) + assert actor == deleting_user.ap_id + end + + test "it fails for incoming deletes with spoofed origin" do + activity = insert(:note_activity) + %{ap_id: ap_id} = insert(:user, ap_id: "https://gensokyo.2hu/users/raymoo") + + data = + File.read!("test/fixtures/mastodon-delete.json") + |> Poison.decode!() + |> Map.put("actor", ap_id) + |> put_in(["object", "id"], activity.data["object"]) + + assert match?({:error, _}, Transmogrifier.handle_incoming(data)) + end + + @tag capture_log: true + test "it works for incoming user deletes" do + %{ap_id: ap_id} = insert(:user, ap_id: "http://mastodon.example.org/users/admin") + + data = + File.read!("test/fixtures/mastodon-delete-user.json") + |> Poison.decode!() + + {:ok, _} = Transmogrifier.handle_incoming(data) + ObanHelpers.perform_all() + + assert User.get_cached_by_ap_id(ap_id).deactivated + end + + test "it fails for incoming user deletes with spoofed origin" do + %{ap_id: ap_id} = insert(:user) + + data = + File.read!("test/fixtures/mastodon-delete-user.json") + |> Poison.decode!() + |> Map.put("actor", ap_id) + + assert match?({:error, _}, Transmogrifier.handle_incoming(data)) + + assert User.get_cached_by_ap_id(ap_id) + end +end diff --git a/test/web/activity_pub/transmogrifier/emoji_react_handling_test.exs b/test/web/activity_pub/transmogrifier/emoji_react_handling_test.exs new file mode 100644 index 000000000..0fb056b50 --- /dev/null +++ b/test/web/activity_pub/transmogrifier/emoji_react_handling_test.exs @@ -0,0 +1,61 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.Transmogrifier.EmojiReactHandlingTest do + use Pleroma.DataCase + + alias Pleroma.Activity + alias Pleroma.Object + alias Pleroma.Web.ActivityPub.Transmogrifier + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + test "it works for incoming emoji reactions" do + user = insert(:user) + other_user = insert(:user, local: false) + {:ok, activity} = CommonAPI.post(user, %{status: "hello"}) + + data = + File.read!("test/fixtures/emoji-reaction.json") + |> Poison.decode!() + |> Map.put("object", activity.data["object"]) + |> Map.put("actor", other_user.ap_id) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["actor"] == other_user.ap_id + assert data["type"] == "EmojiReact" + assert data["id"] == "http://mastodon.example.org/users/admin#reactions/2" + assert data["object"] == activity.data["object"] + assert data["content"] == "👌" + + object = Object.get_by_ap_id(data["object"]) + + assert object.data["reaction_count"] == 1 + assert match?([["👌", _]], object.data["reactions"]) + end + + test "it reject invalid emoji reactions" do + user = insert(:user) + other_user = insert(:user, local: false) + {:ok, activity} = CommonAPI.post(user, %{status: "hello"}) + + data = + File.read!("test/fixtures/emoji-reaction-too-long.json") + |> Poison.decode!() + |> Map.put("object", activity.data["object"]) + |> Map.put("actor", other_user.ap_id) + + assert {:error, _} = Transmogrifier.handle_incoming(data) + + data = + File.read!("test/fixtures/emoji-reaction-no-emoji.json") + |> Poison.decode!() + |> Map.put("object", activity.data["object"]) + |> Map.put("actor", other_user.ap_id) + + assert {:error, _} = Transmogrifier.handle_incoming(data) + end +end diff --git a/test/web/activity_pub/transmogrifier/like_handling_test.exs b/test/web/activity_pub/transmogrifier/like_handling_test.exs new file mode 100644 index 000000000..53fe1d550 --- /dev/null +++ b/test/web/activity_pub/transmogrifier/like_handling_test.exs @@ -0,0 +1,78 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.Transmogrifier.LikeHandlingTest do + use Pleroma.DataCase + + alias Pleroma.Activity + alias Pleroma.Web.ActivityPub.Transmogrifier + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + test "it works for incoming likes" do + user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "hello"}) + + data = + File.read!("test/fixtures/mastodon-like.json") + |> Poison.decode!() + |> Map.put("object", activity.data["object"]) + + _actor = insert(:user, ap_id: data["actor"], local: false) + + {:ok, %Activity{data: data, local: false} = activity} = Transmogrifier.handle_incoming(data) + + refute Enum.empty?(activity.recipients) + + assert data["actor"] == "http://mastodon.example.org/users/admin" + assert data["type"] == "Like" + assert data["id"] == "http://mastodon.example.org/users/admin#likes/2" + assert data["object"] == activity.data["object"] + end + + test "it works for incoming misskey likes, turning them into EmojiReacts" do + user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "hello"}) + + data = + File.read!("test/fixtures/misskey-like.json") + |> Poison.decode!() + |> Map.put("object", activity.data["object"]) + + _actor = insert(:user, ap_id: data["actor"], local: false) + + {:ok, %Activity{data: activity_data, local: false}} = Transmogrifier.handle_incoming(data) + + assert activity_data["actor"] == data["actor"] + assert activity_data["type"] == "EmojiReact" + assert activity_data["id"] == data["id"] + assert activity_data["object"] == activity.data["object"] + assert activity_data["content"] == "🍮" + end + + test "it works for incoming misskey likes that contain unicode emojis, turning them into EmojiReacts" do + user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "hello"}) + + data = + File.read!("test/fixtures/misskey-like.json") + |> Poison.decode!() + |> Map.put("object", activity.data["object"]) + |> Map.put("_misskey_reaction", "⭐") + + _actor = insert(:user, ap_id: data["actor"], local: false) + + {:ok, %Activity{data: activity_data, local: false}} = Transmogrifier.handle_incoming(data) + + assert activity_data["actor"] == data["actor"] + assert activity_data["type"] == "EmojiReact" + assert activity_data["id"] == data["id"] + assert activity_data["object"] == activity.data["object"] + assert activity_data["content"] == "⭐" + end +end diff --git a/test/web/activity_pub/transmogrifier/undo_handling_test.exs b/test/web/activity_pub/transmogrifier/undo_handling_test.exs new file mode 100644 index 000000000..01dd6c370 --- /dev/null +++ b/test/web/activity_pub/transmogrifier/undo_handling_test.exs @@ -0,0 +1,185 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.Transmogrifier.UndoHandlingTest do + use Pleroma.DataCase + + alias Pleroma.Activity + alias Pleroma.Object + alias Pleroma.User + alias Pleroma.Web.ActivityPub.Transmogrifier + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + test "it works for incoming emoji reaction undos" do + user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "hello"}) + {:ok, reaction_activity} = CommonAPI.react_with_emoji(activity.id, user, "👌") + + data = + File.read!("test/fixtures/mastodon-undo-like.json") + |> Poison.decode!() + |> Map.put("object", reaction_activity.data["id"]) + |> Map.put("actor", user.ap_id) + + {:ok, activity} = Transmogrifier.handle_incoming(data) + + assert activity.actor == user.ap_id + assert activity.data["id"] == data["id"] + assert activity.data["type"] == "Undo" + end + + test "it returns an error for incoming unlikes wihout a like activity" do + user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{status: "leave a like pls"}) + + data = + File.read!("test/fixtures/mastodon-undo-like.json") + |> Poison.decode!() + |> Map.put("object", activity.data["object"]) + + assert Transmogrifier.handle_incoming(data) == :error + end + + test "it works for incoming unlikes with an existing like activity" do + user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{status: "leave a like pls"}) + + like_data = + File.read!("test/fixtures/mastodon-like.json") + |> Poison.decode!() + |> Map.put("object", activity.data["object"]) + + _liker = insert(:user, ap_id: like_data["actor"], local: false) + + {:ok, %Activity{data: like_data, local: false}} = Transmogrifier.handle_incoming(like_data) + + data = + File.read!("test/fixtures/mastodon-undo-like.json") + |> Poison.decode!() + |> Map.put("object", like_data) + |> Map.put("actor", like_data["actor"]) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["actor"] == "http://mastodon.example.org/users/admin" + assert data["type"] == "Undo" + assert data["id"] == "http://mastodon.example.org/users/admin#likes/2/undo" + assert data["object"] == "http://mastodon.example.org/users/admin#likes/2" + + note = Object.get_by_ap_id(like_data["object"]) + assert note.data["like_count"] == 0 + assert note.data["likes"] == [] + end + + test "it works for incoming unlikes with an existing like activity and a compact object" do + user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{status: "leave a like pls"}) + + like_data = + File.read!("test/fixtures/mastodon-like.json") + |> Poison.decode!() + |> Map.put("object", activity.data["object"]) + + _liker = insert(:user, ap_id: like_data["actor"], local: false) + + {:ok, %Activity{data: like_data, local: false}} = Transmogrifier.handle_incoming(like_data) + + data = + File.read!("test/fixtures/mastodon-undo-like.json") + |> Poison.decode!() + |> Map.put("object", like_data["id"]) + |> Map.put("actor", like_data["actor"]) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["actor"] == "http://mastodon.example.org/users/admin" + assert data["type"] == "Undo" + assert data["id"] == "http://mastodon.example.org/users/admin#likes/2/undo" + assert data["object"] == "http://mastodon.example.org/users/admin#likes/2" + end + + test "it works for incoming unannounces with an existing notice" do + user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{status: "hey"}) + + announce_data = + File.read!("test/fixtures/mastodon-announce.json") + |> Poison.decode!() + |> Map.put("object", activity.data["object"]) + + _announcer = insert(:user, ap_id: announce_data["actor"], local: false) + + {:ok, %Activity{data: announce_data, local: false}} = + Transmogrifier.handle_incoming(announce_data) + + data = + File.read!("test/fixtures/mastodon-undo-announce.json") + |> Poison.decode!() + |> Map.put("object", announce_data) + |> Map.put("actor", announce_data["actor"]) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["type"] == "Undo" + + assert data["object"] == + "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity" + end + + test "it works for incomming unfollows with an existing follow" do + user = insert(:user) + + follow_data = + File.read!("test/fixtures/mastodon-follow-activity.json") + |> Poison.decode!() + |> Map.put("object", user.ap_id) + + _follower = insert(:user, ap_id: follow_data["actor"], local: false) + + {:ok, %Activity{data: _, local: false}} = Transmogrifier.handle_incoming(follow_data) + + data = + File.read!("test/fixtures/mastodon-unfollow-activity.json") + |> Poison.decode!() + |> Map.put("object", follow_data) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["type"] == "Undo" + assert data["object"]["type"] == "Follow" + assert data["object"]["object"] == user.ap_id + assert data["actor"] == "http://mastodon.example.org/users/admin" + + refute User.following?(User.get_cached_by_ap_id(data["actor"]), user) + end + + test "it works for incoming unblocks with an existing block" do + user = insert(:user) + + block_data = + File.read!("test/fixtures/mastodon-block-activity.json") + |> Poison.decode!() + |> Map.put("object", user.ap_id) + + _blocker = insert(:user, ap_id: block_data["actor"], local: false) + + {:ok, %Activity{data: _, local: false}} = Transmogrifier.handle_incoming(block_data) + + data = + File.read!("test/fixtures/mastodon-unblock-activity.json") + |> Poison.decode!() + |> Map.put("object", block_data) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + assert data["type"] == "Undo" + assert data["object"] == block_data["id"] + + blocker = User.get_cached_by_ap_id(data["actor"]) + + refute User.blocks?(blocker, user) + end +end diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs index b2cabbd30..0a54e3bb9 100644 --- a/test/web/activity_pub/transmogrifier_test.exs +++ b/test/web/activity_pub/transmogrifier_test.exs @@ -212,8 +212,8 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do {:ok, activity} = CommonAPI.post(user, %{ - "status" => "suya...", - "poll" => %{"options" => ["suya", "suya.", "suya.."], "expires_in" => 10} + status: "suya...", + poll: %{options: ["suya", "suya.", "suya.."], expires_in: 10} }) object = Object.normalize(activity) @@ -260,6 +260,24 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do "<p>henlo from my Psion netBook</p><p>message sent from my Psion netBook</p>" end + test "it works for incoming honk announces" do + _user = insert(:user, ap_id: "https://honktest/u/test", local: false) + other_user = insert(:user) + {:ok, post} = CommonAPI.post(other_user, %{status: "bonkeronk"}) + + announce = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "actor" => "https://honktest/u/test", + "id" => "https://honktest/u/test/bonk/1793M7B9MQ48847vdx", + "object" => post.data["object"], + "published" => "2019-06-25T19:33:58Z", + "to" => "https://www.w3.org/ns/activitystreams#Public", + "type" => "Announce" + } + + {:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(announce) + end + test "it works for incoming announces with actor being inlined (kroeg)" do data = File.read!("test/fixtures/kroeg-announce-with-inline-actor.json") |> Poison.decode!() @@ -325,178 +343,6 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do assert object_data["cc"] == to end - test "it works for incoming likes" do - user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "hello"}) - - data = - File.read!("test/fixtures/mastodon-like.json") - |> Poison.decode!() - |> Map.put("object", activity.data["object"]) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert data["actor"] == "http://mastodon.example.org/users/admin" - assert data["type"] == "Like" - assert data["id"] == "http://mastodon.example.org/users/admin#likes/2" - assert data["object"] == activity.data["object"] - end - - test "it works for incoming misskey likes, turning them into EmojiReacts" do - user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "hello"}) - - data = - File.read!("test/fixtures/misskey-like.json") - |> Poison.decode!() - |> Map.put("object", activity.data["object"]) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert data["actor"] == data["actor"] - assert data["type"] == "EmojiReact" - assert data["id"] == data["id"] - assert data["object"] == activity.data["object"] - assert data["content"] == "🍮" - end - - test "it works for incoming misskey likes that contain unicode emojis, turning them into EmojiReacts" do - user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "hello"}) - - data = - File.read!("test/fixtures/misskey-like.json") - |> Poison.decode!() - |> Map.put("object", activity.data["object"]) - |> Map.put("_misskey_reaction", "⭐") - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert data["actor"] == data["actor"] - assert data["type"] == "EmojiReact" - assert data["id"] == data["id"] - assert data["object"] == activity.data["object"] - assert data["content"] == "⭐" - end - - test "it works for incoming emoji reactions" do - user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "hello"}) - - data = - File.read!("test/fixtures/emoji-reaction.json") - |> Poison.decode!() - |> Map.put("object", activity.data["object"]) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert data["actor"] == "http://mastodon.example.org/users/admin" - assert data["type"] == "EmojiReact" - assert data["id"] == "http://mastodon.example.org/users/admin#reactions/2" - assert data["object"] == activity.data["object"] - assert data["content"] == "👌" - end - - test "it reject invalid emoji reactions" do - user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "hello"}) - - data = - File.read!("test/fixtures/emoji-reaction-too-long.json") - |> Poison.decode!() - |> Map.put("object", activity.data["object"]) - - assert :error = Transmogrifier.handle_incoming(data) - - data = - File.read!("test/fixtures/emoji-reaction-no-emoji.json") - |> Poison.decode!() - |> Map.put("object", activity.data["object"]) - - assert :error = Transmogrifier.handle_incoming(data) - end - - test "it works for incoming emoji reaction undos" do - user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{"status" => "hello"}) - {:ok, reaction_activity, _object} = CommonAPI.react_with_emoji(activity.id, user, "👌") - - data = - File.read!("test/fixtures/mastodon-undo-like.json") - |> Poison.decode!() - |> Map.put("object", reaction_activity.data["id"]) - |> Map.put("actor", user.ap_id) - - {:ok, activity} = Transmogrifier.handle_incoming(data) - - assert activity.actor == user.ap_id - assert activity.data["id"] == data["id"] - assert activity.data["type"] == "Undo" - end - - test "it returns an error for incoming unlikes wihout a like activity" do - user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "leave a like pls"}) - - data = - File.read!("test/fixtures/mastodon-undo-like.json") - |> Poison.decode!() - |> Map.put("object", activity.data["object"]) - - assert Transmogrifier.handle_incoming(data) == :error - end - - test "it works for incoming unlikes with an existing like activity" do - user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "leave a like pls"}) - - like_data = - File.read!("test/fixtures/mastodon-like.json") - |> Poison.decode!() - |> Map.put("object", activity.data["object"]) - - {:ok, %Activity{data: like_data, local: false}} = Transmogrifier.handle_incoming(like_data) - - data = - File.read!("test/fixtures/mastodon-undo-like.json") - |> Poison.decode!() - |> Map.put("object", like_data) - |> Map.put("actor", like_data["actor"]) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert data["actor"] == "http://mastodon.example.org/users/admin" - assert data["type"] == "Undo" - assert data["id"] == "http://mastodon.example.org/users/admin#likes/2/undo" - assert data["object"]["id"] == "http://mastodon.example.org/users/admin#likes/2" - end - - test "it works for incoming unlikes with an existing like activity and a compact object" do - user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "leave a like pls"}) - - like_data = - File.read!("test/fixtures/mastodon-like.json") - |> Poison.decode!() - |> Map.put("object", activity.data["object"]) - - {:ok, %Activity{data: like_data, local: false}} = Transmogrifier.handle_incoming(like_data) - - data = - File.read!("test/fixtures/mastodon-undo-like.json") - |> Poison.decode!() - |> Map.put("object", like_data["id"]) - |> Map.put("actor", like_data["actor"]) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert data["actor"] == "http://mastodon.example.org/users/admin" - assert data["type"] == "Undo" - assert data["id"] == "http://mastodon.example.org/users/admin#likes/2/undo" - assert data["object"]["id"] == "http://mastodon.example.org/users/admin#likes/2" - end - test "it works for incoming announces" do data = File.read!("test/fixtures/mastodon-announce.json") |> Poison.decode!() @@ -516,7 +362,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do test "it works for incoming announces with an existing activity" do user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"}) + {:ok, activity} = CommonAPI.post(user, %{status: "hey"}) data = File.read!("test/fixtures/mastodon-announce.json") @@ -566,7 +412,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do test "it does not clobber the addressing on announce activities" do user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"}) + {:ok, activity} = CommonAPI.post(user, %{status: "hey"}) data = File.read!("test/fixtures/mastodon-announce.json") @@ -652,8 +498,8 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do test "it strips internal reactions" do user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "#cofe"}) - {:ok, _, _} = CommonAPI.react_with_emoji(activity.id, user, "📢") + {:ok, activity} = CommonAPI.post(user, %{status: "#cofe"}) + {:ok, _} = CommonAPI.react_with_emoji(activity.id, user, "📢") %{object: object} = Activity.get_by_id_with_object(activity.id) assert Map.has_key?(object.data, "reactions") @@ -744,7 +590,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do user = User.get_cached_by_ap_id(activity.actor) - assert User.fields(user) == [ + assert user.fields == [ %{"name" => "foo", "value" => "bar"}, %{"name" => "foo1", "value" => "bar1"} ] @@ -765,7 +611,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do user = User.get_cached_by_ap_id(user.ap_id) - assert User.fields(user) == [ + assert user.fields == [ %{"name" => "foo", "value" => "updated"}, %{"name" => "foo1", "value" => "updated"} ] @@ -783,7 +629,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do user = User.get_cached_by_ap_id(user.ap_id) - assert User.fields(user) == [ + assert user.fields == [ %{"name" => "foo", "value" => "updated"}, %{"name" => "foo1", "value" => "updated"} ] @@ -794,7 +640,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do user = User.get_cached_by_ap_id(user.ap_id) - assert User.fields(user) == [] + assert user.fields == [] end test "it works for incoming update activities which lock the account" do @@ -820,112 +666,6 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do assert user.locked == true end - test "it works for incoming deletes" do - activity = insert(:note_activity) - deleting_user = insert(:user) - - data = - File.read!("test/fixtures/mastodon-delete.json") - |> Poison.decode!() - - object = - data["object"] - |> Map.put("id", activity.data["object"]) - - data = - data - |> Map.put("object", object) - |> Map.put("actor", deleting_user.ap_id) - - {:ok, %Activity{actor: actor, local: false, data: %{"id" => id}}} = - Transmogrifier.handle_incoming(data) - - assert id == data["id"] - refute Activity.get_by_id(activity.id) - assert actor == deleting_user.ap_id - end - - test "it fails for incoming deletes with spoofed origin" do - activity = insert(:note_activity) - - data = - File.read!("test/fixtures/mastodon-delete.json") - |> Poison.decode!() - - object = - data["object"] - |> Map.put("id", activity.data["object"]) - - data = - data - |> Map.put("object", object) - - assert capture_log(fn -> - :error = Transmogrifier.handle_incoming(data) - end) =~ - "[error] Could not decode user at fetch http://mastodon.example.org/users/gargron, {:error, :nxdomain}" - - assert Activity.get_by_id(activity.id) - end - - @tag capture_log: true - test "it works for incoming user deletes" do - %{ap_id: ap_id} = insert(:user, ap_id: "http://mastodon.example.org/users/admin") - - data = - File.read!("test/fixtures/mastodon-delete-user.json") - |> Poison.decode!() - - {:ok, _} = Transmogrifier.handle_incoming(data) - ObanHelpers.perform_all() - - refute User.get_cached_by_ap_id(ap_id) - end - - test "it fails for incoming user deletes with spoofed origin" do - %{ap_id: ap_id} = insert(:user) - - data = - File.read!("test/fixtures/mastodon-delete-user.json") - |> Poison.decode!() - |> Map.put("actor", ap_id) - - assert capture_log(fn -> - assert :error == Transmogrifier.handle_incoming(data) - end) =~ "Object containment failed" - - assert User.get_cached_by_ap_id(ap_id) - end - - test "it works for incoming unannounces with an existing notice" do - user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"}) - - announce_data = - File.read!("test/fixtures/mastodon-announce.json") - |> Poison.decode!() - |> Map.put("object", activity.data["object"]) - - {:ok, %Activity{data: announce_data, local: false}} = - Transmogrifier.handle_incoming(announce_data) - - data = - File.read!("test/fixtures/mastodon-undo-announce.json") - |> Poison.decode!() - |> Map.put("object", announce_data) - |> Map.put("actor", announce_data["actor"]) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert data["type"] == "Undo" - assert object_data = data["object"] - assert object_data["type"] == "Announce" - assert object_data["object"] == activity.data["object"] - - assert object_data["id"] == - "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity" - end - test "it works for incomming unfollows with an existing follow" do user = insert(:user) @@ -1020,32 +760,6 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do refute User.following?(blocked, blocker) end - test "it works for incoming unblocks with an existing block" do - user = insert(:user) - - block_data = - File.read!("test/fixtures/mastodon-block-activity.json") - |> Poison.decode!() - |> Map.put("object", user.ap_id) - - {:ok, %Activity{data: _, local: false}} = Transmogrifier.handle_incoming(block_data) - - data = - File.read!("test/fixtures/mastodon-unblock-activity.json") - |> Poison.decode!() - |> Map.put("object", block_data) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - assert data["type"] == "Undo" - assert data["object"]["type"] == "Block" - assert data["object"]["object"] == user.ap_id - assert data["actor"] == "http://mastodon.example.org/users/admin" - - blocker = User.get_cached_by_ap_id(data["actor"]) - - refute User.blocks?(blocker, user) - end - test "it works for incoming accepts which were pre-accepted" do follower = insert(:user) followed = insert(:user) @@ -1119,6 +833,12 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do follower = User.get_cached_by_id(follower.id) assert User.following?(follower, followed) == true + + follower = User.get_by_id(follower.id) + assert follower.following_count == 1 + + followed = User.get_by_id(followed.id) + assert followed.follower_count == 1 end test "it fails for incoming accepts which cannot be correlated" do @@ -1219,6 +939,35 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do :error = Transmogrifier.handle_incoming(data) end + test "skip converting the content when it is nil" do + object_id = "https://peertube.social/videos/watch/278d2b7c-0f38-4aaa-afe6-9ecc0c4a34fe" + + {:ok, object} = Fetcher.fetch_and_contain_remote_object_from_id(object_id) + + result = + Pleroma.Web.ActivityPub.Transmogrifier.fix_object(Map.merge(object, %{"content" => nil})) + + assert result["content"] == nil + end + + test "it converts content of object to html" do + object_id = "https://peertube.social/videos/watch/278d2b7c-0f38-4aaa-afe6-9ecc0c4a34fe" + + {:ok, %{"content" => content_markdown}} = + Fetcher.fetch_and_contain_remote_object_from_id(object_id) + + {:ok, %Pleroma.Object{data: %{"content" => content}} = object} = + Fetcher.fetch_object_from_id(object_id) + + assert content_markdown == + "Support this and our other Michigan!/usr/group videos and meetings. Learn more at http://mug.org/membership\n\nTwenty Years in Jail: FreeBSD's Jails, Then and Now\n\nJails started as a limited virtualization system, but over the last two years they've..." + + assert content == + "<p>Support this and our other Michigan!/usr/group videos and meetings. Learn more at <a href=\"http://mug.org/membership\">http://mug.org/membership</a></p><p>Twenty Years in Jail: FreeBSD’s Jails, Then and Now</p><p>Jails started as a limited virtualization system, but over the last two years they’ve…</p>" + + assert object.data["mediaType"] == "text/html" + end + test "it remaps video URLs as attachments if necessary" do {:ok, object} = Fetcher.fetch_object_from_id( @@ -1228,19 +977,13 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do attachment = %{ "type" => "Link", "mediaType" => "video/mp4", - "href" => - "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4", - "mimeType" => "video/mp4", - "size" => 5_015_880, "url" => [ %{ "href" => "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4", - "mediaType" => "video/mp4", - "type" => "Link" + "mediaType" => "video/mp4" } - ], - "width" => 480 + ] } assert object.data["url"] == @@ -1253,7 +996,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do user = insert(:user) other_user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "test post"}) + {:ok, activity} = CommonAPI.post(user, %{status: "test post"}) object = Object.normalize(activity) note_obj = %{ @@ -1397,13 +1140,13 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do setup do user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "post1"}) + {:ok, activity} = CommonAPI.post(user, %{status: "post1"}) {:ok, reply1} = - CommonAPI.post(user, %{"status" => "reply1", "in_reply_to_status_id" => activity.id}) + CommonAPI.post(user, %{status: "reply1", in_reply_to_status_id: activity.id}) {:ok, reply2} = - CommonAPI.post(user, %{"status" => "reply2", "in_reply_to_status_id" => activity.id}) + CommonAPI.post(user, %{status: "reply2", in_reply_to_status_id: activity.id}) replies_uris = Enum.map([reply1, reply2], fn a -> a.object.data["id"] end) @@ -1443,7 +1186,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do test "it inlines private announced objects" do user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "hey", "visibility" => "private"}) + {:ok, activity} = CommonAPI.post(user, %{status: "hey", visibility: "private"}) {:ok, announce_activity, _} = CommonAPI.repeat(activity.id, user) @@ -1458,7 +1201,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do other_user = insert(:user) {:ok, activity} = - CommonAPI.post(user, %{"status" => "hey, @#{other_user.nickname}, how are ya? #2hu"}) + CommonAPI.post(user, %{status: "hey, @#{other_user.nickname}, how are ya? #2hu"}) {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) object = modified["object"] @@ -1482,7 +1225,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do test "it adds the sensitive property" do user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "#nsfw hey"}) + {:ok, activity} = CommonAPI.post(user, %{status: "#nsfw hey"}) {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) assert modified["object"]["sensitive"] @@ -1491,7 +1234,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do test "it adds the json-ld context and the conversation property" do user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"}) + {:ok, activity} = CommonAPI.post(user, %{status: "hey"}) {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) assert modified["@context"] == @@ -1503,7 +1246,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do test "it sets the 'attributedTo' property to the actor of the object if it doesn't have one" do user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"}) + {:ok, activity} = CommonAPI.post(user, %{status: "hey"}) {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) assert modified["object"]["actor"] == modified["object"]["attributedTo"] @@ -1512,7 +1255,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do test "it strips internal hashtag data" do user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "#2hu"}) + {:ok, activity} = CommonAPI.post(user, %{status: "#2hu"}) expected_tag = %{ "href" => Pleroma.Web.Endpoint.url() <> "/tags/2hu", @@ -1528,7 +1271,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do test "it strips internal fields" do user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "#2hu :firefox:"}) + {:ok, activity} = CommonAPI.post(user, %{status: "#2hu :firefox:"}) {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) @@ -1560,14 +1303,13 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do user = insert(:user) other_user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "2hu :moominmamma:"}) + {:ok, activity} = CommonAPI.post(user, %{status: "2hu :moominmamma:"}) {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) assert modified["directMessage"] == false - {:ok, activity} = - CommonAPI.post(user, %{"status" => "@#{other_user.nickname} :moominmamma:"}) + {:ok, activity} = CommonAPI.post(user, %{status: "@#{other_user.nickname} :moominmamma:"}) {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) @@ -1575,8 +1317,8 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do {:ok, activity} = CommonAPI.post(user, %{ - "status" => "@#{other_user.nickname} :moominmamma:", - "visibility" => "direct" + status: "@#{other_user.nickname} :moominmamma:", + visibility: "direct" }) {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) @@ -1588,8 +1330,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do user = insert(:user) {:ok, list} = Pleroma.List.create("foo", user) - {:ok, activity} = - CommonAPI.post(user, %{"status" => "foobar", "visibility" => "list:#{list.id}"}) + {:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"}) {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) @@ -1622,10 +1363,10 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do }) user_two = insert(:user) - Pleroma.FollowingRelationship.follow(user_two, user, "accept") + Pleroma.FollowingRelationship.follow(user_two, user, :follow_accept) - {:ok, activity} = CommonAPI.post(user, %{"status" => "test"}) - {:ok, unrelated_activity} = CommonAPI.post(user_two, %{"status" => "test"}) + {:ok, activity} = CommonAPI.post(user, %{status: "test"}) + {:ok, unrelated_activity} = CommonAPI.post(user_two, %{status: "test"}) assert "http://localhost:4001/users/rye@niu.moe/followers" in activity.recipients user = User.get_cached_by_id(user.id) @@ -1791,8 +1532,8 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do {:ok, poll_activity} = CommonAPI.post(user, %{ - "status" => "suya...", - "poll" => %{"options" => ["suya", "suya.", "suya.."], "expires_in" => 10} + status: "suya...", + poll: %{options: ["suya", "suya.", "suya.."], expires_in: 10} }) poll_object = Object.normalize(poll_activity) @@ -2061,11 +1802,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do %{ "mediaType" => "video/mp4", "url" => [ - %{ - "href" => "https://peertube.moe/stat-480.mp4", - "mediaType" => "video/mp4", - "type" => "Link" - } + %{"href" => "https://peertube.moe/stat-480.mp4", "mediaType" => "video/mp4"} ] } ] @@ -2083,23 +1820,13 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do %{ "mediaType" => "video/mp4", "url" => [ - %{ - "href" => "https://pe.er/stat-480.mp4", - "mediaType" => "video/mp4", - "type" => "Link" - } + %{"href" => "https://pe.er/stat-480.mp4", "mediaType" => "video/mp4"} ] }, %{ - "href" => "https://pe.er/stat-480.mp4", "mediaType" => "video/mp4", - "mimeType" => "video/mp4", "url" => [ - %{ - "href" => "https://pe.er/stat-480.mp4", - "mediaType" => "video/mp4", - "type" => "Link" - } + %{"href" => "https://pe.er/stat-480.mp4", "mediaType" => "video/mp4"} ] } ] @@ -2149,28 +1876,27 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do test "sets `replies` collection with a limited number of self-replies" do [user, another_user] = insert_list(2, :user) - {:ok, %{id: id1} = activity} = CommonAPI.post(user, %{"status" => "1"}) + {:ok, %{id: id1} = activity} = CommonAPI.post(user, %{status: "1"}) {:ok, %{id: id2} = self_reply1} = - CommonAPI.post(user, %{"status" => "self-reply 1", "in_reply_to_status_id" => id1}) + CommonAPI.post(user, %{status: "self-reply 1", in_reply_to_status_id: id1}) {:ok, self_reply2} = - CommonAPI.post(user, %{"status" => "self-reply 2", "in_reply_to_status_id" => id1}) + CommonAPI.post(user, %{status: "self-reply 2", in_reply_to_status_id: id1}) # Assuming to _not_ be present in `replies` due to :note_replies_output_limit is set to 2 - {:ok, _} = - CommonAPI.post(user, %{"status" => "self-reply 3", "in_reply_to_status_id" => id1}) + {:ok, _} = CommonAPI.post(user, %{status: "self-reply 3", in_reply_to_status_id: id1}) {:ok, _} = CommonAPI.post(user, %{ - "status" => "self-reply to self-reply", - "in_reply_to_status_id" => id2 + status: "self-reply to self-reply", + in_reply_to_status_id: id2 }) {:ok, _} = CommonAPI.post(another_user, %{ - "status" => "another user's reply", - "in_reply_to_status_id" => id1 + status: "another user's reply", + in_reply_to_status_id: id1 }) object = Object.normalize(activity) @@ -2180,4 +1906,18 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do Transmogrifier.set_replies(object.data)["replies"] end end + + test "take_emoji_tags/1" do + user = insert(:user, %{emoji: %{"firefox" => "https://example.org/firefox.png"}}) + + assert Transmogrifier.take_emoji_tags(user) == [ + %{ + "icon" => %{"type" => "Image", "url" => "https://example.org/firefox.png"}, + "id" => "https://example.org/firefox.png", + "name" => ":firefox:", + "type" => "Emoji", + "updated" => "1970-01-01T00:00:00Z" + } + ] + end end diff --git a/test/web/activity_pub/utils_test.exs b/test/web/activity_pub/utils_test.exs index e913a5148..9e0a0f1c4 100644 --- a/test/web/activity_pub/utils_test.exs +++ b/test/web/activity_pub/utils_test.exs @@ -102,34 +102,6 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do end end - describe "make_unlike_data/3" do - test "returns data for unlike activity" do - user = insert(:user) - like_activity = insert(:like_activity, data_attrs: %{"context" => "test context"}) - - object = Object.normalize(like_activity.data["object"]) - - assert Utils.make_unlike_data(user, like_activity, nil) == %{ - "type" => "Undo", - "actor" => user.ap_id, - "object" => like_activity.data, - "to" => [user.follower_address, object.data["actor"]], - "cc" => [Pleroma.Constants.as_public()], - "context" => like_activity.data["context"] - } - - assert Utils.make_unlike_data(user, like_activity, "9mJEZK0tky1w2xD2vY") == %{ - "type" => "Undo", - "actor" => user.ap_id, - "object" => like_activity.data, - "to" => [user.follower_address, object.data["actor"]], - "cc" => [Pleroma.Constants.as_public()], - "context" => like_activity.data["context"], - "id" => "9mJEZK0tky1w2xD2vY" - } - end - end - describe "make_like_data" do setup do user = insert(:user) @@ -148,7 +120,7 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do {:ok, activity} = CommonAPI.post(user, %{ - "status" => + status: "hey @#{other_user.nickname}, @#{third_user.nickname} how about beering together this weekend?" }) @@ -167,8 +139,8 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do {:ok, activity} = CommonAPI.post(user, %{ - "status" => "@#{other_user.nickname} @#{third_user.nickname} bought a new swimsuit!", - "visibility" => "private" + status: "@#{other_user.nickname} @#{third_user.nickname} bought a new swimsuit!", + visibility: "private" }) %{"to" => to, "cc" => cc} = Utils.make_like_data(other_user, activity, nil) @@ -196,11 +168,11 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do {:ok, activity} = CommonAPI.post(user, %{ - "status" => "How do I pronounce LaTeX?", - "poll" => %{ - "options" => ["laytekh", "lahtekh", "latex"], - "expires_in" => 20, - "multiple" => true + status: "How do I pronounce LaTeX?", + poll: %{ + options: ["laytekh", "lahtekh", "latex"], + expires_in: 20, + multiple: true } }) @@ -215,17 +187,16 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do {:ok, activity} = CommonAPI.post(user, %{ - "status" => "Are we living in a society?", - "poll" => %{ - "options" => ["yes", "no"], - "expires_in" => 20 + status: "Are we living in a society?", + poll: %{ + options: ["yes", "no"], + expires_in: 20 } }) object = Object.normalize(activity) {:ok, [vote], object} = CommonAPI.vote(other_user, object, [0]) - vote_object = Object.normalize(vote) - {:ok, _activity, _object} = ActivityPub.like(user, vote_object) + {:ok, _activity} = CommonAPI.favorite(user, activity.id) [fetched_vote] = Utils.get_existing_votes(other_user.ap_id, object) assert fetched_vote.id == vote.id end @@ -346,7 +317,7 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do user = insert(:user) refute Utils.get_existing_like(user.ap_id, object) - {:ok, like_activity, _object} = ActivityPub.like(user, object) + {:ok, like_activity} = CommonAPI.favorite(user, note_activity.id) assert ^like_activity = Utils.get_existing_like(user.ap_id, object) end @@ -498,7 +469,7 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do test "returns map with Flag object" do reporter = insert(:user) target_account = insert(:user) - {:ok, activity} = CommonAPI.post(target_account, %{"status" => "foobar"}) + {:ok, activity} = CommonAPI.post(target_account, %{status: "foobar"}) context = Utils.generate_context_id() content = "foobar" diff --git a/test/web/activity_pub/views/object_view_test.exs b/test/web/activity_pub/views/object_view_test.exs index de5ffc5b3..43f0617f0 100644 --- a/test/web/activity_pub/views/object_view_test.exs +++ b/test/web/activity_pub/views/object_view_test.exs @@ -44,7 +44,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectViewTest do activity = insert(:note_activity, user: user) {:ok, self_reply1} = - CommonAPI.post(user, %{"status" => "self-reply 1", "in_reply_to_status_id" => activity.id}) + CommonAPI.post(user, %{status: "self-reply 1", in_reply_to_status_id: activity.id}) replies_uris = [self_reply1.object.data["id"]] result = ObjectView.render("object.json", %{object: refresh_record(activity)}) @@ -59,7 +59,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectViewTest do object = Object.normalize(note) user = insert(:user) - {:ok, like_activity, _} = CommonAPI.favorite(note.id, user) + {:ok, like_activity} = CommonAPI.favorite(user, note.id) result = ObjectView.render("object.json", %{object: like_activity}) diff --git a/test/web/activity_pub/views/user_view_test.exs b/test/web/activity_pub/views/user_view_test.exs index ecb2dc386..20b0f223c 100644 --- a/test/web/activity_pub/views/user_view_test.exs +++ b/test/web/activity_pub/views/user_view_test.exs @@ -29,7 +29,7 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do {:ok, user} = insert(:user) - |> User.upgrade_changeset(%{fields: fields}) + |> User.update_changeset(%{fields: fields}) |> User.update_and_set_cache() assert %{ @@ -38,7 +38,7 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do end test "Renders with emoji tags" do - user = insert(:user, emoji: [%{"bib" => "/test"}]) + user = insert(:user, emoji: %{"bib" => "/test"}) assert %{ "tag" => [ @@ -164,7 +164,7 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do posts = for i <- 0..25 do - {:ok, activity} = CommonAPI.post(user, %{"status" => "post #{i}"}) + {:ok, activity} = CommonAPI.post(user, %{status: "post #{i}"}) activity end diff --git a/test/web/activity_pub/visibilty_test.exs b/test/web/activity_pub/visibilty_test.exs index 5b91630d4..8e9354c65 100644 --- a/test/web/activity_pub/visibilty_test.exs +++ b/test/web/activity_pub/visibilty_test.exs @@ -21,21 +21,21 @@ defmodule Pleroma.Web.ActivityPub.VisibilityTest do Pleroma.List.follow(list, unrelated) {:ok, public} = - CommonAPI.post(user, %{"status" => "@#{mentioned.nickname}", "visibility" => "public"}) + CommonAPI.post(user, %{status: "@#{mentioned.nickname}", visibility: "public"}) {:ok, private} = - CommonAPI.post(user, %{"status" => "@#{mentioned.nickname}", "visibility" => "private"}) + CommonAPI.post(user, %{status: "@#{mentioned.nickname}", visibility: "private"}) {:ok, direct} = - CommonAPI.post(user, %{"status" => "@#{mentioned.nickname}", "visibility" => "direct"}) + CommonAPI.post(user, %{status: "@#{mentioned.nickname}", visibility: "direct"}) {:ok, unlisted} = - CommonAPI.post(user, %{"status" => "@#{mentioned.nickname}", "visibility" => "unlisted"}) + CommonAPI.post(user, %{status: "@#{mentioned.nickname}", visibility: "unlisted"}) {:ok, list} = CommonAPI.post(user, %{ - "status" => "@#{mentioned.nickname}", - "visibility" => "list:#{list.id}" + status: "@#{mentioned.nickname}", + visibility: "list:#{list.id}" }) %{ diff --git a/test/web/admin_api/admin_api_controller_test.exs b/test/web/admin_api/admin_api_controller_test.exs index 0a902585d..370d876d0 100644 --- a/test/web/admin_api/admin_api_controller_test.exs +++ b/test/web/admin_api/admin_api_controller_test.exs @@ -6,22 +6,24 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do use Pleroma.Web.ConnCase use Oban.Testing, repo: Pleroma.Repo - import Pleroma.Factory import ExUnit.CaptureLog + import Mock + import Pleroma.Factory alias Pleroma.Activity alias Pleroma.Config alias Pleroma.ConfigDB alias Pleroma.HTML + alias Pleroma.MFA alias Pleroma.ModerationLog alias Pleroma.Repo alias Pleroma.ReportNote alias Pleroma.Tests.ObanHelpers alias Pleroma.User alias Pleroma.UserInviteToken + alias Pleroma.Web alias Pleroma.Web.ActivityPub.Relay alias Pleroma.Web.CommonAPI - alias Pleroma.Web.MastodonAPI.StatusView alias Pleroma.Web.MediaProxy setup_all do @@ -147,17 +149,26 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do test "single user", %{admin: admin, conn: conn} do user = insert(:user) - conn = - conn - |> put_req_header("accept", "application/json") - |> delete("/api/pleroma/admin/users?nickname=#{user.nickname}") + with_mock Pleroma.Web.Federator, + publish: fn _ -> nil end do + conn = + conn + |> put_req_header("accept", "application/json") + |> delete("/api/pleroma/admin/users?nickname=#{user.nickname}") - log_entry = Repo.one(ModerationLog) + ObanHelpers.perform_all() - assert ModerationLog.get_log_entry_message(log_entry) == - "@#{admin.nickname} deleted users: @#{user.nickname}" + assert User.get_by_nickname(user.nickname).deactivated + + log_entry = Repo.one(ModerationLog) + + assert ModerationLog.get_log_entry_message(log_entry) == + "@#{admin.nickname} deleted users: @#{user.nickname}" - assert json_response(conn, 200) == user.nickname + assert json_response(conn, 200) == [user.nickname] + + assert called(Pleroma.Web.Federator.publish(:_)) + end end test "multiple users", %{admin: admin, conn: conn} do @@ -626,6 +637,39 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do assert json_response(conn, :forbidden) end + + test "email with +", %{conn: conn, admin: admin} do + recipient_email = "foo+bar@baz.com" + + conn + |> put_req_header("content-type", "application/json;charset=utf-8") + |> post("/api/pleroma/admin/users/email_invite", %{email: recipient_email}) + |> json_response(:no_content) + + token_record = + Pleroma.UserInviteToken + |> Repo.all() + |> List.last() + + assert token_record + refute token_record.used + + notify_email = Config.get([:instance, :notify_email]) + instance_name = Config.get([:instance, :name]) + + email = + Pleroma.Emails.UserEmail.user_invitation_email( + admin, + token_record, + recipient_email + ) + + Swoosh.TestAssertions.assert_email_sent( + from: {instance_name, notify_email}, + to: recipient_email, + html_body: email.html_body + ) + end end describe "POST /api/pleroma/admin/users/email_invite, with invalid config" do @@ -638,7 +682,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do conn = post(conn, "/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD") - assert json_response(conn, :internal_server_error) + assert json_response(conn, :bad_request) == + "To send invites you need to set the `invites_enabled` option to true." end test "it returns 500 if `registrations_open` is enabled", %{conn: conn} do @@ -647,7 +692,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do conn = post(conn, "/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD") - assert json_response(conn, :internal_server_error) + assert json_response(conn, :bad_request) == + "To send invites you need to set the `registrations_open` option to false." end end @@ -703,6 +749,39 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do } end + test "pagination works correctly with service users", %{conn: conn} do + service1 = insert(:user, ap_id: Web.base_url() <> "/relay") + service2 = insert(:user, ap_id: Web.base_url() <> "/internal/fetch") + insert_list(25, :user) + + assert %{"count" => 26, "page_size" => 10, "users" => users1} = + conn + |> get("/api/pleroma/admin/users?page=1&filters=", %{page_size: "10"}) + |> json_response(200) + + assert Enum.count(users1) == 10 + assert service1 not in [users1] + assert service2 not in [users1] + + assert %{"count" => 26, "page_size" => 10, "users" => users2} = + conn + |> get("/api/pleroma/admin/users?page=2&filters=", %{page_size: "10"}) + |> json_response(200) + + assert Enum.count(users2) == 10 + assert service1 not in [users2] + assert service2 not in [users2] + + assert %{"count" => 26, "page_size" => 10, "users" => users3} = + conn + |> get("/api/pleroma/admin/users?page=3&filters=", %{page_size: "10"}) + |> json_response(200) + + assert Enum.count(users3) == 6 + assert service1 not in [users3] + assert service2 not in [users3] + end + test "renders empty array for the second page", %{conn: conn} do insert(:user) @@ -1200,6 +1279,38 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do "@#{admin.nickname} deactivated users: @#{user.nickname}" end + describe "PUT disable_mfa" do + test "returns 200 and disable 2fa", %{conn: conn} do + user = + insert(:user, + multi_factor_authentication_settings: %MFA.Settings{ + enabled: true, + totp: %MFA.Settings.TOTP{secret: "otp_secret", confirmed: true} + } + ) + + response = + conn + |> put("/api/pleroma/admin/users/disable_mfa", %{nickname: user.nickname}) + |> json_response(200) + + assert response == user.nickname + mfa_settings = refresh_record(user).multi_factor_authentication_settings + + refute mfa_settings.enabled + refute mfa_settings.totp.confirmed + end + + test "returns 404 if user not found", %{conn: conn} do + response = + conn + |> put("/api/pleroma/admin/users/disable_mfa", %{nickname: "nickname"}) + |> json_response(404) + + assert response == "Not found" + end + end + describe "POST /api/pleroma/admin/users/invite_token" do test "without options", %{conn: conn} do conn = post(conn, "/api/pleroma/admin/users/invite_token") @@ -1313,9 +1424,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do {:ok, %{id: report_id}} = CommonAPI.report(reporter, %{ - "account_id" => target_user.id, - "comment" => "I feel offended", - "status_ids" => [activity.id] + account_id: target_user.id, + comment: "I feel offended", + status_ids: [activity.id] }) response = @@ -1340,16 +1451,16 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do {:ok, %{id: report_id}} = CommonAPI.report(reporter, %{ - "account_id" => target_user.id, - "comment" => "I feel offended", - "status_ids" => [activity.id] + account_id: target_user.id, + comment: "I feel offended", + status_ids: [activity.id] }) {:ok, %{id: second_report_id}} = CommonAPI.report(reporter, %{ - "account_id" => target_user.id, - "comment" => "I feel very offended", - "status_ids" => [activity.id] + account_id: target_user.id, + comment: "I feel very offended", + status_ids: [activity.id] }) %{ @@ -1489,9 +1600,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do {:ok, %{id: report_id}} = CommonAPI.report(reporter, %{ - "account_id" => target_user.id, - "comment" => "I feel offended", - "status_ids" => [activity.id] + account_id: target_user.id, + comment: "I feel offended", + status_ids: [activity.id] }) response = @@ -1513,15 +1624,15 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do {:ok, %{id: first_report_id}} = CommonAPI.report(reporter, %{ - "account_id" => target_user.id, - "comment" => "I feel offended", - "status_ids" => [activity.id] + account_id: target_user.id, + comment: "I feel offended", + status_ids: [activity.id] }) {:ok, %{id: second_report_id}} = CommonAPI.report(reporter, %{ - "account_id" => target_user.id, - "comment" => "I don't like this user" + account_id: target_user.id, + comment: "I don't like this user" }) CommonAPI.update_report_state(second_report_id, "closed") @@ -1586,205 +1697,22 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do end end - describe "GET /api/pleroma/admin/grouped_reports" do - setup do - [reporter, target_user] = insert_pair(:user) - - date1 = (DateTime.to_unix(DateTime.utc_now()) + 1000) |> DateTime.from_unix!() - date2 = (DateTime.to_unix(DateTime.utc_now()) + 2000) |> DateTime.from_unix!() - date3 = (DateTime.to_unix(DateTime.utc_now()) + 3000) |> DateTime.from_unix!() - - first_status = - insert(:note_activity, user: target_user, data_attrs: %{"published" => date1}) - - second_status = - insert(:note_activity, user: target_user, data_attrs: %{"published" => date2}) - - third_status = - insert(:note_activity, user: target_user, data_attrs: %{"published" => date3}) - - {:ok, first_report} = - CommonAPI.report(reporter, %{ - "account_id" => target_user.id, - "status_ids" => [first_status.id, second_status.id, third_status.id] - }) - - {:ok, second_report} = - CommonAPI.report(reporter, %{ - "account_id" => target_user.id, - "status_ids" => [first_status.id, second_status.id] - }) - - {:ok, third_report} = - CommonAPI.report(reporter, %{ - "account_id" => target_user.id, - "status_ids" => [first_status.id] - }) - - %{ - first_status: Activity.get_by_ap_id_with_object(first_status.data["id"]), - second_status: Activity.get_by_ap_id_with_object(second_status.data["id"]), - third_status: Activity.get_by_ap_id_with_object(third_status.data["id"]), - first_report: first_report, - first_status_reports: [first_report, second_report, third_report], - second_status_reports: [first_report, second_report], - third_status_reports: [first_report], - target_user: target_user, - reporter: reporter - } - end - - test "returns reports grouped by status", %{ - conn: conn, - first_status: first_status, - second_status: second_status, - third_status: third_status, - first_status_reports: first_status_reports, - second_status_reports: second_status_reports, - third_status_reports: third_status_reports, - target_user: target_user, - reporter: reporter - } do - response = - conn - |> get("/api/pleroma/admin/grouped_reports") - |> json_response(:ok) - - assert length(response["reports"]) == 3 - - first_group = Enum.find(response["reports"], &(&1["status"]["id"] == first_status.id)) - - second_group = Enum.find(response["reports"], &(&1["status"]["id"] == second_status.id)) - - third_group = Enum.find(response["reports"], &(&1["status"]["id"] == third_status.id)) - - assert length(first_group["reports"]) == 3 - assert length(second_group["reports"]) == 2 - assert length(third_group["reports"]) == 1 - - assert first_group["date"] == - Enum.max_by(first_status_reports, fn act -> - NaiveDateTime.from_iso8601!(act.data["published"]) - end).data["published"] - - assert first_group["status"] == - Map.put( - stringify_keys(StatusView.render("show.json", %{activity: first_status})), - "deleted", - false - ) - - assert(first_group["account"]["id"] == target_user.id) - - assert length(first_group["actors"]) == 1 - assert hd(first_group["actors"])["id"] == reporter.id - - assert Enum.map(first_group["reports"], & &1["id"]) -- - Enum.map(first_status_reports, & &1.id) == [] - - assert second_group["date"] == - Enum.max_by(second_status_reports, fn act -> - NaiveDateTime.from_iso8601!(act.data["published"]) - end).data["published"] - - assert second_group["status"] == - Map.put( - stringify_keys(StatusView.render("show.json", %{activity: second_status})), - "deleted", - false - ) - - assert second_group["account"]["id"] == target_user.id - - assert length(second_group["actors"]) == 1 - assert hd(second_group["actors"])["id"] == reporter.id - - assert Enum.map(second_group["reports"], & &1["id"]) -- - Enum.map(second_status_reports, & &1.id) == [] - - assert third_group["date"] == - Enum.max_by(third_status_reports, fn act -> - NaiveDateTime.from_iso8601!(act.data["published"]) - end).data["published"] - - assert third_group["status"] == - Map.put( - stringify_keys(StatusView.render("show.json", %{activity: third_status})), - "deleted", - false - ) - - assert third_group["account"]["id"] == target_user.id - - assert length(third_group["actors"]) == 1 - assert hd(third_group["actors"])["id"] == reporter.id - - assert Enum.map(third_group["reports"], & &1["id"]) -- - Enum.map(third_status_reports, & &1.id) == [] - end - - test "reopened report renders status data", %{ - conn: conn, - first_report: first_report, - first_status: first_status - } do - {:ok, _} = CommonAPI.update_report_state(first_report.id, "resolved") - - response = - conn - |> get("/api/pleroma/admin/grouped_reports") - |> json_response(:ok) - - first_group = Enum.find(response["reports"], &(&1["status"]["id"] == first_status.id)) - - assert first_group["status"] == - Map.put( - stringify_keys(StatusView.render("show.json", %{activity: first_status})), - "deleted", - false - ) - end - - test "reopened report does not render status data if status has been deleted", %{ - conn: conn, - first_report: first_report, - first_status: first_status, - target_user: target_user - } do - {:ok, _} = CommonAPI.update_report_state(first_report.id, "resolved") - {:ok, _} = CommonAPI.delete(first_status.id, target_user) - - refute Activity.get_by_ap_id(first_status.id) - - response = - conn - |> get("/api/pleroma/admin/grouped_reports") - |> json_response(:ok) - - assert Enum.find(response["reports"], &(&1["status"]["deleted"] == true))["status"][ - "deleted" - ] == true - - assert length(Enum.filter(response["reports"], &(&1["status"]["deleted"] == false))) == 2 + describe "GET /api/pleroma/admin/statuses/:id" do + test "not found", %{conn: conn} do + assert conn + |> get("/api/pleroma/admin/statuses/not_found") + |> json_response(:not_found) end - test "account not empty if status was deleted", %{ - conn: conn, - first_report: first_report, - first_status: first_status, - target_user: target_user - } do - {:ok, _} = CommonAPI.update_report_state(first_report.id, "resolved") - {:ok, _} = CommonAPI.delete(first_status.id, target_user) - - refute Activity.get_by_ap_id(first_status.id) + test "shows activity", %{conn: conn} do + activity = insert(:note_activity) response = conn - |> get("/api/pleroma/admin/grouped_reports") - |> json_response(:ok) + |> get("/api/pleroma/admin/statuses/#{activity.id}") + |> json_response(200) - assert Enum.find(response["reports"], &(&1["status"]["deleted"] == true))["account"] + assert response["id"] == activity.id end end @@ -1819,7 +1747,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do test "change visibility flag", %{conn: conn, id: id, admin: admin} do response = conn - |> put("/api/pleroma/admin/statuses/#{id}", %{"visibility" => "public"}) + |> put("/api/pleroma/admin/statuses/#{id}", %{visibility: "public"}) |> json_response(:ok) assert response["visibility"] == "public" @@ -1831,21 +1759,21 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do response = conn - |> put("/api/pleroma/admin/statuses/#{id}", %{"visibility" => "private"}) + |> put("/api/pleroma/admin/statuses/#{id}", %{visibility: "private"}) |> json_response(:ok) assert response["visibility"] == "private" response = conn - |> put("/api/pleroma/admin/statuses/#{id}", %{"visibility" => "unlisted"}) + |> put("/api/pleroma/admin/statuses/#{id}", %{visibility: "unlisted"}) |> json_response(:ok) assert response["visibility"] == "unlisted" end test "returns 400 when visibility is unknown", %{conn: conn, id: id} do - conn = put(conn, "/api/pleroma/admin/statuses/#{id}", %{"visibility" => "test"}) + conn = put(conn, "/api/pleroma/admin/statuses/#{id}", %{visibility: "test"}) assert json_response(conn, :bad_request) == "Unsupported visibility" end @@ -2278,7 +2206,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do |> get("/api/pleroma/admin/config") |> json_response(200) - refute Map.has_key?(configs, "need_reboot") + assert configs["need_reboot"] == false end test "update setting which need reboot, don't change reboot flag until reboot", %{conn: conn} do @@ -2334,7 +2262,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do |> get("/api/pleroma/admin/config") |> json_response(200) - refute Map.has_key?(configs, "need_reboot") + assert configs["need_reboot"] == false end test "saving config with nested merge", %{conn: conn} do @@ -2441,13 +2369,17 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do value: :erlang.term_to_binary([]) ) + Pleroma.Config.TransferTask.load_and_update_env([], false) + + assert Application.get_env(:logger, :backends) == [] + conn = post(conn, "/api/pleroma/admin/config", %{ configs: [ %{ group: config.group, key: config.key, - value: [":console", %{"tuple" => ["ExSyslogger", ":ex_syslogger"]}] + value: [":console"] } ] }) @@ -2458,8 +2390,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do "group" => ":logger", "key" => ":backends", "value" => [ - ":console", - %{"tuple" => ["ExSyslogger", ":ex_syslogger"]} + ":console" ], "db" => [":backends"] } @@ -2467,14 +2398,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do } assert Application.get_env(:logger, :backends) == [ - :console, - {ExSyslogger, :ex_syslogger} + :console ] - - capture_log(fn -> - require Logger - Logger.warn("Ooops...") - end) =~ "Ooops..." end test "saving full setting if value is not keyword", %{conn: conn} do @@ -2572,9 +2497,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do end test "common config example", %{conn: conn} do - adapter = Application.get_env(:tesla, :adapter) - on_exit(fn -> Application.put_env(:tesla, :adapter, adapter) end) - conn = post(conn, "/api/pleroma/admin/config", %{ configs: [ @@ -2594,16 +2516,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do %{"tuple" => [":regex4", "~r/https:\/\/example.com/s"]}, %{"tuple" => [":name", "Pleroma"]} ] - }, - %{ - "group" => ":tesla", - "key" => ":adapter", - "value" => "Tesla.Adapter.Httpc" } ] }) - assert Application.get_env(:tesla, :adapter) == Tesla.Adapter.Httpc assert Config.get([Pleroma.Captcha.NotReal, :name]) == "Pleroma" assert json_response(conn, 200) == %{ @@ -2637,12 +2553,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do ":regex4", ":name" ] - }, - %{ - "group" => ":tesla", - "key" => ":adapter", - "value" => "Tesla.Adapter.Httpc", - "db" => [":adapter"] } ] } @@ -2955,26 +2865,25 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do group: ":pleroma", key: ":http", value: [ - %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}]}, - %{"tuple" => [":send_user_agent", false]} + %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}]} ] } ] }) - assert json_response(conn, 200) == %{ + assert %{ "configs" => [ %{ "group" => ":pleroma", "key" => ":http", - "value" => [ - %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}]}, - %{"tuple" => [":send_user_agent", false]} - ], - "db" => [":proxy_url", ":send_user_agent"] + "value" => value, + "db" => db } ] - } + } = json_response(conn, 200) + + assert %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}]} in value + assert ":proxy_url" in db end test "proxy tuple domain", %{conn: conn} do @@ -2985,26 +2894,25 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do group: ":pleroma", key: ":http", value: [ - %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}]}, - %{"tuple" => [":send_user_agent", false]} + %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}]} ] } ] }) - assert json_response(conn, 200) == %{ + assert %{ "configs" => [ %{ "group" => ":pleroma", "key" => ":http", - "value" => [ - %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}]}, - %{"tuple" => [":send_user_agent", false]} - ], - "db" => [":proxy_url", ":send_user_agent"] + "value" => value, + "db" => db } ] - } + } = json_response(conn, 200) + + assert %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}]} in value + assert ":proxy_url" in db end test "proxy tuple ip", %{conn: conn} do @@ -3015,26 +2923,52 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do group: ":pleroma", key: ":http", value: [ - %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}]}, - %{"tuple" => [":send_user_agent", false]} + %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}]} ] } ] }) - assert json_response(conn, 200) == %{ + assert %{ "configs" => [ %{ "group" => ":pleroma", "key" => ":http", - "value" => [ - %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}]}, - %{"tuple" => [":send_user_agent", false]} - ], - "db" => [":proxy_url", ":send_user_agent"] + "value" => value, + "db" => db } ] - } + } = json_response(conn, 200) + + assert %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}]} in value + assert ":proxy_url" in db + end + + test "doesn't set keys not in the whitelist", %{conn: conn} do + clear_config(:database_config_whitelist, [ + {:pleroma, :key1}, + {:pleroma, :key2}, + {:pleroma, Pleroma.Captcha.NotReal}, + {:not_real} + ]) + + post(conn, "/api/pleroma/admin/config", %{ + configs: [ + %{group: ":pleroma", key: ":key1", value: "value1"}, + %{group: ":pleroma", key: ":key2", value: "value2"}, + %{group: ":pleroma", key: ":key3", value: "value3"}, + %{group: ":pleroma", key: "Pleroma.Web.Endpoint.NotReal", value: "value4"}, + %{group: ":pleroma", key: "Pleroma.Captcha.NotReal", value: "value5"}, + %{group: ":not_real", key: ":anything", value: "value6"} + ] + }) + + assert Application.get_env(:pleroma, :key1) == "value1" + assert Application.get_env(:pleroma, :key2) == "value2" + assert Application.get_env(:pleroma, :key3) == nil + assert Application.get_env(:pleroma, Pleroma.Web.Endpoint.NotReal) == nil + assert Application.get_env(:pleroma, Pleroma.Captcha.NotReal) == "value5" + assert Application.get_env(:not_real, :anything) == "value6" end end @@ -3050,19 +2984,32 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do end end + test "need_reboot flag", %{conn: conn} do + assert conn + |> get("/api/pleroma/admin/need_reboot") + |> json_response(200) == %{"need_reboot" => false} + + Restarter.Pleroma.need_reboot() + + assert conn + |> get("/api/pleroma/admin/need_reboot") + |> json_response(200) == %{"need_reboot" => true} + + on_exit(fn -> Restarter.Pleroma.refresh() end) + end + describe "GET /api/pleroma/admin/statuses" do test "returns all public and unlisted statuses", %{conn: conn, admin: admin} do blocked = insert(:user) user = insert(:user) User.block(admin, blocked) - {:ok, _} = - CommonAPI.post(user, %{"status" => "@#{admin.nickname}", "visibility" => "direct"}) + {:ok, _} = CommonAPI.post(user, %{status: "@#{admin.nickname}", visibility: "direct"}) - {:ok, _} = CommonAPI.post(user, %{"status" => ".", "visibility" => "unlisted"}) - {:ok, _} = CommonAPI.post(user, %{"status" => ".", "visibility" => "private"}) - {:ok, _} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"}) - {:ok, _} = CommonAPI.post(blocked, %{"status" => ".", "visibility" => "public"}) + {:ok, _} = CommonAPI.post(user, %{status: ".", visibility: "unlisted"}) + {:ok, _} = CommonAPI.post(user, %{status: ".", visibility: "private"}) + {:ok, _} = CommonAPI.post(user, %{status: ".", visibility: "public"}) + {:ok, _} = CommonAPI.post(blocked, %{status: ".", visibility: "public"}) response = conn @@ -3090,11 +3037,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do test "returns private and direct statuses with godmode on", %{conn: conn, admin: admin} do user = insert(:user) - {:ok, _} = - CommonAPI.post(user, %{"status" => "@#{admin.nickname}", "visibility" => "direct"}) + {:ok, _} = CommonAPI.post(user, %{status: "@#{admin.nickname}", visibility: "direct"}) - {:ok, _} = CommonAPI.post(user, %{"status" => ".", "visibility" => "private"}) - {:ok, _} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"}) + {:ok, _} = CommonAPI.post(user, %{status: ".", visibility: "private"}) + {:ok, _} = CommonAPI.post(user, %{status: ".", visibility: "public"}) conn = get(conn, "/api/pleroma/admin/statuses?godmode=true") assert json_response(conn, 200) |> length() == 3 end @@ -3128,11 +3074,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do end test "doesn't return private statuses by default", %{conn: conn, user: user} do - {:ok, _private_status} = - CommonAPI.post(user, %{"status" => "private", "visibility" => "private"}) + {:ok, _private_status} = CommonAPI.post(user, %{status: "private", visibility: "private"}) - {:ok, _public_status} = - CommonAPI.post(user, %{"status" => "public", "visibility" => "public"}) + {:ok, _public_status} = CommonAPI.post(user, %{status: "public", visibility: "public"}) conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses") @@ -3140,11 +3084,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do end test "returns private statuses with godmode on", %{conn: conn, user: user} do - {:ok, _private_status} = - CommonAPI.post(user, %{"status" => "private", "visibility" => "private"}) + {:ok, _private_status} = CommonAPI.post(user, %{status: "private", visibility: "private"}) - {:ok, _public_status} = - CommonAPI.post(user, %{"status" => "public", "visibility" => "public"}) + {:ok, _public_status} = CommonAPI.post(user, %{status: "public", visibility: "public"}) conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?godmode=true") @@ -3153,7 +3095,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do test "excludes reblogs by default", %{conn: conn, user: user} do other_user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "."}) + {:ok, activity} = CommonAPI.post(user, %{status: "."}) {:ok, %Activity{}, _} = CommonAPI.repeat(activity.id, other_user) conn_res = get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses") @@ -3374,6 +3316,75 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do end end + describe "GET /users/:nickname/credentials" do + test "gets the user credentials", %{conn: conn} do + user = insert(:user) + conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials") + + response = assert json_response(conn, 200) + assert response["email"] == user.email + end + + test "returns 403 if requested by a non-admin" do + user = insert(:user) + + conn = + build_conn() + |> assign(:user, user) + |> get("/api/pleroma/admin/users/#{user.nickname}/credentials") + + assert json_response(conn, :forbidden) + end + end + + describe "PATCH /users/:nickname/credentials" do + test "changes password and email", %{conn: conn, admin: admin} do + user = insert(:user) + assert user.password_reset_pending == false + + conn = + patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{ + "password" => "new_password", + "email" => "new_email@example.com", + "name" => "new_name" + }) + + assert json_response(conn, 200) == %{"status" => "success"} + + ObanHelpers.perform_all() + + updated_user = User.get_by_id(user.id) + + assert updated_user.email == "new_email@example.com" + assert updated_user.name == "new_name" + assert updated_user.password_hash != user.password_hash + assert updated_user.password_reset_pending == true + + [log_entry2, log_entry1] = ModerationLog |> Repo.all() |> Enum.sort() + + assert ModerationLog.get_log_entry_message(log_entry1) == + "@#{admin.nickname} updated users: @#{user.nickname}" + + assert ModerationLog.get_log_entry_message(log_entry2) == + "@#{admin.nickname} forced password reset for users: @#{user.nickname}" + end + + test "returns 403 if requested by a non-admin" do + user = insert(:user) + + conn = + build_conn() + |> assign(:user, user) + |> patch("/api/pleroma/admin/users/#{user.nickname}/credentials", %{ + "password" => "new_password", + "email" => "new_email@example.com", + "name" => "new_name" + }) + + assert json_response(conn, :forbidden) + end + end + describe "PATCH /users/:nickname/force_password_reset" do test "sets password_reset_pending to true", %{conn: conn} do user = insert(:user) @@ -3537,9 +3548,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do {:ok, %{id: report_id}} = CommonAPI.report(reporter, %{ - "account_id" => target_user.id, - "comment" => "I feel offended", - "status_ids" => [activity.id] + account_id: target_user.id, + comment: "I feel offended", + status_ids: [activity.id] }) post(conn, "/api/pleroma/admin/reports/#{report_id}/notes", %{ @@ -3590,28 +3601,63 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do end end - test "GET /api/pleroma/admin/config/descriptions", %{conn: conn} do - admin = insert(:user, is_admin: true) + describe "GET /api/pleroma/admin/config/descriptions" do + test "structure", %{conn: conn} do + admin = insert(:user, is_admin: true) - conn = - assign(conn, :user, admin) - |> get("/api/pleroma/admin/config/descriptions") + conn = + assign(conn, :user, admin) + |> get("/api/pleroma/admin/config/descriptions") + + assert [child | _others] = json_response(conn, 200) + + assert child["children"] + assert child["key"] + assert String.starts_with?(child["group"], ":") + assert child["description"] + end + + test "filters by database configuration whitelist", %{conn: conn} do + clear_config(:database_config_whitelist, [ + {:pleroma, :instance}, + {:pleroma, :activitypub}, + {:pleroma, Pleroma.Upload}, + {:esshd} + ]) + + admin = insert(:user, is_admin: true) + + conn = + assign(conn, :user, admin) + |> get("/api/pleroma/admin/config/descriptions") + + children = json_response(conn, 200) + + assert length(children) == 4 - assert [child | _others] = json_response(conn, 200) + assert Enum.count(children, fn c -> c["group"] == ":pleroma" end) == 3 - assert child["children"] - assert child["key"] - assert String.starts_with?(child["group"], ":") - assert child["description"] + instance = Enum.find(children, fn c -> c["key"] == ":instance" end) + assert instance["children"] + + activitypub = Enum.find(children, fn c -> c["key"] == ":activitypub" end) + assert activitypub["children"] + + web_endpoint = Enum.find(children, fn c -> c["key"] == "Pleroma.Upload" end) + assert web_endpoint["children"] + + esshd = Enum.find(children, fn c -> c["group"] == ":esshd" end) + assert esshd["children"] + end end describe "/api/pleroma/admin/stats" do test "status visibility count", %{conn: conn} do admin = insert(:user, is_admin: true) user = insert(:user) - CommonAPI.post(user, %{"visibility" => "public", "status" => "hey"}) - CommonAPI.post(user, %{"visibility" => "unlisted", "status" => "hey"}) - CommonAPI.post(user, %{"visibility" => "unlisted", "status" => "hey"}) + CommonAPI.post(user, %{visibility: "public", status: "hey"}) + CommonAPI.post(user, %{visibility: "unlisted", status: "hey"}) + CommonAPI.post(user, %{visibility: "unlisted", status: "hey"}) response = conn @@ -3623,6 +3669,191 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do response["status_visibility"] end end + + describe "POST /api/pleroma/admin/oauth_app" do + test "errors", %{conn: conn} do + response = conn |> post("/api/pleroma/admin/oauth_app", %{}) |> json_response(200) + + assert response == %{"name" => "can't be blank", "redirect_uris" => "can't be blank"} + end + + test "success", %{conn: conn} do + base_url = Web.base_url() + app_name = "Trusted app" + + response = + conn + |> post("/api/pleroma/admin/oauth_app", %{ + name: app_name, + redirect_uris: base_url + }) + |> json_response(200) + + assert %{ + "client_id" => _, + "client_secret" => _, + "name" => ^app_name, + "redirect_uri" => ^base_url, + "trusted" => false + } = response + end + + test "with trusted", %{conn: conn} do + base_url = Web.base_url() + app_name = "Trusted app" + + response = + conn + |> post("/api/pleroma/admin/oauth_app", %{ + name: app_name, + redirect_uris: base_url, + trusted: true + }) + |> json_response(200) + + assert %{ + "client_id" => _, + "client_secret" => _, + "name" => ^app_name, + "redirect_uri" => ^base_url, + "trusted" => true + } = response + end + end + + describe "GET /api/pleroma/admin/oauth_app" do + setup do + app = insert(:oauth_app) + {:ok, app: app} + end + + test "list", %{conn: conn} do + response = + conn + |> get("/api/pleroma/admin/oauth_app") + |> json_response(200) + + assert %{"apps" => apps, "count" => count, "page_size" => _} = response + + assert length(apps) == count + end + + test "with page size", %{conn: conn} do + insert(:oauth_app) + page_size = 1 + + response = + conn + |> get("/api/pleroma/admin/oauth_app", %{page_size: to_string(page_size)}) + |> json_response(200) + + assert %{"apps" => apps, "count" => _, "page_size" => ^page_size} = response + + assert length(apps) == page_size + end + + test "search by client name", %{conn: conn, app: app} do + response = + conn + |> get("/api/pleroma/admin/oauth_app", %{name: app.client_name}) + |> json_response(200) + + assert %{"apps" => [returned], "count" => _, "page_size" => _} = response + + assert returned["client_id"] == app.client_id + assert returned["name"] == app.client_name + end + + test "search by client id", %{conn: conn, app: app} do + response = + conn + |> get("/api/pleroma/admin/oauth_app", %{client_id: app.client_id}) + |> json_response(200) + + assert %{"apps" => [returned], "count" => _, "page_size" => _} = response + + assert returned["client_id"] == app.client_id + assert returned["name"] == app.client_name + end + + test "only trusted", %{conn: conn} do + app = insert(:oauth_app, trusted: true) + + response = + conn + |> get("/api/pleroma/admin/oauth_app", %{trusted: true}) + |> json_response(200) + + assert %{"apps" => [returned], "count" => _, "page_size" => _} = response + + assert returned["client_id"] == app.client_id + assert returned["name"] == app.client_name + end + end + + describe "DELETE /api/pleroma/admin/oauth_app/:id" do + test "with id", %{conn: conn} do + app = insert(:oauth_app) + + response = + conn + |> delete("/api/pleroma/admin/oauth_app/" <> to_string(app.id)) + |> json_response(:no_content) + + assert response == "" + end + + test "with non existance id", %{conn: conn} do + response = + conn + |> delete("/api/pleroma/admin/oauth_app/0") + |> json_response(:bad_request) + + assert response == "" + end + end + + describe "PATCH /api/pleroma/admin/oauth_app/:id" do + test "with id", %{conn: conn} do + app = insert(:oauth_app) + + name = "another name" + url = "https://example.com" + scopes = ["admin"] + id = app.id + website = "http://website.com" + + response = + conn + |> patch("/api/pleroma/admin/oauth_app/" <> to_string(app.id), %{ + name: name, + trusted: true, + redirect_uris: url, + scopes: scopes, + website: website + }) + |> json_response(200) + + assert %{ + "client_id" => _, + "client_secret" => _, + "id" => ^id, + "name" => ^name, + "redirect_uri" => ^url, + "trusted" => true, + "website" => ^website + } = response + end + + test "without id", %{conn: conn} do + response = + conn + |> patch("/api/pleroma/admin/oauth_app/0") + |> json_response(:bad_request) + + assert response == "" + end + end end # Needed for testing diff --git a/test/web/admin_api/views/report_view_test.exs b/test/web/admin_api/views/report_view_test.exs index 5db6629f2..f00b0afb2 100644 --- a/test/web/admin_api/views/report_view_test.exs +++ b/test/web/admin_api/views/report_view_test.exs @@ -15,7 +15,7 @@ defmodule Pleroma.Web.AdminAPI.ReportViewTest do user = insert(:user) other_user = insert(:user) - {:ok, activity} = CommonAPI.report(user, %{"account_id" => other_user.id}) + {:ok, activity} = CommonAPI.report(user, %{account_id: other_user.id}) expected = %{ content: nil, @@ -45,10 +45,10 @@ defmodule Pleroma.Web.AdminAPI.ReportViewTest do test "includes reported statuses" do user = insert(:user) other_user = insert(:user) - {:ok, activity} = CommonAPI.post(other_user, %{"status" => "toot"}) + {:ok, activity} = CommonAPI.post(other_user, %{status: "toot"}) {:ok, report_activity} = - CommonAPI.report(user, %{"account_id" => other_user.id, "status_ids" => [activity.id]}) + CommonAPI.report(user, %{account_id: other_user.id, status_ids: [activity.id]}) other_user = Pleroma.User.get_by_id(other_user.id) @@ -81,7 +81,7 @@ defmodule Pleroma.Web.AdminAPI.ReportViewTest do user = insert(:user) other_user = insert(:user) - {:ok, activity} = CommonAPI.report(user, %{"account_id" => other_user.id}) + {:ok, activity} = CommonAPI.report(user, %{account_id: other_user.id}) {:ok, activity} = CommonAPI.update_report_state(activity.id, "closed") assert %{state: "closed"} = @@ -94,8 +94,8 @@ defmodule Pleroma.Web.AdminAPI.ReportViewTest do {:ok, activity} = CommonAPI.report(user, %{ - "account_id" => other_user.id, - "comment" => "posts are too good for this instance" + account_id: other_user.id, + comment: "posts are too good for this instance" }) assert %{content: "posts are too good for this instance"} = @@ -108,8 +108,8 @@ defmodule Pleroma.Web.AdminAPI.ReportViewTest do {:ok, activity} = CommonAPI.report(user, %{ - "account_id" => other_user.id, - "comment" => "" + account_id: other_user.id, + comment: "" }) data = Map.put(activity.data, "content", "<script> alert('hecked :D:D:D:D:D:D:D') </script>") @@ -125,8 +125,8 @@ defmodule Pleroma.Web.AdminAPI.ReportViewTest do {:ok, activity} = CommonAPI.report(user, %{ - "account_id" => other_user.id, - "comment" => "" + account_id: other_user.id, + comment: "" }) Pleroma.User.delete(other_user) diff --git a/test/web/api_spec/schema_examples_test.exs b/test/web/api_spec/schema_examples_test.exs new file mode 100644 index 000000000..88b6f07cb --- /dev/null +++ b/test/web/api_spec/schema_examples_test.exs @@ -0,0 +1,43 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.SchemaExamplesTest do + use ExUnit.Case, async: true + import Pleroma.Tests.ApiSpecHelpers + + @content_type "application/json" + + for operation <- api_operations() do + describe operation.operationId <> " Request Body" do + if operation.requestBody do + @media_type operation.requestBody.content[@content_type] + @schema resolve_schema(@media_type.schema) + + if @media_type.example do + test "request body media type example matches schema" do + assert_schema(@media_type.example, @schema) + end + end + + if @schema.example do + test "request body schema example matches schema" do + assert_schema(@schema.example, @schema) + end + end + end + end + + for {status, response} <- operation.responses do + describe "#{operation.operationId} - #{status} Response" do + @schema resolve_schema(response.content[@content_type].schema) + + if @schema.example do + test "example matches schema" do + assert_schema(@schema.example, @schema) + end + end + end + end + end +end diff --git a/test/web/auth/auth_test_controller_test.exs b/test/web/auth/auth_test_controller_test.exs new file mode 100644 index 000000000..fed52b7f3 --- /dev/null +++ b/test/web/auth/auth_test_controller_test.exs @@ -0,0 +1,242 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Tests.AuthTestControllerTest do + use Pleroma.Web.ConnCase + + import Pleroma.Factory + + describe "do_oauth_check" do + test "serves with proper OAuth token (fulfilling requested scopes)" do + %{conn: good_token_conn, user: user} = oauth_access(["read"]) + + assert %{"user_id" => user.id} == + good_token_conn + |> get("/test/authenticated_api/do_oauth_check") + |> json_response(200) + + # Unintended usage (:api) — use with :authenticated_api instead + assert %{"user_id" => user.id} == + good_token_conn + |> get("/test/api/do_oauth_check") + |> json_response(200) + end + + test "fails on no token / missing scope(s)" do + %{conn: bad_token_conn} = oauth_access(["irrelevant_scope"]) + + bad_token_conn + |> get("/test/authenticated_api/do_oauth_check") + |> json_response(403) + + bad_token_conn + |> assign(:token, nil) + |> get("/test/api/do_oauth_check") + |> json_response(403) + end + end + + describe "fallback_oauth_check" do + test "serves with proper OAuth token (fulfilling requested scopes)" do + %{conn: good_token_conn, user: user} = oauth_access(["read"]) + + assert %{"user_id" => user.id} == + good_token_conn + |> get("/test/api/fallback_oauth_check") + |> json_response(200) + + # Unintended usage (:authenticated_api) — use with :api instead + assert %{"user_id" => user.id} == + good_token_conn + |> get("/test/authenticated_api/fallback_oauth_check") + |> json_response(200) + end + + test "for :api on public instance, drops :user and renders on no token / missing scope(s)" do + clear_config([:instance, :public], true) + + %{conn: bad_token_conn} = oauth_access(["irrelevant_scope"]) + + assert %{"user_id" => nil} == + bad_token_conn + |> get("/test/api/fallback_oauth_check") + |> json_response(200) + + assert %{"user_id" => nil} == + bad_token_conn + |> assign(:token, nil) + |> get("/test/api/fallback_oauth_check") + |> json_response(200) + end + + test "for :api on private instance, fails on no token / missing scope(s)" do + clear_config([:instance, :public], false) + + %{conn: bad_token_conn} = oauth_access(["irrelevant_scope"]) + + bad_token_conn + |> get("/test/api/fallback_oauth_check") + |> json_response(403) + + bad_token_conn + |> assign(:token, nil) + |> get("/test/api/fallback_oauth_check") + |> json_response(403) + end + end + + describe "skip_oauth_check" do + test "for :authenticated_api, serves if :user is set (regardless of token / token scopes)" do + user = insert(:user) + + assert %{"user_id" => user.id} == + build_conn() + |> assign(:user, user) + |> get("/test/authenticated_api/skip_oauth_check") + |> json_response(200) + + %{conn: bad_token_conn, user: user} = oauth_access(["irrelevant_scope"]) + + assert %{"user_id" => user.id} == + bad_token_conn + |> get("/test/authenticated_api/skip_oauth_check") + |> json_response(200) + end + + test "serves via :api on public instance if :user is not set" do + clear_config([:instance, :public], true) + + assert %{"user_id" => nil} == + build_conn() + |> get("/test/api/skip_oauth_check") + |> json_response(200) + + build_conn() + |> get("/test/authenticated_api/skip_oauth_check") + |> json_response(403) + end + + test "fails on private instance if :user is not set" do + clear_config([:instance, :public], false) + + build_conn() + |> get("/test/api/skip_oauth_check") + |> json_response(403) + + build_conn() + |> get("/test/authenticated_api/skip_oauth_check") + |> json_response(403) + end + end + + describe "fallback_oauth_skip_publicity_check" do + test "serves with proper OAuth token (fulfilling requested scopes)" do + %{conn: good_token_conn, user: user} = oauth_access(["read"]) + + assert %{"user_id" => user.id} == + good_token_conn + |> get("/test/api/fallback_oauth_skip_publicity_check") + |> json_response(200) + + # Unintended usage (:authenticated_api) + assert %{"user_id" => user.id} == + good_token_conn + |> get("/test/authenticated_api/fallback_oauth_skip_publicity_check") + |> json_response(200) + end + + test "for :api on private / public instance, drops :user and renders on token issue" do + %{conn: bad_token_conn} = oauth_access(["irrelevant_scope"]) + + for is_public <- [true, false] do + clear_config([:instance, :public], is_public) + + assert %{"user_id" => nil} == + bad_token_conn + |> get("/test/api/fallback_oauth_skip_publicity_check") + |> json_response(200) + + assert %{"user_id" => nil} == + bad_token_conn + |> assign(:token, nil) + |> get("/test/api/fallback_oauth_skip_publicity_check") + |> json_response(200) + end + end + end + + describe "skip_oauth_skip_publicity_check" do + test "for :authenticated_api, serves if :user is set (regardless of token / token scopes)" do + user = insert(:user) + + assert %{"user_id" => user.id} == + build_conn() + |> assign(:user, user) + |> get("/test/authenticated_api/skip_oauth_skip_publicity_check") + |> json_response(200) + + %{conn: bad_token_conn, user: user} = oauth_access(["irrelevant_scope"]) + + assert %{"user_id" => user.id} == + bad_token_conn + |> get("/test/authenticated_api/skip_oauth_skip_publicity_check") + |> json_response(200) + end + + test "for :api, serves on private and public instances regardless of whether :user is set" do + user = insert(:user) + + for is_public <- [true, false] do + clear_config([:instance, :public], is_public) + + assert %{"user_id" => nil} == + build_conn() + |> get("/test/api/skip_oauth_skip_publicity_check") + |> json_response(200) + + assert %{"user_id" => user.id} == + build_conn() + |> assign(:user, user) + |> get("/test/api/skip_oauth_skip_publicity_check") + |> json_response(200) + end + end + end + + describe "missing_oauth_check_definition" do + def test_missing_oauth_check_definition_failure(endpoint, expected_error) do + %{conn: conn} = oauth_access(["read", "write", "follow", "push", "admin"]) + + assert %{"error" => expected_error} == + conn + |> get(endpoint) + |> json_response(403) + end + + test "fails if served via :authenticated_api" do + test_missing_oauth_check_definition_failure( + "/test/authenticated_api/missing_oauth_check_definition", + "Security violation: OAuth scopes check was neither handled nor explicitly skipped." + ) + end + + test "fails if served via :api and the instance is private" do + clear_config([:instance, :public], false) + + test_missing_oauth_check_definition_failure( + "/test/api/missing_oauth_check_definition", + "This resource requires authentication." + ) + end + + test "succeeds with dropped :user if served via :api on public instance" do + %{conn: conn} = oauth_access(["read", "write", "follow", "push", "admin"]) + + assert %{"user_id" => nil} == + conn + |> get("/test/api/missing_oauth_check_definition") + |> json_response(200) + end + end +end diff --git a/test/web/auth/basic_auth_test.exs b/test/web/auth/basic_auth_test.exs new file mode 100644 index 000000000..bf6e3d2fc --- /dev/null +++ b/test/web/auth/basic_auth_test.exs @@ -0,0 +1,46 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Auth.BasicAuthTest do + use Pleroma.Web.ConnCase + + import Pleroma.Factory + + test "with HTTP Basic Auth used, grants access to OAuth scope-restricted endpoints", %{ + conn: conn + } do + user = insert(:user) + assert Pbkdf2.verify_pass("test", user.password_hash) + + basic_auth_contents = + (URI.encode_www_form(user.nickname) <> ":" <> URI.encode_www_form("test")) + |> Base.encode64() + + # Succeeds with HTTP Basic Auth + response = + conn + |> put_req_header("authorization", "Basic " <> basic_auth_contents) + |> get("/api/v1/accounts/verify_credentials") + |> json_response(200) + + user_nickname = user.nickname + assert %{"username" => ^user_nickname} = response + + # Succeeds with a properly scoped OAuth token + valid_token = insert(:oauth_token, scopes: ["read:accounts"]) + + conn + |> put_req_header("authorization", "Bearer #{valid_token.token}") + |> get("/api/v1/accounts/verify_credentials") + |> json_response(200) + + # Fails with a wrong-scoped OAuth token (proof of restriction) + invalid_token = insert(:oauth_token, scopes: ["read:something"]) + + conn + |> put_req_header("authorization", "Bearer #{invalid_token.token}") + |> get("/api/v1/accounts/verify_credentials") + |> json_response(403) + end +end diff --git a/test/web/auth/pleroma_authenticator_test.exs b/test/web/auth/pleroma_authenticator_test.exs new file mode 100644 index 000000000..5a421e5ed --- /dev/null +++ b/test/web/auth/pleroma_authenticator_test.exs @@ -0,0 +1,43 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Auth.PleromaAuthenticatorTest do + use Pleroma.Web.ConnCase + + alias Pleroma.Web.Auth.PleromaAuthenticator + import Pleroma.Factory + + setup do + password = "testpassword" + name = "AgentSmith" + user = insert(:user, nickname: name, password_hash: Pbkdf2.hash_pwd_salt(password)) + {:ok, [user: user, name: name, password: password]} + end + + test "get_user/authorization", %{user: user, name: name, password: password} do + params = %{"authorization" => %{"name" => name, "password" => password}} + res = PleromaAuthenticator.get_user(%Plug.Conn{params: params}) + + assert {:ok, user} == res + end + + test "get_user/authorization with invalid password", %{name: name} do + params = %{"authorization" => %{"name" => name, "password" => "password"}} + res = PleromaAuthenticator.get_user(%Plug.Conn{params: params}) + + assert {:error, {:checkpw, false}} == res + end + + test "get_user/grant_type_password", %{user: user, name: name, password: password} do + params = %{"grant_type" => "password", "username" => name, "password" => password} + res = PleromaAuthenticator.get_user(%Plug.Conn{params: params}) + + assert {:ok, user} == res + end + + test "error credintails" do + res = PleromaAuthenticator.get_user(%Plug.Conn{params: %{}}) + assert {:error, :invalid_credentials} == res + end +end diff --git a/test/web/auth/totp_authenticator_test.exs b/test/web/auth/totp_authenticator_test.exs new file mode 100644 index 000000000..e502e0ae8 --- /dev/null +++ b/test/web/auth/totp_authenticator_test.exs @@ -0,0 +1,51 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Auth.TOTPAuthenticatorTest do + use Pleroma.Web.ConnCase + + alias Pleroma.MFA + alias Pleroma.MFA.BackupCodes + alias Pleroma.MFA.TOTP + alias Pleroma.Web.Auth.TOTPAuthenticator + + import Pleroma.Factory + + test "verify token" do + otp_secret = TOTP.generate_secret() + otp_token = TOTP.generate_token(otp_secret) + + user = + insert(:user, + multi_factor_authentication_settings: %MFA.Settings{ + enabled: true, + totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true} + } + ) + + assert TOTPAuthenticator.verify(otp_token, user) == {:ok, :pass} + assert TOTPAuthenticator.verify(nil, user) == {:error, :invalid_token} + assert TOTPAuthenticator.verify("", user) == {:error, :invalid_token} + end + + test "checks backup codes" do + [code | _] = backup_codes = BackupCodes.generate() + + hashed_codes = + backup_codes + |> Enum.map(&Pbkdf2.hash_pwd_salt(&1)) + + user = + insert(:user, + multi_factor_authentication_settings: %MFA.Settings{ + enabled: true, + backup_codes: hashed_codes, + totp: %MFA.Settings.TOTP{secret: "otp_secret", confirmed: true} + } + ) + + assert TOTPAuthenticator.verify_recovery_code(user, code) == {:ok, :pass} + refute TOTPAuthenticator.verify_recovery_code(code, refresh_record(user)) == {:ok, :pass} + end +end diff --git a/test/web/common_api/common_api_test.exs b/test/web/common_api/common_api_test.exs index 0da0bd2e2..fd8299013 100644 --- a/test/web/common_api/common_api_test.exs +++ b/test/web/common_api/common_api_test.exs @@ -9,11 +9,13 @@ defmodule Pleroma.Web.CommonAPITest do alias Pleroma.Object alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.ActivityPub.Transmogrifier alias Pleroma.Web.ActivityPub.Visibility alias Pleroma.Web.AdminAPI.AccountView alias Pleroma.Web.CommonAPI import Pleroma.Factory + import Mock require Pleroma.Constants @@ -21,14 +23,176 @@ defmodule Pleroma.Web.CommonAPITest do setup do: clear_config([:instance, :limit]) setup do: clear_config([:instance, :max_pinned_statuses]) + describe "unblocking" do + test "it works even without an existing block activity" do + blocked = insert(:user) + blocker = insert(:user) + User.block(blocker, blocked) + + assert User.blocks?(blocker, blocked) + assert {:ok, :no_activity} == CommonAPI.unblock(blocker, blocked) + refute User.blocks?(blocker, blocked) + end + end + + describe "deletion" do + test "it works with pruned objects" do + user = insert(:user) + + {:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"}) + + Object.normalize(post, false) + |> Object.prune() + + with_mock Pleroma.Web.Federator, + publish: fn _ -> nil end do + assert {:ok, delete} = CommonAPI.delete(post.id, user) + assert delete.local + assert called(Pleroma.Web.Federator.publish(delete)) + end + + refute Activity.get_by_id(post.id) + end + + test "it allows users to delete their posts" do + user = insert(:user) + + {:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"}) + + with_mock Pleroma.Web.Federator, + publish: fn _ -> nil end do + assert {:ok, delete} = CommonAPI.delete(post.id, user) + assert delete.local + assert called(Pleroma.Web.Federator.publish(delete)) + end + + refute Activity.get_by_id(post.id) + end + + test "it does not allow a user to delete their posts" do + user = insert(:user) + other_user = insert(:user) + + {:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"}) + + assert {:error, "Could not delete"} = CommonAPI.delete(post.id, other_user) + assert Activity.get_by_id(post.id) + end + + test "it allows moderators to delete other user's posts" do + user = insert(:user) + moderator = insert(:user, is_moderator: true) + + {:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"}) + + assert {:ok, delete} = CommonAPI.delete(post.id, moderator) + assert delete.local + + refute Activity.get_by_id(post.id) + end + + test "it allows admins to delete other user's posts" do + user = insert(:user) + moderator = insert(:user, is_admin: true) + + {:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"}) + + assert {:ok, delete} = CommonAPI.delete(post.id, moderator) + assert delete.local + + refute Activity.get_by_id(post.id) + end + + test "superusers deleting non-local posts won't federate the delete" do + # This is the user of the ingested activity + _user = + insert(:user, + local: false, + ap_id: "http://mastodon.example.org/users/admin", + last_refreshed_at: NaiveDateTime.utc_now() + ) + + moderator = insert(:user, is_admin: true) + + data = + File.read!("test/fixtures/mastodon-post-activity.json") + |> Jason.decode!() + + {:ok, post} = Transmogrifier.handle_incoming(data) + + with_mock Pleroma.Web.Federator, + publish: fn _ -> nil end do + assert {:ok, delete} = CommonAPI.delete(post.id, moderator) + assert delete.local + refute called(Pleroma.Web.Federator.publish(:_)) + end + + refute Activity.get_by_id(post.id) + end + end + + test "favoriting race condition" do + user = insert(:user) + users_serial = insert_list(10, :user) + users = insert_list(10, :user) + + {:ok, activity} = CommonAPI.post(user, %{status: "."}) + + users_serial + |> Enum.map(fn user -> + CommonAPI.favorite(user, activity.id) + end) + + object = Object.get_by_ap_id(activity.data["object"]) + assert object.data["like_count"] == 10 + + users + |> Enum.map(fn user -> + Task.async(fn -> + CommonAPI.favorite(user, activity.id) + end) + end) + |> Enum.map(&Task.await/1) + + object = Object.get_by_ap_id(activity.data["object"]) + assert object.data["like_count"] == 20 + end + + test "repeating race condition" do + user = insert(:user) + users_serial = insert_list(10, :user) + users = insert_list(10, :user) + + {:ok, activity} = CommonAPI.post(user, %{status: "."}) + + users_serial + |> Enum.map(fn user -> + CommonAPI.repeat(activity.id, user) + end) + + object = Object.get_by_ap_id(activity.data["object"]) + assert object.data["announcement_count"] == 10 + + users + |> Enum.map(fn user -> + Task.async(fn -> + CommonAPI.repeat(activity.id, user) + end) + end) + |> Enum.map(&Task.await/1) + + object = Object.get_by_ap_id(activity.data["object"]) + assert object.data["announcement_count"] == 20 + end + test "when replying to a conversation / participation, it will set the correct context id even if no explicit reply_to is given" do user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"}) + {:ok, activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"}) [participation] = Participation.for_user(user) {:ok, convo_reply} = - CommonAPI.post(user, %{"status" => ".", "in_reply_to_conversation_id" => participation.id}) + CommonAPI.post(user, %{status: ".", in_reply_to_conversation_id: participation.id}) assert Visibility.is_direct?(convo_reply) @@ -42,8 +206,8 @@ defmodule Pleroma.Web.CommonAPITest do {:ok, activity} = CommonAPI.post(har, %{ - "status" => "@#{jafnhar.nickname} hey", - "visibility" => "direct" + status: "@#{jafnhar.nickname} hey", + visibility: "direct" }) assert har.ap_id in activity.recipients @@ -53,10 +217,10 @@ defmodule Pleroma.Web.CommonAPITest do {:ok, activity} = CommonAPI.post(har, %{ - "status" => "I don't really like @#{tridi.nickname}", - "visibility" => "direct", - "in_reply_to_status_id" => activity.id, - "in_reply_to_conversation_id" => participation.id + status: "I don't really like @#{tridi.nickname}", + visibility: "direct", + in_reply_to_status_id: activity.id, + in_reply_to_conversation_id: participation.id }) assert har.ap_id in activity.recipients @@ -73,8 +237,8 @@ defmodule Pleroma.Web.CommonAPITest do {:ok, activity} = CommonAPI.post(har, %{ - "status" => "@#{jafnhar.nickname} hey, i never want to see @#{tridi.nickname} again", - "visibility" => "direct" + status: "@#{jafnhar.nickname} hey, i never want to see @#{tridi.nickname} again", + visibility: "direct" }) refute tridi.ap_id in activity.recipients @@ -83,7 +247,7 @@ defmodule Pleroma.Web.CommonAPITest do test "it de-duplicates tags" do user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "#2hu #2HU"}) + {:ok, activity} = CommonAPI.post(user, %{status: "#2hu #2HU"}) object = Object.normalize(activity) @@ -92,23 +256,11 @@ defmodule Pleroma.Web.CommonAPITest do test "it adds emoji in the object" do user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => ":firefox:"}) + {:ok, activity} = CommonAPI.post(user, %{status: ":firefox:"}) assert Object.normalize(activity).data["emoji"]["firefox"] end - test "it adds emoji when updating profiles" do - user = insert(:user, %{name: ":firefox:"}) - - {:ok, activity} = CommonAPI.update(user) - user = User.get_cached_by_ap_id(user.ap_id) - [firefox] = user.source_data["tag"] - - assert firefox["name"] == ":firefox:" - - assert Pleroma.Constants.as_public() in activity.recipients - end - describe "posting" do test "it supports explicit addressing" do user = insert(:user) @@ -118,9 +270,9 @@ defmodule Pleroma.Web.CommonAPITest do {:ok, activity} = CommonAPI.post(user, %{ - "status" => + status: "Hey, I think @#{user_three.nickname} is ugly. @#{user_four.nickname} is alright though.", - "to" => [user_two.nickname, user_four.nickname, "nonexistent"] + to: [user_two.nickname, user_four.nickname, "nonexistent"] }) assert user.ap_id in activity.recipients @@ -136,8 +288,8 @@ defmodule Pleroma.Web.CommonAPITest do {:ok, activity} = CommonAPI.post(user, %{ - "status" => post, - "content_type" => "text/html" + status: post, + content_type: "text/html" }) object = Object.normalize(activity) @@ -152,8 +304,8 @@ defmodule Pleroma.Web.CommonAPITest do {:ok, activity} = CommonAPI.post(user, %{ - "status" => post, - "content_type" => "text/markdown" + status: post, + content_type: "text/markdown" }) object = Object.normalize(activity) @@ -164,21 +316,21 @@ defmodule Pleroma.Web.CommonAPITest do test "it does not allow replies to direct messages that are not direct messages themselves" do user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "suya..", "visibility" => "direct"}) + {:ok, activity} = CommonAPI.post(user, %{status: "suya..", visibility: "direct"}) assert {:ok, _} = CommonAPI.post(user, %{ - "status" => "suya..", - "visibility" => "direct", - "in_reply_to_status_id" => activity.id + status: "suya..", + visibility: "direct", + in_reply_to_status_id: activity.id }) Enum.each(["public", "private", "unlisted"], fn visibility -> assert {:error, "The message visibility must be direct"} = CommonAPI.post(user, %{ - "status" => "suya..", - "visibility" => visibility, - "in_reply_to_status_id" => activity.id + status: "suya..", + visibility: visibility, + in_reply_to_status_id: activity.id }) end) end @@ -187,8 +339,7 @@ defmodule Pleroma.Web.CommonAPITest do user = insert(:user) {:ok, list} = Pleroma.List.create("foo", user) - {:ok, activity} = - CommonAPI.post(user, %{"status" => "foobar", "visibility" => "list:#{list.id}"}) + {:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"}) assert activity.data["bcc"] == [list.ap_id] assert activity.recipients == [list.ap_id, user.ap_id] @@ -199,7 +350,7 @@ defmodule Pleroma.Web.CommonAPITest do user = insert(:user) assert {:error, "Cannot post an empty status without attachments"} = - CommonAPI.post(user, %{"status" => ""}) + CommonAPI.post(user, %{status: ""}) end test "it validates character limits are correctly enforced" do @@ -208,9 +359,9 @@ defmodule Pleroma.Web.CommonAPITest do user = insert(:user) assert {:error, "The status is over the character limit"} = - CommonAPI.post(user, %{"status" => "foobar"}) + CommonAPI.post(user, %{status: "foobar"}) - assert {:ok, activity} = CommonAPI.post(user, %{"status" => "12345"}) + assert {:ok, activity} = CommonAPI.post(user, %{status: "12345"}) end test "it can handle activities that expire" do @@ -221,8 +372,7 @@ defmodule Pleroma.Web.CommonAPITest do |> NaiveDateTime.truncate(:second) |> NaiveDateTime.add(1_000_000, :second) - assert {:ok, activity} = - CommonAPI.post(user, %{"status" => "chai", "expires_in" => 1_000_000}) + assert {:ok, activity} = CommonAPI.post(user, %{status: "chai", expires_in: 1_000_000}) assert expiration = Pleroma.ActivityExpiration.get_by_activity_id(activity.id) assert expiration.scheduled_at == expires_at @@ -234,14 +384,14 @@ defmodule Pleroma.Web.CommonAPITest do user = insert(:user) other_user = insert(:user) - {:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"}) + {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"}) - {:ok, reaction, _} = CommonAPI.react_with_emoji(activity.id, user, "👍") + {:ok, reaction} = CommonAPI.react_with_emoji(activity.id, user, "👍") assert reaction.data["actor"] == user.ap_id assert reaction.data["content"] == "👍" - {:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"}) + {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"}) {:error, _} = CommonAPI.react_with_emoji(activity.id, user, ".") end @@ -250,32 +400,43 @@ defmodule Pleroma.Web.CommonAPITest do user = insert(:user) other_user = insert(:user) - {:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"}) - {:ok, reaction, _} = CommonAPI.react_with_emoji(activity.id, user, "👍") + {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"}) + {:ok, reaction} = CommonAPI.react_with_emoji(activity.id, user, "👍") - {:ok, unreaction, _} = CommonAPI.unreact_with_emoji(activity.id, user, "👍") + {:ok, unreaction} = CommonAPI.unreact_with_emoji(activity.id, user, "👍") assert unreaction.data["type"] == "Undo" assert unreaction.data["object"] == reaction.data["id"] + assert unreaction.local end test "repeating a status" do user = insert(:user) other_user = insert(:user) - {:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"}) + {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"}) {:ok, %Activity{}, _} = CommonAPI.repeat(activity.id, user) end + test "can't repeat a repeat" do + user = insert(:user) + other_user = insert(:user) + {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"}) + + {:ok, %Activity{} = announce, _} = CommonAPI.repeat(activity.id, other_user) + + refute match?({:ok, %Activity{}, _}, CommonAPI.repeat(announce.id, user)) + end + test "repeating a status privately" do user = insert(:user) other_user = insert(:user) - {:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"}) + {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"}) {:ok, %Activity{} = announce_activity, _} = - CommonAPI.repeat(activity.id, user, %{"visibility" => "private"}) + CommonAPI.repeat(activity.id, user, %{visibility: "private"}) assert Visibility.is_private?(announce_activity) end @@ -284,27 +445,30 @@ defmodule Pleroma.Web.CommonAPITest do user = insert(:user) other_user = insert(:user) - {:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"}) + {:ok, post_activity} = CommonAPI.post(other_user, %{status: "cofe"}) - {:ok, %Activity{}, _} = CommonAPI.favorite(activity.id, user) + {:ok, %Activity{data: data}} = CommonAPI.favorite(user, post_activity.id) + assert data["type"] == "Like" + assert data["actor"] == user.ap_id + assert data["object"] == post_activity.data["object"] end test "retweeting a status twice returns the status" do user = insert(:user) other_user = insert(:user) - {:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"}) - {:ok, %Activity{} = activity, object} = CommonAPI.repeat(activity.id, user) - {:ok, ^activity, ^object} = CommonAPI.repeat(activity.id, user) + {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"}) + {:ok, %Activity{} = announce, object} = CommonAPI.repeat(activity.id, user) + {:ok, ^announce, ^object} = CommonAPI.repeat(activity.id, user) end - test "favoriting a status twice returns the status" do + test "favoriting a status twice returns ok, but without the like activity" do user = insert(:user) other_user = insert(:user) - {:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"}) - {:ok, %Activity{} = activity, object} = CommonAPI.favorite(activity.id, user) - {:ok, ^activity, ^object} = CommonAPI.favorite(activity.id, user) + {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"}) + {:ok, %Activity{}} = CommonAPI.favorite(user, activity.id) + assert {:ok, :already_liked} = CommonAPI.favorite(user, activity.id) end end @@ -313,7 +477,7 @@ defmodule Pleroma.Web.CommonAPITest do Pleroma.Config.put([:instance, :max_pinned_statuses], 1) user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"}) + {:ok, activity} = CommonAPI.post(user, %{status: "HI!!!"}) [user: user, activity: activity] end @@ -330,8 +494,8 @@ defmodule Pleroma.Web.CommonAPITest do test "pin poll", %{user: user} do {:ok, activity} = CommonAPI.post(user, %{ - "status" => "How is fediverse today?", - "poll" => %{"options" => ["Absolutely outstanding", "Not good"], "expires_in" => 20} + status: "How is fediverse today?", + poll: %{options: ["Absolutely outstanding", "Not good"], expires_in: 20} }) assert {:ok, ^activity} = CommonAPI.pin(activity.id, user) @@ -343,7 +507,7 @@ defmodule Pleroma.Web.CommonAPITest do end test "unlisted statuses can be pinned", %{user: user} do - {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!", "visibility" => "unlisted"}) + {:ok, activity} = CommonAPI.post(user, %{status: "HI!!!", visibility: "unlisted"}) assert {:ok, ^activity} = CommonAPI.pin(activity.id, user) end @@ -354,7 +518,7 @@ defmodule Pleroma.Web.CommonAPITest do end test "max pinned statuses", %{user: user, activity: activity_one} do - {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"}) + {:ok, activity_two} = CommonAPI.post(user, %{status: "HI!!!"}) assert {:ok, ^activity_one} = CommonAPI.pin(activity_one.id, user) @@ -369,7 +533,9 @@ defmodule Pleroma.Web.CommonAPITest do user = refresh_record(user) - assert {:ok, ^activity} = CommonAPI.unpin(activity.id, user) + id = activity.id + + assert match?({:ok, %{id: ^id}}, CommonAPI.unpin(activity.id, user)) user = refresh_record(user) @@ -420,7 +586,7 @@ defmodule Pleroma.Web.CommonAPITest do reporter = insert(:user) target_user = insert(:user) - {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"}) + {:ok, activity} = CommonAPI.post(target_user, %{status: "foobar"}) reporter_ap_id = reporter.ap_id target_ap_id = target_user.ap_id @@ -428,9 +594,9 @@ defmodule Pleroma.Web.CommonAPITest do comment = "foobar" report_data = %{ - "account_id" => target_user.id, - "comment" => comment, - "status_ids" => [activity.id] + account_id: target_user.id, + comment: comment, + status_ids: [activity.id] } note_obj = %{ @@ -460,9 +626,9 @@ defmodule Pleroma.Web.CommonAPITest do {:ok, %Activity{id: report_id}} = CommonAPI.report(reporter, %{ - "account_id" => target_user.id, - "comment" => "I feel offended", - "status_ids" => [activity.id] + account_id: target_user.id, + comment: "I feel offended", + status_ids: [activity.id] }) {:ok, report} = CommonAPI.update_report_state(report_id, "resolved") @@ -481,9 +647,9 @@ defmodule Pleroma.Web.CommonAPITest do {:ok, %Activity{id: report_id}} = CommonAPI.report(reporter, %{ - "account_id" => target_user.id, - "comment" => "I feel offended", - "status_ids" => [activity.id] + account_id: target_user.id, + comment: "I feel offended", + status_ids: [activity.id] }) assert CommonAPI.update_report_state(report_id, "test") == {:error, "Unsupported state"} @@ -495,16 +661,16 @@ defmodule Pleroma.Web.CommonAPITest do {:ok, %Activity{id: first_report_id}} = CommonAPI.report(reporter, %{ - "account_id" => target_user.id, - "comment" => "I feel offended", - "status_ids" => [activity.id] + account_id: target_user.id, + comment: "I feel offended", + status_ids: [activity.id] }) {:ok, %Activity{id: second_report_id}} = CommonAPI.report(reporter, %{ - "account_id" => target_user.id, - "comment" => "I feel very offended!", - "status_ids" => [activity.id] + account_id: target_user.id, + comment: "I feel very offended!", + status_ids: [activity.id] }) {:ok, report_ids} = @@ -562,7 +728,7 @@ defmodule Pleroma.Web.CommonAPITest do assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} = CommonAPI.follow(follower, followed) - assert User.get_follow_state(follower, followed) == "pending" + assert User.get_follow_state(follower, followed) == :follow_pending assert {:ok, follower} = CommonAPI.unfollow(follower, followed) assert User.get_follow_state(follower, followed) == nil @@ -584,7 +750,7 @@ defmodule Pleroma.Web.CommonAPITest do assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} = CommonAPI.follow(follower, followed) - assert User.get_follow_state(follower, followed) == "pending" + assert User.get_follow_state(follower, followed) == :follow_pending assert {:ok, follower} = CommonAPI.unfollow(follower, followed) assert User.get_follow_state(follower, followed) == nil @@ -640,6 +806,14 @@ defmodule Pleroma.Web.CommonAPITest do assert Repo.get(Activity, follow_activity_two.id).data["state"] == "reject" assert Repo.get(Activity, follow_activity_three.id).data["state"] == "pending" end + + test "doesn't create a following relationship if the corresponding follow request doesn't exist" do + user = insert(:user, locked: true) + not_follower = insert(:user) + CommonAPI.accept_follow_request(not_follower, user) + + assert Pleroma.FollowingRelationship.following?(not_follower, user) == false + end end describe "vote/3" do @@ -649,8 +823,8 @@ defmodule Pleroma.Web.CommonAPITest do {:ok, activity} = CommonAPI.post(user, %{ - "status" => "Am I cute?", - "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20} + status: "Am I cute?", + poll: %{options: ["Yes", "No"], expires_in: 20} }) object = Object.normalize(activity) diff --git a/test/web/common_api/common_api_utils_test.exs b/test/web/common_api/common_api_utils_test.exs index 45fc94522..5708db6a4 100644 --- a/test/web/common_api/common_api_utils_test.exs +++ b/test/web/common_api/common_api_utils_test.exs @@ -7,7 +7,6 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do alias Pleroma.Object alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI.Utils - alias Pleroma.Web.Endpoint use Pleroma.DataCase import ExUnit.CaptureLog @@ -42,28 +41,6 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do end end - test "parses emoji from name and bio" do - {:ok, user} = UserBuilder.insert(%{name: ":blank:", bio: ":firefox:"}) - - expected = [ - %{ - "type" => "Emoji", - "icon" => %{"type" => "Image", "url" => "#{Endpoint.url()}/emoji/Firefox.gif"}, - "name" => ":firefox:" - }, - %{ - "type" => "Emoji", - "icon" => %{ - "type" => "Image", - "url" => "#{Endpoint.url()}/emoji/blank.png" - }, - "name" => ":blank:" - } - ] - - assert expected == Utils.emoji_from_profile(user) - end - describe "format_input/3" do test "works for bare text/plain" do text = "hello world!" @@ -159,11 +136,11 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do {output, _, _} = Utils.format_input(text, "text/markdown") assert output == - ~s(<p><strong>hello world</strong></p><p><em>another <span class="h-card"><a data-user="#{ + ~s(<p><strong>hello world</strong></p><p><em>another <span class="h-card"><a class="u-url mention" data-user="#{ user.id - }" class="u-url mention" href="http://foo.com/user__test" rel="ugc">@<span>user__test</span></a></span> and <span class="h-card"><a data-user="#{ + }" href="http://foo.com/user__test" rel="ugc">@<span>user__test</span></a></span> and <span class="h-card"><a class="u-url mention" data-user="#{ user.id - }" class="u-url mention" href="http://foo.com/user__test" rel="ugc">@<span>user__test</span></a></span> <a href="http://google.com" rel="ugc">google.com</a> paragraph</em></p>) + }" href="http://foo.com/user__test" rel="ugc">@<span>user__test</span></a></span> <a href="http://google.com" rel="ugc">google.com</a> paragraph</em></p>) end end @@ -251,7 +228,7 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do user = insert(:user) mentioned_user = insert(:user) third_user = insert(:user) - {:ok, activity} = CommonAPI.post(third_user, %{"status" => "uguu"}) + {:ok, activity} = CommonAPI.post(third_user, %{status: "uguu"}) mentions = [mentioned_user.ap_id] {to, cc} = Utils.get_to_and_cc(user, mentions, activity, "public", nil) @@ -284,7 +261,7 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do user = insert(:user) mentioned_user = insert(:user) third_user = insert(:user) - {:ok, activity} = CommonAPI.post(third_user, %{"status" => "uguu"}) + {:ok, activity} = CommonAPI.post(third_user, %{status: "uguu"}) mentions = [mentioned_user.ap_id] {to, cc} = Utils.get_to_and_cc(user, mentions, activity, "unlisted", nil) @@ -315,7 +292,7 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do user = insert(:user) mentioned_user = insert(:user) third_user = insert(:user) - {:ok, activity} = CommonAPI.post(third_user, %{"status" => "uguu"}) + {:ok, activity} = CommonAPI.post(third_user, %{status: "uguu"}) mentions = [mentioned_user.ap_id] {to, cc} = Utils.get_to_and_cc(user, mentions, activity, "private", nil) @@ -345,7 +322,7 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do user = insert(:user) mentioned_user = insert(:user) third_user = insert(:user) - {:ok, activity} = CommonAPI.post(third_user, %{"status" => "uguu"}) + {:ok, activity} = CommonAPI.post(third_user, %{status: "uguu"}) mentions = [mentioned_user.ap_id] {to, cc} = Utils.get_to_and_cc(user, mentions, activity, "direct", nil) @@ -358,26 +335,6 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do end end - describe "get_by_id_or_ap_id/1" do - test "get activity by id" do - activity = insert(:note_activity) - %Pleroma.Activity{} = note = Utils.get_by_id_or_ap_id(activity.id) - assert note.id == activity.id - end - - test "get activity by ap_id" do - activity = insert(:note_activity) - %Pleroma.Activity{} = note = Utils.get_by_id_or_ap_id(activity.data["object"]) - assert note.id == activity.id - end - - test "get activity by object when type isn't `Create` " do - activity = insert(:like_activity) - %Pleroma.Activity{} = like = Utils.get_by_id_or_ap_id(activity.id) - assert like.data["object"] == activity.data["object"] - end - end - describe "to_master_date/1" do test "removes microseconds from date (NaiveDateTime)" do assert Utils.to_masto_date(~N[2015-01-23 23:50:07.123]) == "2015-01-23T23:50:07.000Z" @@ -472,6 +429,13 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do activity = insert(:note_activity, user: user, note: object) Pleroma.Repo.delete(object) + obj_url = activity.data["object"] + + Tesla.Mock.mock(fn + %{method: :get, url: ^obj_url} -> + %Tesla.Env{status: 404, body: ""} + end) + assert Utils.maybe_notify_mentioned_recipients(["test-test"], activity) == [ "test-test" ] @@ -499,8 +463,8 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do desc = Jason.encode!(%{object.id => "test-desc"}) assert Utils.attachments_from_ids(%{ - "media_ids" => ["#{object.id}"], - "descriptions" => desc + media_ids: ["#{object.id}"], + descriptions: desc }) == [ Map.merge(object.data, %{"name" => "test-desc"}) ] @@ -508,7 +472,7 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do test "returns attachments without descs" do object = insert(:note) - assert Utils.attachments_from_ids(%{"media_ids" => ["#{object.id}"]}) == [object.data] + assert Utils.attachments_from_ids(%{media_ids: ["#{object.id}"]}) == [object.data] end test "returns [] when not pass media_ids" do diff --git a/test/web/federator_test.exs b/test/web/federator_test.exs index da844c24c..de90aa6e0 100644 --- a/test/web/federator_test.exs +++ b/test/web/federator_test.exs @@ -29,7 +29,7 @@ defmodule Pleroma.Web.FederatorTest do describe "Publish an activity" do setup do user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "HI"}) + {:ok, activity} = CommonAPI.post(user, %{status: "HI"}) relay_mock = { Pleroma.Web.ActivityPub.Relay, @@ -78,7 +78,7 @@ defmodule Pleroma.Web.FederatorTest do local: false, nickname: "nick1@domain.com", ap_id: "https://domain.com/users/nick1", - source_data: %{"inbox" => inbox1}, + inbox: inbox1, ap_enabled: true }) @@ -86,7 +86,7 @@ defmodule Pleroma.Web.FederatorTest do local: false, nickname: "nick2@domain2.com", ap_id: "https://domain2.com/users/nick2", - source_data: %{"inbox" => inbox2}, + inbox: inbox2, ap_enabled: true }) @@ -96,7 +96,7 @@ defmodule Pleroma.Web.FederatorTest do Instances.set_consistently_unreachable(URI.parse(inbox2).host) {:ok, _activity} = - CommonAPI.post(user, %{"status" => "HI @nick1@domain.com, @nick2@domain2.com!"}) + CommonAPI.post(user, %{status: "HI @nick1@domain.com, @nick2@domain2.com!"}) expected_dt = NaiveDateTime.to_iso8601(dt) @@ -130,6 +130,9 @@ defmodule Pleroma.Web.FederatorTest do assert {:ok, job} = Federator.incoming_ap_doc(params) assert {:ok, _activity} = ObanHelpers.perform(job) + + assert {:ok, job} = Federator.incoming_ap_doc(params) + assert {:error, :already_present} = ObanHelpers.perform(job) end test "rejects incoming AP docs with incorrect origin" do @@ -148,7 +151,7 @@ defmodule Pleroma.Web.FederatorTest do } assert {:ok, job} = Federator.incoming_ap_doc(params) - assert :error = ObanHelpers.perform(job) + assert {:error, :origin_containment_failed} = ObanHelpers.perform(job) end test "it does not crash if MRF rejects the post" do @@ -164,7 +167,7 @@ defmodule Pleroma.Web.FederatorTest do |> Poison.decode!() assert {:ok, job} = Federator.incoming_ap_doc(params) - assert :error = ObanHelpers.perform(job) + assert {:error, _} = ObanHelpers.perform(job) end end end diff --git a/test/web/feed/tag_controller_test.exs b/test/web/feed/tag_controller_test.exs index e863df86b..3c29cd94f 100644 --- a/test/web/feed/tag_controller_test.exs +++ b/test/web/feed/tag_controller_test.exs @@ -21,7 +21,7 @@ defmodule Pleroma.Web.Feed.TagControllerTest do ) user = insert(:user) - {:ok, activity1} = CommonAPI.post(user, %{"status" => "yeah #PleromaArt"}) + {:ok, activity1} = CommonAPI.post(user, %{status: "yeah #PleromaArt"}) object = Object.normalize(activity1) @@ -43,9 +43,9 @@ defmodule Pleroma.Web.Feed.TagControllerTest do |> Ecto.Changeset.change(data: object_data) |> Pleroma.Repo.update() - {:ok, activity2} = CommonAPI.post(user, %{"status" => "42 This is :moominmamma #PleromaArt"}) + {:ok, activity2} = CommonAPI.post(user, %{status: "42 This is :moominmamma #PleromaArt"}) - {:ok, _activity3} = CommonAPI.post(user, %{"status" => "This is :moominmamma"}) + {:ok, _activity3} = CommonAPI.post(user, %{status: "This is :moominmamma"}) response = conn @@ -88,7 +88,7 @@ defmodule Pleroma.Web.Feed.TagControllerTest do ) user = insert(:user) - {:ok, activity1} = CommonAPI.post(user, %{"status" => "yeah #PleromaArt"}) + {:ok, activity1} = CommonAPI.post(user, %{status: "yeah #PleromaArt"}) object = Object.normalize(activity1) @@ -110,9 +110,9 @@ defmodule Pleroma.Web.Feed.TagControllerTest do |> Ecto.Changeset.change(data: object_data) |> Pleroma.Repo.update() - {:ok, activity2} = CommonAPI.post(user, %{"status" => "42 This is :moominmamma #PleromaArt"}) + {:ok, activity2} = CommonAPI.post(user, %{status: "42 This is :moominmamma #PleromaArt"}) - {:ok, _activity3} = CommonAPI.post(user, %{"status" => "This is :moominmamma"}) + {:ok, _activity3} = CommonAPI.post(user, %{status: "This is :moominmamma"}) response = conn @@ -138,8 +138,8 @@ defmodule Pleroma.Web.Feed.TagControllerTest do ] assert xpath(xml, ~x"//channel/item/pubDate/text()"sl) == [ - FeedView.pub_date(activity1.data["published"]), - FeedView.pub_date(activity2.data["published"]) + FeedView.pub_date(activity2.data["published"]), + FeedView.pub_date(activity1.data["published"]) ] assert xpath(xml, ~x"//channel/item/enclosure/@url"sl) == [ @@ -150,8 +150,8 @@ defmodule Pleroma.Web.Feed.TagControllerTest do obj2 = Object.normalize(activity2) assert xpath(xml, ~x"//channel/item/description/text()"sl) == [ - HtmlEntities.decode(FeedView.activity_content(obj2)), - HtmlEntities.decode(FeedView.activity_content(obj1)) + HtmlEntities.decode(FeedView.activity_content(obj2.data)), + HtmlEntities.decode(FeedView.activity_content(obj1.data)) ] response = diff --git a/test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs b/test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs index 51cebe567..fdb6d4c5d 100644 --- a/test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs +++ b/test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs @@ -14,6 +14,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do describe "updating credentials" do setup do: oauth_access(["write:accounts"]) + setup :request_content_type test "sets user settings in a generic way", %{conn: conn} do res_conn = @@ -25,7 +26,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do } }) - assert user_data = json_response(res_conn, 200) + assert user_data = json_response_and_validate_schema(res_conn, 200) assert user_data["pleroma"]["settings_store"] == %{"pleroma_fe" => %{"theme" => "bla"}} user = Repo.get(User, user_data["id"]) @@ -41,7 +42,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do } }) - assert user_data = json_response(res_conn, 200) + assert user_data = json_response_and_validate_schema(res_conn, 200) assert user_data["pleroma"]["settings_store"] == %{ @@ -62,7 +63,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do } }) - assert user_data = json_response(res_conn, 200) + assert user_data = json_response_and_validate_schema(res_conn, 200) assert user_data["pleroma"]["settings_store"] == %{ @@ -79,18 +80,18 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do "note" => "I drink #cofe with @#{user2.nickname}\n\nsuya.." }) - assert user_data = json_response(conn, 200) + assert user_data = json_response_and_validate_schema(conn, 200) assert user_data["note"] == - ~s(I drink <a class="hashtag" data-tag="cofe" href="http://localhost:4001/tag/cofe">#cofe</a> with <span class="h-card"><a data-user="#{ + ~s(I drink <a class="hashtag" data-tag="cofe" href="http://localhost:4001/tag/cofe">#cofe</a> with <span class="h-card"><a class="u-url mention" data-user="#{ user2.id - }" class="u-url mention" href="#{user2.ap_id}" rel="ugc">@<span>#{user2.nickname}</span></a></span><br/><br/>suya..) + }" href="#{user2.ap_id}" rel="ugc">@<span>#{user2.nickname}</span></a></span><br/><br/>suya..) end test "updates the user's locking status", %{conn: conn} do conn = patch(conn, "/api/v1/accounts/update_credentials", %{locked: "true"}) - assert user_data = json_response(conn, 200) + assert user_data = json_response_and_validate_schema(conn, 200) assert user_data["locked"] == true end @@ -100,24 +101,36 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do conn = patch(conn, "/api/v1/accounts/update_credentials", %{allow_following_move: "false"}) assert refresh_record(user).allow_following_move == false - assert user_data = json_response(conn, 200) + assert user_data = json_response_and_validate_schema(conn, 200) assert user_data["pleroma"]["allow_following_move"] == false end test "updates the user's default scope", %{conn: conn} do - conn = patch(conn, "/api/v1/accounts/update_credentials", %{default_scope: "cofe"}) + conn = patch(conn, "/api/v1/accounts/update_credentials", %{default_scope: "unlisted"}) - assert user_data = json_response(conn, 200) - assert user_data["source"]["privacy"] == "cofe" + assert user_data = json_response_and_validate_schema(conn, 200) + assert user_data["source"]["privacy"] == "unlisted" end test "updates the user's hide_followers status", %{conn: conn} do conn = patch(conn, "/api/v1/accounts/update_credentials", %{hide_followers: "true"}) - assert user_data = json_response(conn, 200) + assert user_data = json_response_and_validate_schema(conn, 200) assert user_data["pleroma"]["hide_followers"] == true end + test "updates the user's discoverable status", %{conn: conn} do + assert %{"source" => %{"pleroma" => %{"discoverable" => true}}} = + conn + |> patch("/api/v1/accounts/update_credentials", %{discoverable: "true"}) + |> json_response_and_validate_schema(:ok) + + assert %{"source" => %{"pleroma" => %{"discoverable" => false}}} = + conn + |> patch("/api/v1/accounts/update_credentials", %{discoverable: "false"}) + |> json_response_and_validate_schema(:ok) + end + test "updates the user's hide_followers_count and hide_follows_count", %{conn: conn} do conn = patch(conn, "/api/v1/accounts/update_credentials", %{ @@ -125,7 +138,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do hide_follows_count: "true" }) - assert user_data = json_response(conn, 200) + assert user_data = json_response_and_validate_schema(conn, 200) assert user_data["pleroma"]["hide_followers_count"] == true assert user_data["pleroma"]["hide_follows_count"] == true end @@ -134,7 +147,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do response = conn |> patch("/api/v1/accounts/update_credentials", %{skip_thread_containment: "true"}) - |> json_response(200) + |> json_response_and_validate_schema(200) assert response["pleroma"]["skip_thread_containment"] == true assert refresh_record(user).skip_thread_containment @@ -143,28 +156,28 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do test "updates the user's hide_follows status", %{conn: conn} do conn = patch(conn, "/api/v1/accounts/update_credentials", %{hide_follows: "true"}) - assert user_data = json_response(conn, 200) + assert user_data = json_response_and_validate_schema(conn, 200) assert user_data["pleroma"]["hide_follows"] == true end test "updates the user's hide_favorites status", %{conn: conn} do conn = patch(conn, "/api/v1/accounts/update_credentials", %{hide_favorites: "true"}) - assert user_data = json_response(conn, 200) + assert user_data = json_response_and_validate_schema(conn, 200) assert user_data["pleroma"]["hide_favorites"] == true end test "updates the user's show_role status", %{conn: conn} do conn = patch(conn, "/api/v1/accounts/update_credentials", %{show_role: "false"}) - assert user_data = json_response(conn, 200) + assert user_data = json_response_and_validate_schema(conn, 200) assert user_data["source"]["pleroma"]["show_role"] == false end test "updates the user's no_rich_text status", %{conn: conn} do conn = patch(conn, "/api/v1/accounts/update_credentials", %{no_rich_text: "true"}) - assert user_data = json_response(conn, 200) + assert user_data = json_response_and_validate_schema(conn, 200) assert user_data["source"]["pleroma"]["no_rich_text"] == true end @@ -172,7 +185,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do conn = patch(conn, "/api/v1/accounts/update_credentials", %{"display_name" => "markorepairs"}) - assert user_data = json_response(conn, 200) + assert user_data = json_response_and_validate_schema(conn, 200) assert user_data["display_name"] == "markorepairs" end @@ -185,7 +198,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do conn = patch(conn, "/api/v1/accounts/update_credentials", %{"avatar" => new_avatar}) - assert user_response = json_response(conn, 200) + assert user_response = json_response_and_validate_schema(conn, 200) assert user_response["avatar"] != User.avatar_url(user) end @@ -198,7 +211,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do conn = patch(conn, "/api/v1/accounts/update_credentials", %{"header" => new_header}) - assert user_response = json_response(conn, 200) + assert user_response = json_response_and_validate_schema(conn, 200) assert user_response["header"] != User.banner_url(user) end @@ -214,7 +227,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do "pleroma_background_image" => new_header }) - assert user_response = json_response(conn, 200) + assert user_response = json_response_and_validate_schema(conn, 200) assert user_response["pleroma"]["background_image"] end @@ -225,14 +238,15 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do for token <- [token1, token2] do conn = build_conn() + |> put_req_header("content-type", "multipart/form-data") |> put_req_header("authorization", "Bearer #{token.token}") |> patch("/api/v1/accounts/update_credentials", %{}) if token == token1 do assert %{"error" => "Insufficient permissions: write:accounts."} == - json_response(conn, 403) + json_response_and_validate_schema(conn, 403) else - assert json_response(conn, 200) + assert json_response_and_validate_schema(conn, 200) end end end @@ -247,11 +261,11 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do "display_name" => name }) - assert json_response(ret_conn, 200) + assert json_response_and_validate_schema(ret_conn, 200) conn = get(conn, "/api/v1/accounts/#{user.id}") - assert user_data = json_response(conn, 200) + assert user_data = json_response_and_validate_schema(conn, 200) assert user_data["note"] == note assert user_data["display_name"] == name @@ -261,17 +275,20 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do test "update fields", %{conn: conn} do fields = [ %{"name" => "<a href=\"http://google.com\">foo</a>", "value" => "<script>bar</script>"}, - %{"name" => "link", "value" => "cofe.io"} + %{"name" => "link.io", "value" => "cofe.io"} ] account_data = conn |> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields}) - |> json_response(200) + |> json_response_and_validate_schema(200) assert account_data["fields"] == [ %{"name" => "<a href=\"http://google.com\">foo</a>", "value" => "bar"}, - %{"name" => "link", "value" => ~S(<a href="http://cofe.io" rel="ugc">cofe.io</a>)} + %{ + "name" => "link.io", + "value" => ~S(<a href="http://cofe.io" rel="ugc">cofe.io</a>) + } ] assert account_data["source"]["fields"] == [ @@ -279,14 +296,16 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do "name" => "<a href=\"http://google.com\">foo</a>", "value" => "<script>bar</script>" }, - %{"name" => "link", "value" => "cofe.io"} + %{"name" => "link.io", "value" => "cofe.io"} ] + end + test "update fields via x-www-form-urlencoded", %{conn: conn} do fields = [ "fields_attributes[1][name]=link", - "fields_attributes[1][value]=cofe.io", - "fields_attributes[0][name]=<a href=\"http://google.com\">foo</a>", + "fields_attributes[1][value]=http://cofe.io", + "fields_attributes[0][name]=foo", "fields_attributes[0][value]=bar" ] |> Enum.join("&") @@ -295,67 +314,70 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do conn |> put_req_header("content-type", "application/x-www-form-urlencoded") |> patch("/api/v1/accounts/update_credentials", fields) - |> json_response(200) + |> json_response_and_validate_schema(200) assert account["fields"] == [ - %{"name" => "<a href=\"http://google.com\">foo</a>", "value" => "bar"}, - %{"name" => "link", "value" => ~S(<a href="http://cofe.io" rel="ugc">cofe.io</a>)} + %{"name" => "foo", "value" => "bar"}, + %{ + "name" => "link", + "value" => ~S(<a href="http://cofe.io" rel="ugc">http://cofe.io</a>) + } ] assert account["source"]["fields"] == [ - %{ - "name" => "<a href=\"http://google.com\">foo</a>", - "value" => "bar" - }, - %{"name" => "link", "value" => "cofe.io"} + %{"name" => "foo", "value" => "bar"}, + %{"name" => "link", "value" => "http://cofe.io"} + ] + end + + test "update fields with empty name", %{conn: conn} do + fields = [ + %{"name" => "foo", "value" => ""}, + %{"name" => "", "value" => "bar"} + ] + + account = + conn + |> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields}) + |> json_response_and_validate_schema(200) + + assert account["fields"] == [ + %{"name" => "foo", "value" => ""} ] + end + test "update fields when invalid request", %{conn: conn} do name_limit = Pleroma.Config.get([:instance, :account_field_name_length]) value_limit = Pleroma.Config.get([:instance, :account_field_value_length]) + long_name = Enum.map(0..name_limit, fn _ -> "x" end) |> Enum.join() long_value = Enum.map(0..value_limit, fn _ -> "x" end) |> Enum.join() - fields = [%{"name" => "<b>foo<b>", "value" => long_value}] + fields = [%{"name" => "foo", "value" => long_value}] assert %{"error" => "Invalid request"} == conn |> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields}) - |> json_response(403) - - long_name = Enum.map(0..name_limit, fn _ -> "x" end) |> Enum.join() + |> json_response_and_validate_schema(403) fields = [%{"name" => long_name, "value" => "bar"}] assert %{"error" => "Invalid request"} == conn |> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields}) - |> json_response(403) + |> json_response_and_validate_schema(403) Pleroma.Config.put([:instance, :max_account_fields], 1) fields = [ - %{"name" => "<b>foo<b>", "value" => "<i>bar</i>"}, + %{"name" => "foo", "value" => "bar"}, %{"name" => "link", "value" => "cofe.io"} ] assert %{"error" => "Invalid request"} == conn |> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields}) - |> json_response(403) - - fields = [ - %{"name" => "foo", "value" => ""}, - %{"name" => "", "value" => "bar"} - ] - - account = - conn - |> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields}) - |> json_response(200) - - assert account["fields"] == [ - %{"name" => "foo", "value" => ""} - ] + |> json_response_and_validate_schema(403) end end end diff --git a/test/web/mastodon_api/controllers/account_controller_test.exs b/test/web/mastodon_api/controllers/account_controller_test.exs index a9fa0ce48..280bd6aca 100644 --- a/test/web/mastodon_api/controllers/account_controller_test.exs +++ b/test/web/mastodon_api/controllers/account_controller_test.exs @@ -19,43 +19,37 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do setup do: clear_config([:instance, :limit_to_local_content]) test "works by id" do - user = insert(:user) - - conn = - build_conn() - |> get("/api/v1/accounts/#{user.id}") - - assert %{"id" => id} = json_response(conn, 200) - assert id == to_string(user.id) + %User{id: user_id} = insert(:user) - conn = - build_conn() - |> get("/api/v1/accounts/-1") + assert %{"id" => ^user_id} = + build_conn() + |> get("/api/v1/accounts/#{user_id}") + |> json_response_and_validate_schema(200) - assert %{"error" => "Can't find user"} = json_response(conn, 404) + assert %{"error" => "Can't find user"} = + build_conn() + |> get("/api/v1/accounts/-1") + |> json_response_and_validate_schema(404) end test "works by nickname" do user = insert(:user) - conn = - build_conn() - |> get("/api/v1/accounts/#{user.nickname}") - - assert %{"id" => id} = json_response(conn, 200) - assert id == user.id + assert %{"id" => user_id} = + build_conn() + |> get("/api/v1/accounts/#{user.nickname}") + |> json_response_and_validate_schema(200) end test "works by nickname for remote users" do Config.put([:instance, :limit_to_local_content], false) - user = insert(:user, nickname: "user@example.com", local: false) - conn = - build_conn() - |> get("/api/v1/accounts/#{user.nickname}") + user = insert(:user, nickname: "user@example.com", local: false) - assert %{"id" => id} = json_response(conn, 200) - assert id == user.id + assert %{"id" => user_id} = + build_conn() + |> get("/api/v1/accounts/#{user.nickname}") + |> json_response_and_validate_schema(200) end test "respects limit_to_local_content == :all for remote user nicknames" do @@ -63,11 +57,9 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do user = insert(:user, nickname: "user@example.com", local: false) - conn = - build_conn() - |> get("/api/v1/accounts/#{user.nickname}") - - assert json_response(conn, 404) + assert build_conn() + |> get("/api/v1/accounts/#{user.nickname}") + |> json_response_and_validate_schema(404) end test "respects limit_to_local_content == :unauthenticated for remote user nicknames" do @@ -80,7 +72,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do build_conn() |> get("/api/v1/accounts/#{user.nickname}") - assert json_response(conn, 404) + assert json_response_and_validate_schema(conn, 404) conn = build_conn() @@ -88,7 +80,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do |> assign(:token, insert(:oauth_token, user: reading_user, scopes: ["read:accounts"])) |> get("/api/v1/accounts/#{user.nickname}") - assert %{"id" => id} = json_response(conn, 200) + assert %{"id" => id} = json_response_and_validate_schema(conn, 200) assert id == user.id end @@ -99,21 +91,21 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do user_one = insert(:user, %{id: 1212}) user_two = insert(:user, %{nickname: "#{user_one.id}garbage"}) - resp_one = + acc_one = conn |> get("/api/v1/accounts/#{user_one.id}") + |> json_response_and_validate_schema(:ok) - resp_two = + acc_two = conn |> get("/api/v1/accounts/#{user_two.nickname}") + |> json_response_and_validate_schema(:ok) - resp_three = + acc_three = conn |> get("/api/v1/accounts/#{user_two.id}") + |> json_response_and_validate_schema(:ok) - acc_one = json_response(resp_one, 200) - acc_two = json_response(resp_two, 200) - acc_three = json_response(resp_three, 200) refute acc_one == acc_two assert acc_two == acc_three end @@ -121,23 +113,19 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do test "returns 404 when user is invisible", %{conn: conn} do user = insert(:user, %{invisible: true}) - resp = - conn - |> get("/api/v1/accounts/#{user.nickname}") - |> json_response(404) - - assert %{"error" => "Can't find user"} = resp + assert %{"error" => "Can't find user"} = + conn + |> get("/api/v1/accounts/#{user.nickname}") + |> json_response_and_validate_schema(404) end test "returns 404 for internal.fetch actor", %{conn: conn} do %User{nickname: "internal.fetch"} = InternalFetchActor.get_actor() - resp = - conn - |> get("/api/v1/accounts/internal.fetch") - |> json_response(404) - - assert %{"error" => "Can't find user"} = resp + assert %{"error" => "Can't find user"} = + conn + |> get("/api/v1/accounts/internal.fetch") + |> json_response_and_validate_schema(404) end end @@ -155,27 +143,25 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do setup do: clear_config([:restrict_unauthenticated, :profiles, :remote], true) test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do - res_conn = get(conn, "/api/v1/accounts/#{local.id}") - - assert json_response(res_conn, :not_found) == %{ - "error" => "Can't find user" - } - - res_conn = get(conn, "/api/v1/accounts/#{remote.id}") - - assert json_response(res_conn, :not_found) == %{ - "error" => "Can't find user" - } + assert %{"error" => "Can't find user"} == + conn + |> get("/api/v1/accounts/#{local.id}") + |> json_response_and_validate_schema(:not_found) + + assert %{"error" => "Can't find user"} == + conn + |> get("/api/v1/accounts/#{remote.id}") + |> json_response_and_validate_schema(:not_found) end test "if user is authenticated", %{local: local, remote: remote} do %{conn: conn} = oauth_access(["read"]) res_conn = get(conn, "/api/v1/accounts/#{local.id}") - assert %{"id" => _} = json_response(res_conn, 200) + assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200) res_conn = get(conn, "/api/v1/accounts/#{remote.id}") - assert %{"id" => _} = json_response(res_conn, 200) + assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200) end end @@ -187,22 +173,22 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do res_conn = get(conn, "/api/v1/accounts/#{local.id}") - assert json_response(res_conn, :not_found) == %{ + assert json_response_and_validate_schema(res_conn, :not_found) == %{ "error" => "Can't find user" } res_conn = get(conn, "/api/v1/accounts/#{remote.id}") - assert %{"id" => _} = json_response(res_conn, 200) + assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200) end test "if user is authenticated", %{local: local, remote: remote} do %{conn: conn} = oauth_access(["read"]) res_conn = get(conn, "/api/v1/accounts/#{local.id}") - assert %{"id" => _} = json_response(res_conn, 200) + assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200) res_conn = get(conn, "/api/v1/accounts/#{remote.id}") - assert %{"id" => _} = json_response(res_conn, 200) + assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200) end end @@ -213,11 +199,11 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do res_conn = get(conn, "/api/v1/accounts/#{local.id}") - assert %{"id" => _} = json_response(res_conn, 200) + assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200) res_conn = get(conn, "/api/v1/accounts/#{remote.id}") - assert json_response(res_conn, :not_found) == %{ + assert json_response_and_validate_schema(res_conn, :not_found) == %{ "error" => "Can't find user" } end @@ -226,46 +212,83 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do %{conn: conn} = oauth_access(["read"]) res_conn = get(conn, "/api/v1/accounts/#{local.id}") - assert %{"id" => _} = json_response(res_conn, 200) + assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200) res_conn = get(conn, "/api/v1/accounts/#{remote.id}") - assert %{"id" => _} = json_response(res_conn, 200) + assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200) end end describe "user timelines" do setup do: oauth_access(["read:statuses"]) + test "works with announces that are just addressed to public", %{conn: conn} do + user = insert(:user, ap_id: "https://honktest/u/test", local: false) + other_user = insert(:user) + + {:ok, post} = CommonAPI.post(other_user, %{status: "bonkeronk"}) + + {:ok, announce, _} = + %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "actor" => "https://honktest/u/test", + "id" => "https://honktest/u/test/bonk/1793M7B9MQ48847vdx", + "object" => post.data["object"], + "published" => "2019-06-25T19:33:58Z", + "to" => ["https://www.w3.org/ns/activitystreams#Public"], + "type" => "Announce" + } + |> ActivityPub.persist(local: false) + + assert resp = + conn + |> get("/api/v1/accounts/#{user.id}/statuses") + |> json_response_and_validate_schema(200) + + assert [%{"id" => id}] = resp + assert id == announce.id + end + test "respects blocks", %{user: user_one, conn: conn} do user_two = insert(:user) user_three = insert(:user) User.block(user_one, user_two) - {:ok, activity} = CommonAPI.post(user_two, %{"status" => "User one sux0rz"}) + {:ok, activity} = CommonAPI.post(user_two, %{status: "User one sux0rz"}) {:ok, repeat, _} = CommonAPI.repeat(activity.id, user_three) - resp = get(conn, "/api/v1/accounts/#{user_two.id}/statuses") + assert resp = + conn + |> get("/api/v1/accounts/#{user_two.id}/statuses") + |> json_response_and_validate_schema(200) - assert [%{"id" => id}] = json_response(resp, 200) + assert [%{"id" => id}] = resp assert id == activity.id # Even a blocked user will deliver the full user timeline, there would be # no point in looking at a blocked users timeline otherwise - resp = get(conn, "/api/v1/accounts/#{user_two.id}/statuses") + assert resp = + conn + |> get("/api/v1/accounts/#{user_two.id}/statuses") + |> json_response_and_validate_schema(200) - assert [%{"id" => id}] = json_response(resp, 200) + assert [%{"id" => id}] = resp assert id == activity.id # Third user's timeline includes the repeat when viewed by unauthenticated user - resp = get(build_conn(), "/api/v1/accounts/#{user_three.id}/statuses") - assert [%{"id" => id}] = json_response(resp, 200) + resp = + build_conn() + |> get("/api/v1/accounts/#{user_three.id}/statuses") + |> json_response_and_validate_schema(200) + + assert [%{"id" => id}] = resp assert id == repeat.id # When viewing a third user's timeline, the blocked users' statuses will NOT be shown resp = get(conn, "/api/v1/accounts/#{user_three.id}/statuses") - assert [] = json_response(resp, 200) + assert [] == json_response_and_validate_schema(resp, 200) end test "gets users statuses", %{conn: conn} do @@ -275,20 +298,24 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do {:ok, _user_three} = User.follow(user_three, user_one) - {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"}) + {:ok, activity} = CommonAPI.post(user_one, %{status: "HI!!!"}) {:ok, direct_activity} = CommonAPI.post(user_one, %{ - "status" => "Hi, @#{user_two.nickname}.", - "visibility" => "direct" + status: "Hi, @#{user_two.nickname}.", + visibility: "direct" }) {:ok, private_activity} = - CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"}) + CommonAPI.post(user_one, %{status: "private", visibility: "private"}) - resp = get(conn, "/api/v1/accounts/#{user_one.id}/statuses") + # TODO!!! + resp = + conn + |> get("/api/v1/accounts/#{user_one.id}/statuses") + |> json_response_and_validate_schema(200) - assert [%{"id" => id}] = json_response(resp, 200) + assert [%{"id" => id}] = resp assert id == to_string(activity.id) resp = @@ -296,8 +323,9 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do |> assign(:user, user_two) |> assign(:token, insert(:oauth_token, user: user_two, scopes: ["read:statuses"])) |> get("/api/v1/accounts/#{user_one.id}/statuses") + |> json_response_and_validate_schema(200) - assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200) + assert [%{"id" => id_one}, %{"id" => id_two}] = resp assert id_one == to_string(direct_activity.id) assert id_two == to_string(activity.id) @@ -306,8 +334,9 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do |> assign(:user, user_three) |> assign(:token, insert(:oauth_token, user: user_three, scopes: ["read:statuses"])) |> get("/api/v1/accounts/#{user_one.id}/statuses") + |> json_response_and_validate_schema(200) - assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200) + assert [%{"id" => id_one}, %{"id" => id_two}] = resp assert id_one == to_string(private_activity.id) assert id_two == to_string(activity.id) end @@ -318,7 +347,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?pinned=true") - assert json_response(conn, 200) == [] + assert json_response_and_validate_schema(conn, 200) == [] end test "gets an users media", %{conn: conn} do @@ -333,56 +362,47 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do {:ok, %{id: media_id}} = ActivityPub.upload(file, actor: user.ap_id) - {:ok, image_post} = CommonAPI.post(user, %{"status" => "cofe", "media_ids" => [media_id]}) + {:ok, %{id: image_post_id}} = CommonAPI.post(user, %{status: "cofe", media_ids: [media_id]}) - conn = get(conn, "/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"}) + conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?only_media=true") - assert [%{"id" => id}] = json_response(conn, 200) - assert id == to_string(image_post.id) + assert [%{"id" => ^image_post_id}] = json_response_and_validate_schema(conn, 200) - conn = get(build_conn(), "/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"}) + conn = get(build_conn(), "/api/v1/accounts/#{user.id}/statuses?only_media=1") - assert [%{"id" => id}] = json_response(conn, 200) - assert id == to_string(image_post.id) + assert [%{"id" => ^image_post_id}] = json_response_and_validate_schema(conn, 200) end test "gets a user's statuses without reblogs", %{user: user, conn: conn} do - {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"}) - {:ok, _, _} = CommonAPI.repeat(post.id, user) + {:ok, %{id: post_id}} = CommonAPI.post(user, %{status: "HI!!!"}) + {:ok, _, _} = CommonAPI.repeat(post_id, user) - conn = get(conn, "/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"}) + conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?exclude_reblogs=true") + assert [%{"id" => ^post_id}] = json_response_and_validate_schema(conn, 200) - assert [%{"id" => id}] = json_response(conn, 200) - assert id == to_string(post.id) - - conn = get(conn, "/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"}) - - assert [%{"id" => id}] = json_response(conn, 200) - assert id == to_string(post.id) + conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?exclude_reblogs=1") + assert [%{"id" => ^post_id}] = json_response_and_validate_schema(conn, 200) end test "filters user's statuses by a hashtag", %{user: user, conn: conn} do - {:ok, post} = CommonAPI.post(user, %{"status" => "#hashtag"}) - {:ok, _post} = CommonAPI.post(user, %{"status" => "hashtag"}) - - conn = get(conn, "/api/v1/accounts/#{user.id}/statuses", %{"tagged" => "hashtag"}) + {:ok, %{id: post_id}} = CommonAPI.post(user, %{status: "#hashtag"}) + {:ok, _post} = CommonAPI.post(user, %{status: "hashtag"}) - assert [%{"id" => id}] = json_response(conn, 200) - assert id == to_string(post.id) + conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?tagged=hashtag") + assert [%{"id" => ^post_id}] = json_response_and_validate_schema(conn, 200) end test "the user views their own timelines and excludes direct messages", %{ user: user, conn: conn } do - {:ok, public_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"}) - {:ok, _direct_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"}) + {:ok, %{id: public_activity_id}} = + CommonAPI.post(user, %{status: ".", visibility: "public"}) - conn = - get(conn, "/api/v1/accounts/#{user.id}/statuses", %{"exclude_visibilities" => ["direct"]}) + {:ok, _direct_activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"}) - assert [%{"id" => id}] = json_response(conn, 200) - assert id == to_string(public_activity.id) + conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?exclude_visibilities[]=direct") + assert [%{"id" => ^public_activity_id}] = json_response_and_validate_schema(conn, 200) end end @@ -402,27 +422,25 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do setup do: clear_config([:restrict_unauthenticated, :profiles, :remote], true) test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do - res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses") - - assert json_response(res_conn, :not_found) == %{ - "error" => "Can't find user" - } - - res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses") - - assert json_response(res_conn, :not_found) == %{ - "error" => "Can't find user" - } + assert %{"error" => "Can't find user"} == + conn + |> get("/api/v1/accounts/#{local.id}/statuses") + |> json_response_and_validate_schema(:not_found) + + assert %{"error" => "Can't find user"} == + conn + |> get("/api/v1/accounts/#{remote.id}/statuses") + |> json_response_and_validate_schema(:not_found) end test "if user is authenticated", %{local: local, remote: remote} do %{conn: conn} = oauth_access(["read"]) res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses") - assert length(json_response(res_conn, 200)) == 1 + assert length(json_response_and_validate_schema(res_conn, 200)) == 1 res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses") - assert length(json_response(res_conn, 200)) == 1 + assert length(json_response_and_validate_schema(res_conn, 200)) == 1 end end @@ -433,24 +451,23 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do setup do: clear_config([:restrict_unauthenticated, :profiles, :local], true) test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do - res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses") - - assert json_response(res_conn, :not_found) == %{ - "error" => "Can't find user" - } + assert %{"error" => "Can't find user"} == + conn + |> get("/api/v1/accounts/#{local.id}/statuses") + |> json_response_and_validate_schema(:not_found) res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses") - assert length(json_response(res_conn, 200)) == 1 + assert length(json_response_and_validate_schema(res_conn, 200)) == 1 end test "if user is authenticated", %{local: local, remote: remote} do %{conn: conn} = oauth_access(["read"]) res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses") - assert length(json_response(res_conn, 200)) == 1 + assert length(json_response_and_validate_schema(res_conn, 200)) == 1 res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses") - assert length(json_response(res_conn, 200)) == 1 + assert length(json_response_and_validate_schema(res_conn, 200)) == 1 end end @@ -462,23 +479,22 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses") - assert length(json_response(res_conn, 200)) == 1 + assert length(json_response_and_validate_schema(res_conn, 200)) == 1 - res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses") - - assert json_response(res_conn, :not_found) == %{ - "error" => "Can't find user" - } + assert %{"error" => "Can't find user"} == + conn + |> get("/api/v1/accounts/#{remote.id}/statuses") + |> json_response_and_validate_schema(:not_found) end test "if user is authenticated", %{local: local, remote: remote} do %{conn: conn} = oauth_access(["read"]) res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses") - assert length(json_response(res_conn, 200)) == 1 + assert length(json_response_and_validate_schema(res_conn, 200)) == 1 res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses") - assert length(json_response(res_conn, 200)) == 1 + assert length(json_response_and_validate_schema(res_conn, 200)) == 1 end end @@ -487,12 +503,11 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do test "getting followers", %{user: user, conn: conn} do other_user = insert(:user) - {:ok, user} = User.follow(user, other_user) + {:ok, %{id: user_id}} = User.follow(user, other_user) conn = get(conn, "/api/v1/accounts/#{other_user.id}/followers") - assert [%{"id" => id}] = json_response(conn, 200) - assert id == to_string(user.id) + assert [%{"id" => ^user_id}] = json_response_and_validate_schema(conn, 200) end test "getting followers, hide_followers", %{user: user, conn: conn} do @@ -501,7 +516,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do conn = get(conn, "/api/v1/accounts/#{other_user.id}/followers") - assert [] == json_response(conn, 200) + assert [] == json_response_and_validate_schema(conn, 200) end test "getting followers, hide_followers, same user requesting" do @@ -515,37 +530,31 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["read:accounts"])) |> get("/api/v1/accounts/#{other_user.id}/followers") - refute [] == json_response(conn, 200) + refute [] == json_response_and_validate_schema(conn, 200) end test "getting followers, pagination", %{user: user, conn: conn} do - follower1 = insert(:user) - follower2 = insert(:user) - follower3 = insert(:user) - {:ok, _} = User.follow(follower1, user) - {:ok, _} = User.follow(follower2, user) - {:ok, _} = User.follow(follower3, user) + {:ok, %User{id: follower1_id}} = :user |> insert() |> User.follow(user) + {:ok, %User{id: follower2_id}} = :user |> insert() |> User.follow(user) + {:ok, %User{id: follower3_id}} = :user |> insert() |> User.follow(user) - res_conn = get(conn, "/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}") + assert [%{"id" => ^follower3_id}, %{"id" => ^follower2_id}] = + conn + |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1_id}") + |> json_response_and_validate_schema(200) - assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200) - assert id3 == follower3.id - assert id2 == follower2.id + assert [%{"id" => ^follower2_id}, %{"id" => ^follower1_id}] = + conn + |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3_id}") + |> json_response_and_validate_schema(200) - res_conn = get(conn, "/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}") + res_conn = get(conn, "/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3_id}") - assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200) - assert id2 == follower2.id - assert id1 == follower1.id - - res_conn = get(conn, "/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}") - - assert [%{"id" => id2}] = json_response(res_conn, 200) - assert id2 == follower2.id + assert [%{"id" => ^follower2_id}] = json_response_and_validate_schema(res_conn, 200) assert [link_header] = get_resp_header(res_conn, "link") - assert link_header =~ ~r/min_id=#{follower2.id}/ - assert link_header =~ ~r/max_id=#{follower2.id}/ + assert link_header =~ ~r/min_id=#{follower2_id}/ + assert link_header =~ ~r/max_id=#{follower2_id}/ end end @@ -558,7 +567,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do conn = get(conn, "/api/v1/accounts/#{user.id}/following") - assert [%{"id" => id}] = json_response(conn, 200) + assert [%{"id" => id}] = json_response_and_validate_schema(conn, 200) assert id == to_string(other_user.id) end @@ -573,7 +582,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["read:accounts"])) |> get("/api/v1/accounts/#{user.id}/following") - assert [] == json_response(conn, 200) + assert [] == json_response_and_validate_schema(conn, 200) end test "getting following, hide_follows, same user requesting" do @@ -587,7 +596,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do |> assign(:token, insert(:oauth_token, user: user, scopes: ["read:accounts"])) |> get("/api/v1/accounts/#{user.id}/following") - refute [] == json_response(conn, 200) + refute [] == json_response_and_validate_schema(conn, 200) end test "getting following, pagination", %{user: user, conn: conn} do @@ -600,20 +609,20 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do res_conn = get(conn, "/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}") - assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200) + assert [%{"id" => id3}, %{"id" => id2}] = json_response_and_validate_schema(res_conn, 200) assert id3 == following3.id assert id2 == following2.id res_conn = get(conn, "/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}") - assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200) + assert [%{"id" => id2}, %{"id" => id1}] = json_response_and_validate_schema(res_conn, 200) assert id2 == following2.id assert id1 == following1.id res_conn = get(conn, "/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}") - assert [%{"id" => id2}] = json_response(res_conn, 200) + assert [%{"id" => id2}] = json_response_and_validate_schema(res_conn, 200) assert id2 == following2.id assert [link_header] = get_resp_header(res_conn, "link") @@ -626,30 +635,37 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do setup do: oauth_access(["follow"]) test "following / unfollowing a user", %{conn: conn} do - other_user = insert(:user) - - ret_conn = post(conn, "/api/v1/accounts/#{other_user.id}/follow") - - assert %{"id" => _id, "following" => true} = json_response(ret_conn, 200) - - ret_conn = post(conn, "/api/v1/accounts/#{other_user.id}/unfollow") - - assert %{"id" => _id, "following" => false} = json_response(ret_conn, 200) - - conn = post(conn, "/api/v1/follows", %{"uri" => other_user.nickname}) - - assert %{"id" => id} = json_response(conn, 200) - assert id == to_string(other_user.id) + %{id: other_user_id, nickname: other_user_nickname} = insert(:user) + + assert %{"id" => _id, "following" => true} = + conn + |> post("/api/v1/accounts/#{other_user_id}/follow") + |> json_response_and_validate_schema(200) + + assert %{"id" => _id, "following" => false} = + conn + |> post("/api/v1/accounts/#{other_user_id}/unfollow") + |> json_response_and_validate_schema(200) + + assert %{"id" => ^other_user_id} = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/follows", %{"uri" => other_user_nickname}) + |> json_response_and_validate_schema(200) end test "cancelling follow request", %{conn: conn} do %{id: other_user_id} = insert(:user, %{locked: true}) assert %{"id" => ^other_user_id, "following" => false, "requested" => true} = - conn |> post("/api/v1/accounts/#{other_user_id}/follow") |> json_response(:ok) + conn + |> post("/api/v1/accounts/#{other_user_id}/follow") + |> json_response_and_validate_schema(:ok) assert %{"id" => ^other_user_id, "following" => false, "requested" => false} = - conn |> post("/api/v1/accounts/#{other_user_id}/unfollow") |> json_response(:ok) + conn + |> post("/api/v1/accounts/#{other_user_id}/unfollow") + |> json_response_and_validate_schema(:ok) end test "following without reblogs" do @@ -659,51 +675,65 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do ret_conn = post(conn, "/api/v1/accounts/#{followed.id}/follow?reblogs=false") - assert %{"showing_reblogs" => false} = json_response(ret_conn, 200) - - {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"}) - {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed) + assert %{"showing_reblogs" => false} = json_response_and_validate_schema(ret_conn, 200) - ret_conn = get(conn, "/api/v1/timelines/home") + {:ok, activity} = CommonAPI.post(other_user, %{status: "hey"}) + {:ok, %{id: reblog_id}, _} = CommonAPI.repeat(activity.id, followed) - assert [] == json_response(ret_conn, 200) + assert [] == + conn + |> get("/api/v1/timelines/home") + |> json_response(200) - ret_conn = post(conn, "/api/v1/accounts/#{followed.id}/follow?reblogs=true") + assert %{"showing_reblogs" => true} = + conn + |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true") + |> json_response_and_validate_schema(200) - assert %{"showing_reblogs" => true} = json_response(ret_conn, 200) - - conn = get(conn, "/api/v1/timelines/home") - - expected_activity_id = reblog.id - assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200) + assert [%{"id" => ^reblog_id}] = + conn + |> get("/api/v1/timelines/home") + |> json_response(200) end test "following / unfollowing errors", %{user: user, conn: conn} do # self follow conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow") - assert %{"error" => "Record not found"} = json_response(conn_res, 404) + + assert %{"error" => "Can not follow yourself"} = + json_response_and_validate_schema(conn_res, 400) # self unfollow user = User.get_cached_by_id(user.id) conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow") - assert %{"error" => "Record not found"} = json_response(conn_res, 404) + + assert %{"error" => "Can not unfollow yourself"} = + json_response_and_validate_schema(conn_res, 400) # self follow via uri user = User.get_cached_by_id(user.id) - conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname}) - assert %{"error" => "Record not found"} = json_response(conn_res, 404) + + assert %{"error" => "Can not follow yourself"} = + conn + |> put_req_header("content-type", "multipart/form-data") + |> post("/api/v1/follows", %{"uri" => user.nickname}) + |> json_response_and_validate_schema(400) # follow non existing user conn_res = post(conn, "/api/v1/accounts/doesntexist/follow") - assert %{"error" => "Record not found"} = json_response(conn_res, 404) + assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn_res, 404) # follow non existing user via uri - conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"}) - assert %{"error" => "Record not found"} = json_response(conn_res, 404) + conn_res = + conn + |> put_req_header("content-type", "multipart/form-data") + |> post("/api/v1/follows", %{"uri" => "doesntexist"}) + + assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn_res, 404) # unfollow non existing user conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow") - assert %{"error" => "Record not found"} = json_response(conn_res, 404) + assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn_res, 404) end end @@ -713,55 +743,52 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do test "with notifications", %{conn: conn} do other_user = insert(:user) - ret_conn = post(conn, "/api/v1/accounts/#{other_user.id}/mute") - - response = json_response(ret_conn, 200) - - assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response + assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/accounts/#{other_user.id}/mute") + |> json_response_and_validate_schema(200) conn = post(conn, "/api/v1/accounts/#{other_user.id}/unmute") - response = json_response(conn, 200) - assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response + assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = + json_response_and_validate_schema(conn, 200) end test "without notifications", %{conn: conn} do other_user = insert(:user) ret_conn = - post(conn, "/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"}) - - response = json_response(ret_conn, 200) + conn + |> put_req_header("content-type", "multipart/form-data") + |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"}) - assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response + assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = + json_response_and_validate_schema(ret_conn, 200) conn = post(conn, "/api/v1/accounts/#{other_user.id}/unmute") - response = json_response(conn, 200) - assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response + assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = + json_response_and_validate_schema(conn, 200) end end describe "pinned statuses" do setup do user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"}) + {:ok, activity} = CommonAPI.post(user, %{status: "HI!!!"}) %{conn: conn} = oauth_access(["read:statuses"], user: user) [conn: conn, user: user, activity: activity] end - test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do - {:ok, _} = CommonAPI.pin(activity.id, user) + test "returns pinned statuses", %{conn: conn, user: user, activity: %{id: activity_id}} do + {:ok, _} = CommonAPI.pin(activity_id, user) - result = - conn - |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true") - |> json_response(200) - - id_str = to_string(activity.id) - - assert [%{"id" => ^id_str, "pinned" => true}] = result + assert [%{"id" => ^activity_id, "pinned" => true}] = + conn + |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true") + |> json_response_and_validate_schema(200) end end @@ -771,11 +798,11 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do ret_conn = post(conn, "/api/v1/accounts/#{other_user.id}/block") - assert %{"id" => _id, "blocking" => true} = json_response(ret_conn, 200) + assert %{"id" => _id, "blocking" => true} = json_response_and_validate_schema(ret_conn, 200) conn = post(conn, "/api/v1/accounts/#{other_user.id}/unblock") - assert %{"id" => _id, "blocking" => false} = json_response(conn, 200) + assert %{"id" => _id, "blocking" => false} = json_response_and_validate_schema(conn, 200) end describe "create account by app" do @@ -794,21 +821,23 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do test "Account registration via Application", %{conn: conn} do conn = - post(conn, "/api/v1/apps", %{ + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/apps", %{ client_name: "client_name", redirect_uris: "urn:ietf:wg:oauth:2.0:oob", scopes: "read, write, follow" }) - %{ - "client_id" => client_id, - "client_secret" => client_secret, - "id" => _, - "name" => "client_name", - "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob", - "vapid_key" => _, - "website" => nil - } = json_response(conn, 200) + assert %{ + "client_id" => client_id, + "client_secret" => client_secret, + "id" => _, + "name" => "client_name", + "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob", + "vapid_key" => _, + "website" => nil + } = json_response_and_validate_schema(conn, 200) conn = post(conn, "/oauth/token", %{ @@ -828,6 +857,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do conn = build_conn() + |> put_req_header("content-type", "multipart/form-data") |> put_req_header("authorization", "Bearer " <> token) |> post("/api/v1/accounts", %{ username: "lain", @@ -842,7 +872,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do "created_at" => _created_at, "scope" => _scope, "token_type" => "Bearer" - } = json_response(conn, 200) + } = json_response_and_validate_schema(conn, 200) token_from_db = Repo.get_by(Token, token: token) assert token_from_db @@ -856,12 +886,15 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do _user = insert(:user, email: "lain@example.org") app_token = insert(:oauth_token, user: nil) - conn = + res = conn |> put_req_header("authorization", "Bearer " <> app_token.token) + |> put_req_header("content-type", "application/json") + |> post("/api/v1/accounts", valid_params) - res = post(conn, "/api/v1/accounts", valid_params) - assert json_response(res, 400) == %{"error" => "{\"email\":[\"has already been taken\"]}"} + assert json_response_and_validate_schema(res, 400) == %{ + "error" => "{\"email\":[\"has already been taken\"]}" + } end test "returns bad_request if missing required params", %{ @@ -870,10 +903,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do } do app_token = insert(:oauth_token, user: nil) - conn = put_req_header(conn, "authorization", "Bearer " <> app_token.token) + conn = + conn + |> put_req_header("authorization", "Bearer " <> app_token.token) + |> put_req_header("content-type", "application/json") res = post(conn, "/api/v1/accounts", valid_params) - assert json_response(res, 200) + assert json_response_and_validate_schema(res, 200) [{127, 0, 0, 1}, {127, 0, 0, 2}, {127, 0, 0, 3}, {127, 0, 0, 4}] |> Stream.zip(Map.delete(valid_params, :email)) @@ -882,9 +918,18 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do conn |> Map.put(:remote_ip, ip) |> post("/api/v1/accounts", Map.delete(valid_params, attr)) - |> json_response(400) - - assert res == %{"error" => "Missing parameters"} + |> json_response_and_validate_schema(400) + + assert res == %{ + "error" => "Missing field: #{attr}.", + "errors" => [ + %{ + "message" => "Missing field: #{attr}", + "source" => %{"pointer" => "/#{attr}"}, + "title" => "Invalid value" + } + ] + } end) end @@ -895,21 +940,28 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do Pleroma.Config.put([:instance, :account_activation_required], true) app_token = insert(:oauth_token, user: nil) - conn = put_req_header(conn, "authorization", "Bearer " <> app_token.token) + + conn = + conn + |> put_req_header("authorization", "Bearer " <> app_token.token) + |> put_req_header("content-type", "application/json") res = conn |> Map.put(:remote_ip, {127, 0, 0, 5}) |> post("/api/v1/accounts", Map.delete(valid_params, :email)) - assert json_response(res, 400) == %{"error" => "Missing parameters"} + assert json_response_and_validate_schema(res, 400) == + %{"error" => "Missing parameter: email"} res = conn |> Map.put(:remote_ip, {127, 0, 0, 6}) |> post("/api/v1/accounts", Map.put(valid_params, :email, "")) - assert json_response(res, 400) == %{"error" => "{\"email\":[\"can't be blank\"]}"} + assert json_response_and_validate_schema(res, 400) == %{ + "error" => "{\"email\":[\"can't be blank\"]}" + } end test "allow registration without an email", %{conn: conn, valid_params: valid_params} do @@ -918,10 +970,11 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do res = conn + |> put_req_header("content-type", "application/json") |> Map.put(:remote_ip, {127, 0, 0, 7}) |> post("/api/v1/accounts", Map.delete(valid_params, :email)) - assert json_response(res, 200) + assert json_response_and_validate_schema(res, 200) end test "allow registration with an empty email", %{conn: conn, valid_params: valid_params} do @@ -930,17 +983,89 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do res = conn + |> put_req_header("content-type", "application/json") |> Map.put(:remote_ip, {127, 0, 0, 8}) |> post("/api/v1/accounts", Map.put(valid_params, :email, "")) - assert json_response(res, 200) + assert json_response_and_validate_schema(res, 200) end test "returns forbidden if token is invalid", %{conn: conn, valid_params: valid_params} do - conn = put_req_header(conn, "authorization", "Bearer " <> "invalid-token") + res = + conn + |> put_req_header("authorization", "Bearer " <> "invalid-token") + |> put_req_header("content-type", "multipart/form-data") + |> post("/api/v1/accounts", valid_params) - res = post(conn, "/api/v1/accounts", valid_params) - assert json_response(res, 403) == %{"error" => "Invalid credentials"} + assert json_response_and_validate_schema(res, 403) == %{"error" => "Invalid credentials"} + end + + test "registration from trusted app" do + clear_config([Pleroma.Captcha, :enabled], true) + app = insert(:oauth_app, trusted: true, scopes: ["read", "write", "follow", "push"]) + + conn = + build_conn() + |> post("/oauth/token", %{ + "grant_type" => "client_credentials", + "client_id" => app.client_id, + "client_secret" => app.client_secret + }) + + assert %{"access_token" => token, "token_type" => "Bearer"} = json_response(conn, 200) + + response = + build_conn() + |> Plug.Conn.put_req_header("authorization", "Bearer " <> token) + |> put_req_header("content-type", "multipart/form-data") + |> post("/api/v1/accounts", %{ + nickname: "nickanme", + agreement: true, + email: "email@example.com", + fullname: "Lain", + username: "Lain", + password: "some_password", + confirm: "some_password" + }) + |> json_response_and_validate_schema(200) + + assert %{ + "access_token" => access_token, + "created_at" => _, + "scope" => ["read", "write", "follow", "push"], + "token_type" => "Bearer" + } = response + + response = + build_conn() + |> Plug.Conn.put_req_header("authorization", "Bearer " <> access_token) + |> get("/api/v1/accounts/verify_credentials") + |> json_response_and_validate_schema(200) + + assert %{ + "acct" => "Lain", + "bot" => false, + "display_name" => "Lain", + "follow_requests_count" => 0, + "followers_count" => 0, + "following_count" => 0, + "locked" => false, + "note" => "", + "source" => %{ + "fields" => [], + "note" => "", + "pleroma" => %{ + "actor_type" => "Person", + "discoverable" => false, + "no_rich_text" => false, + "show_role" => true + }, + "privacy" => "public", + "sensitive" => false + }, + "statuses_count" => 0, + "username" => "Lain" + } = response end end @@ -954,10 +1079,12 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do conn |> put_req_header("authorization", "Bearer " <> app_token.token) |> Map.put(:remote_ip, {15, 15, 15, 15}) + |> put_req_header("content-type", "multipart/form-data") for i <- 1..2 do conn = - post(conn, "/api/v1/accounts", %{ + conn + |> post("/api/v1/accounts", %{ username: "#{i}lain", email: "#{i}lain@example.org", password: "PlzDontHackLain", @@ -969,7 +1096,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do "created_at" => _created_at, "scope" => _scope, "token_type" => "Bearer" - } = json_response(conn, 200) + } = json_response_and_validate_schema(conn, 200) token_from_db = Repo.get_by(Token, token: token) assert token_from_db @@ -987,7 +1114,94 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do agreement: true }) - assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"} + assert json_response_and_validate_schema(conn, :too_many_requests) == %{ + "error" => "Throttled" + } + end + end + + describe "create account with enabled captcha" do + setup %{conn: conn} do + app_token = insert(:oauth_token, user: nil) + + conn = + conn + |> put_req_header("authorization", "Bearer " <> app_token.token) + |> put_req_header("content-type", "multipart/form-data") + + [conn: conn] + end + + setup do: clear_config([Pleroma.Captcha, :enabled], true) + + test "creates an account and returns 200 if captcha is valid", %{conn: conn} do + %{token: token, answer_data: answer_data} = Pleroma.Captcha.new() + + params = %{ + username: "lain", + email: "lain@example.org", + password: "PlzDontHackLain", + agreement: true, + captcha_solution: Pleroma.Captcha.Mock.solution(), + captcha_token: token, + captcha_answer_data: answer_data + } + + assert %{ + "access_token" => access_token, + "created_at" => _, + "scope" => ["read"], + "token_type" => "Bearer" + } = + conn + |> post("/api/v1/accounts", params) + |> json_response_and_validate_schema(:ok) + + assert Token |> Repo.get_by(token: access_token) |> Repo.preload(:user) |> Map.get(:user) + + Cachex.del(:used_captcha_cache, token) + end + + test "returns 400 if any captcha field is not provided", %{conn: conn} do + captcha_fields = [:captcha_solution, :captcha_token, :captcha_answer_data] + + valid_params = %{ + username: "lain", + email: "lain@example.org", + password: "PlzDontHackLain", + agreement: true, + captcha_solution: "xx", + captcha_token: "xx", + captcha_answer_data: "xx" + } + + for field <- captcha_fields do + expected = %{ + "error" => "{\"captcha\":[\"Invalid CAPTCHA (Missing parameter: #{field})\"]}" + } + + assert expected == + conn + |> post("/api/v1/accounts", Map.delete(valid_params, field)) + |> json_response_and_validate_schema(:bad_request) + end + end + + test "returns an error if captcha is invalid", %{conn: conn} do + params = %{ + username: "lain", + email: "lain@example.org", + password: "PlzDontHackLain", + agreement: true, + captcha_solution: "cofe", + captcha_token: "cofe", + captcha_answer_data: "cofe" + } + + assert %{"error" => "{\"captcha\":[\"Invalid answer data\"]}"} == + conn + |> post("/api/v1/accounts", params) + |> json_response_and_validate_schema(:bad_request) end end @@ -995,27 +1209,28 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do test "returns lists to which the account belongs" do %{user: user, conn: conn} = oauth_access(["read:lists"]) other_user = insert(:user) - assert {:ok, %Pleroma.List{} = list} = Pleroma.List.create("Test List", user) + assert {:ok, %Pleroma.List{id: list_id} = list} = Pleroma.List.create("Test List", user) {:ok, %{following: _following}} = Pleroma.List.follow(list, other_user) - res = - conn - |> get("/api/v1/accounts/#{other_user.id}/lists") - |> json_response(200) - - assert res == [%{"id" => to_string(list.id), "title" => "Test List"}] + assert [%{"id" => list_id, "title" => "Test List"}] = + conn + |> get("/api/v1/accounts/#{other_user.id}/lists") + |> json_response_and_validate_schema(200) end end describe "verify_credentials" do test "verify_credentials" do %{user: user, conn: conn} = oauth_access(["read:accounts"]) + [notification | _] = insert_list(7, :notification, user: user) + Pleroma.Notification.set_read_up_to(user, notification.id) conn = get(conn, "/api/v1/accounts/verify_credentials") - response = json_response(conn, 200) + response = json_response_and_validate_schema(conn, 200) assert %{"id" => id, "source" => %{"privacy" => "public"}} = response assert response["pleroma"]["chat_token"] + assert response["pleroma"]["unread_notifications_count"] == 6 assert id == to_string(user.id) end @@ -1025,7 +1240,9 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do conn = get(conn, "/api/v1/accounts/verify_credentials") - assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200) + assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = + json_response_and_validate_schema(conn, 200) + assert id == to_string(user.id) end @@ -1035,7 +1252,9 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do conn = get(conn, "/api/v1/accounts/verify_credentials") - assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200) + assert %{"id" => id, "source" => %{"privacy" => "private"}} = + json_response_and_validate_schema(conn, 200) + assert id == to_string(user.id) end end @@ -1044,20 +1263,24 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do setup do: oauth_access(["read:follows"]) test "returns the relationships for the current user", %{user: user, conn: conn} do - other_user = insert(:user) + %{id: other_user_id} = other_user = insert(:user) {:ok, _user} = User.follow(user, other_user) - conn = get(conn, "/api/v1/accounts/relationships", %{"id" => [other_user.id]}) - - assert [relationship] = json_response(conn, 200) + assert [%{"id" => ^other_user_id}] = + conn + |> get("/api/v1/accounts/relationships?id=#{other_user.id}") + |> json_response_and_validate_schema(200) - assert to_string(other_user.id) == relationship["id"] + assert [%{"id" => ^other_user_id}] = + conn + |> get("/api/v1/accounts/relationships?id[]=#{other_user.id}") + |> json_response_and_validate_schema(200) end test "returns an empty list on a bad request", %{conn: conn} do conn = get(conn, "/api/v1/accounts/relationships", %{}) - assert [] = json_response(conn, 200) + assert [] = json_response_and_validate_schema(conn, 200) end end @@ -1070,7 +1293,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do conn = get(conn, "/api/v1/mutes") other_user_id = to_string(other_user.id) - assert [%{"id" => ^other_user_id}] = json_response(conn, 200) + assert [%{"id" => ^other_user_id}] = json_response_and_validate_schema(conn, 200) end test "getting a list of blocks" do @@ -1085,6 +1308,6 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do |> get("/api/v1/blocks") other_user_id = to_string(other_user.id) - assert [%{"id" => ^other_user_id}] = json_response(conn, 200) + assert [%{"id" => ^other_user_id}] = json_response_and_validate_schema(conn, 200) end end diff --git a/test/web/mastodon_api/controllers/app_controller_test.exs b/test/web/mastodon_api/controllers/app_controller_test.exs index 77d234d67..a0b8b126c 100644 --- a/test/web/mastodon_api/controllers/app_controller_test.exs +++ b/test/web/mastodon_api/controllers/app_controller_test.exs @@ -16,8 +16,7 @@ defmodule Pleroma.Web.MastodonAPI.AppControllerTest do conn = conn - |> assign(:user, token.user) - |> assign(:token, token) + |> put_req_header("authorization", "Bearer #{token.token}") |> get("/api/v1/apps/verify_credentials") app = Repo.preload(token, :app).app @@ -28,7 +27,7 @@ defmodule Pleroma.Web.MastodonAPI.AppControllerTest do "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key) } - assert expected == json_response(conn, 200) + assert expected == json_response_and_validate_schema(conn, 200) end test "creates an oauth app", %{conn: conn} do @@ -37,6 +36,7 @@ defmodule Pleroma.Web.MastodonAPI.AppControllerTest do conn = conn + |> put_req_header("content-type", "application/json") |> assign(:user, user) |> post("/api/v1/apps", %{ client_name: app_attrs.client_name, @@ -55,6 +55,6 @@ defmodule Pleroma.Web.MastodonAPI.AppControllerTest do "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key) } - assert expected == json_response(conn, 200) + assert expected == json_response_and_validate_schema(conn, 200) end end diff --git a/test/web/mastodon_api/controllers/conversation_controller_test.exs b/test/web/mastodon_api/controllers/conversation_controller_test.exs index 801b0259b..693ba51e5 100644 --- a/test/web/mastodon_api/controllers/conversation_controller_test.exs +++ b/test/web/mastodon_api/controllers/conversation_controller_test.exs @@ -22,21 +22,21 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do {:ok, direct} = CommonAPI.post(user_one, %{ - "status" => "Hi @#{user_two.nickname}, @#{user_three.nickname}!", - "visibility" => "direct" + status: "Hi @#{user_two.nickname}, @#{user_three.nickname}!", + visibility: "direct" }) assert User.get_cached_by_id(user_two.id).unread_conversation_count == 1 {:ok, _follower_only} = CommonAPI.post(user_one, %{ - "status" => "Hi @#{user_two.nickname}!", - "visibility" => "private" + status: "Hi @#{user_two.nickname}!", + visibility: "private" }) res_conn = get(conn, "/api/v1/conversations") - assert response = json_response(res_conn, 200) + assert response = json_response_and_validate_schema(res_conn, 200) assert [ %{ @@ -63,46 +63,46 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do {:ok, direct1} = CommonAPI.post(user_one, %{ - "status" => "Hi @#{user_two.nickname}!", - "visibility" => "direct" + status: "Hi @#{user_two.nickname}!", + visibility: "direct" }) {:ok, _direct2} = CommonAPI.post(user_one, %{ - "status" => "Hi @#{user_three.nickname}!", - "visibility" => "direct" + status: "Hi @#{user_three.nickname}!", + visibility: "direct" }) {:ok, direct3} = CommonAPI.post(user_one, %{ - "status" => "Hi @#{user_two.nickname}, @#{user_three.nickname}!", - "visibility" => "direct" + status: "Hi @#{user_two.nickname}, @#{user_three.nickname}!", + visibility: "direct" }) {:ok, _direct4} = CommonAPI.post(user_two, %{ - "status" => "Hi @#{user_three.nickname}!", - "visibility" => "direct" + status: "Hi @#{user_three.nickname}!", + visibility: "direct" }) {:ok, direct5} = CommonAPI.post(user_two, %{ - "status" => "Hi @#{user_one.nickname}!", - "visibility" => "direct" + status: "Hi @#{user_one.nickname}!", + visibility: "direct" }) - [conversation1, conversation2] = - conn - |> get("/api/v1/conversations", %{"recipients" => [user_two.id]}) - |> json_response(200) + assert [conversation1, conversation2] = + conn + |> get("/api/v1/conversations?recipients[]=#{user_two.id}") + |> json_response_and_validate_schema(200) assert conversation1["last_status"]["id"] == direct5.id assert conversation2["last_status"]["id"] == direct1.id [conversation1] = conn - |> get("/api/v1/conversations", %{"recipients" => [user_two.id, user_three.id]}) - |> json_response(200) + |> get("/api/v1/conversations?recipients[]=#{user_two.id}&recipients[]=#{user_three.id}") + |> json_response_and_validate_schema(200) assert conversation1["last_status"]["id"] == direct3.id end @@ -112,21 +112,21 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do {:ok, direct} = CommonAPI.post(user_one, %{ - "status" => "Hi @#{user_two.nickname}", - "visibility" => "direct" + status: "Hi @#{user_two.nickname}", + visibility: "direct" }) {:ok, direct_reply} = CommonAPI.post(user_two, %{ - "status" => "reply", - "visibility" => "direct", - "in_reply_to_status_id" => direct.id + status: "reply", + visibility: "direct", + in_reply_to_status_id: direct.id }) [%{"last_status" => res_last_status}] = conn |> get("/api/v1/conversations") - |> json_response(200) + |> json_response_and_validate_schema(200) assert res_last_status["id"] == direct_reply.id end @@ -136,8 +136,8 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do {:ok, direct} = CommonAPI.post(user_one, %{ - "status" => "Hi @#{user_two.nickname}", - "visibility" => "direct" + status: "Hi @#{user_two.nickname}", + visibility: "direct" }) assert User.get_cached_by_id(user_one.id).unread_conversation_count == 0 @@ -154,12 +154,12 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do [%{"id" => direct_conversation_id, "unread" => true}] = user_two_conn |> get("/api/v1/conversations") - |> json_response(200) + |> json_response_and_validate_schema(200) %{"unread" => false} = user_two_conn |> post("/api/v1/conversations/#{direct_conversation_id}/read") - |> json_response(200) + |> json_response_and_validate_schema(200) assert User.get_cached_by_id(user_one.id).unread_conversation_count == 0 assert User.get_cached_by_id(user_two.id).unread_conversation_count == 0 @@ -167,15 +167,15 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do # The conversation is marked as unread on reply {:ok, _} = CommonAPI.post(user_two, %{ - "status" => "reply", - "visibility" => "direct", - "in_reply_to_status_id" => direct.id + status: "reply", + visibility: "direct", + in_reply_to_status_id: direct.id }) [%{"unread" => true}] = conn |> get("/api/v1/conversations") - |> json_response(200) + |> json_response_and_validate_schema(200) assert User.get_cached_by_id(user_one.id).unread_conversation_count == 1 assert User.get_cached_by_id(user_two.id).unread_conversation_count == 0 @@ -183,9 +183,9 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do # A reply doesn't increment the user's unread_conversation_count if the conversation is unread {:ok, _} = CommonAPI.post(user_two, %{ - "status" => "reply", - "visibility" => "direct", - "in_reply_to_status_id" => direct.id + status: "reply", + visibility: "direct", + in_reply_to_status_id: direct.id }) assert User.get_cached_by_id(user_one.id).unread_conversation_count == 1 @@ -197,8 +197,8 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do {:ok, direct} = CommonAPI.post(user_one, %{ - "status" => "Hi @#{user_two.nickname}!", - "visibility" => "direct" + status: "Hi @#{user_two.nickname}!", + visibility: "direct" }) res_conn = get(conn, "/api/v1/statuses/#{direct.id}/context") diff --git a/test/web/mastodon_api/controllers/custom_emoji_controller_test.exs b/test/web/mastodon_api/controllers/custom_emoji_controller_test.exs index 6567a0667..ab0027f90 100644 --- a/test/web/mastodon_api/controllers/custom_emoji_controller_test.exs +++ b/test/web/mastodon_api/controllers/custom_emoji_controller_test.exs @@ -6,11 +6,12 @@ defmodule Pleroma.Web.MastodonAPI.CustomEmojiControllerTest do use Pleroma.Web.ConnCase, async: true test "with tags", %{conn: conn} do - [emoji | _body] = - conn - |> get("/api/v1/custom_emojis") - |> json_response(200) + assert resp = + conn + |> get("/api/v1/custom_emojis") + |> json_response_and_validate_schema(200) + assert [emoji | _body] = resp assert Map.has_key?(emoji, "shortcode") assert Map.has_key?(emoji, "static_url") assert Map.has_key?(emoji, "tags") diff --git a/test/web/mastodon_api/controllers/domain_block_controller_test.exs b/test/web/mastodon_api/controllers/domain_block_controller_test.exs index 8d24b3b88..01a24afcf 100644 --- a/test/web/mastodon_api/controllers/domain_block_controller_test.exs +++ b/test/web/mastodon_api/controllers/domain_block_controller_test.exs @@ -13,15 +13,21 @@ defmodule Pleroma.Web.MastodonAPI.DomainBlockControllerTest do %{user: user, conn: conn} = oauth_access(["write:blocks"]) other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"}) - ret_conn = post(conn, "/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"}) + ret_conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"}) - assert %{} = json_response(ret_conn, 200) + assert %{} == json_response_and_validate_schema(ret_conn, 200) user = User.get_cached_by_ap_id(user.ap_id) assert User.blocks?(user, other_user) - ret_conn = delete(conn, "/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"}) + ret_conn = + conn + |> put_req_header("content-type", "application/json") + |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"}) - assert %{} = json_response(ret_conn, 200) + assert %{} == json_response_and_validate_schema(ret_conn, 200) user = User.get_cached_by_ap_id(user.ap_id) refute User.blocks?(user, other_user) end @@ -32,14 +38,10 @@ defmodule Pleroma.Web.MastodonAPI.DomainBlockControllerTest do {:ok, user} = User.block_domain(user, "bad.site") {:ok, user} = User.block_domain(user, "even.worse.site") - conn = - conn - |> assign(:user, user) - |> get("/api/v1/domain_blocks") - - domain_blocks = json_response(conn, 200) - - assert "bad.site" in domain_blocks - assert "even.worse.site" in domain_blocks + assert ["even.worse.site", "bad.site"] == + conn + |> assign(:user, user) + |> get("/api/v1/domain_blocks") + |> json_response_and_validate_schema(200) end end diff --git a/test/web/mastodon_api/controllers/filter_controller_test.exs b/test/web/mastodon_api/controllers/filter_controller_test.exs index 97ab005e0..f29547d13 100644 --- a/test/web/mastodon_api/controllers/filter_controller_test.exs +++ b/test/web/mastodon_api/controllers/filter_controller_test.exs @@ -15,9 +15,12 @@ defmodule Pleroma.Web.MastodonAPI.FilterControllerTest do context: ["home"] } - conn = post(conn, "/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context}) + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context}) - assert response = json_response(conn, 200) + assert response = json_response_and_validate_schema(conn, 200) assert response["phrase"] == filter.phrase assert response["context"] == filter.context assert response["irreversible"] == false @@ -48,12 +51,12 @@ defmodule Pleroma.Web.MastodonAPI.FilterControllerTest do response = conn |> get("/api/v1/filters") - |> json_response(200) + |> json_response_and_validate_schema(200) assert response == render_json( FilterView, - "filters.json", + "index.json", filters: [filter_two, filter_one] ) end @@ -72,7 +75,7 @@ defmodule Pleroma.Web.MastodonAPI.FilterControllerTest do conn = get(conn, "/api/v1/filters/#{filter.filter_id}") - assert _response = json_response(conn, 200) + assert response = json_response_and_validate_schema(conn, 200) end test "update a filter" do @@ -82,7 +85,8 @@ defmodule Pleroma.Web.MastodonAPI.FilterControllerTest do user_id: user.id, filter_id: 2, phrase: "knight", - context: ["home"] + context: ["home"], + hide: true } {:ok, _filter} = Pleroma.Filter.create(query) @@ -93,14 +97,17 @@ defmodule Pleroma.Web.MastodonAPI.FilterControllerTest do } conn = - put(conn, "/api/v1/filters/#{query.filter_id}", %{ + conn + |> put_req_header("content-type", "application/json") + |> put("/api/v1/filters/#{query.filter_id}", %{ phrase: new.phrase, context: new.context }) - assert response = json_response(conn, 200) + assert response = json_response_and_validate_schema(conn, 200) assert response["phrase"] == new.phrase assert response["context"] == new.context + assert response["irreversible"] == true end test "delete a filter" do @@ -117,7 +124,6 @@ defmodule Pleroma.Web.MastodonAPI.FilterControllerTest do conn = delete(conn, "/api/v1/filters/#{filter.filter_id}") - assert response = json_response(conn, 200) - assert response == %{} + assert json_response_and_validate_schema(conn, 200) == %{} end end diff --git a/test/web/mastodon_api/controllers/follow_request_controller_test.exs b/test/web/mastodon_api/controllers/follow_request_controller_test.exs index dd848821a..44e12d15a 100644 --- a/test/web/mastodon_api/controllers/follow_request_controller_test.exs +++ b/test/web/mastodon_api/controllers/follow_request_controller_test.exs @@ -21,13 +21,13 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestControllerTest do other_user = insert(:user) {:ok, _activity} = ActivityPub.follow(other_user, user) - {:ok, other_user} = User.follow(other_user, user, "pending") + {:ok, other_user} = User.follow(other_user, user, :follow_pending) assert User.following?(other_user, user) == false conn = get(conn, "/api/v1/follow_requests") - assert [relationship] = json_response(conn, 200) + assert [relationship] = json_response_and_validate_schema(conn, 200) assert to_string(other_user.id) == relationship["id"] end @@ -35,7 +35,7 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestControllerTest do other_user = insert(:user) {:ok, _activity} = ActivityPub.follow(other_user, user) - {:ok, other_user} = User.follow(other_user, user, "pending") + {:ok, other_user} = User.follow(other_user, user, :follow_pending) user = User.get_cached_by_id(user.id) other_user = User.get_cached_by_id(other_user.id) @@ -44,7 +44,7 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestControllerTest do conn = post(conn, "/api/v1/follow_requests/#{other_user.id}/authorize") - assert relationship = json_response(conn, 200) + assert relationship = json_response_and_validate_schema(conn, 200) assert to_string(other_user.id) == relationship["id"] user = User.get_cached_by_id(user.id) @@ -62,7 +62,7 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestControllerTest do conn = post(conn, "/api/v1/follow_requests/#{other_user.id}/reject") - assert relationship = json_response(conn, 200) + assert relationship = json_response_and_validate_schema(conn, 200) assert to_string(other_user.id) == relationship["id"] user = User.get_cached_by_id(user.id) diff --git a/test/web/mastodon_api/controllers/instance_controller_test.exs b/test/web/mastodon_api/controllers/instance_controller_test.exs index 2737dcaba..2c61dc5ba 100644 --- a/test/web/mastodon_api/controllers/instance_controller_test.exs +++ b/test/web/mastodon_api/controllers/instance_controller_test.exs @@ -10,7 +10,7 @@ defmodule Pleroma.Web.MastodonAPI.InstanceControllerTest do test "get instance information", %{conn: conn} do conn = get(conn, "/api/v1/instance") - assert result = json_response(conn, 200) + assert result = json_response_and_validate_schema(conn, 200) email = Pleroma.Config.get([:instance, :email]) # Note: not checking for "max_toot_chars" since it's optional @@ -34,6 +34,10 @@ defmodule Pleroma.Web.MastodonAPI.InstanceControllerTest do "banner_upload_limit" => _ } = result + assert result["pleroma"]["metadata"]["features"] + assert result["pleroma"]["metadata"]["federation"] + assert result["pleroma"]["vapid_public_key"] + assert email == from_config_email end @@ -46,13 +50,13 @@ defmodule Pleroma.Web.MastodonAPI.InstanceControllerTest do insert(:user, %{local: false, nickname: "u@peer1.com"}) insert(:user, %{local: false, nickname: "u@peer2.com"}) - {:ok, _} = Pleroma.Web.CommonAPI.post(user, %{"status" => "cofe"}) + {:ok, _} = Pleroma.Web.CommonAPI.post(user, %{status: "cofe"}) Pleroma.Stats.force_update() conn = get(conn, "/api/v1/instance") - assert result = json_response(conn, 200) + assert result = json_response_and_validate_schema(conn, 200) stats = result["stats"] @@ -70,7 +74,7 @@ defmodule Pleroma.Web.MastodonAPI.InstanceControllerTest do conn = get(conn, "/api/v1/instance/peers") - assert result = json_response(conn, 200) + assert result = json_response_and_validate_schema(conn, 200) assert ["peer1.com", "peer2.com"] == Enum.sort(result) end diff --git a/test/web/mastodon_api/controllers/list_controller_test.exs b/test/web/mastodon_api/controllers/list_controller_test.exs index c9c4cbb49..57a9ef4a4 100644 --- a/test/web/mastodon_api/controllers/list_controller_test.exs +++ b/test/web/mastodon_api/controllers/list_controller_test.exs @@ -12,37 +12,44 @@ defmodule Pleroma.Web.MastodonAPI.ListControllerTest do test "creating a list" do %{conn: conn} = oauth_access(["write:lists"]) - conn = post(conn, "/api/v1/lists", %{"title" => "cuties"}) - - assert %{"title" => title} = json_response(conn, 200) - assert title == "cuties" + assert %{"title" => "cuties"} = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/lists", %{"title" => "cuties"}) + |> json_response_and_validate_schema(:ok) end test "renders error for invalid params" do %{conn: conn} = oauth_access(["write:lists"]) - conn = post(conn, "/api/v1/lists", %{"title" => nil}) + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/lists", %{"title" => nil}) - assert %{"error" => "can't be blank"} == json_response(conn, :unprocessable_entity) + assert %{"error" => "title - null value where string expected."} = + json_response_and_validate_schema(conn, 400) end test "listing a user's lists" do %{conn: conn} = oauth_access(["read:lists", "write:lists"]) conn + |> put_req_header("content-type", "application/json") |> post("/api/v1/lists", %{"title" => "cuties"}) - |> json_response(:ok) + |> json_response_and_validate_schema(:ok) conn + |> put_req_header("content-type", "application/json") |> post("/api/v1/lists", %{"title" => "cofe"}) - |> json_response(:ok) + |> json_response_and_validate_schema(:ok) conn = get(conn, "/api/v1/lists") assert [ %{"id" => _, "title" => "cofe"}, %{"id" => _, "title" => "cuties"} - ] = json_response(conn, :ok) + ] = json_response_and_validate_schema(conn, :ok) end test "adding users to a list" do @@ -50,9 +57,12 @@ defmodule Pleroma.Web.MastodonAPI.ListControllerTest do other_user = insert(:user) {:ok, list} = Pleroma.List.create("name", user) - conn = post(conn, "/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]}) + assert %{} == + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]}) + |> json_response_and_validate_schema(:ok) - assert %{} == json_response(conn, 200) %Pleroma.List{following: following} = Pleroma.List.get(list.id, user) assert following == [other_user.follower_address] end @@ -65,9 +75,12 @@ defmodule Pleroma.Web.MastodonAPI.ListControllerTest do {:ok, list} = Pleroma.List.follow(list, other_user) {:ok, list} = Pleroma.List.follow(list, third_user) - conn = delete(conn, "/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]}) + assert %{} == + conn + |> put_req_header("content-type", "application/json") + |> delete("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]}) + |> json_response_and_validate_schema(:ok) - assert %{} == json_response(conn, 200) %Pleroma.List{following: following} = Pleroma.List.get(list.id, user) assert following == [third_user.follower_address] end @@ -83,7 +96,7 @@ defmodule Pleroma.Web.MastodonAPI.ListControllerTest do |> assign(:user, user) |> get("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]}) - assert [%{"id" => id}] = json_response(conn, 200) + assert [%{"id" => id}] = json_response_and_validate_schema(conn, 200) assert id == to_string(other_user.id) end @@ -96,7 +109,7 @@ defmodule Pleroma.Web.MastodonAPI.ListControllerTest do |> assign(:user, user) |> get("/api/v1/lists/#{list.id}") - assert %{"id" => id} = json_response(conn, 200) + assert %{"id" => id} = json_response_and_validate_schema(conn, 200) assert id == to_string(list.id) end @@ -105,17 +118,18 @@ defmodule Pleroma.Web.MastodonAPI.ListControllerTest do conn = get(conn, "/api/v1/lists/666") - assert %{"error" => "List not found"} = json_response(conn, :not_found) + assert %{"error" => "List not found"} = json_response_and_validate_schema(conn, :not_found) end test "renaming a list" do %{user: user, conn: conn} = oauth_access(["write:lists"]) {:ok, list} = Pleroma.List.create("name", user) - conn = put(conn, "/api/v1/lists/#{list.id}", %{"title" => "newname"}) - - assert %{"title" => name} = json_response(conn, 200) - assert name == "newname" + assert %{"title" => "newname"} = + conn + |> put_req_header("content-type", "application/json") + |> put("/api/v1/lists/#{list.id}", %{"title" => "newname"}) + |> json_response_and_validate_schema(:ok) end test "validates title when renaming a list" do @@ -125,9 +139,11 @@ defmodule Pleroma.Web.MastodonAPI.ListControllerTest do conn = conn |> assign(:user, user) + |> put_req_header("content-type", "application/json") |> put("/api/v1/lists/#{list.id}", %{"title" => " "}) - assert %{"error" => "can't be blank"} == json_response(conn, :unprocessable_entity) + assert %{"error" => "can't be blank"} == + json_response_and_validate_schema(conn, :unprocessable_entity) end test "deleting a list" do @@ -136,7 +152,7 @@ defmodule Pleroma.Web.MastodonAPI.ListControllerTest do conn = delete(conn, "/api/v1/lists/#{list.id}") - assert %{} = json_response(conn, 200) + assert %{} = json_response_and_validate_schema(conn, 200) assert is_nil(Repo.get(Pleroma.List, list.id)) end end diff --git a/test/web/mastodon_api/controllers/marker_controller_test.exs b/test/web/mastodon_api/controllers/marker_controller_test.exs index 919f295bd..6dd40fb4a 100644 --- a/test/web/mastodon_api/controllers/marker_controller_test.exs +++ b/test/web/mastodon_api/controllers/marker_controller_test.exs @@ -11,6 +11,7 @@ defmodule Pleroma.Web.MastodonAPI.MarkerControllerTest do test "gets markers with correct scopes", %{conn: conn} do user = insert(:user) token = insert(:oauth_token, user: user, scopes: ["read:statuses"]) + insert_list(7, :notification, user: user) {:ok, %{"notifications" => marker}} = Pleroma.Marker.upsert( @@ -22,14 +23,15 @@ defmodule Pleroma.Web.MastodonAPI.MarkerControllerTest do conn |> assign(:user, user) |> assign(:token, token) - |> get("/api/v1/markers", %{timeline: ["notifications"]}) - |> json_response(200) + |> get("/api/v1/markers?timeline[]=notifications") + |> json_response_and_validate_schema(200) assert response == %{ "notifications" => %{ "last_read_id" => "69420", "updated_at" => NaiveDateTime.to_iso8601(marker.updated_at), - "version" => 0 + "version" => 0, + "pleroma" => %{"unread_count" => 7} } } end @@ -45,7 +47,7 @@ defmodule Pleroma.Web.MastodonAPI.MarkerControllerTest do |> assign(:user, user) |> assign(:token, token) |> get("/api/v1/markers", %{timeline: ["notifications"]}) - |> json_response(403) + |> json_response_and_validate_schema(403) assert response == %{"error" => "Insufficient permissions: read:statuses."} end @@ -60,17 +62,19 @@ defmodule Pleroma.Web.MastodonAPI.MarkerControllerTest do conn |> assign(:user, user) |> assign(:token, token) + |> put_req_header("content-type", "application/json") |> post("/api/v1/markers", %{ home: %{last_read_id: "777"}, notifications: %{"last_read_id" => "69420"} }) - |> json_response(200) + |> json_response_and_validate_schema(200) assert %{ "notifications" => %{ "last_read_id" => "69420", "updated_at" => _, - "version" => 0 + "version" => 0, + "pleroma" => %{"unread_count" => 0} } } = response end @@ -89,17 +93,19 @@ defmodule Pleroma.Web.MastodonAPI.MarkerControllerTest do conn |> assign(:user, user) |> assign(:token, token) + |> put_req_header("content-type", "application/json") |> post("/api/v1/markers", %{ home: %{last_read_id: "777"}, notifications: %{"last_read_id" => "69888"} }) - |> json_response(200) + |> json_response_and_validate_schema(200) assert response == %{ "notifications" => %{ "last_read_id" => "69888", "updated_at" => NaiveDateTime.to_iso8601(marker.updated_at), - "version" => 0 + "version" => 0, + "pleroma" => %{"unread_count" => 0} } } end @@ -112,11 +118,12 @@ defmodule Pleroma.Web.MastodonAPI.MarkerControllerTest do conn |> assign(:user, user) |> assign(:token, token) + |> put_req_header("content-type", "application/json") |> post("/api/v1/markers", %{ home: %{last_read_id: "777"}, notifications: %{"last_read_id" => "69420"} }) - |> json_response(403) + |> json_response_and_validate_schema(403) assert response == %{"error" => "Insufficient permissions: write:statuses."} end diff --git a/test/web/mastodon_api/controllers/notification_controller_test.exs b/test/web/mastodon_api/controllers/notification_controller_test.exs index 7a0011646..d9356a844 100644 --- a/test/web/mastodon_api/controllers/notification_controller_test.exs +++ b/test/web/mastodon_api/controllers/notification_controller_test.exs @@ -12,11 +12,31 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do import Pleroma.Factory + test "does NOT render account/pleroma/relationship if this is disabled by default" do + clear_config([:extensions, :output_relationships_in_statuses_by_default], false) + + %{user: user, conn: conn} = oauth_access(["read:notifications"]) + other_user = insert(:user) + + {:ok, activity} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"}) + {:ok, [_notification]} = Notification.create_notifications(activity) + + response = + conn + |> assign(:user, user) + |> get("/api/v1/notifications") + |> json_response_and_validate_schema(200) + + assert Enum.all?(response, fn n -> + get_in(n, ["account", "pleroma", "relationship"]) == %{} + end) + end + test "list of notifications" do %{user: user, conn: conn} = oauth_access(["read:notifications"]) other_user = insert(:user) - {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"}) + {:ok, activity} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"}) {:ok, [_notification]} = Notification.create_notifications(activity) @@ -26,11 +46,13 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do |> get("/api/v1/notifications") expected_response = - "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{ + "hi <span class=\"h-card\"><a class=\"u-url mention\" data-user=\"#{user.id}\" href=\"#{ user.ap_id }\" rel=\"ugc\">@<span>#{user.nickname}</span></a></span>" - assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200) + assert [%{"status" => %{"content" => response}} | _rest] = + json_response_and_validate_schema(conn, 200) + assert response == expected_response end @@ -38,52 +60,69 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do %{user: user, conn: conn} = oauth_access(["read:notifications"]) other_user = insert(:user) - {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"}) + {:ok, activity} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"}) {:ok, [notification]} = Notification.create_notifications(activity) conn = get(conn, "/api/v1/notifications/#{notification.id}") expected_response = - "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{ + "hi <span class=\"h-card\"><a class=\"u-url mention\" data-user=\"#{user.id}\" href=\"#{ user.ap_id }\" rel=\"ugc\">@<span>#{user.nickname}</span></a></span>" - assert %{"status" => %{"content" => response}} = json_response(conn, 200) + assert %{"status" => %{"content" => response}} = json_response_and_validate_schema(conn, 200) assert response == expected_response end + test "dismissing a single notification (deprecated endpoint)" do + %{user: user, conn: conn} = oauth_access(["write:notifications"]) + other_user = insert(:user) + + {:ok, activity} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"}) + + {:ok, [notification]} = Notification.create_notifications(activity) + + conn = + conn + |> assign(:user, user) + |> put_req_header("content-type", "application/json") + |> post("/api/v1/notifications/dismiss", %{"id" => to_string(notification.id)}) + + assert %{} = json_response_and_validate_schema(conn, 200) + end + test "dismissing a single notification" do %{user: user, conn: conn} = oauth_access(["write:notifications"]) other_user = insert(:user) - {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"}) + {:ok, activity} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"}) {:ok, [notification]} = Notification.create_notifications(activity) conn = conn |> assign(:user, user) - |> post("/api/v1/notifications/dismiss", %{"id" => notification.id}) + |> post("/api/v1/notifications/#{notification.id}/dismiss") - assert %{} = json_response(conn, 200) + assert %{} = json_response_and_validate_schema(conn, 200) end test "clearing all notifications" do %{user: user, conn: conn} = oauth_access(["write:notifications", "read:notifications"]) other_user = insert(:user) - {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"}) + {:ok, activity} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"}) {:ok, [_notification]} = Notification.create_notifications(activity) ret_conn = post(conn, "/api/v1/notifications/clear") - assert %{} = json_response(ret_conn, 200) + assert %{} = json_response_and_validate_schema(ret_conn, 200) ret_conn = get(conn, "/api/v1/notifications") - assert all = json_response(ret_conn, 200) + assert all = json_response_and_validate_schema(ret_conn, 200) assert all == [] end @@ -91,10 +130,10 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do %{user: user, conn: conn} = oauth_access(["read:notifications"]) other_user = insert(:user) - {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"}) - {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"}) - {:ok, activity3} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"}) - {:ok, activity4} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"}) + {:ok, activity1} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"}) + {:ok, activity2} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"}) + {:ok, activity3} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"}) + {:ok, activity4} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"}) notification1_id = get_notification_id_by_activity(activity1) notification2_id = get_notification_id_by_activity(activity2) @@ -107,7 +146,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do result = conn |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}") - |> json_response(:ok) + |> json_response_and_validate_schema(:ok) assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result @@ -115,7 +154,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do result = conn |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}") - |> json_response(:ok) + |> json_response_and_validate_schema(:ok) assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result @@ -123,7 +162,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do result = conn |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}") - |> json_response(:ok) + |> json_response_and_validate_schema(:ok) assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result end @@ -134,47 +173,39 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do other_user = insert(:user) {:ok, public_activity} = - CommonAPI.post(other_user, %{"status" => "@#{user.nickname}", "visibility" => "public"}) + CommonAPI.post(other_user, %{status: "@#{user.nickname}", visibility: "public"}) {:ok, direct_activity} = - CommonAPI.post(other_user, %{"status" => "@#{user.nickname}", "visibility" => "direct"}) + CommonAPI.post(other_user, %{status: "@#{user.nickname}", visibility: "direct"}) {:ok, unlisted_activity} = - CommonAPI.post(other_user, %{"status" => "@#{user.nickname}", "visibility" => "unlisted"}) + CommonAPI.post(other_user, %{status: "@#{user.nickname}", visibility: "unlisted"}) {:ok, private_activity} = - CommonAPI.post(other_user, %{"status" => "@#{user.nickname}", "visibility" => "private"}) + CommonAPI.post(other_user, %{status: "@#{user.nickname}", visibility: "private"}) - conn_res = - get(conn, "/api/v1/notifications", %{ - exclude_visibilities: ["public", "unlisted", "private"] - }) + query = params_to_query(%{exclude_visibilities: ["public", "unlisted", "private"]}) + conn_res = get(conn, "/api/v1/notifications?" <> query) - assert [%{"status" => %{"id" => id}}] = json_response(conn_res, 200) + assert [%{"status" => %{"id" => id}}] = json_response_and_validate_schema(conn_res, 200) assert id == direct_activity.id - conn_res = - get(conn, "/api/v1/notifications", %{ - exclude_visibilities: ["public", "unlisted", "direct"] - }) + query = params_to_query(%{exclude_visibilities: ["public", "unlisted", "direct"]}) + conn_res = get(conn, "/api/v1/notifications?" <> query) - assert [%{"status" => %{"id" => id}}] = json_response(conn_res, 200) + assert [%{"status" => %{"id" => id}}] = json_response_and_validate_schema(conn_res, 200) assert id == private_activity.id - conn_res = - get(conn, "/api/v1/notifications", %{ - exclude_visibilities: ["public", "private", "direct"] - }) + query = params_to_query(%{exclude_visibilities: ["public", "private", "direct"]}) + conn_res = get(conn, "/api/v1/notifications?" <> query) - assert [%{"status" => %{"id" => id}}] = json_response(conn_res, 200) + assert [%{"status" => %{"id" => id}}] = json_response_and_validate_schema(conn_res, 200) assert id == unlisted_activity.id - conn_res = - get(conn, "/api/v1/notifications", %{ - exclude_visibilities: ["unlisted", "private", "direct"] - }) + query = params_to_query(%{exclude_visibilities: ["unlisted", "private", "direct"]}) + conn_res = get(conn, "/api/v1/notifications?" <> query) - assert [%{"status" => %{"id" => id}}] = json_response(conn_res, 200) + assert [%{"status" => %{"id" => id}}] = json_response_and_validate_schema(conn_res, 200) assert id == public_activity.id end @@ -182,27 +213,25 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do user = insert(:user) %{user: other_user, conn: conn} = oauth_access(["read:notifications"]) - {:ok, public_activity} = - CommonAPI.post(other_user, %{"status" => ".", "visibility" => "public"}) + {:ok, public_activity} = CommonAPI.post(other_user, %{status: ".", visibility: "public"}) {:ok, direct_activity} = - CommonAPI.post(other_user, %{"status" => "@#{user.nickname}", "visibility" => "direct"}) + CommonAPI.post(other_user, %{status: "@#{user.nickname}", visibility: "direct"}) {:ok, unlisted_activity} = - CommonAPI.post(other_user, %{"status" => ".", "visibility" => "unlisted"}) + CommonAPI.post(other_user, %{status: ".", visibility: "unlisted"}) - {:ok, private_activity} = - CommonAPI.post(other_user, %{"status" => ".", "visibility" => "private"}) + {:ok, private_activity} = CommonAPI.post(other_user, %{status: ".", visibility: "private"}) - {:ok, _, _} = CommonAPI.favorite(public_activity.id, user) - {:ok, _, _} = CommonAPI.favorite(direct_activity.id, user) - {:ok, _, _} = CommonAPI.favorite(unlisted_activity.id, user) - {:ok, _, _} = CommonAPI.favorite(private_activity.id, user) + {:ok, _} = CommonAPI.favorite(user, public_activity.id) + {:ok, _} = CommonAPI.favorite(user, direct_activity.id) + {:ok, _} = CommonAPI.favorite(user, unlisted_activity.id) + {:ok, _} = CommonAPI.favorite(user, private_activity.id) activity_ids = conn - |> get("/api/v1/notifications", %{exclude_visibilities: ["direct"]}) - |> json_response(200) + |> get("/api/v1/notifications?exclude_visibilities[]=direct") + |> json_response_and_validate_schema(200) |> Enum.map(& &1["status"]["id"]) assert public_activity.id in activity_ids @@ -212,8 +241,8 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do activity_ids = conn - |> get("/api/v1/notifications", %{exclude_visibilities: ["unlisted"]}) - |> json_response(200) + |> get("/api/v1/notifications?exclude_visibilities[]=unlisted") + |> json_response_and_validate_schema(200) |> Enum.map(& &1["status"]["id"]) assert public_activity.id in activity_ids @@ -223,8 +252,8 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do activity_ids = conn - |> get("/api/v1/notifications", %{exclude_visibilities: ["private"]}) - |> json_response(200) + |> get("/api/v1/notifications?exclude_visibilities[]=private") + |> json_response_and_validate_schema(200) |> Enum.map(& &1["status"]["id"]) assert public_activity.id in activity_ids @@ -234,8 +263,8 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do activity_ids = conn - |> get("/api/v1/notifications", %{exclude_visibilities: ["public"]}) - |> json_response(200) + |> get("/api/v1/notifications?exclude_visibilities[]=public") + |> json_response_and_validate_schema(200) |> Enum.map(& &1["status"]["id"]) refute public_activity.id in activity_ids @@ -248,19 +277,18 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do user = insert(:user) %{user: other_user, conn: conn} = oauth_access(["read:notifications"]) - {:ok, public_activity} = - CommonAPI.post(other_user, %{"status" => ".", "visibility" => "public"}) + {:ok, public_activity} = CommonAPI.post(other_user, %{status: ".", visibility: "public"}) {:ok, unlisted_activity} = - CommonAPI.post(other_user, %{"status" => ".", "visibility" => "unlisted"}) + CommonAPI.post(other_user, %{status: ".", visibility: "unlisted"}) {:ok, _, _} = CommonAPI.repeat(public_activity.id, user) {:ok, _, _} = CommonAPI.repeat(unlisted_activity.id, user) activity_ids = conn - |> get("/api/v1/notifications", %{exclude_visibilities: ["unlisted"]}) - |> json_response(200) + |> get("/api/v1/notifications?exclude_visibilities[]=unlisted") + |> json_response_and_validate_schema(200) |> Enum.map(& &1["status"]["id"]) assert public_activity.id in activity_ids @@ -272,9 +300,9 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do %{user: user, conn: conn} = oauth_access(["read:notifications"]) other_user = insert(:user) - {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"}) - {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"}) - {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user) + {:ok, mention_activity} = CommonAPI.post(other_user, %{status: "hey @#{user.nickname}"}) + {:ok, create_activity} = CommonAPI.post(user, %{status: "hey"}) + {:ok, favorite_activity} = CommonAPI.favorite(other_user, create_activity.id) {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user) {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user) @@ -283,34 +311,36 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do reblog_notification_id = get_notification_id_by_activity(reblog_activity) follow_notification_id = get_notification_id_by_activity(follow_activity) - conn_res = - get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]}) + query = params_to_query(%{exclude_types: ["mention", "favourite", "reblog"]}) + conn_res = get(conn, "/api/v1/notifications?" <> query) - assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200) + assert [%{"id" => ^follow_notification_id}] = json_response_and_validate_schema(conn_res, 200) - conn_res = - get(conn, "/api/v1/notifications", %{exclude_types: ["favourite", "reblog", "follow"]}) + query = params_to_query(%{exclude_types: ["favourite", "reblog", "follow"]}) + conn_res = get(conn, "/api/v1/notifications?" <> query) - assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200) + assert [%{"id" => ^mention_notification_id}] = + json_response_and_validate_schema(conn_res, 200) - conn_res = - get(conn, "/api/v1/notifications", %{exclude_types: ["reblog", "follow", "mention"]}) + query = params_to_query(%{exclude_types: ["reblog", "follow", "mention"]}) + conn_res = get(conn, "/api/v1/notifications?" <> query) - assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200) + assert [%{"id" => ^favorite_notification_id}] = + json_response_and_validate_schema(conn_res, 200) - conn_res = - get(conn, "/api/v1/notifications", %{exclude_types: ["follow", "mention", "favourite"]}) + query = params_to_query(%{exclude_types: ["follow", "mention", "favourite"]}) + conn_res = get(conn, "/api/v1/notifications?" <> query) - assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200) + assert [%{"id" => ^reblog_notification_id}] = json_response_and_validate_schema(conn_res, 200) end test "filters notifications using include_types" do %{user: user, conn: conn} = oauth_access(["read:notifications"]) other_user = insert(:user) - {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"}) - {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"}) - {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user) + {:ok, mention_activity} = CommonAPI.post(other_user, %{status: "hey @#{user.nickname}"}) + {:ok, create_activity} = CommonAPI.post(user, %{status: "hey"}) + {:ok, favorite_activity} = CommonAPI.favorite(other_user, create_activity.id) {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user) {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user) @@ -319,32 +349,34 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do reblog_notification_id = get_notification_id_by_activity(reblog_activity) follow_notification_id = get_notification_id_by_activity(follow_activity) - conn_res = get(conn, "/api/v1/notifications", %{include_types: ["follow"]}) + conn_res = get(conn, "/api/v1/notifications?include_types[]=follow") - assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200) + assert [%{"id" => ^follow_notification_id}] = json_response_and_validate_schema(conn_res, 200) - conn_res = get(conn, "/api/v1/notifications", %{include_types: ["mention"]}) + conn_res = get(conn, "/api/v1/notifications?include_types[]=mention") - assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200) + assert [%{"id" => ^mention_notification_id}] = + json_response_and_validate_schema(conn_res, 200) - conn_res = get(conn, "/api/v1/notifications", %{include_types: ["favourite"]}) + conn_res = get(conn, "/api/v1/notifications?include_types[]=favourite") - assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200) + assert [%{"id" => ^favorite_notification_id}] = + json_response_and_validate_schema(conn_res, 200) - conn_res = get(conn, "/api/v1/notifications", %{include_types: ["reblog"]}) + conn_res = get(conn, "/api/v1/notifications?include_types[]=reblog") - assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200) + assert [%{"id" => ^reblog_notification_id}] = json_response_and_validate_schema(conn_res, 200) - result = conn |> get("/api/v1/notifications") |> json_response(200) + result = conn |> get("/api/v1/notifications") |> json_response_and_validate_schema(200) assert length(result) == 4 + query = params_to_query(%{include_types: ["follow", "mention", "favourite", "reblog"]}) + result = conn - |> get("/api/v1/notifications", %{ - include_types: ["follow", "mention", "favourite", "reblog"] - }) - |> json_response(200) + |> get("/api/v1/notifications?" <> query) + |> json_response_and_validate_schema(200) assert length(result) == 4 end @@ -353,10 +385,10 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do %{user: user, conn: conn} = oauth_access(["read:notifications", "write:notifications"]) other_user = insert(:user) - {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"}) - {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"}) - {:ok, activity3} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"}) - {:ok, activity4} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"}) + {:ok, activity1} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"}) + {:ok, activity2} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"}) + {:ok, activity3} = CommonAPI.post(user, %{status: "hi @#{other_user.nickname}"}) + {:ok, activity4} = CommonAPI.post(user, %{status: "hi @#{other_user.nickname}"}) notification1_id = get_notification_id_by_activity(activity1) notification2_id = get_notification_id_by_activity(activity2) @@ -366,7 +398,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do result = conn |> get("/api/v1/notifications") - |> json_response(:ok) + |> json_response_and_validate_schema(:ok) assert [%{"id" => ^notification2_id}, %{"id" => ^notification1_id}] = result @@ -378,22 +410,19 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do result = conn2 |> get("/api/v1/notifications") - |> json_response(:ok) + |> json_response_and_validate_schema(:ok) assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result - conn_destroy = - conn - |> delete("/api/v1/notifications/destroy_multiple", %{ - "ids" => [notification1_id, notification2_id] - }) + query = params_to_query(%{ids: [notification1_id, notification2_id]}) + conn_destroy = delete(conn, "/api/v1/notifications/destroy_multiple?" <> query) - assert json_response(conn_destroy, 200) == %{} + assert json_response_and_validate_schema(conn_destroy, 200) == %{} result = conn2 |> get("/api/v1/notifications") - |> json_response(:ok) + |> json_response_and_validate_schema(:ok) assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result end @@ -403,17 +432,17 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do user2 = insert(:user) {:ok, _, _, _} = CommonAPI.follow(user, user2) - {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"}) + {:ok, _} = CommonAPI.post(user2, %{status: "hey @#{user.nickname}"}) ret_conn = get(conn, "/api/v1/notifications") - assert length(json_response(ret_conn, 200)) == 1 + assert length(json_response_and_validate_schema(ret_conn, 200)) == 1 {:ok, _user_relationships} = User.mute(user, user2) conn = get(conn, "/api/v1/notifications") - assert json_response(conn, 200) == [] + assert json_response_and_validate_schema(conn, 200) == [] end test "see notifications after muting user without notifications" do @@ -421,17 +450,17 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do user2 = insert(:user) {:ok, _, _, _} = CommonAPI.follow(user, user2) - {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"}) + {:ok, _} = CommonAPI.post(user2, %{status: "hey @#{user.nickname}"}) ret_conn = get(conn, "/api/v1/notifications") - assert length(json_response(ret_conn, 200)) == 1 + assert length(json_response_and_validate_schema(ret_conn, 200)) == 1 {:ok, _user_relationships} = User.mute(user, user2, false) conn = get(conn, "/api/v1/notifications") - assert length(json_response(conn, 200)) == 1 + assert length(json_response_and_validate_schema(conn, 200)) == 1 end test "see notifications after muting user with notifications and with_muted parameter" do @@ -439,31 +468,44 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do user2 = insert(:user) {:ok, _, _, _} = CommonAPI.follow(user, user2) - {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"}) + {:ok, _} = CommonAPI.post(user2, %{status: "hey @#{user.nickname}"}) ret_conn = get(conn, "/api/v1/notifications") - assert length(json_response(ret_conn, 200)) == 1 + assert length(json_response_and_validate_schema(ret_conn, 200)) == 1 {:ok, _user_relationships} = User.mute(user, user2) - conn = get(conn, "/api/v1/notifications", %{"with_muted" => "true"}) + conn = get(conn, "/api/v1/notifications?with_muted=true") - assert length(json_response(conn, 200)) == 1 + assert length(json_response_and_validate_schema(conn, 200)) == 1 end + @tag capture_log: true test "see move notifications" do old_user = insert(:user) new_user = insert(:user, also_known_as: [old_user.ap_id]) %{user: follower, conn: conn} = oauth_access(["read:notifications"]) + old_user_url = old_user.ap_id + + body = + File.read!("test/fixtures/users_mock/localhost.json") + |> String.replace("{{nickname}}", old_user.nickname) + |> Jason.encode!() + + Tesla.Mock.mock(fn + %{method: :get, url: ^old_user_url} -> + %Tesla.Env{status: 200, body: body} + end) + User.follow(follower, old_user) Pleroma.Web.ActivityPub.ActivityPub.move(old_user, new_user) Pleroma.Tests.ObanHelpers.perform_all() conn = get(conn, "/api/v1/notifications") - assert length(json_response(conn, 200)) == 1 + assert length(json_response_and_validate_schema(conn, 200)) == 1 end describe "link headers" do @@ -473,14 +515,14 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do {:ok, activity1} = CommonAPI.post(other_user, %{ - "status" => "hi @#{user.nickname}", - "visibility" => "public" + status: "hi @#{user.nickname}", + visibility: "public" }) {:ok, activity2} = CommonAPI.post(other_user, %{ - "status" => "hi @#{user.nickname}", - "visibility" => "public" + status: "hi @#{user.nickname}", + visibility: "public" }) notification1 = Repo.get_by(Notification, activity_id: activity1.id) @@ -489,10 +531,10 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do conn = conn |> assign(:user, user) - |> get("/api/v1/notifications", %{media_only: true}) + |> get("/api/v1/notifications?limit=5") assert [link_header] = get_resp_header(conn, "link") - assert link_header =~ ~r/media_only=true/ + assert link_header =~ ~r/limit=5/ assert link_header =~ ~r/min_id=#{notification2.id}/ assert link_header =~ ~r/max_id=#{notification1.id}/ end @@ -505,20 +547,20 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do %{id: account_id} = other_user1 = insert(:user) other_user2 = insert(:user) - {:ok, _activity} = CommonAPI.post(other_user1, %{"status" => "hi @#{user.nickname}"}) - {:ok, _activity} = CommonAPI.post(other_user2, %{"status" => "bye @#{user.nickname}"}) + {:ok, _activity} = CommonAPI.post(other_user1, %{status: "hi @#{user.nickname}"}) + {:ok, _activity} = CommonAPI.post(other_user2, %{status: "bye @#{user.nickname}"}) assert [%{"account" => %{"id" => ^account_id}}] = conn |> assign(:user, user) - |> get("/api/v1/notifications", %{account_id: account_id}) - |> json_response(200) + |> get("/api/v1/notifications?account_id=#{account_id}") + |> json_response_and_validate_schema(200) assert %{"error" => "Account is not found"} = conn |> assign(:user, user) - |> get("/api/v1/notifications", %{account_id: "cofe"}) - |> json_response(404) + |> get("/api/v1/notifications?account_id=cofe") + |> json_response_and_validate_schema(404) end end @@ -528,4 +570,11 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do |> Map.get(:id) |> to_string() end + + defp params_to_query(%{} = params) do + Enum.map_join(params, "&", fn + {k, v} when is_list(v) -> Enum.map_join(v, "&", &"#{k}[]=#{&1}") + {k, v} -> k <> "=" <> v + end) + end end diff --git a/test/web/mastodon_api/controllers/poll_controller_test.exs b/test/web/mastodon_api/controllers/poll_controller_test.exs index 88b13a25a..f41de6448 100644 --- a/test/web/mastodon_api/controllers/poll_controller_test.exs +++ b/test/web/mastodon_api/controllers/poll_controller_test.exs @@ -16,15 +16,15 @@ defmodule Pleroma.Web.MastodonAPI.PollControllerTest do test "returns poll entity for object id", %{user: user, conn: conn} do {:ok, activity} = CommonAPI.post(user, %{ - "status" => "Pleroma does", - "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20} + status: "Pleroma does", + poll: %{options: ["what Mastodon't", "n't what Mastodoes"], expires_in: 20} }) object = Object.normalize(activity) conn = get(conn, "/api/v1/polls/#{object.id}") - response = json_response(conn, 200) + response = json_response_and_validate_schema(conn, 200) id = to_string(object.id) assert %{"id" => ^id, "expired" => false, "multiple" => false} = response end @@ -34,16 +34,16 @@ defmodule Pleroma.Web.MastodonAPI.PollControllerTest do {:ok, activity} = CommonAPI.post(other_user, %{ - "status" => "Pleroma does", - "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}, - "visibility" => "private" + status: "Pleroma does", + poll: %{options: ["what Mastodon't", "n't what Mastodoes"], expires_in: 20}, + visibility: "private" }) object = Object.normalize(activity) conn = get(conn, "/api/v1/polls/#{object.id}") - assert json_response(conn, 404) + assert json_response_and_validate_schema(conn, 404) end end @@ -55,19 +55,22 @@ defmodule Pleroma.Web.MastodonAPI.PollControllerTest do {:ok, activity} = CommonAPI.post(other_user, %{ - "status" => "A very delicious sandwich", - "poll" => %{ - "options" => ["Lettuce", "Grilled Bacon", "Tomato"], - "expires_in" => 20, - "multiple" => true + status: "A very delicious sandwich", + poll: %{ + options: ["Lettuce", "Grilled Bacon", "Tomato"], + expires_in: 20, + multiple: true } }) object = Object.normalize(activity) - conn = post(conn, "/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]}) + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]}) - assert json_response(conn, 200) + assert json_response_and_validate_schema(conn, 200) object = Object.get_by_id(object.id) assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} -> @@ -78,15 +81,16 @@ defmodule Pleroma.Web.MastodonAPI.PollControllerTest do test "author can't vote", %{user: user, conn: conn} do {:ok, activity} = CommonAPI.post(user, %{ - "status" => "Am I cute?", - "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20} + status: "Am I cute?", + poll: %{options: ["Yes", "No"], expires_in: 20} }) object = Object.normalize(activity) assert conn + |> put_req_header("content-type", "application/json") |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]}) - |> json_response(422) == %{"error" => "Poll's author can't vote"} + |> json_response_and_validate_schema(422) == %{"error" => "Poll's author can't vote"} object = Object.get_by_id(object.id) @@ -98,15 +102,16 @@ defmodule Pleroma.Web.MastodonAPI.PollControllerTest do {:ok, activity} = CommonAPI.post(other_user, %{ - "status" => "The glass is", - "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20} + status: "The glass is", + poll: %{options: ["half empty", "half full"], expires_in: 20} }) object = Object.normalize(activity) assert conn + |> put_req_header("content-type", "application/json") |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]}) - |> json_response(422) == %{"error" => "Too many choices"} + |> json_response_and_validate_schema(422) == %{"error" => "Too many choices"} object = Object.get_by_id(object.id) @@ -120,21 +125,27 @@ defmodule Pleroma.Web.MastodonAPI.PollControllerTest do {:ok, activity} = CommonAPI.post(other_user, %{ - "status" => "Am I cute?", - "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20} + status: "Am I cute?", + poll: %{options: ["Yes", "No"], expires_in: 20} }) object = Object.normalize(activity) - conn = post(conn, "/api/v1/polls/#{object.id}/votes", %{"choices" => [2]}) + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]}) - assert json_response(conn, 422) == %{"error" => "Invalid indices"} + assert json_response_and_validate_schema(conn, 422) == %{"error" => "Invalid indices"} end test "returns 404 error when object is not exist", %{conn: conn} do - conn = post(conn, "/api/v1/polls/1/votes", %{"choices" => [0]}) + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/polls/1/votes", %{"choices" => [0]}) - assert json_response(conn, 404) == %{"error" => "Record not found"} + assert json_response_and_validate_schema(conn, 404) == %{"error" => "Record not found"} end test "returns 404 when poll is private and not available for user", %{conn: conn} do @@ -142,16 +153,19 @@ defmodule Pleroma.Web.MastodonAPI.PollControllerTest do {:ok, activity} = CommonAPI.post(other_user, %{ - "status" => "Am I cute?", - "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}, - "visibility" => "private" + status: "Am I cute?", + poll: %{options: ["Yes", "No"], expires_in: 20}, + visibility: "private" }) object = Object.normalize(activity) - conn = post(conn, "/api/v1/polls/#{object.id}/votes", %{"choices" => [0]}) + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]}) - assert json_response(conn, 404) == %{"error" => "Record not found"} + assert json_response_and_validate_schema(conn, 404) == %{"error" => "Record not found"} end end end diff --git a/test/web/mastodon_api/controllers/report_controller_test.exs b/test/web/mastodon_api/controllers/report_controller_test.exs index 34ec8119e..6636cff96 100644 --- a/test/web/mastodon_api/controllers/report_controller_test.exs +++ b/test/web/mastodon_api/controllers/report_controller_test.exs @@ -14,7 +14,7 @@ defmodule Pleroma.Web.MastodonAPI.ReportControllerTest do setup do target_user = insert(:user) - {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"}) + {:ok, activity} = CommonAPI.post(target_user, %{status: "foobar"}) [target_user: target_user, activity: activity] end @@ -22,8 +22,9 @@ defmodule Pleroma.Web.MastodonAPI.ReportControllerTest do test "submit a basic report", %{conn: conn, target_user: target_user} do assert %{"action_taken" => false, "id" => _} = conn + |> put_req_header("content-type", "application/json") |> post("/api/v1/reports", %{"account_id" => target_user.id}) - |> json_response(200) + |> json_response_and_validate_schema(200) end test "submit a report with statuses and comment", %{ @@ -33,23 +34,25 @@ defmodule Pleroma.Web.MastodonAPI.ReportControllerTest do } do assert %{"action_taken" => false, "id" => _} = conn + |> put_req_header("content-type", "application/json") |> post("/api/v1/reports", %{ "account_id" => target_user.id, "status_ids" => [activity.id], "comment" => "bad status!", "forward" => "false" }) - |> json_response(200) + |> json_response_and_validate_schema(200) end test "account_id is required", %{ conn: conn, activity: activity } do - assert %{"error" => "Valid `account_id` required"} = + assert %{"error" => "Missing field: account_id."} = conn + |> put_req_header("content-type", "application/json") |> post("/api/v1/reports", %{"status_ids" => [activity.id]}) - |> json_response(400) + |> json_response_and_validate_schema(400) end test "comment must be up to the size specified in the config", %{ @@ -63,17 +66,21 @@ defmodule Pleroma.Web.MastodonAPI.ReportControllerTest do assert ^error = conn + |> put_req_header("content-type", "application/json") |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment}) - |> json_response(400) + |> json_response_and_validate_schema(400) end test "returns error when account is not exist", %{ conn: conn, activity: activity } do - conn = post(conn, "/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"}) + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"}) - assert json_response(conn, 400) == %{"error" => "Account not found"} + assert json_response_and_validate_schema(conn, 400) == %{"error" => "Account not found"} end test "doesn't fail if an admin has no email", %{conn: conn, target_user: target_user} do @@ -81,7 +88,8 @@ defmodule Pleroma.Web.MastodonAPI.ReportControllerTest do assert %{"action_taken" => false, "id" => _} = conn + |> put_req_header("content-type", "application/json") |> post("/api/v1/reports", %{"account_id" => target_user.id}) - |> json_response(200) + |> json_response_and_validate_schema(200) end end diff --git a/test/web/mastodon_api/controllers/scheduled_activity_controller_test.exs b/test/web/mastodon_api/controllers/scheduled_activity_controller_test.exs index f86274d57..1ff871c89 100644 --- a/test/web/mastodon_api/controllers/scheduled_activity_controller_test.exs +++ b/test/web/mastodon_api/controllers/scheduled_activity_controller_test.exs @@ -24,19 +24,19 @@ defmodule Pleroma.Web.MastodonAPI.ScheduledActivityControllerTest do # min_id conn_res = get(conn, "/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}") - result = json_response(conn_res, 200) + result = json_response_and_validate_schema(conn_res, 200) assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result # since_id conn_res = get(conn, "/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}") - result = json_response(conn_res, 200) + result = json_response_and_validate_schema(conn_res, 200) assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result # max_id conn_res = get(conn, "/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}") - result = json_response(conn_res, 200) + result = json_response_and_validate_schema(conn_res, 200) assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result end @@ -46,12 +46,12 @@ defmodule Pleroma.Web.MastodonAPI.ScheduledActivityControllerTest do res_conn = get(conn, "/api/v1/scheduled_statuses/#{scheduled_activity.id}") - assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200) + assert %{"id" => scheduled_activity_id} = json_response_and_validate_schema(res_conn, 200) assert scheduled_activity_id == scheduled_activity.id |> to_string() res_conn = get(conn, "/api/v1/scheduled_statuses/404") - assert %{"error" => "Record not found"} = json_response(res_conn, 404) + assert %{"error" => "Record not found"} = json_response_and_validate_schema(res_conn, 404) end test "updates a scheduled activity" do @@ -74,22 +74,32 @@ defmodule Pleroma.Web.MastodonAPI.ScheduledActivityControllerTest do assert job.args == %{"activity_id" => scheduled_activity.id} assert DateTime.truncate(job.scheduled_at, :second) == to_datetime(scheduled_at) - new_scheduled_at = Timex.shift(NaiveDateTime.utc_now(), minutes: 120) + new_scheduled_at = + NaiveDateTime.utc_now() + |> Timex.shift(minutes: 120) + |> Timex.format!("%Y-%m-%dT%H:%M:%S.%fZ", :strftime) res_conn = - put(conn, "/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{ + conn + |> put_req_header("content-type", "application/json") + |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{ scheduled_at: new_scheduled_at }) - assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200) + assert %{"scheduled_at" => expected_scheduled_at} = + json_response_and_validate_schema(res_conn, 200) + assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at) job = refresh_record(job) assert DateTime.truncate(job.scheduled_at, :second) == to_datetime(new_scheduled_at) - res_conn = put(conn, "/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at}) + res_conn = + conn + |> put_req_header("content-type", "application/json") + |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at}) - assert %{"error" => "Record not found"} = json_response(res_conn, 404) + assert %{"error" => "Record not found"} = json_response_and_validate_schema(res_conn, 404) end test "deletes a scheduled activity" do @@ -115,7 +125,7 @@ defmodule Pleroma.Web.MastodonAPI.ScheduledActivityControllerTest do |> assign(:user, user) |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}") - assert %{} = json_response(res_conn, 200) + assert %{} = json_response_and_validate_schema(res_conn, 200) refute Repo.get(ScheduledActivity, scheduled_activity.id) refute Repo.get(Oban.Job, job.id) @@ -124,6 +134,6 @@ defmodule Pleroma.Web.MastodonAPI.ScheduledActivityControllerTest do |> assign(:user, user) |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}") - assert %{"error" => "Record not found"} = json_response(res_conn, 404) + assert %{"error" => "Record not found"} = json_response_and_validate_schema(res_conn, 404) end end diff --git a/test/web/mastodon_api/controllers/search_controller_test.exs b/test/web/mastodon_api/controllers/search_controller_test.exs index 11133ff66..7d0cafccc 100644 --- a/test/web/mastodon_api/controllers/search_controller_test.exs +++ b/test/web/mastodon_api/controllers/search_controller_test.exs @@ -13,7 +13,7 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do import Tesla.Mock import Mock - setup do + setup_all do mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) :ok end @@ -27,8 +27,8 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do capture_log(fn -> results = conn - |> get("/api/v2/search", %{"q" => "2hu"}) - |> json_response(200) + |> get("/api/v2/search?q=2hu") + |> json_response_and_validate_schema(200) assert results["accounts"] == [] assert results["statuses"] == [] @@ -42,20 +42,20 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do user_two = insert(:user, %{nickname: "shp@shitposter.club"}) user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"}) - {:ok, activity} = CommonAPI.post(user, %{"status" => "This is about 2hu private 天子"}) + {:ok, activity} = CommonAPI.post(user, %{status: "This is about 2hu private 天子"}) {:ok, _activity} = CommonAPI.post(user, %{ - "status" => "This is about 2hu, but private", - "visibility" => "private" + status: "This is about 2hu, but private", + visibility: "private" }) - {:ok, _} = CommonAPI.post(user_two, %{"status" => "This isn't"}) + {:ok, _} = CommonAPI.post(user_two, %{status: "This isn't"}) results = conn - |> get("/api/v2/search", %{"q" => "2hu #private"}) - |> json_response(200) + |> get("/api/v2/search?#{URI.encode_query(%{q: "2hu #private"})}") + |> json_response_and_validate_schema(200) [account | _] = results["accounts"] assert account["id"] == to_string(user_three.id) @@ -68,8 +68,8 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do assert status["id"] == to_string(activity.id) results = - get(conn, "/api/v2/search", %{"q" => "天子"}) - |> json_response(200) + get(conn, "/api/v2/search?q=天子") + |> json_response_and_validate_schema(200) [status] = results["statuses"] assert status["id"] == to_string(activity.id) @@ -80,17 +80,17 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do user_smith = insert(:user, %{nickname: "Agent", name: "I love 2hu"}) user_neo = insert(:user, %{nickname: "Agent Neo", name: "Agent"}) - {:ok, act1} = CommonAPI.post(user, %{"status" => "This is about 2hu private 天子"}) - {:ok, act2} = CommonAPI.post(user_smith, %{"status" => "Agent Smith"}) - {:ok, act3} = CommonAPI.post(user_neo, %{"status" => "Agent Smith"}) + {:ok, act1} = CommonAPI.post(user, %{status: "This is about 2hu private 天子"}) + {:ok, act2} = CommonAPI.post(user_smith, %{status: "Agent Smith"}) + {:ok, act3} = CommonAPI.post(user_neo, %{status: "Agent Smith"}) Pleroma.User.block(user, user_smith) results = conn |> assign(:user, user) |> assign(:token, insert(:oauth_token, user: user, scopes: ["read"])) - |> get("/api/v2/search", %{"q" => "Agent"}) - |> json_response(200) + |> get("/api/v2/search?q=Agent") + |> json_response_and_validate_schema(200) status_ids = Enum.map(results["statuses"], fn g -> g["id"] end) @@ -107,8 +107,8 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do results = conn - |> get("/api/v1/accounts/search", %{"q" => "shp"}) - |> json_response(200) + |> get("/api/v1/accounts/search?q=shp") + |> json_response_and_validate_schema(200) result_ids = for result <- results, do: result["acct"] @@ -117,8 +117,8 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do results = conn - |> get("/api/v1/accounts/search", %{"q" => "2hu"}) - |> json_response(200) + |> get("/api/v1/accounts/search?q=2hu") + |> json_response_and_validate_schema(200) result_ids = for result <- results, do: result["acct"] @@ -130,8 +130,8 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do results = conn - |> get("/api/v1/accounts/search", %{"q" => "shp@shitposter.club xxx "}) - |> json_response(200) + |> get("/api/v1/accounts/search?q=shp@shitposter.club xxx") + |> json_response_and_validate_schema(200) assert length(results) == 1 end @@ -146,8 +146,8 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do capture_log(fn -> results = conn - |> get("/api/v1/search", %{"q" => "2hu"}) - |> json_response(200) + |> get("/api/v1/search?q=2hu") + |> json_response_and_validate_schema(200) assert results["accounts"] == [] assert results["statuses"] == [] @@ -161,20 +161,20 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do user_two = insert(:user, %{nickname: "shp@shitposter.club"}) user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"}) - {:ok, activity} = CommonAPI.post(user, %{"status" => "This is about 2hu"}) + {:ok, activity} = CommonAPI.post(user, %{status: "This is about 2hu"}) {:ok, _activity} = CommonAPI.post(user, %{ - "status" => "This is about 2hu, but private", - "visibility" => "private" + status: "This is about 2hu, but private", + visibility: "private" }) - {:ok, _} = CommonAPI.post(user_two, %{"status" => "This isn't"}) + {:ok, _} = CommonAPI.post(user_two, %{status: "This isn't"}) results = conn - |> get("/api/v1/search", %{"q" => "2hu"}) - |> json_response(200) + |> get("/api/v1/search?q=2hu") + |> json_response_and_validate_schema(200) [account | _] = results["accounts"] assert account["id"] == to_string(user_three.id) @@ -189,13 +189,13 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do capture_log(fn -> {:ok, %{id: activity_id}} = CommonAPI.post(insert(:user), %{ - "status" => "check out https://shitposter.club/notice/2827873" + status: "check out https://shitposter.club/notice/2827873" }) results = conn - |> get("/api/v1/search", %{"q" => "https://shitposter.club/notice/2827873"}) - |> json_response(200) + |> get("/api/v1/search?q=https://shitposter.club/notice/2827873") + |> json_response_and_validate_schema(200) [status, %{"id" => ^activity_id}] = results["statuses"] @@ -207,15 +207,17 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do test "search doesn't show statuses that it shouldn't", %{conn: conn} do {:ok, activity} = CommonAPI.post(insert(:user), %{ - "status" => "This is about 2hu, but private", - "visibility" => "private" + status: "This is about 2hu, but private", + visibility: "private" }) capture_log(fn -> + q = Object.normalize(activity).data["id"] + results = conn - |> get("/api/v1/search", %{"q" => Object.normalize(activity).data["id"]}) - |> json_response(200) + |> get("/api/v1/search?q=#{q}") + |> json_response_and_validate_schema(200) [] = results["statuses"] end) @@ -228,8 +230,8 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do conn |> assign(:user, user) |> assign(:token, insert(:oauth_token, user: user, scopes: ["read"])) - |> get("/api/v1/search", %{"q" => "mike@osada.macgirvin.com", "resolve" => "true"}) - |> json_response(200) + |> get("/api/v1/search?q=mike@osada.macgirvin.com&resolve=true") + |> json_response_and_validate_schema(200) [account] = results["accounts"] assert account["acct"] == "mike@osada.macgirvin.com" @@ -238,8 +240,8 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do test "search doesn't fetch remote accounts if resolve is false", %{conn: conn} do results = conn - |> get("/api/v1/search", %{"q" => "mike@osada.macgirvin.com", "resolve" => "false"}) - |> json_response(200) + |> get("/api/v1/search?q=mike@osada.macgirvin.com&resolve=false") + |> json_response_and_validate_schema(200) assert [] == results["accounts"] end @@ -249,21 +251,21 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do _user_two = insert(:user, %{nickname: "shp@shitposter.club"}) _user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"}) - {:ok, _activity1} = CommonAPI.post(user, %{"status" => "This is about 2hu"}) - {:ok, _activity2} = CommonAPI.post(user, %{"status" => "This is also about 2hu"}) + {:ok, _activity1} = CommonAPI.post(user, %{status: "This is about 2hu"}) + {:ok, _activity2} = CommonAPI.post(user, %{status: "This is also about 2hu"}) result = conn - |> get("/api/v1/search", %{"q" => "2hu", "limit" => 1}) + |> get("/api/v1/search?q=2hu&limit=1") - assert results = json_response(result, 200) + assert results = json_response_and_validate_schema(result, 200) assert [%{"id" => activity_id1}] = results["statuses"] assert [_] = results["accounts"] results = conn - |> get("/api/v1/search", %{"q" => "2hu", "limit" => 1, "offset" => 1}) - |> json_response(200) + |> get("/api/v1/search?q=2hu&limit=1&offset=1") + |> json_response_and_validate_schema(200) assert [%{"id" => activity_id2}] = results["statuses"] assert [] = results["accounts"] @@ -275,30 +277,30 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do user = insert(:user) _user_two = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"}) - {:ok, _activity} = CommonAPI.post(user, %{"status" => "This is about 2hu"}) + {:ok, _activity} = CommonAPI.post(user, %{status: "This is about 2hu"}) assert %{"statuses" => [_activity], "accounts" => [], "hashtags" => []} = conn - |> get("/api/v1/search", %{"q" => "2hu", "type" => "statuses"}) - |> json_response(200) + |> get("/api/v1/search?q=2hu&type=statuses") + |> json_response_and_validate_schema(200) assert %{"statuses" => [], "accounts" => [_user_two], "hashtags" => []} = conn - |> get("/api/v1/search", %{"q" => "2hu", "type" => "accounts"}) - |> json_response(200) + |> get("/api/v1/search?q=2hu&type=accounts") + |> json_response_and_validate_schema(200) end test "search uses account_id to filter statuses by the author", %{conn: conn} do user = insert(:user, %{nickname: "shp@shitposter.club"}) user_two = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"}) - {:ok, activity1} = CommonAPI.post(user, %{"status" => "This is about 2hu"}) - {:ok, activity2} = CommonAPI.post(user_two, %{"status" => "This is also about 2hu"}) + {:ok, activity1} = CommonAPI.post(user, %{status: "This is about 2hu"}) + {:ok, activity2} = CommonAPI.post(user_two, %{status: "This is also about 2hu"}) results = conn - |> get("/api/v1/search", %{"q" => "2hu", "account_id" => user.id}) - |> json_response(200) + |> get("/api/v1/search?q=2hu&account_id=#{user.id}") + |> json_response_and_validate_schema(200) assert [%{"id" => activity_id1}] = results["statuses"] assert activity_id1 == activity1.id @@ -306,8 +308,8 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do results = conn - |> get("/api/v1/search", %{"q" => "2hu", "account_id" => user_two.id}) - |> json_response(200) + |> get("/api/v1/search?q=2hu&account_id=#{user_two.id}") + |> json_response_and_validate_schema(200) assert [%{"id" => activity_id2}] = results["statuses"] assert activity_id2 == activity2.id diff --git a/test/web/mastodon_api/controllers/status_controller_test.exs b/test/web/mastodon_api/controllers/status_controller_test.exs index d59974d50..a4403132c 100644 --- a/test/web/mastodon_api/controllers/status_controller_test.exs +++ b/test/web/mastodon_api/controllers/status_controller_test.exs @@ -32,13 +32,14 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do response = conn + |> put_req_header("content-type", "application/json") |> post("api/v1/statuses", %{ "content_type" => "text/plain", "source" => "Pleroma FE", "status" => "Hello world", "visibility" => "public" }) - |> json_response(200) + |> json_response_and_validate_schema(200) assert response["reblogs_count"] == 0 ObanHelpers.perform_all() @@ -46,7 +47,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do response = conn |> get("api/v1/statuses/#{response["id"]}", %{}) - |> json_response(200) + |> json_response_and_validate_schema(200) assert response["reblogs_count"] == 0 end @@ -56,6 +57,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do conn_one = conn + |> put_req_header("content-type", "application/json") |> put_req_header("idempotency-key", idempotency_key) |> post("/api/v1/statuses", %{ "status" => "cofe", @@ -68,12 +70,13 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do assert ttl > :timer.seconds(6 * 60 * 60 - 1) assert %{"content" => "cofe", "id" => id, "spoiler_text" => "2hu", "sensitive" => false} = - json_response(conn_one, 200) + json_response_and_validate_schema(conn_one, 200) assert Activity.get_by_id(id) conn_two = conn + |> put_req_header("content-type", "application/json") |> put_req_header("idempotency-key", idempotency_key) |> post("/api/v1/statuses", %{ "status" => "cofe", @@ -86,13 +89,14 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do conn_three = conn + |> put_req_header("content-type", "application/json") |> post("/api/v1/statuses", %{ "status" => "cofe", "spoiler_text" => "2hu", "sensitive" => "false" }) - assert %{"id" => third_id} = json_response(conn_three, 200) + assert %{"id" => third_id} = json_response_and_validate_schema(conn_three, 200) refute id == third_id # An activity that will expire: @@ -101,12 +105,15 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do conn_four = conn + |> put_req_header("content-type", "application/json") |> post("api/v1/statuses", %{ "status" => "oolong", "expires_in" => expires_in }) - assert fourth_response = %{"id" => fourth_id} = json_response(conn_four, 200) + assert fourth_response = + %{"id" => fourth_id} = json_response_and_validate_schema(conn_four, 200) + assert activity = Activity.get_by_id(fourth_id) assert expiration = ActivityExpiration.get_by_activity_id(fourth_id) @@ -130,22 +137,24 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do assert %{"error" => "Expiry date is too soon"} = conn + |> put_req_header("content-type", "application/json") |> post("api/v1/statuses", %{ "status" => "oolong", "expires_in" => expires_in }) - |> json_response(422) + |> json_response_and_validate_schema(422) # 30 minutes expires_in = 30 * 60 assert %{"error" => "Expiry date is too soon"} = conn + |> put_req_header("content-type", "application/json") |> post("api/v1/statuses", %{ "status" => "oolong", "expires_in" => expires_in }) - |> json_response(422) + |> json_response_and_validate_schema(422) end test "posting an undefined status with an attachment", %{user: user, conn: conn} do @@ -158,21 +167,24 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id) conn = - post(conn, "/api/v1/statuses", %{ + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses", %{ "media_ids" => [to_string(upload.id)] }) - assert json_response(conn, 200) + assert json_response_and_validate_schema(conn, 200) end test "replying to a status", %{user: user, conn: conn} do - {:ok, replied_to} = CommonAPI.post(user, %{"status" => "cofe"}) + {:ok, replied_to} = CommonAPI.post(user, %{status: "cofe"}) conn = conn + |> put_req_header("content-type", "application/json") |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id}) - assert %{"content" => "xD", "id" => id} = json_response(conn, 200) + assert %{"content" => "xD", "id" => id} = json_response_and_validate_schema(conn, 200) activity = Activity.get_by_id(id) @@ -184,43 +196,56 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do user: user, conn: conn } do - {:ok, replied_to} = CommonAPI.post(user, %{"status" => "suya..", "visibility" => "direct"}) + {:ok, replied_to} = CommonAPI.post(user, %{status: "suya..", visibility: "direct"}) Enum.each(["public", "private", "unlisted"], fn visibility -> conn = conn + |> put_req_header("content-type", "application/json") |> post("/api/v1/statuses", %{ "status" => "@#{user.nickname} hey", "in_reply_to_id" => replied_to.id, "visibility" => visibility }) - assert json_response(conn, 422) == %{"error" => "The message visibility must be direct"} + assert json_response_and_validate_schema(conn, 422) == %{ + "error" => "The message visibility must be direct" + } end) end test "posting a status with an invalid in_reply_to_id", %{conn: conn} do - conn = post(conn, "/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""}) + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""}) - assert %{"content" => "xD", "id" => id} = json_response(conn, 200) + assert %{"content" => "xD", "id" => id} = json_response_and_validate_schema(conn, 200) assert Activity.get_by_id(id) end test "posting a sensitive status", %{conn: conn} do - conn = post(conn, "/api/v1/statuses", %{"status" => "cofe", "sensitive" => true}) + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true}) + + assert %{"content" => "cofe", "id" => id, "sensitive" => true} = + json_response_and_validate_schema(conn, 200) - assert %{"content" => "cofe", "id" => id, "sensitive" => true} = json_response(conn, 200) assert Activity.get_by_id(id) end test "posting a fake status", %{conn: conn} do real_conn = - post(conn, "/api/v1/statuses", %{ + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses", %{ "status" => "\"Tenshi Eating a Corndog\" is a much discussed concept on /jp/. The significance of it is disputed, so I will focus on one core concept: the symbolism behind it" }) - real_status = json_response(real_conn, 200) + real_status = json_response_and_validate_schema(real_conn, 200) assert real_status assert Object.get_by_ap_id(real_status["uri"]) @@ -234,13 +259,15 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do |> Kernel.put_in(["pleroma", "conversation_id"], nil) fake_conn = - post(conn, "/api/v1/statuses", %{ + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses", %{ "status" => "\"Tenshi Eating a Corndog\" is a much discussed concept on /jp/. The significance of it is disputed, so I will focus on one core concept: the symbolism behind it", "preview" => true }) - fake_status = json_response(fake_conn, 200) + fake_status = json_response_and_validate_schema(fake_conn, 200) assert fake_status refute Object.get_by_ap_id(fake_status["uri"]) @@ -261,11 +288,15 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do Config.put([:rich_media, :enabled], true) conn = - post(conn, "/api/v1/statuses", %{ + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses", %{ "status" => "https://example.com/ogp" }) - assert %{"id" => id, "card" => %{"title" => "The Rock"}} = json_response(conn, 200) + assert %{"id" => id, "card" => %{"title" => "The Rock"}} = + json_response_and_validate_schema(conn, 200) + assert Activity.get_by_id(id) end @@ -273,9 +304,12 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do user2 = insert(:user) content = "direct cofe @#{user2.nickname}" - conn = post(conn, "api/v1/statuses", %{"status" => content, "visibility" => "direct"}) + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("api/v1/statuses", %{"status" => content, "visibility" => "direct"}) - assert %{"id" => id} = response = json_response(conn, 200) + assert %{"id" => id} = response = json_response_and_validate_schema(conn, 200) assert response["visibility"] == "direct" assert response["pleroma"]["direct_conversation_id"] assert activity = Activity.get_by_id(id) @@ -289,21 +323,45 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do setup do: oauth_access(["write:statuses"]) test "creates a scheduled activity", %{conn: conn} do - scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond) + scheduled_at = + NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond) + |> NaiveDateTime.to_iso8601() + |> Kernel.<>("Z") conn = - post(conn, "/api/v1/statuses", %{ + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses", %{ "status" => "scheduled", "scheduled_at" => scheduled_at }) - assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200) + assert %{"scheduled_at" => expected_scheduled_at} = + json_response_and_validate_schema(conn, 200) + assert expected_scheduled_at == CommonAPI.Utils.to_masto_date(scheduled_at) assert [] == Repo.all(Activity) end + test "ignores nil values", %{conn: conn} do + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses", %{ + "status" => "not scheduled", + "scheduled_at" => nil + }) + + assert result = json_response_and_validate_schema(conn, 200) + assert Activity.get_by_id(result["id"]) + end + test "creates a scheduled activity with a media attachment", %{user: user, conn: conn} do - scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond) + scheduled_at = + NaiveDateTime.utc_now() + |> NaiveDateTime.add(:timer.minutes(120), :millisecond) + |> NaiveDateTime.to_iso8601() + |> Kernel.<>("Z") file = %Plug.Upload{ content_type: "image/jpg", @@ -314,13 +372,17 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id) conn = - post(conn, "/api/v1/statuses", %{ + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses", %{ "media_ids" => [to_string(upload.id)], "status" => "scheduled", "scheduled_at" => scheduled_at }) - assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200) + assert %{"media_attachments" => [media_attachment]} = + json_response_and_validate_schema(conn, 200) + assert %{"type" => "image"} = media_attachment end @@ -328,14 +390,18 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do %{conn: conn} do scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond) + |> NaiveDateTime.to_iso8601() + |> Kernel.<>("Z") conn = - post(conn, "/api/v1/statuses", %{ + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses", %{ "status" => "not scheduled", "scheduled_at" => scheduled_at }) - assert %{"content" => "not scheduled"} = json_response(conn, 200) + assert %{"content" => "not scheduled"} = json_response_and_validate_schema(conn, 200) assert [] == Repo.all(ScheduledActivity) end @@ -344,14 +410,19 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do NaiveDateTime.utc_now() |> NaiveDateTime.add(:timer.minutes(6), :millisecond) |> NaiveDateTime.to_iso8601() + # TODO + |> Kernel.<>("Z") attrs = %{params: %{}, scheduled_at: today} {:ok, _} = ScheduledActivity.create(user, attrs) {:ok, _} = ScheduledActivity.create(user, attrs) - conn = post(conn, "/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today}) + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today}) - assert %{"error" => "daily limit exceeded"} == json_response(conn, 422) + assert %{"error" => "daily limit exceeded"} == json_response_and_validate_schema(conn, 422) end test "returns error when total user limit is exceeded", %{user: user, conn: conn} do @@ -359,11 +430,13 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do NaiveDateTime.utc_now() |> NaiveDateTime.add(:timer.minutes(6), :millisecond) |> NaiveDateTime.to_iso8601() + |> Kernel.<>("Z") tomorrow = NaiveDateTime.utc_now() |> NaiveDateTime.add(:timer.hours(36), :millisecond) |> NaiveDateTime.to_iso8601() + |> Kernel.<>("Z") attrs = %{params: %{}, scheduled_at: today} {:ok, _} = ScheduledActivity.create(user, attrs) @@ -371,9 +444,11 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow}) conn = - post(conn, "/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow}) + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow}) - assert %{"error" => "total limit exceeded"} == json_response(conn, 422) + assert %{"error" => "total limit exceeded"} == json_response_and_validate_schema(conn, 422) end end @@ -384,12 +459,17 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do time = NaiveDateTime.utc_now() conn = - post(conn, "/api/v1/statuses", %{ + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses", %{ "status" => "Who is the #bestgrill?", - "poll" => %{"options" => ["Rei", "Asuka", "Misato"], "expires_in" => 420} + "poll" => %{ + "options" => ["Rei", "Asuka", "Misato"], + "expires_in" => 420 + } }) - response = json_response(conn, 200) + response = json_response_and_validate_schema(conn, 200) assert Enum.all?(response["poll"]["options"], fn %{"title" => title} -> title in ["Rei", "Asuka", "Misato"] @@ -408,12 +488,14 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do limit = Config.get([:instance, :poll_limits, :max_options]) conn = - post(conn, "/api/v1/statuses", %{ + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses", %{ "status" => "desu~", "poll" => %{"options" => Enum.map(0..limit, fn _ -> "desu" end), "expires_in" => 1} }) - %{"error" => error} = json_response(conn, 422) + %{"error" => error} = json_response_and_validate_schema(conn, 422) assert error == "Poll can't contain more than #{limit} options" end @@ -421,7 +503,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do limit = Config.get([:instance, :poll_limits, :max_option_chars]) conn = - post(conn, "/api/v1/statuses", %{ + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses", %{ "status" => "...", "poll" => %{ "options" => [Enum.reduce(0..limit, "", fn _, acc -> acc <> "." end)], @@ -429,7 +513,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do } }) - %{"error" => error} = json_response(conn, 422) + %{"error" => error} = json_response_and_validate_schema(conn, 422) assert error == "Poll options cannot be longer than #{limit} characters each" end @@ -437,7 +521,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do limit = Config.get([:instance, :poll_limits, :min_expiration]) conn = - post(conn, "/api/v1/statuses", %{ + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses", %{ "status" => "imagine arbitrary limits", "poll" => %{ "options" => ["this post was made by pleroma gang"], @@ -445,7 +531,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do } }) - %{"error" => error} = json_response(conn, 422) + %{"error" => error} = json_response_and_validate_schema(conn, 422) assert error == "Expiration date is too soon" end @@ -453,7 +539,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do limit = Config.get([:instance, :poll_limits, :max_expiration]) conn = - post(conn, "/api/v1/statuses", %{ + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses", %{ "status" => "imagine arbitrary limits", "poll" => %{ "options" => ["this post was made by pleroma gang"], @@ -461,7 +549,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do } }) - %{"error" => error} = json_response(conn, 422) + %{"error" => error} = json_response_and_validate_schema(conn, 422) assert error == "Expiration date is too far in the future" end end @@ -472,7 +560,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do conn = get(conn, "/api/v1/statuses/#{activity.id}") - assert %{"id" => id} = json_response(conn, 200) + assert %{"id" => id} = json_response_and_validate_schema(conn, 200) assert id == to_string(activity.id) end @@ -492,13 +580,13 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do res_conn = get(conn, "/api/v1/statuses/#{local.id}") - assert json_response(res_conn, :not_found) == %{ + assert json_response_and_validate_schema(res_conn, :not_found) == %{ "error" => "Record not found" } res_conn = get(conn, "/api/v1/statuses/#{remote.id}") - assert json_response(res_conn, :not_found) == %{ + assert json_response_and_validate_schema(res_conn, :not_found) == %{ "error" => "Record not found" } end @@ -506,10 +594,10 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do test "if user is authenticated", %{local: local, remote: remote} do %{conn: conn} = oauth_access(["read"]) res_conn = get(conn, "/api/v1/statuses/#{local.id}") - assert %{"id" => _} = json_response(res_conn, 200) + assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200) res_conn = get(conn, "/api/v1/statuses/#{remote.id}") - assert %{"id" => _} = json_response(res_conn, 200) + assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200) end end @@ -521,21 +609,21 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do res_conn = get(conn, "/api/v1/statuses/#{local.id}") - assert json_response(res_conn, :not_found) == %{ + assert json_response_and_validate_schema(res_conn, :not_found) == %{ "error" => "Record not found" } res_conn = get(conn, "/api/v1/statuses/#{remote.id}") - assert %{"id" => _} = json_response(res_conn, 200) + assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200) end test "if user is authenticated", %{local: local, remote: remote} do %{conn: conn} = oauth_access(["read"]) res_conn = get(conn, "/api/v1/statuses/#{local.id}") - assert %{"id" => _} = json_response(res_conn, 200) + assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200) res_conn = get(conn, "/api/v1/statuses/#{remote.id}") - assert %{"id" => _} = json_response(res_conn, 200) + assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200) end end @@ -546,11 +634,11 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do res_conn = get(conn, "/api/v1/statuses/#{local.id}") - assert %{"id" => _} = json_response(res_conn, 200) + assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200) res_conn = get(conn, "/api/v1/statuses/#{remote.id}") - assert json_response(res_conn, :not_found) == %{ + assert json_response_and_validate_schema(res_conn, :not_found) == %{ "error" => "Record not found" } end @@ -558,10 +646,10 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do test "if user is authenticated", %{local: local, remote: remote} do %{conn: conn} = oauth_access(["read"]) res_conn = get(conn, "/api/v1/statuses/#{local.id}") - assert %{"id" => _} = json_response(res_conn, 200) + assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200) res_conn = get(conn, "/api/v1/statuses/#{remote.id}") - assert %{"id" => _} = json_response(res_conn, 200) + assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200) end end @@ -571,7 +659,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do conn = get(conn, "/api/v1/statuses/#{String.downcase(activity.id)}") - assert json_response(conn, 404) == %{"error" => "Record not found"} + assert json_response_and_validate_schema(conn, 404) == %{"error" => "Record not found"} end test "get a direct status" do @@ -579,7 +667,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do other_user = insert(:user) {:ok, activity} = - CommonAPI.post(user, %{"status" => "@#{other_user.nickname}", "visibility" => "direct"}) + CommonAPI.post(user, %{status: "@#{other_user.nickname}", visibility: "direct"}) conn = conn @@ -588,7 +676,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do [participation] = Participation.for_user(user) - res = json_response(conn, 200) + res = json_response_and_validate_schema(conn, 200) assert res["pleroma"]["direct_conversation_id"] == participation.id end @@ -600,7 +688,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do query_string = "ids[]=#{id1}&ids[]=#{id2}" conn = get(conn, "/api/v1/statuses/?#{query_string}") - assert [%{"id" => ^id1}, %{"id" => ^id2}] = Enum.sort_by(json_response(conn, :ok), & &1["id"]) + assert [%{"id" => ^id1}, %{"id" => ^id2}] = + Enum.sort_by(json_response_and_validate_schema(conn, :ok), & &1["id"]) end describe "getting statuses by ids with restricted unauthenticated for local and remote" do @@ -611,17 +700,17 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do setup do: clear_config([:restrict_unauthenticated, :activities, :remote], true) test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do - res_conn = get(conn, "/api/v1/statuses", %{ids: [local.id, remote.id]}) + res_conn = get(conn, "/api/v1/statuses?ids[]=#{local.id}&ids[]=#{remote.id}") - assert json_response(res_conn, 200) == [] + assert json_response_and_validate_schema(res_conn, 200) == [] end test "if user is authenticated", %{local: local, remote: remote} do %{conn: conn} = oauth_access(["read"]) - res_conn = get(conn, "/api/v1/statuses", %{ids: [local.id, remote.id]}) + res_conn = get(conn, "/api/v1/statuses?ids[]=#{local.id}&ids[]=#{remote.id}") - assert length(json_response(res_conn, 200)) == 2 + assert length(json_response_and_validate_schema(res_conn, 200)) == 2 end end @@ -631,18 +720,18 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do setup do: clear_config([:restrict_unauthenticated, :activities, :local], true) test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do - res_conn = get(conn, "/api/v1/statuses", %{ids: [local.id, remote.id]}) + res_conn = get(conn, "/api/v1/statuses?ids[]=#{local.id}&ids[]=#{remote.id}") remote_id = remote.id - assert [%{"id" => ^remote_id}] = json_response(res_conn, 200) + assert [%{"id" => ^remote_id}] = json_response_and_validate_schema(res_conn, 200) end test "if user is authenticated", %{local: local, remote: remote} do %{conn: conn} = oauth_access(["read"]) - res_conn = get(conn, "/api/v1/statuses", %{ids: [local.id, remote.id]}) + res_conn = get(conn, "/api/v1/statuses?ids[]=#{local.id}&ids[]=#{remote.id}") - assert length(json_response(res_conn, 200)) == 2 + assert length(json_response_and_validate_schema(res_conn, 200)) == 2 end end @@ -652,18 +741,18 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do setup do: clear_config([:restrict_unauthenticated, :activities, :remote], true) test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do - res_conn = get(conn, "/api/v1/statuses", %{ids: [local.id, remote.id]}) + res_conn = get(conn, "/api/v1/statuses?ids[]=#{local.id}&ids[]=#{remote.id}") local_id = local.id - assert [%{"id" => ^local_id}] = json_response(res_conn, 200) + assert [%{"id" => ^local_id}] = json_response_and_validate_schema(res_conn, 200) end test "if user is authenticated", %{local: local, remote: remote} do %{conn: conn} = oauth_access(["read"]) - res_conn = get(conn, "/api/v1/statuses", %{ids: [local.id, remote.id]}) + res_conn = get(conn, "/api/v1/statuses?ids[]=#{local.id}&ids[]=#{remote.id}") - assert length(json_response(res_conn, 200)) == 2 + assert length(json_response_and_validate_schema(res_conn, 200)) == 2 end end @@ -677,7 +766,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do |> assign(:user, author) |> delete("/api/v1/statuses/#{activity.id}") - assert %{} = json_response(conn, 200) + assert %{} = json_response_and_validate_schema(conn, 200) refute Activity.get_by_id(activity.id) end @@ -691,7 +780,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do |> assign(:user, author) |> delete("/api/v1/statuses/#{String.downcase(activity.id)}") - assert %{"error" => "Record not found"} == json_response(conn, 404) + assert %{"error" => "Record not found"} == json_response_and_validate_schema(conn, 404) end test "when you didn't create it" do @@ -700,7 +789,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do conn = delete(conn, "/api/v1/statuses/#{activity.id}") - assert %{"error" => _} = json_response(conn, 403) + assert %{"error" => _} = json_response_and_validate_schema(conn, 403) assert Activity.get_by_id(activity.id) == activity end @@ -717,7 +806,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do |> assign(:token, insert(:oauth_token, user: admin, scopes: ["write:statuses"])) |> delete("/api/v1/statuses/#{activity1.id}") - assert %{} = json_response(res_conn, 200) + assert %{} = json_response_and_validate_schema(res_conn, 200) res_conn = conn @@ -725,7 +814,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do |> assign(:token, insert(:oauth_token, user: moderator, scopes: ["write:statuses"])) |> delete("/api/v1/statuses/#{activity2.id}") - assert %{} = json_response(res_conn, 200) + assert %{} = json_response_and_validate_schema(res_conn, 200) refute Activity.get_by_id(activity1.id) refute Activity.get_by_id(activity2.id) @@ -738,12 +827,15 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do test "reblogs and returns the reblogged status", %{conn: conn} do activity = insert(:note_activity) - conn = post(conn, "/api/v1/statuses/#{activity.id}/reblog") + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses/#{activity.id}/reblog") assert %{ "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}, "reblogged" => true - } = json_response(conn, 200) + } = json_response_and_validate_schema(conn, 200) assert to_string(activity.id) == id end @@ -751,21 +843,30 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do test "returns 404 if the reblogged status doesn't exist", %{conn: conn} do activity = insert(:note_activity) - conn = post(conn, "/api/v1/statuses/#{String.downcase(activity.id)}/reblog") + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses/#{String.downcase(activity.id)}/reblog") - assert %{"error" => "Record not found"} = json_response(conn, 404) + assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn, 404) end test "reblogs privately and returns the reblogged status", %{conn: conn} do activity = insert(:note_activity) - conn = post(conn, "/api/v1/statuses/#{activity.id}/reblog", %{"visibility" => "private"}) + conn = + conn + |> put_req_header("content-type", "application/json") + |> post( + "/api/v1/statuses/#{activity.id}/reblog", + %{"visibility" => "private"} + ) assert %{ "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}, "reblogged" => true, "visibility" => "private" - } = json_response(conn, 200) + } = json_response_and_validate_schema(conn, 200) assert to_string(activity.id) == id end @@ -775,7 +876,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do user1 = insert(:user) user2 = insert(:user) user3 = insert(:user) - CommonAPI.favorite(activity.id, user2) + {:ok, _} = CommonAPI.favorite(user2, activity.id) {:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id) {:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1) {:ok, _, _object} = CommonAPI.repeat(activity.id, user2) @@ -791,7 +892,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do "reblogged" => false, "favourited" => false, "bookmarked" => false - } = json_response(conn_res, 200) + } = json_response_and_validate_schema(conn_res, 200) conn_res = build_conn() @@ -804,7 +905,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do "reblogged" => true, "favourited" => true, "bookmarked" => true - } = json_response(conn_res, 200) + } = json_response_and_validate_schema(conn_res, 200) assert to_string(activity.id) == id end @@ -818,17 +919,24 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do {:ok, _, _} = CommonAPI.repeat(activity.id, user) - conn = post(conn, "/api/v1/statuses/#{activity.id}/unreblog") + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses/#{activity.id}/unreblog") - assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200) + assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = + json_response_and_validate_schema(conn, 200) assert to_string(activity.id) == id end test "returns 404 error when activity does not exist", %{conn: conn} do - conn = post(conn, "/api/v1/statuses/foo/unreblog") + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses/foo/unreblog") - assert json_response(conn, 404) == %{"error" => "Record not found"} + assert json_response_and_validate_schema(conn, 404) == %{"error" => "Record not found"} end end @@ -838,10 +946,13 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do test "favs a status and returns it", %{conn: conn} do activity = insert(:note_activity) - conn = post(conn, "/api/v1/statuses/#{activity.id}/favourite") + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses/#{activity.id}/favourite") assert %{"id" => id, "favourites_count" => 1, "favourited" => true} = - json_response(conn, 200) + json_response_and_validate_schema(conn, 200) assert to_string(activity.id) == id end @@ -849,14 +960,23 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do test "favoriting twice will just return 200", %{conn: conn} do activity = insert(:note_activity) - post(conn, "/api/v1/statuses/#{activity.id}/favourite") - assert post(conn, "/api/v1/statuses/#{activity.id}/favourite") |> json_response(200) + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses/#{activity.id}/favourite") + + assert conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses/#{activity.id}/favourite") + |> json_response_and_validate_schema(200) end test "returns 404 error for a wrong id", %{conn: conn} do - conn = post(conn, "/api/v1/statuses/1/favourite") + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses/1/favourite") - assert json_response(conn, 404) == %{"error" => "Record not found"} + assert json_response_and_validate_schema(conn, 404) == %{"error" => "Record not found"} end end @@ -866,20 +986,26 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do test "unfavorites a status and returns it", %{user: user, conn: conn} do activity = insert(:note_activity) - {:ok, _, _} = CommonAPI.favorite(activity.id, user) + {:ok, _} = CommonAPI.favorite(user, activity.id) - conn = post(conn, "/api/v1/statuses/#{activity.id}/unfavourite") + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses/#{activity.id}/unfavourite") assert %{"id" => id, "favourites_count" => 0, "favourited" => false} = - json_response(conn, 200) + json_response_and_validate_schema(conn, 200) assert to_string(activity.id) == id end test "returns 404 error for a wrong id", %{conn: conn} do - conn = post(conn, "/api/v1/statuses/1/unfavourite") + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses/1/unfavourite") - assert json_response(conn, 404) == %{"error" => "Record not found"} + assert json_response_and_validate_schema(conn, 404) == %{"error" => "Record not found"} end end @@ -887,7 +1013,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do setup do: oauth_access(["write:accounts"]) setup %{user: user} do - {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"}) + {:ok, activity} = CommonAPI.post(user, %{status: "HI!!!"}) %{activity: activity} end @@ -899,21 +1025,25 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do assert %{"id" => ^id_str, "pinned" => true} = conn + |> put_req_header("content-type", "application/json") |> post("/api/v1/statuses/#{activity.id}/pin") - |> json_response(200) + |> json_response_and_validate_schema(200) assert [%{"id" => ^id_str, "pinned" => true}] = conn |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true") - |> json_response(200) + |> json_response_and_validate_schema(200) end test "/pin: returns 400 error when activity is not public", %{conn: conn, user: user} do - {:ok, dm} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"}) + {:ok, dm} = CommonAPI.post(user, %{status: "test", visibility: "direct"}) - conn = post(conn, "/api/v1/statuses/#{dm.id}/pin") + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses/#{dm.id}/pin") - assert json_response(conn, 400) == %{"error" => "Could not pin"} + assert json_response_and_validate_schema(conn, 400) == %{"error" => "Could not pin"} end test "unpin status", %{conn: conn, user: user, activity: activity} do @@ -926,29 +1056,33 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do conn |> assign(:user, user) |> post("/api/v1/statuses/#{activity.id}/unpin") - |> json_response(200) + |> json_response_and_validate_schema(200) assert [] = conn |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true") - |> json_response(200) + |> json_response_and_validate_schema(200) end test "/unpin: returns 400 error when activity is not exist", %{conn: conn} do - conn = post(conn, "/api/v1/statuses/1/unpin") + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses/1/unpin") - assert json_response(conn, 400) == %{"error" => "Could not unpin"} + assert json_response_and_validate_schema(conn, 400) == %{"error" => "Could not unpin"} end test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do - {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"}) + {:ok, activity_two} = CommonAPI.post(user, %{status: "HI!!!"}) id_str_one = to_string(activity_one.id) assert %{"id" => ^id_str_one, "pinned" => true} = conn + |> put_req_header("content-type", "application/json") |> post("/api/v1/statuses/#{id_str_one}/pin") - |> json_response(200) + |> json_response_and_validate_schema(200) user = refresh_record(user) @@ -956,7 +1090,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do conn |> assign(:user, user) |> post("/api/v1/statuses/#{activity_two.id}/pin") - |> json_response(400) + |> json_response_and_validate_schema(400) end end @@ -970,7 +1104,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do test "returns rich-media card", %{conn: conn, user: user} do Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end) - {:ok, activity} = CommonAPI.post(user, %{"status" => "https://example.com/ogp"}) + {:ok, activity} = CommonAPI.post(user, %{status: "https://example.com/ogp"}) card_data = %{ "image" => "http://ia.media-imdb.com/images/rock.jpg", @@ -996,18 +1130,18 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do response = conn |> get("/api/v1/statuses/#{activity.id}/card") - |> json_response(200) + |> json_response_and_validate_schema(200) assert response == card_data # works with private posts {:ok, activity} = - CommonAPI.post(user, %{"status" => "https://example.com/ogp", "visibility" => "direct"}) + CommonAPI.post(user, %{status: "https://example.com/ogp", visibility: "direct"}) response_two = conn |> get("/api/v1/statuses/#{activity.id}/card") - |> json_response(200) + |> json_response_and_validate_schema(200) assert response_two == card_data end @@ -1015,13 +1149,12 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do test "replaces missing description with an empty string", %{conn: conn, user: user} do Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end) - {:ok, activity} = - CommonAPI.post(user, %{"status" => "https://example.com/ogp-missing-data"}) + {:ok, activity} = CommonAPI.post(user, %{status: "https://example.com/ogp-missing-data"}) response = conn |> get("/api/v1/statuses/#{activity.id}/card") - |> json_response(:ok) + |> json_response_and_validate_schema(:ok) assert response == %{ "type" => "link", @@ -1043,39 +1176,47 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do end test "bookmarks" do + bookmarks_uri = "/api/v1/bookmarks?with_relationships=true" + %{conn: conn} = oauth_access(["write:bookmarks", "read:bookmarks"]) author = insert(:user) - {:ok, activity1} = - CommonAPI.post(author, %{ - "status" => "heweoo?" - }) - - {:ok, activity2} = - CommonAPI.post(author, %{ - "status" => "heweoo!" - }) + {:ok, activity1} = CommonAPI.post(author, %{status: "heweoo?"}) + {:ok, activity2} = CommonAPI.post(author, %{status: "heweoo!"}) - response1 = post(conn, "/api/v1/statuses/#{activity1.id}/bookmark") + response1 = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses/#{activity1.id}/bookmark") - assert json_response(response1, 200)["bookmarked"] == true + assert json_response_and_validate_schema(response1, 200)["bookmarked"] == true - response2 = post(conn, "/api/v1/statuses/#{activity2.id}/bookmark") + response2 = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses/#{activity2.id}/bookmark") - assert json_response(response2, 200)["bookmarked"] == true + assert json_response_and_validate_schema(response2, 200)["bookmarked"] == true - bookmarks = get(conn, "/api/v1/bookmarks") + bookmarks = get(conn, bookmarks_uri) - assert [json_response(response2, 200), json_response(response1, 200)] == - json_response(bookmarks, 200) + assert [ + json_response_and_validate_schema(response2, 200), + json_response_and_validate_schema(response1, 200) + ] == + json_response_and_validate_schema(bookmarks, 200) - response1 = post(conn, "/api/v1/statuses/#{activity1.id}/unbookmark") + response1 = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses/#{activity1.id}/unbookmark") - assert json_response(response1, 200)["bookmarked"] == false + assert json_response_and_validate_schema(response1, 200)["bookmarked"] == false - bookmarks = get(conn, "/api/v1/bookmarks") + bookmarks = get(conn, bookmarks_uri) - assert [json_response(response2, 200)] == json_response(bookmarks, 200) + assert [json_response_and_validate_schema(response2, 200)] == + json_response_and_validate_schema(bookmarks, 200) end describe "conversation muting" do @@ -1083,7 +1224,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do setup do post_user = insert(:user) - {:ok, activity} = CommonAPI.post(post_user, %{"status" => "HIE"}) + {:ok, activity} = CommonAPI.post(post_user, %{status: "HIE"}) %{activity: activity} end @@ -1092,16 +1233,22 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do assert %{"id" => ^id_str, "muted" => true} = conn + |> put_req_header("content-type", "application/json") |> post("/api/v1/statuses/#{activity.id}/mute") - |> json_response(200) + |> json_response_and_validate_schema(200) end test "cannot mute already muted conversation", %{conn: conn, user: user, activity: activity} do {:ok, _} = CommonAPI.add_mute(user, activity) - conn = post(conn, "/api/v1/statuses/#{activity.id}/mute") + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses/#{activity.id}/mute") - assert json_response(conn, 400) == %{"error" => "conversation is already muted"} + assert json_response_and_validate_schema(conn, 400) == %{ + "error" => "conversation is already muted" + } end test "unmute conversation", %{conn: conn, user: user, activity: activity} do @@ -1113,7 +1260,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do conn # |> assign(:user, user) |> post("/api/v1/statuses/#{activity.id}/unmute") - |> json_response(200) + |> json_response_and_validate_schema(200) end end @@ -1122,16 +1269,17 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do user2 = insert(:user) user3 = insert(:user) - {:ok, replied_to} = CommonAPI.post(user1, %{"status" => "cofe"}) + {:ok, replied_to} = CommonAPI.post(user1, %{status: "cofe"}) # Reply to status from another user conn1 = conn |> assign(:user, user2) |> assign(:token, insert(:oauth_token, user: user2, scopes: ["write:statuses"])) + |> put_req_header("content-type", "application/json") |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id}) - assert %{"content" => "xD", "id" => id} = json_response(conn1, 200) + assert %{"content" => "xD", "id" => id} = json_response_and_validate_schema(conn1, 200) activity = Activity.get_by_id_with_object(id) @@ -1143,10 +1291,11 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do conn |> assign(:user, user3) |> assign(:token, insert(:oauth_token, user: user3, scopes: ["write:statuses"])) + |> put_req_header("content-type", "application/json") |> post("/api/v1/statuses/#{activity.id}/reblog") assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} = - json_response(conn2, 200) + json_response_and_validate_schema(conn2, 200) assert to_string(activity.id) == id @@ -1169,19 +1318,19 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do setup do: oauth_access(["read:accounts"]) setup %{user: user} do - {:ok, activity} = CommonAPI.post(user, %{"status" => "test"}) + {:ok, activity} = CommonAPI.post(user, %{status: "test"}) %{activity: activity} end test "returns users who have favorited the status", %{conn: conn, activity: activity} do other_user = insert(:user) - {:ok, _, _} = CommonAPI.favorite(activity.id, other_user) + {:ok, _} = CommonAPI.favorite(other_user, activity.id) response = conn |> get("/api/v1/statuses/#{activity.id}/favourited_by") - |> json_response(:ok) + |> json_response_and_validate_schema(:ok) [%{"id" => id}] = response @@ -1195,7 +1344,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do response = conn |> get("/api/v1/statuses/#{activity.id}/favourited_by") - |> json_response(:ok) + |> json_response_and_validate_schema(:ok) assert Enum.empty?(response) end @@ -1207,24 +1356,24 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do other_user = insert(:user) {:ok, _user_relationship} = User.block(user, other_user) - {:ok, _, _} = CommonAPI.favorite(activity.id, other_user) + {:ok, _} = CommonAPI.favorite(other_user, activity.id) response = conn |> get("/api/v1/statuses/#{activity.id}/favourited_by") - |> json_response(:ok) + |> json_response_and_validate_schema(:ok) assert Enum.empty?(response) end test "does not fail on an unauthenticated request", %{activity: activity} do other_user = insert(:user) - {:ok, _, _} = CommonAPI.favorite(activity.id, other_user) + {:ok, _} = CommonAPI.favorite(other_user, activity.id) response = build_conn() |> get("/api/v1/statuses/#{activity.id}/favourited_by") - |> json_response(:ok) + |> json_response_and_validate_schema(:ok) [%{"id" => id}] = response assert id == other_user.id @@ -1235,17 +1384,17 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do {:ok, activity} = CommonAPI.post(user, %{ - "status" => "@#{other_user.nickname} wanna get some #cofe together?", - "visibility" => "direct" + status: "@#{other_user.nickname} wanna get some #cofe together?", + visibility: "direct" }) - {:ok, _, _} = CommonAPI.favorite(activity.id, other_user) + {:ok, _} = CommonAPI.favorite(other_user, activity.id) favourited_by_url = "/api/v1/statuses/#{activity.id}/favourited_by" build_conn() |> get(favourited_by_url) - |> json_response(404) + |> json_response_and_validate_schema(404) conn = build_conn() @@ -1255,12 +1404,12 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do conn |> assign(:token, nil) |> get(favourited_by_url) - |> json_response(404) + |> json_response_and_validate_schema(404) response = conn |> get(favourited_by_url) - |> json_response(200) + |> json_response_and_validate_schema(200) [%{"id" => id}] = response assert id == other_user.id @@ -1271,7 +1420,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do setup do: oauth_access(["read:accounts"]) setup %{user: user} do - {:ok, activity} = CommonAPI.post(user, %{"status" => "test"}) + {:ok, activity} = CommonAPI.post(user, %{status: "test"}) %{activity: activity} end @@ -1283,7 +1432,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do response = conn |> get("/api/v1/statuses/#{activity.id}/reblogged_by") - |> json_response(:ok) + |> json_response_and_validate_schema(:ok) [%{"id" => id}] = response @@ -1297,7 +1446,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do response = conn |> get("/api/v1/statuses/#{activity.id}/reblogged_by") - |> json_response(:ok) + |> json_response_and_validate_schema(:ok) assert Enum.empty?(response) end @@ -1314,7 +1463,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do response = conn |> get("/api/v1/statuses/#{activity.id}/reblogged_by") - |> json_response(:ok) + |> json_response_and_validate_schema(:ok) assert Enum.empty?(response) end @@ -1325,12 +1474,12 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do } do other_user = insert(:user) - {:ok, _, _} = CommonAPI.repeat(activity.id, other_user, %{"visibility" => "private"}) + {:ok, _, _} = CommonAPI.repeat(activity.id, other_user, %{visibility: "private"}) response = conn |> get("/api/v1/statuses/#{activity.id}/reblogged_by") - |> json_response(:ok) + |> json_response_and_validate_schema(:ok) assert Enum.empty?(response) end @@ -1342,7 +1491,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do response = build_conn() |> get("/api/v1/statuses/#{activity.id}/reblogged_by") - |> json_response(:ok) + |> json_response_and_validate_schema(:ok) [%{"id" => id}] = response assert id == other_user.id @@ -1353,20 +1502,20 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do {:ok, activity} = CommonAPI.post(user, %{ - "status" => "@#{other_user.nickname} wanna get some #cofe together?", - "visibility" => "direct" + status: "@#{other_user.nickname} wanna get some #cofe together?", + visibility: "direct" }) build_conn() |> get("/api/v1/statuses/#{activity.id}/reblogged_by") - |> json_response(404) + |> json_response_and_validate_schema(404) response = build_conn() |> assign(:user, other_user) |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["read:accounts"])) |> get("/api/v1/statuses/#{activity.id}/reblogged_by") - |> json_response(200) + |> json_response_and_validate_schema(200) assert [] == response end @@ -1375,16 +1524,16 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do test "context" do user = insert(:user) - {:ok, %{id: id1}} = CommonAPI.post(user, %{"status" => "1"}) - {:ok, %{id: id2}} = CommonAPI.post(user, %{"status" => "2", "in_reply_to_status_id" => id1}) - {:ok, %{id: id3}} = CommonAPI.post(user, %{"status" => "3", "in_reply_to_status_id" => id2}) - {:ok, %{id: id4}} = CommonAPI.post(user, %{"status" => "4", "in_reply_to_status_id" => id3}) - {:ok, %{id: id5}} = CommonAPI.post(user, %{"status" => "5", "in_reply_to_status_id" => id4}) + {:ok, %{id: id1}} = CommonAPI.post(user, %{status: "1"}) + {:ok, %{id: id2}} = CommonAPI.post(user, %{status: "2", in_reply_to_status_id: id1}) + {:ok, %{id: id3}} = CommonAPI.post(user, %{status: "3", in_reply_to_status_id: id2}) + {:ok, %{id: id4}} = CommonAPI.post(user, %{status: "4", in_reply_to_status_id: id3}) + {:ok, %{id: id5}} = CommonAPI.post(user, %{status: "5", in_reply_to_status_id: id4}) response = build_conn() |> get("/api/v1/statuses/#{id3}/context") - |> json_response(:ok) + |> json_response_and_validate_schema(:ok) assert %{ "ancestors" => [%{"id" => ^id1}, %{"id" => ^id2}], @@ -1396,14 +1545,14 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do %{user: user, conn: conn} = oauth_access(["read:favourites"]) other_user = insert(:user) - {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"}) - {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"}) + {:ok, _} = CommonAPI.post(other_user, %{status: "bla"}) + {:ok, activity} = CommonAPI.post(other_user, %{status: "traps are happy"}) - {:ok, _, _} = CommonAPI.favorite(activity.id, user) + {:ok, _} = CommonAPI.favorite(user, activity.id) first_conn = get(conn, "/api/v1/favourites") - assert [status] = json_response(first_conn, 200) + assert [status] = json_response_and_validate_schema(first_conn, 200) assert status["id"] == to_string(activity.id) assert [{"link", _link_header}] = @@ -1412,27 +1561,26 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do # Honours query params {:ok, second_activity} = CommonAPI.post(other_user, %{ - "status" => - "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful." + status: "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful." }) - {:ok, _, _} = CommonAPI.favorite(second_activity.id, user) + {:ok, _} = CommonAPI.favorite(user, second_activity.id) last_like = status["id"] second_conn = get(conn, "/api/v1/favourites?since_id=#{last_like}") - assert [second_status] = json_response(second_conn, 200) + assert [second_status] = json_response_and_validate_schema(second_conn, 200) assert second_status["id"] == to_string(second_activity.id) third_conn = get(conn, "/api/v1/favourites?limit=0") - assert [] = json_response(third_conn, 200) + assert [] = json_response_and_validate_schema(third_conn, 200) end test "expires_at is nil for another user" do %{conn: conn, user: user} = oauth_access(["read:statuses"]) - {:ok, activity} = CommonAPI.post(user, %{"status" => "foobar", "expires_in" => 1_000_000}) + {:ok, activity} = CommonAPI.post(user, %{status: "foobar", expires_in: 1_000_000}) expires_at = activity.id @@ -1441,11 +1589,15 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do |> NaiveDateTime.to_iso8601() assert %{"pleroma" => %{"expires_at" => ^expires_at}} = - conn |> get("/api/v1/statuses/#{activity.id}") |> json_response(:ok) + conn + |> get("/api/v1/statuses/#{activity.id}") + |> json_response_and_validate_schema(:ok) %{conn: conn} = oauth_access(["read:statuses"]) assert %{"pleroma" => %{"expires_at" => nil}} = - conn |> get("/api/v1/statuses/#{activity.id}") |> json_response(:ok) + conn + |> get("/api/v1/statuses/#{activity.id}") + |> json_response_and_validate_schema(:ok) end end diff --git a/test/web/mastodon_api/controllers/subscription_controller_test.exs b/test/web/mastodon_api/controllers/subscription_controller_test.exs index 987158a74..4aa260663 100644 --- a/test/web/mastodon_api/controllers/subscription_controller_test.exs +++ b/test/web/mastodon_api/controllers/subscription_controller_test.exs @@ -6,6 +6,7 @@ defmodule Pleroma.Web.MastodonAPI.SubscriptionControllerTest do use Pleroma.Web.ConnCase import Pleroma.Factory + alias Pleroma.Web.Push alias Pleroma.Web.Push.Subscription @@ -27,6 +28,7 @@ defmodule Pleroma.Web.MastodonAPI.SubscriptionControllerTest do build_conn() |> assign(:user, user) |> assign(:token, token) + |> put_req_header("content-type", "application/json") %{conn: conn, user: user, token: token} end @@ -35,7 +37,10 @@ defmodule Pleroma.Web.MastodonAPI.SubscriptionControllerTest do quote do vapid_details = Application.get_env(:web_push_encryption, :vapid_details, []) Application.put_env(:web_push_encryption, :vapid_details, []) - assert "Something went wrong" == unquote(yield) + + assert %{"error" => "Web push subscription is disabled on this Pleroma instance"} == + unquote(yield) + Application.put_env(:web_push_encryption, :vapid_details, vapid_details) end end @@ -44,8 +49,8 @@ defmodule Pleroma.Web.MastodonAPI.SubscriptionControllerTest do test "returns error when push disabled ", %{conn: conn} do assert_error_when_disable_push do conn - |> post("/api/v1/push/subscription", %{}) - |> json_response(500) + |> post("/api/v1/push/subscription", %{subscription: @sub}) + |> json_response_and_validate_schema(403) end end @@ -56,7 +61,7 @@ defmodule Pleroma.Web.MastodonAPI.SubscriptionControllerTest do "data" => %{"alerts" => %{"mention" => true, "test" => true}}, "subscription" => @sub }) - |> json_response(200) + |> json_response_and_validate_schema(200) [subscription] = Pleroma.Repo.all(Subscription) @@ -74,7 +79,7 @@ defmodule Pleroma.Web.MastodonAPI.SubscriptionControllerTest do assert_error_when_disable_push do conn |> get("/api/v1/push/subscription", %{}) - |> json_response(500) + |> json_response_and_validate_schema(403) end end @@ -82,9 +87,9 @@ defmodule Pleroma.Web.MastodonAPI.SubscriptionControllerTest do res = conn |> get("/api/v1/push/subscription", %{}) - |> json_response(404) + |> json_response_and_validate_schema(404) - assert "Not found" == res + assert %{"error" => "Record not found"} == res end test "returns a user subsciption", %{conn: conn, user: user, token: token} do @@ -98,7 +103,7 @@ defmodule Pleroma.Web.MastodonAPI.SubscriptionControllerTest do res = conn |> get("/api/v1/push/subscription", %{}) - |> json_response(200) + |> json_response_and_validate_schema(200) expect = %{ "alerts" => %{"mention" => true}, @@ -127,7 +132,7 @@ defmodule Pleroma.Web.MastodonAPI.SubscriptionControllerTest do assert_error_when_disable_push do conn |> put("/api/v1/push/subscription", %{data: %{"alerts" => %{"mention" => false}}}) - |> json_response(500) + |> json_response_and_validate_schema(403) end end @@ -137,7 +142,7 @@ defmodule Pleroma.Web.MastodonAPI.SubscriptionControllerTest do |> put("/api/v1/push/subscription", %{ data: %{"alerts" => %{"mention" => false, "follow" => true}} }) - |> json_response(200) + |> json_response_and_validate_schema(200) expect = %{ "alerts" => %{"follow" => true, "mention" => false}, @@ -155,7 +160,7 @@ defmodule Pleroma.Web.MastodonAPI.SubscriptionControllerTest do assert_error_when_disable_push do conn |> delete("/api/v1/push/subscription", %{}) - |> json_response(500) + |> json_response_and_validate_schema(403) end end @@ -163,9 +168,9 @@ defmodule Pleroma.Web.MastodonAPI.SubscriptionControllerTest do res = conn |> delete("/api/v1/push/subscription", %{}) - |> json_response(404) + |> json_response_and_validate_schema(404) - assert "Not found" == res + assert %{"error" => "Record not found"} == res end test "returns empty result and delete user subsciption", %{ @@ -183,7 +188,7 @@ defmodule Pleroma.Web.MastodonAPI.SubscriptionControllerTest do res = conn |> delete("/api/v1/push/subscription", %{}) - |> json_response(200) + |> json_response_and_validate_schema(200) assert %{} == res refute Pleroma.Repo.get(Subscription, subscription.id) diff --git a/test/web/mastodon_api/controllers/suggestion_controller_test.exs b/test/web/mastodon_api/controllers/suggestion_controller_test.exs index c697a39f8..7f08e187c 100644 --- a/test/web/mastodon_api/controllers/suggestion_controller_test.exs +++ b/test/web/mastodon_api/controllers/suggestion_controller_test.exs @@ -5,41 +5,13 @@ defmodule Pleroma.Web.MastodonAPI.SuggestionControllerTest do use Pleroma.Web.ConnCase - alias Pleroma.Config - - import Pleroma.Factory - import Tesla.Mock - setup do: oauth_access(["read"]) - setup %{user: user} do - other_user = insert(:user) - host = Config.get([Pleroma.Web.Endpoint, :url, :host]) - url500 = "http://test500?#{host}&#{user.nickname}" - url200 = "http://test200?#{host}&#{user.nickname}" - - mock(fn - %{method: :get, url: ^url500} -> - %Tesla.Env{status: 500, body: "bad request"} - - %{method: :get, url: ^url200} -> - %Tesla.Env{ - status: 200, - body: - ~s([{"acct":"yj455","avatar":"https://social.heldscal.la/avatar/201.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/201.jpeg"}, {"acct":"#{ - other_user.ap_id - }","avatar":"https://social.heldscal.la/avatar/202.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/202.jpeg"}]) - } - end) - - [other_user: other_user] - end - test "returns empty result", %{conn: conn} do res = conn |> get("/api/v1/suggestions") - |> json_response(200) + |> json_response_and_validate_schema(200) assert res == [] end diff --git a/test/web/mastodon_api/controllers/timeline_controller_test.exs b/test/web/mastodon_api/controllers/timeline_controller_test.exs index 6fedb4223..f8b9289f3 100644 --- a/test/web/mastodon_api/controllers/timeline_controller_test.exs +++ b/test/web/mastodon_api/controllers/timeline_controller_test.exs @@ -20,35 +20,104 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do describe "home" do setup do: oauth_access(["read:statuses"]) + test "does NOT render account/pleroma/relationship if this is disabled by default", %{ + user: user, + conn: conn + } do + clear_config([:extensions, :output_relationships_in_statuses_by_default], false) + + other_user = insert(:user) + + {:ok, _} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"}) + + response = + conn + |> assign(:user, user) + |> get("/api/v1/timelines/home") + |> json_response_and_validate_schema(200) + + assert Enum.all?(response, fn n -> + get_in(n, ["account", "pleroma", "relationship"]) == %{} + end) + end + test "the home timeline", %{user: user, conn: conn} do - following = insert(:user) + uri = "/api/v1/timelines/home?with_relationships=1" - {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"}) + following = insert(:user, nickname: "followed") + third_user = insert(:user, nickname: "repeated") - ret_conn = get(conn, "/api/v1/timelines/home") + {:ok, _activity} = CommonAPI.post(following, %{status: "post"}) + {:ok, activity} = CommonAPI.post(third_user, %{status: "repeated post"}) + {:ok, _, _} = CommonAPI.repeat(activity.id, following) - assert Enum.empty?(json_response(ret_conn, :ok)) + ret_conn = get(conn, uri) - {:ok, _user} = User.follow(user, following) + assert Enum.empty?(json_response_and_validate_schema(ret_conn, :ok)) - conn = get(conn, "/api/v1/timelines/home") + {:ok, _user} = User.follow(user, following) - assert [%{"content" => "test"}] = json_response(conn, :ok) + ret_conn = get(conn, uri) + + assert [ + %{ + "reblog" => %{ + "content" => "repeated post", + "account" => %{ + "pleroma" => %{ + "relationship" => %{"following" => false, "followed_by" => false} + } + } + }, + "account" => %{"pleroma" => %{"relationship" => %{"following" => true}}} + }, + %{ + "content" => "post", + "account" => %{ + "acct" => "followed", + "pleroma" => %{"relationship" => %{"following" => true}} + } + } + ] = json_response_and_validate_schema(ret_conn, :ok) + + {:ok, _user} = User.follow(third_user, user) + + ret_conn = get(conn, uri) + + assert [ + %{ + "reblog" => %{ + "content" => "repeated post", + "account" => %{ + "acct" => "repeated", + "pleroma" => %{ + "relationship" => %{"following" => false, "followed_by" => true} + } + } + }, + "account" => %{"pleroma" => %{"relationship" => %{"following" => true}}} + }, + %{ + "content" => "post", + "account" => %{ + "acct" => "followed", + "pleroma" => %{"relationship" => %{"following" => true}} + } + } + ] = json_response_and_validate_schema(ret_conn, :ok) end test "the home timeline when the direct messages are excluded", %{user: user, conn: conn} do - {:ok, public_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"}) - {:ok, direct_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"}) + {:ok, public_activity} = CommonAPI.post(user, %{status: ".", visibility: "public"}) + {:ok, direct_activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"}) - {:ok, unlisted_activity} = - CommonAPI.post(user, %{"status" => ".", "visibility" => "unlisted"}) + {:ok, unlisted_activity} = CommonAPI.post(user, %{status: ".", visibility: "unlisted"}) - {:ok, private_activity} = - CommonAPI.post(user, %{"status" => ".", "visibility" => "private"}) + {:ok, private_activity} = CommonAPI.post(user, %{status: ".", visibility: "private"}) - conn = get(conn, "/api/v1/timelines/home", %{"exclude_visibilities" => ["direct"]}) + conn = get(conn, "/api/v1/timelines/home?exclude_visibilities[]=direct") - assert status_ids = json_response(conn, :ok) |> Enum.map(& &1["id"]) + assert status_ids = json_response_and_validate_schema(conn, :ok) |> Enum.map(& &1["id"]) assert public_activity.id in status_ids assert unlisted_activity.id in status_ids assert private_activity.id in status_ids @@ -61,33 +130,33 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do test "the public timeline", %{conn: conn} do following = insert(:user) - {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"}) + {:ok, _activity} = CommonAPI.post(following, %{status: "test"}) _activity = insert(:note_activity, local: false) - conn = get(conn, "/api/v1/timelines/public", %{"local" => "False"}) + conn = get(conn, "/api/v1/timelines/public?local=False") - assert length(json_response(conn, :ok)) == 2 + assert length(json_response_and_validate_schema(conn, :ok)) == 2 - conn = get(build_conn(), "/api/v1/timelines/public", %{"local" => "True"}) + conn = get(build_conn(), "/api/v1/timelines/public?local=True") - assert [%{"content" => "test"}] = json_response(conn, :ok) + assert [%{"content" => "test"}] = json_response_and_validate_schema(conn, :ok) - conn = get(build_conn(), "/api/v1/timelines/public", %{"local" => "1"}) + conn = get(build_conn(), "/api/v1/timelines/public?local=1") - assert [%{"content" => "test"}] = json_response(conn, :ok) + assert [%{"content" => "test"}] = json_response_and_validate_schema(conn, :ok) end test "the public timeline includes only public statuses for an authenticated user" do %{user: user, conn: conn} = oauth_access(["read:statuses"]) - {:ok, _activity} = CommonAPI.post(user, %{"status" => "test"}) - {:ok, _activity} = CommonAPI.post(user, %{"status" => "test", "visibility" => "private"}) - {:ok, _activity} = CommonAPI.post(user, %{"status" => "test", "visibility" => "unlisted"}) - {:ok, _activity} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"}) + {:ok, _activity} = CommonAPI.post(user, %{status: "test"}) + {:ok, _activity} = CommonAPI.post(user, %{status: "test", visibility: "private"}) + {:ok, _activity} = CommonAPI.post(user, %{status: "test", visibility: "unlisted"}) + {:ok, _activity} = CommonAPI.post(user, %{status: "test", visibility: "direct"}) res_conn = get(conn, "/api/v1/timelines/public") - assert length(json_response(res_conn, 200)) == 1 + assert length(json_response_and_validate_schema(res_conn, 200)) == 1 end end @@ -105,15 +174,15 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do setup do: clear_config([:restrict_unauthenticated, :timelines, :federated], true) test "if user is unauthenticated", %{conn: conn} do - res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "true"}) + res_conn = get(conn, "/api/v1/timelines/public?local=true") - assert json_response(res_conn, :unauthorized) == %{ + assert json_response_and_validate_schema(res_conn, :unauthorized) == %{ "error" => "authorization required for timeline view" } - res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "false"}) + res_conn = get(conn, "/api/v1/timelines/public?local=false") - assert json_response(res_conn, :unauthorized) == %{ + assert json_response_and_validate_schema(res_conn, :unauthorized) == %{ "error" => "authorization required for timeline view" } end @@ -121,11 +190,11 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do test "if user is authenticated" do %{conn: conn} = oauth_access(["read:statuses"]) - res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "true"}) - assert length(json_response(res_conn, 200)) == 1 + res_conn = get(conn, "/api/v1/timelines/public?local=true") + assert length(json_response_and_validate_schema(res_conn, 200)) == 1 - res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "false"}) - assert length(json_response(res_conn, 200)) == 2 + res_conn = get(conn, "/api/v1/timelines/public?local=false") + assert length(json_response_and_validate_schema(res_conn, 200)) == 2 end end @@ -135,24 +204,24 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do setup do: clear_config([:restrict_unauthenticated, :timelines, :local], true) test "if user is unauthenticated", %{conn: conn} do - res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "true"}) + res_conn = get(conn, "/api/v1/timelines/public?local=true") - assert json_response(res_conn, :unauthorized) == %{ + assert json_response_and_validate_schema(res_conn, :unauthorized) == %{ "error" => "authorization required for timeline view" } - res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "false"}) - assert length(json_response(res_conn, 200)) == 2 + res_conn = get(conn, "/api/v1/timelines/public?local=false") + assert length(json_response_and_validate_schema(res_conn, 200)) == 2 end test "if user is authenticated", %{conn: _conn} do %{conn: conn} = oauth_access(["read:statuses"]) - res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "true"}) - assert length(json_response(res_conn, 200)) == 1 + res_conn = get(conn, "/api/v1/timelines/public?local=true") + assert length(json_response_and_validate_schema(res_conn, 200)) == 1 - res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "false"}) - assert length(json_response(res_conn, 200)) == 2 + res_conn = get(conn, "/api/v1/timelines/public?local=false") + assert length(json_response_and_validate_schema(res_conn, 200)) == 2 end end @@ -162,12 +231,12 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do setup do: clear_config([:restrict_unauthenticated, :timelines, :federated], true) test "if user is unauthenticated", %{conn: conn} do - res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "true"}) - assert length(json_response(res_conn, 200)) == 1 + res_conn = get(conn, "/api/v1/timelines/public?local=true") + assert length(json_response_and_validate_schema(res_conn, 200)) == 1 - res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "false"}) + res_conn = get(conn, "/api/v1/timelines/public?local=false") - assert json_response(res_conn, :unauthorized) == %{ + assert json_response_and_validate_schema(res_conn, :unauthorized) == %{ "error" => "authorization required for timeline view" } end @@ -175,11 +244,11 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do test "if user is authenticated", %{conn: _conn} do %{conn: conn} = oauth_access(["read:statuses"]) - res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "true"}) - assert length(json_response(res_conn, 200)) == 1 + res_conn = get(conn, "/api/v1/timelines/public?local=true") + assert length(json_response_and_validate_schema(res_conn, 200)) == 1 - res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "false"}) - assert length(json_response(res_conn, 200)) == 2 + res_conn = get(conn, "/api/v1/timelines/public?local=false") + assert length(json_response_and_validate_schema(res_conn, 200)) == 2 end end @@ -192,14 +261,14 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do {:ok, direct} = CommonAPI.post(user_one, %{ - "status" => "Hi @#{user_two.nickname}!", - "visibility" => "direct" + status: "Hi @#{user_two.nickname}!", + visibility: "direct" }) {:ok, _follower_only} = CommonAPI.post(user_one, %{ - "status" => "Hi @#{user_two.nickname}!", - "visibility" => "private" + status: "Hi @#{user_two.nickname}!", + visibility: "private" }) conn_user_two = @@ -210,7 +279,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do # Only direct should be visible here res_conn = get(conn_user_two, "api/v1/timelines/direct") - [status] = json_response(res_conn, :ok) + assert [status] = json_response_and_validate_schema(res_conn, :ok) assert %{"visibility" => "direct"} = status assert status["url"] != direct.data["id"] @@ -222,33 +291,34 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do |> assign(:token, insert(:oauth_token, user: user_one, scopes: ["read:statuses"])) |> get("api/v1/timelines/direct") - [status] = json_response(res_conn, :ok) + [status] = json_response_and_validate_schema(res_conn, :ok) assert %{"visibility" => "direct"} = status # Both should be visible here res_conn = get(conn_user_two, "api/v1/timelines/home") - [_s1, _s2] = json_response(res_conn, :ok) + [_s1, _s2] = json_response_and_validate_schema(res_conn, :ok) # Test pagination Enum.each(1..20, fn _ -> {:ok, _} = CommonAPI.post(user_one, %{ - "status" => "Hi @#{user_two.nickname}!", - "visibility" => "direct" + status: "Hi @#{user_two.nickname}!", + visibility: "direct" }) end) res_conn = get(conn_user_two, "api/v1/timelines/direct") - statuses = json_response(res_conn, :ok) + statuses = json_response_and_validate_schema(res_conn, :ok) assert length(statuses) == 20 - res_conn = - get(conn_user_two, "api/v1/timelines/direct", %{max_id: List.last(statuses)["id"]}) + max_id = List.last(statuses)["id"] + + res_conn = get(conn_user_two, "api/v1/timelines/direct?max_id=#{max_id}") - [status] = json_response(res_conn, :ok) + assert [status] = json_response_and_validate_schema(res_conn, :ok) assert status["url"] != direct.data["id"] end @@ -261,19 +331,19 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do {:ok, _blocked_direct} = CommonAPI.post(blocked, %{ - "status" => "Hi @#{blocker.nickname}!", - "visibility" => "direct" + status: "Hi @#{blocker.nickname}!", + visibility: "direct" }) {:ok, direct} = CommonAPI.post(other_user, %{ - "status" => "Hi @#{blocker.nickname}!", - "visibility" => "direct" + status: "Hi @#{blocker.nickname}!", + visibility: "direct" }) res_conn = get(conn, "api/v1/timelines/direct") - [status] = json_response(res_conn, :ok) + [status] = json_response_and_validate_schema(res_conn, :ok) assert status["id"] == direct.id end end @@ -283,14 +353,14 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do test "list timeline", %{user: user, conn: conn} do other_user = insert(:user) - {:ok, _activity_one} = CommonAPI.post(user, %{"status" => "Marisa is cute."}) - {:ok, activity_two} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."}) + {:ok, _activity_one} = CommonAPI.post(user, %{status: "Marisa is cute."}) + {:ok, activity_two} = CommonAPI.post(other_user, %{status: "Marisa is cute."}) {:ok, list} = Pleroma.List.create("name", user) {:ok, list} = Pleroma.List.follow(list, other_user) conn = get(conn, "/api/v1/timelines/list/#{list.id}") - assert [%{"id" => id}] = json_response(conn, :ok) + assert [%{"id" => id}] = json_response_and_validate_schema(conn, :ok) assert id == to_string(activity_two.id) end @@ -300,12 +370,12 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do conn: conn } do other_user = insert(:user) - {:ok, activity_one} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."}) + {:ok, activity_one} = CommonAPI.post(other_user, %{status: "Marisa is cute."}) {:ok, _activity_two} = CommonAPI.post(other_user, %{ - "status" => "Marisa is cute.", - "visibility" => "private" + status: "Marisa is cute.", + visibility: "private" }) {:ok, list} = Pleroma.List.create("name", user) @@ -313,7 +383,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do conn = get(conn, "/api/v1/timelines/list/#{list.id}") - assert [%{"id" => id}] = json_response(conn, :ok) + assert [%{"id" => id}] = json_response_and_validate_schema(conn, :ok) assert id == to_string(activity_one.id) end @@ -326,18 +396,18 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do test "hashtag timeline", %{conn: conn} do following = insert(:user) - {:ok, activity} = CommonAPI.post(following, %{"status" => "test #2hu"}) + {:ok, activity} = CommonAPI.post(following, %{status: "test #2hu"}) nconn = get(conn, "/api/v1/timelines/tag/2hu") - assert [%{"id" => id}] = json_response(nconn, :ok) + assert [%{"id" => id}] = json_response_and_validate_schema(nconn, :ok) assert id == to_string(activity.id) # works for different capitalization too nconn = get(conn, "/api/v1/timelines/tag/2HU") - assert [%{"id" => id}] = json_response(nconn, :ok) + assert [%{"id" => id}] = json_response_and_validate_schema(nconn, :ok) assert id == to_string(activity.id) end @@ -345,26 +415,25 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do test "multi-hashtag timeline", %{conn: conn} do user = insert(:user) - {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"}) - {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"}) - {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"}) + {:ok, activity_test} = CommonAPI.post(user, %{status: "#test"}) + {:ok, activity_test1} = CommonAPI.post(user, %{status: "#test #test1"}) + {:ok, activity_none} = CommonAPI.post(user, %{status: "#test #none"}) - any_test = get(conn, "/api/v1/timelines/tag/test", %{"any" => ["test1"]}) + any_test = get(conn, "/api/v1/timelines/tag/test?any[]=test1") - [status_none, status_test1, status_test] = json_response(any_test, :ok) + [status_none, status_test1, status_test] = json_response_and_validate_schema(any_test, :ok) assert to_string(activity_test.id) == status_test["id"] assert to_string(activity_test1.id) == status_test1["id"] assert to_string(activity_none.id) == status_none["id"] - restricted_test = - get(conn, "/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]}) + restricted_test = get(conn, "/api/v1/timelines/tag/test?all[]=test1&none[]=none") - assert [status_test1] == json_response(restricted_test, :ok) + assert [status_test1] == json_response_and_validate_schema(restricted_test, :ok) - all_test = get(conn, "/api/v1/timelines/tag/test", %{"all" => ["none"]}) + all_test = get(conn, "/api/v1/timelines/tag/test?all[]=none") - assert [status_none] == json_response(all_test, :ok) + assert [status_none] == json_response_and_validate_schema(all_test, :ok) end end end diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs index 75f184242..bb4bc4396 100644 --- a/test/web/mastodon_api/mastodon_api_controller_test.exs +++ b/test/web/mastodon_api/mastodon_api_controller_test.exs @@ -7,35 +7,28 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do describe "empty_array/2 (stubs)" do test "GET /api/v1/accounts/:id/identity_proofs" do - %{user: user, conn: conn} = oauth_access(["n/a"]) + %{user: user, conn: conn} = oauth_access(["read:accounts"]) - res = - conn - |> assign(:user, user) - |> get("/api/v1/accounts/#{user.id}/identity_proofs") - |> json_response(200) - - assert res == [] + assert [] == + conn + |> get("/api/v1/accounts/#{user.id}/identity_proofs") + |> json_response(200) end test "GET /api/v1/endorsements" do %{conn: conn} = oauth_access(["read:accounts"]) - res = - conn - |> get("/api/v1/endorsements") - |> json_response(200) - - assert res == [] + assert [] == + conn + |> get("/api/v1/endorsements") + |> json_response(200) end test "GET /api/v1/trends", %{conn: conn} do - res = - conn - |> get("/api/v1/trends") - |> json_response(200) - - assert res == [] + assert [] == + conn + |> get("/api/v1/trends") + |> json_response(200) end end end diff --git a/test/web/mastodon_api/mastodon_api_test.exs b/test/web/mastodon_api/mastodon_api_test.exs index cb971806a..a7f9c5205 100644 --- a/test/web/mastodon_api/mastodon_api_test.exs +++ b/test/web/mastodon_api/mastodon_api_test.exs @@ -75,9 +75,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPITest do User.subscribe(subscriber, user) - {:ok, status} = CommonAPI.post(user, %{"status" => "Akariiiin"}) + {:ok, status} = CommonAPI.post(user, %{status: "Akariiiin"}) - {:ok, status1} = CommonAPI.post(user, %{"status" => "Magi"}) + {:ok, status1} = CommonAPI.post(user, %{status: "Magi"}) {:ok, [notification]} = Notification.create_notifications(status) {:ok, [notification1]} = Notification.create_notifications(status1) res = MastodonAPI.get_notifications(subscriber) diff --git a/test/web/mastodon_api/views/account_view_test.exs b/test/web/mastodon_api/views/account_view_test.exs index 983886c6b..69ddbb5d4 100644 --- a/test/web/mastodon_api/views/account_view_test.exs +++ b/test/web/mastodon_api/views/account_view_test.exs @@ -4,22 +4,21 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do use Pleroma.DataCase - import Pleroma.Factory + alias Pleroma.User + alias Pleroma.UserRelationship alias Pleroma.Web.CommonAPI alias Pleroma.Web.MastodonAPI.AccountView - test "Represent a user account" do - source_data = %{ - "tag" => [ - %{ - "type" => "Emoji", - "icon" => %{"url" => "/file.png"}, - "name" => ":karjalanpiirakka:" - } - ] - } + import Pleroma.Factory + import Tesla.Mock + + setup do + mock(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + test "Represent a user account" do background_image = %{ "url" => [%{"href" => "https://example.com/images/asuka_hospital.png"}] } @@ -28,13 +27,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do insert(:user, %{ follower_count: 3, note_count: 5, - source_data: source_data, background: background_image, nickname: "shp@shitposter.club", name: ":karjalanpiirakka: shp", bio: - "<script src=\"invalid-html\"></script><span>valid html</span>. a<br>b<br/>c<br >d<br />f", - inserted_at: ~N[2017-08-15 15:47:06.597036] + "<script src=\"invalid-html\"></script><span>valid html</span>. a<br>b<br/>c<br >d<br />f '&<>\"", + inserted_at: ~N[2017-08-15 15:47:06.597036], + emoji: %{"karjalanpiirakka" => "/file.png"} }) expected = %{ @@ -47,7 +46,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do followers_count: 3, following_count: 0, statuses_count: 5, - note: "<span>valid html</span>. a<br/>b<br/>c<br/>d<br/>f", + note: "<span>valid html</span>. a<br/>b<br/>c<br/>d<br/>f '&<>"", url: user.ap_id, avatar: "http://localhost:4001/images/avi.png", avatar_static: "http://localhost:4001/images/avi.png", @@ -64,7 +63,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do fields: [], bot: false, source: %{ - note: "valid html. a\nb\nc\nd\nf", + note: "valid html. a\nb\nc\nd\nf '&<>\"", sensitive: false, pleroma: %{ actor_type: "Person", @@ -94,7 +93,14 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do test "Represent the user account for the account owner" do user = insert(:user) - notification_settings = %Pleroma.User.NotificationSetting{} + notification_settings = %{ + followers: true, + follows: true, + non_followers: true, + non_follows: true, + privacy_option: false + } + privacy = user.default_scope assert %{ @@ -108,7 +114,6 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do insert(:user, %{ follower_count: 3, note_count: 5, - source_data: %{}, actor_type: "Service", nickname: "shp@shitposter.club", inserted_at: ~N[2017-08-15 15:47:06.597036] @@ -161,6 +166,17 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do assert expected == AccountView.render("show.json", %{user: user}) end + test "Represent a Funkwhale channel" do + {:ok, user} = + User.get_or_fetch_by_ap_id( + "https://channels.tests.funkwhale.audio/federation/actors/compositions" + ) + + assert represented = AccountView.render("show.json", %{user: user}) + assert represented.acct == "compositions@channels.tests.funkwhale.audio" + assert represented.url == "https://channels.tests.funkwhale.audio/channels/compositions" + end + test "Represent a deactivated user for an admin" do admin = insert(:user, is_admin: true) deactivated_user = insert(:user, deactivated: true) @@ -182,6 +198,32 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do end describe "relationship" do + defp test_relationship_rendering(user, other_user, expected_result) do + opts = %{user: user, target: other_user, relationships: nil} + assert expected_result == AccountView.render("relationship.json", opts) + + relationships_opt = UserRelationship.view_relationships_option(user, [other_user]) + opts = Map.put(opts, :relationships, relationships_opt) + assert expected_result == AccountView.render("relationship.json", opts) + + assert [expected_result] == + AccountView.render("relationships.json", %{user: user, targets: [other_user]}) + end + + @blank_response %{ + following: false, + followed_by: false, + blocking: false, + blocked_by: false, + muting: false, + muting_notifications: false, + subscribing: false, + requested: false, + domain_blocking: false, + showing_reblogs: true, + endorsed: false + } + test "represent a relationship for the following and followed user" do user = insert(:user) other_user = insert(:user) @@ -192,23 +234,21 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do {:ok, _user_relationships} = User.mute(user, other_user, true) {:ok, _reblog_mute} = CommonAPI.hide_reblogs(user, other_user) - expected = %{ - id: to_string(other_user.id), - following: true, - followed_by: true, - blocking: false, - blocked_by: false, - muting: true, - muting_notifications: true, - subscribing: true, - requested: false, - domain_blocking: false, - showing_reblogs: false, - endorsed: false - } - - assert expected == - AccountView.render("relationship.json", %{user: user, target: other_user}) + expected = + Map.merge( + @blank_response, + %{ + following: true, + followed_by: true, + muting: true, + muting_notifications: true, + subscribing: true, + showing_reblogs: false, + id: to_string(other_user.id) + } + ) + + test_relationship_rendering(user, other_user, expected) end test "represent a relationship for the blocking and blocked user" do @@ -220,23 +260,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do {:ok, _user_relationship} = User.block(user, other_user) {:ok, _user_relationship} = User.block(other_user, user) - expected = %{ - id: to_string(other_user.id), - following: false, - followed_by: false, - blocking: true, - blocked_by: true, - muting: false, - muting_notifications: false, - subscribing: false, - requested: false, - domain_blocking: false, - showing_reblogs: true, - endorsed: false - } + expected = + Map.merge( + @blank_response, + %{following: false, blocking: true, blocked_by: true, id: to_string(other_user.id)} + ) - assert expected == - AccountView.render("relationship.json", %{user: user, target: other_user}) + test_relationship_rendering(user, other_user, expected) end test "represent a relationship for the user blocking a domain" do @@ -245,8 +275,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do {:ok, user} = User.block_domain(user, "bad.site") - assert %{domain_blocking: true, blocking: false} = - AccountView.render("relationship.json", %{user: user, target: other_user}) + expected = + Map.merge( + @blank_response, + %{domain_blocking: true, blocking: false, id: to_string(other_user.id)} + ) + + test_relationship_rendering(user, other_user, expected) end test "represent a relationship for the user with a pending follow request" do @@ -257,23 +292,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do user = User.get_cached_by_id(user.id) other_user = User.get_cached_by_id(other_user.id) - expected = %{ - id: to_string(other_user.id), - following: false, - followed_by: false, - blocking: false, - blocked_by: false, - muting: false, - muting_notifications: false, - subscribing: false, - requested: true, - domain_blocking: false, - showing_reblogs: true, - endorsed: false - } + expected = + Map.merge( + @blank_response, + %{requested: true, following: false, id: to_string(other_user.id)} + ) - assert expected == - AccountView.render("relationship.json", %{user: user, target: other_user}) + test_relationship_rendering(user, other_user, expected) end end @@ -282,7 +307,6 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do insert(:user, %{ follower_count: 0, note_count: 5, - source_data: %{}, actor_type: "Service", nickname: "shp@shitposter.club", inserted_at: ~N[2017-08-15 15:47:06.597036] @@ -435,8 +459,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do {:ok, _activity} = CommonAPI.post(other_user, %{ - "status" => "Hey @#{user.nickname}.", - "visibility" => "direct" + status: "Hey @#{user.nickname}.", + visibility: "direct" }) user = User.get_cached_by_ap_id(user.ap_id) @@ -449,6 +473,24 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do :unread_conversation_count ] == 1 end + + test "shows unread_count only to the account owner" do + user = insert(:user) + insert_list(7, :notification, user: user) + other_user = insert(:user) + + user = User.get_cached_by_ap_id(user.ap_id) + + assert AccountView.render( + "show.json", + %{user: user, for: other_user} + )[:pleroma][:unread_notifications_count] == nil + + assert AccountView.render( + "show.json", + %{user: user, for: user} + )[:pleroma][:unread_notifications_count] == 7 + end end describe "follow requests counter" do diff --git a/test/web/mastodon_api/views/conversation_view_test.exs b/test/web/mastodon_api/views/conversation_view_test.exs index dbf3c51e2..6f84366f8 100644 --- a/test/web/mastodon_api/views/conversation_view_test.exs +++ b/test/web/mastodon_api/views/conversation_view_test.exs @@ -16,7 +16,7 @@ defmodule Pleroma.Web.MastodonAPI.ConversationViewTest do other_user = insert(:user) {:ok, activity} = - CommonAPI.post(user, %{"status" => "hey @#{other_user.nickname}", "visibility" => "direct"}) + CommonAPI.post(user, %{status: "hey @#{other_user.nickname}", visibility: "direct"}) [participation] = Participation.for_user_with_last_activity_id(user) diff --git a/test/web/mastodon_api/views/marker_view_test.exs b/test/web/mastodon_api/views/marker_view_test.exs index 893cf8857..48a0a6d33 100644 --- a/test/web/mastodon_api/views/marker_view_test.exs +++ b/test/web/mastodon_api/views/marker_view_test.exs @@ -8,19 +8,21 @@ defmodule Pleroma.Web.MastodonAPI.MarkerViewTest do import Pleroma.Factory test "returns markers" do - marker1 = insert(:marker, timeline: "notifications", last_read_id: "17") + marker1 = insert(:marker, timeline: "notifications", last_read_id: "17", unread_count: 5) marker2 = insert(:marker, timeline: "home", last_read_id: "42") assert MarkerView.render("markers.json", %{markers: [marker1, marker2]}) == %{ "home" => %{ last_read_id: "42", updated_at: NaiveDateTime.to_iso8601(marker2.updated_at), - version: 0 + version: 0, + pleroma: %{unread_count: 0} }, "notifications" => %{ last_read_id: "17", updated_at: NaiveDateTime.to_iso8601(marker1.updated_at), - version: 0 + version: 0, + pleroma: %{unread_count: 5} } } end diff --git a/test/web/mastodon_api/views/notification_view_test.exs b/test/web/mastodon_api/views/notification_view_test.exs index d04c3022f..04a774d17 100644 --- a/test/web/mastodon_api/views/notification_view_test.exs +++ b/test/web/mastodon_api/views/notification_view_test.exs @@ -16,10 +16,25 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do alias Pleroma.Web.MastodonAPI.StatusView import Pleroma.Factory + defp test_notifications_rendering(notifications, user, expected_result) do + result = NotificationView.render("index.json", %{notifications: notifications, for: user}) + + assert expected_result == result + + result = + NotificationView.render("index.json", %{ + notifications: notifications, + for: user, + relationships: nil + }) + + assert expected_result == result + end + test "Mention notification" do user = insert(:user) mentioned_user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "hey @#{mentioned_user.nickname}"}) + {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{mentioned_user.nickname}"}) {:ok, [notification]} = Notification.create_notifications(activity) user = User.get_cached_by_id(user.id) @@ -32,17 +47,14 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do created_at: Utils.to_masto_date(notification.inserted_at) } - result = - NotificationView.render("index.json", %{notifications: [notification], for: mentioned_user}) - - assert [expected] == result + test_notifications_rendering([notification], mentioned_user, [expected]) end test "Favourite notification" do user = insert(:user) another_user = insert(:user) - {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"}) - {:ok, favorite_activity, _object} = CommonAPI.favorite(create_activity.id, another_user) + {:ok, create_activity} = CommonAPI.post(user, %{status: "hey"}) + {:ok, favorite_activity} = CommonAPI.favorite(another_user, create_activity.id) {:ok, [notification]} = Notification.create_notifications(favorite_activity) create_activity = Activity.get_by_id(create_activity.id) @@ -55,15 +67,13 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do created_at: Utils.to_masto_date(notification.inserted_at) } - result = NotificationView.render("index.json", %{notifications: [notification], for: user}) - - assert [expected] == result + test_notifications_rendering([notification], user, [expected]) end test "Reblog notification" do user = insert(:user) another_user = insert(:user) - {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"}) + {:ok, create_activity} = CommonAPI.post(user, %{status: "hey"}) {:ok, reblog_activity, _object} = CommonAPI.repeat(create_activity.id, another_user) {:ok, [notification]} = Notification.create_notifications(reblog_activity) reblog_activity = Activity.get_by_id(create_activity.id) @@ -77,9 +87,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do created_at: Utils.to_masto_date(notification.inserted_at) } - result = NotificationView.render("index.json", %{notifications: [notification], for: user}) - - assert [expected] == result + test_notifications_rendering([notification], user, [expected]) end test "Follow notification" do @@ -96,23 +104,32 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do created_at: Utils.to_masto_date(notification.inserted_at) } - result = - NotificationView.render("index.json", %{notifications: [notification], for: followed}) - - assert [expected] == result + test_notifications_rendering([notification], followed, [expected]) User.perform(:delete, follower) notification = Notification |> Repo.one() |> Repo.preload(:activity) - assert [] == - NotificationView.render("index.json", %{notifications: [notification], for: followed}) + test_notifications_rendering([notification], followed, []) end + @tag capture_log: true test "Move notification" do old_user = insert(:user) new_user = insert(:user, also_known_as: [old_user.ap_id]) follower = insert(:user) + old_user_url = old_user.ap_id + + body = + File.read!("test/fixtures/users_mock/localhost.json") + |> String.replace("{{nickname}}", old_user.nickname) + |> Jason.encode!() + + Tesla.Mock.mock(fn + %{method: :get, url: ^old_user_url} -> + %Tesla.Env{status: 200, body: body} + end) + User.follow(follower, old_user) Pleroma.Web.ActivityPub.ActivityPub.move(old_user, new_user) Pleroma.Tests.ObanHelpers.perform_all() @@ -131,16 +148,15 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do created_at: Utils.to_masto_date(notification.inserted_at) } - assert [expected] == - NotificationView.render("index.json", %{notifications: [notification], for: follower}) + test_notifications_rendering([notification], follower, [expected]) end test "EmojiReact notification" do user = insert(:user) other_user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "#cofe"}) - {:ok, _activity, _} = CommonAPI.react_with_emoji(activity.id, other_user, "☕") + {:ok, activity} = CommonAPI.post(user, %{status: "#cofe"}) + {:ok, _activity} = CommonAPI.react_with_emoji(activity.id, other_user, "☕") activity = Repo.get(Activity, activity.id) @@ -158,7 +174,6 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do created_at: Utils.to_masto_date(notification.inserted_at) } - assert expected == - NotificationView.render("show.json", %{notification: notification, for: user}) + test_notifications_rendering([notification], user, [expected]) end end diff --git a/test/web/mastodon_api/views/poll_view_test.exs b/test/web/mastodon_api/views/poll_view_test.exs index 6211fa888..76672f36c 100644 --- a/test/web/mastodon_api/views/poll_view_test.exs +++ b/test/web/mastodon_api/views/poll_view_test.exs @@ -22,10 +22,10 @@ defmodule Pleroma.Web.MastodonAPI.PollViewTest do {:ok, activity} = CommonAPI.post(user, %{ - "status" => "Is Tenshi eating a corndog cute?", - "poll" => %{ - "options" => ["absolutely!", "sure", "yes", "why are you even asking?"], - "expires_in" => 20 + status: "Is Tenshi eating a corndog cute?", + poll: %{ + options: ["absolutely!", "sure", "yes", "why are you even asking?"], + expires_in: 20 } }) @@ -43,7 +43,8 @@ defmodule Pleroma.Web.MastodonAPI.PollViewTest do %{title: "why are you even asking?", votes_count: 0} ], voted: false, - votes_count: 0 + votes_count: 0, + voters_count: nil } result = PollView.render("show.json", %{object: object}) @@ -61,17 +62,28 @@ defmodule Pleroma.Web.MastodonAPI.PollViewTest do {:ok, activity} = CommonAPI.post(user, %{ - "status" => "Which Mastodon developer is your favourite?", - "poll" => %{ - "options" => ["Gargron", "Eugen"], - "expires_in" => 20, - "multiple" => true + status: "Which Mastodon developer is your favourite?", + poll: %{ + options: ["Gargron", "Eugen"], + expires_in: 20, + multiple: true } }) + voter = insert(:user) + object = Object.normalize(activity) - assert %{multiple: true} = PollView.render("show.json", %{object: object}) + {:ok, _votes, object} = CommonAPI.vote(voter, object, [0, 1]) + + assert match?( + %{ + multiple: true, + voters_count: 1, + votes_count: 2 + }, + PollView.render("show.json", %{object: object}) + ) end test "detects emoji" do @@ -79,10 +91,10 @@ defmodule Pleroma.Web.MastodonAPI.PollViewTest do {:ok, activity} = CommonAPI.post(user, %{ - "status" => "What's with the smug face?", - "poll" => %{ - "options" => [":blank: sip", ":blank::blank: sip", ":blank::blank::blank: sip"], - "expires_in" => 20 + status: "What's with the smug face?", + poll: %{ + options: [":blank: sip", ":blank::blank: sip", ":blank::blank::blank: sip"], + expires_in: 20 } }) @@ -97,11 +109,11 @@ defmodule Pleroma.Web.MastodonAPI.PollViewTest do {:ok, activity} = CommonAPI.post(user, %{ - "status" => "Which input devices do you use?", - "poll" => %{ - "options" => ["mouse", "trackball", "trackpoint"], - "multiple" => true, - "expires_in" => 20 + status: "Which input devices do you use?", + poll: %{ + options: ["mouse", "trackball", "trackpoint"], + multiple: true, + expires_in: 20 } }) diff --git a/test/web/mastodon_api/views/scheduled_activity_view_test.exs b/test/web/mastodon_api/views/scheduled_activity_view_test.exs index 0c0987593..fbfd873ef 100644 --- a/test/web/mastodon_api/views/scheduled_activity_view_test.exs +++ b/test/web/mastodon_api/views/scheduled_activity_view_test.exs @@ -14,7 +14,7 @@ defmodule Pleroma.Web.MastodonAPI.ScheduledActivityViewTest do test "A scheduled activity with a media attachment" do user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "hi"}) + {:ok, activity} = CommonAPI.post(user, %{status: "hi"}) scheduled_at = NaiveDateTime.utc_now() @@ -47,7 +47,7 @@ defmodule Pleroma.Web.MastodonAPI.ScheduledActivityViewTest do expected = %{ id: to_string(scheduled_activity.id), media_attachments: - %{"media_ids" => [upload.id]} + %{media_ids: [upload.id]} |> Utils.attachments_from_ids() |> Enum.map(&StatusView.render("attachment.json", %{attachment: &1})), params: %{ diff --git a/test/web/mastodon_api/views/status_view_test.exs b/test/web/mastodon_api/views/status_view_test.exs index 191895c6f..ffad65b01 100644 --- a/test/web/mastodon_api/views/status_view_test.exs +++ b/test/web/mastodon_api/views/status_view_test.exs @@ -12,12 +12,15 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do alias Pleroma.Object alias Pleroma.Repo alias Pleroma.User + alias Pleroma.UserRelationship alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI.Utils alias Pleroma.Web.MastodonAPI.AccountView alias Pleroma.Web.MastodonAPI.StatusView + import Pleroma.Factory import Tesla.Mock + import OpenApiSpex.TestAssertions setup do mock(fn env -> apply(HttpRequestMock, :request, [env]) end) @@ -28,14 +31,16 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do user = insert(:user) other_user = insert(:user) third_user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "dae cofe??"}) + {:ok, activity} = CommonAPI.post(user, %{status: "dae cofe??"}) - {:ok, _, _} = CommonAPI.react_with_emoji(activity.id, user, "☕") - {:ok, _, _} = CommonAPI.react_with_emoji(activity.id, third_user, "🍵") - {:ok, _, _} = CommonAPI.react_with_emoji(activity.id, other_user, "☕") + {:ok, _} = CommonAPI.react_with_emoji(activity.id, user, "☕") + {:ok, _} = CommonAPI.react_with_emoji(activity.id, third_user, "🍵") + {:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "☕") activity = Repo.get(Activity, activity.id) status = StatusView.render("show.json", activity: activity) + assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec()) + assert status[:pleroma][:emoji_reactions] == [ %{name: "☕", count: 2, me: false}, %{name: "🍵", count: 1, me: false} @@ -43,6 +48,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do status = StatusView.render("show.json", activity: activity, for: user) + assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec()) + assert status[:pleroma][:emoji_reactions] == [ %{name: "☕", count: 2, me: true}, %{name: "🍵", count: 1, me: false} @@ -52,7 +59,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do test "loads and returns the direct conversation id when given the `with_direct_conversation_id` option" do user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!", "visibility" => "direct"}) + {:ok, activity} = CommonAPI.post(user, %{status: "Hey @shp!", visibility: "direct"}) [participation] = Participation.for_user(user) status = @@ -66,12 +73,13 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do status = StatusView.render("show.json", activity: activity, for: user) assert status[:pleroma][:direct_conversation_id] == nil + assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec()) end test "returns the direct conversation id when given the `direct_conversation_id` option" do user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!", "visibility" => "direct"}) + {:ok, activity} = CommonAPI.post(user, %{status: "Hey @shp!", visibility: "direct"}) [participation] = Participation.for_user(user) status = @@ -82,16 +90,34 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do ) assert status[:pleroma][:direct_conversation_id] == participation.id + assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec()) end test "returns a temporary ap_id based user for activities missing db users" do user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!", "visibility" => "direct"}) + {:ok, activity} = CommonAPI.post(user, %{status: "Hey @shp!", visibility: "direct"}) Repo.delete(user) Cachex.clear(:user_cache) + finger_url = + "https://localhost/.well-known/webfinger?resource=acct:#{user.nickname}@localhost" + + Tesla.Mock.mock_global(fn + %{method: :get, url: "http://localhost/.well-known/host-meta"} -> + %Tesla.Env{status: 404, body: ""} + + %{method: :get, url: "https://localhost/.well-known/host-meta"} -> + %Tesla.Env{status: 404, body: ""} + + %{ + method: :get, + url: ^finger_url + } -> + %Tesla.Env{status: 404, body: ""} + end) + %{account: ms_user} = StatusView.render("show.json", activity: activity) assert ms_user.acct == "erroruser@example.com" @@ -100,7 +126,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do test "tries to get a user by nickname if fetching by ap_id doesn't work" do user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!", "visibility" => "direct"}) + {:ok, activity} = CommonAPI.post(user, %{status: "Hey @shp!", visibility: "direct"}) {:ok, user} = user @@ -112,6 +138,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do result = StatusView.render("show.json", activity: activity) assert result[:account][:id] == to_string(user.id) + assert_schema(result, "Status", Pleroma.Web.ApiSpec.spec()) end test "a note with null content" do @@ -130,6 +157,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do status = StatusView.render("show.json", %{activity: note}) assert status.content == "" + assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec()) end test "a note activity" do @@ -203,6 +231,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do } assert status == expected + assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec()) end test "tells if the message is muted for some reason" do @@ -211,14 +240,25 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do {:ok, _user_relationships} = User.mute(user, other_user) - {:ok, activity} = CommonAPI.post(other_user, %{"status" => "test"}) - status = StatusView.render("show.json", %{activity: activity}) + {:ok, activity} = CommonAPI.post(other_user, %{status: "test"}) + + relationships_opt = UserRelationship.view_relationships_option(user, [other_user]) + opts = %{activity: activity} + status = StatusView.render("show.json", opts) assert status.muted == false + assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec()) - status = StatusView.render("show.json", %{activity: activity, for: user}) + status = StatusView.render("show.json", Map.put(opts, :relationships, relationships_opt)) + assert status.muted == false + + for_opts = %{activity: activity, for: user} + status = StatusView.render("show.json", for_opts) + assert status.muted == true + status = StatusView.render("show.json", Map.put(for_opts, :relationships, relationships_opt)) assert status.muted == true + assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec()) end test "tells if the message is thread muted" do @@ -227,7 +267,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do {:ok, _user_relationships} = User.mute(user, other_user) - {:ok, activity} = CommonAPI.post(other_user, %{"status" => "test"}) + {:ok, activity} = CommonAPI.post(other_user, %{status: "test"}) status = StatusView.render("show.json", %{activity: activity, for: user}) assert status.pleroma.thread_muted == false @@ -242,7 +282,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do test "tells if the status is bookmarked" do user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "Cute girls doing cute things"}) + {:ok, activity} = CommonAPI.post(user, %{status: "Cute girls doing cute things"}) status = StatusView.render("show.json", %{activity: activity}) assert status.bookmarked == false @@ -264,8 +304,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do note = insert(:note_activity) user = insert(:user) - {:ok, activity} = - CommonAPI.post(user, %{"status" => "he", "in_reply_to_status_id" => note.id}) + {:ok, activity} = CommonAPI.post(user, %{status: "he", in_reply_to_status_id: note.id}) status = StatusView.render("show.json", %{activity: activity}) @@ -280,12 +319,14 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do user = insert(:user) mentioned = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "hi @#{mentioned.nickname}"}) + {:ok, activity} = CommonAPI.post(user, %{status: "hi @#{mentioned.nickname}"}) status = StatusView.render("show.json", %{activity: activity}) assert status.mentions == Enum.map([mentioned], fn u -> AccountView.render("mention.json", %{user: u}) end) + + assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec()) end test "create mentions from the 'to' field" do @@ -374,11 +415,17 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do pleroma: %{mime_type: "image/png"} } + api_spec = Pleroma.Web.ApiSpec.spec() + assert expected == StatusView.render("attachment.json", %{attachment: object}) + assert_schema(expected, "Attachment", api_spec) # If theres a "id", use that instead of the generated one object = Map.put(object, "id", 2) - assert %{id: "2"} = StatusView.render("attachment.json", %{attachment: object}) + result = StatusView.render("attachment.json", %{attachment: object}) + + assert %{id: "2"} = result + assert_schema(result, "Attachment", api_spec) end test "put the url advertised in the Activity in to the url attribute" do @@ -402,6 +449,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do assert represented[:id] == to_string(reblog.id) assert represented[:reblog][:id] == to_string(activity.id) assert represented[:emojis] == [] + assert_schema(represented, "Status", Pleroma.Web.ApiSpec.spec()) end test "a peertube video" do @@ -418,6 +466,23 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do assert represented[:id] == to_string(activity.id) assert length(represented[:media_attachments]) == 1 + assert_schema(represented, "Status", Pleroma.Web.ApiSpec.spec()) + end + + test "funkwhale audio" do + user = insert(:user) + + {:ok, object} = + Pleroma.Object.Fetcher.fetch_object_from_id( + "https://channels.tests.funkwhale.audio/federation/music/uploads/42342395-0208-4fee-a38d-259a6dae0871" + ) + + %Activity{} = activity = Activity.get_create_by_object_ap_id(object.data["id"]) + + represented = StatusView.render("show.json", %{for: user, activity: activity}) + + assert represented[:id] == to_string(activity.id) + assert length(represented[:media_attachments]) == 1 end test "a Mobilizon event" do @@ -517,13 +582,15 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do {:ok, activity} = CommonAPI.post(user, %{ - "status" => "drink more water" + status: "drink more water" }) result = StatusView.render("show.json", %{activity: activity, for: other_user}) assert result[:account][:pleroma][:relationship] == AccountView.render("relationship.json", %{user: other_user, target: user}) + + assert_schema(result, "Status", Pleroma.Web.ApiSpec.spec()) end test "embeds a relationship in the account in reposts" do @@ -532,7 +599,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do {:ok, activity} = CommonAPI.post(user, %{ - "status" => "˙˙ɐʎns" + status: "˙˙ɐʎns" }) {:ok, activity, _object} = CommonAPI.repeat(activity.id, other_user) @@ -544,6 +611,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do assert result[:reblog][:account][:pleroma][:relationship] == AccountView.render("relationship.json", %{user: user, target: user}) + + assert_schema(result, "Status", Pleroma.Web.ApiSpec.spec()) end test "visibility/list" do @@ -551,8 +620,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do {:ok, list} = Pleroma.List.create("foo", user) - {:ok, activity} = - CommonAPI.post(user, %{"status" => "foobar", "visibility" => "list:#{list.id}"}) + {:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"}) status = StatusView.render("show.json", activity: activity) @@ -566,5 +634,6 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do assert status.length == listen_activity.data["object"]["length"] assert status.title == listen_activity.data["object"]["title"] + assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec()) end end diff --git a/test/web/mastodon_api/views/push_subscription_view_test.exs b/test/web/mastodon_api/views/subscription_view_test.exs index 10c6082a5..981524c0e 100644 --- a/test/web/mastodon_api/views/push_subscription_view_test.exs +++ b/test/web/mastodon_api/views/subscription_view_test.exs @@ -2,10 +2,10 @@ # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Web.MastodonAPI.PushSubscriptionViewTest do +defmodule Pleroma.Web.MastodonAPI.SubscriptionViewTest do use Pleroma.DataCase import Pleroma.Factory - alias Pleroma.Web.MastodonAPI.PushSubscriptionView, as: View + alias Pleroma.Web.MastodonAPI.SubscriptionView, as: View alias Pleroma.Web.Push test "Represent a subscription" do @@ -18,6 +18,6 @@ defmodule Pleroma.Web.MastodonAPI.PushSubscriptionViewTest do server_key: Keyword.get(Push.vapid_config(), :public_key) } - assert expected == View.render("push_subscription.json", %{subscription: subscription}) + assert expected == View.render("show.json", %{subscription: subscription}) end end diff --git a/test/web/metadata/metadata_test.exs b/test/web/metadata/metadata_test.exs new file mode 100644 index 000000000..3f8b29e58 --- /dev/null +++ b/test/web/metadata/metadata_test.exs @@ -0,0 +1,25 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MetadataTest do + use Pleroma.DataCase, async: true + + import Pleroma.Factory + + describe "restrict indexing remote users" do + test "for remote user" do + user = insert(:user, local: false) + + assert Pleroma.Web.Metadata.build_tags(%{user: user}) =~ + "<meta content=\"noindex, noarchive\" name=\"robots\">" + end + + test "for local user" do + user = insert(:user) + + refute Pleroma.Web.Metadata.build_tags(%{user: user}) =~ + "<meta content=\"noindex, noarchive\" name=\"robots\">" + end + end +end diff --git a/test/web/metadata/restrict_indexing_test.exs b/test/web/metadata/restrict_indexing_test.exs new file mode 100644 index 000000000..aad0bac42 --- /dev/null +++ b/test/web/metadata/restrict_indexing_test.exs @@ -0,0 +1,21 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Metadata.Providers.RestrictIndexingTest do + use ExUnit.Case, async: true + + describe "build_tags/1" do + test "for remote user" do + assert Pleroma.Web.Metadata.Providers.RestrictIndexing.build_tags(%{ + user: %Pleroma.User{local: false} + }) == [{:meta, [name: "robots", content: "noindex, noarchive"], []}] + end + + test "for local user" do + assert Pleroma.Web.Metadata.Providers.RestrictIndexing.build_tags(%{ + user: %Pleroma.User{local: true} + }) == [] + end + end +end diff --git a/test/web/metadata/twitter_card_test.exs b/test/web/metadata/twitter_card_test.exs index 9e9c6853a..10931b5ba 100644 --- a/test/web/metadata/twitter_card_test.exs +++ b/test/web/metadata/twitter_card_test.exs @@ -30,7 +30,7 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCardTest do test "it uses summary twittercard if post has no attachment" do user = insert(:user, name: "Jimmy Hendriks", bio: "born 19 March 1994") - {:ok, activity} = CommonAPI.post(user, %{"status" => "HI"}) + {:ok, activity} = CommonAPI.post(user, %{status: "HI"}) note = insert(:note, %{ @@ -56,7 +56,7 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCardTest do test "it renders avatar not attachment if post is nsfw and unfurl_nsfw is disabled" do Pleroma.Config.put([Pleroma.Web.Metadata, :unfurl_nsfw], false) user = insert(:user, name: "Jimmy Hendriks", bio: "born 19 March 1994") - {:ok, activity} = CommonAPI.post(user, %{"status" => "HI"}) + {:ok, activity} = CommonAPI.post(user, %{status: "HI"}) note = insert(:note, %{ @@ -100,7 +100,7 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCardTest do test "it renders supported types of attachments and skips unknown types" do user = insert(:user, name: "Jimmy Hendriks", bio: "born 19 March 1994") - {:ok, activity} = CommonAPI.post(user, %{"status" => "HI"}) + {:ok, activity} = CommonAPI.post(user, %{status: "HI"}) note = insert(:note, %{ diff --git a/test/web/mongooseim/mongoose_im_controller_test.exs b/test/web/mongooseim/mongoose_im_controller_test.exs index 291ae54fc..5176cde84 100644 --- a/test/web/mongooseim/mongoose_im_controller_test.exs +++ b/test/web/mongooseim/mongoose_im_controller_test.exs @@ -9,6 +9,7 @@ defmodule Pleroma.Web.MongooseIMController do test "/user_exists", %{conn: conn} do _user = insert(:user, nickname: "lain") _remote_user = insert(:user, nickname: "alice", local: false) + _deactivated_user = insert(:user, nickname: "konata", deactivated: true) res = conn @@ -30,10 +31,24 @@ defmodule Pleroma.Web.MongooseIMController do |> json_response(404) assert res == false + + res = + conn + |> get(mongoose_im_path(conn, :user_exists), user: "konata") + |> json_response(404) + + assert res == false end test "/check_password", %{conn: conn} do - user = insert(:user, password_hash: Comeonin.Pbkdf2.hashpwsalt("cool")) + user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt("cool")) + + _deactivated_user = + insert(:user, + nickname: "konata", + deactivated: true, + password_hash: Pbkdf2.hash_pwd_salt("cool") + ) res = conn @@ -51,6 +66,13 @@ defmodule Pleroma.Web.MongooseIMController do res = conn + |> get(mongoose_im_path(conn, :check_password), user: "konata", pass: "cool") + |> json_response(404) + + assert res == false + + res = + conn |> get(mongoose_im_path(conn, :check_password), user: "nobody", pass: "cool") |> json_response(404) diff --git a/test/web/node_info_test.exs b/test/web/node_info_test.exs index 43f322606..9bcc07b37 100644 --- a/test/web/node_info_test.exs +++ b/test/web/node_info_test.exs @@ -7,6 +7,8 @@ defmodule Pleroma.Web.NodeInfoTest do import Pleroma.Factory + alias Pleroma.Config + setup do: clear_config([:mrf_simple]) setup do: clear_config(:instance) @@ -47,7 +49,7 @@ defmodule Pleroma.Web.NodeInfoTest do assert result = json_response(conn, 200) - assert Pleroma.Config.get([Pleroma.User, :restricted_nicknames]) == + assert Config.get([Pleroma.User, :restricted_nicknames]) == result["metadata"]["restrictedNicknames"] end @@ -65,10 +67,10 @@ defmodule Pleroma.Web.NodeInfoTest do end test "returns fieldsLimits field", %{conn: conn} do - Pleroma.Config.put([:instance, :max_account_fields], 10) - Pleroma.Config.put([:instance, :max_remote_account_fields], 15) - Pleroma.Config.put([:instance, :account_field_name_length], 255) - Pleroma.Config.put([:instance, :account_field_value_length], 2048) + Config.put([:instance, :max_account_fields], 10) + Config.put([:instance, :max_remote_account_fields], 15) + Config.put([:instance, :account_field_name_length], 255) + Config.put([:instance, :account_field_value_length], 2048) response = conn @@ -82,8 +84,8 @@ defmodule Pleroma.Web.NodeInfoTest do end test "it returns the safe_dm_mentions feature if enabled", %{conn: conn} do - option = Pleroma.Config.get([:instance, :safe_dm_mentions]) - Pleroma.Config.put([:instance, :safe_dm_mentions], true) + option = Config.get([:instance, :safe_dm_mentions]) + Config.put([:instance, :safe_dm_mentions], true) response = conn @@ -92,7 +94,7 @@ defmodule Pleroma.Web.NodeInfoTest do assert "safe_dm_mentions" in response["metadata"]["features"] - Pleroma.Config.put([:instance, :safe_dm_mentions], false) + Config.put([:instance, :safe_dm_mentions], false) response = conn @@ -101,14 +103,14 @@ defmodule Pleroma.Web.NodeInfoTest do refute "safe_dm_mentions" in response["metadata"]["features"] - Pleroma.Config.put([:instance, :safe_dm_mentions], option) + Config.put([:instance, :safe_dm_mentions], option) end describe "`metadata/federation/enabled`" do setup do: clear_config([:instance, :federating]) test "it shows if federation is enabled/disabled", %{conn: conn} do - Pleroma.Config.put([:instance, :federating], true) + Config.put([:instance, :federating], true) response = conn @@ -117,7 +119,7 @@ defmodule Pleroma.Web.NodeInfoTest do assert response["metadata"]["federation"]["enabled"] == true - Pleroma.Config.put([:instance, :federating], false) + Config.put([:instance, :federating], false) response = conn @@ -128,15 +130,39 @@ defmodule Pleroma.Web.NodeInfoTest do end end + test "it shows default features flags", %{conn: conn} do + response = + conn + |> get("/nodeinfo/2.1.json") + |> json_response(:ok) + + default_features = [ + "pleroma_api", + "mastodon_api", + "mastodon_api_streaming", + "polls", + "pleroma_explicit_addressing", + "shareable_emoji_packs", + "multifetch", + "pleroma_emoji_reactions", + "pleroma:api/v1/notifications:include_types_filter" + ] + + assert MapSet.subset?( + MapSet.new(default_features), + MapSet.new(response["metadata"]["features"]) + ) + end + test "it shows MRF transparency data if enabled", %{conn: conn} do - config = Pleroma.Config.get([:instance, :rewrite_policy]) - Pleroma.Config.put([:instance, :rewrite_policy], [Pleroma.Web.ActivityPub.MRF.SimplePolicy]) + config = Config.get([:instance, :rewrite_policy]) + Config.put([:instance, :rewrite_policy], [Pleroma.Web.ActivityPub.MRF.SimplePolicy]) - option = Pleroma.Config.get([:instance, :mrf_transparency]) - Pleroma.Config.put([:instance, :mrf_transparency], true) + option = Config.get([:instance, :mrf_transparency]) + Config.put([:instance, :mrf_transparency], true) simple_config = %{"reject" => ["example.com"]} - Pleroma.Config.put(:mrf_simple, simple_config) + Config.put(:mrf_simple, simple_config) response = conn @@ -145,25 +171,25 @@ defmodule Pleroma.Web.NodeInfoTest do assert response["metadata"]["federation"]["mrf_simple"] == simple_config - Pleroma.Config.put([:instance, :rewrite_policy], config) - Pleroma.Config.put([:instance, :mrf_transparency], option) - Pleroma.Config.put(:mrf_simple, %{}) + Config.put([:instance, :rewrite_policy], config) + Config.put([:instance, :mrf_transparency], option) + Config.put(:mrf_simple, %{}) end test "it performs exclusions from MRF transparency data if configured", %{conn: conn} do - config = Pleroma.Config.get([:instance, :rewrite_policy]) - Pleroma.Config.put([:instance, :rewrite_policy], [Pleroma.Web.ActivityPub.MRF.SimplePolicy]) + config = Config.get([:instance, :rewrite_policy]) + Config.put([:instance, :rewrite_policy], [Pleroma.Web.ActivityPub.MRF.SimplePolicy]) - option = Pleroma.Config.get([:instance, :mrf_transparency]) - Pleroma.Config.put([:instance, :mrf_transparency], true) + option = Config.get([:instance, :mrf_transparency]) + Config.put([:instance, :mrf_transparency], true) - exclusions = Pleroma.Config.get([:instance, :mrf_transparency_exclusions]) - Pleroma.Config.put([:instance, :mrf_transparency_exclusions], ["other.site"]) + exclusions = Config.get([:instance, :mrf_transparency_exclusions]) + Config.put([:instance, :mrf_transparency_exclusions], ["other.site"]) simple_config = %{"reject" => ["example.com", "other.site"]} expected_config = %{"reject" => ["example.com"]} - Pleroma.Config.put(:mrf_simple, simple_config) + Config.put(:mrf_simple, simple_config) response = conn @@ -173,9 +199,9 @@ defmodule Pleroma.Web.NodeInfoTest do assert response["metadata"]["federation"]["mrf_simple"] == expected_config assert response["metadata"]["federation"]["exclusions"] == true - Pleroma.Config.put([:instance, :rewrite_policy], config) - Pleroma.Config.put([:instance, :mrf_transparency], option) - Pleroma.Config.put([:instance, :mrf_transparency_exclusions], exclusions) - Pleroma.Config.put(:mrf_simple, %{}) + Config.put([:instance, :rewrite_policy], config) + Config.put([:instance, :mrf_transparency], option) + Config.put([:instance, :mrf_transparency_exclusions], exclusions) + Config.put(:mrf_simple, %{}) end end diff --git a/test/web/oauth/ldap_authorization_test.exs b/test/web/oauth/ldap_authorization_test.exs index a8fe8a841..011642c08 100644 --- a/test/web/oauth/ldap_authorization_test.exs +++ b/test/web/oauth/ldap_authorization_test.exs @@ -19,7 +19,7 @@ defmodule Pleroma.Web.OAuth.LDAPAuthorizationTest do @tag @skip test "authorizes the existing user using LDAP credentials" do password = "testpassword" - user = insert(:user, password_hash: Comeonin.Pbkdf2.hashpwsalt(password)) + user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt(password)) app = insert(:oauth_app, scopes: ["read", "write"]) host = Pleroma.Config.get([:ldap, :host]) |> to_charlist @@ -104,7 +104,7 @@ defmodule Pleroma.Web.OAuth.LDAPAuthorizationTest do @tag @skip test "falls back to the default authorization when LDAP is unavailable" do password = "testpassword" - user = insert(:user, password_hash: Comeonin.Pbkdf2.hashpwsalt(password)) + user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt(password)) app = insert(:oauth_app, scopes: ["read", "write"]) host = Pleroma.Config.get([:ldap, :host]) |> to_charlist @@ -148,7 +148,7 @@ defmodule Pleroma.Web.OAuth.LDAPAuthorizationTest do @tag @skip test "disallow authorization for wrong LDAP credentials" do password = "testpassword" - user = insert(:user, password_hash: Comeonin.Pbkdf2.hashpwsalt(password)) + user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt(password)) app = insert(:oauth_app, scopes: ["read", "write"]) host = Pleroma.Config.get([:ldap, :host]) |> to_charlist diff --git a/test/web/oauth/mfa_controller_test.exs b/test/web/oauth/mfa_controller_test.exs new file mode 100644 index 000000000..3c341facd --- /dev/null +++ b/test/web/oauth/mfa_controller_test.exs @@ -0,0 +1,306 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.OAuth.MFAControllerTest do + use Pleroma.Web.ConnCase + import Pleroma.Factory + + alias Pleroma.MFA + alias Pleroma.MFA.BackupCodes + alias Pleroma.MFA.TOTP + alias Pleroma.Repo + alias Pleroma.Web.OAuth.Authorization + alias Pleroma.Web.OAuth.OAuthController + + setup %{conn: conn} do + otp_secret = TOTP.generate_secret() + + user = + insert(:user, + multi_factor_authentication_settings: %MFA.Settings{ + enabled: true, + backup_codes: [Pbkdf2.hash_pwd_salt("test-code")], + totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true} + } + ) + + app = insert(:oauth_app) + {:ok, conn: conn, user: user, app: app} + end + + describe "show" do + setup %{conn: conn, user: user, app: app} do + mfa_token = + insert(:mfa_token, + user: user, + authorization: build(:oauth_authorization, app: app, scopes: ["write"]) + ) + + {:ok, conn: conn, mfa_token: mfa_token} + end + + test "GET /oauth/mfa renders mfa forms", %{conn: conn, mfa_token: mfa_token} do + conn = + get( + conn, + "/oauth/mfa", + %{ + "mfa_token" => mfa_token.token, + "state" => "a_state", + "redirect_uri" => "http://localhost:8080/callback" + } + ) + + assert response = html_response(conn, 200) + assert response =~ "Two-factor authentication" + assert response =~ mfa_token.token + assert response =~ "http://localhost:8080/callback" + end + + test "GET /oauth/mfa renders mfa recovery forms", %{conn: conn, mfa_token: mfa_token} do + conn = + get( + conn, + "/oauth/mfa", + %{ + "mfa_token" => mfa_token.token, + "state" => "a_state", + "redirect_uri" => "http://localhost:8080/callback", + "challenge_type" => "recovery" + } + ) + + assert response = html_response(conn, 200) + assert response =~ "Two-factor recovery" + assert response =~ mfa_token.token + assert response =~ "http://localhost:8080/callback" + end + end + + describe "verify" do + setup %{conn: conn, user: user, app: app} do + mfa_token = + insert(:mfa_token, + user: user, + authorization: build(:oauth_authorization, app: app, scopes: ["write"]) + ) + + {:ok, conn: conn, user: user, mfa_token: mfa_token, app: app} + end + + test "POST /oauth/mfa/verify, verify totp code", %{ + conn: conn, + user: user, + mfa_token: mfa_token, + app: app + } do + otp_token = TOTP.generate_token(user.multi_factor_authentication_settings.totp.secret) + + conn = + conn + |> post("/oauth/mfa/verify", %{ + "mfa" => %{ + "mfa_token" => mfa_token.token, + "challenge_type" => "totp", + "code" => otp_token, + "state" => "a_state", + "redirect_uri" => OAuthController.default_redirect_uri(app) + } + }) + + target = redirected_to(conn) + target_url = %URI{URI.parse(target) | query: nil} |> URI.to_string() + query = URI.parse(target).query |> URI.query_decoder() |> Map.new() + assert %{"state" => "a_state", "code" => code} = query + assert target_url == OAuthController.default_redirect_uri(app) + auth = Repo.get_by(Authorization, token: code) + assert auth.scopes == ["write"] + end + + test "POST /oauth/mfa/verify, verify recovery code", %{ + conn: conn, + mfa_token: mfa_token, + app: app + } do + conn = + conn + |> post("/oauth/mfa/verify", %{ + "mfa" => %{ + "mfa_token" => mfa_token.token, + "challenge_type" => "recovery", + "code" => "test-code", + "state" => "a_state", + "redirect_uri" => OAuthController.default_redirect_uri(app) + } + }) + + target = redirected_to(conn) + target_url = %URI{URI.parse(target) | query: nil} |> URI.to_string() + query = URI.parse(target).query |> URI.query_decoder() |> Map.new() + assert %{"state" => "a_state", "code" => code} = query + assert target_url == OAuthController.default_redirect_uri(app) + auth = Repo.get_by(Authorization, token: code) + assert auth.scopes == ["write"] + end + end + + describe "challenge/totp" do + test "returns access token with valid code", %{conn: conn, user: user, app: app} do + otp_token = TOTP.generate_token(user.multi_factor_authentication_settings.totp.secret) + + mfa_token = + insert(:mfa_token, + user: user, + authorization: build(:oauth_authorization, app: app, scopes: ["write"]) + ) + + response = + conn + |> post("/oauth/mfa/challenge", %{ + "mfa_token" => mfa_token.token, + "challenge_type" => "totp", + "code" => otp_token, + "client_id" => app.client_id, + "client_secret" => app.client_secret + }) + |> json_response(:ok) + + ap_id = user.ap_id + + assert match?( + %{ + "access_token" => _, + "expires_in" => 600, + "me" => ^ap_id, + "refresh_token" => _, + "scope" => "write", + "token_type" => "Bearer" + }, + response + ) + end + + test "returns errors when mfa token invalid", %{conn: conn, user: user, app: app} do + otp_token = TOTP.generate_token(user.multi_factor_authentication_settings.totp.secret) + + response = + conn + |> post("/oauth/mfa/challenge", %{ + "mfa_token" => "XXX", + "challenge_type" => "totp", + "code" => otp_token, + "client_id" => app.client_id, + "client_secret" => app.client_secret + }) + |> json_response(400) + + assert response == %{"error" => "Invalid code"} + end + + test "returns error when otp code is invalid", %{conn: conn, user: user, app: app} do + mfa_token = insert(:mfa_token, user: user) + + response = + conn + |> post("/oauth/mfa/challenge", %{ + "mfa_token" => mfa_token.token, + "challenge_type" => "totp", + "code" => "XXX", + "client_id" => app.client_id, + "client_secret" => app.client_secret + }) + |> json_response(400) + + assert response == %{"error" => "Invalid code"} + end + + test "returns error when client credentails is wrong ", %{conn: conn, user: user} do + otp_token = TOTP.generate_token(user.multi_factor_authentication_settings.totp.secret) + mfa_token = insert(:mfa_token, user: user) + + response = + conn + |> post("/oauth/mfa/challenge", %{ + "mfa_token" => mfa_token.token, + "challenge_type" => "totp", + "code" => otp_token, + "client_id" => "xxx", + "client_secret" => "xxx" + }) + |> json_response(400) + + assert response == %{"error" => "Invalid code"} + end + end + + describe "challenge/recovery" do + setup %{conn: conn} do + app = insert(:oauth_app) + {:ok, conn: conn, app: app} + end + + test "returns access token with valid code", %{conn: conn, app: app} do + otp_secret = TOTP.generate_secret() + + [code | _] = backup_codes = BackupCodes.generate() + + hashed_codes = + backup_codes + |> Enum.map(&Pbkdf2.hash_pwd_salt(&1)) + + user = + insert(:user, + multi_factor_authentication_settings: %MFA.Settings{ + enabled: true, + backup_codes: hashed_codes, + totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true} + } + ) + + mfa_token = + insert(:mfa_token, + user: user, + authorization: build(:oauth_authorization, app: app, scopes: ["write"]) + ) + + response = + conn + |> post("/oauth/mfa/challenge", %{ + "mfa_token" => mfa_token.token, + "challenge_type" => "recovery", + "code" => code, + "client_id" => app.client_id, + "client_secret" => app.client_secret + }) + |> json_response(:ok) + + ap_id = user.ap_id + + assert match?( + %{ + "access_token" => _, + "expires_in" => 600, + "me" => ^ap_id, + "refresh_token" => _, + "scope" => "write", + "token_type" => "Bearer" + }, + response + ) + + error_response = + conn + |> post("/oauth/mfa/challenge", %{ + "mfa_token" => mfa_token.token, + "challenge_type" => "recovery", + "code" => code, + "client_id" => app.client_id, + "client_secret" => app.client_secret + }) + |> json_response(400) + + assert error_response == %{"error" => "Invalid code"} + end + end +end diff --git a/test/web/oauth/oauth_controller_test.exs b/test/web/oauth/oauth_controller_test.exs index 0b0972b17..d389e4ce0 100644 --- a/test/web/oauth/oauth_controller_test.exs +++ b/test/web/oauth/oauth_controller_test.exs @@ -6,6 +6,8 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do use Pleroma.Web.ConnCase import Pleroma.Factory + alias Pleroma.MFA + alias Pleroma.MFA.TOTP alias Pleroma.Repo alias Pleroma.User alias Pleroma.Web.OAuth.Authorization @@ -309,7 +311,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do app: app, conn: conn } do - user = insert(:user, password_hash: Comeonin.Pbkdf2.hashpwsalt("testpassword")) + user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt("testpassword")) registration = insert(:registration, user: nil) redirect_uri = OAuthController.default_redirect_uri(app) @@ -340,7 +342,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do app: app, conn: conn } do - user = insert(:user, password_hash: Comeonin.Pbkdf2.hashpwsalt("testpassword")) + user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt("testpassword")) registration = insert(:registration, user: nil) unlisted_redirect_uri = "http://cross-site-request.com" @@ -575,7 +577,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do # In case scope param is missing, expecting _all_ app-supported scopes to be granted for user <- [non_admin, admin], {requested_scopes, expected_scopes} <- - %{scopes_subset => scopes_subset, nil => app_scopes} do + %{scopes_subset => scopes_subset, nil: app_scopes} do conn = post( build_conn(), @@ -604,6 +606,41 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do end end + test "redirect to on two-factor auth page" do + otp_secret = TOTP.generate_secret() + + user = + insert(:user, + multi_factor_authentication_settings: %MFA.Settings{ + enabled: true, + totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true} + } + ) + + app = insert(:oauth_app, scopes: ["read", "write", "follow"]) + + conn = + build_conn() + |> post("/oauth/authorize", %{ + "authorization" => %{ + "name" => user.nickname, + "password" => "test", + "client_id" => app.client_id, + "redirect_uri" => app.redirect_uris, + "scope" => "read write", + "state" => "statepassed" + } + }) + + result = html_response(conn, 200) + + mfa_token = Repo.get_by(MFA.Token, user_id: user.id) + assert result =~ app.redirect_uris + assert result =~ "statepassed" + assert result =~ mfa_token.token + assert result =~ "Two-factor authentication" + end + test "returns 401 for wrong credentials", %{conn: conn} do user = insert(:user) app = insert(:oauth_app) @@ -713,7 +750,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do test "issues a token for `password` grant_type with valid credentials, with full permissions by default" do password = "testpassword" - user = insert(:user, password_hash: Comeonin.Pbkdf2.hashpwsalt(password)) + user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt(password)) app = insert(:oauth_app, scopes: ["read", "write"]) @@ -735,6 +772,46 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do assert token.scopes == app.scopes end + test "issues a mfa token for `password` grant_type, when MFA enabled" do + password = "testpassword" + otp_secret = TOTP.generate_secret() + + user = + insert(:user, + password_hash: Pbkdf2.hash_pwd_salt(password), + multi_factor_authentication_settings: %MFA.Settings{ + enabled: true, + totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true} + } + ) + + app = insert(:oauth_app, scopes: ["read", "write"]) + + response = + build_conn() + |> post("/oauth/token", %{ + "grant_type" => "password", + "username" => user.nickname, + "password" => password, + "client_id" => app.client_id, + "client_secret" => app.client_secret + }) + |> json_response(403) + + assert match?( + %{ + "supported_challenge_types" => "totp", + "mfa_token" => _, + "error" => "mfa_required" + }, + response + ) + + token = Repo.get_by(MFA.Token, token: response["mfa_token"]) + assert token.user_id == user.id + assert token.authorization_id + end + test "issues a token for request with HTTP basic auth client credentials" do user = insert(:user) app = insert(:oauth_app, scopes: ["scope1", "scope2", "scope3"]) @@ -810,7 +887,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do password = "testpassword" {:ok, user} = - insert(:user, password_hash: Comeonin.Pbkdf2.hashpwsalt(password)) + insert(:user, password_hash: Pbkdf2.hash_pwd_salt(password)) |> User.confirmation_changeset(need_confirmation: true) |> User.update_and_set_cache() @@ -838,7 +915,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do user = insert(:user, - password_hash: Comeonin.Pbkdf2.hashpwsalt(password), + password_hash: Pbkdf2.hash_pwd_salt(password), deactivated: true ) @@ -866,7 +943,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do user = insert(:user, - password_hash: Comeonin.Pbkdf2.hashpwsalt(password), + password_hash: Pbkdf2.hash_pwd_salt(password), password_reset_pending: true ) @@ -895,7 +972,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do user = insert(:user, - password_hash: Comeonin.Pbkdf2.hashpwsalt(password), + password_hash: Pbkdf2.hash_pwd_salt(password), confirmation_pending: true ) diff --git a/test/web/ostatus/ostatus_controller_test.exs b/test/web/ostatus/ostatus_controller_test.exs index 6787b414b..bb349cb19 100644 --- a/test/web/ostatus/ostatus_controller_test.exs +++ b/test/web/ostatus/ostatus_controller_test.exs @@ -136,7 +136,7 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do user = insert(:user) - {:ok, like_activity, _} = CommonAPI.favorite(note_activity.id, user) + {:ok, like_activity} = CommonAPI.favorite(user, note_activity.id) assert like_activity.data["type"] == "Like" diff --git a/test/web/pleroma_api/controllers/account_controller_test.exs b/test/web/pleroma_api/controllers/account_controller_test.exs index 2aa87ac30..103997c31 100644 --- a/test/web/pleroma_api/controllers/account_controller_test.exs +++ b/test/web/pleroma_api/controllers/account_controller_test.exs @@ -31,8 +31,28 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do test "resend account confirmation email", %{conn: conn, user: user} do conn + |> put_req_header("content-type", "application/json") |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}") - |> json_response(:no_content) + |> json_response_and_validate_schema(:no_content) + + ObanHelpers.perform_all() + + email = Pleroma.Emails.UserEmail.account_confirmation_email(user) + notify_email = Config.get([:instance, :notify_email]) + instance_name = Config.get([:instance, :name]) + + assert_email_sent( + from: {instance_name, notify_email}, + to: {user.name, user.email}, + html_body: email.html_body + ) + end + + test "resend account confirmation email (with nickname)", %{conn: conn, user: user} do + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/pleroma/accounts/confirmation_resend?nickname=#{user.nickname}") + |> json_response_and_validate_schema(:no_content) ObanHelpers.perform_all() @@ -54,7 +74,10 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do test "user avatar can be set", %{user: user, conn: conn} do avatar_image = File.read!("test/fixtures/avatar_data_uri") - conn = patch(conn, "/api/v1/pleroma/accounts/update_avatar", %{img: avatar_image}) + conn = + conn + |> put_req_header("content-type", "multipart/form-data") + |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: avatar_image}) user = refresh_record(user) @@ -70,17 +93,20 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do ] } = user.avatar - assert %{"url" => _} = json_response(conn, 200) + assert %{"url" => _} = json_response_and_validate_schema(conn, 200) end test "user avatar can be reset", %{user: user, conn: conn} do - conn = patch(conn, "/api/v1/pleroma/accounts/update_avatar", %{img: ""}) + conn = + conn + |> put_req_header("content-type", "multipart/form-data") + |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: ""}) user = User.get_cached_by_id(user.id) assert user.avatar == nil - assert %{"url" => nil} = json_response(conn, 200) + assert %{"url" => nil} = json_response_and_validate_schema(conn, 200) end end @@ -88,21 +114,27 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do setup do: oauth_access(["write:accounts"]) test "can set profile banner", %{user: user, conn: conn} do - conn = patch(conn, "/api/v1/pleroma/accounts/update_banner", %{"banner" => @image}) + conn = + conn + |> put_req_header("content-type", "multipart/form-data") + |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => @image}) user = refresh_record(user) assert user.banner["type"] == "Image" - assert %{"url" => _} = json_response(conn, 200) + assert %{"url" => _} = json_response_and_validate_schema(conn, 200) end test "can reset profile banner", %{user: user, conn: conn} do - conn = patch(conn, "/api/v1/pleroma/accounts/update_banner", %{"banner" => ""}) + conn = + conn + |> put_req_header("content-type", "multipart/form-data") + |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => ""}) user = refresh_record(user) assert user.banner == %{} - assert %{"url" => nil} = json_response(conn, 200) + assert %{"url" => nil} = json_response_and_validate_schema(conn, 200) end end @@ -110,19 +142,26 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do setup do: oauth_access(["write:accounts"]) test "background image can be set", %{user: user, conn: conn} do - conn = patch(conn, "/api/v1/pleroma/accounts/update_background", %{"img" => @image}) + conn = + conn + |> put_req_header("content-type", "multipart/form-data") + |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => @image}) user = refresh_record(user) assert user.background["type"] == "Image" - assert %{"url" => _} = json_response(conn, 200) + # assert %{"url" => _} = json_response(conn, 200) + assert %{"url" => _} = json_response_and_validate_schema(conn, 200) end test "background image can be reset", %{user: user, conn: conn} do - conn = patch(conn, "/api/v1/pleroma/accounts/update_background", %{"img" => ""}) + conn = + conn + |> put_req_header("content-type", "multipart/form-data") + |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => ""}) user = refresh_record(user) assert user.background == %{} - assert %{"url" => nil} = json_response(conn, 200) + assert %{"url" => nil} = json_response_and_validate_schema(conn, 200) end end @@ -138,12 +177,12 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do user: user } do [activity | _] = insert_pair(:note_activity) - CommonAPI.favorite(activity.id, user) + CommonAPI.favorite(user, activity.id) response = conn |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") - |> json_response(:ok) + |> json_response_and_validate_schema(:ok) [like] = response @@ -151,15 +190,18 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do assert like["id"] == activity.id end - test "does not return favorites for specified user_id when user is not logged in", %{ + test "returns favorites for specified user_id when requester is not logged in", %{ user: user } do activity = insert(:note_activity) - CommonAPI.favorite(activity.id, user) + CommonAPI.favorite(user, activity.id) + + response = + build_conn() + |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") + |> json_response_and_validate_schema(200) - build_conn() - |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") - |> json_response(403) + assert length(response) == 1 end test "returns favorited DM only when user is logged in and he is one of recipients", %{ @@ -168,11 +210,11 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do } do {:ok, direct} = CommonAPI.post(current_user, %{ - "status" => "Hi @#{user.nickname}!", - "visibility" => "direct" + status: "Hi @#{user.nickname}!", + visibility: "direct" }) - CommonAPI.favorite(direct.id, user) + CommonAPI.favorite(user, direct.id) for u <- [user, current_user] do response = @@ -180,14 +222,17 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do |> assign(:user, u) |> assign(:token, insert(:oauth_token, user: u, scopes: ["read:favourites"])) |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") - |> json_response(:ok) + |> json_response_and_validate_schema(:ok) assert length(response) == 1 end - build_conn() - |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") - |> json_response(403) + response = + build_conn() + |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") + |> json_response_and_validate_schema(200) + + assert length(response) == 0 end test "does not return others' favorited DM when user is not one of recipients", %{ @@ -198,16 +243,16 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do {:ok, direct} = CommonAPI.post(user_two, %{ - "status" => "Hi @#{user.nickname}!", - "visibility" => "direct" + status: "Hi @#{user.nickname}!", + visibility: "direct" }) - CommonAPI.favorite(direct.id, user) + CommonAPI.favorite(user, direct.id) response = conn |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") - |> json_response(:ok) + |> json_response_and_validate_schema(:ok) assert Enum.empty?(response) end @@ -219,7 +264,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do activities = insert_list(10, :note_activity) Enum.each(activities, fn activity -> - CommonAPI.favorite(activity.id, user) + CommonAPI.favorite(user, activity.id) end) third_activity = Enum.at(activities, 2) @@ -227,11 +272,12 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do response = conn - |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{ - since_id: third_activity.id, - max_id: seventh_activity.id - }) - |> json_response(:ok) + |> get( + "/api/v1/pleroma/accounts/#{user.id}/favourites?since_id=#{third_activity.id}&max_id=#{ + seventh_activity.id + }" + ) + |> json_response_and_validate_schema(:ok) assert length(response) == 3 refute third_activity in response @@ -245,13 +291,13 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do 7 |> insert_list(:note_activity) |> Enum.each(fn activity -> - CommonAPI.favorite(activity.id, user) + CommonAPI.favorite(user, activity.id) end) response = conn - |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"}) - |> json_response(:ok) + |> get("/api/v1/pleroma/accounts/#{user.id}/favourites?limit=3") + |> json_response_and_validate_schema(:ok) assert length(response) == 3 end @@ -263,7 +309,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do response = conn |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") - |> json_response(:ok) + |> json_response_and_validate_schema(:ok) assert Enum.empty?(response) end @@ -271,28 +317,28 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do test "returns 404 error when specified user is not exist", %{conn: conn} do conn = get(conn, "/api/v1/pleroma/accounts/test/favourites") - assert json_response(conn, 404) == %{"error" => "Record not found"} + assert json_response_and_validate_schema(conn, 404) == %{"error" => "Record not found"} end test "returns 403 error when user has hidden own favorites", %{conn: conn} do user = insert(:user, hide_favorites: true) activity = insert(:note_activity) - CommonAPI.favorite(activity.id, user) + CommonAPI.favorite(user, activity.id) conn = get(conn, "/api/v1/pleroma/accounts/#{user.id}/favourites") - assert json_response(conn, 403) == %{"error" => "Can't get favorites"} + assert json_response_and_validate_schema(conn, 403) == %{"error" => "Can't get favorites"} end test "hides favorites for new users by default", %{conn: conn} do user = insert(:user) activity = insert(:note_activity) - CommonAPI.favorite(activity.id, user) + CommonAPI.favorite(user, activity.id) assert user.hide_favorites conn = get(conn, "/api/v1/pleroma/accounts/#{user.id}/favourites") - assert json_response(conn, 403) == %{"error" => "Can't get favorites"} + assert json_response_and_validate_schema(conn, 403) == %{"error" => "Can't get favorites"} end end @@ -306,11 +352,12 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do |> assign(:user, user) |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe") - assert %{"id" => _id, "subscribing" => true} = json_response(ret_conn, 200) + assert %{"id" => _id, "subscribing" => true} = + json_response_and_validate_schema(ret_conn, 200) conn = post(conn, "/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe") - assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200) + assert %{"id" => _id, "subscribing" => false} = json_response_and_validate_schema(conn, 200) end end @@ -320,7 +367,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do conn = post(conn, "/api/v1/pleroma/accounts/target_id/subscribe") - assert %{"error" => "Record not found"} = json_response(conn, 404) + assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn, 404) end end @@ -330,7 +377,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do conn = post(conn, "/api/v1/pleroma/accounts/target_id/unsubscribe") - assert %{"error" => "Record not found"} = json_response(conn, 404) + assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn, 404) end end end diff --git a/test/web/pleroma_api/controllers/emoji_api_controller_test.exs b/test/web/pleroma_api/controllers/emoji_api_controller_test.exs index 435fb6592..d343256fe 100644 --- a/test/web/pleroma_api/controllers/emoji_api_controller_test.exs +++ b/test/web/pleroma_api/controllers/emoji_api_controller_test.exs @@ -8,213 +8,298 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do import Tesla.Mock import Pleroma.Factory - @emoji_dir_path Path.join( - Pleroma.Config.get!([:instance, :static_dir]), - "emoji" - ) + @emoji_path Path.join( + Pleroma.Config.get!([:instance, :static_dir]), + "emoji" + ) setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], false) - test "shared & non-shared pack information in list_packs is ok" do - conn = build_conn() - resp = conn |> get(emoji_api_path(conn, :list_packs)) |> json_response(200) - - assert Map.has_key?(resp, "test_pack") + setup do + admin = insert(:user, is_admin: true) + token = insert(:oauth_admin_token, user: admin) - pack = resp["test_pack"] + admin_conn = + build_conn() + |> assign(:user, admin) + |> assign(:token, token) - assert Map.has_key?(pack["pack"], "download-sha256") - assert pack["pack"]["can-download"] + Pleroma.Emoji.reload() + {:ok, %{admin_conn: admin_conn}} + end - assert pack["files"] == %{"blank" => "blank.png"} + test "GET /api/pleroma/emoji/packs", %{conn: conn} do + resp = conn |> get("/api/pleroma/emoji/packs") |> json_response(200) - # Non-shared pack + shared = resp["test_pack"] + assert shared["files"] == %{"blank" => "blank.png"} + assert Map.has_key?(shared["pack"], "download-sha256") + assert shared["pack"]["can-download"] + assert shared["pack"]["share-files"] - assert Map.has_key?(resp, "test_pack_nonshared") + non_shared = resp["test_pack_nonshared"] + assert non_shared["pack"]["share-files"] == false + assert non_shared["pack"]["can-download"] == false + end - pack = resp["test_pack_nonshared"] + describe "GET /api/pleroma/emoji/packs/remote" do + test "shareable instance", %{admin_conn: admin_conn, conn: conn} do + resp = + conn + |> get("/api/pleroma/emoji/packs") + |> json_response(200) - refute pack["pack"]["shared"] - refute pack["pack"]["can-download"] - end + mock(fn + %{method: :get, url: "https://example.com/.well-known/nodeinfo"} -> + json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]}) - test "listing remote packs" do - admin = insert(:user, is_admin: true) - %{conn: conn} = oauth_access(["admin:write"], user: admin) + %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} -> + json(%{metadata: %{features: ["shareable_emoji_packs"]}}) - resp = - build_conn() - |> get(emoji_api_path(conn, :list_packs)) - |> json_response(200) + %{method: :get, url: "https://example.com/api/pleroma/emoji/packs"} -> + json(resp) + end) - mock(fn - %{method: :get, url: "https://example.com/.well-known/nodeinfo"} -> - json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]}) + assert admin_conn + |> get("/api/pleroma/emoji/packs/remote", %{ + url: "https://example.com" + }) + |> json_response(200) == resp + end - %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} -> - json(%{metadata: %{features: ["shareable_emoji_packs"]}}) + test "non shareable instance", %{admin_conn: admin_conn} do + mock(fn + %{method: :get, url: "https://example.com/.well-known/nodeinfo"} -> + json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]}) - %{method: :get, url: "https://example.com/api/pleroma/emoji/packs"} -> - json(resp) - end) + %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} -> + json(%{metadata: %{features: []}}) + end) - assert conn - |> post(emoji_api_path(conn, :list_from), %{instance_address: "https://example.com"}) - |> json_response(200) == resp + assert admin_conn + |> get("/api/pleroma/emoji/packs/remote", %{url: "https://example.com"}) + |> json_response(500) == %{ + "error" => "The requested instance does not support sharing emoji packs" + } + end end - test "downloading a shared pack from download_shared" do - conn = build_conn() + describe "GET /api/pleroma/emoji/packs/:name/archive" do + test "download shared pack", %{conn: conn} do + resp = + conn + |> get("/api/pleroma/emoji/packs/test_pack/archive") + |> response(200) + + {:ok, arch} = :zip.unzip(resp, [:memory]) - resp = - conn - |> get(emoji_api_path(conn, :download_shared, "test_pack")) - |> response(200) + assert Enum.find(arch, fn {n, _} -> n == 'pack.json' end) + assert Enum.find(arch, fn {n, _} -> n == 'blank.png' end) + end - {:ok, arch} = :zip.unzip(resp, [:memory]) + test "non existing pack", %{conn: conn} do + assert conn + |> get("/api/pleroma/emoji/packs/test_pack_for_import/archive") + |> json_response(:not_found) == %{ + "error" => "Pack test_pack_for_import does not exist" + } + end - assert Enum.find(arch, fn {n, _} -> n == 'pack.json' end) - assert Enum.find(arch, fn {n, _} -> n == 'blank.png' end) + test "non downloadable pack", %{conn: conn} do + assert conn + |> get("/api/pleroma/emoji/packs/test_pack_nonshared/archive") + |> json_response(:forbidden) == %{ + "error" => + "Pack test_pack_nonshared cannot be downloaded from this instance, either pack sharing was disabled for this pack or some files are missing" + } + end end - test "downloading shared & unshared packs from another instance via download_from, deleting them" do - on_exit(fn -> - File.rm_rf!("#{@emoji_dir_path}/test_pack2") - File.rm_rf!("#{@emoji_dir_path}/test_pack_nonshared2") - end) + describe "POST /api/pleroma/emoji/packs/download" do + test "shared pack from remote and non shared from fallback-src", %{ + admin_conn: admin_conn, + conn: conn + } do + mock(fn + %{method: :get, url: "https://example.com/.well-known/nodeinfo"} -> + json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]}) - mock(fn - %{method: :get, url: "https://old-instance/.well-known/nodeinfo"} -> - json(%{links: [%{href: "https://old-instance/nodeinfo/2.1.json"}]}) + %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} -> + json(%{metadata: %{features: ["shareable_emoji_packs"]}}) - %{method: :get, url: "https://old-instance/nodeinfo/2.1.json"} -> - json(%{metadata: %{features: []}}) + %{ + method: :get, + url: "https://example.com/api/pleroma/emoji/packs/test_pack" + } -> + conn + |> get("/api/pleroma/emoji/packs/test_pack") + |> json_response(200) + |> json() - %{method: :get, url: "https://example.com/.well-known/nodeinfo"} -> - json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]}) + %{ + method: :get, + url: "https://example.com/api/pleroma/emoji/packs/test_pack/archive" + } -> + conn + |> get("/api/pleroma/emoji/packs/test_pack/archive") + |> response(200) + |> text() - %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} -> - json(%{metadata: %{features: ["shareable_emoji_packs"]}}) + %{ + method: :get, + url: "https://example.com/api/pleroma/emoji/packs/test_pack_nonshared" + } -> + conn + |> get("/api/pleroma/emoji/packs/test_pack_nonshared") + |> json_response(200) + |> json() - %{ - method: :get, - url: "https://example.com/api/pleroma/emoji/packs/list" - } -> - conn = build_conn() + %{ + method: :get, + url: "https://nonshared-pack" + } -> + text(File.read!("#{@emoji_path}/test_pack_nonshared/nonshared.zip")) + end) - conn - |> get(emoji_api_path(conn, :list_packs)) - |> json_response(200) - |> json() + assert admin_conn + |> post("/api/pleroma/emoji/packs/download", %{ + url: "https://example.com", + name: "test_pack", + as: "test_pack2" + }) + |> json_response(200) == "ok" - %{ - method: :get, - url: "https://example.com/api/pleroma/emoji/packs/download_shared/test_pack" - } -> - conn = build_conn() + assert File.exists?("#{@emoji_path}/test_pack2/pack.json") + assert File.exists?("#{@emoji_path}/test_pack2/blank.png") - conn - |> get(emoji_api_path(conn, :download_shared, "test_pack")) - |> response(200) - |> text() + assert admin_conn + |> delete("/api/pleroma/emoji/packs/test_pack2") + |> json_response(200) == "ok" - %{ - method: :get, - url: "https://nonshared-pack" - } -> - text(File.read!("#{@emoji_dir_path}/test_pack_nonshared/nonshared.zip")) - end) + refute File.exists?("#{@emoji_path}/test_pack2") - admin = insert(:user, is_admin: true) + assert admin_conn + |> post( + "/api/pleroma/emoji/packs/download", + %{ + url: "https://example.com", + name: "test_pack_nonshared", + as: "test_pack_nonshared2" + } + ) + |> json_response(200) == "ok" - conn = - build_conn() - |> assign(:user, admin) - |> assign(:token, insert(:oauth_admin_token, user: admin, scopes: ["admin:write"])) - - assert (conn - |> put_req_header("content-type", "application/json") - |> post( - emoji_api_path( - conn, - :download_from - ), - %{ - instance_address: "https://old-instance", - pack_name: "test_pack", - as: "test_pack2" - } - |> Jason.encode!() - ) - |> json_response(500))["error"] =~ "does not support" - - assert conn - |> put_req_header("content-type", "application/json") - |> post( - emoji_api_path( - conn, - :download_from - ), - %{ - instance_address: "https://example.com", - pack_name: "test_pack", - as: "test_pack2" + assert File.exists?("#{@emoji_path}/test_pack_nonshared2/pack.json") + assert File.exists?("#{@emoji_path}/test_pack_nonshared2/blank.png") + + assert admin_conn + |> delete("/api/pleroma/emoji/packs/test_pack_nonshared2") + |> json_response(200) == "ok" + + refute File.exists?("#{@emoji_path}/test_pack_nonshared2") + end + + test "nonshareable instance", %{admin_conn: admin_conn} do + mock(fn + %{method: :get, url: "https://old-instance/.well-known/nodeinfo"} -> + json(%{links: [%{href: "https://old-instance/nodeinfo/2.1.json"}]}) + + %{method: :get, url: "https://old-instance/nodeinfo/2.1.json"} -> + json(%{metadata: %{features: []}}) + end) + + assert admin_conn + |> post( + "/api/pleroma/emoji/packs/download", + %{ + url: "https://old-instance", + name: "test_pack", + as: "test_pack2" + } + ) + |> json_response(500) == %{ + "error" => "The requested instance does not support sharing emoji packs" } - |> Jason.encode!() - ) - |> json_response(200) == "ok" - - assert File.exists?("#{@emoji_dir_path}/test_pack2/pack.json") - assert File.exists?("#{@emoji_dir_path}/test_pack2/blank.png") - - assert conn - |> delete(emoji_api_path(conn, :delete, "test_pack2")) - |> json_response(200) == "ok" - - refute File.exists?("#{@emoji_dir_path}/test_pack2") - - # non-shared, downloaded from the fallback URL - - assert conn - |> put_req_header("content-type", "application/json") - |> post( - emoji_api_path( - conn, - :download_from - ), - %{ - instance_address: "https://example.com", - pack_name: "test_pack_nonshared", - as: "test_pack_nonshared2" + end + + test "checksum fail", %{admin_conn: admin_conn} do + mock(fn + %{method: :get, url: "https://example.com/.well-known/nodeinfo"} -> + json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]}) + + %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} -> + json(%{metadata: %{features: ["shareable_emoji_packs"]}}) + + %{ + method: :get, + url: "https://example.com/api/pleroma/emoji/packs/pack_bad_sha" + } -> + %Tesla.Env{ + status: 200, + body: Pleroma.Emoji.Pack.load_pack("pack_bad_sha") |> Jason.encode!() + } + + %{ + method: :get, + url: "https://example.com/api/pleroma/emoji/packs/pack_bad_sha/archive" + } -> + %Tesla.Env{ + status: 200, + body: File.read!("test/instance_static/emoji/pack_bad_sha/pack_bad_sha.zip") + } + end) + + assert admin_conn + |> post("/api/pleroma/emoji/packs/download", %{ + url: "https://example.com", + name: "pack_bad_sha", + as: "pack_bad_sha2" + }) + |> json_response(:internal_server_error) == %{ + "error" => "SHA256 for the pack doesn't match the one sent by the server" } - |> Jason.encode!() - ) - |> json_response(200) == "ok" + end + + test "other error", %{admin_conn: admin_conn} do + mock(fn + %{method: :get, url: "https://example.com/.well-known/nodeinfo"} -> + json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]}) - assert File.exists?("#{@emoji_dir_path}/test_pack_nonshared2/pack.json") - assert File.exists?("#{@emoji_dir_path}/test_pack_nonshared2/blank.png") + %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} -> + json(%{metadata: %{features: ["shareable_emoji_packs"]}}) - assert conn - |> delete(emoji_api_path(conn, :delete, "test_pack_nonshared2")) - |> json_response(200) == "ok" + %{ + method: :get, + url: "https://example.com/api/pleroma/emoji/packs/test_pack" + } -> + %Tesla.Env{ + status: 200, + body: Pleroma.Emoji.Pack.load_pack("test_pack") |> Jason.encode!() + } + end) - refute File.exists?("#{@emoji_dir_path}/test_pack_nonshared2") + assert admin_conn + |> post("/api/pleroma/emoji/packs/download", %{ + url: "https://example.com", + name: "test_pack", + as: "test_pack2" + }) + |> json_response(:internal_server_error) == %{ + "error" => + "The pack was not set as shared and there is no fallback src to download from" + } + end end - describe "updating pack metadata" do + describe "PATCH /api/pleroma/emoji/packs/:name" do setup do - pack_file = "#{@emoji_dir_path}/test_pack/pack.json" + pack_file = "#{@emoji_path}/test_pack/pack.json" original_content = File.read!(pack_file) on_exit(fn -> File.write!(pack_file, original_content) end) - admin = insert(:user, is_admin: true) - %{conn: conn} = oauth_access(["admin:write"], user: admin) - {:ok, - admin: admin, - conn: conn, pack_file: pack_file, new_data: %{ "license" => "Test license changed", @@ -225,15 +310,8 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do end test "for a pack without a fallback source", ctx do - conn = ctx[:conn] - - assert conn - |> post( - emoji_api_path(conn, :update_metadata, "test_pack"), - %{ - "new_data" => ctx[:new_data] - } - ) + assert ctx[:admin_conn] + |> patch("/api/pleroma/emoji/packs/test_pack", %{"metadata" => ctx[:new_data]}) |> json_response(200) == ctx[:new_data] assert Jason.decode!(File.read!(ctx[:pack_file]))["pack"] == ctx[:new_data] @@ -245,7 +323,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do method: :get, url: "https://nonshared-pack" } -> - text(File.read!("#{@emoji_dir_path}/test_pack_nonshared/nonshared.zip")) + text(File.read!("#{@emoji_path}/test_pack_nonshared/nonshared.zip")) end) new_data = Map.put(ctx[:new_data], "fallback-src", "https://nonshared-pack") @@ -257,15 +335,8 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do "74409E2674DAA06C072729C6C8426C4CB3B7E0B85ED77792DB7A436E11D76DAF" ) - conn = ctx[:conn] - - assert conn - |> post( - emoji_api_path(conn, :update_metadata, "test_pack"), - %{ - "new_data" => new_data - } - ) + assert ctx[:admin_conn] + |> patch("/api/pleroma/emoji/packs/test_pack", %{metadata: new_data}) |> json_response(200) == new_data_with_sha assert Jason.decode!(File.read!(ctx[:pack_file]))["pack"] == new_data_with_sha @@ -283,181 +354,377 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do new_data = Map.put(ctx[:new_data], "fallback-src", "https://nonshared-pack") - conn = ctx[:conn] - - assert (conn - |> post( - emoji_api_path(conn, :update_metadata, "test_pack"), - %{ - "new_data" => new_data - } - ) - |> json_response(:bad_request))["error"] =~ "does not have all" + assert ctx[:admin_conn] + |> patch("/api/pleroma/emoji/packs/test_pack", %{metadata: new_data}) + |> json_response(:bad_request) == %{ + "error" => "The fallback archive does not have all files specified in pack.json" + } end end - test "updating pack files" do - pack_file = "#{@emoji_dir_path}/test_pack/pack.json" - original_content = File.read!(pack_file) + describe "POST/PATCH/DELETE /api/pleroma/emoji/packs/:name/files" do + setup do + pack_file = "#{@emoji_path}/test_pack/pack.json" + original_content = File.read!(pack_file) - on_exit(fn -> - File.write!(pack_file, original_content) + on_exit(fn -> + File.write!(pack_file, original_content) + end) - File.rm_rf!("#{@emoji_dir_path}/test_pack/blank_url.png") - File.rm_rf!("#{@emoji_dir_path}/test_pack/dir") - File.rm_rf!("#{@emoji_dir_path}/test_pack/dir_2") - end) + :ok + end - admin = insert(:user, is_admin: true) - %{conn: conn} = oauth_access(["admin:write"], user: admin) - - same_name = %{ - "action" => "add", - "shortcode" => "blank", - "filename" => "dir/blank.png", - "file" => %Plug.Upload{ - filename: "blank.png", - path: "#{@emoji_dir_path}/test_pack/blank.png" - } - } - - different_name = %{same_name | "shortcode" => "blank_2"} - - assert (conn - |> post(emoji_api_path(conn, :update_file, "test_pack"), same_name) - |> json_response(:conflict))["error"] =~ "already exists" - - assert conn - |> post(emoji_api_path(conn, :update_file, "test_pack"), different_name) - |> json_response(200) == %{"blank" => "blank.png", "blank_2" => "dir/blank.png"} - - assert File.exists?("#{@emoji_dir_path}/test_pack/dir/blank.png") - - assert conn - |> post(emoji_api_path(conn, :update_file, "test_pack"), %{ - "action" => "update", - "shortcode" => "blank_2", - "new_shortcode" => "blank_3", - "new_filename" => "dir_2/blank_3.png" - }) - |> json_response(200) == %{"blank" => "blank.png", "blank_3" => "dir_2/blank_3.png"} - - refute File.exists?("#{@emoji_dir_path}/test_pack/dir/") - assert File.exists?("#{@emoji_dir_path}/test_pack/dir_2/blank_3.png") - - assert conn - |> post(emoji_api_path(conn, :update_file, "test_pack"), %{ - "action" => "remove", - "shortcode" => "blank_3" - }) - |> json_response(200) == %{"blank" => "blank.png"} - - refute File.exists?("#{@emoji_dir_path}/test_pack/dir_2/") - - mock(fn - %{ - method: :get, - url: "https://test-blank/blank_url.png" - } -> - text(File.read!("#{@emoji_dir_path}/test_pack/blank.png")) - end) + test "create shortcode exists", %{admin_conn: admin_conn} do + assert admin_conn + |> post("/api/pleroma/emoji/packs/test_pack/files", %{ + shortcode: "blank", + filename: "dir/blank.png", + file: %Plug.Upload{ + filename: "blank.png", + path: "#{@emoji_path}/test_pack/blank.png" + } + }) + |> json_response(:conflict) == %{ + "error" => "An emoji with the \"blank\" shortcode already exists" + } + end - # The name should be inferred from the URL ending - from_url = %{ - "action" => "add", - "shortcode" => "blank_url", - "file" => "https://test-blank/blank_url.png" - } + test "don't rewrite old emoji", %{admin_conn: admin_conn} do + on_exit(fn -> File.rm_rf!("#{@emoji_path}/test_pack/dir/") end) - assert conn - |> post(emoji_api_path(conn, :update_file, "test_pack"), from_url) - |> json_response(200) == %{ - "blank" => "blank.png", - "blank_url" => "blank_url.png" - } + assert admin_conn + |> post("/api/pleroma/emoji/packs/test_pack/files", %{ + shortcode: "blank2", + filename: "dir/blank.png", + file: %Plug.Upload{ + filename: "blank.png", + path: "#{@emoji_path}/test_pack/blank.png" + } + }) + |> json_response(200) == %{"blank" => "blank.png", "blank2" => "dir/blank.png"} + + assert File.exists?("#{@emoji_path}/test_pack/dir/blank.png") + + assert admin_conn + |> patch("/api/pleroma/emoji/packs/test_pack/files", %{ + shortcode: "blank", + new_shortcode: "blank2", + new_filename: "dir_2/blank_3.png" + }) + |> json_response(:conflict) == %{ + "error" => + "New shortcode \"blank2\" is already used. If you want to override emoji use 'force' option" + } + end + + test "rewrite old emoji with force option", %{admin_conn: admin_conn} do + on_exit(fn -> File.rm_rf!("#{@emoji_path}/test_pack/dir_2/") end) + + assert admin_conn + |> post("/api/pleroma/emoji/packs/test_pack/files", %{ + shortcode: "blank2", + filename: "dir/blank.png", + file: %Plug.Upload{ + filename: "blank.png", + path: "#{@emoji_path}/test_pack/blank.png" + } + }) + |> json_response(200) == %{"blank" => "blank.png", "blank2" => "dir/blank.png"} + + assert File.exists?("#{@emoji_path}/test_pack/dir/blank.png") + + assert admin_conn + |> patch("/api/pleroma/emoji/packs/test_pack/files", %{ + shortcode: "blank2", + new_shortcode: "blank3", + new_filename: "dir_2/blank_3.png", + force: true + }) + |> json_response(200) == %{ + "blank" => "blank.png", + "blank3" => "dir_2/blank_3.png" + } + + assert File.exists?("#{@emoji_path}/test_pack/dir_2/blank_3.png") + end + + test "with empty filename", %{admin_conn: admin_conn} do + assert admin_conn + |> post("/api/pleroma/emoji/packs/test_pack/files", %{ + shortcode: "blank2", + filename: "", + file: %Plug.Upload{ + filename: "blank.png", + path: "#{@emoji_path}/test_pack/blank.png" + } + }) + |> json_response(:bad_request) == %{ + "error" => "pack name, shortcode or filename cannot be empty" + } + end + + test "add file with not loaded pack", %{admin_conn: admin_conn} do + assert admin_conn + |> post("/api/pleroma/emoji/packs/not_loaded/files", %{ + shortcode: "blank2", + filename: "dir/blank.png", + file: %Plug.Upload{ + filename: "blank.png", + path: "#{@emoji_path}/test_pack/blank.png" + } + }) + |> json_response(:bad_request) == %{ + "error" => "pack \"not_loaded\" is not found" + } + end + + test "remove file with not loaded pack", %{admin_conn: admin_conn} do + assert admin_conn + |> delete("/api/pleroma/emoji/packs/not_loaded/files", %{shortcode: "blank3"}) + |> json_response(:bad_request) == %{"error" => "pack \"not_loaded\" is not found"} + end + + test "remove file with empty shortcode", %{admin_conn: admin_conn} do + assert admin_conn + |> delete("/api/pleroma/emoji/packs/not_loaded/files", %{shortcode: ""}) + |> json_response(:bad_request) == %{ + "error" => "pack name or shortcode cannot be empty" + } + end + + test "update file with not loaded pack", %{admin_conn: admin_conn} do + assert admin_conn + |> patch("/api/pleroma/emoji/packs/not_loaded/files", %{ + shortcode: "blank4", + new_shortcode: "blank3", + new_filename: "dir_2/blank_3.png" + }) + |> json_response(:bad_request) == %{"error" => "pack \"not_loaded\" is not found"} + end + + test "new with shortcode as file with update", %{admin_conn: admin_conn} do + assert admin_conn + |> post("/api/pleroma/emoji/packs/test_pack/files", %{ + shortcode: "blank4", + filename: "dir/blank.png", + file: %Plug.Upload{ + filename: "blank.png", + path: "#{@emoji_path}/test_pack/blank.png" + } + }) + |> json_response(200) == %{"blank" => "blank.png", "blank4" => "dir/blank.png"} + + assert File.exists?("#{@emoji_path}/test_pack/dir/blank.png") + + assert admin_conn + |> patch("/api/pleroma/emoji/packs/test_pack/files", %{ + shortcode: "blank4", + new_shortcode: "blank3", + new_filename: "dir_2/blank_3.png" + }) + |> json_response(200) == %{"blank3" => "dir_2/blank_3.png", "blank" => "blank.png"} + + refute File.exists?("#{@emoji_path}/test_pack/dir/") + assert File.exists?("#{@emoji_path}/test_pack/dir_2/blank_3.png") - assert File.exists?("#{@emoji_dir_path}/test_pack/blank_url.png") + assert admin_conn + |> delete("/api/pleroma/emoji/packs/test_pack/files", %{shortcode: "blank3"}) + |> json_response(200) == %{"blank" => "blank.png"} - assert conn - |> post(emoji_api_path(conn, :update_file, "test_pack"), %{ - "action" => "remove", - "shortcode" => "blank_url" - }) - |> json_response(200) == %{"blank" => "blank.png"} + refute File.exists?("#{@emoji_path}/test_pack/dir_2/") - refute File.exists?("#{@emoji_dir_path}/test_pack/blank_url.png") + on_exit(fn -> File.rm_rf!("#{@emoji_path}/test_pack/dir") end) + end + + test "new with shortcode from url", %{admin_conn: admin_conn} do + mock(fn + %{ + method: :get, + url: "https://test-blank/blank_url.png" + } -> + text(File.read!("#{@emoji_path}/test_pack/blank.png")) + end) + + assert admin_conn + |> post("/api/pleroma/emoji/packs/test_pack/files", %{ + shortcode: "blank_url", + file: "https://test-blank/blank_url.png" + }) + |> json_response(200) == %{ + "blank_url" => "blank_url.png", + "blank" => "blank.png" + } + + assert File.exists?("#{@emoji_path}/test_pack/blank_url.png") + + on_exit(fn -> File.rm_rf!("#{@emoji_path}/test_pack/blank_url.png") end) + end + + test "new without shortcode", %{admin_conn: admin_conn} do + on_exit(fn -> File.rm_rf!("#{@emoji_path}/test_pack/shortcode.png") end) + + assert admin_conn + |> post("/api/pleroma/emoji/packs/test_pack/files", %{ + file: %Plug.Upload{ + filename: "shortcode.png", + path: "#{Pleroma.Config.get([:instance, :static_dir])}/add/shortcode.png" + } + }) + |> json_response(200) == %{"shortcode" => "shortcode.png", "blank" => "blank.png"} + end + + test "remove non existing shortcode in pack.json", %{admin_conn: admin_conn} do + assert admin_conn + |> delete("/api/pleroma/emoji/packs/test_pack/files", %{shortcode: "blank2"}) + |> json_response(:bad_request) == %{"error" => "Emoji \"blank2\" does not exist"} + end + + test "update non existing emoji", %{admin_conn: admin_conn} do + assert admin_conn + |> patch("/api/pleroma/emoji/packs/test_pack/files", %{ + shortcode: "blank2", + new_shortcode: "blank3", + new_filename: "dir_2/blank_3.png" + }) + |> json_response(:bad_request) == %{"error" => "Emoji \"blank2\" does not exist"} + end + + test "update with empty shortcode", %{admin_conn: admin_conn} do + assert admin_conn + |> patch("/api/pleroma/emoji/packs/test_pack/files", %{ + shortcode: "blank", + new_filename: "dir_2/blank_3.png" + }) + |> json_response(:bad_request) == %{ + "error" => "new_shortcode or new_filename cannot be empty" + } + end end - test "creating and deleting a pack" do - on_exit(fn -> - File.rm_rf!("#{@emoji_dir_path}/test_created") - end) + describe "POST/DELETE /api/pleroma/emoji/packs/:name" do + test "creating and deleting a pack", %{admin_conn: admin_conn} do + assert admin_conn + |> post("/api/pleroma/emoji/packs/test_created") + |> json_response(200) == "ok" - admin = insert(:user, is_admin: true) - %{conn: conn} = oauth_access(["admin:write"], user: admin) - - assert conn - |> put_req_header("content-type", "application/json") - |> put( - emoji_api_path( - conn, - :create, - "test_created" - ) - ) - |> json_response(200) == "ok" + assert File.exists?("#{@emoji_path}/test_created/pack.json") + + assert Jason.decode!(File.read!("#{@emoji_path}/test_created/pack.json")) == %{ + "pack" => %{}, + "files" => %{} + } - assert File.exists?("#{@emoji_dir_path}/test_created/pack.json") + assert admin_conn + |> delete("/api/pleroma/emoji/packs/test_created") + |> json_response(200) == "ok" - assert Jason.decode!(File.read!("#{@emoji_dir_path}/test_created/pack.json")) == %{ - "pack" => %{}, - "files" => %{} - } + refute File.exists?("#{@emoji_path}/test_created/pack.json") + end + + test "if pack exists", %{admin_conn: admin_conn} do + path = Path.join(@emoji_path, "test_created") + File.mkdir(path) + pack_file = Jason.encode!(%{files: %{}, pack: %{}}) + File.write!(Path.join(path, "pack.json"), pack_file) + + assert admin_conn + |> post("/api/pleroma/emoji/packs/test_created") + |> json_response(:conflict) == %{ + "error" => "A pack named \"test_created\" already exists" + } + + on_exit(fn -> File.rm_rf(path) end) + end + + test "with empty name", %{admin_conn: admin_conn} do + assert admin_conn + |> post("/api/pleroma/emoji/packs/ ") + |> json_response(:bad_request) == %{"error" => "pack name cannot be empty"} + end + end - assert conn - |> delete(emoji_api_path(conn, :delete, "test_created")) - |> json_response(200) == "ok" + test "deleting nonexisting pack", %{admin_conn: admin_conn} do + assert admin_conn + |> delete("/api/pleroma/emoji/packs/non_existing") + |> json_response(:not_found) == %{"error" => "Pack non_existing does not exist"} + end - refute File.exists?("#{@emoji_dir_path}/test_created/pack.json") + test "deleting with empty name", %{admin_conn: admin_conn} do + assert admin_conn + |> delete("/api/pleroma/emoji/packs/ ") + |> json_response(:bad_request) == %{"error" => "pack name cannot be empty"} end - test "filesystem import" do + test "filesystem import", %{admin_conn: admin_conn, conn: conn} do on_exit(fn -> - File.rm!("#{@emoji_dir_path}/test_pack_for_import/emoji.txt") - File.rm!("#{@emoji_dir_path}/test_pack_for_import/pack.json") + File.rm!("#{@emoji_path}/test_pack_for_import/emoji.txt") + File.rm!("#{@emoji_path}/test_pack_for_import/pack.json") end) - conn = build_conn() - resp = conn |> get(emoji_api_path(conn, :list_packs)) |> json_response(200) + resp = conn |> get("/api/pleroma/emoji/packs") |> json_response(200) refute Map.has_key?(resp, "test_pack_for_import") - admin = insert(:user, is_admin: true) - %{conn: conn} = oauth_access(["admin:write"], user: admin) - - assert conn - |> post(emoji_api_path(conn, :import_from_fs)) + assert admin_conn + |> get("/api/pleroma/emoji/packs/import") |> json_response(200) == ["test_pack_for_import"] - resp = conn |> get(emoji_api_path(conn, :list_packs)) |> json_response(200) + resp = conn |> get("/api/pleroma/emoji/packs") |> json_response(200) assert resp["test_pack_for_import"]["files"] == %{"blank" => "blank.png"} - File.rm!("#{@emoji_dir_path}/test_pack_for_import/pack.json") - refute File.exists?("#{@emoji_dir_path}/test_pack_for_import/pack.json") + File.rm!("#{@emoji_path}/test_pack_for_import/pack.json") + refute File.exists?("#{@emoji_path}/test_pack_for_import/pack.json") - emoji_txt_content = "blank, blank.png, Fun\n\nblank2, blank.png" + emoji_txt_content = """ + blank, blank.png, Fun + blank2, blank.png + foo, /emoji/test_pack_for_import/blank.png + bar + """ - File.write!("#{@emoji_dir_path}/test_pack_for_import/emoji.txt", emoji_txt_content) + File.write!("#{@emoji_path}/test_pack_for_import/emoji.txt", emoji_txt_content) - assert conn - |> post(emoji_api_path(conn, :import_from_fs)) + assert admin_conn + |> get("/api/pleroma/emoji/packs/import") |> json_response(200) == ["test_pack_for_import"] - resp = build_conn() |> get(emoji_api_path(conn, :list_packs)) |> json_response(200) + resp = conn |> get("/api/pleroma/emoji/packs") |> json_response(200) assert resp["test_pack_for_import"]["files"] == %{ "blank" => "blank.png", - "blank2" => "blank.png" + "blank2" => "blank.png", + "foo" => "blank.png" } end + + describe "GET /api/pleroma/emoji/packs/:name" do + test "shows pack.json", %{conn: conn} do + assert %{ + "files" => %{"blank" => "blank.png"}, + "pack" => %{ + "can-download" => true, + "description" => "Test description", + "download-sha256" => _, + "homepage" => "https://pleroma.social", + "license" => "Test license", + "share-files" => true + } + } = + conn + |> get("/api/pleroma/emoji/packs/test_pack") + |> json_response(200) + end + + test "non existing pack", %{conn: conn} do + assert conn + |> get("/api/pleroma/emoji/packs/non_existing") + |> json_response(:not_found) == %{"error" => "Pack non_existing does not exist"} + end + + test "error name", %{conn: conn} do + assert conn + |> get("/api/pleroma/emoji/packs/ ") + |> json_response(:bad_request) == %{"error" => "pack name cannot be empty"} + end + end end diff --git a/test/web/pleroma_api/controllers/pleroma_api_controller_test.exs b/test/web/pleroma_api/controllers/pleroma_api_controller_test.exs index 32250f06f..cfd1dbd24 100644 --- a/test/web/pleroma_api/controllers/pleroma_api_controller_test.exs +++ b/test/web/pleroma_api/controllers/pleroma_api_controller_test.exs @@ -3,12 +3,14 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.PleromaAPIControllerTest do + use Oban.Testing, repo: Pleroma.Repo use Pleroma.Web.ConnCase alias Pleroma.Conversation.Participation alias Pleroma.Notification alias Pleroma.Object alias Pleroma.Repo + alias Pleroma.Tests.ObanHelpers alias Pleroma.User alias Pleroma.Web.CommonAPI @@ -18,7 +20,7 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIControllerTest do user = insert(:user) other_user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "#cofe"}) + {:ok, activity} = CommonAPI.post(user, %{status: "#cofe"}) result = conn @@ -40,8 +42,10 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIControllerTest do user = insert(:user) other_user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "#cofe"}) - {:ok, activity, _object} = CommonAPI.react_with_emoji(activity.id, other_user, "☕") + {:ok, activity} = CommonAPI.post(user, %{status: "#cofe"}) + {:ok, _reaction_activity} = CommonAPI.react_with_emoji(activity.id, other_user, "☕") + + ObanHelpers.perform_all() result = conn @@ -52,7 +56,9 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIControllerTest do assert %{"id" => id} = json_response(result, 200) assert to_string(activity.id) == id - object = Object.normalize(activity) + ObanHelpers.perform_all() + + object = Object.get_by_ap_id(activity.data["object"]) assert object.data["reaction_count"] == 0 end @@ -62,7 +68,7 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIControllerTest do other_user = insert(:user) doomed_user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "#cofe"}) + {:ok, activity} = CommonAPI.post(user, %{status: "#cofe"}) result = conn @@ -71,8 +77,8 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIControllerTest do assert result == [] - {:ok, _, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅") - {:ok, _, _} = CommonAPI.react_with_emoji(activity.id, doomed_user, "🎅") + {:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅") + {:ok, _} = CommonAPI.react_with_emoji(activity.id, doomed_user, "🎅") User.perform(:delete, doomed_user) @@ -100,7 +106,7 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIControllerTest do user = insert(:user) other_user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "#cofe"}) + {:ok, activity} = CommonAPI.post(user, %{status: "#cofe"}) result = conn @@ -109,8 +115,8 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIControllerTest do assert result == [] - {:ok, _, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅") - {:ok, _, _} = CommonAPI.react_with_emoji(activity.id, other_user, "☕") + {:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅") + {:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "☕") result = conn @@ -127,7 +133,7 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIControllerTest do %{user: other_user, conn: conn} = oauth_access(["read:statuses"]) {:ok, _activity} = - CommonAPI.post(user, %{"status" => "Hi @#{other_user.nickname}!", "visibility" => "direct"}) + CommonAPI.post(user, %{status: "Hi @#{other_user.nickname}!", visibility: "direct"}) [participation] = Participation.for_user(other_user) @@ -145,18 +151,18 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIControllerTest do third_user = insert(:user) {:ok, _activity} = - CommonAPI.post(user, %{"status" => "Hi @#{third_user.nickname}!", "visibility" => "direct"}) + CommonAPI.post(user, %{status: "Hi @#{third_user.nickname}!", visibility: "direct"}) {:ok, activity} = - CommonAPI.post(user, %{"status" => "Hi @#{other_user.nickname}!", "visibility" => "direct"}) + CommonAPI.post(user, %{status: "Hi @#{other_user.nickname}!", visibility: "direct"}) [participation] = Participation.for_user(other_user) {:ok, activity_two} = CommonAPI.post(other_user, %{ - "status" => "Hi!", - "in_reply_to_status_id" => activity.id, - "in_reply_to_conversation_id" => participation.id + status: "Hi!", + in_reply_to_status_id: activity.id, + in_reply_to_conversation_id: participation.id }) result = @@ -169,13 +175,30 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIControllerTest do id_one = activity.id id_two = activity_two.id assert [%{"id" => ^id_one}, %{"id" => ^id_two}] = result + + {:ok, %{id: id_three}} = + CommonAPI.post(other_user, %{ + status: "Bye!", + in_reply_to_status_id: activity.id, + in_reply_to_conversation_id: participation.id + }) + + assert [%{"id" => ^id_two}, %{"id" => ^id_three}] = + conn + |> get("/api/v1/pleroma/conversations/#{participation.id}/statuses?limit=2") + |> json_response(:ok) + + assert [%{"id" => ^id_three}] = + conn + |> get("/api/v1/pleroma/conversations/#{participation.id}/statuses?min_id=#{id_two}") + |> json_response(:ok) end test "PATCH /api/v1/pleroma/conversations/:id" do %{user: user, conn: conn} = oauth_access(["write:conversations"]) other_user = insert(:user) - {:ok, _activity} = CommonAPI.post(user, %{"status" => "Hi", "visibility" => "direct"}) + {:ok, _activity} = CommonAPI.post(user, %{status: "Hi", visibility: "direct"}) [participation] = Participation.for_user(user) @@ -203,13 +226,13 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIControllerTest do test "POST /api/v1/pleroma/conversations/read" do user = insert(:user) - %{user: other_user, conn: conn} = oauth_access(["write:notifications"]) + %{user: other_user, conn: conn} = oauth_access(["write:conversations"]) {:ok, _activity} = - CommonAPI.post(user, %{"status" => "Hi @#{other_user.nickname}", "visibility" => "direct"}) + CommonAPI.post(user, %{status: "Hi @#{other_user.nickname}", visibility: "direct"}) {:ok, _activity} = - CommonAPI.post(user, %{"status" => "Hi @#{other_user.nickname}", "visibility" => "direct"}) + CommonAPI.post(user, %{status: "Hi @#{other_user.nickname}", visibility: "direct"}) [participation2, participation1] = Participation.for_user(other_user) assert Participation.get(participation2.id).read == false @@ -232,8 +255,8 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIControllerTest do test "it marks a single notification as read", %{user: user1, conn: conn} do user2 = insert(:user) - {:ok, activity1} = CommonAPI.post(user2, %{"status" => "hi @#{user1.nickname}"}) - {:ok, activity2} = CommonAPI.post(user2, %{"status" => "hi @#{user1.nickname}"}) + {:ok, activity1} = CommonAPI.post(user2, %{status: "hi @#{user1.nickname}"}) + {:ok, activity2} = CommonAPI.post(user2, %{status: "hi @#{user1.nickname}"}) {:ok, [notification1]} = Notification.create_notifications(activity1) {:ok, [notification2]} = Notification.create_notifications(activity2) @@ -249,9 +272,9 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIControllerTest do test "it marks multiple notifications as read", %{user: user1, conn: conn} do user2 = insert(:user) - {:ok, _activity1} = CommonAPI.post(user2, %{"status" => "hi @#{user1.nickname}"}) - {:ok, _activity2} = CommonAPI.post(user2, %{"status" => "hi @#{user1.nickname}"}) - {:ok, _activity3} = CommonAPI.post(user2, %{"status" => "HIE @#{user1.nickname}"}) + {:ok, _activity1} = CommonAPI.post(user2, %{status: "hi @#{user1.nickname}"}) + {:ok, _activity2} = CommonAPI.post(user2, %{status: "hi @#{user1.nickname}"}) + {:ok, _activity3} = CommonAPI.post(user2, %{status: "HIE @#{user1.nickname}"}) [notification3, notification2, notification1] = Notification.for_user(user1, %{limit: 3}) diff --git a/test/web/pleroma_api/controllers/two_factor_authentication_controller_test.exs b/test/web/pleroma_api/controllers/two_factor_authentication_controller_test.exs new file mode 100644 index 000000000..d23d08a00 --- /dev/null +++ b/test/web/pleroma_api/controllers/two_factor_authentication_controller_test.exs @@ -0,0 +1,260 @@ +defmodule Pleroma.Web.PleromaAPI.TwoFactorAuthenticationControllerTest do + use Pleroma.Web.ConnCase + + import Pleroma.Factory + alias Pleroma.MFA.Settings + alias Pleroma.MFA.TOTP + + describe "GET /api/pleroma/accounts/mfa/settings" do + test "returns user mfa settings for new user", %{conn: conn} do + token = insert(:oauth_token, scopes: ["read", "follow"]) + token2 = insert(:oauth_token, scopes: ["write"]) + + assert conn + |> put_req_header("authorization", "Bearer #{token.token}") + |> get("/api/pleroma/accounts/mfa") + |> json_response(:ok) == %{ + "settings" => %{"enabled" => false, "totp" => false} + } + + assert conn + |> put_req_header("authorization", "Bearer #{token2.token}") + |> get("/api/pleroma/accounts/mfa") + |> json_response(403) == %{ + "error" => "Insufficient permissions: read:security." + } + end + + test "returns user mfa settings with enabled totp", %{conn: conn} do + user = + insert(:user, + multi_factor_authentication_settings: %Settings{ + enabled: true, + totp: %Settings.TOTP{secret: "XXX", delivery_type: "app", confirmed: true} + } + ) + + token = insert(:oauth_token, scopes: ["read", "follow"], user: user) + + assert conn + |> put_req_header("authorization", "Bearer #{token.token}") + |> get("/api/pleroma/accounts/mfa") + |> json_response(:ok) == %{ + "settings" => %{"enabled" => true, "totp" => true} + } + end + end + + describe "GET /api/pleroma/accounts/mfa/backup_codes" do + test "returns backup codes", %{conn: conn} do + user = + insert(:user, + multi_factor_authentication_settings: %Settings{ + backup_codes: ["1", "2", "3"], + totp: %Settings.TOTP{secret: "secret"} + } + ) + + token = insert(:oauth_token, scopes: ["write", "follow"], user: user) + token2 = insert(:oauth_token, scopes: ["read"]) + + response = + conn + |> put_req_header("authorization", "Bearer #{token.token}") + |> get("/api/pleroma/accounts/mfa/backup_codes") + |> json_response(:ok) + + assert [<<_::bytes-size(6)>>, <<_::bytes-size(6)>>] = response["codes"] + user = refresh_record(user) + mfa_settings = user.multi_factor_authentication_settings + assert mfa_settings.totp.secret == "secret" + refute mfa_settings.backup_codes == ["1", "2", "3"] + refute mfa_settings.backup_codes == [] + + assert conn + |> put_req_header("authorization", "Bearer #{token2.token}") + |> get("/api/pleroma/accounts/mfa/backup_codes") + |> json_response(403) == %{ + "error" => "Insufficient permissions: write:security." + } + end + end + + describe "GET /api/pleroma/accounts/mfa/setup/totp" do + test "return errors when method is invalid", %{conn: conn} do + user = insert(:user) + token = insert(:oauth_token, scopes: ["write", "follow"], user: user) + + response = + conn + |> put_req_header("authorization", "Bearer #{token.token}") + |> get("/api/pleroma/accounts/mfa/setup/torf") + |> json_response(400) + + assert response == %{"error" => "undefined method"} + end + + test "returns key and provisioning_uri", %{conn: conn} do + user = + insert(:user, + multi_factor_authentication_settings: %Settings{backup_codes: ["1", "2", "3"]} + ) + + token = insert(:oauth_token, scopes: ["write", "follow"], user: user) + token2 = insert(:oauth_token, scopes: ["read"]) + + response = + conn + |> put_req_header("authorization", "Bearer #{token.token}") + |> get("/api/pleroma/accounts/mfa/setup/totp") + |> json_response(:ok) + + user = refresh_record(user) + mfa_settings = user.multi_factor_authentication_settings + secret = mfa_settings.totp.secret + refute mfa_settings.enabled + assert mfa_settings.backup_codes == ["1", "2", "3"] + + assert response == %{ + "key" => secret, + "provisioning_uri" => TOTP.provisioning_uri(secret, "#{user.email}") + } + + assert conn + |> put_req_header("authorization", "Bearer #{token2.token}") + |> get("/api/pleroma/accounts/mfa/setup/totp") + |> json_response(403) == %{ + "error" => "Insufficient permissions: write:security." + } + end + end + + describe "GET /api/pleroma/accounts/mfa/confirm/totp" do + test "returns success result", %{conn: conn} do + secret = TOTP.generate_secret() + code = TOTP.generate_token(secret) + + user = + insert(:user, + multi_factor_authentication_settings: %Settings{ + backup_codes: ["1", "2", "3"], + totp: %Settings.TOTP{secret: secret} + } + ) + + token = insert(:oauth_token, scopes: ["write", "follow"], user: user) + token2 = insert(:oauth_token, scopes: ["read"]) + + assert conn + |> put_req_header("authorization", "Bearer #{token.token}") + |> post("/api/pleroma/accounts/mfa/confirm/totp", %{password: "test", code: code}) + |> json_response(:ok) + + settings = refresh_record(user).multi_factor_authentication_settings + assert settings.enabled + assert settings.totp.secret == secret + assert settings.totp.confirmed + assert settings.backup_codes == ["1", "2", "3"] + + assert conn + |> put_req_header("authorization", "Bearer #{token2.token}") + |> post("/api/pleroma/accounts/mfa/confirm/totp", %{password: "test", code: code}) + |> json_response(403) == %{ + "error" => "Insufficient permissions: write:security." + } + end + + test "returns error if password incorrect", %{conn: conn} do + secret = TOTP.generate_secret() + code = TOTP.generate_token(secret) + + user = + insert(:user, + multi_factor_authentication_settings: %Settings{ + backup_codes: ["1", "2", "3"], + totp: %Settings.TOTP{secret: secret} + } + ) + + token = insert(:oauth_token, scopes: ["write", "follow"], user: user) + + response = + conn + |> put_req_header("authorization", "Bearer #{token.token}") + |> post("/api/pleroma/accounts/mfa/confirm/totp", %{password: "xxx", code: code}) + |> json_response(422) + + settings = refresh_record(user).multi_factor_authentication_settings + refute settings.enabled + refute settings.totp.confirmed + assert settings.backup_codes == ["1", "2", "3"] + assert response == %{"error" => "Invalid password."} + end + + test "returns error if code incorrect", %{conn: conn} do + secret = TOTP.generate_secret() + + user = + insert(:user, + multi_factor_authentication_settings: %Settings{ + backup_codes: ["1", "2", "3"], + totp: %Settings.TOTP{secret: secret} + } + ) + + token = insert(:oauth_token, scopes: ["write", "follow"], user: user) + token2 = insert(:oauth_token, scopes: ["read"]) + + response = + conn + |> put_req_header("authorization", "Bearer #{token.token}") + |> post("/api/pleroma/accounts/mfa/confirm/totp", %{password: "test", code: "code"}) + |> json_response(422) + + settings = refresh_record(user).multi_factor_authentication_settings + refute settings.enabled + refute settings.totp.confirmed + assert settings.backup_codes == ["1", "2", "3"] + assert response == %{"error" => "invalid_token"} + + assert conn + |> put_req_header("authorization", "Bearer #{token2.token}") + |> post("/api/pleroma/accounts/mfa/confirm/totp", %{password: "test", code: "code"}) + |> json_response(403) == %{ + "error" => "Insufficient permissions: write:security." + } + end + end + + describe "DELETE /api/pleroma/accounts/mfa/totp" do + test "returns success result", %{conn: conn} do + user = + insert(:user, + multi_factor_authentication_settings: %Settings{ + backup_codes: ["1", "2", "3"], + totp: %Settings.TOTP{secret: "secret"} + } + ) + + token = insert(:oauth_token, scopes: ["write", "follow"], user: user) + token2 = insert(:oauth_token, scopes: ["read"]) + + assert conn + |> put_req_header("authorization", "Bearer #{token.token}") + |> delete("/api/pleroma/accounts/mfa/totp", %{password: "test"}) + |> json_response(:ok) + + settings = refresh_record(user).multi_factor_authentication_settings + refute settings.enabled + assert settings.totp.secret == nil + refute settings.totp.confirmed + + assert conn + |> put_req_header("authorization", "Bearer #{token2.token}") + |> delete("/api/pleroma/accounts/mfa/totp", %{password: "test"}) + |> json_response(403) == %{ + "error" => "Insufficient permissions: write:security." + } + end + end +end diff --git a/test/web/plugs/plug_test.exs b/test/web/plugs/plug_test.exs new file mode 100644 index 000000000..943e484e7 --- /dev/null +++ b/test/web/plugs/plug_test.exs @@ -0,0 +1,91 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PlugTest do + @moduledoc "Tests for the functionality added via `use Pleroma.Web, :plug`" + + alias Pleroma.Plugs.ExpectAuthenticatedCheckPlug + alias Pleroma.Plugs.ExpectPublicOrAuthenticatedCheckPlug + alias Pleroma.Plugs.PlugHelper + + import Mock + + use Pleroma.Web.ConnCase + + describe "when plug is skipped, " do + setup_with_mocks( + [ + {ExpectPublicOrAuthenticatedCheckPlug, [:passthrough], []} + ], + %{conn: conn} + ) do + conn = ExpectPublicOrAuthenticatedCheckPlug.skip_plug(conn) + %{conn: conn} + end + + test "it neither adds plug to called plugs list nor calls `perform/2`, " <> + "regardless of :if_func / :unless_func options", + %{conn: conn} do + for opts <- [%{}, %{if_func: fn _ -> true end}, %{unless_func: fn _ -> false end}] do + ret_conn = ExpectPublicOrAuthenticatedCheckPlug.call(conn, opts) + + refute called(ExpectPublicOrAuthenticatedCheckPlug.perform(:_, :_)) + refute PlugHelper.plug_called?(ret_conn, ExpectPublicOrAuthenticatedCheckPlug) + end + end + end + + describe "when plug is NOT skipped, " do + setup_with_mocks([{ExpectAuthenticatedCheckPlug, [:passthrough], []}]) do + :ok + end + + test "with no pre-run checks, adds plug to called plugs list and calls `perform/2`", %{ + conn: conn + } do + ret_conn = ExpectAuthenticatedCheckPlug.call(conn, %{}) + + assert called(ExpectAuthenticatedCheckPlug.perform(ret_conn, :_)) + assert PlugHelper.plug_called?(ret_conn, ExpectAuthenticatedCheckPlug) + end + + test "when :if_func option is given, calls the plug only if provided function evals tru-ish", + %{conn: conn} do + ret_conn = ExpectAuthenticatedCheckPlug.call(conn, %{if_func: fn _ -> false end}) + + refute called(ExpectAuthenticatedCheckPlug.perform(:_, :_)) + refute PlugHelper.plug_called?(ret_conn, ExpectAuthenticatedCheckPlug) + + ret_conn = ExpectAuthenticatedCheckPlug.call(conn, %{if_func: fn _ -> true end}) + + assert called(ExpectAuthenticatedCheckPlug.perform(ret_conn, :_)) + assert PlugHelper.plug_called?(ret_conn, ExpectAuthenticatedCheckPlug) + end + + test "if :unless_func option is given, calls the plug only if provided function evals falsy", + %{conn: conn} do + ret_conn = ExpectAuthenticatedCheckPlug.call(conn, %{unless_func: fn _ -> true end}) + + refute called(ExpectAuthenticatedCheckPlug.perform(:_, :_)) + refute PlugHelper.plug_called?(ret_conn, ExpectAuthenticatedCheckPlug) + + ret_conn = ExpectAuthenticatedCheckPlug.call(conn, %{unless_func: fn _ -> false end}) + + assert called(ExpectAuthenticatedCheckPlug.perform(ret_conn, :_)) + assert PlugHelper.plug_called?(ret_conn, ExpectAuthenticatedCheckPlug) + end + + test "allows a plug to be called multiple times (even if it's in called plugs list)", %{ + conn: conn + } do + conn = ExpectAuthenticatedCheckPlug.call(conn, %{an_option: :value1}) + assert called(ExpectAuthenticatedCheckPlug.perform(conn, %{an_option: :value1})) + + assert PlugHelper.plug_called?(conn, ExpectAuthenticatedCheckPlug) + + conn = ExpectAuthenticatedCheckPlug.call(conn, %{an_option: :value2}) + assert called(ExpectAuthenticatedCheckPlug.perform(conn, %{an_option: :value2})) + end + end +end diff --git a/test/web/push/impl_test.exs b/test/web/push/impl_test.exs index 089d55577..2acd0939f 100644 --- a/test/web/push/impl_test.exs +++ b/test/web/push/impl_test.exs @@ -13,8 +13,8 @@ defmodule Pleroma.Web.Push.ImplTest do import Pleroma.Factory - setup_all do - Tesla.Mock.mock_global(fn + setup do + Tesla.Mock.mock(fn %{method: :post, url: "https://example.com/example/1234"} -> %Tesla.Env{status: 200} @@ -55,7 +55,7 @@ defmodule Pleroma.Web.Push.ImplTest do data: %{alerts: %{"follow" => true, "mention" => false}} ) - {:ok, activity} = CommonAPI.post(user, %{"status" => "<Lorem ipsum dolor sit amet."}) + {:ok, activity} = CommonAPI.post(user, %{status: "<Lorem ipsum dolor sit amet."}) notif = insert(:notification, @@ -63,12 +63,12 @@ defmodule Pleroma.Web.Push.ImplTest do activity: activity ) - assert Impl.perform(notif) == [:ok, :ok] + assert Impl.perform(notif) == {:ok, [:ok, :ok]} end @tag capture_log: true test "returns error if notif does not match " do - assert Impl.perform(%{}) == :error + assert Impl.perform(%{}) == {:error, :unknown_type} end test "successful message sending" do @@ -111,7 +111,7 @@ defmodule Pleroma.Web.Push.ImplTest do {:ok, activity} = CommonAPI.post(user, %{ - "status" => + status: "<span>Lorem ipsum dolor sit amet</span>, consectetur :firefox: adipiscing elit. Fusce sagittis finibus turpis." }) @@ -134,7 +134,7 @@ defmodule Pleroma.Web.Push.ImplTest do user = insert(:user, nickname: "Bob") other_user = insert(:user) {:ok, _, _, activity} = CommonAPI.follow(user, other_user) - object = Object.normalize(activity) + object = Object.normalize(activity, false) assert Impl.format_body(%{activity: activity}, user, object) == "@Bob has followed you" @@ -147,7 +147,7 @@ defmodule Pleroma.Web.Push.ImplTest do {:ok, activity} = CommonAPI.post(user, %{ - "status" => + status: "<span>Lorem ipsum dolor sit amet</span>, consectetur :firefox: adipiscing elit. Fusce sagittis finibus turpis." }) @@ -166,11 +166,11 @@ defmodule Pleroma.Web.Push.ImplTest do {:ok, activity} = CommonAPI.post(user, %{ - "status" => + status: "<span>Lorem ipsum dolor sit amet</span>, consectetur :firefox: adipiscing elit. Fusce sagittis finibus turpis." }) - {:ok, activity, _} = CommonAPI.favorite(activity.id, user) + {:ok, activity} = CommonAPI.favorite(user, activity.id) object = Object.normalize(activity) assert Impl.format_body(%{activity: activity}, user, object) == "@Bob has favorited your post" @@ -184,8 +184,8 @@ defmodule Pleroma.Web.Push.ImplTest do {:ok, activity} = CommonAPI.post(user, %{ - "visibility" => "direct", - "status" => "This is just between you and me, pal" + visibility: "direct", + status: "This is just between you and me, pal" }) assert Impl.format_title(%{activity: activity}) == @@ -193,14 +193,14 @@ defmodule Pleroma.Web.Push.ImplTest do end describe "build_content/3" do - test "returns info content for direct message with enabled privacy option" do + test "hides details for notifications when privacy option enabled" do user = insert(:user, nickname: "Bob") user2 = insert(:user, nickname: "Rob", notification_settings: %{privacy_option: true}) {:ok, activity} = CommonAPI.post(user, %{ - "visibility" => "direct", - "status" => "<Lorem ipsum dolor sit amet." + visibility: "direct", + status: "<Lorem ipsum dolor sit amet." }) notif = insert(:notification, user: user2, activity: activity) @@ -209,19 +209,44 @@ defmodule Pleroma.Web.Push.ImplTest do object = Object.normalize(activity) assert Impl.build_content(notif, actor, object) == %{ - body: "@Bob", - title: "New Direct Message" + body: "New Direct Message" + } + + {:ok, activity} = + CommonAPI.post(user, %{ + visibility: "public", + status: "<Lorem ipsum dolor sit amet." + }) + + notif = insert(:notification, user: user2, activity: activity) + + actor = User.get_cached_by_ap_id(notif.activity.data["actor"]) + object = Object.normalize(activity) + + assert Impl.build_content(notif, actor, object) == %{ + body: "New Mention" + } + + {:ok, activity} = CommonAPI.favorite(user, activity.id) + + notif = insert(:notification, user: user2, activity: activity) + + actor = User.get_cached_by_ap_id(notif.activity.data["actor"]) + object = Object.normalize(activity) + + assert Impl.build_content(notif, actor, object) == %{ + body: "New Favorite" } end - test "returns regular content for direct message with disabled privacy option" do + test "returns regular content for notifications with privacy option disabled" do user = insert(:user, nickname: "Bob") user2 = insert(:user, nickname: "Rob", notification_settings: %{privacy_option: false}) {:ok, activity} = CommonAPI.post(user, %{ - "visibility" => "direct", - "status" => + visibility: "direct", + status: "<span>Lorem ipsum dolor sit amet</span>, consectetur :firefox: adipiscing elit. Fusce sagittis finibus turpis." }) @@ -235,6 +260,36 @@ defmodule Pleroma.Web.Push.ImplTest do "@Bob: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce sagittis fini...", title: "New Direct Message" } + + {:ok, activity} = + CommonAPI.post(user, %{ + visibility: "public", + status: + "<span>Lorem ipsum dolor sit amet</span>, consectetur :firefox: adipiscing elit. Fusce sagittis finibus turpis." + }) + + notif = insert(:notification, user: user2, activity: activity) + + actor = User.get_cached_by_ap_id(notif.activity.data["actor"]) + object = Object.normalize(activity) + + assert Impl.build_content(notif, actor, object) == %{ + body: + "@Bob: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce sagittis fini...", + title: "New Mention" + } + + {:ok, activity} = CommonAPI.favorite(user, activity.id) + + notif = insert(:notification, user: user2, activity: activity) + + actor = User.get_cached_by_ap_id(notif.activity.data["actor"]) + object = Object.normalize(activity) + + assert Impl.build_content(notif, actor, object) == %{ + body: "@Bob has favorited your post", + title: "New Favorite" + } end end end diff --git a/test/web/rel_me_test.exs b/test/web/rel_me_test.exs index e05a8863d..65255916d 100644 --- a/test/web/rel_me_test.exs +++ b/test/web/rel_me_test.exs @@ -3,7 +3,7 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.RelMeTest do - use ExUnit.Case, async: true + use ExUnit.Case setup_all do Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) diff --git a/test/web/rich_media/helpers_test.exs b/test/web/rich_media/helpers_test.exs index aa0c5c830..8264a9c41 100644 --- a/test/web/rich_media/helpers_test.exs +++ b/test/web/rich_media/helpers_test.exs @@ -26,8 +26,8 @@ defmodule Pleroma.Web.RichMedia.HelpersTest do {:ok, activity} = CommonAPI.post(user, %{ - "status" => "[test](example.com/ogp)", - "content_type" => "text/markdown" + status: "[test](example.com/ogp)", + content_type: "text/markdown" }) Config.put([:rich_media, :enabled], true) @@ -40,8 +40,8 @@ defmodule Pleroma.Web.RichMedia.HelpersTest do {:ok, activity} = CommonAPI.post(user, %{ - "status" => "[test](example.com[]/ogp)", - "content_type" => "text/markdown" + status: "[test](example.com[]/ogp)", + content_type: "text/markdown" }) Config.put([:rich_media, :enabled], true) @@ -54,8 +54,8 @@ defmodule Pleroma.Web.RichMedia.HelpersTest do {:ok, activity} = CommonAPI.post(user, %{ - "status" => "[test](https://example.com/ogp)", - "content_type" => "text/markdown" + status: "[test](https://example.com/ogp)", + content_type: "text/markdown" }) Config.put([:rich_media, :enabled], true) @@ -69,8 +69,8 @@ defmodule Pleroma.Web.RichMedia.HelpersTest do {:ok, activity} = CommonAPI.post(user, %{ - "status" => "http://example.com/ogp", - "sensitive" => true + status: "http://example.com/ogp", + sensitive: true }) %Object{} = object = Object.normalize(activity) @@ -87,7 +87,7 @@ defmodule Pleroma.Web.RichMedia.HelpersTest do {:ok, activity} = CommonAPI.post(user, %{ - "status" => "http://example.com/ogp #nsfw" + status: "http://example.com/ogp #nsfw" }) %Object{} = object = Object.normalize(activity) @@ -103,12 +103,12 @@ defmodule Pleroma.Web.RichMedia.HelpersTest do user = insert(:user) {:ok, activity} = - CommonAPI.post(user, %{"status" => "http://127.0.0.1:4000/notice/9kCP7VNyPJXFOXDrgO"}) + CommonAPI.post(user, %{status: "http://127.0.0.1:4000/notice/9kCP7VNyPJXFOXDrgO"}) - {:ok, activity2} = CommonAPI.post(user, %{"status" => "https://10.111.10.1/notice/9kCP7V"}) - {:ok, activity3} = CommonAPI.post(user, %{"status" => "https://172.16.32.40/notice/9kCP7V"}) - {:ok, activity4} = CommonAPI.post(user, %{"status" => "https://192.168.10.40/notice/9kCP7V"}) - {:ok, activity5} = CommonAPI.post(user, %{"status" => "https://pleroma.local/notice/9kCP7V"}) + {:ok, activity2} = CommonAPI.post(user, %{status: "https://10.111.10.1/notice/9kCP7V"}) + {:ok, activity3} = CommonAPI.post(user, %{status: "https://172.16.32.40/notice/9kCP7V"}) + {:ok, activity4} = CommonAPI.post(user, %{status: "https://192.168.10.40/notice/9kCP7V"}) + {:ok, activity5} = CommonAPI.post(user, %{status: "https://pleroma.local/notice/9kCP7V"}) Config.put([:rich_media, :enabled], true) diff --git a/test/web/static_fe/static_fe_controller_test.exs b/test/web/static_fe/static_fe_controller_test.exs index 430683ea0..a49ab002f 100644 --- a/test/web/static_fe/static_fe_controller_test.exs +++ b/test/web/static_fe/static_fe_controller_test.exs @@ -32,8 +32,8 @@ defmodule Pleroma.Web.StaticFE.StaticFEControllerTest do end test "profile does not include private messages", %{conn: conn, user: user} do - CommonAPI.post(user, %{"status" => "public"}) - CommonAPI.post(user, %{"status" => "private", "visibility" => "private"}) + CommonAPI.post(user, %{status: "public"}) + CommonAPI.post(user, %{status: "private", visibility: "private"}) conn = get(conn, "/users/#{user.nickname}") @@ -44,7 +44,7 @@ defmodule Pleroma.Web.StaticFE.StaticFEControllerTest do end test "pagination", %{conn: conn, user: user} do - Enum.map(1..30, fn i -> CommonAPI.post(user, %{"status" => "test#{i}"}) end) + Enum.map(1..30, fn i -> CommonAPI.post(user, %{status: "test#{i}"}) end) conn = get(conn, "/users/#{user.nickname}") @@ -57,7 +57,7 @@ defmodule Pleroma.Web.StaticFE.StaticFEControllerTest do end test "pagination, page 2", %{conn: conn, user: user} do - activities = Enum.map(1..30, fn i -> CommonAPI.post(user, %{"status" => "test#{i}"}) end) + activities = Enum.map(1..30, fn i -> CommonAPI.post(user, %{status: "test#{i}"}) end) {:ok, a11} = Enum.at(activities, 11) conn = get(conn, "/users/#{user.nickname}?max_id=#{a11.id}") @@ -77,7 +77,7 @@ defmodule Pleroma.Web.StaticFE.StaticFEControllerTest do describe "notice html" do test "single notice page", %{conn: conn, user: user} do - {:ok, activity} = CommonAPI.post(user, %{"status" => "testing a thing!"}) + {:ok, activity} = CommonAPI.post(user, %{status: "testing a thing!"}) conn = get(conn, "/notice/#{activity.id}") @@ -89,7 +89,7 @@ defmodule Pleroma.Web.StaticFE.StaticFEControllerTest do test "filters HTML tags", %{conn: conn} do user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "<script>alert('xss')</script>"}) + {:ok, activity} = CommonAPI.post(user, %{status: "<script>alert('xss')</script>"}) conn = conn @@ -101,11 +101,11 @@ defmodule Pleroma.Web.StaticFE.StaticFEControllerTest do end test "shows the whole thread", %{conn: conn, user: user} do - {:ok, activity} = CommonAPI.post(user, %{"status" => "space: the final frontier"}) + {:ok, activity} = CommonAPI.post(user, %{status: "space: the final frontier"}) CommonAPI.post(user, %{ - "status" => "these are the voyages or something", - "in_reply_to_status_id" => activity.id + status: "these are the voyages or something", + in_reply_to_status_id: activity.id }) conn = get(conn, "/notice/#{activity.id}") @@ -117,7 +117,7 @@ defmodule Pleroma.Web.StaticFE.StaticFEControllerTest do test "redirect by AP object ID", %{conn: conn, user: user} do {:ok, %Activity{data: %{"object" => object_url}}} = - CommonAPI.post(user, %{"status" => "beam me up"}) + CommonAPI.post(user, %{status: "beam me up"}) conn = get(conn, URI.parse(object_url).path) @@ -126,7 +126,7 @@ defmodule Pleroma.Web.StaticFE.StaticFEControllerTest do test "redirect by activity ID", %{conn: conn, user: user} do {:ok, %Activity{data: %{"id" => id}}} = - CommonAPI.post(user, %{"status" => "I'm a doctor, not a devops!"}) + CommonAPI.post(user, %{status: "I'm a doctor, not a devops!"}) conn = get(conn, URI.parse(id).path) @@ -140,8 +140,7 @@ defmodule Pleroma.Web.StaticFE.StaticFEControllerTest do end test "404 for private status", %{conn: conn, user: user} do - {:ok, activity} = - CommonAPI.post(user, %{"status" => "don't show me!", "visibility" => "private"}) + {:ok, activity} = CommonAPI.post(user, %{status: "don't show me!", visibility: "private"}) conn = get(conn, "/notice/#{activity.id}") @@ -171,7 +170,7 @@ defmodule Pleroma.Web.StaticFE.StaticFEControllerTest do end test "it requires authentication if instance is NOT federating", %{conn: conn, user: user} do - {:ok, activity} = CommonAPI.post(user, %{"status" => "testing a thing!"}) + {:ok, activity} = CommonAPI.post(user, %{status: "testing a thing!"}) ensure_federating_or_authenticated(conn, "/notice/#{activity.id}", user) end diff --git a/test/web/streamer/ping_test.exs b/test/web/streamer/ping_test.exs deleted file mode 100644 index 5df6c1cc3..000000000 --- a/test/web/streamer/ping_test.exs +++ /dev/null @@ -1,36 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.PingTest do - use Pleroma.DataCase - - import Pleroma.Factory - alias Pleroma.Web.Streamer - - setup do - start_supervised({Streamer.supervisor(), [ping_interval: 30]}) - - :ok - end - - describe "sockets" do - setup do - user = insert(:user) - {:ok, %{user: user}} - end - - test "it sends pings", %{user: user} do - task = - Task.async(fn -> - assert_receive {:text, received_event}, 40 - assert_receive {:text, received_event}, 40 - assert_receive {:text, received_event}, 40 - end) - - Streamer.add_socket("public", %{transport_pid: task.pid, assigns: %{user: user}}) - - Task.await(task) - end - end -end diff --git a/test/web/streamer/state_test.exs b/test/web/streamer/state_test.exs deleted file mode 100644 index a755e75c0..000000000 --- a/test/web/streamer/state_test.exs +++ /dev/null @@ -1,54 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.StateTest do - use Pleroma.DataCase - - import Pleroma.Factory - alias Pleroma.Web.Streamer - alias Pleroma.Web.Streamer.StreamerSocket - - @moduletag needs_streamer: true - - describe "sockets" do - setup do - user = insert(:user) - user2 = insert(:user) - {:ok, %{user: user, user2: user2}} - end - - test "it can add a socket", %{user: user} do - Streamer.add_socket("public", %{transport_pid: 1, assigns: %{user: user}}) - - assert(%{"public" => [%StreamerSocket{transport_pid: 1}]} = Streamer.get_sockets()) - end - - test "it can add multiple sockets per user", %{user: user} do - Streamer.add_socket("public", %{transport_pid: 1, assigns: %{user: user}}) - Streamer.add_socket("public", %{transport_pid: 2, assigns: %{user: user}}) - - assert( - %{ - "public" => [ - %StreamerSocket{transport_pid: 2}, - %StreamerSocket{transport_pid: 1} - ] - } = Streamer.get_sockets() - ) - end - - test "it will not add a duplicate socket", %{user: user} do - Streamer.add_socket("activity", %{transport_pid: 1, assigns: %{user: user}}) - Streamer.add_socket("activity", %{transport_pid: 1, assigns: %{user: user}}) - - assert( - %{ - "activity" => [ - %StreamerSocket{transport_pid: 1} - ] - } = Streamer.get_sockets() - ) - end - end -end diff --git a/test/web/streamer/streamer_test.exs b/test/web/streamer/streamer_test.exs index a5d6e8ecf..95b7d1420 100644 --- a/test/web/streamer/streamer_test.exs +++ b/test/web/streamer/streamer_test.exs @@ -12,15 +12,81 @@ defmodule Pleroma.Web.StreamerTest do alias Pleroma.User alias Pleroma.Web.CommonAPI alias Pleroma.Web.Streamer - alias Pleroma.Web.Streamer.StreamerSocket - alias Pleroma.Web.Streamer.Worker @moduletag needs_streamer: true, capture_log: true - @streamer_timeout 150 - @streamer_start_wait 10 setup do: clear_config([:instance, :skip_thread_containment]) + describe "get_topic without an user" do + test "allows public" do + assert {:ok, "public"} = Streamer.get_topic("public", nil) + assert {:ok, "public:local"} = Streamer.get_topic("public:local", nil) + assert {:ok, "public:media"} = Streamer.get_topic("public:media", nil) + assert {:ok, "public:local:media"} = Streamer.get_topic("public:local:media", nil) + end + + test "allows hashtag streams" do + assert {:ok, "hashtag:cofe"} = Streamer.get_topic("hashtag", nil, %{"tag" => "cofe"}) + end + + test "disallows user streams" do + assert {:error, _} = Streamer.get_topic("user", nil) + assert {:error, _} = Streamer.get_topic("user:notification", nil) + assert {:error, _} = Streamer.get_topic("direct", nil) + end + + test "disallows list streams" do + assert {:error, _} = Streamer.get_topic("list", nil, %{"list" => 42}) + end + end + + describe "get_topic with an user" do + setup do + user = insert(:user) + {:ok, %{user: user}} + end + + test "allows public streams", %{user: user} do + assert {:ok, "public"} = Streamer.get_topic("public", user) + assert {:ok, "public:local"} = Streamer.get_topic("public:local", user) + assert {:ok, "public:media"} = Streamer.get_topic("public:media", user) + assert {:ok, "public:local:media"} = Streamer.get_topic("public:local:media", user) + end + + test "allows user streams", %{user: user} do + expected_user_topic = "user:#{user.id}" + expected_notif_topic = "user:notification:#{user.id}" + expected_direct_topic = "direct:#{user.id}" + assert {:ok, ^expected_user_topic} = Streamer.get_topic("user", user) + assert {:ok, ^expected_notif_topic} = Streamer.get_topic("user:notification", user) + assert {:ok, ^expected_direct_topic} = Streamer.get_topic("direct", user) + end + + test "allows hashtag streams", %{user: user} do + assert {:ok, "hashtag:cofe"} = Streamer.get_topic("hashtag", user, %{"tag" => "cofe"}) + end + + test "disallows registering to an user stream", %{user: user} do + another_user = insert(:user) + assert {:error, _} = Streamer.get_topic("user:#{another_user.id}", user) + assert {:error, _} = Streamer.get_topic("user:notification:#{another_user.id}", user) + assert {:error, _} = Streamer.get_topic("direct:#{another_user.id}", user) + end + + test "allows list stream that are owned by the user", %{user: user} do + {:ok, list} = List.create("Test", user) + assert {:error, _} = Streamer.get_topic("list:#{list.id}", user) + assert {:ok, _} = Streamer.get_topic("list", user, %{"list" => list.id}) + end + + test "disallows list stream that are not owned by the user", %{user: user} do + another_user = insert(:user) + {:ok, list} = List.create("Test", another_user) + assert {:error, _} = Streamer.get_topic("list:#{list.id}", user) + assert {:error, _} = Streamer.get_topic("list", user, %{"list" => list.id}) + end + end + describe "user streams" do setup do user = insert(:user) @@ -28,34 +94,36 @@ defmodule Pleroma.Web.StreamerTest do {:ok, %{user: user, notify: notify}} end - test "it sends notify to in the 'user' stream", %{user: user, notify: notify} do - task = - Task.async(fn -> - assert_receive {:text, _}, @streamer_timeout - end) + test "it streams the user's post in the 'user' stream", %{user: user} do + Streamer.get_topic_and_add_socket("user", user) + {:ok, activity} = CommonAPI.post(user, %{status: "hey"}) + assert_receive {:render_with_user, _, _, ^activity} + refute Streamer.filtered_by_user?(user, activity) + end - Streamer.add_socket( - "user", - %{transport_pid: task.pid, assigns: %{user: user}} - ) + test "it streams boosts of the user in the 'user' stream", %{user: user} do + Streamer.get_topic_and_add_socket("user", user) + other_user = insert(:user) + {:ok, activity} = CommonAPI.post(other_user, %{status: "hey"}) + {:ok, announce, _} = CommonAPI.repeat(activity.id, user) + + assert_receive {:render_with_user, Pleroma.Web.StreamerView, "update.json", ^announce} + refute Streamer.filtered_by_user?(user, announce) + end + + test "it sends notify to in the 'user' stream", %{user: user, notify: notify} do + Streamer.get_topic_and_add_socket("user", user) Streamer.stream("user", notify) - Task.await(task) + assert_receive {:render_with_user, _, _, ^notify} + refute Streamer.filtered_by_user?(user, notify) end test "it sends notify to in the 'user:notification' stream", %{user: user, notify: notify} do - task = - Task.async(fn -> - assert_receive {:text, _}, @streamer_timeout - end) - - Streamer.add_socket( - "user:notification", - %{transport_pid: task.pid, assigns: %{user: user}} - ) - + Streamer.get_topic_and_add_socket("user:notification", user) Streamer.stream("user:notification", notify) - Task.await(task) + assert_receive {:render_with_user, _, _, ^notify} + refute Streamer.filtered_by_user?(user, notify) end test "it doesn't send notify to the 'user:notification' stream when a user is blocked", %{ @@ -64,18 +132,12 @@ defmodule Pleroma.Web.StreamerTest do blocked = insert(:user) {:ok, _user_relationship} = User.block(user, blocked) - {:ok, activity} = CommonAPI.post(user, %{"status" => ":("}) - {:ok, notif, _} = CommonAPI.favorite(activity.id, blocked) + Streamer.get_topic_and_add_socket("user:notification", user) - task = Task.async(fn -> refute_receive {:text, _}, @streamer_timeout end) + {:ok, activity} = CommonAPI.post(user, %{status: ":("}) + {:ok, _} = CommonAPI.favorite(blocked, activity.id) - Streamer.add_socket( - "user:notification", - %{transport_pid: task.pid, assigns: %{user: user}} - ) - - Streamer.stream("user:notification", notif) - Task.await(task) + refute_receive _ end test "it doesn't send notify to the 'user:notification' stream when a thread is muted", %{ @@ -83,121 +145,116 @@ defmodule Pleroma.Web.StreamerTest do } do user2 = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "super hot take"}) - {:ok, activity} = CommonAPI.add_mute(user, activity) - {:ok, notif, _} = CommonAPI.favorite(activity.id, user2) + {:ok, activity} = CommonAPI.post(user, %{status: "super hot take"}) + {:ok, _} = CommonAPI.add_mute(user, activity) - task = Task.async(fn -> refute_receive {:text, _}, @streamer_timeout end) + Streamer.get_topic_and_add_socket("user:notification", user) - Streamer.add_socket( - "user:notification", - %{transport_pid: task.pid, assigns: %{user: user}} - ) + {:ok, favorite_activity} = CommonAPI.favorite(user2, activity.id) - Streamer.stream("user:notification", notif) - Task.await(task) + refute_receive _ + assert Streamer.filtered_by_user?(user, favorite_activity) end - test "it doesn't send notify to the 'user:notification' stream' when a domain is blocked", %{ + test "it sends favorite to 'user:notification' stream'", %{ user: user } do user2 = insert(:user, %{ap_id: "https://hecking-lewd-place.com/user/meanie"}) - {:ok, user} = User.block_domain(user, "hecking-lewd-place.com") - {:ok, activity} = CommonAPI.post(user, %{"status" => "super hot take"}) - {:ok, notif, _} = CommonAPI.favorite(activity.id, user2) + {:ok, activity} = CommonAPI.post(user, %{status: "super hot take"}) + Streamer.get_topic_and_add_socket("user:notification", user) + {:ok, favorite_activity} = CommonAPI.favorite(user2, activity.id) - task = Task.async(fn -> refute_receive {:text, _}, @streamer_timeout end) + assert_receive {:render_with_user, _, "notification.json", notif} + assert notif.activity.id == favorite_activity.id + refute Streamer.filtered_by_user?(user, notif) + end - Streamer.add_socket( - "user:notification", - %{transport_pid: task.pid, assigns: %{user: user}} - ) + test "it doesn't send the 'user:notification' stream' when a domain is blocked", %{ + user: user + } do + user2 = insert(:user, %{ap_id: "https://hecking-lewd-place.com/user/meanie"}) - Streamer.stream("user:notification", notif) - Task.await(task) + {:ok, user} = User.block_domain(user, "hecking-lewd-place.com") + {:ok, activity} = CommonAPI.post(user, %{status: "super hot take"}) + Streamer.get_topic_and_add_socket("user:notification", user) + {:ok, favorite_activity} = CommonAPI.favorite(user2, activity.id) + + refute_receive _ + assert Streamer.filtered_by_user?(user, favorite_activity) end test "it sends follow activities to the 'user:notification' stream", %{ user: user } do + user_url = user.ap_id user2 = insert(:user) - task = Task.async(fn -> assert_receive {:text, _}, @streamer_timeout end) - Process.sleep(@streamer_start_wait) + body = + File.read!("test/fixtures/users_mock/localhost.json") + |> String.replace("{{nickname}}", user.nickname) + |> Jason.encode!() - Streamer.add_socket( - "user:notification", - %{transport_pid: task.pid, assigns: %{user: user}} - ) + Tesla.Mock.mock_global(fn + %{method: :get, url: ^user_url} -> + %Tesla.Env{status: 200, body: body} + end) - {:ok, _follower, _followed, _activity} = CommonAPI.follow(user2, user) + Streamer.get_topic_and_add_socket("user:notification", user) + {:ok, _follower, _followed, follow_activity} = CommonAPI.follow(user2, user) - # We don't directly pipe the notification to the streamer as it's already - # generated as a side effect of CommonAPI.follow(). - Task.await(task) + assert_receive {:render_with_user, _, "notification.json", notif} + assert notif.activity.id == follow_activity.id + refute Streamer.filtered_by_user?(user, notif) end end - test "it sends to public" do + test "it sends to public authenticated" do user = insert(:user) other_user = insert(:user) - task = - Task.async(fn -> - assert_receive {:text, _}, @streamer_timeout - end) - - fake_socket = %StreamerSocket{ - transport_pid: task.pid, - user: user - } - - {:ok, activity} = CommonAPI.post(other_user, %{"status" => "Test"}) + Streamer.get_topic_and_add_socket("public", other_user) - topics = %{ - "public" => [fake_socket] - } - - Worker.push_to_socket(topics, "public", activity) - - Task.await(task) + {:ok, activity} = CommonAPI.post(user, %{status: "Test"}) + assert_receive {:render_with_user, _, _, ^activity} + refute Streamer.filtered_by_user?(user, activity) + end - task = - Task.async(fn -> - expected_event = - %{ - "event" => "delete", - "payload" => activity.id - } - |> Jason.encode!() + test "works for deletions" do + user = insert(:user) + other_user = insert(:user) + {:ok, activity} = CommonAPI.post(other_user, %{status: "Test"}) - assert_receive {:text, received_event}, @streamer_timeout - assert received_event == expected_event - end) + Streamer.get_topic_and_add_socket("public", user) - fake_socket = %StreamerSocket{ - transport_pid: task.pid, - user: user - } + {:ok, _} = CommonAPI.delete(activity.id, other_user) + activity_id = activity.id + assert_receive {:text, event} + assert %{"event" => "delete", "payload" => ^activity_id} = Jason.decode!(event) + end - {:ok, activity} = CommonAPI.delete(activity.id, other_user) + test "it sends to public unauthenticated" do + user = insert(:user) - topics = %{ - "public" => [fake_socket] - } + Streamer.get_topic_and_add_socket("public", nil) - Worker.push_to_socket(topics, "public", activity) + {:ok, activity} = CommonAPI.post(user, %{status: "Test"}) + activity_id = activity.id + assert_receive {:text, event} + assert %{"event" => "update", "payload" => payload} = Jason.decode!(event) + assert %{"id" => ^activity_id} = Jason.decode!(payload) - Task.await(task) + {:ok, _} = CommonAPI.delete(activity.id, user) + assert_receive {:text, event} + assert %{"event" => "delete", "payload" => ^activity_id} = Jason.decode!(event) end describe "thread_containment" do - test "it doesn't send to user if recipients invalid and thread containment is enabled" do + test "it filters to user if recipients invalid and thread containment is enabled" do Pleroma.Config.put([:instance, :skip_thread_containment], false) author = insert(:user) user = insert(:user) - User.follow(user, author, "accept") + User.follow(user, author, :follow_accept) activity = insert(:note_activity, @@ -208,19 +265,17 @@ defmodule Pleroma.Web.StreamerTest do ) ) - task = Task.async(fn -> refute_receive {:text, _}, 1_000 end) - fake_socket = %StreamerSocket{transport_pid: task.pid, user: user} - topics = %{"public" => [fake_socket]} - Worker.push_to_socket(topics, "public", activity) - - Task.await(task) + Streamer.get_topic_and_add_socket("public", user) + Streamer.stream("public", activity) + assert_receive {:render_with_user, _, _, ^activity} + assert Streamer.filtered_by_user?(user, activity) end test "it sends message if recipients invalid and thread containment is disabled" do Pleroma.Config.put([:instance, :skip_thread_containment], true) author = insert(:user) user = insert(:user) - User.follow(user, author, "accept") + User.follow(user, author, :follow_accept) activity = insert(:note_activity, @@ -231,19 +286,18 @@ defmodule Pleroma.Web.StreamerTest do ) ) - task = Task.async(fn -> assert_receive {:text, _}, 1_000 end) - fake_socket = %StreamerSocket{transport_pid: task.pid, user: user} - topics = %{"public" => [fake_socket]} - Worker.push_to_socket(topics, "public", activity) + Streamer.get_topic_and_add_socket("public", user) + Streamer.stream("public", activity) - Task.await(task) + assert_receive {:render_with_user, _, _, ^activity} + refute Streamer.filtered_by_user?(user, activity) end test "it sends message if recipients invalid and thread containment is enabled but user's thread containment is disabled" do Pleroma.Config.put([:instance, :skip_thread_containment], false) author = insert(:user) user = insert(:user, skip_thread_containment: true) - User.follow(user, author, "accept") + User.follow(user, author, :follow_accept) activity = insert(:note_activity, @@ -254,255 +308,168 @@ defmodule Pleroma.Web.StreamerTest do ) ) - task = Task.async(fn -> assert_receive {:text, _}, 1_000 end) - fake_socket = %StreamerSocket{transport_pid: task.pid, user: user} - topics = %{"public" => [fake_socket]} - Worker.push_to_socket(topics, "public", activity) + Streamer.get_topic_and_add_socket("public", user) + Streamer.stream("public", activity) - Task.await(task) + assert_receive {:render_with_user, _, _, ^activity} + refute Streamer.filtered_by_user?(user, activity) end end describe "blocks" do - test "it doesn't send messages involving blocked users" do + test "it filters messages involving blocked users" do user = insert(:user) blocked_user = insert(:user) {:ok, _user_relationship} = User.block(user, blocked_user) - {:ok, activity} = CommonAPI.post(blocked_user, %{"status" => "Test"}) - - task = - Task.async(fn -> - refute_receive {:text, _}, 1_000 - end) - - fake_socket = %StreamerSocket{ - transport_pid: task.pid, - user: user - } - - topics = %{ - "public" => [fake_socket] - } - - Worker.push_to_socket(topics, "public", activity) - - Task.await(task) + Streamer.get_topic_and_add_socket("public", user) + {:ok, activity} = CommonAPI.post(blocked_user, %{status: "Test"}) + assert_receive {:render_with_user, _, _, ^activity} + assert Streamer.filtered_by_user?(user, activity) end - test "it doesn't send messages transitively involving blocked users" do + test "it filters messages transitively involving blocked users" do blocker = insert(:user) blockee = insert(:user) friend = insert(:user) - task = - Task.async(fn -> - refute_receive {:text, _}, 1_000 - end) - - fake_socket = %StreamerSocket{ - transport_pid: task.pid, - user: blocker - } - - topics = %{ - "public" => [fake_socket] - } + Streamer.get_topic_and_add_socket("public", blocker) {:ok, _user_relationship} = User.block(blocker, blockee) - {:ok, activity_one} = CommonAPI.post(friend, %{"status" => "hey! @#{blockee.nickname}"}) + {:ok, activity_one} = CommonAPI.post(friend, %{status: "hey! @#{blockee.nickname}"}) - Worker.push_to_socket(topics, "public", activity_one) + assert_receive {:render_with_user, _, _, ^activity_one} + assert Streamer.filtered_by_user?(blocker, activity_one) - {:ok, activity_two} = CommonAPI.post(blockee, %{"status" => "hey! @#{friend.nickname}"}) + {:ok, activity_two} = CommonAPI.post(blockee, %{status: "hey! @#{friend.nickname}"}) - Worker.push_to_socket(topics, "public", activity_two) + assert_receive {:render_with_user, _, _, ^activity_two} + assert Streamer.filtered_by_user?(blocker, activity_two) - {:ok, activity_three} = CommonAPI.post(blockee, %{"status" => "hey! @#{blocker.nickname}"}) + {:ok, activity_three} = CommonAPI.post(blockee, %{status: "hey! @#{blocker.nickname}"}) - Worker.push_to_socket(topics, "public", activity_three) - - Task.await(task) + assert_receive {:render_with_user, _, _, ^activity_three} + assert Streamer.filtered_by_user?(blocker, activity_three) end end - test "it doesn't send unwanted DMs to list" do - user_a = insert(:user) - user_b = insert(:user) - user_c = insert(:user) - - {:ok, user_a} = User.follow(user_a, user_b) - - {:ok, list} = List.create("Test", user_a) - {:ok, list} = List.follow(list, user_b) - - {:ok, activity} = - CommonAPI.post(user_b, %{ - "status" => "@#{user_c.nickname} Test", - "visibility" => "direct" - }) - - task = - Task.async(fn -> - refute_receive {:text, _}, 1_000 - end) - - fake_socket = %StreamerSocket{ - transport_pid: task.pid, - user: user_a - } - - topics = %{ - "list:#{list.id}" => [fake_socket] - } - - Worker.handle_call({:stream, "list", activity}, self(), topics) - - Task.await(task) - end - - test "it doesn't send unwanted private posts to list" do - user_a = insert(:user) - user_b = insert(:user) + describe "lists" do + test "it doesn't send unwanted DMs to list" do + user_a = insert(:user) + user_b = insert(:user) + user_c = insert(:user) - {:ok, list} = List.create("Test", user_a) - {:ok, list} = List.follow(list, user_b) + {:ok, user_a} = User.follow(user_a, user_b) - {:ok, activity} = - CommonAPI.post(user_b, %{ - "status" => "Test", - "visibility" => "private" - }) + {:ok, list} = List.create("Test", user_a) + {:ok, list} = List.follow(list, user_b) - task = - Task.async(fn -> - refute_receive {:text, _}, 1_000 - end) + Streamer.get_topic_and_add_socket("list", user_a, %{"list" => list.id}) - fake_socket = %StreamerSocket{ - transport_pid: task.pid, - user: user_a - } + {:ok, _activity} = + CommonAPI.post(user_b, %{ + status: "@#{user_c.nickname} Test", + visibility: "direct" + }) - topics = %{ - "list:#{list.id}" => [fake_socket] - } + refute_receive _ + end - Worker.handle_call({:stream, "list", activity}, self(), topics) + test "it doesn't send unwanted private posts to list" do + user_a = insert(:user) + user_b = insert(:user) - Task.await(task) - end + {:ok, list} = List.create("Test", user_a) + {:ok, list} = List.follow(list, user_b) - test "it sends wanted private posts to list" do - user_a = insert(:user) - user_b = insert(:user) + Streamer.get_topic_and_add_socket("list", user_a, %{"list" => list.id}) - {:ok, user_a} = User.follow(user_a, user_b) + {:ok, _activity} = + CommonAPI.post(user_b, %{ + status: "Test", + visibility: "private" + }) - {:ok, list} = List.create("Test", user_a) - {:ok, list} = List.follow(list, user_b) + refute_receive _ + end - {:ok, activity} = - CommonAPI.post(user_b, %{ - "status" => "Test", - "visibility" => "private" - }) + test "it sends wanted private posts to list" do + user_a = insert(:user) + user_b = insert(:user) - task = - Task.async(fn -> - assert_receive {:text, _}, 1_000 - end) + {:ok, user_a} = User.follow(user_a, user_b) - fake_socket = %StreamerSocket{ - transport_pid: task.pid, - user: user_a - } + {:ok, list} = List.create("Test", user_a) + {:ok, list} = List.follow(list, user_b) - Streamer.add_socket( - "list:#{list.id}", - fake_socket - ) + Streamer.get_topic_and_add_socket("list", user_a, %{"list" => list.id}) - Worker.handle_call({:stream, "list", activity}, self(), %{}) + {:ok, activity} = + CommonAPI.post(user_b, %{ + status: "Test", + visibility: "private" + }) - Task.await(task) + assert_receive {:render_with_user, _, _, ^activity} + refute Streamer.filtered_by_user?(user_a, activity) + end end - test "it doesn't send muted reblogs" do - user1 = insert(:user) - user2 = insert(:user) - user3 = insert(:user) - CommonAPI.hide_reblogs(user1, user2) - - {:ok, create_activity} = CommonAPI.post(user3, %{"status" => "I'm kawen"}) - {:ok, announce_activity, _} = CommonAPI.repeat(create_activity.id, user2) - - task = - Task.async(fn -> - refute_receive {:text, _}, 1_000 - end) - - fake_socket = %StreamerSocket{ - transport_pid: task.pid, - user: user1 - } - - topics = %{ - "public" => [fake_socket] - } - - Worker.push_to_socket(topics, "public", announce_activity) + describe "muted reblogs" do + test "it filters muted reblogs" do + user1 = insert(:user) + user2 = insert(:user) + user3 = insert(:user) + CommonAPI.follow(user1, user2) + CommonAPI.hide_reblogs(user1, user2) - Task.await(task) - end + {:ok, create_activity} = CommonAPI.post(user3, %{status: "I'm kawen"}) - test "it does send non-reblog notification for reblog-muted actors" do - user1 = insert(:user) - user2 = insert(:user) - user3 = insert(:user) - CommonAPI.hide_reblogs(user1, user2) + Streamer.get_topic_and_add_socket("user", user1) + {:ok, announce_activity, _} = CommonAPI.repeat(create_activity.id, user2) + assert_receive {:render_with_user, _, _, ^announce_activity} + assert Streamer.filtered_by_user?(user1, announce_activity) + end - {:ok, create_activity} = CommonAPI.post(user3, %{"status" => "I'm kawen"}) - {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, user2) + test "it filters reblog notification for reblog-muted actors" do + user1 = insert(:user) + user2 = insert(:user) + CommonAPI.follow(user1, user2) + CommonAPI.hide_reblogs(user1, user2) - task = - Task.async(fn -> - assert_receive {:text, _}, 1_000 - end) + {:ok, create_activity} = CommonAPI.post(user1, %{status: "I'm kawen"}) + Streamer.get_topic_and_add_socket("user", user1) + {:ok, _favorite_activity, _} = CommonAPI.repeat(create_activity.id, user2) - fake_socket = %StreamerSocket{ - transport_pid: task.pid, - user: user1 - } + assert_receive {:render_with_user, _, "notification.json", notif} + assert Streamer.filtered_by_user?(user1, notif) + end - topics = %{ - "public" => [fake_socket] - } + test "it send non-reblog notification for reblog-muted actors" do + user1 = insert(:user) + user2 = insert(:user) + CommonAPI.follow(user1, user2) + CommonAPI.hide_reblogs(user1, user2) - Worker.push_to_socket(topics, "public", favorite_activity) + {:ok, create_activity} = CommonAPI.post(user1, %{status: "I'm kawen"}) + Streamer.get_topic_and_add_socket("user", user1) + {:ok, _favorite_activity} = CommonAPI.favorite(user2, create_activity.id) - Task.await(task) + assert_receive {:render_with_user, _, "notification.json", notif} + refute Streamer.filtered_by_user?(user1, notif) + end end - test "it doesn't send posts from muted threads" do + test "it filters posts from muted threads" do user = insert(:user) user2 = insert(:user) + Streamer.get_topic_and_add_socket("user", user2) {:ok, user2, user, _activity} = CommonAPI.follow(user2, user) - - {:ok, activity} = CommonAPI.post(user, %{"status" => "super hot take"}) - - {:ok, activity} = CommonAPI.add_mute(user2, activity) - - task = Task.async(fn -> refute_receive {:text, _}, @streamer_timeout end) - - Streamer.add_socket( - "user", - %{transport_pid: task.pid, assigns: %{user: user2}} - ) - - Streamer.stream("user", activity) - Task.await(task) + {:ok, activity} = CommonAPI.post(user, %{status: "super hot take"}) + {:ok, _} = CommonAPI.add_mute(user2, activity) + assert_receive {:render_with_user, _, _, ^activity} + assert Streamer.filtered_by_user?(user2, activity) end describe "direct streams" do @@ -514,103 +481,88 @@ defmodule Pleroma.Web.StreamerTest do user = insert(:user) another_user = insert(:user) - task = - Task.async(fn -> - assert_receive {:text, received_event}, @streamer_timeout - - assert %{"event" => "conversation", "payload" => received_payload} = - Jason.decode!(received_event) - - assert %{"last_status" => last_status} = Jason.decode!(received_payload) - [participation] = Participation.for_user(user) - assert last_status["pleroma"]["direct_conversation_id"] == participation.id - end) - - Streamer.add_socket( - "direct", - %{transport_pid: task.pid, assigns: %{user: user}} - ) + Streamer.get_topic_and_add_socket("direct", user) {:ok, _create_activity} = CommonAPI.post(another_user, %{ - "status" => "hey @#{user.nickname}", - "visibility" => "direct" + status: "hey @#{user.nickname}", + visibility: "direct" }) - Task.await(task) + assert_receive {:text, received_event} + + assert %{"event" => "conversation", "payload" => received_payload} = + Jason.decode!(received_event) + + assert %{"last_status" => last_status} = Jason.decode!(received_payload) + [participation] = Participation.for_user(user) + assert last_status["pleroma"]["direct_conversation_id"] == participation.id end test "it doesn't send conversation update to the 'direct' stream when the last message in the conversation is deleted" do user = insert(:user) another_user = insert(:user) + Streamer.get_topic_and_add_socket("direct", user) + {:ok, create_activity} = CommonAPI.post(another_user, %{ - "status" => "hi @#{user.nickname}", - "visibility" => "direct" + status: "hi @#{user.nickname}", + visibility: "direct" }) - task = - Task.async(fn -> - assert_receive {:text, received_event}, @streamer_timeout - assert %{"event" => "delete", "payload" => _} = Jason.decode!(received_event) + create_activity_id = create_activity.id + assert_receive {:render_with_user, _, _, ^create_activity} + assert_receive {:text, received_conversation1} + assert %{"event" => "conversation", "payload" => _} = Jason.decode!(received_conversation1) - refute_receive {:text, _}, @streamer_timeout - end) + {:ok, _} = CommonAPI.delete(create_activity_id, another_user) - Process.sleep(@streamer_start_wait) + assert_receive {:text, received_event} - Streamer.add_socket( - "direct", - %{transport_pid: task.pid, assigns: %{user: user}} - ) + assert %{"event" => "delete", "payload" => ^create_activity_id} = + Jason.decode!(received_event) - {:ok, _} = CommonAPI.delete(create_activity.id, another_user) - - Task.await(task) + refute_receive _ end test "it sends conversation update to the 'direct' stream when a message is deleted" do user = insert(:user) another_user = insert(:user) + Streamer.get_topic_and_add_socket("direct", user) {:ok, create_activity} = CommonAPI.post(another_user, %{ - "status" => "hi @#{user.nickname}", - "visibility" => "direct" + status: "hi @#{user.nickname}", + visibility: "direct" }) {:ok, create_activity2} = CommonAPI.post(another_user, %{ - "status" => "hi @#{user.nickname}", - "in_reply_to_status_id" => create_activity.id, - "visibility" => "direct" + status: "hi @#{user.nickname} 2", + in_reply_to_status_id: create_activity.id, + visibility: "direct" }) - task = - Task.async(fn -> - assert_receive {:text, received_event}, @streamer_timeout - assert %{"event" => "delete", "payload" => _} = Jason.decode!(received_event) - - assert_receive {:text, received_event}, @streamer_timeout + assert_receive {:render_with_user, _, _, ^create_activity} + assert_receive {:render_with_user, _, _, ^create_activity2} + assert_receive {:text, received_conversation1} + assert %{"event" => "conversation", "payload" => _} = Jason.decode!(received_conversation1) + assert_receive {:text, received_conversation1} + assert %{"event" => "conversation", "payload" => _} = Jason.decode!(received_conversation1) - assert %{"event" => "conversation", "payload" => received_payload} = - Jason.decode!(received_event) - - assert %{"last_status" => last_status} = Jason.decode!(received_payload) - assert last_status["id"] == to_string(create_activity.id) - end) + {:ok, _} = CommonAPI.delete(create_activity2.id, another_user) - Process.sleep(@streamer_start_wait) + assert_receive {:text, received_event} + assert %{"event" => "delete", "payload" => _} = Jason.decode!(received_event) - Streamer.add_socket( - "direct", - %{transport_pid: task.pid, assigns: %{user: user}} - ) + assert_receive {:text, received_event} - {:ok, _} = CommonAPI.delete(create_activity2.id, another_user) + assert %{"event" => "conversation", "payload" => received_payload} = + Jason.decode!(received_event) - Task.await(task) + assert %{"last_status" => last_status} = Jason.decode!(received_payload) + assert last_status["id"] == to_string(create_activity.id) end end end diff --git a/test/web/twitter_api/password_controller_test.exs b/test/web/twitter_api/password_controller_test.exs index 0a24860d3..231a46c67 100644 --- a/test/web/twitter_api/password_controller_test.exs +++ b/test/web/twitter_api/password_controller_test.exs @@ -54,7 +54,7 @@ defmodule Pleroma.Web.TwitterAPI.PasswordControllerTest do assert response =~ "<h2>Password changed!</h2>" user = refresh_record(user) - assert Comeonin.Pbkdf2.checkpw("test", user.password_hash) + assert Pbkdf2.verify_pass("test", user.password_hash) assert Enum.empty?(Token.get_user_tokens(user)) end diff --git a/test/web/twitter_api/remote_follow_controller_test.exs b/test/web/twitter_api/remote_follow_controller_test.exs index 5ff8694a8..f7e54c26a 100644 --- a/test/web/twitter_api/remote_follow_controller_test.exs +++ b/test/web/twitter_api/remote_follow_controller_test.exs @@ -6,11 +6,14 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do use Pleroma.Web.ConnCase alias Pleroma.Config + alias Pleroma.MFA + alias Pleroma.MFA.TOTP alias Pleroma.User alias Pleroma.Web.CommonAPI import ExUnit.CaptureLog import Pleroma.Factory + import Ecto.Query setup do Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end) @@ -160,6 +163,119 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do end end + describe "POST /ostatus_subscribe - follow/2 with enabled Two-Factor Auth " do + test "render the MFA login form", %{conn: conn} do + otp_secret = TOTP.generate_secret() + + user = + insert(:user, + multi_factor_authentication_settings: %MFA.Settings{ + enabled: true, + totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true} + } + ) + + user2 = insert(:user) + + response = + conn + |> post(remote_follow_path(conn, :do_follow), %{ + "authorization" => %{"name" => user.nickname, "password" => "test", "id" => user2.id} + }) + |> response(200) + + mfa_token = Pleroma.Repo.one(from(q in Pleroma.MFA.Token, where: q.user_id == ^user.id)) + + assert response =~ "Two-factor authentication" + assert response =~ "Authentication code" + assert response =~ mfa_token.token + refute user2.follower_address in User.following(user) + end + + test "returns error when password is incorrect", %{conn: conn} do + otp_secret = TOTP.generate_secret() + + user = + insert(:user, + multi_factor_authentication_settings: %MFA.Settings{ + enabled: true, + totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true} + } + ) + + user2 = insert(:user) + + response = + conn + |> post(remote_follow_path(conn, :do_follow), %{ + "authorization" => %{"name" => user.nickname, "password" => "test1", "id" => user2.id} + }) + |> response(200) + + assert response =~ "Wrong username or password" + refute user2.follower_address in User.following(user) + end + + test "follows", %{conn: conn} do + otp_secret = TOTP.generate_secret() + + user = + insert(:user, + multi_factor_authentication_settings: %MFA.Settings{ + enabled: true, + totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true} + } + ) + + {:ok, %{token: token}} = MFA.Token.create_token(user) + + user2 = insert(:user) + otp_token = TOTP.generate_token(otp_secret) + + conn = + conn + |> post( + remote_follow_path(conn, :do_follow), + %{ + "mfa" => %{"code" => otp_token, "token" => token, "id" => user2.id} + } + ) + + assert redirected_to(conn) == "/users/#{user2.id}" + assert user2.follower_address in User.following(user) + end + + test "returns error when auth code is incorrect", %{conn: conn} do + otp_secret = TOTP.generate_secret() + + user = + insert(:user, + multi_factor_authentication_settings: %MFA.Settings{ + enabled: true, + totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true} + } + ) + + {:ok, %{token: token}} = MFA.Token.create_token(user) + + user2 = insert(:user) + otp_token = TOTP.generate_token(TOTP.generate_secret()) + + response = + conn + |> post( + remote_follow_path(conn, :do_follow), + %{ + "mfa" => %{"code" => otp_token, "token" => token, "id" => user2.id} + } + ) + |> response(200) + + assert response =~ "Wrong authentication code" + refute user2.follower_address in User.following(user) + end + end + describe "POST /ostatus_subscribe - follow/2 without assigned user " do test "follows", %{conn: conn} do user = insert(:user) diff --git a/test/web/twitter_api/twitter_api_controller_test.exs b/test/web/twitter_api/twitter_api_controller_test.exs index ab0a2c3df..464d0ea2e 100644 --- a/test/web/twitter_api/twitter_api_controller_test.exs +++ b/test/web/twitter_api/twitter_api_controller_test.exs @@ -19,13 +19,9 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do end test "with credentials, without any params" do - %{user: current_user, conn: conn} = - oauth_access(["read:notifications", "write:notifications"]) + %{conn: conn} = oauth_access(["write:notifications"]) - conn = - conn - |> assign(:user, current_user) - |> post("/api/qvitter/statuses/notifications/read") + conn = post(conn, "/api/qvitter/statuses/notifications/read") assert json_response(conn, 400) == %{ "error" => "You need to specify latest_id", diff --git a/test/web/twitter_api/twitter_api_test.exs b/test/web/twitter_api/twitter_api_test.exs index 92f9aa0f5..368533292 100644 --- a/test/web/twitter_api/twitter_api_test.exs +++ b/test/web/twitter_api/twitter_api_test.exs @@ -18,11 +18,11 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do test "it registers a new user and returns the user." do data = %{ - "nickname" => "lain", - "email" => "lain@wired.jp", - "fullname" => "lain iwakura", - "password" => "bear", - "confirm" => "bear" + :username => "lain", + :email => "lain@wired.jp", + :fullname => "lain iwakura", + :password => "bear", + :confirm => "bear" } {:ok, user} = TwitterAPI.register_user(data) @@ -35,12 +35,12 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do test "it registers a new user with empty string in bio and returns the user." do data = %{ - "nickname" => "lain", - "email" => "lain@wired.jp", - "fullname" => "lain iwakura", - "bio" => "", - "password" => "bear", - "confirm" => "bear" + :username => "lain", + :email => "lain@wired.jp", + :fullname => "lain iwakura", + :bio => "", + :password => "bear", + :confirm => "bear" } {:ok, user} = TwitterAPI.register_user(data) @@ -60,12 +60,12 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do end data = %{ - "nickname" => "lain", - "email" => "lain@wired.jp", - "fullname" => "lain iwakura", - "bio" => "", - "password" => "bear", - "confirm" => "bear" + :username => "lain", + :email => "lain@wired.jp", + :fullname => "lain iwakura", + :bio => "", + :password => "bear", + :confirm => "bear" } {:ok, user} = TwitterAPI.register_user(data) @@ -87,29 +87,29 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do test "it registers a new user and parses mentions in the bio" do data1 = %{ - "nickname" => "john", - "email" => "john@gmail.com", - "fullname" => "John Doe", - "bio" => "test", - "password" => "bear", - "confirm" => "bear" + :username => "john", + :email => "john@gmail.com", + :fullname => "John Doe", + :bio => "test", + :password => "bear", + :confirm => "bear" } {:ok, user1} = TwitterAPI.register_user(data1) data2 = %{ - "nickname" => "lain", - "email" => "lain@wired.jp", - "fullname" => "lain iwakura", - "bio" => "@john test", - "password" => "bear", - "confirm" => "bear" + :username => "lain", + :email => "lain@wired.jp", + :fullname => "lain iwakura", + :bio => "@john test", + :password => "bear", + :confirm => "bear" } {:ok, user2} = TwitterAPI.register_user(data2) expected_text = - ~s(<span class="h-card"><a data-user="#{user1.id}" class="u-url mention" href="#{ + ~s(<span class="h-card"><a class="u-url mention" data-user="#{user1.id}" href="#{ user1.ap_id }" rel="ugc">@<span>john</span></a></span> test) @@ -123,13 +123,13 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do {:ok, invite} = UserInviteToken.create_invite() data = %{ - "nickname" => "vinny", - "email" => "pasta@pizza.vs", - "fullname" => "Vinny Vinesauce", - "bio" => "streamer", - "password" => "hiptofbees", - "confirm" => "hiptofbees", - "token" => invite.token + :username => "vinny", + :email => "pasta@pizza.vs", + :fullname => "Vinny Vinesauce", + :bio => "streamer", + :password => "hiptofbees", + :confirm => "hiptofbees", + :token => invite.token } {:ok, user} = TwitterAPI.register_user(data) @@ -145,13 +145,13 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do test "returns error on invalid token" do data = %{ - "nickname" => "GrimReaper", - "email" => "death@reapers.afterlife", - "fullname" => "Reaper Grim", - "bio" => "Your time has come", - "password" => "scythe", - "confirm" => "scythe", - "token" => "DudeLetMeInImAFairy" + :username => "GrimReaper", + :email => "death@reapers.afterlife", + :fullname => "Reaper Grim", + :bio => "Your time has come", + :password => "scythe", + :confirm => "scythe", + :token => "DudeLetMeInImAFairy" } {:error, msg} = TwitterAPI.register_user(data) @@ -165,13 +165,13 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do UserInviteToken.update_invite!(invite, used: true) data = %{ - "nickname" => "GrimReaper", - "email" => "death@reapers.afterlife", - "fullname" => "Reaper Grim", - "bio" => "Your time has come", - "password" => "scythe", - "confirm" => "scythe", - "token" => invite.token + :username => "GrimReaper", + :email => "death@reapers.afterlife", + :fullname => "Reaper Grim", + :bio => "Your time has come", + :password => "scythe", + :confirm => "scythe", + :token => invite.token } {:error, msg} = TwitterAPI.register_user(data) @@ -186,16 +186,16 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do setup do data = %{ - "nickname" => "vinny", - "email" => "pasta@pizza.vs", - "fullname" => "Vinny Vinesauce", - "bio" => "streamer", - "password" => "hiptofbees", - "confirm" => "hiptofbees" + :username => "vinny", + :email => "pasta@pizza.vs", + :fullname => "Vinny Vinesauce", + :bio => "streamer", + :password => "hiptofbees", + :confirm => "hiptofbees" } check_fn = fn invite -> - data = Map.put(data, "token", invite.token) + data = Map.put(data, :token, invite.token) {:ok, user} = TwitterAPI.register_user(data) fetched_user = User.get_cached_by_nickname("vinny") @@ -250,13 +250,13 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do UserInviteToken.update_invite!(invite, uses: 99) data = %{ - "nickname" => "vinny", - "email" => "pasta@pizza.vs", - "fullname" => "Vinny Vinesauce", - "bio" => "streamer", - "password" => "hiptofbees", - "confirm" => "hiptofbees", - "token" => invite.token + :username => "vinny", + :email => "pasta@pizza.vs", + :fullname => "Vinny Vinesauce", + :bio => "streamer", + :password => "hiptofbees", + :confirm => "hiptofbees", + :token => invite.token } {:ok, user} = TwitterAPI.register_user(data) @@ -269,13 +269,13 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do AccountView.render("show.json", %{user: fetched_user}) data = %{ - "nickname" => "GrimReaper", - "email" => "death@reapers.afterlife", - "fullname" => "Reaper Grim", - "bio" => "Your time has come", - "password" => "scythe", - "confirm" => "scythe", - "token" => invite.token + :username => "GrimReaper", + :email => "death@reapers.afterlife", + :fullname => "Reaper Grim", + :bio => "Your time has come", + :password => "scythe", + :confirm => "scythe", + :token => invite.token } {:error, msg} = TwitterAPI.register_user(data) @@ -292,13 +292,13 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do {:ok, invite} = UserInviteToken.create_invite(%{expires_at: Date.utc_today(), max_use: 100}) data = %{ - "nickname" => "vinny", - "email" => "pasta@pizza.vs", - "fullname" => "Vinny Vinesauce", - "bio" => "streamer", - "password" => "hiptofbees", - "confirm" => "hiptofbees", - "token" => invite.token + :username => "vinny", + :email => "pasta@pizza.vs", + :fullname => "Vinny Vinesauce", + :bio => "streamer", + :password => "hiptofbees", + :confirm => "hiptofbees", + :token => invite.token } {:ok, user} = TwitterAPI.register_user(data) @@ -317,13 +317,13 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do UserInviteToken.update_invite!(invite, uses: 99) data = %{ - "nickname" => "vinny", - "email" => "pasta@pizza.vs", - "fullname" => "Vinny Vinesauce", - "bio" => "streamer", - "password" => "hiptofbees", - "confirm" => "hiptofbees", - "token" => invite.token + :username => "vinny", + :email => "pasta@pizza.vs", + :fullname => "Vinny Vinesauce", + :bio => "streamer", + :password => "hiptofbees", + :confirm => "hiptofbees", + :token => invite.token } {:ok, user} = TwitterAPI.register_user(data) @@ -335,13 +335,13 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do AccountView.render("show.json", %{user: fetched_user}) data = %{ - "nickname" => "GrimReaper", - "email" => "death@reapers.afterlife", - "fullname" => "Reaper Grim", - "bio" => "Your time has come", - "password" => "scythe", - "confirm" => "scythe", - "token" => invite.token + :username => "GrimReaper", + :email => "death@reapers.afterlife", + :fullname => "Reaper Grim", + :bio => "Your time has come", + :password => "scythe", + :confirm => "scythe", + :token => invite.token } {:error, msg} = TwitterAPI.register_user(data) @@ -355,13 +355,13 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do UserInviteToken.create_invite(%{expires_at: Date.add(Date.utc_today(), -1), max_use: 100}) data = %{ - "nickname" => "GrimReaper", - "email" => "death@reapers.afterlife", - "fullname" => "Reaper Grim", - "bio" => "Your time has come", - "password" => "scythe", - "confirm" => "scythe", - "token" => invite.token + :username => "GrimReaper", + :email => "death@reapers.afterlife", + :fullname => "Reaper Grim", + :bio => "Your time has come", + :password => "scythe", + :confirm => "scythe", + :token => invite.token } {:error, msg} = TwitterAPI.register_user(data) @@ -377,13 +377,13 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do UserInviteToken.update_invite!(invite, uses: 100) data = %{ - "nickname" => "GrimReaper", - "email" => "death@reapers.afterlife", - "fullname" => "Reaper Grim", - "bio" => "Your time has come", - "password" => "scythe", - "confirm" => "scythe", - "token" => invite.token + :username => "GrimReaper", + :email => "death@reapers.afterlife", + :fullname => "Reaper Grim", + :bio => "Your time has come", + :password => "scythe", + :confirm => "scythe", + :token => invite.token } {:error, msg} = TwitterAPI.register_user(data) @@ -395,16 +395,15 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do test "it returns the error on registration problems" do data = %{ - "nickname" => "lain", - "email" => "lain@wired.jp", - "fullname" => "lain iwakura", - "bio" => "close the world.", - "password" => "bear" + :username => "lain", + :email => "lain@wired.jp", + :fullname => "lain iwakura", + :bio => "close the world." } - {:error, error_object} = TwitterAPI.register_user(data) + {:error, error} = TwitterAPI.register_user(data) - assert is_binary(error_object[:error]) + assert is_binary(error) refute User.get_cached_by_nickname("lain") end diff --git a/test/web/twitter_api/util_controller_test.exs b/test/web/twitter_api/util_controller_test.exs index 5ad682b0b..76e9369f7 100644 --- a/test/web/twitter_api/util_controller_test.exs +++ b/test/web/twitter_api/util_controller_test.exs @@ -95,6 +95,30 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do end end end + + test "it imports follows with different nickname variations", %{conn: conn} do + [user2, user3, user4, user5, user6] = insert_list(5, :user) + + identifiers = + [ + user2.ap_id, + user3.nickname, + " ", + "@" <> user4.nickname, + user5.nickname <> "@localhost", + "@" <> user6.nickname <> "@localhost" + ] + |> Enum.join("\n") + + response = + conn + |> post("/api/pleroma/follow_import", %{"list" => identifiers}) + |> json_response(:ok) + + assert response == "job started" + assert [{:ok, job_result}] = ObanHelpers.perform_all() + assert job_result == [user2, user3, user4, user5, user6] + end end describe "POST /api/pleroma/blocks_import" do @@ -136,6 +160,29 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do ) end end + + test "it imports blocks with different nickname variations", %{conn: conn} do + [user2, user3, user4, user5, user6] = insert_list(5, :user) + + identifiers = + [ + user2.ap_id, + user3.nickname, + "@" <> user4.nickname, + user5.nickname <> "@localhost", + "@" <> user6.nickname <> "@localhost" + ] + |> Enum.join(" ") + + response = + conn + |> post("/api/pleroma/blocks_import", %{"list" => identifiers}) + |> json_response(:ok) + + assert response == "job started" + assert [{:ok, job_result}] = ObanHelpers.perform_all() + assert job_result == [user2, user3, user4, user5, user6] + end end describe "PUT /api/pleroma/notification_settings" do @@ -520,7 +567,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do assert json_response(conn, 200) == %{"status" => "success"} fetched_user = User.get_cached_by_id(user.id) - assert Comeonin.Pbkdf2.checkpw("newpass", fetched_user.password_hash) == true + assert Pbkdf2.verify_pass("newpass", fetched_user.password_hash) == true end end diff --git a/test/web/web_finger/web_finger_test.exs b/test/web/web_finger/web_finger_test.exs index 4b4282727..f4884e0a2 100644 --- a/test/web/web_finger/web_finger_test.exs +++ b/test/web/web_finger/web_finger_test.exs @@ -67,7 +67,7 @@ defmodule Pleroma.Web.WebFingerTest do assert data["magic_key"] == nil assert data["salmon"] == nil - assert data["topic"] == "https://mstdn.jp/users/kPherox.atom" + assert data["topic"] == nil assert data["subject"] == "acct:kPherox@mstdn.jp" assert data["ap_id"] == "https://mstdn.jp/users/kPherox" assert data["subscribe_address"] == "https://mstdn.jp/authorize_interaction?acct={uri}" diff --git a/test/workers/cron/digest_emails_worker_test.exs b/test/workers/cron/digest_emails_worker_test.exs index 0a63bf4e0..f9bc50db5 100644 --- a/test/workers/cron/digest_emails_worker_test.exs +++ b/test/workers/cron/digest_emails_worker_test.exs @@ -29,7 +29,7 @@ defmodule Pleroma.Workers.Cron.DigestEmailsWorkerTest do user2 = insert(:user, last_digest_emailed_at: date) {:ok, _} = User.switch_email_notifications(user2, "digest", true) - CommonAPI.post(user, %{"status" => "hey @#{user2.nickname}!"}) + CommonAPI.post(user, %{status: "hey @#{user2.nickname}!"}) {:ok, user2: user2} end diff --git a/test/workers/cron/new_users_digest_worker_test.exs b/test/workers/cron/new_users_digest_worker_test.exs index e6d050ecc..54cf0ca46 100644 --- a/test/workers/cron/new_users_digest_worker_test.exs +++ b/test/workers/cron/new_users_digest_worker_test.exs @@ -15,7 +15,7 @@ defmodule Pleroma.Workers.Cron.NewUsersDigestWorkerTest do admin = insert(:user, %{is_admin: true}) user = insert(:user, %{inserted_at: yesterday}) user2 = insert(:user, %{inserted_at: yesterday}) - CommonAPI.post(user, %{"status" => "cofe"}) + CommonAPI.post(user, %{status: "cofe"}) NewUsersDigestWorker.perform(nil, nil) ObanHelpers.perform_all() @@ -36,7 +36,7 @@ defmodule Pleroma.Workers.Cron.NewUsersDigestWorkerTest do insert(:user, %{inserted_at: yesterday}) user = insert(:user, %{inserted_at: yesterday}) - CommonAPI.post(user, %{"status" => "cofe"}) + CommonAPI.post(user, %{status: "cofe"}) NewUsersDigestWorker.perform(nil, nil) ObanHelpers.perform_all() |