diff options
Diffstat (limited to 'test/web')
114 files changed, 5383 insertions, 2293 deletions
diff --git a/test/web/activity_pub/activity_pub_controller_test.exs b/test/web/activity_pub/activity_pub_controller_test.exs index 8c6ee68b2..b11e2f961 100644 --- a/test/web/activity_pub/activity_pub_controller_test.exs +++ b/test/web/activity_pub/activity_pub_controller_test.exs @@ -533,9 +533,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do end) :ok = Mix.Tasks.Pleroma.Relay.run(["list"]) - assert_receive {:mix_shell, :info, ["relay.mastodon.host"]} + assert_receive {:mix_shell, :info, ["https://relay.mastodon.host/actor"]} end + @tag capture_log: true test "without valid signature, " <> "it only accepts Create activities and requires enabled federation", %{conn: conn} do @@ -648,11 +649,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do test "it accepts announces with to as string instead of array", %{conn: conn} do user = insert(:user) + {:ok, post} = CommonAPI.post(user, %{status: "hey"}) + announcer = insert(:user, local: false) + data = %{ "@context" => "https://www.w3.org/ns/activitystreams", - "actor" => "http://mastodon.example.org/users/admin", - "id" => "http://mastodon.example.org/users/admin/statuses/19512778738411822/activity", - "object" => "https://mastodon.social/users/emelie/statuses/101849165031453009", + "actor" => announcer.ap_id, + "id" => "#{announcer.ap_id}/statuses/19512778738411822/activity", + "object" => post.data["object"], "to" => "https://www.w3.org/ns/activitystreams#Public", "cc" => [user.ap_id], "type" => "Announce" @@ -901,6 +905,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do end describe "POST /users/:nickname/outbox (C2S)" do + setup do: clear_config([:instance, :limit]) + setup do [ activity: %{ @@ -1078,6 +1084,59 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do assert object = Object.get_by_ap_id(note_object.data["id"]) assert object.data["like_count"] == 1 end + + test "it doesn't spreads faulty attributedTo or actor fields", %{ + conn: conn, + activity: activity + } do + reimu = insert(:user, nickname: "reimu") + cirno = insert(:user, nickname: "cirno") + + assert reimu.ap_id + assert cirno.ap_id + + activity = + activity + |> put_in(["object", "actor"], reimu.ap_id) + |> put_in(["object", "attributedTo"], reimu.ap_id) + |> put_in(["actor"], reimu.ap_id) + |> put_in(["attributedTo"], reimu.ap_id) + + _reimu_outbox = + conn + |> assign(:user, cirno) + |> put_req_header("content-type", "application/activity+json") + |> post("/users/#{reimu.nickname}/outbox", activity) + |> json_response(403) + + cirno_outbox = + conn + |> assign(:user, cirno) + |> put_req_header("content-type", "application/activity+json") + |> post("/users/#{cirno.nickname}/outbox", activity) + |> json_response(201) + + assert cirno_outbox["attributedTo"] == nil + assert cirno_outbox["actor"] == cirno.ap_id + + assert cirno_object = Object.normalize(cirno_outbox["object"]) + assert cirno_object.data["actor"] == cirno.ap_id + assert cirno_object.data["attributedTo"] == cirno.ap_id + end + + test "Character limitation", %{conn: conn, activity: activity} do + Pleroma.Config.put([:instance, :limit], 5) + user = insert(:user) + + result = + conn + |> assign(:user, user) + |> put_req_header("content-type", "application/activity+json") + |> post("/users/#{user.nickname}/outbox", activity) + |> json_response(400) + + assert result == "Note is over the character limit" + end end describe "/relay/followers" do diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs index 7693f6400..d8caa0b00 100644 --- a/test/web/activity_pub/activity_pub_test.exs +++ b/test/web/activity_pub/activity_pub_test.exs @@ -184,36 +184,43 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do assert User.invisible?(user) end - test "it fetches the appropriate tag-restricted posts" do - user = insert(:user) + test "it returns a user that accepts chat messages" do + user_id = "http://mastodon.example.org/users/admin" + {:ok, user} = ActivityPub.make_user_from_ap_id(user_id) - {: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"}) + assert user.accepts_chat_messages + end + end - fetch_one = ActivityPub.fetch_activities([], %{type: "Create", tag: "test"}) + test "it fetches the appropriate tag-restricted posts" do + user = insert(:user) - fetch_two = ActivityPub.fetch_activities([], %{type: "Create", tag: ["test", "essais"]}) + {: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_three = - ActivityPub.fetch_activities([], %{ - type: "Create", - tag: ["test", "essais"], - tag_reject: ["reject"] - }) + fetch_one = ActivityPub.fetch_activities([], %{type: "Create", tag: "test"}) - fetch_four = - ActivityPub.fetch_activities([], %{ - type: "Create", - tag: ["test"], - tag_all: ["test", "reject"] - }) + fetch_two = ActivityPub.fetch_activities([], %{type: "Create", tag: ["test", "essais"]}) - assert fetch_one == [status_one, status_three] - assert fetch_two == [status_one, status_two, status_three] - assert fetch_three == [status_one, status_two] - assert fetch_four == [status_three] - end + fetch_three = + ActivityPub.fetch_activities([], %{ + type: "Create", + tag: ["test", "essais"], + tag_reject: ["reject"] + }) + + fetch_four = + ActivityPub.fetch_activities([], %{ + type: "Create", + tag: ["test"], + tag_all: ["test", "reject"] + }) + + assert fetch_one == [status_one, status_three] + assert fetch_two == [status_one, status_two, status_three] + assert fetch_three == [status_one, status_two] + assert fetch_four == [status_three] end describe "insertion" do @@ -232,7 +239,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do } } - assert {:error, {:remote_limit_error, _}} = ActivityPub.insert(data) + assert {:error, :remote_limit} = ActivityPub.insert(data) end test "doesn't drop activities with content being null" do @@ -379,9 +386,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do end describe "create activities" do - test "it reverts create" do - user = insert(:user) + setup do + [user: insert(:user)] + end + test "it reverts create", %{user: user} do with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do assert {:error, :reverted} = ActivityPub.create(%{ @@ -400,9 +409,47 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do assert Repo.aggregate(Object, :count, :id) == 0 end - test "removes doubled 'to' recipients" do - user = insert(:user) + test "creates activity if expiration is not configured and expires_at is not passed", %{ + user: user + } do + clear_config([Pleroma.Workers.PurgeExpiredActivity, :enabled], false) + + assert {:ok, _} = + ActivityPub.create(%{ + to: ["user1", "user2"], + actor: user, + context: "", + object: %{ + "to" => ["user1", "user2"], + "type" => "Note", + "content" => "testing" + } + }) + end + + test "rejects activity if expires_at present but expiration is not configured", %{user: user} do + clear_config([Pleroma.Workers.PurgeExpiredActivity, :enabled], false) + + assert {:error, :expired_activities_disabled} = + ActivityPub.create(%{ + to: ["user1", "user2"], + actor: user, + context: "", + object: %{ + "to" => ["user1", "user2"], + "type" => "Note", + "content" => "testing" + }, + additional: %{ + "expires_at" => DateTime.utc_now() + } + }) + assert Repo.aggregate(Activity, :count, :id) == 0 + assert Repo.aggregate(Object, :count, :id) == 0 + end + + test "removes doubled 'to' recipients", %{user: user} do {:ok, activity} = ActivityPub.create(%{ to: ["user1", "user1", "user2"], @@ -420,9 +467,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do assert activity.recipients == ["user1", "user2", user.ap_id] end - test "increases user note count only for public activities" do - user = insert(:user) - + test "increases user note count only for public activities", %{user: user} do {:ok, _} = CommonAPI.post(User.get_cached_by_id(user.id), %{ status: "1", @@ -451,8 +496,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do assert user.note_count == 2 end - test "increases replies count" do - user = insert(:user) + test "increases replies count", %{user: user} do user2 = insert(:user) {:ok, activity} = CommonAPI.post(user, %{status: "1", visibility: "public"}) @@ -507,6 +551,33 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do activities = ActivityPub.fetch_activities_for_context("2hu", %{blocking_user: user}) assert activities == [activity_two, activity] end + + test "doesn't return activities with filtered words" do + user = insert(:user) + user_two = insert(:user) + insert(:filter, user: user, phrase: "test", hide: true) + + {:ok, %{id: id1, data: %{"context" => context}}} = CommonAPI.post(user, %{status: "1"}) + + {:ok, %{id: id2}} = CommonAPI.post(user_two, %{status: "2", in_reply_to_status_id: id1}) + + {:ok, %{id: id3} = user_activity} = + CommonAPI.post(user, %{status: "3 test?", in_reply_to_status_id: id2}) + + {:ok, %{id: id4} = filtered_activity} = + CommonAPI.post(user_two, %{status: "4 test!", in_reply_to_status_id: id3}) + + {:ok, _} = CommonAPI.post(user, %{status: "5", in_reply_to_status_id: id4}) + + activities = + context + |> ActivityPub.fetch_activities_for_context(%{user: user}) + |> Enum.map(& &1.id) + + assert length(activities) == 4 + assert user_activity.id in activities + refute filtered_activity.id in activities + end end test "doesn't return blocked activities" do @@ -642,7 +713,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do refute activity in activities followed_user = insert(:user) - ActivityPub.follow(user, followed_user) + CommonAPI.follow(user, followed_user) {:ok, repeat_activity} = CommonAPI.repeat(activity.id, followed_user) activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true}) @@ -785,6 +856,75 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do assert activity == expected_activity end + describe "irreversible filters" do + setup do + user = insert(:user) + user_two = insert(:user) + + insert(:filter, user: user_two, phrase: "cofe", hide: true) + insert(:filter, user: user_two, phrase: "ok boomer", hide: true) + insert(:filter, user: user_two, phrase: "test", hide: false) + + params = %{ + type: ["Create", "Announce"], + user: user_two + } + + {:ok, %{user: user, user_two: user_two, params: params}} + end + + test "it returns statuses if they don't contain exact filter words", %{ + user: user, + params: params + } do + {:ok, _} = CommonAPI.post(user, %{status: "hey"}) + {:ok, _} = CommonAPI.post(user, %{status: "got cofefe?"}) + {:ok, _} = CommonAPI.post(user, %{status: "I am not a boomer"}) + {:ok, _} = CommonAPI.post(user, %{status: "ok boomers"}) + {:ok, _} = CommonAPI.post(user, %{status: "ccofee is not a word"}) + {:ok, _} = CommonAPI.post(user, %{status: "this is a test"}) + + activities = ActivityPub.fetch_activities([], params) + + assert Enum.count(activities) == 6 + end + + test "it does not filter user's own statuses", %{user_two: user_two, params: params} do + {:ok, _} = CommonAPI.post(user_two, %{status: "Give me some cofe!"}) + {:ok, _} = CommonAPI.post(user_two, %{status: "ok boomer"}) + + activities = ActivityPub.fetch_activities([], params) + + assert Enum.count(activities) == 2 + end + + test "it excludes statuses with filter words", %{user: user, params: params} do + {:ok, _} = CommonAPI.post(user, %{status: "Give me some cofe!"}) + {:ok, _} = CommonAPI.post(user, %{status: "ok boomer"}) + {:ok, _} = CommonAPI.post(user, %{status: "is it a cOfE?"}) + {:ok, _} = CommonAPI.post(user, %{status: "cofe is all I need"}) + {:ok, _} = CommonAPI.post(user, %{status: "— ok BOOMER\n"}) + + activities = ActivityPub.fetch_activities([], params) + + assert Enum.empty?(activities) + end + + test "it returns all statuses if user does not have any filters" do + another_user = insert(:user) + {:ok, _} = CommonAPI.post(another_user, %{status: "got cofe?"}) + {:ok, _} = CommonAPI.post(another_user, %{status: "test!"}) + + activities = + ActivityPub.fetch_activities([], %{ + type: ["Create", "Announce"], + user: another_user + }) + + assert Enum.count(activities) == 2 + end + end + describe "public fetch activities" do test "doesn't retrieve unlisted activities" do user = insert(:user) @@ -887,13 +1027,39 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do end describe "uploading files" do - test "copies the file to the configured folder" do - file = %Plug.Upload{ + setup do + test_file = %Plug.Upload{ content_type: "image/jpg", path: Path.absname("test/fixtures/image.jpg"), filename: "an_image.jpg" } + %{test_file: test_file} + end + + test "sets a description if given", %{test_file: file} do + {:ok, %Object{} = object} = ActivityPub.upload(file, description: "a cool file") + assert object.data["name"] == "a cool file" + end + + test "it sets the default description depending on the configuration", %{test_file: file} do + clear_config([Pleroma.Upload, :default_description]) + + Pleroma.Config.put([Pleroma.Upload, :default_description], nil) + {:ok, %Object{} = object} = ActivityPub.upload(file) + assert object.data["name"] == "" + + Pleroma.Config.put([Pleroma.Upload, :default_description], :filename) + {:ok, %Object{} = object} = ActivityPub.upload(file) + assert object.data["name"] == "an_image.jpg" + + Pleroma.Config.put([Pleroma.Upload, :default_description], "unnamed attachment") + {:ok, %Object{} = object} = ActivityPub.upload(file) + assert object.data["name"] == "unnamed attachment" + end + + test "copies the file to the configured folder", %{test_file: file} do + clear_config([Pleroma.Upload, :default_description], :filename) {:ok, %Object{} = object} = ActivityPub.upload(file) assert object.data["name"] == "an_image.jpg" end @@ -917,24 +1083,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do end end - describe "following / unfollowing" do - test "it reverts follow activity" do - follower = insert(:user) - followed = insert(:user) - - with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do - assert {:error, :reverted} = ActivityPub.follow(follower, followed) - end - - assert Repo.aggregate(Activity, :count, :id) == 0 - assert Repo.aggregate(Object, :count, :id) == 0 - end - + describe "unfollowing" do test "it reverts unfollow activity" do follower = insert(:user) followed = insert(:user) - {:ok, follow_activity} = ActivityPub.follow(follower, followed) + {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed) with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do assert {:error, :reverted} = ActivityPub.unfollow(follower, followed) @@ -947,21 +1101,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do assert activity.data["object"] == followed.ap_id end - test "creates a follow activity" do - follower = insert(:user) - followed = insert(:user) - - {:ok, activity} = ActivityPub.follow(follower, followed) - assert activity.data["type"] == "Follow" - assert activity.data["actor"] == follower.ap_id - assert activity.data["object"] == followed.ap_id - end - test "creates an undo activity for the last follow" do follower = insert(:user) followed = insert(:user) - {:ok, follow_activity} = ActivityPub.follow(follower, followed) + {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed) {:ok, activity} = ActivityPub.unfollow(follower, followed) assert activity.data["type"] == "Undo" @@ -978,7 +1122,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do follower = insert(:user) followed = insert(:user, %{locked: true}) - {:ok, follow_activity} = ActivityPub.follow(follower, followed) + {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed) {:ok, activity} = ActivityPub.unfollow(follower, followed) assert activity.data["type"] == "Undo" @@ -992,54 +1136,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do end end - describe "blocking" do - test "reverts block activity on error" do - [blocker, blocked] = insert_list(2, :user) - - with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do - assert {:error, :reverted} = ActivityPub.block(blocker, blocked) - end - - assert Repo.aggregate(Activity, :count, :id) == 0 - assert Repo.aggregate(Object, :count, :id) == 0 - end - - test "creates a block activity" do - clear_config([:instance, :federating], true) - blocker = insert(:user) - blocked = insert(:user) - - with_mock Pleroma.Web.Federator, - publish: fn _ -> nil end do - {: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 - - assert called(Pleroma.Web.Federator.publish(activity)) - end - end - - 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) - - with_mock Pleroma.Web.Federator, - publish: fn _ -> nil end do - {: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 - - refute called(Pleroma.Web.Federator.publish(:_)) - end - end - end - describe "timeline post-processing" do test "it filters broken threads" do user1 = insert(:user) @@ -1092,52 +1188,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do end end - describe "update" do - setup do: clear_config([:instance, :max_pinned_statuses]) - - test "it creates an update activity with the new user data" do - user = insert(:user) - {:ok, user} = User.ensure_keys_present(user) - user_data = Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: user}) - - {:ok, update} = - ActivityPub.update(%{ - actor: user_data["id"], - to: [user.follower_address], - cc: [], - object: user_data - }) - - assert update.data["actor"] == user.ap_id - assert update.data["to"] == [user.follower_address] - assert embedded_object = update.data["object"] - assert embedded_object["id"] == user_data["id"] - assert embedded_object["type"] == user_data["type"] - end - end - - test "returned pinned statuses" 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!!!"}) - - CommonAPI.pin(activity_one.id, user) - user = refresh_record(user) - - CommonAPI.pin(activity_two.id, user) - user = refresh_record(user) - - CommonAPI.pin(activity_three.id, user) - user = refresh_record(user) - - activities = ActivityPub.fetch_user_activities(user, nil, %{pinned: true}) - - assert 3 = length(activities) - end - describe "flag/1" do setup do reporter = insert(:user) @@ -1192,7 +1242,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do "id" => activity_ap_id, "content" => content, "published" => activity_with_object.object.data["published"], - "actor" => AccountView.render("show.json", %{user: target_account}) + "actor" => + AccountView.render("show.json", %{user: target_account, skip_visibility_check: true}) } assert %Activity{ @@ -1457,7 +1508,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do assert_enqueued(worker: Pleroma.Workers.BackgroundWorker, args: params) - Pleroma.Workers.BackgroundWorker.perform(params, nil) + Pleroma.Workers.BackgroundWorker.perform(%Oban.Job{args: params}) refute User.following?(follower, old_user) assert User.following?(follower, new_user) @@ -2055,18 +2106,67 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do end describe "global activity expiration" do - setup do: clear_config([:instance, :rewrite_policy]) - test "creates an activity expiration for local Create activities" do - Pleroma.Config.put( - [:instance, :rewrite_policy], - Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy + clear_config([:mrf, :policies], Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy) + + {:ok, activity} = ActivityBuilder.insert(%{"type" => "Create", "context" => "3hu"}) + {:ok, follow} = ActivityBuilder.insert(%{"type" => "Follow", "context" => "3hu"}) + + assert_enqueued( + worker: Pleroma.Workers.PurgeExpiredActivity, + args: %{activity_id: activity.id}, + scheduled_at: + activity.inserted_at + |> DateTime.from_naive!("Etc/UTC") + |> Timex.shift(days: 365) + ) + + refute_enqueued( + worker: Pleroma.Workers.PurgeExpiredActivity, + args: %{activity_id: follow.id} ) + end + end + + describe "handling of clashing nicknames" do + test "renames an existing user with a clashing nickname and a different ap id" do + orig_user = + insert( + :user, + local: false, + nickname: "admin@mastodon.example.org", + ap_id: "http://mastodon.example.org/users/harinezumigari" + ) + + %{ + nickname: orig_user.nickname, + ap_id: orig_user.ap_id <> "part_2" + } + |> ActivityPub.maybe_handle_clashing_nickname() + + user = User.get_by_id(orig_user.id) + + assert user.nickname == "#{orig_user.id}.admin@mastodon.example.org" + end + + test "does nothing with a clashing nickname and the same ap id" do + orig_user = + insert( + :user, + local: false, + nickname: "admin@mastodon.example.org", + ap_id: "http://mastodon.example.org/users/harinezumigari" + ) + + %{ + nickname: orig_user.nickname, + ap_id: orig_user.ap_id + } + |> ActivityPub.maybe_handle_clashing_nickname() - {:ok, %{id: id_create}} = ActivityBuilder.insert(%{"type" => "Create", "context" => "3hu"}) - {:ok, _follow} = ActivityBuilder.insert(%{"type" => "Follow", "context" => "3hu"}) + user = User.get_by_id(orig_user.id) - assert [%{activity_id: ^id_create}] = Pleroma.ActivityExpiration |> Repo.all() + assert user.nickname == orig_user.nickname end end end diff --git a/test/web/activity_pub/mrf/activity_expiration_policy_test.exs b/test/web/activity_pub/mrf/activity_expiration_policy_test.exs index 8babf49e7..e7370d4ef 100644 --- a/test/web/activity_pub/mrf/activity_expiration_policy_test.exs +++ b/test/web/activity_pub/mrf/activity_expiration_policy_test.exs @@ -7,24 +7,27 @@ defmodule Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicyTest do alias Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy @id Pleroma.Web.Endpoint.url() <> "/activities/cofe" + @local_actor Pleroma.Web.Endpoint.url() <> "/users/cofe" test "adds `expires_at` property" do assert {:ok, %{"type" => "Create", "expires_at" => expires_at}} = ActivityExpirationPolicy.filter(%{ "id" => @id, + "actor" => @local_actor, "type" => "Create", "object" => %{"type" => "Note"} }) - assert Timex.diff(expires_at, NaiveDateTime.utc_now(), :days) == 364 + assert Timex.diff(expires_at, DateTime.utc_now(), :days) == 364 end test "keeps existing `expires_at` if it less than the config setting" do - expires_at = NaiveDateTime.utc_now() |> Timex.shift(days: 1) + expires_at = DateTime.utc_now() |> Timex.shift(days: 1) assert {:ok, %{"type" => "Create", "expires_at" => ^expires_at}} = ActivityExpirationPolicy.filter(%{ "id" => @id, + "actor" => @local_actor, "type" => "Create", "expires_at" => expires_at, "object" => %{"type" => "Note"} @@ -32,23 +35,25 @@ defmodule Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicyTest do end test "overwrites existing `expires_at` if it greater than the config setting" do - too_distant_future = NaiveDateTime.utc_now() |> Timex.shift(years: 2) + too_distant_future = DateTime.utc_now() |> Timex.shift(years: 2) assert {:ok, %{"type" => "Create", "expires_at" => expires_at}} = ActivityExpirationPolicy.filter(%{ "id" => @id, + "actor" => @local_actor, "type" => "Create", "expires_at" => too_distant_future, "object" => %{"type" => "Note"} }) - assert Timex.diff(expires_at, NaiveDateTime.utc_now(), :days) == 364 + assert Timex.diff(expires_at, DateTime.utc_now(), :days) == 364 end test "ignores remote activities" do assert {:ok, activity} = ActivityExpirationPolicy.filter(%{ "id" => "https://example.com/123", + "actor" => "https://example.com/users/cofe", "type" => "Create", "object" => %{"type" => "Note"} }) @@ -60,6 +65,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicyTest do assert {:ok, activity} = ActivityExpirationPolicy.filter(%{ "id" => "https://example.com/123", + "actor" => "https://example.com/users/cofe", "type" => "Follow" }) @@ -68,6 +74,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicyTest do assert {:ok, activity} = ActivityExpirationPolicy.filter(%{ "id" => "https://example.com/123", + "actor" => "https://example.com/users/cofe", "type" => "Create", "object" => %{"type" => "Cofe"} }) 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 fca0de7c6..3c795f5ac 100644 --- a/test/web/activity_pub/mrf/anti_followbot_policy_test.exs +++ b/test/web/activity_pub/mrf/anti_followbot_policy_test.exs @@ -21,7 +21,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicyTest do "id" => "https://example.com/activities/1234" } - {:reject, nil} = AntiFollowbotPolicy.filter(message) + assert {:reject, "[AntiFollowbotPolicy]" <> _} = AntiFollowbotPolicy.filter(message) end test "matches followbots by display name" do @@ -36,7 +36,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicyTest do "id" => "https://example.com/activities/1234" } - {:reject, nil} = AntiFollowbotPolicy.filter(message) + assert {:reject, "[AntiFollowbotPolicy]" <> _} = AntiFollowbotPolicy.filter(message) end end 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 1a13699be..6867c9853 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 @@ -33,7 +33,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicyTest do describe "with new user" do test "it allows posts without links" do - user = insert(:user) + user = insert(:user, local: false) assert user.note_count == 0 @@ -45,7 +45,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicyTest do end test "it disallows posts with links" do - user = insert(:user) + user = insert(:user, local: false) assert user.note_count == 0 @@ -55,6 +55,18 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicyTest do {:reject, _} = AntiLinkSpamPolicy.filter(message) end + + test "it allows posts with links for local users" do + user = insert(:user) + + assert user.note_count == 0 + + message = + @linkful_message + |> Map.put("actor", user.ap_id) + + {:ok, _message} = AntiLinkSpamPolicy.filter(message) + end end describe "with old user" do 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 38ddec5bb..9a283f27d 100644 --- a/test/web/activity_pub/mrf/ensure_re_prepended_test.exs +++ b/test/web/activity_pub/mrf/ensure_re_prepended_test.exs @@ -78,5 +78,15 @@ defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrependedTest do assert {:ok, res} = EnsureRePrepended.filter(message) assert res == message end + + test "it skips if the object is only a reference" do + message = %{ + "type" => "Create", + "object" => "somereference" + } + + assert {:ok, res} = EnsureRePrepended.filter(message) + assert res == message + end end end diff --git a/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs b/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs new file mode 100644 index 000000000..86dd9ddae --- /dev/null +++ b/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs @@ -0,0 +1,60 @@ +# 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.MRF.ForceBotUnlistedPolicyTest do + use Pleroma.DataCase + import Pleroma.Factory + + alias Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy + @public "https://www.w3.org/ns/activitystreams#Public" + + defp generate_messages(actor) do + {%{ + "actor" => actor.ap_id, + "type" => "Create", + "object" => %{}, + "to" => [@public, "f"], + "cc" => [actor.follower_address, "d"] + }, + %{ + "actor" => actor.ap_id, + "type" => "Create", + "object" => %{"to" => ["f", actor.follower_address], "cc" => ["d", @public]}, + "to" => ["f", actor.follower_address], + "cc" => ["d", @public] + }} + end + + test "removes from the federated timeline by nickname heuristics 1" do + actor = insert(:user, %{nickname: "annoying_ebooks@example.com"}) + + {message, except_message} = generate_messages(actor) + + assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_message} + end + + test "removes from the federated timeline by nickname heuristics 2" do + actor = insert(:user, %{nickname: "cirnonewsnetworkbot@meow.cat"}) + + {message, except_message} = generate_messages(actor) + + assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_message} + end + + test "removes from the federated timeline by actor type Application" do + actor = insert(:user, %{actor_type: "Application"}) + + {message, except_message} = generate_messages(actor) + + assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_message} + end + + test "removes from the federated timeline by actor type Service" do + actor = insert(:user, %{actor_type: "Service"}) + + {message, except_message} = generate_messages(actor) + + assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_message} + end +end diff --git a/test/web/activity_pub/mrf/hellthread_policy_test.exs b/test/web/activity_pub/mrf/hellthread_policy_test.exs index 95ef0b168..26f5bcdaa 100644 --- a/test/web/activity_pub/mrf/hellthread_policy_test.exs +++ b/test/web/activity_pub/mrf/hellthread_policy_test.exs @@ -8,6 +8,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicyTest do import Pleroma.Web.ActivityPub.MRF.HellthreadPolicy + alias Pleroma.Web.CommonAPI + setup do user = insert(:user) @@ -20,7 +22,10 @@ defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicyTest do "https://instance.tld/users/user1", "https://instance.tld/users/user2", "https://instance.tld/users/user3" - ] + ], + "object" => %{ + "type" => "Note" + } } [user: user, message: message] @@ -28,13 +33,25 @@ defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicyTest do setup do: clear_config(:mrf_hellthread) + test "doesn't die on chat messages" do + Pleroma.Config.put([:mrf_hellthread], %{delist_threshold: 2, reject_threshold: 0}) + + user = insert(:user) + other_user = insert(:user) + + {:ok, activity} = CommonAPI.post_chat_message(user, other_user, "moin") + + assert {:ok, _} = filter(activity.data) + end + describe "reject" do test "rejects the message if the recipient count is above reject_threshold", %{ message: message } do Pleroma.Config.put([:mrf_hellthread], %{delist_threshold: 0, reject_threshold: 2}) - {:reject, nil} = filter(message) + assert {:reject, "[HellthreadPolicy] 3 recipients is over the limit of 2"} == + filter(message) end test "does not reject the message if the recipient count is below reject_threshold", %{ diff --git a/test/web/activity_pub/mrf/keyword_policy_test.exs b/test/web/activity_pub/mrf/keyword_policy_test.exs index fd1f7aec8..b3d0f3d90 100644 --- a/test/web/activity_pub/mrf/keyword_policy_test.exs +++ b/test/web/activity_pub/mrf/keyword_policy_test.exs @@ -25,7 +25,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicyTest do } } - assert {:reject, nil} == KeywordPolicy.filter(message) + assert {:reject, "[KeywordPolicy] Matches with rejected keyword"} = + KeywordPolicy.filter(message) end test "rejects if string matches in summary" do @@ -39,7 +40,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicyTest do } } - assert {:reject, nil} == KeywordPolicy.filter(message) + assert {:reject, "[KeywordPolicy] Matches with rejected keyword"} = + KeywordPolicy.filter(message) end test "rejects if regex matches in content" do @@ -55,7 +57,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicyTest do } } - {:reject, nil} == KeywordPolicy.filter(message) + {:reject, "[KeywordPolicy] Matches with rejected keyword"} == + KeywordPolicy.filter(message) end) end @@ -72,7 +75,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicyTest do } } - {:reject, nil} == KeywordPolicy.filter(message) + {:reject, "[KeywordPolicy] Matches with rejected keyword"} == + KeywordPolicy.filter(message) end) end end diff --git a/test/web/activity_pub/mrf/mention_policy_test.exs b/test/web/activity_pub/mrf/mention_policy_test.exs index aa003bef5..220309cc9 100644 --- a/test/web/activity_pub/mrf/mention_policy_test.exs +++ b/test/web/activity_pub/mrf/mention_policy_test.exs @@ -76,7 +76,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.MentionPolicyTest do "to" => ["https://example.com/blocked"] } - assert MentionPolicy.filter(message) == {:reject, nil} + assert MentionPolicy.filter(message) == + {:reject, "[MentionPolicy] Rejected for mention of https://example.com/blocked"} end test "cc" do @@ -88,7 +89,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.MentionPolicyTest do "cc" => ["https://example.com/blocked"] } - assert MentionPolicy.filter(message) == {:reject, nil} + assert MentionPolicy.filter(message) == + {:reject, "[MentionPolicy] Rejected for mention of https://example.com/blocked"} end end end diff --git a/test/web/activity_pub/mrf/mrf_test.exs b/test/web/activity_pub/mrf/mrf_test.exs index c941066f2..a63b25423 100644 --- a/test/web/activity_pub/mrf/mrf_test.exs +++ b/test/web/activity_pub/mrf/mrf_test.exs @@ -60,8 +60,6 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do end describe "describe/0" do - setup do: clear_config([:instance, :rewrite_policy]) - test "it works as expected with noop policy" do expected = %{ mrf_policies: ["NoOpPolicy"], @@ -72,7 +70,7 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do end test "it works as expected with mock policy" do - Pleroma.Config.put([:instance, :rewrite_policy], [MRFModuleMock]) + clear_config([:mrf, :policies], [MRFModuleMock]) expected = %{ mrf_policies: ["MRFModuleMock"], 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 b0fb753bd..cf6acc9a2 100644 --- a/test/web/activity_pub/mrf/object_age_policy_test.exs +++ b/test/web/activity_pub/mrf/object_age_policy_test.exs @@ -38,6 +38,17 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicyTest do end describe "with reject action" do + test "works with objects with empty to or cc fields" do + Config.put([:mrf_object_age, :actions], [:reject]) + + data = + get_old_message() + |> Map.put("cc", nil) + |> Map.put("to", nil) + + assert match?({:reject, _}, ObjectAgePolicy.filter(data)) + end + test "it rejects an old post" do Config.put([:mrf_object_age, :actions], [:reject]) @@ -56,6 +67,21 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicyTest do end describe "with delist action" do + test "works with objects with empty to or cc fields" do + Config.put([:mrf_object_age, :actions], [:delist]) + + data = + get_old_message() + |> Map.put("cc", nil) + |> Map.put("to", nil) + + {:ok, _u} = User.get_or_fetch_by_ap_id(data["actor"]) + + {:ok, data} = ObjectAgePolicy.filter(data) + + assert Visibility.get_visibility(%{data: data}) == "unlisted" + end + test "it delists an old post" do Config.put([:mrf_object_age, :actions], [:delist]) @@ -80,6 +106,22 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicyTest do end describe "with strip_followers action" do + test "works with objects with empty to or cc fields" do + Config.put([:mrf_object_age, :actions], [:strip_followers]) + + data = + get_old_message() + |> Map.put("cc", nil) + |> Map.put("to", nil) + + {:ok, user} = User.get_or_fetch_by_ap_id(data["actor"]) + + {:ok, data} = ObjectAgePolicy.filter(data) + + refute user.follower_address in data["to"] + refute user.follower_address in data["cc"] + end + test "it strips followers collections from an old post" do Config.put([:mrf_object_age, :actions], [:strip_followers]) 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 f36299b86..58b46b9a2 100644 --- a/test/web/activity_pub/mrf/reject_non_public_test.exs +++ b/test/web/activity_pub/mrf/reject_non_public_test.exs @@ -64,7 +64,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublicTest do } Pleroma.Config.put([:mrf_rejectnonpublic, :allow_followersonly], false) - assert {:reject, nil} = RejectNonPublic.filter(message) + assert {:reject, _} = RejectNonPublic.filter(message) end end @@ -94,7 +94,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublicTest do } Pleroma.Config.put([:mrf_rejectnonpublic, :allow_direct], false) - assert {:reject, nil} = RejectNonPublic.filter(message) + assert {:reject, _} = RejectNonPublic.filter(message) end end end diff --git a/test/web/activity_pub/mrf/simple_policy_test.exs b/test/web/activity_pub/mrf/simple_policy_test.exs index b7b9bc6a2..d7dde62c4 100644 --- a/test/web/activity_pub/mrf/simple_policy_test.exs +++ b/test/web/activity_pub/mrf/simple_policy_test.exs @@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do import Pleroma.Factory alias Pleroma.Config alias Pleroma.Web.ActivityPub.MRF.SimplePolicy + alias Pleroma.Web.CommonAPI setup do: clear_config(:mrf_simple, @@ -15,6 +16,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do federated_timeline_removal: [], report_removal: [], reject: [], + followers_only: [], accept: [], avatar_removal: [], banner_removal: [], @@ -124,7 +126,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do report_message = build_report_message() local_message = build_local_message() - assert SimplePolicy.filter(report_message) == {:reject, nil} + assert {:reject, _} = SimplePolicy.filter(report_message) assert SimplePolicy.filter(local_message) == {:ok, local_message} end @@ -133,7 +135,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do report_message = build_report_message() local_message = build_local_message() - assert SimplePolicy.filter(report_message) == {:reject, nil} + assert {:reject, _} = SimplePolicy.filter(report_message) assert SimplePolicy.filter(local_message) == {:ok, local_message} end end @@ -241,7 +243,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do remote_message = build_remote_message() - assert SimplePolicy.filter(remote_message) == {:reject, nil} + assert {:reject, _} = SimplePolicy.filter(remote_message) end test "activity matches with wildcard domain" do @@ -249,7 +251,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do remote_message = build_remote_message() - assert SimplePolicy.filter(remote_message) == {:reject, nil} + assert {:reject, _} = SimplePolicy.filter(remote_message) end test "actor has a matching host" do @@ -257,7 +259,65 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do remote_user = build_remote_user() - assert SimplePolicy.filter(remote_user) == {:reject, nil} + assert {:reject, _} = SimplePolicy.filter(remote_user) + end + end + + describe "when :followers_only" do + test "is empty" do + Config.put([:mrf_simple, :followers_only], []) + {_, ftl_message} = build_ftl_actor_and_message() + local_message = build_local_message() + + assert SimplePolicy.filter(ftl_message) == {:ok, ftl_message} + assert SimplePolicy.filter(local_message) == {:ok, local_message} + end + + test "has a matching host" do + actor = insert(:user) + following_user = insert(:user) + non_following_user = insert(:user) + + {:ok, _, _, _} = CommonAPI.follow(following_user, actor) + + activity = %{ + "actor" => actor.ap_id, + "to" => [ + "https://www.w3.org/ns/activitystreams#Public", + following_user.ap_id, + non_following_user.ap_id + ], + "cc" => [actor.follower_address, "http://foo.bar/qux"] + } + + dm_activity = %{ + "actor" => actor.ap_id, + "to" => [ + following_user.ap_id, + non_following_user.ap_id + ], + "cc" => [] + } + + actor_domain = + activity + |> Map.fetch!("actor") + |> URI.parse() + |> Map.fetch!(:host) + + Config.put([:mrf_simple, :followers_only], [actor_domain]) + + assert {:ok, new_activity} = SimplePolicy.filter(activity) + assert actor.follower_address in new_activity["cc"] + assert following_user.ap_id in new_activity["to"] + refute "https://www.w3.org/ns/activitystreams#Public" in new_activity["to"] + refute "https://www.w3.org/ns/activitystreams#Public" in new_activity["cc"] + refute non_following_user.ap_id in new_activity["to"] + refute non_following_user.ap_id in new_activity["cc"] + + assert {:ok, new_dm_activity} = SimplePolicy.filter(dm_activity) + assert new_dm_activity["to"] == [following_user.ap_id] + assert new_dm_activity["cc"] == [] end end @@ -279,7 +339,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do remote_message = build_remote_message() assert SimplePolicy.filter(local_message) == {:ok, local_message} - assert SimplePolicy.filter(remote_message) == {:reject, nil} + assert {:reject, _} = SimplePolicy.filter(remote_message) end test "activity has a matching host" do @@ -429,7 +489,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do test "it rejects the deletion" do deletion_message = build_remote_deletion_message() - assert SimplePolicy.filter(deletion_message) == {:reject, nil} + assert {:reject, _} = SimplePolicy.filter(deletion_message) end end @@ -439,7 +499,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do test "it rejects the deletion" do deletion_message = build_remote_deletion_message() - assert SimplePolicy.filter(deletion_message) == {:reject, nil} + assert {:reject, _} = SimplePolicy.filter(deletion_message) end end diff --git a/test/web/activity_pub/mrf/tag_policy_test.exs b/test/web/activity_pub/mrf/tag_policy_test.exs index e7793641a..6ff71d640 100644 --- a/test/web/activity_pub/mrf/tag_policy_test.exs +++ b/test/web/activity_pub/mrf/tag_policy_test.exs @@ -12,8 +12,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicyTest do describe "mrf_tag:disable-any-subscription" do test "rejects message" do actor = insert(:user, tags: ["mrf_tag:disable-any-subscription"]) - message = %{"object" => actor.ap_id, "type" => "Follow"} - assert {:reject, nil} = TagPolicy.filter(message) + message = %{"object" => actor.ap_id, "type" => "Follow", "actor" => actor.ap_id} + assert {:reject, _} = TagPolicy.filter(message) end end @@ -22,7 +22,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicyTest do actor = insert(:user, tags: ["mrf_tag:disable-remote-subscription"]) follower = insert(:user, tags: ["mrf_tag:disable-remote-subscription"], local: false) message = %{"object" => actor.ap_id, "type" => "Follow", "actor" => follower.ap_id} - assert {:reject, nil} = TagPolicy.filter(message) + assert {:reject, _} = TagPolicy.filter(message) end test "allows non-local follow requests" do diff --git a/test/web/activity_pub/mrf/user_allowlist_policy_test.exs b/test/web/activity_pub/mrf/user_allowlist_policy_test.exs index ba1b69658..8e1ad5bc8 100644 --- a/test/web/activity_pub/mrf/user_allowlist_policy_test.exs +++ b/test/web/activity_pub/mrf/user_allowlist_policy_test.exs @@ -26,6 +26,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.UserAllowListPolicyTest do actor = insert(:user) Pleroma.Config.put([:mrf_user_allowlist], %{"localhost" => ["test-ap-id"]}) message = %{"actor" => actor.ap_id} - assert UserAllowListPolicy.filter(message) == {:reject, nil} + assert {:reject, _} = UserAllowListPolicy.filter(message) end end diff --git a/test/web/activity_pub/mrf/vocabulary_policy_test.exs b/test/web/activity_pub/mrf/vocabulary_policy_test.exs index 69f22bb77..2bceb67ee 100644 --- a/test/web/activity_pub/mrf/vocabulary_policy_test.exs +++ b/test/web/activity_pub/mrf/vocabulary_policy_test.exs @@ -46,7 +46,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicyTest do } } - {:reject, nil} = VocabularyPolicy.filter(message) + {:reject, _} = VocabularyPolicy.filter(message) end test "it does not accept disallowed parent types" do @@ -60,7 +60,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicyTest do } } - {:reject, nil} = VocabularyPolicy.filter(message) + {:reject, _} = VocabularyPolicy.filter(message) end end @@ -75,7 +75,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicyTest do "object" => "whatever" } - {:reject, nil} = VocabularyPolicy.filter(message) + {:reject, _} = VocabularyPolicy.filter(message) end test "it rejects based on child object type" do @@ -89,7 +89,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicyTest do } } - {:reject, nil} = VocabularyPolicy.filter(message) + {:reject, _} = VocabularyPolicy.filter(message) end test "it passes through objects that aren't disallowed" do diff --git a/test/web/activity_pub/object_validator_test.exs b/test/web/activity_pub/object_validator_test.exs deleted file mode 100644 index ee1e1bcfe..000000000 --- a/test/web/activity_pub/object_validator_test.exs +++ /dev/null @@ -1,625 +0,0 @@ -defmodule Pleroma.Web.ActivityPub.ObjectValidatorTest do - use Pleroma.DataCase - - alias Pleroma.Object - alias Pleroma.Web.ActivityPub.ActivityPub - alias Pleroma.Web.ActivityPub.Builder - alias Pleroma.Web.ActivityPub.ObjectValidator - alias Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator - alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator - alias Pleroma.Web.ActivityPub.Utils - alias Pleroma.Web.CommonAPI - - import Pleroma.Factory - - describe "attachments" do - test "works with honkerific attachments" do - attachment = %{ - "mediaType" => "", - "name" => "", - "summary" => "298p3RG7j27tfsZ9RQ.jpg", - "type" => "Document", - "url" => "https://honk.tedunangst.com/d/298p3RG7j27tfsZ9RQ.jpg" - } - - assert {:ok, attachment} = - AttachmentValidator.cast_and_validate(attachment) - |> Ecto.Changeset.apply_action(:insert) - - assert attachment.mediaType == "application/octet-stream" - end - - test "it turns mastodon attachments into our attachments" do - attachment = %{ - "url" => - "http://mastodon.example.org/system/media_attachments/files/000/000/002/original/334ce029e7bfb920.jpg", - "type" => "Document", - "name" => nil, - "mediaType" => "image/jpeg" - } - - {:ok, attachment} = - AttachmentValidator.cast_and_validate(attachment) - |> Ecto.Changeset.apply_action(:insert) - - assert [ - %{ - href: - "http://mastodon.example.org/system/media_attachments/files/000/000/002/original/334ce029e7bfb920.jpg", - type: "Link", - mediaType: "image/jpeg" - } - ] = attachment.url - - assert attachment.mediaType == "image/jpeg" - end - - test "it handles our own uploads" do - user = insert(:user) - - file = %Plug.Upload{ - content_type: "image/jpeg", - path: Path.absname("test/fixtures/image.jpg"), - filename: "an_image.jpg" - } - - {:ok, attachment} = ActivityPub.upload(file, actor: user.ap_id) - - {:ok, attachment} = - attachment.data - |> AttachmentValidator.cast_and_validate() - |> Ecto.Changeset.apply_action(:insert) - - assert attachment.mediaType == "image/jpeg" - end - end - - describe "chat message create activities" do - test "it is invalid if the object already exists" do - user = insert(:user) - recipient = insert(:user) - {:ok, activity} = CommonAPI.post_chat_message(user, recipient, "hey") - object = Object.normalize(activity, false) - - {:ok, create_data, _} = Builder.create(user, object.data, [recipient.ap_id]) - - {:error, cng} = ObjectValidator.validate(create_data, []) - - assert {:object, {"The object to create already exists", []}} in cng.errors - end - - test "it is invalid if the object data has a different `to` or `actor` field" do - user = insert(:user) - recipient = insert(:user) - {:ok, object_data, _} = Builder.chat_message(recipient, user.ap_id, "Hey") - - {:ok, create_data, _} = Builder.create(user, object_data, [recipient.ap_id]) - - {:error, cng} = ObjectValidator.validate(create_data, []) - - assert {:to, {"Recipients don't match with object recipients", []}} in cng.errors - assert {:actor, {"Actor doesn't match with object actor", []}} in cng.errors - end - end - - describe "chat messages" do - setup do - clear_config([:instance, :remote_limit]) - user = insert(:user) - recipient = insert(:user, local: false) - - {:ok, valid_chat_message, _} = Builder.chat_message(user, recipient.ap_id, "hey :firefox:") - - %{user: user, recipient: recipient, valid_chat_message: valid_chat_message} - end - - test "let's through some basic html", %{user: user, recipient: recipient} do - {:ok, valid_chat_message, _} = - Builder.chat_message( - user, - recipient.ap_id, - "hey <a href='https://example.org'>example</a> <script>alert('uguu')</script>" - ) - - assert {:ok, object, _meta} = ObjectValidator.validate(valid_chat_message, []) - - assert object["content"] == - "hey <a href=\"https://example.org\">example</a> alert('uguu')" - end - - test "validates for a basic object we build", %{valid_chat_message: valid_chat_message} do - assert {:ok, object, _meta} = ObjectValidator.validate(valid_chat_message, []) - - assert Map.put(valid_chat_message, "attachment", nil) == object - end - - test "validates for a basic object with an attachment", %{ - valid_chat_message: valid_chat_message, - user: user - } do - file = %Plug.Upload{ - content_type: "image/jpg", - path: Path.absname("test/fixtures/image.jpg"), - filename: "an_image.jpg" - } - - {:ok, attachment} = ActivityPub.upload(file, actor: user.ap_id) - - valid_chat_message = - valid_chat_message - |> Map.put("attachment", attachment.data) - - assert {:ok, object, _meta} = ObjectValidator.validate(valid_chat_message, []) - - assert object["attachment"] - end - - test "validates for a basic object with an attachment in an array", %{ - valid_chat_message: valid_chat_message, - user: user - } do - file = %Plug.Upload{ - content_type: "image/jpg", - path: Path.absname("test/fixtures/image.jpg"), - filename: "an_image.jpg" - } - - {:ok, attachment} = ActivityPub.upload(file, actor: user.ap_id) - - valid_chat_message = - valid_chat_message - |> Map.put("attachment", [attachment.data]) - - assert {:ok, object, _meta} = ObjectValidator.validate(valid_chat_message, []) - - assert object["attachment"] - end - - test "validates for a basic object with an attachment but without content", %{ - valid_chat_message: valid_chat_message, - user: user - } do - file = %Plug.Upload{ - content_type: "image/jpg", - path: Path.absname("test/fixtures/image.jpg"), - filename: "an_image.jpg" - } - - {:ok, attachment} = ActivityPub.upload(file, actor: user.ap_id) - - valid_chat_message = - valid_chat_message - |> Map.put("attachment", attachment.data) - |> Map.delete("content") - - assert {:ok, object, _meta} = ObjectValidator.validate(valid_chat_message, []) - - assert object["attachment"] - end - - test "does not validate if the message has no content", %{ - valid_chat_message: valid_chat_message - } do - contentless = - valid_chat_message - |> Map.delete("content") - - refute match?({:ok, _object, _meta}, ObjectValidator.validate(contentless, [])) - end - - test "does not validate if the message is longer than the remote_limit", %{ - valid_chat_message: valid_chat_message - } do - Pleroma.Config.put([:instance, :remote_limit], 2) - refute match?({:ok, _object, _meta}, ObjectValidator.validate(valid_chat_message, [])) - end - - test "does not validate if the recipient is blocking the actor", %{ - valid_chat_message: valid_chat_message, - user: user, - recipient: recipient - } do - Pleroma.User.block(recipient, user) - refute match?({:ok, _object, _meta}, ObjectValidator.validate(valid_chat_message, [])) - end - - test "does not validate if the actor or the recipient is not in our system", %{ - valid_chat_message: valid_chat_message - } do - chat_message = - valid_chat_message - |> Map.put("actor", "https://raymoo.com/raymoo") - - {:error, _} = ObjectValidator.validate(chat_message, []) - - chat_message = - valid_chat_message - |> Map.put("to", ["https://raymoo.com/raymoo"]) - - {:error, _} = ObjectValidator.validate(chat_message, []) - end - - test "does not validate for a message with multiple recipients", %{ - valid_chat_message: valid_chat_message, - user: user, - recipient: recipient - } do - chat_message = - valid_chat_message - |> Map.put("to", [user.ap_id, recipient.ap_id]) - - assert {:error, _} = ObjectValidator.validate(chat_message, []) - end - - test "does not validate if it doesn't concern local users" do - user = insert(:user, local: false) - recipient = insert(:user, local: false) - - {:ok, valid_chat_message, _} = Builder.chat_message(user, recipient.ap_id, "hey") - assert {:error, _} = ObjectValidator.validate(valid_chat_message, []) - end - end - - 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 - - describe "announces" do - setup do - user = insert(:user) - announcer = insert(:user) - {:ok, post_activity} = CommonAPI.post(user, %{status: "uguu"}) - - object = Object.normalize(post_activity, false) - {:ok, valid_announce, []} = Builder.announce(announcer, object) - - %{ - valid_announce: valid_announce, - user: user, - post_activity: post_activity, - announcer: announcer - } - end - - test "returns ok for a valid announce", %{valid_announce: valid_announce} do - assert {:ok, _object, _meta} = ObjectValidator.validate(valid_announce, []) - end - - test "returns an error if the object can't be found", %{valid_announce: valid_announce} do - without_object = - valid_announce - |> Map.delete("object") - - {:error, cng} = ObjectValidator.validate(without_object, []) - - assert {:object, {"can't be blank", [validation: :required]}} in cng.errors - - nonexisting_object = - valid_announce - |> Map.put("object", "https://gensokyo.2hu/objects/99999999") - - {:error, cng} = ObjectValidator.validate(nonexisting_object, []) - - assert {:object, {"can't find object", []}} in cng.errors - end - - test "returns an error if we don't have the actor", %{valid_announce: valid_announce} do - nonexisting_actor = - valid_announce - |> Map.put("actor", "https://gensokyo.2hu/users/raymoo") - - {:error, cng} = ObjectValidator.validate(nonexisting_actor, []) - - assert {:actor, {"can't find user", []}} in cng.errors - end - - test "returns an error if the actor already announced the object", %{ - valid_announce: valid_announce, - announcer: announcer, - post_activity: post_activity - } do - _announce = CommonAPI.repeat(post_activity.id, announcer) - - {:error, cng} = ObjectValidator.validate(valid_announce, []) - - assert {:actor, {"already announced this object", []}} in cng.errors - assert {:object, {"already announced by this actor", []}} in cng.errors - end - - test "returns an error if the actor can't announce the object", %{ - announcer: announcer, - user: user - } do - {:ok, post_activity} = - CommonAPI.post(user, %{status: "a secret post", visibility: "private"}) - - object = Object.normalize(post_activity, false) - - # Another user can't announce it - {:ok, announce, []} = Builder.announce(announcer, object, public: false) - - {:error, cng} = ObjectValidator.validate(announce, []) - - assert {:actor, {"can not announce this object", []}} in cng.errors - - # The actor of the object can announce it - {:ok, announce, []} = Builder.announce(user, object, public: false) - - assert {:ok, _, _} = ObjectValidator.validate(announce, []) - - # The actor of the object can not announce it publicly - {:ok, announce, []} = Builder.announce(user, object, public: true) - - {:error, cng} = ObjectValidator.validate(announce, []) - - assert {:actor, {"can not announce this object publicly", []}} in cng.errors - end - end -end diff --git a/test/web/activity_pub/object_validators/accept_validation_test.exs b/test/web/activity_pub/object_validators/accept_validation_test.exs new file mode 100644 index 000000000..d6111ba41 --- /dev/null +++ b/test/web/activity_pub/object_validators/accept_validation_test.exs @@ -0,0 +1,56 @@ +# 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.AcceptValidationTest do + use Pleroma.DataCase + + alias Pleroma.Web.ActivityPub.Builder + alias Pleroma.Web.ActivityPub.ObjectValidator + alias Pleroma.Web.ActivityPub.Pipeline + + import Pleroma.Factory + + setup do + follower = insert(:user) + followed = insert(:user, local: false) + + {:ok, follow_data, _} = Builder.follow(follower, followed) + {:ok, follow_activity, _} = Pipeline.common_pipeline(follow_data, local: true) + + {:ok, accept_data, _} = Builder.accept(followed, follow_activity) + + %{accept_data: accept_data, followed: followed} + end + + test "it validates a basic 'accept'", %{accept_data: accept_data} do + assert {:ok, _, _} = ObjectValidator.validate(accept_data, []) + end + + test "it fails when the actor doesn't exist", %{accept_data: accept_data} do + accept_data = + accept_data + |> Map.put("actor", "https://gensokyo.2hu/users/raymoo") + + assert {:error, _} = ObjectValidator.validate(accept_data, []) + end + + test "it fails when the accepted activity doesn't exist", %{accept_data: accept_data} do + accept_data = + accept_data + |> Map.put("object", "https://gensokyo.2hu/users/raymoo/follows/1") + + assert {:error, _} = ObjectValidator.validate(accept_data, []) + end + + test "for an accepted follow, it only validates if the actor of the accept is the followed actor", + %{accept_data: accept_data} do + stranger = insert(:user) + + accept_data = + accept_data + |> Map.put("actor", stranger.ap_id) + + assert {:error, _} = ObjectValidator.validate(accept_data, []) + end +end diff --git a/test/web/activity_pub/object_validators/announce_validation_test.exs b/test/web/activity_pub/object_validators/announce_validation_test.exs new file mode 100644 index 000000000..623342f76 --- /dev/null +++ b/test/web/activity_pub/object_validators/announce_validation_test.exs @@ -0,0 +1,106 @@ +# 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.AnnouncValidationTest do + use Pleroma.DataCase + + alias Pleroma.Object + alias Pleroma.Web.ActivityPub.Builder + alias Pleroma.Web.ActivityPub.ObjectValidator + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + describe "announces" do + setup do + user = insert(:user) + announcer = insert(:user) + {:ok, post_activity} = CommonAPI.post(user, %{status: "uguu"}) + + object = Object.normalize(post_activity, false) + {:ok, valid_announce, []} = Builder.announce(announcer, object) + + %{ + valid_announce: valid_announce, + user: user, + post_activity: post_activity, + announcer: announcer + } + end + + test "returns ok for a valid announce", %{valid_announce: valid_announce} do + assert {:ok, _object, _meta} = ObjectValidator.validate(valid_announce, []) + end + + test "returns an error if the object can't be found", %{valid_announce: valid_announce} do + without_object = + valid_announce + |> Map.delete("object") + + {:error, cng} = ObjectValidator.validate(without_object, []) + + assert {:object, {"can't be blank", [validation: :required]}} in cng.errors + + nonexisting_object = + valid_announce + |> Map.put("object", "https://gensokyo.2hu/objects/99999999") + + {:error, cng} = ObjectValidator.validate(nonexisting_object, []) + + assert {:object, {"can't find object", []}} in cng.errors + end + + test "returns an error if we don't have the actor", %{valid_announce: valid_announce} do + nonexisting_actor = + valid_announce + |> Map.put("actor", "https://gensokyo.2hu/users/raymoo") + + {:error, cng} = ObjectValidator.validate(nonexisting_actor, []) + + assert {:actor, {"can't find user", []}} in cng.errors + end + + test "returns an error if the actor already announced the object", %{ + valid_announce: valid_announce, + announcer: announcer, + post_activity: post_activity + } do + _announce = CommonAPI.repeat(post_activity.id, announcer) + + {:error, cng} = ObjectValidator.validate(valid_announce, []) + + assert {:actor, {"already announced this object", []}} in cng.errors + assert {:object, {"already announced by this actor", []}} in cng.errors + end + + test "returns an error if the actor can't announce the object", %{ + announcer: announcer, + user: user + } do + {:ok, post_activity} = + CommonAPI.post(user, %{status: "a secret post", visibility: "private"}) + + object = Object.normalize(post_activity, false) + + # Another user can't announce it + {:ok, announce, []} = Builder.announce(announcer, object, public: false) + + {:error, cng} = ObjectValidator.validate(announce, []) + + assert {:actor, {"can not announce this object", []}} in cng.errors + + # The actor of the object can announce it + {:ok, announce, []} = Builder.announce(user, object, public: false) + + assert {:ok, _, _} = ObjectValidator.validate(announce, []) + + # The actor of the object can not announce it publicly + {:ok, announce, []} = Builder.announce(user, object, public: true) + + {:error, cng} = ObjectValidator.validate(announce, []) + + assert {:actor, {"can not announce this object publicly", []}} in cng.errors + end + end +end diff --git a/test/web/activity_pub/object_validators/attachment_validator_test.exs b/test/web/activity_pub/object_validators/attachment_validator_test.exs new file mode 100644 index 000000000..558bb3131 --- /dev/null +++ b/test/web/activity_pub/object_validators/attachment_validator_test.exs @@ -0,0 +1,74 @@ +# 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.AttachmentValidatorTest do + use Pleroma.DataCase + + alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator + + import Pleroma.Factory + + describe "attachments" do + test "works with honkerific attachments" do + attachment = %{ + "mediaType" => "", + "name" => "", + "summary" => "298p3RG7j27tfsZ9RQ.jpg", + "type" => "Document", + "url" => "https://honk.tedunangst.com/d/298p3RG7j27tfsZ9RQ.jpg" + } + + assert {:ok, attachment} = + AttachmentValidator.cast_and_validate(attachment) + |> Ecto.Changeset.apply_action(:insert) + + assert attachment.mediaType == "application/octet-stream" + end + + test "it turns mastodon attachments into our attachments" do + attachment = %{ + "url" => + "http://mastodon.example.org/system/media_attachments/files/000/000/002/original/334ce029e7bfb920.jpg", + "type" => "Document", + "name" => nil, + "mediaType" => "image/jpeg" + } + + {:ok, attachment} = + AttachmentValidator.cast_and_validate(attachment) + |> Ecto.Changeset.apply_action(:insert) + + assert [ + %{ + href: + "http://mastodon.example.org/system/media_attachments/files/000/000/002/original/334ce029e7bfb920.jpg", + type: "Link", + mediaType: "image/jpeg" + } + ] = attachment.url + + assert attachment.mediaType == "image/jpeg" + end + + test "it handles our own uploads" do + user = insert(:user) + + file = %Plug.Upload{ + content_type: "image/jpg", + path: Path.absname("test/fixtures/image.jpg"), + filename: "an_image.jpg" + } + + {:ok, attachment} = ActivityPub.upload(file, actor: user.ap_id) + + {:ok, attachment} = + attachment.data + |> AttachmentValidator.cast_and_validate() + |> Ecto.Changeset.apply_action(:insert) + + assert attachment.mediaType == "image/jpeg" + end + end +end diff --git a/test/web/activity_pub/object_validators/block_validation_test.exs b/test/web/activity_pub/object_validators/block_validation_test.exs new file mode 100644 index 000000000..c08d4b2e8 --- /dev/null +++ b/test/web/activity_pub/object_validators/block_validation_test.exs @@ -0,0 +1,39 @@ +# 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.BlockValidationTest do + use Pleroma.DataCase + + alias Pleroma.Web.ActivityPub.Builder + alias Pleroma.Web.ActivityPub.ObjectValidator + + import Pleroma.Factory + + describe "blocks" do + setup do + user = insert(:user, local: false) + blocked = insert(:user) + + {:ok, valid_block, []} = Builder.block(user, blocked) + + %{user: user, valid_block: valid_block} + end + + test "validates a basic object", %{ + valid_block: valid_block + } do + assert {:ok, _block, []} = ObjectValidator.validate(valid_block, []) + end + + test "returns an error if we don't know the blocked user", %{ + valid_block: valid_block + } do + block = + valid_block + |> Map.put("object", "https://gensokyo.2hu/users/raymoo") + + assert {:error, _cng} = ObjectValidator.validate(block, []) + end + end +end diff --git a/test/web/activity_pub/object_validators/chat_validation_test.exs b/test/web/activity_pub/object_validators/chat_validation_test.exs new file mode 100644 index 000000000..16e4808e5 --- /dev/null +++ b/test/web/activity_pub/object_validators/chat_validation_test.exs @@ -0,0 +1,212 @@ +# 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.ChatValidationTest do + use Pleroma.DataCase + alias Pleroma.Object + alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.ActivityPub.Builder + alias Pleroma.Web.ActivityPub.ObjectValidator + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + describe "chat message create activities" do + test "it is invalid if the object already exists" do + user = insert(:user) + recipient = insert(:user) + {:ok, activity} = CommonAPI.post_chat_message(user, recipient, "hey") + object = Object.normalize(activity, false) + + {:ok, create_data, _} = Builder.create(user, object.data, [recipient.ap_id]) + + {:error, cng} = ObjectValidator.validate(create_data, []) + + assert {:object, {"The object to create already exists", []}} in cng.errors + end + + test "it is invalid if the object data has a different `to` or `actor` field" do + user = insert(:user) + recipient = insert(:user) + {:ok, object_data, _} = Builder.chat_message(recipient, user.ap_id, "Hey") + + {:ok, create_data, _} = Builder.create(user, object_data, [recipient.ap_id]) + + {:error, cng} = ObjectValidator.validate(create_data, []) + + assert {:to, {"Recipients don't match with object recipients", []}} in cng.errors + assert {:actor, {"Actor doesn't match with object actor", []}} in cng.errors + end + end + + describe "chat messages" do + setup do + clear_config([:instance, :remote_limit]) + user = insert(:user) + recipient = insert(:user, local: false) + + {:ok, valid_chat_message, _} = Builder.chat_message(user, recipient.ap_id, "hey :firefox:") + + %{user: user, recipient: recipient, valid_chat_message: valid_chat_message} + end + + test "let's through some basic html", %{user: user, recipient: recipient} do + {:ok, valid_chat_message, _} = + Builder.chat_message( + user, + recipient.ap_id, + "hey <a href='https://example.org'>example</a> <script>alert('uguu')</script>" + ) + + assert {:ok, object, _meta} = ObjectValidator.validate(valid_chat_message, []) + + assert object["content"] == + "hey <a href=\"https://example.org\">example</a> alert('uguu')" + end + + test "validates for a basic object we build", %{valid_chat_message: valid_chat_message} do + assert {:ok, object, _meta} = ObjectValidator.validate(valid_chat_message, []) + + assert Map.put(valid_chat_message, "attachment", nil) == object + assert match?(%{"firefox" => _}, object["emoji"]) + end + + test "validates for a basic object with an attachment", %{ + valid_chat_message: valid_chat_message, + user: user + } do + file = %Plug.Upload{ + content_type: "image/jpg", + path: Path.absname("test/fixtures/image.jpg"), + filename: "an_image.jpg" + } + + {:ok, attachment} = ActivityPub.upload(file, actor: user.ap_id) + + valid_chat_message = + valid_chat_message + |> Map.put("attachment", attachment.data) + + assert {:ok, object, _meta} = ObjectValidator.validate(valid_chat_message, []) + + assert object["attachment"] + end + + test "validates for a basic object with an attachment in an array", %{ + valid_chat_message: valid_chat_message, + user: user + } do + file = %Plug.Upload{ + content_type: "image/jpg", + path: Path.absname("test/fixtures/image.jpg"), + filename: "an_image.jpg" + } + + {:ok, attachment} = ActivityPub.upload(file, actor: user.ap_id) + + valid_chat_message = + valid_chat_message + |> Map.put("attachment", [attachment.data]) + + assert {:ok, object, _meta} = ObjectValidator.validate(valid_chat_message, []) + + assert object["attachment"] + end + + test "validates for a basic object with an attachment but without content", %{ + valid_chat_message: valid_chat_message, + user: user + } do + file = %Plug.Upload{ + content_type: "image/jpg", + path: Path.absname("test/fixtures/image.jpg"), + filename: "an_image.jpg" + } + + {:ok, attachment} = ActivityPub.upload(file, actor: user.ap_id) + + valid_chat_message = + valid_chat_message + |> Map.put("attachment", attachment.data) + |> Map.delete("content") + + assert {:ok, object, _meta} = ObjectValidator.validate(valid_chat_message, []) + + assert object["attachment"] + end + + test "does not validate if the message has no content", %{ + valid_chat_message: valid_chat_message + } do + contentless = + valid_chat_message + |> Map.delete("content") + + refute match?({:ok, _object, _meta}, ObjectValidator.validate(contentless, [])) + end + + test "does not validate if the message is longer than the remote_limit", %{ + valid_chat_message: valid_chat_message + } do + Pleroma.Config.put([:instance, :remote_limit], 2) + refute match?({:ok, _object, _meta}, ObjectValidator.validate(valid_chat_message, [])) + end + + test "does not validate if the recipient is blocking the actor", %{ + valid_chat_message: valid_chat_message, + user: user, + recipient: recipient + } do + Pleroma.User.block(recipient, user) + refute match?({:ok, _object, _meta}, ObjectValidator.validate(valid_chat_message, [])) + end + + test "does not validate if the recipient is not accepting chat messages", %{ + valid_chat_message: valid_chat_message, + recipient: recipient + } do + recipient + |> Ecto.Changeset.change(%{accepts_chat_messages: false}) + |> Pleroma.Repo.update!() + + refute match?({:ok, _object, _meta}, ObjectValidator.validate(valid_chat_message, [])) + end + + test "does not validate if the actor or the recipient is not in our system", %{ + valid_chat_message: valid_chat_message + } do + chat_message = + valid_chat_message + |> Map.put("actor", "https://raymoo.com/raymoo") + + {:error, _} = ObjectValidator.validate(chat_message, []) + + chat_message = + valid_chat_message + |> Map.put("to", ["https://raymoo.com/raymoo"]) + + {:error, _} = ObjectValidator.validate(chat_message, []) + end + + test "does not validate for a message with multiple recipients", %{ + valid_chat_message: valid_chat_message, + user: user, + recipient: recipient + } do + chat_message = + valid_chat_message + |> Map.put("to", [user.ap_id, recipient.ap_id]) + + assert {:error, _} = ObjectValidator.validate(chat_message, []) + end + + test "does not validate if it doesn't concern local users" do + user = insert(:user, local: false) + recipient = insert(:user, local: false) + + {:ok, valid_chat_message, _} = Builder.chat_message(user, recipient.ap_id, "hey") + assert {:error, _} = ObjectValidator.validate(valid_chat_message, []) + end + end +end diff --git a/test/web/activity_pub/object_validators/delete_validation_test.exs b/test/web/activity_pub/object_validators/delete_validation_test.exs new file mode 100644 index 000000000..02683b899 --- /dev/null +++ b/test/web/activity_pub/object_validators/delete_validation_test.exs @@ -0,0 +1,106 @@ +# 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.DeleteValidationTest do + use Pleroma.DataCase + + alias Pleroma.Object + alias Pleroma.Web.ActivityPub.Builder + alias Pleroma.Web.ActivityPub.ObjectValidator + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + 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 modify 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 +end diff --git a/test/web/activity_pub/object_validators/emoji_react_validation_test.exs b/test/web/activity_pub/object_validators/emoji_react_validation_test.exs new file mode 100644 index 000000000..582e6d785 --- /dev/null +++ b/test/web/activity_pub/object_validators/emoji_react_validation_test.exs @@ -0,0 +1,53 @@ +# 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.EmojiReactHandlingTest do + use Pleroma.DataCase + + alias Pleroma.Web.ActivityPub.Builder + alias Pleroma.Web.ActivityPub.ObjectValidator + 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 +end diff --git a/test/web/activity_pub/object_validators/follow_validation_test.exs b/test/web/activity_pub/object_validators/follow_validation_test.exs new file mode 100644 index 000000000..6e1378be2 --- /dev/null +++ b/test/web/activity_pub/object_validators/follow_validation_test.exs @@ -0,0 +1,26 @@ +# 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.FollowValidationTest do + use Pleroma.DataCase + + alias Pleroma.Web.ActivityPub.Builder + alias Pleroma.Web.ActivityPub.ObjectValidator + + import Pleroma.Factory + + describe "Follows" do + setup do + follower = insert(:user) + followed = insert(:user) + + {:ok, valid_follow, []} = Builder.follow(follower, followed) + %{follower: follower, followed: followed, valid_follow: valid_follow} + end + + test "validates a basic follow object", %{valid_follow: valid_follow} do + assert {:ok, _follow, []} = ObjectValidator.validate(valid_follow, []) + end + end +end diff --git a/test/web/activity_pub/object_validators/like_validation_test.exs b/test/web/activity_pub/object_validators/like_validation_test.exs new file mode 100644 index 000000000..2c033b7e2 --- /dev/null +++ b/test/web/activity_pub/object_validators/like_validation_test.exs @@ -0,0 +1,113 @@ +# 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.LikeValidationTest do + use Pleroma.DataCase + + alias Pleroma.Web.ActivityPub.ObjectValidator + alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator + alias Pleroma.Web.ActivityPub.Utils + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + 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/reject_validation_test.exs b/test/web/activity_pub/object_validators/reject_validation_test.exs new file mode 100644 index 000000000..370bb6e5c --- /dev/null +++ b/test/web/activity_pub/object_validators/reject_validation_test.exs @@ -0,0 +1,56 @@ +# 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.RejectValidationTest do + use Pleroma.DataCase + + alias Pleroma.Web.ActivityPub.Builder + alias Pleroma.Web.ActivityPub.ObjectValidator + alias Pleroma.Web.ActivityPub.Pipeline + + import Pleroma.Factory + + setup do + follower = insert(:user) + followed = insert(:user, local: false) + + {:ok, follow_data, _} = Builder.follow(follower, followed) + {:ok, follow_activity, _} = Pipeline.common_pipeline(follow_data, local: true) + + {:ok, reject_data, _} = Builder.reject(followed, follow_activity) + + %{reject_data: reject_data, followed: followed} + end + + test "it validates a basic 'reject'", %{reject_data: reject_data} do + assert {:ok, _, _} = ObjectValidator.validate(reject_data, []) + end + + test "it fails when the actor doesn't exist", %{reject_data: reject_data} do + reject_data = + reject_data + |> Map.put("actor", "https://gensokyo.2hu/users/raymoo") + + assert {:error, _} = ObjectValidator.validate(reject_data, []) + end + + test "it fails when the rejected activity doesn't exist", %{reject_data: reject_data} do + reject_data = + reject_data + |> Map.put("object", "https://gensokyo.2hu/users/raymoo/follows/1") + + assert {:error, _} = ObjectValidator.validate(reject_data, []) + end + + test "for an rejected follow, it only validates if the actor of the reject is the followed actor", + %{reject_data: reject_data} do + stranger = insert(:user) + + reject_data = + reject_data + |> Map.put("actor", stranger.ap_id) + + assert {:error, _} = ObjectValidator.validate(reject_data, []) + 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 index 3e17a9497..43be8e936 100644 --- a/test/web/activity_pub/object_validators/types/date_time_test.exs +++ b/test/web/activity_pub/object_validators/types/date_time_test.exs @@ -1,5 +1,5 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.Types.DateTimeTest do - alias Pleroma.Web.ActivityPub.ObjectValidators.Types.DateTime + alias Pleroma.EctoType.ActivityPub.ObjectValidators.DateTime use Pleroma.DataCase test "it validates an xsd:Datetime" do 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 index c8911948e..e0ab76379 100644 --- a/test/web/activity_pub/object_validators/types/object_id_test.exs +++ b/test/web/activity_pub/object_validators/types/object_id_test.exs @@ -3,7 +3,7 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ObjectValidators.Types.ObjectIDTest do - alias Pleroma.Web.ActivityPub.ObjectValidators.Types.ObjectID + alias Pleroma.EctoType.ActivityPub.ObjectValidators.ObjectID use Pleroma.DataCase @uris [ diff --git a/test/web/activity_pub/object_validators/types/recipients_test.exs b/test/web/activity_pub/object_validators/types/recipients_test.exs index f278f039b..053916bdd 100644 --- a/test/web/activity_pub/object_validators/types/recipients_test.exs +++ b/test/web/activity_pub/object_validators/types/recipients_test.exs @@ -1,5 +1,5 @@ defmodule Pleroma.Web.ObjectValidators.Types.RecipientsTest do - alias Pleroma.Web.ActivityPub.ObjectValidators.Types.Recipients + alias Pleroma.EctoType.ActivityPub.ObjectValidators.Recipients use Pleroma.DataCase test "it asserts that all elements of the list are object ids" do diff --git a/test/web/activity_pub/object_validators/types/safe_text_test.exs b/test/web/activity_pub/object_validators/types/safe_text_test.exs index d4a574554..9c08606f6 100644 --- a/test/web/activity_pub/object_validators/types/safe_text_test.exs +++ b/test/web/activity_pub/object_validators/types/safe_text_test.exs @@ -5,7 +5,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.Types.SafeTextTest do use Pleroma.DataCase - alias Pleroma.Web.ActivityPub.ObjectValidators.Types.SafeText + alias Pleroma.EctoType.ActivityPub.ObjectValidators.SafeText test "it lets normal text go through" do text = "hey how are you" diff --git a/test/web/activity_pub/object_validators/undo_validation_test.exs b/test/web/activity_pub/object_validators/undo_validation_test.exs new file mode 100644 index 000000000..75bbcc4b6 --- /dev/null +++ b/test/web/activity_pub/object_validators/undo_validation_test.exs @@ -0,0 +1,53 @@ +# 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.UndoHandlingTest do + use Pleroma.DataCase + + alias Pleroma.Web.ActivityPub.Builder + alias Pleroma.Web.ActivityPub.ObjectValidator + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + 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 +end diff --git a/test/web/activity_pub/object_validators/update_validation_test.exs b/test/web/activity_pub/object_validators/update_validation_test.exs new file mode 100644 index 000000000..5e80cf731 --- /dev/null +++ b/test/web/activity_pub/object_validators/update_validation_test.exs @@ -0,0 +1,44 @@ +# 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.UpdateHandlingTest do + use Pleroma.DataCase + + alias Pleroma.Web.ActivityPub.Builder + alias Pleroma.Web.ActivityPub.ObjectValidator + + import Pleroma.Factory + + describe "updates" do + setup do + user = insert(:user) + + object = %{ + "id" => user.ap_id, + "name" => "A new name", + "summary" => "A new bio" + } + + {:ok, valid_update, []} = Builder.update(user, object) + + %{user: user, valid_update: valid_update} + end + + test "validates a basic object", %{valid_update: valid_update} do + assert {:ok, _update, []} = ObjectValidator.validate(valid_update, []) + end + + test "returns an error if the object can't be updated by the actor", %{ + valid_update: valid_update + } do + other_user = insert(:user) + + update = + valid_update + |> Map.put("actor", other_user.ap_id) + + assert {:error, _cng} = ObjectValidator.validate(update, []) + end + end +end diff --git a/test/web/activity_pub/pipeline_test.exs b/test/web/activity_pub/pipeline_test.exs index 8deb64501..f2a231eaf 100644 --- a/test/web/activity_pub/pipeline_test.exs +++ b/test/web/activity_pub/pipeline_test.exs @@ -14,6 +14,51 @@ defmodule Pleroma.Web.ActivityPub.PipelineTest do :ok end + test "when given an `object_data` in meta, Federation will receive a the original activity with the `object` field set to this embedded object" do + activity = insert(:note_activity) + object = %{"id" => "1", "type" => "Love"} + meta = [local: true, object_data: object] + + activity_with_object = %{activity | data: Map.put(activity.data, "object", object)} + + 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, + handle_after_transaction: fn m -> 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)) + refute called(Pleroma.Web.Federator.publish(activity)) + assert_called(Pleroma.Web.Federator.publish(activity_with_object)) + end + end + test "it goes through validation, filtering, persisting, side effects and federation for local activities" do activity = insert(:note_activity) meta = [local: true] diff --git a/test/web/activity_pub/publisher_test.exs b/test/web/activity_pub/publisher_test.exs index c2bc38d52..b9388b966 100644 --- a/test/web/activity_pub/publisher_test.exs +++ b/test/web/activity_pub/publisher_test.exs @@ -123,6 +123,39 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do end describe "publish_one/1" do + test "publish to url with with different ports" do + inbox80 = "http://42.site/users/nick1/inbox" + inbox42 = "http://42.site:42/users/nick1/inbox" + + mock(fn + %{method: :post, url: "http://42.site:42/users/nick1/inbox"} -> + {:ok, %Tesla.Env{status: 200, body: "port 42"}} + + %{method: :post, url: "http://42.site/users/nick1/inbox"} -> + {:ok, %Tesla.Env{status: 200, body: "port 80"}} + end) + + actor = insert(:user) + + assert {:ok, %{body: "port 42"}} = + Publisher.publish_one(%{ + inbox: inbox42, + json: "{}", + actor: actor, + id: 1, + unreachable_since: true + }) + + assert {:ok, %{body: "port 80"}} = + Publisher.publish_one(%{ + inbox: inbox80, + json: "{}", + actor: actor, + id: 1, + unreachable_since: true + }) + end + test_with_mock "calls `Instances.set_reachable` on successful federation if `unreachable_since` is not specified", Instances, [:passthrough], @@ -131,7 +164,6 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do inbox = "http://200.site/users/nick1/inbox" assert {:ok, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1}) - assert called(Instances.set_reachable(inbox)) end diff --git a/test/web/activity_pub/relay_test.exs b/test/web/activity_pub/relay_test.exs index b3b573c9b..9d657ac4f 100644 --- a/test/web/activity_pub/relay_test.exs +++ b/test/web/activity_pub/relay_test.exs @@ -7,8 +7,8 @@ defmodule Pleroma.Web.ActivityPub.RelayTest do alias Pleroma.Activity alias Pleroma.User - alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.Relay + alias Pleroma.Web.CommonAPI import ExUnit.CaptureLog import Pleroma.Factory @@ -53,8 +53,7 @@ defmodule Pleroma.Web.ActivityPub.RelayTest do test "returns activity" do user = insert(:user) service_actor = Relay.get_actor() - ActivityPub.follow(service_actor, user) - Pleroma.User.follow(service_actor, user) + CommonAPI.follow(service_actor, user) assert "#{user.ap_id}/followers" in User.following(service_actor) assert {:ok, %Activity{} = activity} = Relay.unfollow(user.ap_id) assert activity.actor == "#{Pleroma.Web.Endpoint.url()}/relay" @@ -74,6 +73,7 @@ defmodule Pleroma.Web.ActivityPub.RelayTest do assert Relay.publish(activity) == {:error, "Not implemented"} end + @tag capture_log: true test "returns error when activity not public" do activity = insert(:direct_note_activity) assert Relay.publish(activity) == {:error, false} diff --git a/test/web/activity_pub/side_effects_test.exs b/test/web/activity_pub/side_effects_test.exs index 6bbbaae87..9efbaad04 100644 --- a/test/web/activity_pub/side_effects_test.exs +++ b/test/web/activity_pub/side_effects_test.exs @@ -19,8 +19,9 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do alias Pleroma.Web.ActivityPub.SideEffects alias Pleroma.Web.CommonAPI - import Pleroma.Factory + import ExUnit.CaptureLog import Mock + import Pleroma.Factory describe "handle_after_transaction" do test "it streams out notifications and streams" do @@ -64,6 +65,72 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do end end + describe "blocking users" do + setup do + user = insert(:user) + blocked = insert(:user) + User.follow(blocked, user) + User.follow(user, blocked) + + {:ok, block_data, []} = Builder.block(user, blocked) + {:ok, block, _meta} = ActivityPub.persist(block_data, local: true) + + %{user: user, blocked: blocked, block: block} + end + + test "it unfollows and blocks", %{user: user, blocked: blocked, block: block} do + assert User.following?(user, blocked) + assert User.following?(blocked, user) + + {:ok, _, _} = SideEffects.handle(block) + + refute User.following?(user, blocked) + refute User.following?(blocked, user) + assert User.blocks?(user, blocked) + end + + test "it blocks but does not unfollow if the relevant setting is set", %{ + user: user, + blocked: blocked, + block: block + } do + clear_config([:activitypub, :unfollow_blocked], false) + assert User.following?(user, blocked) + assert User.following?(blocked, user) + + {:ok, _, _} = SideEffects.handle(block) + + refute User.following?(user, blocked) + assert User.following?(blocked, user) + assert User.blocks?(user, blocked) + end + end + + describe "update users" do + setup do + user = insert(:user) + {:ok, update_data, []} = Builder.update(user, %{"id" => user.ap_id, "name" => "new name!"}) + {:ok, update, _meta} = ActivityPub.persist(update_data, local: true) + + %{user: user, update_data: update_data, update: update} + end + + test "it updates the user", %{user: user, update: update} do + {:ok, _, _} = SideEffects.handle(update) + user = User.get_by_id(user.id) + assert user.name == "new name!" + end + + test "it uses a given changeset to update", %{user: user, update: update} do + changeset = Ecto.Changeset.change(user, %{default_scope: "direct"}) + + assert user.default_scope == "public" + {:ok, _, _} = SideEffects.handle(update, user_update_changeset: changeset) + user = User.get_by_id(user.id) + assert user.default_scope == "direct" + end + end + describe "delete objects" do setup do user = insert(:user) @@ -155,6 +222,22 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do assert User.get_cached_by_ap_id(user.ap_id).deactivated end + + test "it logs issues with objects deletion", %{ + delete: delete, + object: object + } do + {:ok, object} = + object + |> Object.change(%{data: Map.delete(object.data, "actor")}) + |> Repo.update() + + Object.invalid_object_cache(object) + + assert capture_log(fn -> + {:error, :no_object_actor} = SideEffects.handle(delete) + end) =~ "object doesn't have an actor" + end end describe "EmojiReact objects" do @@ -217,8 +300,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do {: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, block} = CommonAPI.block(user, poster) {:ok, undo_data, _meta} = Builder.undo(user, like) {:ok, like_undo, _meta} = ActivityPub.persist(undo_data, local: true) @@ -247,8 +329,12 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do } end - test "deletes the original block", %{block_undo: block_undo, block: block} do - {:ok, _block_undo, _} = SideEffects.handle(block_undo) + test "deletes the original block", %{ + block_undo: block_undo, + block: block + } do + {:ok, _block_undo, _meta} = SideEffects.handle(block_undo) + refute Activity.get_by_id(block.id) end @@ -524,10 +610,29 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do end test "it streams out the announce", %{announce: announce} do - with_mock Pleroma.Web.ActivityPub.ActivityPub, [:passthrough], stream_out: fn _ -> nil end do + with_mocks([ + { + Pleroma.Web.Streamer, + [], + [ + stream: fn _, _ -> nil end + ] + }, + { + Pleroma.Web.Push, + [], + [ + send: fn _ -> nil end + ] + } + ]) do {:ok, announce, _} = SideEffects.handle(announce) - assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out(announce)) + assert called( + Pleroma.Web.Streamer.stream(["user", "list", "public", "public:local"], announce) + ) + + assert called(Pleroma.Web.Push.send(:_)) end end end diff --git a/test/web/activity_pub/transmogrifier/accept_handling_test.exs b/test/web/activity_pub/transmogrifier/accept_handling_test.exs new file mode 100644 index 000000000..77d468f5c --- /dev/null +++ b/test/web/activity_pub/transmogrifier/accept_handling_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.ActivityPub.Transmogrifier.AcceptHandlingTest do + use Pleroma.DataCase + + alias Pleroma.User + alias Pleroma.Web.ActivityPub.Transmogrifier + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + test "it works for incoming accepts which were pre-accepted" do + follower = insert(:user) + followed = insert(:user) + + {:ok, follower} = User.follow(follower, followed) + assert User.following?(follower, followed) == true + + {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed) + + accept_data = + File.read!("test/fixtures/mastodon-accept-activity.json") + |> Poison.decode!() + |> Map.put("actor", followed.ap_id) + + object = + accept_data["object"] + |> Map.put("actor", follower.ap_id) + |> Map.put("id", follow_activity.data["id"]) + + accept_data = Map.put(accept_data, "object", object) + + {:ok, activity} = Transmogrifier.handle_incoming(accept_data) + refute activity.local + + assert activity.data["object"] == follow_activity.data["id"] + + assert activity.data["id"] == accept_data["id"] + + follower = User.get_cached_by_id(follower.id) + + assert User.following?(follower, followed) == true + end + + test "it works for incoming accepts which are referenced by IRI only" do + follower = insert(:user) + followed = insert(:user, locked: true) + + {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed) + + accept_data = + File.read!("test/fixtures/mastodon-accept-activity.json") + |> Poison.decode!() + |> Map.put("actor", followed.ap_id) + |> Map.put("object", follow_activity.data["id"]) + + {:ok, activity} = Transmogrifier.handle_incoming(accept_data) + assert activity.data["object"] == follow_activity.data["id"] + + 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 + follower = insert(:user) + followed = insert(:user, locked: true) + + accept_data = + File.read!("test/fixtures/mastodon-accept-activity.json") + |> Poison.decode!() + |> Map.put("actor", followed.ap_id) + + accept_data = + Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id)) + + {:error, _} = Transmogrifier.handle_incoming(accept_data) + + follower = User.get_cached_by_id(follower.id) + + refute User.following?(follower, followed) == true + end +end diff --git a/test/web/activity_pub/transmogrifier/answer_handling_test.exs b/test/web/activity_pub/transmogrifier/answer_handling_test.exs new file mode 100644 index 000000000..0f6605c3f --- /dev/null +++ b/test/web/activity_pub/transmogrifier/answer_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.AnswerHandlingTest do + use Pleroma.DataCase + + alias Pleroma.Activity + alias Pleroma.Object + alias Pleroma.Web.ActivityPub.Transmogrifier + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + setup_all do + Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + + test "incoming, rewrites Note to Answer and increments vote counters" do + user = insert(:user) + + {:ok, activity} = + CommonAPI.post(user, %{ + status: "suya...", + poll: %{options: ["suya", "suya.", "suya.."], expires_in: 10} + }) + + object = Object.normalize(activity) + + data = + File.read!("test/fixtures/mastodon-vote.json") + |> Poison.decode!() + |> Kernel.put_in(["to"], user.ap_id) + |> Kernel.put_in(["object", "inReplyTo"], object.data["id"]) + |> Kernel.put_in(["object", "to"], user.ap_id) + + {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) + answer_object = Object.normalize(activity) + assert answer_object.data["type"] == "Answer" + assert answer_object.data["inReplyTo"] == object.data["id"] + + new_object = Object.get_by_ap_id(object.data["id"]) + assert new_object.data["replies_count"] == object.data["replies_count"] + + assert Enum.any?( + new_object.data["oneOf"], + fn + %{"name" => "suya..", "replies" => %{"totalItems" => 1}} -> true + _ -> false + end + ) + end + + test "outgoing, rewrites Answer to Note" do + user = insert(:user) + + {:ok, poll_activity} = + CommonAPI.post(user, %{ + status: "suya...", + poll: %{options: ["suya", "suya.", "suya.."], expires_in: 10} + }) + + poll_object = Object.normalize(poll_activity) + # TODO: Replace with CommonAPI vote creation when implemented + data = + File.read!("test/fixtures/mastodon-vote.json") + |> Poison.decode!() + |> Kernel.put_in(["to"], user.ap_id) + |> Kernel.put_in(["object", "inReplyTo"], poll_object.data["id"]) + |> Kernel.put_in(["object", "to"], user.ap_id) + + {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) + {:ok, data} = Transmogrifier.prepare_outgoing(activity.data) + + assert data["object"]["type"] == "Note" + end +end diff --git a/test/web/activity_pub/transmogrifier/audio_handling_test.exs b/test/web/activity_pub/transmogrifier/audio_handling_test.exs new file mode 100644 index 000000000..0636d00c5 --- /dev/null +++ b/test/web/activity_pub/transmogrifier/audio_handling_test.exs @@ -0,0 +1,83 @@ +# 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.AudioHandlingTest do + use Oban.Testing, repo: Pleroma.Repo + use Pleroma.DataCase + + alias Pleroma.Activity + alias Pleroma.Object + alias Pleroma.Web.ActivityPub.Transmogrifier + + import Pleroma.Factory + + test "it works for incoming listens" do + _user = insert(:user, ap_id: "http://mastodon.example.org/users/admin") + + data = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "to" => ["https://www.w3.org/ns/activitystreams#Public"], + "cc" => [], + "type" => "Listen", + "id" => "http://mastodon.example.org/users/admin/listens/1234/activity", + "actor" => "http://mastodon.example.org/users/admin", + "object" => %{ + "type" => "Audio", + "id" => "http://mastodon.example.org/users/admin/listens/1234", + "attributedTo" => "http://mastodon.example.org/users/admin", + "title" => "lain radio episode 1", + "artist" => "lain", + "album" => "lain radio", + "length" => 180_000 + } + } + + {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) + + object = Object.normalize(activity) + + assert object.data["title"] == "lain radio episode 1" + assert object.data["artist"] == "lain" + assert object.data["album"] == "lain radio" + assert object.data["length"] == 180_000 + end + + test "Funkwhale Audio object" do + Tesla.Mock.mock(fn + %{url: "https://channels.tests.funkwhale.audio/federation/actors/compositions"} -> + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/funkwhale_channel.json") + } + end) + + data = File.read!("test/fixtures/tesla_mock/funkwhale_create_audio.json") |> Poison.decode!() + + {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) + + assert object = Object.normalize(activity, false) + + assert object.data["to"] == ["https://www.w3.org/ns/activitystreams#Public"] + + assert object.data["cc"] == [] + + assert object.data["url"] == "https://channels.tests.funkwhale.audio/library/tracks/74" + + assert object.data["attachment"] == [ + %{ + "mediaType" => "audio/ogg", + "type" => "Link", + "name" => nil, + "url" => [ + %{ + "href" => + "https://channels.tests.funkwhale.audio/api/v1/listen/3901e5d8-0445-49d5-9711-e096cf32e515/?upload=42342395-0208-4fee-a38d-259a6dae0871&download=false", + "mediaType" => "audio/ogg", + "type" => "Link" + } + ] + } + ] + end +end diff --git a/test/web/activity_pub/transmogrifier/block_handling_test.exs b/test/web/activity_pub/transmogrifier/block_handling_test.exs new file mode 100644 index 000000000..71f1a0ed5 --- /dev/null +++ b/test/web/activity_pub/transmogrifier/block_handling_test.exs @@ -0,0 +1,63 @@ +# 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.BlockHandlingTest do + use Pleroma.DataCase + + alias Pleroma.Activity + alias Pleroma.User + alias Pleroma.Web.ActivityPub.Transmogrifier + + import Pleroma.Factory + + test "it works for incoming blocks" do + user = insert(:user) + + data = + File.read!("test/fixtures/mastodon-block-activity.json") + |> Poison.decode!() + |> Map.put("object", user.ap_id) + + blocker = insert(:user, ap_id: data["actor"]) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["type"] == "Block" + assert data["object"] == user.ap_id + assert data["actor"] == "http://mastodon.example.org/users/admin" + + assert User.blocks?(blocker, user) + end + + test "incoming blocks successfully tear down any follow relationship" do + blocker = insert(:user) + blocked = insert(:user) + + data = + File.read!("test/fixtures/mastodon-block-activity.json") + |> Poison.decode!() + |> Map.put("object", blocked.ap_id) + |> Map.put("actor", blocker.ap_id) + + {:ok, blocker} = User.follow(blocker, blocked) + {:ok, blocked} = User.follow(blocked, blocker) + + assert User.following?(blocker, blocked) + assert User.following?(blocked, blocker) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["type"] == "Block" + assert data["object"] == blocked.ap_id + assert data["actor"] == blocker.ap_id + + blocker = User.get_cached_by_ap_id(data["actor"]) + blocked = User.get_cached_by_ap_id(data["object"]) + + assert User.blocks?(blocker, blocked) + + refute User.following?(blocker, blocked) + refute User.following?(blocked, blocker) + end +end diff --git a/test/web/activity_pub/transmogrifier/chat_message_test.exs b/test/web/activity_pub/transmogrifier/chat_message_test.exs index d6736dc3e..31274c067 100644 --- a/test/web/activity_pub/transmogrifier/chat_message_test.exs +++ b/test/web/activity_pub/transmogrifier/chat_message_test.exs @@ -124,6 +124,24 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.ChatMessageTest do {:ok, %Activity{} = _activity} = Transmogrifier.handle_incoming(data) end + test "it doesn't work for deactivated users" do + data = + File.read!("test/fixtures/create-chat-message.json") + |> Poison.decode!() + + _author = + insert(:user, + ap_id: data["actor"], + local: false, + last_refreshed_at: DateTime.utc_now(), + deactivated: true + ) + + _recipient = insert(:user, ap_id: List.first(data["to"]), local: true) + + assert {:error, _} = Transmogrifier.handle_incoming(data) + end + test "it inserts it and creates a chat" do data = File.read!("test/fixtures/create-chat-message.json") diff --git a/test/web/activity_pub/transmogrifier/event_handling_test.exs b/test/web/activity_pub/transmogrifier/event_handling_test.exs new file mode 100644 index 000000000..7f1ef2cbd --- /dev/null +++ b/test/web/activity_pub/transmogrifier/event_handling_test.exs @@ -0,0 +1,40 @@ +# 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.EventHandlingTest do + use Oban.Testing, repo: Pleroma.Repo + use Pleroma.DataCase + + alias Pleroma.Object.Fetcher + + test "Mobilizon Event object" do + Tesla.Mock.mock(fn + %{url: "https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39"} -> + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/mobilizon.org-event.json") + } + + %{url: "https://mobilizon.org/@tcit"} -> + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/mobilizon.org-user.json") + } + end) + + assert {:ok, object} = + Fetcher.fetch_object_from_id( + "https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39" + ) + + assert object.data["to"] == ["https://www.w3.org/ns/activitystreams#Public"] + assert object.data["cc"] == [] + + assert object.data["url"] == + "https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39" + + assert object.data["published"] == "2019-12-17T11:33:56Z" + assert object.data["name"] == "Mobilizon Launching Party" + end +end diff --git a/test/web/activity_pub/transmogrifier/follow_handling_test.exs b/test/web/activity_pub/transmogrifier/follow_handling_test.exs index 06c39eed6..757d90941 100644 --- a/test/web/activity_pub/transmogrifier/follow_handling_test.exs +++ b/test/web/activity_pub/transmogrifier/follow_handling_test.exs @@ -160,7 +160,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.FollowHandlingTest do |> Poison.decode!() |> Map.put("object", user.ap_id) - with_mock Pleroma.User, [:passthrough], follow: fn _, _ -> {:error, :testing} end do + with_mock Pleroma.User, [:passthrough], follow: fn _, _, _ -> {:error, :testing} end do {:ok, %Activity{data: %{"id" => id}}} = Transmogrifier.handle_incoming(data) %Activity{} = activity = Activity.get_by_ap_id(id) @@ -185,5 +185,24 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.FollowHandlingTest do assert data["id"] == "https://hubzilla.example.org/channel/kaniini#follows/2" assert User.following?(User.get_cached_by_ap_id(data["actor"]), user) end + + test "it works for incoming follows to locked account" do + pending_follower = insert(:user, ap_id: "http://mastodon.example.org/users/admin") + user = insert(:user, locked: true) + + data = + File.read!("test/fixtures/mastodon-follow-activity.json") + |> Poison.decode!() + |> Map.put("object", user.ap_id) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["type"] == "Follow" + assert data["object"] == user.ap_id + assert data["state"] == "pending" + assert data["actor"] == "http://mastodon.example.org/users/admin" + + assert [^pending_follower] = User.get_follow_requests(user) + end end end diff --git a/test/web/activity_pub/transmogrifier/question_handling_test.exs b/test/web/activity_pub/transmogrifier/question_handling_test.exs new file mode 100644 index 000000000..74ee79543 --- /dev/null +++ b/test/web/activity_pub/transmogrifier/question_handling_test.exs @@ -0,0 +1,176 @@ +# 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.QuestionHandlingTest do + use Pleroma.DataCase + + alias Pleroma.Activity + alias Pleroma.Object + alias Pleroma.Web.ActivityPub.Transmogrifier + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + setup_all do + Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + + test "Mastodon Question activity" do + data = File.read!("test/fixtures/mastodon-question-activity.json") |> Poison.decode!() + + {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) + + object = Object.normalize(activity, false) + + assert object.data["url"] == "https://mastodon.sdf.org/@rinpatch/102070944809637304" + + assert object.data["closed"] == "2019-05-11T09:03:36Z" + + assert object.data["context"] == activity.data["context"] + + assert object.data["context"] == + "tag:mastodon.sdf.org,2019-05-10:objectId=15095122:objectType=Conversation" + + assert object.data["context_id"] + + assert object.data["anyOf"] == [] + + assert Enum.sort(object.data["oneOf"]) == + Enum.sort([ + %{ + "name" => "25 char limit is dumb", + "replies" => %{"totalItems" => 0, "type" => "Collection"}, + "type" => "Note" + }, + %{ + "name" => "Dunno", + "replies" => %{"totalItems" => 0, "type" => "Collection"}, + "type" => "Note" + }, + %{ + "name" => "Everyone knows that!", + "replies" => %{"totalItems" => 1, "type" => "Collection"}, + "type" => "Note" + }, + %{ + "name" => "I can't even fit a funny", + "replies" => %{"totalItems" => 1, "type" => "Collection"}, + "type" => "Note" + } + ]) + + user = insert(:user) + + {:ok, reply_activity} = CommonAPI.post(user, %{status: "hewwo", in_reply_to_id: activity.id}) + + reply_object = Object.normalize(reply_activity, false) + + assert reply_object.data["context"] == object.data["context"] + assert reply_object.data["context_id"] == object.data["context_id"] + end + + test "Mastodon Question activity with HTML tags in plaintext" do + options = [ + %{ + "type" => "Note", + "name" => "<input type=\"date\">", + "replies" => %{"totalItems" => 0, "type" => "Collection"} + }, + %{ + "type" => "Note", + "name" => "<input type=\"date\"/>", + "replies" => %{"totalItems" => 0, "type" => "Collection"} + }, + %{ + "type" => "Note", + "name" => "<input type=\"date\" />", + "replies" => %{"totalItems" => 1, "type" => "Collection"} + }, + %{ + "type" => "Note", + "name" => "<input type=\"date\"></input>", + "replies" => %{"totalItems" => 1, "type" => "Collection"} + } + ] + + data = + File.read!("test/fixtures/mastodon-question-activity.json") + |> Poison.decode!() + |> Kernel.put_in(["object", "oneOf"], options) + + {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) + object = Object.normalize(activity, false) + + assert Enum.sort(object.data["oneOf"]) == Enum.sort(options) + end + + test "Mastodon Question activity with custom emojis" do + options = [ + %{ + "type" => "Note", + "name" => ":blobcat:", + "replies" => %{"totalItems" => 0, "type" => "Collection"} + }, + %{ + "type" => "Note", + "name" => ":blobfox:", + "replies" => %{"totalItems" => 0, "type" => "Collection"} + } + ] + + tag = [ + %{ + "icon" => %{ + "type" => "Image", + "url" => "https://blob.cat/emoji/custom/blobcats/blobcat.png" + }, + "id" => "https://blob.cat/emoji/custom/blobcats/blobcat.png", + "name" => ":blobcat:", + "type" => "Emoji", + "updated" => "1970-01-01T00:00:00Z" + }, + %{ + "icon" => %{"type" => "Image", "url" => "https://blob.cat/emoji/blobfox/blobfox.png"}, + "id" => "https://blob.cat/emoji/blobfox/blobfox.png", + "name" => ":blobfox:", + "type" => "Emoji", + "updated" => "1970-01-01T00:00:00Z" + } + ] + + data = + File.read!("test/fixtures/mastodon-question-activity.json") + |> Poison.decode!() + |> Kernel.put_in(["object", "oneOf"], options) + |> Kernel.put_in(["object", "tag"], tag) + + {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) + object = Object.normalize(activity, false) + + assert object.data["oneOf"] == options + + assert object.data["emoji"] == %{ + "blobcat" => "https://blob.cat/emoji/custom/blobcats/blobcat.png", + "blobfox" => "https://blob.cat/emoji/blobfox/blobfox.png" + } + end + + test "returns an error if received a second time" do + data = File.read!("test/fixtures/mastodon-question-activity.json") |> Poison.decode!() + + assert {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) + + assert {:error, {:validate_object, {:error, _}}} = Transmogrifier.handle_incoming(data) + end + + test "accepts a Question with no content" do + data = + File.read!("test/fixtures/mastodon-question-activity.json") + |> Poison.decode!() + |> Kernel.put_in(["object", "content"], "") + + assert {:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(data) + end +end diff --git a/test/web/activity_pub/transmogrifier/reject_handling_test.exs b/test/web/activity_pub/transmogrifier/reject_handling_test.exs new file mode 100644 index 000000000..7592fbe1c --- /dev/null +++ b/test/web/activity_pub/transmogrifier/reject_handling_test.exs @@ -0,0 +1,67 @@ +# 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.RejectHandlingTest do + use Pleroma.DataCase + + alias Pleroma.Activity + alias Pleroma.User + alias Pleroma.Web.ActivityPub.Transmogrifier + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + test "it fails for incoming rejects which cannot be correlated" do + follower = insert(:user) + followed = insert(:user, locked: true) + + accept_data = + File.read!("test/fixtures/mastodon-reject-activity.json") + |> Poison.decode!() + |> Map.put("actor", followed.ap_id) + + accept_data = + Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id)) + + {:error, _} = Transmogrifier.handle_incoming(accept_data) + + follower = User.get_cached_by_id(follower.id) + + refute User.following?(follower, followed) == true + end + + test "it works for incoming rejects which are referenced by IRI only" do + follower = insert(:user) + followed = insert(:user, locked: true) + + {:ok, follower} = User.follow(follower, followed) + {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed) + + assert User.following?(follower, followed) == true + + reject_data = + File.read!("test/fixtures/mastodon-reject-activity.json") + |> Poison.decode!() + |> Map.put("actor", followed.ap_id) + |> Map.put("object", follow_activity.data["id"]) + + {:ok, %Activity{data: _}} = Transmogrifier.handle_incoming(reject_data) + + follower = User.get_cached_by_id(follower.id) + + assert User.following?(follower, followed) == false + end + + test "it rejects activities without a valid ID" do + user = insert(:user) + + data = + File.read!("test/fixtures/mastodon-follow-activity.json") + |> Poison.decode!() + |> Map.put("object", user.ap_id) + |> Map.put("id", "") + + :error = Transmogrifier.handle_incoming(data) + end +end diff --git a/test/web/activity_pub/transmogrifier/undo_handling_test.exs b/test/web/activity_pub/transmogrifier/undo_handling_test.exs index 01dd6c370..8683f7135 100644 --- a/test/web/activity_pub/transmogrifier/undo_handling_test.exs +++ b/test/web/activity_pub/transmogrifier/undo_handling_test.exs @@ -130,7 +130,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.UndoHandlingTest do "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity" end - test "it works for incomming unfollows with an existing follow" do + test "it works for incoming unfollows with an existing follow" do user = insert(:user) follow_data = diff --git a/test/web/activity_pub/transmogrifier/user_update_handling_test.exs b/test/web/activity_pub/transmogrifier/user_update_handling_test.exs new file mode 100644 index 000000000..64636656c --- /dev/null +++ b/test/web/activity_pub/transmogrifier/user_update_handling_test.exs @@ -0,0 +1,159 @@ +# 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.UserUpdateHandlingTest do + use Pleroma.DataCase + + alias Pleroma.Activity + alias Pleroma.User + alias Pleroma.Web.ActivityPub.Transmogrifier + + import Pleroma.Factory + + test "it works for incoming update activities" do + user = insert(:user, local: false) + + update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!() + + object = + update_data["object"] + |> Map.put("actor", user.ap_id) + |> Map.put("id", user.ap_id) + + update_data = + update_data + |> Map.put("actor", user.ap_id) + |> Map.put("object", object) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(update_data) + + assert data["id"] == update_data["id"] + + user = User.get_cached_by_ap_id(data["actor"]) + assert user.name == "gargle" + + assert user.avatar["url"] == [ + %{ + "href" => + "https://cd.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg" + } + ] + + assert user.banner["url"] == [ + %{ + "href" => + "https://cd.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png" + } + ] + + assert user.bio == "<p>Some bio</p>" + end + + test "it works with alsoKnownAs" do + %{ap_id: actor} = insert(:user, local: false) + + assert User.get_cached_by_ap_id(actor).also_known_as == [] + + {:ok, _activity} = + "test/fixtures/mastodon-update.json" + |> File.read!() + |> Poison.decode!() + |> Map.put("actor", actor) + |> Map.update!("object", fn object -> + object + |> Map.put("actor", actor) + |> Map.put("id", actor) + |> Map.put("alsoKnownAs", [ + "http://mastodon.example.org/users/foo", + "http://example.org/users/bar" + ]) + end) + |> Transmogrifier.handle_incoming() + + assert User.get_cached_by_ap_id(actor).also_known_as == [ + "http://mastodon.example.org/users/foo", + "http://example.org/users/bar" + ] + end + + test "it works with custom profile fields" do + user = insert(:user, local: false) + + assert user.fields == [] + + update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!() + + object = + update_data["object"] + |> Map.put("actor", user.ap_id) + |> Map.put("id", user.ap_id) + + update_data = + update_data + |> Map.put("actor", user.ap_id) + |> Map.put("object", object) + + {:ok, _update_activity} = Transmogrifier.handle_incoming(update_data) + + user = User.get_cached_by_ap_id(user.ap_id) + + assert user.fields == [ + %{"name" => "foo", "value" => "updated"}, + %{"name" => "foo1", "value" => "updated"} + ] + + Pleroma.Config.put([:instance, :max_remote_account_fields], 2) + + update_data = + update_data + |> put_in(["object", "attachment"], [ + %{"name" => "foo", "type" => "PropertyValue", "value" => "bar"}, + %{"name" => "foo11", "type" => "PropertyValue", "value" => "bar11"}, + %{"name" => "foo22", "type" => "PropertyValue", "value" => "bar22"} + ]) + |> Map.put("id", update_data["id"] <> ".") + + {:ok, _} = Transmogrifier.handle_incoming(update_data) + + user = User.get_cached_by_ap_id(user.ap_id) + + assert user.fields == [ + %{"name" => "foo", "value" => "updated"}, + %{"name" => "foo1", "value" => "updated"} + ] + + update_data = + update_data + |> put_in(["object", "attachment"], []) + |> Map.put("id", update_data["id"] <> ".") + + {:ok, _} = Transmogrifier.handle_incoming(update_data) + + user = User.get_cached_by_ap_id(user.ap_id) + + assert user.fields == [] + end + + test "it works for incoming update activities which lock the account" do + user = insert(:user, local: false) + + update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!() + + object = + update_data["object"] + |> Map.put("actor", user.ap_id) + |> Map.put("id", user.ap_id) + |> Map.put("manuallyApprovesFollowers", true) + + update_data = + update_data + |> Map.put("actor", user.ap_id) + |> Map.put("object", object) + + {:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(update_data) + + user = User.get_cached_by_ap_id(user.ap_id) + assert user.locked == true + end +end diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs index 94d8552e8..cc55a7be7 100644 --- a/test/web/activity_pub/transmogrifier_test.exs +++ b/test/web/activity_pub/transmogrifier_test.exs @@ -11,7 +11,6 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do alias Pleroma.Object.Fetcher alias Pleroma.Tests.ObanHelpers alias Pleroma.User - alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.Transmogrifier alias Pleroma.Web.AdminAPI.AccountView alias Pleroma.Web.CommonAPI @@ -106,7 +105,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do object = data["object"] - |> Map.put("inReplyTo", "https://shitposter.club/notice/2827873") + |> Map.put("inReplyTo", "https://mstdn.io/users/mayuutann/statuses/99568293732299394") data = Map.put(data, "object", object) {:ok, returned_activity} = Transmogrifier.handle_incoming(data) @@ -114,10 +113,11 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do assert activity = Activity.get_create_by_object_ap_id( - "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment" + "https://mstdn.io/users/mayuutann/statuses/99568293732299394" ) - assert returned_object.data["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873" + assert returned_object.data["inReplyTo"] == + "https://mstdn.io/users/mayuutann/statuses/99568293732299394" end test "it does not fetch reply-to activities beyond max replies depth limit" do @@ -141,8 +141,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment" ) - assert returned_object.data["inReplyToAtomUri"] == - "https://shitposter.club/notice/2827873" + assert returned_object.data["inReplyTo"] == "https://shitposter.club/notice/2827873" end end @@ -161,7 +160,15 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do assert capture_log(fn -> {:ok, _returned_activity} = Transmogrifier.handle_incoming(data) - end) =~ "[error] Couldn't fetch \"https://404.site/whatever\", error: nil" + end) =~ "[warn] Couldn't fetch \"https://404.site/whatever\", error: nil" + end + + test "it does not work for deactivated users" do + data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!() + + insert(:user, ap_id: data["actor"], deactivated: true) + + assert {:error, _} = Transmogrifier.handle_incoming(data) end test "it works for incoming notices" do @@ -218,84 +225,6 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do assert Enum.at(object.data["tag"], 2) == "moo" end - test "it works for incoming questions" do - data = File.read!("test/fixtures/mastodon-question-activity.json") |> Poison.decode!() - - {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) - - object = Object.normalize(activity) - - assert Enum.all?(object.data["oneOf"], fn choice -> - choice["name"] in [ - "Dunno", - "Everyone knows that!", - "25 char limit is dumb", - "I can't even fit a funny" - ] - end) - end - - test "it works for incoming listens" do - data = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "type" => "Listen", - "id" => "http://mastodon.example.org/users/admin/listens/1234/activity", - "actor" => "http://mastodon.example.org/users/admin", - "object" => %{ - "type" => "Audio", - "id" => "http://mastodon.example.org/users/admin/listens/1234", - "attributedTo" => "http://mastodon.example.org/users/admin", - "title" => "lain radio episode 1", - "artist" => "lain", - "album" => "lain radio", - "length" => 180_000 - } - } - - {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) - - object = Object.normalize(activity) - - assert object.data["title"] == "lain radio episode 1" - assert object.data["artist"] == "lain" - assert object.data["album"] == "lain radio" - assert object.data["length"] == 180_000 - end - - test "it rewrites Note votes to Answers and increments vote counters on question activities" do - user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{ - status: "suya...", - poll: %{options: ["suya", "suya.", "suya.."], expires_in: 10} - }) - - object = Object.normalize(activity) - - data = - File.read!("test/fixtures/mastodon-vote.json") - |> Poison.decode!() - |> Kernel.put_in(["to"], user.ap_id) - |> Kernel.put_in(["object", "inReplyTo"], object.data["id"]) - |> Kernel.put_in(["object", "to"], user.ap_id) - - {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) - answer_object = Object.normalize(activity) - assert answer_object.data["type"] == "Answer" - object = Object.get_by_ap_id(object.data["id"]) - - assert Enum.any?( - object.data["oneOf"], - fn - %{"name" => "suya..", "replies" => %{"totalItems" => 1}} -> true - _ -> false - end - ) - end - test "it works for incoming notices with contentMap" do data = File.read!("test/fixtures/mastodon-post-activity-contentmap.json") |> Poison.decode!() @@ -401,163 +330,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do refute Map.has_key?(object_data, "reaction_count") end - test "it works for incoming update activities" do - data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!() - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!() - - object = - update_data["object"] - |> Map.put("actor", data["actor"]) - |> Map.put("id", data["actor"]) - - update_data = - update_data - |> Map.put("actor", data["actor"]) - |> Map.put("object", object) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(update_data) - - assert data["id"] == update_data["id"] - - user = User.get_cached_by_ap_id(data["actor"]) - assert user.name == "gargle" - - assert user.avatar["url"] == [ - %{ - "href" => - "https://cd.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg" - } - ] - - assert user.banner["url"] == [ - %{ - "href" => - "https://cd.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png" - } - ] - - assert user.bio == "<p>Some bio</p>" - end - - test "it works with alsoKnownAs" do - {:ok, %Activity{data: %{"actor" => actor}}} = - "test/fixtures/mastodon-post-activity.json" - |> File.read!() - |> Poison.decode!() - |> Transmogrifier.handle_incoming() - - assert User.get_cached_by_ap_id(actor).also_known_as == ["http://example.org/users/foo"] - - {:ok, _activity} = - "test/fixtures/mastodon-update.json" - |> File.read!() - |> Poison.decode!() - |> Map.put("actor", actor) - |> Map.update!("object", fn object -> - object - |> Map.put("actor", actor) - |> Map.put("id", actor) - |> Map.put("alsoKnownAs", [ - "http://mastodon.example.org/users/foo", - "http://example.org/users/bar" - ]) - end) - |> Transmogrifier.handle_incoming() - - assert User.get_cached_by_ap_id(actor).also_known_as == [ - "http://mastodon.example.org/users/foo", - "http://example.org/users/bar" - ] - end - - test "it works with custom profile fields" do - {:ok, activity} = - "test/fixtures/mastodon-post-activity.json" - |> File.read!() - |> Poison.decode!() - |> Transmogrifier.handle_incoming() - - user = User.get_cached_by_ap_id(activity.actor) - - assert user.fields == [ - %{"name" => "foo", "value" => "bar"}, - %{"name" => "foo1", "value" => "bar1"} - ] - - update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!() - - object = - update_data["object"] - |> Map.put("actor", user.ap_id) - |> Map.put("id", user.ap_id) - - update_data = - update_data - |> Map.put("actor", user.ap_id) - |> Map.put("object", object) - - {:ok, _update_activity} = Transmogrifier.handle_incoming(update_data) - - user = User.get_cached_by_ap_id(user.ap_id) - - assert user.fields == [ - %{"name" => "foo", "value" => "updated"}, - %{"name" => "foo1", "value" => "updated"} - ] - - Pleroma.Config.put([:instance, :max_remote_account_fields], 2) - - update_data = - put_in(update_data, ["object", "attachment"], [ - %{"name" => "foo", "type" => "PropertyValue", "value" => "bar"}, - %{"name" => "foo11", "type" => "PropertyValue", "value" => "bar11"}, - %{"name" => "foo22", "type" => "PropertyValue", "value" => "bar22"} - ]) - - {:ok, _} = Transmogrifier.handle_incoming(update_data) - - user = User.get_cached_by_ap_id(user.ap_id) - - assert user.fields == [ - %{"name" => "foo", "value" => "updated"}, - %{"name" => "foo1", "value" => "updated"} - ] - - update_data = put_in(update_data, ["object", "attachment"], []) - - {:ok, _} = Transmogrifier.handle_incoming(update_data) - - user = User.get_cached_by_ap_id(user.ap_id) - - assert user.fields == [] - end - - test "it works for incoming update activities which lock the account" do - data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!() - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!() - - object = - update_data["object"] - |> Map.put("actor", data["actor"]) - |> Map.put("id", data["actor"]) - |> Map.put("manuallyApprovesFollowers", true) - - update_data = - update_data - |> Map.put("actor", data["actor"]) - |> Map.put("object", object) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(update_data) - - user = User.get_cached_by_ap_id(data["actor"]) - assert user.locked == true - end - - test "it works for incomming unfollows with an existing follow" do + test "it works for incoming unfollows with an existing follow" do user = insert(:user) follow_data = @@ -582,254 +355,6 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do refute User.following?(User.get_cached_by_ap_id(data["actor"]), user) end - test "it works for incoming follows to locked account" do - pending_follower = insert(:user, ap_id: "http://mastodon.example.org/users/admin") - user = insert(:user, locked: true) - - data = - File.read!("test/fixtures/mastodon-follow-activity.json") - |> Poison.decode!() - |> Map.put("object", user.ap_id) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert data["type"] == "Follow" - assert data["object"] == user.ap_id - assert data["state"] == "pending" - assert data["actor"] == "http://mastodon.example.org/users/admin" - - assert [^pending_follower] = User.get_follow_requests(user) - end - - test "it works for incoming blocks" do - user = insert(:user) - - data = - File.read!("test/fixtures/mastodon-block-activity.json") - |> Poison.decode!() - |> Map.put("object", user.ap_id) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert data["type"] == "Block" - assert data["object"] == user.ap_id - assert data["actor"] == "http://mastodon.example.org/users/admin" - - blocker = User.get_cached_by_ap_id(data["actor"]) - - assert User.blocks?(blocker, user) - end - - test "incoming blocks successfully tear down any follow relationship" do - blocker = insert(:user) - blocked = insert(:user) - - data = - File.read!("test/fixtures/mastodon-block-activity.json") - |> Poison.decode!() - |> Map.put("object", blocked.ap_id) - |> Map.put("actor", blocker.ap_id) - - {:ok, blocker} = User.follow(blocker, blocked) - {:ok, blocked} = User.follow(blocked, blocker) - - assert User.following?(blocker, blocked) - assert User.following?(blocked, blocker) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert data["type"] == "Block" - assert data["object"] == blocked.ap_id - assert data["actor"] == blocker.ap_id - - blocker = User.get_cached_by_ap_id(data["actor"]) - blocked = User.get_cached_by_ap_id(data["object"]) - - assert User.blocks?(blocker, blocked) - - refute User.following?(blocker, blocked) - refute User.following?(blocked, blocker) - end - - test "it works for incoming accepts which were pre-accepted" do - follower = insert(:user) - followed = insert(:user) - - {:ok, follower} = User.follow(follower, followed) - assert User.following?(follower, followed) == true - - {:ok, follow_activity} = ActivityPub.follow(follower, followed) - - accept_data = - File.read!("test/fixtures/mastodon-accept-activity.json") - |> Poison.decode!() - |> Map.put("actor", followed.ap_id) - - object = - accept_data["object"] - |> Map.put("actor", follower.ap_id) - |> Map.put("id", follow_activity.data["id"]) - - accept_data = Map.put(accept_data, "object", object) - - {:ok, activity} = Transmogrifier.handle_incoming(accept_data) - refute activity.local - - assert activity.data["object"] == follow_activity.data["id"] - - assert activity.data["id"] == accept_data["id"] - - follower = User.get_cached_by_id(follower.id) - - assert User.following?(follower, followed) == true - end - - test "it works for incoming accepts which were orphaned" do - follower = insert(:user) - followed = insert(:user, locked: true) - - {:ok, follow_activity} = ActivityPub.follow(follower, followed) - - accept_data = - File.read!("test/fixtures/mastodon-accept-activity.json") - |> Poison.decode!() - |> Map.put("actor", followed.ap_id) - - accept_data = - Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id)) - - {:ok, activity} = Transmogrifier.handle_incoming(accept_data) - assert activity.data["object"] == follow_activity.data["id"] - - follower = User.get_cached_by_id(follower.id) - - assert User.following?(follower, followed) == true - end - - test "it works for incoming accepts which are referenced by IRI only" do - follower = insert(:user) - followed = insert(:user, locked: true) - - {:ok, follow_activity} = ActivityPub.follow(follower, followed) - - accept_data = - File.read!("test/fixtures/mastodon-accept-activity.json") - |> Poison.decode!() - |> Map.put("actor", followed.ap_id) - |> Map.put("object", follow_activity.data["id"]) - - {:ok, activity} = Transmogrifier.handle_incoming(accept_data) - assert activity.data["object"] == follow_activity.data["id"] - - 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 - follower = insert(:user) - followed = insert(:user, locked: true) - - accept_data = - File.read!("test/fixtures/mastodon-accept-activity.json") - |> Poison.decode!() - |> Map.put("actor", followed.ap_id) - - accept_data = - Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id)) - - :error = Transmogrifier.handle_incoming(accept_data) - - follower = User.get_cached_by_id(follower.id) - - refute User.following?(follower, followed) == true - end - - test "it fails for incoming rejects which cannot be correlated" do - follower = insert(:user) - followed = insert(:user, locked: true) - - accept_data = - File.read!("test/fixtures/mastodon-reject-activity.json") - |> Poison.decode!() - |> Map.put("actor", followed.ap_id) - - accept_data = - Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id)) - - :error = Transmogrifier.handle_incoming(accept_data) - - follower = User.get_cached_by_id(follower.id) - - refute User.following?(follower, followed) == true - end - - test "it works for incoming rejects which are orphaned" do - follower = insert(:user) - followed = insert(:user, locked: true) - - {:ok, follower} = User.follow(follower, followed) - {:ok, _follow_activity} = ActivityPub.follow(follower, followed) - - assert User.following?(follower, followed) == true - - reject_data = - File.read!("test/fixtures/mastodon-reject-activity.json") - |> Poison.decode!() - |> Map.put("actor", followed.ap_id) - - reject_data = - Map.put(reject_data, "object", Map.put(reject_data["object"], "actor", follower.ap_id)) - - {:ok, activity} = Transmogrifier.handle_incoming(reject_data) - refute activity.local - assert activity.data["id"] == reject_data["id"] - - follower = User.get_cached_by_id(follower.id) - - assert User.following?(follower, followed) == false - end - - test "it works for incoming rejects which are referenced by IRI only" do - follower = insert(:user) - followed = insert(:user, locked: true) - - {:ok, follower} = User.follow(follower, followed) - {:ok, follow_activity} = ActivityPub.follow(follower, followed) - - assert User.following?(follower, followed) == true - - reject_data = - File.read!("test/fixtures/mastodon-reject-activity.json") - |> Poison.decode!() - |> Map.put("actor", followed.ap_id) - |> Map.put("object", follow_activity.data["id"]) - - {:ok, %Activity{data: _}} = Transmogrifier.handle_incoming(reject_data) - - follower = User.get_cached_by_id(follower.id) - - assert User.following?(follower, followed) == false - end - - test "it rejects activities without a valid ID" do - user = insert(:user) - - data = - File.read!("test/fixtures/mastodon-follow-activity.json") - |> Poison.decode!() - |> Map.put("object", user.ap_id) - |> Map.put("id", "") - - :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" @@ -865,22 +390,46 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3" ) - attachment = %{ - "type" => "Link", - "mediaType" => "video/mp4", - "url" => [ - %{ - "href" => - "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4", - "mediaType" => "video/mp4" - } - ] - } - assert object.data["url"] == "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3" - assert object.data["attachment"] == [attachment] + assert object.data["attachment"] == [ + %{ + "type" => "Link", + "mediaType" => "video/mp4", + "url" => [ + %{ + "href" => + "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4", + "mediaType" => "video/mp4", + "type" => "Link" + } + ] + } + ] + + {:ok, object} = + Fetcher.fetch_object_from_id( + "https://framatube.org/videos/watch/6050732a-8a7a-43d4-a6cd-809525a1d206" + ) + + assert object.data["attachment"] == [ + %{ + "type" => "Link", + "mediaType" => "video/mp4", + "url" => [ + %{ + "href" => + "https://framatube.org/static/webseed/6050732a-8a7a-43d4-a6cd-809525a1d206-1080.mp4", + "mediaType" => "video/mp4", + "type" => "Link" + } + ] + } + ] + + assert object.data["url"] == + "https://framatube.org/videos/watch/6050732a-8a7a-43d4-a6cd-809525a1d206" end test "it accepts Flag activities" do @@ -895,7 +444,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do "id" => activity.data["id"], "content" => "test post", "published" => object.data["published"], - "actor" => AccountView.render("show.json", %{user: user}) + "actor" => AccountView.render("show.json", %{user: user, skip_visibility_check: true}) } message = %{ @@ -959,6 +508,29 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do assert [user.follower_address] == activity.data["to"] end + test "it correctly processes messages with weirdness in address fields" do + user = insert(:user) + + message = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "to" => [nil, user.follower_address], + "cc" => ["https://www.w3.org/ns/activitystreams#Public", ["¿"]], + "type" => "Create", + "object" => %{ + "content" => "…", + "type" => "Note", + "attributedTo" => user.ap_id, + "inReplyTo" => nil + }, + "actor" => user.ap_id + } + + assert {:ok, activity} = Transmogrifier.handle_incoming(message) + + assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["cc"] + assert [user.follower_address] == activity.data["to"] + end + test "it accepts Move activities" do old_user = insert(:user) new_user = insert(:user) @@ -1423,30 +995,6 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do end end - test "Rewrites Answers to Notes" do - user = insert(:user) - - {:ok, poll_activity} = - CommonAPI.post(user, %{ - status: "suya...", - poll: %{options: ["suya", "suya.", "suya.."], expires_in: 10} - }) - - poll_object = Object.normalize(poll_activity) - # TODO: Replace with CommonAPI vote creation when implemented - data = - File.read!("test/fixtures/mastodon-vote.json") - |> Poison.decode!() - |> Kernel.put_in(["to"], user.ap_id) - |> Kernel.put_in(["object", "inReplyTo"], poll_object.data["id"]) - |> Kernel.put_in(["object", "to"], user.ap_id) - - {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) - {:ok, data} = Transmogrifier.prepare_outgoing(activity.data) - - assert data["object"]["type"] == "Note" - end - describe "fix_explicit_addressing" do setup do user = insert(:user) @@ -1524,7 +1072,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do assert Transmogrifier.fix_in_reply_to(data) == data end - test "returns object with inReplyToAtomUri when denied incoming reply", %{data: data} do + test "returns object with inReplyTo when denied incoming reply", %{data: data} do Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 0) object_with_reply = @@ -1532,26 +1080,22 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do modified_object = Transmogrifier.fix_in_reply_to(object_with_reply) assert modified_object["inReplyTo"] == "https://shitposter.club/notice/2827873" - assert modified_object["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873" object_with_reply = Map.put(data["object"], "inReplyTo", %{"id" => "https://shitposter.club/notice/2827873"}) modified_object = Transmogrifier.fix_in_reply_to(object_with_reply) assert modified_object["inReplyTo"] == %{"id" => "https://shitposter.club/notice/2827873"} - assert modified_object["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873" object_with_reply = Map.put(data["object"], "inReplyTo", ["https://shitposter.club/notice/2827873"]) modified_object = Transmogrifier.fix_in_reply_to(object_with_reply) assert modified_object["inReplyTo"] == ["https://shitposter.club/notice/2827873"] - assert modified_object["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873" object_with_reply = Map.put(data["object"], "inReplyTo", []) modified_object = Transmogrifier.fix_in_reply_to(object_with_reply) assert modified_object["inReplyTo"] == [] - assert modified_object["inReplyToAtomUri"] == "" end @tag capture_log: true @@ -1560,22 +1104,17 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do Map.put( data["object"], "inReplyTo", - "https://shitposter.club/notice/2827873" + "https://mstdn.io/users/mayuutann/statuses/99568293732299394" ) Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 5) modified_object = Transmogrifier.fix_in_reply_to(object_with_reply) assert modified_object["inReplyTo"] == - "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment" - - assert modified_object["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873" - - assert modified_object["conversation"] == - "tag:shitposter.club,2017-05-05:objectType=thread:nonce=3c16e9c2681f6d26" + "https://mstdn.io/users/mayuutann/statuses/99568293732299394" assert modified_object["context"] == - "tag:shitposter.club,2017-05-05:objectType=thread:nonce=3c16e9c2681f6d26" + "tag:shitposter.club,2018-02-22:objectType=thread:nonce=e5a7c72d60a9c0e4" end end @@ -1677,7 +1216,9 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do @tag capture_log: true test "returns {:ok, %Object{}} for success case" do assert {:ok, %Object{}} = - Transmogrifier.get_obj_helper("https://shitposter.club/notice/2827873") + Transmogrifier.get_obj_helper( + "https://mstdn.io/users/mayuutann/statuses/99568293732299394" + ) end end @@ -1697,8 +1238,13 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do "attachment" => [ %{ "mediaType" => "video/mp4", + "type" => "Document", "url" => [ - %{"href" => "https://peertube.moe/stat-480.mp4", "mediaType" => "video/mp4"} + %{ + "href" => "https://peertube.moe/stat-480.mp4", + "mediaType" => "video/mp4", + "type" => "Link" + } ] } ] @@ -1715,14 +1261,24 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do "attachment" => [ %{ "mediaType" => "video/mp4", + "type" => "Document", "url" => [ - %{"href" => "https://pe.er/stat-480.mp4", "mediaType" => "video/mp4"} + %{ + "href" => "https://pe.er/stat-480.mp4", + "mediaType" => "video/mp4", + "type" => "Link" + } ] }, %{ "mediaType" => "video/mp4", + "type" => "Document", "url" => [ - %{"href" => "https://pe.er/stat-480.mp4", "mediaType" => "video/mp4"} + %{ + "href" => "https://pe.er/stat-480.mp4", + "mediaType" => "video/mp4", + "type" => "Link" + } ] } ] diff --git a/test/web/activity_pub/utils_test.exs b/test/web/activity_pub/utils_test.exs index 15f03f193..d50213545 100644 --- a/test/web/activity_pub/utils_test.exs +++ b/test/web/activity_pub/utils_test.exs @@ -8,7 +8,6 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do alias Pleroma.Object alias Pleroma.Repo alias Pleroma.User - alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.AdminAPI.AccountView alias Pleroma.Web.CommonAPI @@ -27,16 +26,6 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do end end - describe "fetch the latest Block" do - test "fetches the latest Block activity" do - blocker = insert(:user) - blocked = insert(:user) - {:ok, activity} = ActivityPub.block(blocker, blocked) - - assert activity == Utils.fetch_latest_block(blocker, blocked) - end - end - describe "determine_explicit_mentions()" do test "works with an object that has mentions" do object = %{ @@ -207,8 +196,8 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do user = insert(:user, locked: true) follower = insert(:user) - {:ok, follow_activity} = ActivityPub.follow(follower, user) - {:ok, follow_activity_two} = ActivityPub.follow(follower, user) + {:ok, _, _, follow_activity} = CommonAPI.follow(follower, user) + {:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user) data = follow_activity_two.data @@ -231,8 +220,8 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do user = insert(:user, locked: true) follower = insert(:user) - {:ok, follow_activity} = ActivityPub.follow(follower, user) - {:ok, follow_activity_two} = ActivityPub.follow(follower, user) + {:ok, _, _, follow_activity} = CommonAPI.follow(follower, user) + {:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user) data = follow_activity_two.data @@ -344,9 +333,9 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do user1 = insert(:user) user2 = insert(:user) - assert {:ok, %Activity{} = _} = ActivityPub.block(user1, user2) - assert {:ok, %Activity{} = _} = ActivityPub.block(user1, user2) - assert {:ok, %Activity{} = activity} = ActivityPub.block(user1, user2) + assert {:ok, %Activity{} = _} = CommonAPI.block(user1, user2) + assert {:ok, %Activity{} = _} = CommonAPI.block(user1, user2) + assert {:ok, %Activity{} = activity} = CommonAPI.block(user1, user2) assert Utils.fetch_latest_block(user1, user2) == activity end @@ -493,7 +482,8 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do "id" => activity_ap_id, "content" => content, "published" => activity.object.data["published"], - "actor" => AccountView.render("show.json", %{user: target_account}) + "actor" => + AccountView.render("show.json", %{user: target_account, skip_visibility_check: true}) } assert %{ diff --git a/test/web/activity_pub/views/user_view_test.exs b/test/web/activity_pub/views/user_view_test.exs index bec15a996..98c7c9d09 100644 --- a/test/web/activity_pub/views/user_view_test.exs +++ b/test/web/activity_pub/views/user_view_test.exs @@ -158,4 +158,23 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do assert %{"totalItems" => 1} = UserView.render("following.json", %{user: user}) end end + + describe "acceptsChatMessages" do + test "it returns this value if it is set" do + true_user = insert(:user, accepts_chat_messages: true) + false_user = insert(:user, accepts_chat_messages: false) + nil_user = insert(:user, accepts_chat_messages: nil) + + assert %{"capabilities" => %{"acceptsChatMessages" => true}} = + UserView.render("user.json", user: true_user) + + assert %{"capabilities" => %{"acceptsChatMessages" => false}} = + UserView.render("user.json", user: false_user) + + refute Map.has_key?( + UserView.render("user.json", user: nil_user)["capabilities"], + "acceptsChatMessages" + ) + end + end end diff --git a/test/web/admin_api/controllers/admin_api_controller_test.exs b/test/web/admin_api/controllers/admin_api_controller_test.exs index e3d3ccb8d..3bc88c6a9 100644 --- a/test/web/admin_api/controllers/admin_api_controller_test.exs +++ b/test/web/admin_api/controllers/admin_api_controller_test.exs @@ -9,6 +9,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do import ExUnit.CaptureLog import Mock import Pleroma.Factory + import Swoosh.TestAssertions alias Pleroma.Activity alias Pleroma.Config @@ -41,6 +42,16 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do {:ok, %{admin: admin, token: token, conn: conn}} end + test "with valid `admin_token` query parameter, skips OAuth scopes check" do + clear_config([:admin_token], "password123") + + user = insert(:user) + + conn = get(build_conn(), "/api/pleroma/admin/users/#{user.nickname}?admin_token=password123") + + assert json_response(conn, 200) + end + describe "with [:auth, :enforce_oauth_admin_scope_usage]," do setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], true) @@ -144,11 +155,30 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do describe "DELETE /api/pleroma/admin/users" do test "single user", %{admin: admin, conn: conn} do - user = insert(:user) clear_config([:instance, :federating], true) + user = + insert(:user, + avatar: %{"url" => [%{"href" => "https://someurl"}]}, + banner: %{"url" => [%{"href" => "https://somebanner"}]}, + bio: "Hello world!", + name: "A guy" + ) + + # Create some activities to check they got deleted later + follower = insert(:user) + {:ok, _} = CommonAPI.post(user, %{status: "test"}) + {:ok, _, _, _} = CommonAPI.follow(user, follower) + {:ok, _, _, _} = CommonAPI.follow(follower, user) + user = Repo.get(User, user.id) + assert user.note_count == 1 + assert user.follower_count == 1 + assert user.following_count == 1 + refute user.deactivated + with_mock Pleroma.Web.Federator, - publish: fn _ -> nil end do + publish: fn _ -> nil end, + perform: fn _, _ -> nil end do conn = conn |> put_req_header("accept", "application/json") @@ -165,6 +195,17 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do assert json_response(conn, 200) == [user.nickname] + user = Repo.get(User, user.id) + assert user.deactivated + + assert user.avatar == %{} + assert user.banner == %{} + assert user.note_count == 0 + assert user.follower_count == 0 + assert user.following_count == 0 + assert user.bio == "" + assert user.name == nil + assert called(Pleroma.Web.Federator.publish(:_)) end end @@ -338,7 +379,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do "avatar" => User.avatar_url(user) |> MediaProxy.url(), "display_name" => HTML.strip_tags(user.name || user.nickname), "confirmation_pending" => false, - "url" => user.ap_id + "approval_pending" => false, + "url" => user.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" } assert expected == json_response(conn, 200) @@ -426,7 +470,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do user1: user1, user2: user2 } do - assert json_response(conn, :no_content) + assert empty_json_response(conn) assert User.get_cached_by_id(user1.id).tags == ["x", "foo", "bar"] assert User.get_cached_by_id(user2.id).tags == ["y", "foo", "bar"] @@ -444,7 +488,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do end test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do - assert json_response(conn, :no_content) + assert empty_json_response(conn) assert User.get_cached_by_id(user3.id).tags == ["unchanged"] end end @@ -472,7 +516,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do user1: user1, user2: user2 } do - assert json_response(conn, :no_content) + assert empty_json_response(conn) assert User.get_cached_by_id(user1.id).tags == [] assert User.get_cached_by_id(user2.id).tags == ["y"] @@ -490,7 +534,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do end test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do - assert json_response(conn, :no_content) + assert empty_json_response(conn) assert User.get_cached_by_id(user3.id).tags == ["unchanged"] end end @@ -602,6 +646,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do describe "GET /api/pleroma/admin/users" do test "renders users array for the first page", %{conn: conn, admin: admin} do user = insert(:user, local: false, tags: ["foo", "bar"]) + user2 = insert(:user, approval_pending: true, registration_reason: "I'm a chill dude") + conn = get(conn, "/api/pleroma/admin/users?page=1") users = @@ -616,7 +662,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do "avatar" => User.avatar_url(admin) |> MediaProxy.url(), "display_name" => HTML.strip_tags(admin.name || admin.nickname), "confirmation_pending" => false, - "url" => admin.ap_id + "approval_pending" => false, + "url" => admin.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" }, %{ "deactivated" => user.deactivated, @@ -628,13 +677,31 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do "avatar" => User.avatar_url(user) |> MediaProxy.url(), "display_name" => HTML.strip_tags(user.name || user.nickname), "confirmation_pending" => false, - "url" => user.ap_id + "approval_pending" => false, + "url" => user.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" + }, + %{ + "deactivated" => user2.deactivated, + "id" => user2.id, + "nickname" => user2.nickname, + "roles" => %{"admin" => false, "moderator" => false}, + "local" => true, + "tags" => [], + "avatar" => User.avatar_url(user2) |> MediaProxy.url(), + "display_name" => HTML.strip_tags(user2.name || user2.nickname), + "confirmation_pending" => false, + "approval_pending" => true, + "url" => user2.ap_id, + "registration_reason" => "I'm a chill dude", + "actor_type" => "Person" } ] |> Enum.sort_by(& &1["nickname"]) assert json_response(conn, 200) == %{ - "count" => 2, + "count" => 3, "page_size" => 50, "users" => users } @@ -701,7 +768,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do "avatar" => User.avatar_url(user) |> MediaProxy.url(), "display_name" => HTML.strip_tags(user.name || user.nickname), "confirmation_pending" => false, - "url" => user.ap_id + "approval_pending" => false, + "url" => user.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" } ] } @@ -727,7 +797,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do "avatar" => User.avatar_url(user) |> MediaProxy.url(), "display_name" => HTML.strip_tags(user.name || user.nickname), "confirmation_pending" => false, - "url" => user.ap_id + "approval_pending" => false, + "url" => user.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" } ] } @@ -753,7 +826,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do "avatar" => User.avatar_url(user) |> MediaProxy.url(), "display_name" => HTML.strip_tags(user.name || user.nickname), "confirmation_pending" => false, - "url" => user.ap_id + "approval_pending" => false, + "url" => user.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" } ] } @@ -779,7 +855,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do "avatar" => User.avatar_url(user) |> MediaProxy.url(), "display_name" => HTML.strip_tags(user.name || user.nickname), "confirmation_pending" => false, - "url" => user.ap_id + "approval_pending" => false, + "url" => user.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" } ] } @@ -805,7 +884,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do "avatar" => User.avatar_url(user) |> MediaProxy.url(), "display_name" => HTML.strip_tags(user.name || user.nickname), "confirmation_pending" => false, - "url" => user.ap_id + "approval_pending" => false, + "url" => user.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" } ] } @@ -831,7 +913,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do "avatar" => User.avatar_url(user) |> MediaProxy.url(), "display_name" => HTML.strip_tags(user.name || user.nickname), "confirmation_pending" => false, - "url" => user.ap_id + "approval_pending" => false, + "url" => user.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" } ] } @@ -852,7 +937,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do "avatar" => User.avatar_url(user2) |> MediaProxy.url(), "display_name" => HTML.strip_tags(user2.name || user2.nickname), "confirmation_pending" => false, - "url" => user2.ap_id + "approval_pending" => false, + "url" => user2.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" } ] } @@ -885,7 +973,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do "avatar" => User.avatar_url(user) |> MediaProxy.url(), "display_name" => HTML.strip_tags(user.name || user.nickname), "confirmation_pending" => false, - "url" => user.ap_id + "approval_pending" => false, + "url" => user.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" } ] } @@ -911,7 +1002,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do "avatar" => User.avatar_url(user) |> MediaProxy.url(), "display_name" => HTML.strip_tags(user.name || user.nickname), "confirmation_pending" => false, - "url" => user.ap_id + "approval_pending" => false, + "url" => user.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" }, %{ "deactivated" => admin.deactivated, @@ -923,7 +1017,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do "avatar" => User.avatar_url(admin) |> MediaProxy.url(), "display_name" => HTML.strip_tags(admin.name || admin.nickname), "confirmation_pending" => false, - "url" => admin.ap_id + "approval_pending" => false, + "url" => admin.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" }, %{ "deactivated" => false, @@ -935,7 +1032,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do "avatar" => User.avatar_url(old_admin) |> MediaProxy.url(), "display_name" => HTML.strip_tags(old_admin.name || old_admin.nickname), "confirmation_pending" => false, - "url" => old_admin.ap_id + "approval_pending" => false, + "url" => old_admin.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" } ] |> Enum.sort_by(& &1["nickname"]) @@ -947,6 +1047,45 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do } end + test "only unapproved users", %{conn: conn} do + user = + insert(:user, + nickname: "sadboy", + approval_pending: true, + registration_reason: "Plz let me in!" + ) + + insert(:user, nickname: "happyboy", approval_pending: false) + + conn = get(conn, "/api/pleroma/admin/users?filters=need_approval") + + users = + [ + %{ + "deactivated" => user.deactivated, + "id" => user.id, + "nickname" => user.nickname, + "roles" => %{"admin" => false, "moderator" => false}, + "local" => true, + "tags" => [], + "avatar" => User.avatar_url(user) |> MediaProxy.url(), + "display_name" => HTML.strip_tags(user.name || user.nickname), + "confirmation_pending" => false, + "approval_pending" => true, + "url" => user.ap_id, + "registration_reason" => "Plz let me in!", + "actor_type" => "Person" + } + ] + |> Enum.sort_by(& &1["nickname"]) + + assert json_response(conn, 200) == %{ + "count" => 1, + "page_size" => 50, + "users" => users + } + end + test "load only admins", %{conn: conn, admin: admin} do second_admin = insert(:user, is_admin: true) insert(:user) @@ -966,7 +1105,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do "avatar" => User.avatar_url(admin) |> MediaProxy.url(), "display_name" => HTML.strip_tags(admin.name || admin.nickname), "confirmation_pending" => false, - "url" => admin.ap_id + "approval_pending" => false, + "url" => admin.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" }, %{ "deactivated" => false, @@ -978,7 +1120,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do "avatar" => User.avatar_url(second_admin) |> MediaProxy.url(), "display_name" => HTML.strip_tags(second_admin.name || second_admin.nickname), "confirmation_pending" => false, - "url" => second_admin.ap_id + "approval_pending" => false, + "url" => second_admin.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" } ] |> Enum.sort_by(& &1["nickname"]) @@ -1011,7 +1156,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do "avatar" => User.avatar_url(moderator) |> MediaProxy.url(), "display_name" => HTML.strip_tags(moderator.name || moderator.nickname), "confirmation_pending" => false, - "url" => moderator.ap_id + "approval_pending" => false, + "url" => moderator.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" } ] } @@ -1037,7 +1185,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do "avatar" => User.avatar_url(user1) |> MediaProxy.url(), "display_name" => HTML.strip_tags(user1.name || user1.nickname), "confirmation_pending" => false, - "url" => user1.ap_id + "approval_pending" => false, + "url" => user1.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" }, %{ "deactivated" => false, @@ -1049,7 +1200,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do "avatar" => User.avatar_url(user2) |> MediaProxy.url(), "display_name" => HTML.strip_tags(user2.name || user2.nickname), "confirmation_pending" => false, - "url" => user2.ap_id + "approval_pending" => false, + "url" => user2.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" } ] |> Enum.sort_by(& &1["nickname"]) @@ -1061,6 +1215,27 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do } end + test "`active` filters out users pending approval", %{token: token} do + insert(:user, approval_pending: true) + %{id: user_id} = insert(:user, approval_pending: false) + %{id: admin_id} = token.user + + conn = + build_conn() + |> assign(:user, token.user) + |> assign(:token, token) + |> get("/api/pleroma/admin/users?filters=active") + + assert %{ + "count" => 2, + "page_size" => 50, + "users" => [ + %{"id" => ^admin_id}, + %{"id" => ^user_id} + ] + } = json_response(conn, 200) + end + test "it works with multiple filters" do admin = insert(:user, nickname: "john", is_admin: true) token = insert(:oauth_admin_token, user: admin) @@ -1089,7 +1264,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do "avatar" => User.avatar_url(user) |> MediaProxy.url(), "display_name" => HTML.strip_tags(user.name || user.nickname), "confirmation_pending" => false, - "url" => user.ap_id + "approval_pending" => false, + "url" => user.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" } ] } @@ -1114,7 +1292,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do "avatar" => User.avatar_url(admin) |> MediaProxy.url(), "display_name" => HTML.strip_tags(admin.name || admin.nickname), "confirmation_pending" => false, - "url" => admin.ap_id + "approval_pending" => false, + "url" => admin.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" } ] } @@ -1161,6 +1342,26 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do "@#{admin.nickname} deactivated users: @#{user_one.nickname}, @#{user_two.nickname}" end + test "PATCH /api/pleroma/admin/users/approve", %{admin: admin, conn: conn} do + user_one = insert(:user, approval_pending: true) + user_two = insert(:user, approval_pending: true) + + conn = + patch( + conn, + "/api/pleroma/admin/users/approve", + %{nicknames: [user_one.nickname, user_two.nickname]} + ) + + response = json_response(conn, 200) + assert Enum.map(response["users"], & &1["approval_pending"]) == [false, false] + + log_entry = Repo.one(ModerationLog) + + assert ModerationLog.get_log_entry_message(log_entry) == + "@#{admin.nickname} approved users: @#{user_one.nickname}, @#{user_two.nickname}" + end + test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation", %{admin: admin, conn: conn} do user = insert(:user) @@ -1177,7 +1378,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do "avatar" => User.avatar_url(user) |> MediaProxy.url(), "display_name" => HTML.strip_tags(user.name || user.nickname), "confirmation_pending" => false, - "url" => user.ap_id + "approval_pending" => false, + "url" => user.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" } log_entry = Repo.one(ModerationLog) @@ -1514,6 +1718,15 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do end end + test "gets a remote users when [:instance, :limit_to_local_content] is set to :unauthenticated", + %{conn: conn} do + clear_config(Pleroma.Config.get([:instance, :limit_to_local_content]), :unauthenticated) + user = insert(:user, %{local: false, nickname: "u@peer1.com"}) + conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials") + + assert json_response(conn, 200) + end + describe "GET /users/:nickname/credentials" do test "gets the user credentials", %{conn: conn} do user = insert(:user) @@ -1599,14 +1812,14 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do assert patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{ "actor_type" => "Application" }) - |> json_response(200) == %{"errors" => %{"actor_type" => "is invalid"}} + |> json_response(400) == %{"errors" => %{"actor_type" => "is invalid"}} end test "update non existing user", %{conn: conn} do assert patch(conn, "/api/pleroma/admin/users/non-existing/credentials", %{ "password" => "new_password" }) - |> json_response(200) == %{"error" => "Unable to update user."} + |> json_response(404) == %{"error" => "Not found"} end end @@ -1618,7 +1831,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do conn = patch(conn, "/api/pleroma/admin/users/force_password_reset", %{nicknames: [user.nickname]}) - assert json_response(conn, 204) == "" + assert empty_json_response(conn) == "" ObanHelpers.perform_all() @@ -1712,6 +1925,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do "@#{admin.nickname} re-sent confirmation email for users: @#{first_user.nickname}, @#{ second_user.nickname }" + + ObanHelpers.perform_all() + assert_email_sent(Pleroma.Emails.UserEmail.account_confirmation_email(first_user)) end end @@ -1732,6 +1948,26 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do assert %{"direct" => 0, "private" => 0, "public" => 1, "unlisted" => 2} = response["status_visibility"] end + + test "by instance", %{conn: conn} do + admin = insert(:user, is_admin: true) + user1 = insert(:user) + instance2 = "instance2.tld" + user2 = insert(:user, %{ap_id: "https://#{instance2}/@actor"}) + + CommonAPI.post(user1, %{visibility: "public", status: "hey"}) + CommonAPI.post(user2, %{visibility: "unlisted", status: "hey"}) + CommonAPI.post(user2, %{visibility: "private", status: "hey"}) + + response = + conn + |> assign(:user, admin) + |> get("/api/pleroma/admin/stats", instance: instance2) + |> json_response(200) + + assert %{"direct" => 0, "private" => 1, "public" => 0, "unlisted" => 1} = + response["status_visibility"] + end end end diff --git a/test/web/admin_api/controllers/config_controller_test.exs b/test/web/admin_api/controllers/config_controller_test.exs index 780de8d18..4e897455f 100644 --- a/test/web/admin_api/controllers/config_controller_test.exs +++ b/test/web/admin_api/controllers/config_controller_test.exs @@ -57,12 +57,12 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do ] } = json_response_and_validate_schema(conn, 200) - assert key1 == config1.key - assert key2 == config2.key + assert key1 == inspect(config1.key) + assert key2 == inspect(config2.key) end test "db is added to settings that are in db", %{conn: conn} do - _config = insert(:config, key: ":instance", value: ConfigDB.to_binary(name: "Some name")) + _config = insert(:config, key: ":instance", value: [name: "Some name"]) %{"configs" => configs} = conn @@ -83,7 +83,7 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do config3 = insert(:config, - value: ConfigDB.to_binary(k1: :v1, k2: :v2) + value: [k1: :v1, k2: :v2] ) %{"configs" => configs} = @@ -93,42 +93,45 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do assert length(configs) > 3 + saved_configs = [config1, config2, config3] + keys = Enum.map(saved_configs, &inspect(&1.key)) + received_configs = Enum.filter(configs, fn %{"group" => group, "key" => key} -> - group == ":pleroma" and key in [config1.key, config2.key, config3.key] + group == ":pleroma" and key in keys end) assert length(received_configs) == 3 db_keys = config3.value - |> ConfigDB.from_binary() |> Keyword.keys() - |> ConfigDB.convert() + |> ConfigDB.to_json_types() + + keys = Enum.map(saved_configs -- [config3], &inspect(&1.key)) + + values = Enum.map(saved_configs, &ConfigDB.to_json_types(&1.value)) + + mapset_keys = MapSet.new(keys ++ db_keys) Enum.each(received_configs, fn %{"value" => value, "db" => db} -> - assert db in [[config1.key], [config2.key], db_keys] + db = MapSet.new(db) + assert MapSet.subset?(db, mapset_keys) - assert value in [ - ConfigDB.from_binary_with_convert(config1.value), - ConfigDB.from_binary_with_convert(config2.value), - ConfigDB.from_binary_with_convert(config3.value) - ] + assert value in values end) end test "subkeys with full update right merge", %{conn: conn} do - config1 = - insert(:config, - key: ":emoji", - value: ConfigDB.to_binary(groups: [a: 1, b: 2], key: [a: 1]) - ) + insert(:config, + key: ":emoji", + value: [groups: [a: 1, b: 2], key: [a: 1]] + ) - config2 = - insert(:config, - key: ":assets", - value: ConfigDB.to_binary(mascots: [a: 1, b: 2], key: [a: 1]) - ) + insert(:config, + key: ":assets", + value: [mascots: [a: 1, b: 2], key: [a: 1]] + ) %{"configs" => configs} = conn @@ -137,18 +140,26 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do vals = Enum.filter(configs, fn %{"group" => group, "key" => key} -> - group == ":pleroma" and key in [config1.key, config2.key] + group == ":pleroma" and key in [":emoji", ":assets"] end) emoji = Enum.find(vals, fn %{"key" => key} -> key == ":emoji" end) assets = Enum.find(vals, fn %{"key" => key} -> key == ":assets" end) - emoji_val = ConfigDB.transform_with_out_binary(emoji["value"]) - assets_val = ConfigDB.transform_with_out_binary(assets["value"]) + emoji_val = ConfigDB.to_elixir_types(emoji["value"]) + assets_val = ConfigDB.to_elixir_types(assets["value"]) assert emoji_val[:groups] == [a: 1, b: 2] assert assets_val[:mascots] == [a: 1, b: 2] end + + test "with valid `admin_token` query parameter, skips OAuth scopes check" do + clear_config([:admin_token], "password123") + + build_conn() + |> get("/api/pleroma/admin/config?admin_token=password123") + |> json_response_and_validate_schema(200) + end end test "POST /api/pleroma/admin/config error", %{conn: conn} do @@ -277,7 +288,8 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do "value" => %{"tuple" => ["string", "Pleroma.Captcha.NotReal", []]}, "db" => [":key5"] } - ] + ], + "need_reboot" => false } assert Application.get_env(:pleroma, :key1) == "value1" @@ -357,7 +369,8 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do "value" => "https://hooks.slack.com/services/KEY", "db" => [":webhook_url"] } - ] + ], + "need_reboot" => false } assert Application.get_env(:quack, :level) == :info @@ -366,14 +379,14 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do end test "saving config with partial update", %{conn: conn} do - config = insert(:config, key: ":key1", value: :erlang.term_to_binary(key1: 1, key2: 2)) + insert(:config, key: ":key1", value: :erlang.term_to_binary(key1: 1, key2: 2)) conn = conn |> put_req_header("content-type", "application/json") |> post("/api/pleroma/admin/config", %{ configs: [ - %{group: config.group, key: config.key, value: [%{"tuple" => [":key3", 3]}]} + %{group: ":pleroma", key: ":key1", value: [%{"tuple" => [":key3", 3]}]} ] }) @@ -389,7 +402,8 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do ], "db" => [":key1", ":key2", ":key3"] } - ] + ], + "need_reboot" => false } end @@ -500,8 +514,7 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do end test "saving config with nested merge", %{conn: conn} do - config = - insert(:config, key: ":key1", value: :erlang.term_to_binary(key1: 1, key2: [k1: 1, k2: 2])) + insert(:config, key: :key1, value: [key1: 1, key2: [k1: 1, k2: 2]]) conn = conn @@ -509,8 +522,8 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do |> post("/api/pleroma/admin/config", %{ configs: [ %{ - group: config.group, - key: config.key, + group: ":pleroma", + key: ":key1", value: [ %{"tuple" => [":key3", 3]}, %{ @@ -548,7 +561,8 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do ], "db" => [":key1", ":key3", ":key2"] } - ] + ], + "need_reboot" => false } end @@ -588,7 +602,8 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do ], "db" => [":ssl_options"] } - ] + ], + "need_reboot" => false } assert Application.get_env(:pleroma, :key1) == [ @@ -600,12 +615,11 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do backends = Application.get_env(:logger, :backends) on_exit(fn -> Application.put_env(:logger, :backends, backends) end) - config = - insert(:config, - group: ":logger", - key: ":backends", - value: :erlang.term_to_binary([]) - ) + insert(:config, + group: :logger, + key: :backends, + value: [] + ) Pleroma.Config.TransferTask.load_and_update_env([], false) @@ -617,8 +631,8 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do |> post("/api/pleroma/admin/config", %{ configs: [ %{ - group: config.group, - key: config.key, + group: ":logger", + key: ":backends", value: [":console"] } ] @@ -634,7 +648,8 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do ], "db" => [":backends"] } - ] + ], + "need_reboot" => false } assert Application.get_env(:logger, :backends) == [ @@ -643,19 +658,18 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do end test "saving full setting if value is not keyword", %{conn: conn} do - config = - insert(:config, - group: ":tesla", - key: ":adapter", - value: :erlang.term_to_binary(Tesla.Adapter.Hackey) - ) + insert(:config, + group: :tesla, + key: :adapter, + value: Tesla.Adapter.Hackey + ) conn = conn |> put_req_header("content-type", "application/json") |> post("/api/pleroma/admin/config", %{ configs: [ - %{group: config.group, key: config.key, value: "Tesla.Adapter.Httpc"} + %{group: ":tesla", key: ":adapter", value: "Tesla.Adapter.Httpc"} ] }) @@ -667,7 +681,8 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do "value" => "Tesla.Adapter.Httpc", "db" => [":adapter"] } - ] + ], + "need_reboot" => false } end @@ -677,13 +692,13 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do token: token } do ueberauth = Application.get_env(:ueberauth, Ueberauth) - config1 = insert(:config, key: ":keyaa1") - config2 = insert(:config, key: ":keyaa2") + insert(:config, key: :keyaa1) + insert(:config, key: :keyaa2) config3 = insert(:config, - group: ":ueberauth", - key: "Ueberauth" + group: :ueberauth, + key: Ueberauth ) conn = @@ -691,8 +706,8 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do |> put_req_header("content-type", "application/json") |> post("/api/pleroma/admin/config", %{ configs: [ - %{group: config1.group, key: config1.key, value: "another_value"}, - %{group: config2.group, key: config2.key, value: "another_value"} + %{group: ":pleroma", key: ":keyaa1", value: "another_value"}, + %{group: ":pleroma", key: ":keyaa2", value: "another_value"} ] }) @@ -700,22 +715,23 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do "configs" => [ %{ "group" => ":pleroma", - "key" => config1.key, + "key" => ":keyaa1", "value" => "another_value", "db" => [":keyaa1"] }, %{ "group" => ":pleroma", - "key" => config2.key, + "key" => ":keyaa2", "value" => "another_value", "db" => [":keyaa2"] } - ] + ], + "need_reboot" => false } assert Application.get_env(:pleroma, :keyaa1) == "another_value" assert Application.get_env(:pleroma, :keyaa2) == "another_value" - assert Application.get_env(:ueberauth, Ueberauth) == ConfigDB.from_binary(config3.value) + assert Application.get_env(:ueberauth, Ueberauth) == config3.value conn = build_conn() @@ -724,7 +740,7 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do |> put_req_header("content-type", "application/json") |> post("/api/pleroma/admin/config", %{ configs: [ - %{group: config2.group, key: config2.key, delete: true}, + %{group: ":pleroma", key: ":keyaa2", delete: true}, %{ group: ":ueberauth", key: "Ueberauth", @@ -734,7 +750,8 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do }) assert json_response_and_validate_schema(conn, 200) == %{ - "configs" => [] + "configs" => [], + "need_reboot" => false } assert Application.get_env(:ueberauth, Ueberauth) == ueberauth @@ -801,7 +818,8 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do ":name" ] } - ] + ], + "need_reboot" => false } end @@ -935,7 +953,8 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do ], "db" => [":http"] } - ] + ], + "need_reboot" => false } end @@ -1000,7 +1019,8 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do ], "db" => [":key2", ":key3"] } - ] + ], + "need_reboot" => false } end @@ -1027,7 +1047,8 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do "value" => %{"key" => "some_val"}, "db" => [":key1"] } - ] + ], + "need_reboot" => false } end @@ -1077,16 +1098,16 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do ":background" ] } - ] + ], + "need_reboot" => false } end test "delete part of settings by atom subkeys", %{conn: conn} do - config = - insert(:config, - key: ":keyaa1", - value: :erlang.term_to_binary(subkey1: "val1", subkey2: "val2", subkey3: "val3") - ) + insert(:config, + key: :keyaa1, + value: [subkey1: "val1", subkey2: "val2", subkey3: "val3"] + ) conn = conn @@ -1094,8 +1115,8 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do |> post("/api/pleroma/admin/config", %{ configs: [ %{ - group: config.group, - key: config.key, + group: ":pleroma", + key: ":keyaa1", subkeys: [":subkey1", ":subkey3"], delete: true } @@ -1110,7 +1131,8 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do "value" => [%{"tuple" => [":subkey2", "val2"]}], "db" => [":subkey2"] } - ] + ], + "need_reboot" => false } end @@ -1236,6 +1258,159 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do assert Application.get_env(:pleroma, Pleroma.Captcha.NotReal) == "value5" assert Application.get_env(:not_real, :anything) == "value6" end + + test "args for Pleroma.Upload.Filter.Mogrify with custom tuples", %{conn: conn} do + clear_config(Pleroma.Upload.Filter.Mogrify) + + assert conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/config", %{ + configs: [ + %{ + group: ":pleroma", + key: "Pleroma.Upload.Filter.Mogrify", + value: [ + %{"tuple" => [":args", ["auto-orient", "strip"]]} + ] + } + ] + }) + |> json_response_and_validate_schema(200) == %{ + "configs" => [ + %{ + "group" => ":pleroma", + "key" => "Pleroma.Upload.Filter.Mogrify", + "value" => [ + %{"tuple" => [":args", ["auto-orient", "strip"]]} + ], + "db" => [":args"] + } + ], + "need_reboot" => false + } + + assert Config.get(Pleroma.Upload.Filter.Mogrify) == [args: ["auto-orient", "strip"]] + + assert conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/config", %{ + configs: [ + %{ + group: ":pleroma", + key: "Pleroma.Upload.Filter.Mogrify", + value: [ + %{ + "tuple" => [ + ":args", + [ + "auto-orient", + "strip", + "{\"implode\", \"1\"}", + "{\"resize\", \"3840x1080>\"}" + ] + ] + } + ] + } + ] + }) + |> json_response(200) == %{ + "configs" => [ + %{ + "group" => ":pleroma", + "key" => "Pleroma.Upload.Filter.Mogrify", + "value" => [ + %{ + "tuple" => [ + ":args", + [ + "auto-orient", + "strip", + "{\"implode\", \"1\"}", + "{\"resize\", \"3840x1080>\"}" + ] + ] + } + ], + "db" => [":args"] + } + ], + "need_reboot" => false + } + + assert Config.get(Pleroma.Upload.Filter.Mogrify) == [ + args: ["auto-orient", "strip", {"implode", "1"}, {"resize", "3840x1080>"}] + ] + end + + test "enables the welcome messages", %{conn: conn} do + clear_config([:welcome]) + + params = %{ + "group" => ":pleroma", + "key" => ":welcome", + "value" => [ + %{ + "tuple" => [ + ":direct_message", + [ + %{"tuple" => [":enabled", true]}, + %{"tuple" => [":message", "Welcome to Pleroma!"]}, + %{"tuple" => [":sender_nickname", "pleroma"]} + ] + ] + }, + %{ + "tuple" => [ + ":chat_message", + [ + %{"tuple" => [":enabled", true]}, + %{"tuple" => [":message", "Welcome to Pleroma!"]}, + %{"tuple" => [":sender_nickname", "pleroma"]} + ] + ] + }, + %{ + "tuple" => [ + ":email", + [ + %{"tuple" => [":enabled", true]}, + %{"tuple" => [":sender", %{"tuple" => ["pleroma@dev.dev", "Pleroma"]}]}, + %{"tuple" => [":subject", "Welcome to <%= instance_name %>!"]}, + %{"tuple" => [":html", "Welcome to <%= instance_name %>!"]}, + %{"tuple" => [":text", "Welcome to <%= instance_name %>!"]} + ] + ] + } + ] + } + + refute Pleroma.User.WelcomeEmail.enabled?() + refute Pleroma.User.WelcomeMessage.enabled?() + refute Pleroma.User.WelcomeChatMessage.enabled?() + + res = + assert conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/config", %{"configs" => [params]}) + |> json_response_and_validate_schema(200) + + assert Pleroma.User.WelcomeEmail.enabled?() + assert Pleroma.User.WelcomeMessage.enabled?() + assert Pleroma.User.WelcomeChatMessage.enabled?() + + assert res == %{ + "configs" => [ + %{ + "db" => [":direct_message", ":chat_message", ":email"], + "group" => ":pleroma", + "key" => ":welcome", + "value" => params["value"] + } + ], + "need_reboot" => false + } + end end describe "GET /api/pleroma/admin/config/descriptions" do diff --git a/test/web/admin_api/controllers/media_proxy_cache_controller_test.exs b/test/web/admin_api/controllers/media_proxy_cache_controller_test.exs new file mode 100644 index 000000000..f243d1fb2 --- /dev/null +++ b/test/web/admin_api/controllers/media_proxy_cache_controller_test.exs @@ -0,0 +1,167 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.AdminAPI.MediaProxyCacheControllerTest do + use Pleroma.Web.ConnCase + + import Pleroma.Factory + import Mock + + alias Pleroma.Web.MediaProxy + + setup do: clear_config([:media_proxy]) + + setup do + on_exit(fn -> Cachex.clear(:banned_urls_cache) end) + end + + setup do + admin = insert(:user, is_admin: true) + token = insert(:oauth_admin_token, user: admin) + + conn = + build_conn() + |> assign(:user, admin) + |> assign(:token, token) + + Config.put([:media_proxy, :enabled], true) + Config.put([:media_proxy, :invalidation, :enabled], true) + Config.put([:media_proxy, :invalidation, :provider], MediaProxy.Invalidation.Script) + + {:ok, %{admin: admin, token: token, conn: conn}} + end + + describe "GET /api/pleroma/admin/media_proxy_caches" do + test "shows banned MediaProxy URLs", %{conn: conn} do + MediaProxy.put_in_banned_urls([ + "http://localhost:4001/media/a688346.jpg", + "http://localhost:4001/media/fb1f4d.jpg" + ]) + + MediaProxy.put_in_banned_urls("http://localhost:4001/media/gb1f44.jpg") + MediaProxy.put_in_banned_urls("http://localhost:4001/media/tb13f47.jpg") + MediaProxy.put_in_banned_urls("http://localhost:4001/media/wb1f46.jpg") + + response = + conn + |> get("/api/pleroma/admin/media_proxy_caches?page_size=2") + |> json_response_and_validate_schema(200) + + assert response["page_size"] == 2 + assert response["count"] == 5 + + assert response["urls"] == [ + "http://localhost:4001/media/fb1f4d.jpg", + "http://localhost:4001/media/a688346.jpg" + ] + + response = + conn + |> get("/api/pleroma/admin/media_proxy_caches?page_size=2&page=2") + |> json_response_and_validate_schema(200) + + assert response["urls"] == [ + "http://localhost:4001/media/gb1f44.jpg", + "http://localhost:4001/media/tb13f47.jpg" + ] + + assert response["page_size"] == 2 + assert response["count"] == 5 + + response = + conn + |> get("/api/pleroma/admin/media_proxy_caches?page_size=2&page=3") + |> json_response_and_validate_schema(200) + + assert response["urls"] == ["http://localhost:4001/media/wb1f46.jpg"] + end + + test "search banned MediaProxy URLs", %{conn: conn} do + MediaProxy.put_in_banned_urls([ + "http://localhost:4001/media/a688346.jpg", + "http://localhost:4001/media/ff44b1f4d.jpg" + ]) + + MediaProxy.put_in_banned_urls("http://localhost:4001/media/gb1f44.jpg") + MediaProxy.put_in_banned_urls("http://localhost:4001/media/tb13f47.jpg") + MediaProxy.put_in_banned_urls("http://localhost:4001/media/wb1f46.jpg") + + response = + conn + |> get("/api/pleroma/admin/media_proxy_caches?page_size=2&query=F44") + |> json_response_and_validate_schema(200) + + assert response["urls"] == [ + "http://localhost:4001/media/gb1f44.jpg", + "http://localhost:4001/media/ff44b1f4d.jpg" + ] + + assert response["page_size"] == 2 + assert response["count"] == 2 + end + end + + describe "POST /api/pleroma/admin/media_proxy_caches/delete" do + test "deleted MediaProxy URLs from banned", %{conn: conn} do + MediaProxy.put_in_banned_urls([ + "http://localhost:4001/media/a688346.jpg", + "http://localhost:4001/media/fb1f4d.jpg" + ]) + + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/media_proxy_caches/delete", %{ + urls: ["http://localhost:4001/media/a688346.jpg"] + }) + |> json_response_and_validate_schema(200) + + refute MediaProxy.in_banned_urls("http://localhost:4001/media/a688346.jpg") + assert MediaProxy.in_banned_urls("http://localhost:4001/media/fb1f4d.jpg") + end + end + + describe "POST /api/pleroma/admin/media_proxy_caches/purge" do + test "perform invalidates cache of MediaProxy", %{conn: conn} do + urls = [ + "http://example.com/media/a688346.jpg", + "http://example.com/media/fb1f4d.jpg" + ] + + with_mocks [ + {MediaProxy.Invalidation.Script, [], + [ + purge: fn _, _ -> {"ok", 0} end + ]} + ] do + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/media_proxy_caches/purge", %{urls: urls, ban: false}) + |> json_response_and_validate_schema(200) + + refute MediaProxy.in_banned_urls("http://example.com/media/a688346.jpg") + refute MediaProxy.in_banned_urls("http://example.com/media/fb1f4d.jpg") + end + end + + test "perform invalidates cache of MediaProxy and adds url to banned", %{conn: conn} do + urls = [ + "http://example.com/media/a688346.jpg", + "http://example.com/media/fb1f4d.jpg" + ] + + with_mocks [{MediaProxy.Invalidation.Script, [], [purge: fn _, _ -> {"ok", 0} end]}] do + conn + |> put_req_header("content-type", "application/json") + |> post( + "/api/pleroma/admin/media_proxy_caches/purge", + %{urls: urls, ban: true} + ) + |> json_response_and_validate_schema(200) + + assert MediaProxy.in_banned_urls("http://example.com/media/a688346.jpg") + assert MediaProxy.in_banned_urls("http://example.com/media/fb1f4d.jpg") + end + end + end +end diff --git a/test/web/admin_api/controllers/relay_controller_test.exs b/test/web/admin_api/controllers/relay_controller_test.exs index 64086adc5..adadf2b5c 100644 --- a/test/web/admin_api/controllers/relay_controller_test.exs +++ b/test/web/admin_api/controllers/relay_controller_test.exs @@ -39,8 +39,10 @@ defmodule Pleroma.Web.AdminAPI.RelayControllerTest do relay_url: "http://mastodon.example.org/users/admin" }) - assert json_response_and_validate_schema(conn, 200) == - "http://mastodon.example.org/users/admin" + assert json_response_and_validate_schema(conn, 200) == %{ + "actor" => "http://mastodon.example.org/users/admin", + "followed_back" => false + } log_entry = Repo.one(ModerationLog) @@ -59,8 +61,13 @@ defmodule Pleroma.Web.AdminAPI.RelayControllerTest do conn = get(conn, "/api/pleroma/admin/relay") - assert json_response_and_validate_schema(conn, 200)["relays"] -- - ["mastodon.example.org", "mstdn.io"] == [] + assert json_response_and_validate_schema(conn, 200)["relays"] == [ + %{ + "actor" => "http://mastodon.example.org/users/admin", + "followed_back" => true + }, + %{"actor" => "https://mstdn.io/users/mayuutann", "followed_back" => true} + ] end test "DELETE /relay", %{conn: conn, admin: admin} do diff --git a/test/web/admin_api/controllers/report_controller_test.exs b/test/web/admin_api/controllers/report_controller_test.exs index 940bce340..57946e6bb 100644 --- a/test/web/admin_api/controllers/report_controller_test.exs +++ b/test/web/admin_api/controllers/report_controller_test.exs @@ -204,7 +204,7 @@ defmodule Pleroma.Web.AdminAPI.ReportControllerTest do test "returns empty response when no reports created", %{conn: conn} do response = conn - |> get("/api/pleroma/admin/reports") + |> get(report_path(conn, :index)) |> json_response_and_validate_schema(:ok) assert Enum.empty?(response["reports"]) @@ -224,7 +224,7 @@ defmodule Pleroma.Web.AdminAPI.ReportControllerTest do response = conn - |> get("/api/pleroma/admin/reports") + |> get(report_path(conn, :index)) |> json_response_and_validate_schema(:ok) [report] = response["reports"] @@ -256,7 +256,7 @@ defmodule Pleroma.Web.AdminAPI.ReportControllerTest do response = conn - |> get("/api/pleroma/admin/reports?state=open") + |> get(report_path(conn, :index, %{state: "open"})) |> json_response_and_validate_schema(:ok) assert [open_report] = response["reports"] @@ -268,7 +268,7 @@ defmodule Pleroma.Web.AdminAPI.ReportControllerTest do response = conn - |> get("/api/pleroma/admin/reports?state=closed") + |> get(report_path(conn, :index, %{state: "closed"})) |> json_response_and_validate_schema(:ok) assert [closed_report] = response["reports"] @@ -280,9 +280,7 @@ defmodule Pleroma.Web.AdminAPI.ReportControllerTest do assert %{"total" => 0, "reports" => []} == conn - |> get("/api/pleroma/admin/reports?state=resolved", %{ - "" => "" - }) + |> get(report_path(conn, :index, %{state: "resolved"})) |> json_response_and_validate_schema(:ok) end @@ -297,7 +295,7 @@ defmodule Pleroma.Web.AdminAPI.ReportControllerTest do |> get("/api/pleroma/admin/reports") assert json_response(conn, :forbidden) == - %{"error" => "User is not an admin or OAuth admin scope is not granted."} + %{"error" => "User is not an admin."} end test "returns 403 when requested by anonymous" do diff --git a/test/web/admin_api/search_test.exs b/test/web/admin_api/search_test.exs index e0e3d4153..b974cedd5 100644 --- a/test/web/admin_api/search_test.exs +++ b/test/web/admin_api/search_test.exs @@ -166,5 +166,16 @@ defmodule Pleroma.Web.AdminAPI.SearchTest do assert total == 3 assert count == 1 end + + test "it returns unapproved user" do + unapproved = insert(:user, approval_pending: true) + insert(:user) + insert(:user) + + {:ok, _results, total} = Search.user() + {:ok, [^unapproved], count} = Search.user(%{need_approval: true}) + assert total == 3 + assert count == 1 + end end end diff --git a/test/web/admin_api/views/report_view_test.exs b/test/web/admin_api/views/report_view_test.exs index f00b0afb2..5a02292be 100644 --- a/test/web/admin_api/views/report_view_test.exs +++ b/test/web/admin_api/views/report_view_test.exs @@ -4,11 +4,14 @@ defmodule Pleroma.Web.AdminAPI.ReportViewTest do use Pleroma.DataCase + import Pleroma.Factory + + alias Pleroma.Web.AdminAPI alias Pleroma.Web.AdminAPI.Report alias Pleroma.Web.AdminAPI.ReportView alias Pleroma.Web.CommonAPI - alias Pleroma.Web.MastodonAPI.AccountView + alias Pleroma.Web.MastodonAPI alias Pleroma.Web.MastodonAPI.StatusView test "renders a report" do @@ -21,13 +24,16 @@ defmodule Pleroma.Web.AdminAPI.ReportViewTest do content: nil, actor: Map.merge( - AccountView.render("show.json", %{user: user}), - Pleroma.Web.AdminAPI.AccountView.render("show.json", %{user: user}) + MastodonAPI.AccountView.render("show.json", %{user: user, skip_visibility_check: true}), + AdminAPI.AccountView.render("show.json", %{user: user}) ), account: Map.merge( - AccountView.render("show.json", %{user: other_user}), - Pleroma.Web.AdminAPI.AccountView.render("show.json", %{user: other_user}) + MastodonAPI.AccountView.render("show.json", %{ + user: other_user, + skip_visibility_check: true + }), + AdminAPI.AccountView.render("show.json", %{user: other_user}) ), statuses: [], notes: [], @@ -56,13 +62,16 @@ defmodule Pleroma.Web.AdminAPI.ReportViewTest do content: nil, actor: Map.merge( - AccountView.render("show.json", %{user: user}), - Pleroma.Web.AdminAPI.AccountView.render("show.json", %{user: user}) + MastodonAPI.AccountView.render("show.json", %{user: user, skip_visibility_check: true}), + AdminAPI.AccountView.render("show.json", %{user: user}) ), account: Map.merge( - AccountView.render("show.json", %{user: other_user}), - Pleroma.Web.AdminAPI.AccountView.render("show.json", %{user: other_user}) + MastodonAPI.AccountView.render("show.json", %{ + user: other_user, + skip_visibility_check: true + }), + AdminAPI.AccountView.render("show.json", %{user: other_user}) ), statuses: [StatusView.render("show.json", %{activity: activity})], state: "open", diff --git a/test/web/common_api/common_api_test.exs b/test/web/common_api/common_api_test.exs index 6bd26050e..5afb0a6dc 100644 --- a/test/web/common_api/common_api_test.exs +++ b/test/web/common_api/common_api_test.exs @@ -4,11 +4,14 @@ defmodule Pleroma.Web.CommonAPITest do use Pleroma.DataCase + use Oban.Testing, repo: Pleroma.Repo + alias Pleroma.Activity alias Pleroma.Chat alias Pleroma.Conversation.Participation alias Pleroma.Notification alias Pleroma.Object + alias Pleroma.Repo alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.Transmogrifier @@ -18,6 +21,7 @@ defmodule Pleroma.Web.CommonAPITest do import Pleroma.Factory import Mock + import Ecto.Query, only: [from: 2] require Pleroma.Constants @@ -25,6 +29,52 @@ defmodule Pleroma.Web.CommonAPITest do setup do: clear_config([:instance, :limit]) setup do: clear_config([:instance, :max_pinned_statuses]) + describe "blocking" do + setup do + blocker = insert(:user) + blocked = insert(:user) + User.follow(blocker, blocked) + User.follow(blocked, blocker) + %{blocker: blocker, blocked: blocked} + end + + test "it blocks and federates", %{blocker: blocker, blocked: blocked} do + clear_config([:instance, :federating], true) + + with_mock Pleroma.Web.Federator, + publish: fn _ -> nil end do + assert {:ok, block} = CommonAPI.block(blocker, blocked) + + assert block.local + assert User.blocks?(blocker, blocked) + refute User.following?(blocker, blocked) + refute User.following?(blocked, blocker) + + assert called(Pleroma.Web.Federator.publish(block)) + end + end + + test "it blocks and does not federate if outgoing blocks are disabled", %{ + blocker: blocker, + blocked: blocked + } do + clear_config([:instance, :federating], true) + clear_config([:activitypub, :outgoing_blocks], false) + + with_mock Pleroma.Web.Federator, + publish: fn _ -> nil end do + assert {:ok, block} = CommonAPI.block(blocker, blocked) + + assert block.local + assert User.blocks?(blocker, blocked) + refute User.following?(blocker, blocked) + refute User.following?(blocked, blocker) + + refute called(Pleroma.Web.Federator.publish(block)) + end + end + end + describe "posting chat messages" do setup do: clear_config([:instance, :chat_limit]) @@ -412,6 +462,11 @@ defmodule Pleroma.Web.CommonAPITest do end describe "posting" do + test "deactivated users can't post" do + user = insert(:user, deactivated: true) + assert {:error, _} = CommonAPI.post(user, %{status: "ye"}) + end + test "it supports explicit addressing" do user = insert(:user) user_two = insert(:user) @@ -445,6 +500,7 @@ defmodule Pleroma.Web.CommonAPITest do object = Object.normalize(activity) assert object.data["content"] == "<p><b>2hu</b></p>alert('xss')" + assert object.data["source"] == post end test "it filters out obviously bad tags when accepting a post as Markdown" do @@ -461,6 +517,7 @@ defmodule Pleroma.Web.CommonAPITest do object = Object.normalize(activity) assert object.data["content"] == "<p><b>2hu</b></p>alert('xss')" + assert object.data["source"] == post end test "it does not allow replies to direct messages that are not direct messages themselves" do @@ -543,15 +600,15 @@ defmodule Pleroma.Web.CommonAPITest do test "it can handle activities that expire" do user = insert(:user) - expires_at = - NaiveDateTime.utc_now() - |> NaiveDateTime.truncate(:second) - |> NaiveDateTime.add(1_000_000, :second) + expires_at = DateTime.add(DateTime.utc_now(), 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 + assert_enqueued( + worker: Pleroma.Workers.PurgeExpiredActivity, + args: %{activity_id: activity.id}, + scheduled_at: expires_at + ) end end @@ -576,14 +633,27 @@ 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, "👍") + clear_config([:instance, :federating], true) - {:ok, unreaction} = CommonAPI.unreact_with_emoji(activity.id, user, "👍") + with_mock Pleroma.Web.Federator, + publish: fn _ -> nil end do + {: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, "👍") + + assert unreaction.data["type"] == "Undo" + assert unreaction.data["object"] == reaction.data["id"] + assert unreaction.local - assert unreaction.data["type"] == "Undo" - assert unreaction.data["object"] == reaction.data["id"] - assert unreaction.local + # On federation, it contains the undone (and deleted) object + unreaction_with_object = %{ + unreaction + | data: Map.put(unreaction.data, "object", reaction.data) + } + + assert called(Pleroma.Web.Federator.publish(unreaction_with_object)) + end end test "repeating a status" do @@ -742,6 +812,69 @@ defmodule Pleroma.Web.CommonAPITest do [user: user, activity: activity] end + test "marks notifications as read after mute" do + author = insert(:user) + activity = insert(:note_activity, user: author) + + friend1 = insert(:user) + friend2 = insert(:user) + + {:ok, reply_activity} = + CommonAPI.post( + friend2, + %{ + status: "@#{author.nickname} @#{friend1.nickname} test reply", + in_reply_to_status_id: activity.id + } + ) + + {:ok, favorite_activity} = CommonAPI.favorite(friend2, activity.id) + {:ok, repeat_activity} = CommonAPI.repeat(activity.id, friend1) + + assert Repo.aggregate( + from(n in Notification, where: n.seen == false and n.user_id == ^friend1.id), + :count + ) == 1 + + unread_notifications = + Repo.all(from(n in Notification, where: n.seen == false, where: n.user_id == ^author.id)) + + assert Enum.any?(unread_notifications, fn n -> + n.type == "favourite" && n.activity_id == favorite_activity.id + end) + + assert Enum.any?(unread_notifications, fn n -> + n.type == "reblog" && n.activity_id == repeat_activity.id + end) + + assert Enum.any?(unread_notifications, fn n -> + n.type == "mention" && n.activity_id == reply_activity.id + end) + + {:ok, _} = CommonAPI.add_mute(author, activity) + assert CommonAPI.thread_muted?(author, activity) + + assert Repo.aggregate( + from(n in Notification, where: n.seen == false and n.user_id == ^friend1.id), + :count + ) == 1 + + read_notifications = + Repo.all(from(n in Notification, where: n.seen == true, where: n.user_id == ^author.id)) + + assert Enum.any?(read_notifications, fn n -> + n.type == "favourite" && n.activity_id == favorite_activity.id + end) + + assert Enum.any?(read_notifications, fn n -> + n.type == "reblog" && n.activity_id == repeat_activity.id + end) + + assert Enum.any?(read_notifications, fn n -> + n.type == "mention" && n.activity_id == reply_activity.id + end) + end + test "add mute", %{user: user, activity: activity} do {:ok, _} = CommonAPI.add_mute(user, activity) assert CommonAPI.thread_muted?(user, activity) @@ -886,6 +1019,15 @@ defmodule Pleroma.Web.CommonAPITest do end end + describe "follow/2" do + test "directly follows a non-locked local user" do + [follower, followed] = insert_pair(:user) + {:ok, follower, followed, _} = CommonAPI.follow(follower, followed) + + assert User.following?(follower, followed) + end + end + describe "unfollow/2" do test "also unsubscribes a user" do [follower, followed] = insert_pair(:user) @@ -950,9 +1092,9 @@ defmodule Pleroma.Web.CommonAPITest do follower = insert(:user) follower_two = insert(:user) - {:ok, follow_activity} = ActivityPub.follow(follower, user) - {:ok, follow_activity_two} = ActivityPub.follow(follower, user) - {:ok, follow_activity_three} = ActivityPub.follow(follower_two, user) + {:ok, _, _, follow_activity} = CommonAPI.follow(follower, user) + {:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user) + {:ok, _, _, follow_activity_three} = CommonAPI.follow(follower_two, user) assert follow_activity.data["state"] == "pending" assert follow_activity_two.data["state"] == "pending" @@ -970,9 +1112,9 @@ defmodule Pleroma.Web.CommonAPITest do follower = insert(:user) follower_two = insert(:user) - {:ok, follow_activity} = ActivityPub.follow(follower, user) - {:ok, follow_activity_two} = ActivityPub.follow(follower, user) - {:ok, follow_activity_three} = ActivityPub.follow(follower_two, user) + {:ok, _, _, follow_activity} = CommonAPI.follow(follower, user) + {:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user) + {:ok, _, _, follow_activity_three} = CommonAPI.follow(follower_two, user) assert follow_activity.data["state"] == "pending" assert follow_activity_two.data["state"] == "pending" diff --git a/test/web/fallback_test.exs b/test/web/fallback_test.exs index 3919ef93a..a65865860 100644 --- a/test/web/fallback_test.exs +++ b/test/web/fallback_test.exs @@ -6,22 +6,56 @@ defmodule Pleroma.Web.FallbackTest do use Pleroma.Web.ConnCase import Pleroma.Factory - test "GET /registration/:token", %{conn: conn} do - assert conn - |> get("/registration/foo") - |> html_response(200) =~ "<!--server-generated-meta-->" + describe "neither preloaded data nor metadata attached to" do + test "GET /registration/:token", %{conn: conn} do + response = get(conn, "/registration/foo") + + assert html_response(response, 200) =~ "<!--server-generated-meta-->" + end + + test "GET /*path", %{conn: conn} do + assert conn + |> get("/foo") + |> html_response(200) =~ "<!--server-generated-meta-->" + end end - test "GET /:maybe_nickname_or_id", %{conn: conn} do - user = insert(:user) + describe "preloaded data and metadata attached to" do + test "GET /:maybe_nickname_or_id", %{conn: conn} do + user = insert(:user) + user_missing = get(conn, "/foo") + user_present = get(conn, "/#{user.nickname}") - assert conn - |> get("/foo") - |> html_response(200) =~ "<!--server-generated-meta-->" + assert(html_response(user_missing, 200) =~ "<!--server-generated-meta-->") + refute html_response(user_present, 200) =~ "<!--server-generated-meta-->" + assert html_response(user_present, 200) =~ "initial-results" + end - refute conn - |> get("/" <> user.nickname) - |> html_response(200) =~ "<!--server-generated-meta-->" + test "GET /*path", %{conn: conn} do + assert conn + |> get("/foo") + |> html_response(200) =~ "<!--server-generated-meta-->" + + refute conn + |> get("/foo/bar") + |> html_response(200) =~ "<!--server-generated-meta-->" + end + end + + describe "preloaded data is attached to" do + test "GET /main/public", %{conn: conn} do + public_page = get(conn, "/main/public") + + refute html_response(public_page, 200) =~ "<!--server-generated-meta-->" + assert html_response(public_page, 200) =~ "initial-results" + end + + test "GET /main/all", %{conn: conn} do + public_page = get(conn, "/main/all") + + refute html_response(public_page, 200) =~ "<!--server-generated-meta-->" + assert html_response(public_page, 200) =~ "initial-results" + end end test "GET /api*path", %{conn: conn} do @@ -34,16 +68,6 @@ defmodule Pleroma.Web.FallbackTest do assert redirected_to(get(conn, "/pleroma/admin")) =~ "/pleroma/admin/" end - test "GET /*path", %{conn: conn} do - assert conn - |> get("/foo") - |> html_response(200) =~ "<!--server-generated-meta-->" - - assert conn - |> get("/foo/bar") - |> html_response(200) =~ "<!--server-generated-meta-->" - end - test "OPTIONS /*path", %{conn: conn} do assert conn |> options("/foo") diff --git a/test/web/federator_test.exs b/test/web/federator_test.exs index de90aa6e0..592fdccd1 100644 --- a/test/web/federator_test.exs +++ b/test/web/federator_test.exs @@ -23,7 +23,7 @@ defmodule Pleroma.Web.FederatorTest do setup_all do: clear_config([:instance, :federating], true) setup do: clear_config([:instance, :allow_relay]) - setup do: clear_config([:instance, :rewrite_policy]) + setup do: clear_config([:mrf, :policies]) setup do: clear_config([:mrf_keyword]) describe "Publish an activity" do @@ -158,7 +158,7 @@ defmodule Pleroma.Web.FederatorTest do Pleroma.Config.put([:mrf_keyword, :reject], ["lain"]) Pleroma.Config.put( - [:instance, :rewrite_policy], + [:mrf, :policies], Pleroma.Web.ActivityPub.MRF.KeywordPolicy ) diff --git a/test/web/feed/tag_controller_test.exs b/test/web/feed/tag_controller_test.exs index 3c29cd94f..868e40965 100644 --- a/test/web/feed/tag_controller_test.exs +++ b/test/web/feed/tag_controller_test.exs @@ -181,4 +181,17 @@ defmodule Pleroma.Web.Feed.TagControllerTest do 'yeah #PleromaArt' ] end + + describe "private instance" do + setup do: clear_config([:instance, :public]) + + test "returns 404 for tags feed", %{conn: conn} do + Config.put([:instance, :public], false) + + conn + |> put_req_header("accept", "application/rss+xml") + |> get(tag_feed_path(conn, :feed, "pleromaart")) + |> response(404) + end + end end diff --git a/test/web/feed/user_controller_test.exs b/test/web/feed/user_controller_test.exs index fa2ed1ea5..9a5610baa 100644 --- a/test/web/feed/user_controller_test.exs +++ b/test/web/feed/user_controller_test.exs @@ -181,6 +181,17 @@ defmodule Pleroma.Web.Feed.UserControllerTest do assert activity_titles == ['public', 'unlisted'] end + + test "returns 404 when the user is remote", %{conn: conn} do + user = insert(:user, local: false) + + {:ok, _} = CommonAPI.post(user, %{status: "test"}) + + assert conn + |> put_req_header("accept", "application/atom+xml") + |> get(user_feed_path(conn, :feed, user.nickname)) + |> response(404) + end end # Note: see ActivityPubControllerTest for JSON format tests @@ -235,4 +246,20 @@ defmodule Pleroma.Web.Feed.UserControllerTest do assert response == ~S({"error":"Not found"}) end end + + describe "private instance" do + setup do: clear_config([:instance, :public]) + + test "returns 404 for user feed", %{conn: conn} do + Config.put([:instance, :public], false) + user = insert(:user) + + {:ok, _} = CommonAPI.post(user, %{status: "test"}) + + assert conn + |> put_req_header("accept", "application/atom+xml") + |> get(user_feed_path(conn, :feed, user.nickname)) + |> response(404) + end + end end diff --git a/test/web/instances/instance_test.exs b/test/web/instances/instance_test.exs index e463200ca..dc6ace843 100644 --- a/test/web/instances/instance_test.exs +++ b/test/web/instances/instance_test.exs @@ -8,6 +8,7 @@ defmodule Pleroma.Instances.InstanceTest do use Pleroma.DataCase + import ExUnit.CaptureLog import Pleroma.Factory setup_all do: clear_config([:instance, :federation_reachability_timeout_days], 1) @@ -97,4 +98,36 @@ defmodule Pleroma.Instances.InstanceTest do assert initial_value == instance.unreachable_since end end + + test "Scrapes favicon URLs" do + Tesla.Mock.mock(fn %{url: "https://favicon.example.org/"} -> + %Tesla.Env{ + status: 200, + body: ~s[<html><head><link rel="icon" href="/favicon.png"></head></html>] + } + end) + + assert "https://favicon.example.org/favicon.png" == + Instance.get_or_update_favicon(URI.parse("https://favicon.example.org/")) + end + + test "Returns nil on too long favicon URLs" do + long_favicon_url = + "https://Lorem.ipsum.dolor.sit.amet/consecteturadipiscingelit/Praesentpharetrapurusutaliquamtempus/Mauriseulaoreetarcu/atfacilisisorci/Nullamporttitor/nequesedfeugiatmollis/dolormagnaefficiturlorem/nonpretiumsapienorcieurisus/Nullamveleratsem/Maecenassedaccumsanexnam/favicon.png" + + Tesla.Mock.mock(fn %{url: "https://long-favicon.example.org/"} -> + %Tesla.Env{ + status: 200, + body: ~s[<html><head><link rel="icon" href="] <> long_favicon_url <> ~s["></head></html>] + } + end) + + assert capture_log(fn -> + assert nil == + Instance.get_or_update_favicon( + URI.parse("https://long-favicon.example.org/") + ) + end) =~ + "Instance.get_or_update_favicon(\"long-favicon.example.org\") error: %Postgrex.Error{" + end end diff --git a/test/web/masto_fe_controller_test.exs b/test/web/masto_fe_controller_test.exs index 1d107d56c..f3b54b5f2 100644 --- a/test/web/masto_fe_controller_test.exs +++ b/test/web/masto_fe_controller_test.exs @@ -24,7 +24,7 @@ defmodule Pleroma.Web.MastodonAPI.MastoFEController do assert _result = json_response(conn, 200) user = User.get_cached_by_ap_id(user.ap_id) - assert user.settings == %{"programming" => "socks"} + assert user.mastofe_settings == %{"programming" => "socks"} end describe "index/2 redirections" do 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 7c420985d..2e6704726 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 @@ -83,10 +83,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do test "updates the user's bio", %{conn: conn} do user2 = insert(:user) - conn = - patch(conn, "/api/v1/accounts/update_credentials", %{ - "note" => "I drink #cofe with @#{user2.nickname}\n\nsuya.." - }) + raw_bio = "I drink #cofe with @#{user2.nickname}\n\nsuya.." + + conn = patch(conn, "/api/v1/accounts/update_credentials", %{"note" => raw_bio}) assert user_data = json_response_and_validate_schema(conn, 200) @@ -94,6 +93,12 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do ~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 }" href="#{user2.ap_id}" rel="ugc">@<span>#{user2.nickname}</span></a></span><br/><br/>suya..) + + assert user_data["source"]["note"] == raw_bio + + user = Repo.get(User, user_data["id"]) + + assert user.raw_bio == raw_bio end test "updates the user's locking status", %{conn: conn} do @@ -103,6 +108,13 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do assert user_data["locked"] == true end + test "updates the user's chat acceptance status", %{conn: conn} do + conn = patch(conn, "/api/v1/accounts/update_credentials", %{accepts_chat_messages: "false"}) + + assert user_data = json_response_and_validate_schema(conn, 200) + assert user_data["pleroma"]["accepts_chat_messages"] == false + end + test "updates the user's allow_following_move", %{user: user, conn: conn} do assert user.allow_following_move == true @@ -202,6 +214,10 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do assert user_data = json_response_and_validate_schema(conn, 200) assert user_data["display_name"] == "markorepairs" + + update_activity = Repo.one(Pleroma.Activity) + assert update_activity.data["type"] == "Update" + assert update_activity.data["object"]["name"] == "markorepairs" end test "updates the user's avatar", %{user: user, conn: conn} do @@ -211,10 +227,21 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do filename: "an_image.jpg" } - conn = patch(conn, "/api/v1/accounts/update_credentials", %{"avatar" => new_avatar}) + assert user.avatar == %{} - assert user_response = json_response_and_validate_schema(conn, 200) + res = patch(conn, "/api/v1/accounts/update_credentials", %{"avatar" => new_avatar}) + + assert user_response = json_response_and_validate_schema(res, 200) assert user_response["avatar"] != User.avatar_url(user) + + user = User.get_by_id(user.id) + refute user.avatar == %{} + + # Also resets it + _res = patch(conn, "/api/v1/accounts/update_credentials", %{"avatar" => ""}) + + user = User.get_by_id(user.id) + assert user.avatar == nil end test "updates the user's banner", %{user: user, conn: conn} do @@ -224,26 +251,39 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do filename: "an_image.jpg" } - conn = patch(conn, "/api/v1/accounts/update_credentials", %{"header" => new_header}) + res = patch(conn, "/api/v1/accounts/update_credentials", %{"header" => new_header}) - assert user_response = json_response_and_validate_schema(conn, 200) + assert user_response = json_response_and_validate_schema(res, 200) assert user_response["header"] != User.banner_url(user) + + # Also resets it + _res = patch(conn, "/api/v1/accounts/update_credentials", %{"header" => ""}) + + user = User.get_by_id(user.id) + assert user.banner == nil end - test "updates the user's background", %{conn: conn} do + test "updates the user's background", %{conn: conn, user: user} do new_header = %Plug.Upload{ content_type: "image/jpg", path: Path.absname("test/fixtures/image.jpg"), filename: "an_image.jpg" } - conn = + res = patch(conn, "/api/v1/accounts/update_credentials", %{ "pleroma_background_image" => new_header }) - assert user_response = json_response_and_validate_schema(conn, 200) + assert user_response = json_response_and_validate_schema(res, 200) assert user_response["pleroma"]["background_image"] + # + # Also resets it + _res = + patch(conn, "/api/v1/accounts/update_credentials", %{"pleroma_background_image" => ""}) + + user = User.get_by_id(user.id) + assert user.background == nil end test "requires 'write:accounts' permission" do @@ -315,6 +355,30 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do ] end + test "emojis in fields labels", %{conn: conn} do + fields = [ + %{"name" => ":firefox:", "value" => "is best 2hu"}, + %{"name" => "they wins", "value" => ":blank:"} + ] + + account_data = + conn + |> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields}) + |> json_response_and_validate_schema(200) + + assert account_data["fields"] == [ + %{"name" => ":firefox:", "value" => "is best 2hu"}, + %{"name" => "they wins", "value" => ":blank:"} + ] + + assert account_data["source"]["fields"] == [ + %{"name" => ":firefox:", "value" => "is best 2hu"}, + %{"name" => "they wins", "value" => ":blank:"} + ] + + assert [%{"shortcode" => "blank"}, %{"shortcode" => "firefox"}] = account_data["emojis"] + end + test "update fields via x-www-form-urlencoded", %{conn: conn} do fields = [ @@ -395,4 +459,71 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do |> json_response_and_validate_schema(403) end end + + describe "Mark account as bot" do + setup do: oauth_access(["write:accounts"]) + setup :request_content_type + + test "changing actor_type to Service makes account a bot", %{conn: conn} do + account = + conn + |> patch("/api/v1/accounts/update_credentials", %{actor_type: "Service"}) + |> json_response_and_validate_schema(200) + + assert account["bot"] + assert account["source"]["pleroma"]["actor_type"] == "Service" + end + + test "changing actor_type to Person makes account a human", %{conn: conn} do + account = + conn + |> patch("/api/v1/accounts/update_credentials", %{actor_type: "Person"}) + |> json_response_and_validate_schema(200) + + refute account["bot"] + assert account["source"]["pleroma"]["actor_type"] == "Person" + end + + test "changing actor_type to Application causes error", %{conn: conn} do + response = + conn + |> patch("/api/v1/accounts/update_credentials", %{actor_type: "Application"}) + |> json_response_and_validate_schema(403) + + assert %{"error" => "Invalid request"} == response + end + + test "changing bot field to true changes actor_type to Service", %{conn: conn} do + account = + conn + |> patch("/api/v1/accounts/update_credentials", %{bot: "true"}) + |> json_response_and_validate_schema(200) + + assert account["bot"] + assert account["source"]["pleroma"]["actor_type"] == "Service" + end + + test "changing bot field to false changes actor_type to Person", %{conn: conn} do + account = + conn + |> patch("/api/v1/accounts/update_credentials", %{bot: "false"}) + |> json_response_and_validate_schema(200) + + refute account["bot"] + assert account["source"]["pleroma"]["actor_type"] == "Person" + end + + test "actor_type field has a higher priority than bot", %{conn: conn} do + account = + conn + |> patch("/api/v1/accounts/update_credentials", %{ + actor_type: "Person", + bot: "true" + }) + |> json_response_and_validate_schema(200) + + refute account["bot"] + assert account["source"]["pleroma"]["actor_type"] == "Person" + 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 1ce97378d..17a1e7d66 100644 --- a/test/web/mastodon_api/controllers/account_controller_test.exs +++ b/test/web/mastodon_api/controllers/account_controller_test.exs @@ -5,7 +5,6 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do use Pleroma.Web.ConnCase - alias Pleroma.Config alias Pleroma.Repo alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub @@ -16,8 +15,6 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do import Pleroma.Factory describe "account fetching" do - setup do: clear_config([:instance, :limit_to_local_content]) - test "works by id" do %User{id: user_id} = insert(:user) @@ -42,7 +39,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do end test "works by nickname for remote users" do - Config.put([:instance, :limit_to_local_content], false) + clear_config([:instance, :limit_to_local_content], false) user = insert(:user, nickname: "user@example.com", local: false) @@ -53,7 +50,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do end test "respects limit_to_local_content == :all for remote user nicknames" do - Config.put([:instance, :limit_to_local_content], :all) + clear_config([:instance, :limit_to_local_content], :all) user = insert(:user, nickname: "user@example.com", local: false) @@ -63,7 +60,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do end test "respects limit_to_local_content == :unauthenticated for remote user nicknames" do - Config.put([:instance, :limit_to_local_content], :unauthenticated) + clear_config([:instance, :limit_to_local_content], :unauthenticated) user = insert(:user, nickname: "user@example.com", local: false) reading_user = insert(:user) @@ -127,6 +124,15 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do |> get("/api/v1/accounts/internal.fetch") |> json_response_and_validate_schema(404) end + + test "returns 404 for deactivated user", %{conn: conn} do + user = insert(:user, deactivated: true) + + assert %{"error" => "Can't find user"} = + conn + |> get("/api/v1/accounts/#{user.id}") + |> json_response_and_validate_schema(:not_found) + end end defp local_and_remote_users do @@ -143,15 +149,15 @@ 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 - assert %{"error" => "Can't find user"} == + assert %{"error" => "This API requires an authenticated user"} == conn |> get("/api/v1/accounts/#{local.id}") - |> json_response_and_validate_schema(:not_found) + |> json_response_and_validate_schema(:unauthorized) - assert %{"error" => "Can't find user"} == + assert %{"error" => "This API requires an authenticated user"} == conn |> get("/api/v1/accounts/#{remote.id}") - |> json_response_and_validate_schema(:not_found) + |> json_response_and_validate_schema(:unauthorized) end test "if user is authenticated", %{local: local, remote: remote} do @@ -173,8 +179,8 @@ 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_and_validate_schema(res_conn, :not_found) == %{ - "error" => "Can't find user" + assert json_response_and_validate_schema(res_conn, :unauthorized) == %{ + "error" => "This API requires an authenticated user" } res_conn = get(conn, "/api/v1/accounts/#{remote.id}") @@ -203,8 +209,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do res_conn = get(conn, "/api/v1/accounts/#{remote.id}") - assert json_response_and_validate_schema(res_conn, :not_found) == %{ - "error" => "Can't find user" + assert json_response_and_validate_schema(res_conn, :unauthorized) == %{ + "error" => "This API requires an authenticated user" } end @@ -249,6 +255,24 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do assert id == announce.id end + test "deactivated user", %{conn: conn} do + user = insert(:user, deactivated: true) + + assert %{"error" => "Can't find user"} == + conn + |> get("/api/v1/accounts/#{user.id}/statuses") + |> json_response_and_validate_schema(:not_found) + end + + test "returns 404 when user is invisible", %{conn: conn} do + user = insert(:user, %{invisible: true}) + + assert %{"error" => "Can't find user"} = + conn + |> get("/api/v1/accounts/#{user.id}") + |> json_response_and_validate_schema(404) + end + test "respects blocks", %{user: user_one, conn: conn} do user_two = insert(:user) user_three = insert(:user) @@ -350,9 +374,10 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do assert json_response_and_validate_schema(conn, 200) == [] end - test "gets an users media", %{conn: conn} do + test "gets an users media, excludes reblogs", %{conn: conn} do note = insert(:note_activity) user = User.get_cached_by_ap_id(note.data["actor"]) + other_user = insert(:user) file = %Plug.Upload{ content_type: "image/jpg", @@ -364,6 +389,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do {:ok, %{id: image_post_id}} = CommonAPI.post(user, %{status: "cofe", media_ids: [media_id]}) + {:ok, %{id: media_id}} = ActivityPub.upload(file, actor: other_user.ap_id) + + {:ok, %{id: other_image_post_id}} = + CommonAPI.post(other_user, %{status: "cofe2", media_ids: [media_id]}) + + {:ok, _announce} = CommonAPI.repeat(other_image_post_id, user) + conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?only_media=true") assert [%{"id" => ^image_post_id}] = json_response_and_validate_schema(conn, 200) @@ -422,15 +454,15 @@ 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 - assert %{"error" => "Can't find user"} == + assert %{"error" => "This API requires an authenticated user"} == conn |> get("/api/v1/accounts/#{local.id}/statuses") - |> json_response_and_validate_schema(:not_found) + |> json_response_and_validate_schema(:unauthorized) - assert %{"error" => "Can't find user"} == + assert %{"error" => "This API requires an authenticated user"} == conn |> get("/api/v1/accounts/#{remote.id}/statuses") - |> json_response_and_validate_schema(:not_found) + |> json_response_and_validate_schema(:unauthorized) end test "if user is authenticated", %{local: local, remote: remote} do @@ -451,10 +483,10 @@ 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 - assert %{"error" => "Can't find user"} == + assert %{"error" => "This API requires an authenticated user"} == conn |> get("/api/v1/accounts/#{local.id}/statuses") - |> json_response_and_validate_schema(:not_found) + |> json_response_and_validate_schema(:unauthorized) res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses") assert length(json_response_and_validate_schema(res_conn, 200)) == 1 @@ -481,10 +513,10 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses") assert length(json_response_and_validate_schema(res_conn, 200)) == 1 - assert %{"error" => "Can't find user"} == + assert %{"error" => "This API requires an authenticated user"} == conn |> get("/api/v1/accounts/#{remote.id}/statuses") - |> json_response_and_validate_schema(:not_found) + |> json_response_and_validate_schema(:unauthorized) end test "if user is authenticated", %{local: local, remote: remote} do @@ -548,6 +580,15 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3_id}") |> json_response_and_validate_schema(200) + assert [%{"id" => ^follower2_id}, %{"id" => ^follower1_id}] = + conn + |> get( + "/api/v1/accounts/#{user.id}/followers?id=#{user.id}&limit=20&max_id=#{ + follower3_id + }" + ) + |> json_response_and_validate_schema(200) + res_conn = get(conn, "/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3_id}") assert [%{"id" => ^follower2_id}] = json_response_and_validate_schema(res_conn, 200) @@ -620,6 +661,16 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do assert id1 == following1.id res_conn = + get( + conn, + "/api/v1/accounts/#{user.id}/following?id=#{user.id}&limit=20&max_id=#{following3.id}" + ) + + 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_and_validate_schema(res_conn, 200) @@ -673,7 +724,10 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do followed = insert(:user) other_user = insert(:user) - ret_conn = post(conn, "/api/v1/accounts/#{followed.id}/follow?reblogs=false") + ret_conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/accounts/#{followed.id}/follow", %{reblogs: false}) assert %{"showing_reblogs" => false} = json_response_and_validate_schema(ret_conn, 200) @@ -687,7 +741,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do assert %{"showing_reblogs" => true} = conn - |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true") + |> put_req_header("content-type", "application/json") + |> post("/api/v1/accounts/#{followed.id}/follow", %{reblogs: true}) |> json_response_and_validate_schema(200) assert [%{"id" => ^reblog_id}] = @@ -696,6 +751,35 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do |> json_response(200) end + test "following with reblogs" do + %{conn: conn} = oauth_access(["follow", "read:statuses"]) + followed = insert(:user) + other_user = insert(:user) + + ret_conn = post(conn, "/api/v1/accounts/#{followed.id}/follow") + + assert %{"showing_reblogs" => true} = json_response_and_validate_schema(ret_conn, 200) + + {:ok, activity} = CommonAPI.post(other_user, %{status: "hey"}) + {:ok, %{id: reblog_id}} = CommonAPI.repeat(activity.id, followed) + + assert [%{"id" => ^reblog_id}] = + conn + |> get("/api/v1/timelines/home") + |> json_response(200) + + assert %{"showing_reblogs" => false} = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/accounts/#{followed.id}/follow", %{reblogs: false}) + |> json_response_and_validate_schema(200) + + assert [] == + 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") @@ -745,7 +829,6 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do 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) @@ -817,9 +900,93 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do [valid_params: valid_params] end - setup do: clear_config([:instance, :account_activation_required]) + test "registers and logs in without :account_activation_required / :account_approval_required", + %{conn: conn} do + clear_config([:instance, :account_activation_required], false) + clear_config([:instance, :account_approval_required], false) + + conn = + 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" + }) + + 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", %{ + grant_type: "client_credentials", + client_id: client_id, + client_secret: client_secret + }) + + assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} = + json_response(conn, 200) + + assert token + token_from_db = Repo.get_by(Token, token: token) + assert token_from_db + assert refresh + assert scope == "read write follow" + + clear_config([User, :email_blacklist], ["example.org"]) + + params = %{ + username: "lain", + email: "lain@example.org", + password: "PlzDontHackLain", + bio: "Test Bio", + agreement: true + } + + conn = + build_conn() + |> put_req_header("content-type", "multipart/form-data") + |> put_req_header("authorization", "Bearer " <> token) + |> post("/api/v1/accounts", params) + + assert %{"error" => "{\"email\":[\"Invalid email\"]}"} = + json_response_and_validate_schema(conn, 400) + + Pleroma.Config.put([User, :email_blacklist], []) + + conn = + build_conn() + |> put_req_header("content-type", "multipart/form-data") + |> put_req_header("authorization", "Bearer " <> token) + |> post("/api/v1/accounts", params) + + %{ + "access_token" => token, + "created_at" => _created_at, + "scope" => ^scope, + "token_type" => "Bearer" + } = json_response_and_validate_schema(conn, 200) + + token_from_db = Repo.get_by(Token, token: token) + assert token_from_db + user = Repo.preload(token_from_db, :user).user + + assert user + refute user.confirmation_pending + refute user.approval_pending + end + + test "registers but does not log in with :account_activation_required", %{conn: conn} do + clear_config([:instance, :account_activation_required], true) + clear_config([:instance, :account_approval_required], false) - test "Account registration via Application", %{conn: conn} do conn = conn |> put_req_header("content-type", "application/json") @@ -867,19 +1034,76 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do agreement: true }) - %{ - "access_token" => token, - "created_at" => _created_at, - "scope" => _scope, - "token_type" => "Bearer" - } = json_response_and_validate_schema(conn, 200) + response = json_response_and_validate_schema(conn, 200) + assert %{"identifier" => "missing_confirmed_email"} = response + refute response["access_token"] + refute response["token_type"] + + user = Repo.get_by(User, email: "lain@example.org") + assert user.confirmation_pending + end + test "registers but does not log in with :account_approval_required", %{conn: conn} do + clear_config([:instance, :account_approval_required], true) + clear_config([:instance, :account_activation_required], false) + + conn = + 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" + }) + + 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", %{ + grant_type: "client_credentials", + client_id: client_id, + client_secret: client_secret + }) + + assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} = + json_response(conn, 200) + + assert token token_from_db = Repo.get_by(Token, token: token) assert token_from_db - token_from_db = Repo.preload(token_from_db, :user) - assert token_from_db.user + assert refresh + assert scope == "read write follow" - assert token_from_db.user.confirmation_pending + conn = + build_conn() + |> put_req_header("content-type", "multipart/form-data") + |> put_req_header("authorization", "Bearer " <> token) + |> post("/api/v1/accounts", %{ + username: "lain", + email: "lain@example.org", + password: "PlzDontHackLain", + bio: "Test Bio", + agreement: true, + reason: "I'm a cool dude, bro" + }) + + response = json_response_and_validate_schema(conn, 200) + assert %{"identifier" => "awaiting_approval"} = response + refute response["access_token"] + refute response["token_type"] + + user = Repo.get_by(User, email: "lain@example.org") + + assert user.approval_pending + assert user.registration_reason == "I'm a cool dude, bro" end test "returns error when user already registred", %{conn: conn, valid_params: valid_params} do @@ -933,11 +1157,9 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do end) end - setup do: clear_config([:instance, :account_activation_required]) - test "returns bad_request if missing email params when :account_activation_required is enabled", %{conn: conn, valid_params: valid_params} do - Pleroma.Config.put([:instance, :account_activation_required], true) + clear_config([:instance, :account_activation_required], true) app_token = insert(:oauth_token, user: nil) @@ -1032,7 +1254,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do assert %{ "access_token" => access_token, "created_at" => _, - "scope" => ["read", "write", "follow", "push"], + "scope" => "read write follow push", "token_type" => "Bearer" } = response @@ -1102,8 +1324,6 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do assert token_from_db token_from_db = Repo.preload(token_from_db, :user) assert token_from_db.user - - assert token_from_db.user.confirmation_pending end conn = @@ -1150,7 +1370,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do assert %{ "access_token" => access_token, "created_at" => _, - "scope" => ["read"], + "scope" => "read", "token_type" => "Bearer" } = conn diff --git a/test/web/mastodon_api/controllers/auth_controller_test.exs b/test/web/mastodon_api/controllers/auth_controller_test.exs index a485f8e41..4fa95fce1 100644 --- a/test/web/mastodon_api/controllers/auth_controller_test.exs +++ b/test/web/mastodon_api/controllers/auth_controller_test.exs @@ -122,17 +122,27 @@ defmodule Pleroma.Web.MastodonAPI.AuthControllerTest do {:ok, user: user} end - test "it returns 404 when user is not found", %{conn: conn, user: user} do + test "it returns 204 when user is not found", %{conn: conn, user: user} do conn = post(conn, "/auth/password?email=nonexisting_#{user.email}") - assert conn.status == 404 - assert conn.resp_body == "" + + assert conn + |> json_response(:no_content) end - test "it returns 400 when user is not local", %{conn: conn, user: user} do + test "it returns 204 when user is not local", %{conn: conn, user: user} do {:ok, user} = Repo.update(Ecto.Changeset.change(user, local: false)) conn = post(conn, "/auth/password?email=#{user.email}") - assert conn.status == 400 - assert conn.resp_body == "" + + assert conn + |> json_response(:no_content) + end + + test "it returns 204 when user is deactivated", %{conn: conn, user: user} do + {:ok, user} = Repo.update(Ecto.Changeset.change(user, deactivated: true, local: true)) + conn = post(conn, "/auth/password?email=#{user.email}") + + assert conn + |> json_response(:no_content) end end 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 01a24afcf..664654500 100644 --- a/test/web/mastodon_api/controllers/domain_block_controller_test.exs +++ b/test/web/mastodon_api/controllers/domain_block_controller_test.exs @@ -32,6 +32,38 @@ defmodule Pleroma.Web.MastodonAPI.DomainBlockControllerTest do refute User.blocks?(user, other_user) end + test "blocking a domain via query params" do + %{user: user, conn: conn} = oauth_access(["write:blocks"]) + other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"}) + + ret_conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/domain_blocks?domain=dogwhistle.zone") + + 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) + end + + test "unblocking a domain via query params" do + %{user: user, conn: conn} = oauth_access(["write:blocks"]) + other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"}) + + User.block_domain(user, "dogwhistle.zone") + user = refresh_record(user) + assert User.blocks?(user, other_user) + + ret_conn = + conn + |> put_req_header("content-type", "application/json") + |> delete("/api/v1/domain_blocks?domain=dogwhistle.zone") + + 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 + test "getting a list of domain blocks" do %{user: user, conn: conn} = oauth_access(["read:blocks"]) diff --git a/test/web/mastodon_api/controllers/filter_controller_test.exs b/test/web/mastodon_api/controllers/filter_controller_test.exs index f29547d13..0d426ec34 100644 --- a/test/web/mastodon_api/controllers/filter_controller_test.exs +++ b/test/web/mastodon_api/controllers/filter_controller_test.exs @@ -64,11 +64,31 @@ defmodule Pleroma.Web.MastodonAPI.FilterControllerTest do test "get a filter" do %{user: user, conn: conn} = oauth_access(["read:filters"]) + # check whole_word false query = %Pleroma.Filter{ user_id: user.id, filter_id: 2, phrase: "knight", - context: ["home"] + context: ["home"], + whole_word: false + } + + {:ok, filter} = Pleroma.Filter.create(query) + + conn = get(conn, "/api/v1/filters/#{filter.filter_id}") + + assert response = json_response_and_validate_schema(conn, 200) + assert response["whole_word"] == false + + # check whole_word true + %{user: user, conn: conn} = oauth_access(["read:filters"]) + + query = %Pleroma.Filter{ + user_id: user.id, + filter_id: 3, + phrase: "knight", + context: ["home"], + whole_word: true } {:ok, filter} = Pleroma.Filter.create(query) @@ -76,6 +96,7 @@ defmodule Pleroma.Web.MastodonAPI.FilterControllerTest do conn = get(conn, "/api/v1/filters/#{filter.filter_id}") assert response = json_response_and_validate_schema(conn, 200) + assert response["whole_word"] == true end test "update a filter" do @@ -86,7 +107,8 @@ defmodule Pleroma.Web.MastodonAPI.FilterControllerTest do filter_id: 2, phrase: "knight", context: ["home"], - hide: true + hide: true, + whole_word: true } {:ok, _filter} = Pleroma.Filter.create(query) @@ -108,6 +130,7 @@ defmodule Pleroma.Web.MastodonAPI.FilterControllerTest do assert response["phrase"] == new.phrase assert response["context"] == new.context assert response["irreversible"] == true + assert response["whole_word"] == true end test "delete a filter" do 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 44e12d15a..6749e0e83 100644 --- a/test/web/mastodon_api/controllers/follow_request_controller_test.exs +++ b/test/web/mastodon_api/controllers/follow_request_controller_test.exs @@ -6,7 +6,7 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestControllerTest do use Pleroma.Web.ConnCase alias Pleroma.User - alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.CommonAPI import Pleroma.Factory @@ -20,7 +20,7 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestControllerTest do test "/api/v1/follow_requests works", %{user: user, conn: conn} do other_user = insert(:user) - {:ok, _activity} = ActivityPub.follow(other_user, user) + {:ok, _, _, _activity} = CommonAPI.follow(other_user, user) {:ok, other_user} = User.follow(other_user, user, :follow_pending) assert User.following?(other_user, user) == false @@ -34,7 +34,7 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestControllerTest do test "/api/v1/follow_requests/:id/authorize works", %{user: user, conn: conn} do other_user = insert(:user) - {:ok, _activity} = ActivityPub.follow(other_user, user) + {:ok, _, _, _activity} = CommonAPI.follow(other_user, user) {:ok, other_user} = User.follow(other_user, user, :follow_pending) user = User.get_cached_by_id(user.id) @@ -56,7 +56,7 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestControllerTest do test "/api/v1/follow_requests/:id/reject works", %{user: user, conn: conn} do other_user = insert(:user) - {:ok, _activity} = ActivityPub.follow(other_user, user) + {:ok, _, _, _activity} = CommonAPI.follow(other_user, user) 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 8bdfdddd1..6a9ccd979 100644 --- a/test/web/mastodon_api/controllers/instance_controller_test.exs +++ b/test/web/mastodon_api/controllers/instance_controller_test.exs @@ -27,16 +27,21 @@ defmodule Pleroma.Web.MastodonAPI.InstanceControllerTest do "thumbnail" => _, "languages" => _, "registrations" => _, + "approval_required" => _, "poll_limits" => _, "upload_limit" => _, "avatar_upload_limit" => _, "background_upload_limit" => _, "banner_upload_limit" => _, - "background_image" => _ + "background_image" => _, + "chat_limit" => _, + "description_limit" => _ } = result + assert result["pleroma"]["metadata"]["account_activation_required"] != nil assert result["pleroma"]["metadata"]["features"] assert result["pleroma"]["metadata"]["federation"] + assert result["pleroma"]["metadata"]["fields_limits"] assert result["pleroma"]["vapid_public_key"] assert email == from_config_email diff --git a/test/web/mastodon_api/controllers/list_controller_test.exs b/test/web/mastodon_api/controllers/list_controller_test.exs index 57a9ef4a4..091ec006c 100644 --- a/test/web/mastodon_api/controllers/list_controller_test.exs +++ b/test/web/mastodon_api/controllers/list_controller_test.exs @@ -67,7 +67,7 @@ defmodule Pleroma.Web.MastodonAPI.ListControllerTest do assert following == [other_user.follower_address] end - test "removing users from a list" do + test "removing users from a list, body params" do %{user: user, conn: conn} = oauth_access(["write:lists"]) other_user = insert(:user) third_user = insert(:user) @@ -85,6 +85,24 @@ defmodule Pleroma.Web.MastodonAPI.ListControllerTest do assert following == [third_user.follower_address] end + test "removing users from a list, query params" do + %{user: user, conn: conn} = oauth_access(["write:lists"]) + other_user = insert(:user) + third_user = insert(:user) + {:ok, list} = Pleroma.List.create("name", user) + {:ok, list} = Pleroma.List.follow(list, other_user) + {:ok, list} = Pleroma.List.follow(list, third_user) + + 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) + + %Pleroma.List{following: following} = Pleroma.List.get(list.id, user) + assert following == [third_user.follower_address] + end + test "listing users in a list" do %{user: user, conn: conn} = oauth_access(["read:lists"]) other_user = insert(:user) diff --git a/test/web/mastodon_api/controllers/search_controller_test.exs b/test/web/mastodon_api/controllers/search_controller_test.exs index c605957b1..04dc6f445 100644 --- a/test/web/mastodon_api/controllers/search_controller_test.exs +++ b/test/web/mastodon_api/controllers/search_controller_test.exs @@ -79,6 +79,7 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do assert status["id"] == to_string(activity.id) end + @tag capture_log: true test "constructs hashtags from search query", %{conn: conn} do results = conn @@ -151,6 +152,22 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do ] end + test "supports pagination of hashtags search results", %{conn: conn} do + results = + conn + |> get( + "/api/v2/search?#{ + URI.encode_query(%{q: "#some #text #with #hashtags", limit: 2, offset: 1}) + }" + ) + |> json_response_and_validate_schema(200) + + assert results["hashtags"] == [ + %{"name" => "text", "url" => "#{Web.base_url()}/tag/text"}, + %{"name" => "with", "url" => "#{Web.base_url()}/tag/with"} + ] + end + test "excludes a blocked users from search results", %{conn: conn} do user = insert(:user) user_smith = insert(:user, %{nickname: "Agent", name: "I love 2hu"}) @@ -265,18 +282,18 @@ 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 http://mastodon.example.org/@admin/99541947525187367" }) results = conn - |> get("/api/v1/search?q=https://shitposter.club/notice/2827873") + |> get("/api/v1/search?q=http://mastodon.example.org/@admin/99541947525187367") |> json_response_and_validate_schema(200) - [status, %{"id" => ^activity_id}] = results["statuses"] - - assert status["uri"] == - "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment" + assert [ + %{"url" => "http://mastodon.example.org/@admin/99541947525187367"}, + %{"id" => ^activity_id} + ] = results["statuses"] end) end @@ -302,11 +319,13 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do test "search fetches remote accounts", %{conn: conn} do user = insert(:user) + query = URI.encode_query(%{q: " mike@osada.macgirvin.com ", resolve: true}) + results = conn |> assign(:user, user) |> assign(:token, insert(:oauth_token, user: user, scopes: ["read"])) - |> get("/api/v1/search?q=mike@osada.macgirvin.com&resolve=true") + |> get("/api/v1/search?#{query}") |> json_response_and_validate_schema(200) [account] = results["accounts"] diff --git a/test/web/mastodon_api/controllers/status_controller_test.exs b/test/web/mastodon_api/controllers/status_controller_test.exs index 648e6f2ce..633a25e50 100644 --- a/test/web/mastodon_api/controllers/status_controller_test.exs +++ b/test/web/mastodon_api/controllers/status_controller_test.exs @@ -4,9 +4,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do use Pleroma.Web.ConnCase + use Oban.Testing, repo: Pleroma.Repo alias Pleroma.Activity - alias Pleroma.ActivityExpiration alias Pleroma.Config alias Pleroma.Conversation.Participation alias Pleroma.Object @@ -22,13 +22,15 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do setup do: clear_config([:instance, :federating]) setup do: clear_config([:instance, :allow_relay]) setup do: clear_config([:rich_media, :enabled]) + setup do: clear_config([:mrf, :policies]) + setup do: clear_config([:mrf_keyword, :reject]) describe "posting statuses" do setup do: oauth_access(["write:statuses"]) test "posting a status does not increment reblog_count when relaying", %{conn: conn} do - Pleroma.Config.put([:instance, :federating], true) - Pleroma.Config.get([:instance, :allow_relay], true) + Config.put([:instance, :federating], true) + Config.get([:instance, :allow_relay], true) response = conn @@ -101,7 +103,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do # An activity that will expire: # 2 hours - expires_in = 120 * 60 + expires_in = 2 * 60 * 60 + + expires_at = DateTime.add(DateTime.utc_now(), expires_in) conn_four = conn @@ -111,29 +115,22 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do "expires_in" => expires_in }) - 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) + assert %{"id" => fourth_id} = json_response_and_validate_schema(conn_four, 200) - estimated_expires_at = - NaiveDateTime.utc_now() - |> NaiveDateTime.add(expires_in) - |> NaiveDateTime.truncate(:second) + assert Activity.get_by_id(fourth_id) - # This assert will fail if the test takes longer than a minute. I sure hope it never does: - assert abs(NaiveDateTime.diff(expiration.scheduled_at, estimated_expires_at, :second)) < 60 - - assert fourth_response["pleroma"]["expires_at"] == - NaiveDateTime.to_iso8601(expiration.scheduled_at) + assert_enqueued( + worker: Pleroma.Workers.PurgeExpiredActivity, + args: %{activity_id: fourth_id}, + scheduled_at: expires_at + ) end test "it fails to create a status if `expires_in` is less or equal than an hour", %{ conn: conn } do - # 1 hour - expires_in = 60 * 60 + # 1 minute + expires_in = 1 * 60 assert %{"error" => "Expiry date is too soon"} = conn @@ -144,8 +141,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do }) |> json_response_and_validate_schema(422) - # 30 minutes - expires_in = 30 * 60 + # 5 minutes + expires_in = 5 * 60 assert %{"error" => "Expiry date is too soon"} = conn @@ -157,6 +154,17 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do |> json_response_and_validate_schema(422) end + test "Get MRF reason when posting a status is rejected by one", %{conn: conn} do + Config.put([:mrf_keyword, :reject], ["GNO"]) + Config.put([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.KeywordPolicy]) + + assert %{"error" => "[KeywordPolicy] Matches with rejected keyword"} = + conn + |> put_req_header("content-type", "application/json") + |> post("api/v1/statuses", %{"status" => "GNO/Linux"}) + |> json_response_and_validate_schema(422) + end + test "posting an undefined status with an attachment", %{user: user, conn: conn} do file = %Plug.Upload{ content_type: "image/jpg", @@ -283,9 +291,45 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do assert real_status == fake_status end + test "fake statuses' preview card is not cached", %{conn: conn} do + clear_config([:rich_media, :enabled], true) + + Tesla.Mock.mock(fn + %{ + method: :get, + url: "https://example.com/twitter-card" + } -> + %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/twitter_card.html")} + + env -> + apply(HttpRequestMock, :request, [env]) + end) + + conn1 = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses", %{ + "status" => "https://example.com/ogp", + "preview" => true + }) + + conn2 = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses", %{ + "status" => "https://example.com/twitter-card", + "preview" => true + }) + + assert %{"card" => %{"title" => "The Rock"}} = json_response_and_validate_schema(conn1, 200) + + assert %{"card" => %{"title" => "Small Island Developing States Photo Submission"}} = + json_response_and_validate_schema(conn2, 200) + end + test "posting a status with OGP link preview", %{conn: conn} do Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end) - Config.put([:rich_media, :enabled], true) + clear_config([:rich_media, :enabled], true) conn = conn @@ -760,13 +804,18 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do test "when you created it" do %{user: author, conn: conn} = oauth_access(["write:statuses"]) activity = insert(:note_activity, user: author) + object = Object.normalize(activity) - conn = + content = object.data["content"] + source = object.data["source"] + + result = conn |> assign(:user, author) |> delete("/api/v1/statuses/#{activity.id}") + |> json_response_and_validate_schema(200) - assert %{} = json_response_and_validate_schema(conn, 200) + assert match?(%{"content" => ^content, "text" => ^source}, result) refute Activity.get_by_id(activity.id) end @@ -789,7 +838,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do conn = delete(conn, "/api/v1/statuses/#{activity.id}") - assert %{"error" => _} = json_response_and_validate_schema(conn, 403) + assert %{"error" => "Record not found"} == json_response_and_validate_schema(conn, 404) assert Activity.get_by_id(activity.id) == activity end @@ -1092,6 +1141,52 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do |> post("/api/v1/statuses/#{activity_two.id}/pin") |> json_response_and_validate_schema(400) end + + test "on pin removes deletion job, on unpin reschedule deletion" do + %{conn: conn} = oauth_access(["write:accounts", "write:statuses"]) + expires_in = 2 * 60 * 60 + + expires_at = DateTime.add(DateTime.utc_now(), expires_in) + + assert %{"id" => id} = + conn + |> put_req_header("content-type", "application/json") + |> post("api/v1/statuses", %{ + "status" => "oolong", + "expires_in" => expires_in + }) + |> json_response_and_validate_schema(200) + + assert_enqueued( + worker: Pleroma.Workers.PurgeExpiredActivity, + args: %{activity_id: id}, + scheduled_at: expires_at + ) + + assert %{"id" => ^id, "pinned" => true} = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses/#{id}/pin") + |> json_response_and_validate_schema(200) + + refute_enqueued( + worker: Pleroma.Workers.PurgeExpiredActivity, + args: %{activity_id: id}, + scheduled_at: expires_at + ) + + assert %{"id" => ^id, "pinned" => false} = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses/#{id}/unpin") + |> json_response_and_validate_schema(200) + + assert_enqueued( + worker: Pleroma.Workers.PurgeExpiredActivity, + args: %{activity_id: id}, + scheduled_at: expires_at + ) + end end describe "cards" do @@ -1414,6 +1509,20 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do [%{"id" => id}] = response assert id == other_user.id end + + test "returns empty array when :show_reactions is disabled", %{conn: conn, activity: activity} do + clear_config([:instance, :show_reactions], false) + + other_user = insert(:user) + {:ok, _} = CommonAPI.favorite(other_user, activity.id) + + response = + conn + |> get("/api/v1/statuses/#{activity.id}/favourited_by") + |> json_response_and_validate_schema(:ok) + + assert Enum.empty?(response) + end end describe "GET /api/v1/statuses/:id/reblogged_by" do @@ -1561,7 +1670,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do # Using the header for pagination works correctly [next, _] = get_resp_header(result, "link") |> hd() |> String.split(", ") - [_, max_id] = Regex.run(~r/max_id=(.*)>;/, next) + [_, max_id] = Regex.run(~r/max_id=([^&]+)/, next) assert max_id == third_favorite.id @@ -1613,19 +1722,17 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do test "expires_at is nil for another user" do %{conn: conn, user: user} = oauth_access(["read:statuses"]) + expires_at = DateTime.add(DateTime.utc_now(), 1_000_000) {:ok, activity} = CommonAPI.post(user, %{status: "foobar", expires_in: 1_000_000}) - expires_at = - activity.id - |> ActivityExpiration.get_by_activity_id() - |> Map.get(:scheduled_at) - |> NaiveDateTime.to_iso8601() - - assert %{"pleroma" => %{"expires_at" => ^expires_at}} = + assert %{"pleroma" => %{"expires_at" => a_expires_at}} = conn |> get("/api/v1/statuses/#{activity.id}") |> json_response_and_validate_schema(:ok) + {:ok, a_expires_at, 0} = DateTime.from_iso8601(a_expires_at) + assert DateTime.diff(expires_at, a_expires_at) == 0 + %{conn: conn} = oauth_access(["read:statuses"]) assert %{"pleroma" => %{"expires_at" => nil}} = diff --git a/test/web/mastodon_api/controllers/timeline_controller_test.exs b/test/web/mastodon_api/controllers/timeline_controller_test.exs index f069390c1..517cabcff 100644 --- a/test/web/mastodon_api/controllers/timeline_controller_test.exs +++ b/test/web/mastodon_api/controllers/timeline_controller_test.exs @@ -333,6 +333,46 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do describe "list" do setup do: oauth_access(["read:lists"]) + test "does not contain retoots", %{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 stupid."}) + {:ok, _} = CommonAPI.repeat(activity_one.id, other_user) + + {: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_and_validate_schema(conn, :ok) + + assert id == to_string(activity_two.id) + end + + test "works with pagination", %{user: user, conn: conn} do + other_user = insert(:user) + {:ok, list} = Pleroma.List.create("name", user) + {:ok, list} = Pleroma.List.follow(list, other_user) + + Enum.each(1..30, fn i -> + CommonAPI.post(other_user, %{status: "post number #{i}"}) + end) + + res = + get(conn, "/api/v1/timelines/list/#{list.id}?limit=1") + |> json_response_and_validate_schema(:ok) + + assert length(res) == 1 + + [first] = res + + res = + get(conn, "/api/v1/timelines/list/#{list.id}?max_id=#{first["id"]}&limit=30") + |> json_response_and_validate_schema(:ok) + + assert length(res) == 29 + end + test "list timeline", %{user: user, conn: conn} do other_user = insert(:user) {:ok, _activity_one} = CommonAPI.post(user, %{status: "Marisa is cute."}) @@ -418,4 +458,95 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do assert [status_none] == json_response_and_validate_schema(all_test, :ok) end end + + describe "hashtag timeline handling of :restrict_unauthenticated setting" do + setup do + user = insert(:user) + {:ok, activity1} = CommonAPI.post(user, %{status: "test #tag1"}) + {:ok, _activity2} = CommonAPI.post(user, %{status: "test #tag1"}) + + activity1 + |> Ecto.Changeset.change(%{local: false}) + |> Pleroma.Repo.update() + + base_uri = "/api/v1/timelines/tag/tag1" + error_response = %{"error" => "authorization required for timeline view"} + + %{base_uri: base_uri, error_response: error_response} + end + + defp ensure_authenticated_access(base_uri) do + %{conn: auth_conn} = oauth_access(["read:statuses"]) + + res_conn = get(auth_conn, "#{base_uri}?local=true") + assert length(json_response(res_conn, 200)) == 1 + + res_conn = get(auth_conn, "#{base_uri}?local=false") + assert length(json_response(res_conn, 200)) == 2 + end + + test "with default settings on private instances, returns 403 for unauthenticated users", %{ + conn: conn, + base_uri: base_uri, + error_response: error_response + } do + clear_config([:instance, :public], false) + clear_config([:restrict_unauthenticated, :timelines]) + + for local <- [true, false] do + res_conn = get(conn, "#{base_uri}?local=#{local}") + + assert json_response(res_conn, :unauthorized) == error_response + end + + ensure_authenticated_access(base_uri) + end + + test "with `%{local: true, federated: true}`, returns 403 for unauthenticated users", %{ + conn: conn, + base_uri: base_uri, + error_response: error_response + } do + clear_config([:restrict_unauthenticated, :timelines, :local], true) + clear_config([:restrict_unauthenticated, :timelines, :federated], true) + + for local <- [true, false] do + res_conn = get(conn, "#{base_uri}?local=#{local}") + + assert json_response(res_conn, :unauthorized) == error_response + end + + ensure_authenticated_access(base_uri) + end + + test "with `%{local: false, federated: true}`, forbids unauthenticated access to federated timeline", + %{conn: conn, base_uri: base_uri, error_response: error_response} do + clear_config([:restrict_unauthenticated, :timelines, :local], false) + clear_config([:restrict_unauthenticated, :timelines, :federated], true) + + res_conn = get(conn, "#{base_uri}?local=true") + assert length(json_response(res_conn, 200)) == 1 + + res_conn = get(conn, "#{base_uri}?local=false") + assert json_response(res_conn, :unauthorized) == error_response + + ensure_authenticated_access(base_uri) + end + + test "with `%{local: true, federated: false}`, forbids unauthenticated access to public timeline" <> + "(but not to local public activities which are delivered as part of federated timeline)", + %{conn: conn, base_uri: base_uri, error_response: error_response} do + clear_config([:restrict_unauthenticated, :timelines, :local], true) + clear_config([:restrict_unauthenticated, :timelines, :federated], false) + + res_conn = get(conn, "#{base_uri}?local=true") + assert json_response(res_conn, :unauthorized) == error_response + + # Note: local activities get delivered as part of federated timeline + res_conn = get(conn, "#{base_uri}?local=false") + assert length(json_response(res_conn, 200)) == 2 + + ensure_authenticated_access(base_uri) + end + end end diff --git a/test/web/mastodon_api/mastodon_api_test.exs b/test/web/mastodon_api/mastodon_api_test.exs index a7f9c5205..0c5a38bf6 100644 --- a/test/web/mastodon_api/mastodon_api_test.exs +++ b/test/web/mastodon_api/mastodon_api_test.exs @@ -17,8 +17,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPITest do test "returns error when followed user is deactivated" do follower = insert(:user) user = insert(:user, local: true, deactivated: true) - {:error, error} = MastodonAPI.follow(follower, user) - assert error == "Could not follow user: #{user.nickname} is deactivated." + assert {:error, _error} = MastodonAPI.follow(follower, user) end test "following for user" do diff --git a/test/web/mastodon_api/views/account_view_test.exs b/test/web/mastodon_api/views/account_view_test.exs index 044f088a4..9f22f9dcf 100644 --- a/test/web/mastodon_api/views/account_view_test.exs +++ b/test/web/mastodon_api/views/account_view_test.exs @@ -33,7 +33,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do 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], - emoji: %{"karjalanpiirakka" => "/file.png"} + emoji: %{"karjalanpiirakka" => "/file.png"}, + raw_bio: "valid html. a\nb\nc\nd\nf '&<>\"" }) expected = %{ @@ -74,6 +75,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do pleroma: %{ ap_id: user.ap_id, background_image: "https://example.com/images/asuka_hospital.png", + favicon: nil, confirmation_pending: false, tags: [], is_admin: false, @@ -84,22 +86,42 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do hide_followers_count: false, hide_follows_count: false, relationship: %{}, - skip_thread_containment: false + skip_thread_containment: false, + accepts_chat_messages: nil } } - assert expected == AccountView.render("show.json", %{user: user}) + assert expected == AccountView.render("show.json", %{user: user, skip_visibility_check: true}) + end + + describe "favicon" do + setup do + [user: insert(:user)] + end + + test "is parsed when :instance_favicons is enabled", %{user: user} do + clear_config([:instances_favicons, :enabled], true) + + assert %{ + pleroma: %{ + favicon: + "https://shitposter.club/plugins/Qvitter/img/gnusocial-favicons/favicon-16x16.png" + } + } = AccountView.render("show.json", %{user: user, skip_visibility_check: true}) + end + + test "is nil when :instances_favicons is disabled", %{user: user} do + assert %{pleroma: %{favicon: nil}} = + AccountView.render("show.json", %{user: user, skip_visibility_check: true}) + end end test "Represent the user account for the account owner" do user = insert(:user) notification_settings = %{ - followers: true, - follows: true, - non_followers: true, - non_follows: true, - privacy_option: false + block_from_strangers: false, + hide_notification_contents: false } privacy = user.default_scope @@ -151,6 +173,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do pleroma: %{ ap_id: user.ap_id, background_image: nil, + favicon: nil, confirmation_pending: false, tags: [], is_admin: false, @@ -161,11 +184,12 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do hide_followers_count: false, hide_follows_count: false, relationship: %{}, - skip_thread_containment: false + skip_thread_containment: false, + accepts_chat_messages: nil } } - assert expected == AccountView.render("show.json", %{user: user}) + assert expected == AccountView.render("show.json", %{user: user, skip_visibility_check: true}) end test "Represent a Funkwhale channel" do @@ -174,7 +198,9 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do "https://channels.tests.funkwhale.audio/federation/actors/compositions" ) - assert represented = AccountView.render("show.json", %{user: user}) + assert represented = + AccountView.render("show.json", %{user: user, skip_visibility_check: true}) + assert represented.acct == "compositions@channels.tests.funkwhale.audio" assert represented.url == "https://channels.tests.funkwhale.audio/channels/compositions" end @@ -199,6 +225,23 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do assert expected == AccountView.render("mention.json", %{user: user}) end + test "demands :for or :skip_visibility_check option for account rendering" do + clear_config([:restrict_unauthenticated, :profiles, :local], false) + + user = insert(:user) + user_id = user.id + + assert %{id: ^user_id} = AccountView.render("show.json", %{user: user, for: nil}) + assert %{id: ^user_id} = AccountView.render("show.json", %{user: user, for: user}) + + assert %{id: ^user_id} = + AccountView.render("show.json", %{user: user, skip_visibility_check: true}) + + assert_raise RuntimeError, ~r/:skip_visibility_check or :for option is required/, fn -> + AccountView.render("show.json", %{user: user}) + end + end + describe "relationship" do defp test_relationship_rendering(user, other_user, expected_result) do opts = %{user: user, target: other_user, relationships: nil} @@ -312,7 +355,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do assert result.pleroma.settings_store == %{:fe => "test"} - result = AccountView.render("show.json", %{user: user, with_pleroma_settings: true}) + result = AccountView.render("show.json", %{user: user, for: nil, with_pleroma_settings: true}) assert result.pleroma[:settings_store] == nil result = AccountView.render("show.json", %{user: user, for: user}) @@ -321,13 +364,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do test "doesn't sanitize display names" do user = insert(:user, name: "<marquee> username </marquee>") - result = AccountView.render("show.json", %{user: user}) + result = AccountView.render("show.json", %{user: user, skip_visibility_check: true}) assert result.display_name == "<marquee> username </marquee>" end test "never display nil user follow counts" do user = insert(:user, following_count: 0, follower_count: 0) - result = AccountView.render("show.json", %{user: user}) + result = AccountView.render("show.json", %{user: user, skip_visibility_check: true}) assert result.following_count == 0 assert result.followers_count == 0 @@ -351,7 +394,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do followers_count: 0, following_count: 0, pleroma: %{hide_follows_count: true, hide_followers_count: true} - } = AccountView.render("show.json", %{user: user}) + } = AccountView.render("show.json", %{user: user, skip_visibility_check: true}) end test "shows when follows/followers are hidden" do @@ -364,13 +407,16 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do followers_count: 1, following_count: 1, pleroma: %{hide_follows: true, hide_followers: true} - } = AccountView.render("show.json", %{user: user}) + } = AccountView.render("show.json", %{user: user, skip_visibility_check: true}) end test "shows actual follower/following count to the account owner" do user = insert(:user, hide_followers: true, hide_follows: true) other_user = insert(:user) {:ok, user, other_user, _activity} = CommonAPI.follow(user, other_user) + + assert User.following?(user, other_user) + assert Pleroma.FollowingRelationship.follower_count(other_user) == 1 {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user) assert %{ @@ -504,7 +550,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do emoji: %{"joker_smile" => "https://evil.website/society.png"} ) - AccountView.render("show.json", %{user: user}) + AccountView.render("show.json", %{user: user, skip_visibility_check: true}) |> Enum.all?(fn {key, url} when key in [:avatar, :avatar_static, :header, :header_static] -> String.starts_with?(url, Pleroma.Web.base_url()) diff --git a/test/web/mastodon_api/views/conversation_view_test.exs b/test/web/mastodon_api/views/conversation_view_test.exs index 6f84366f8..2e8203c9b 100644 --- a/test/web/mastodon_api/views/conversation_view_test.exs +++ b/test/web/mastodon_api/views/conversation_view_test.exs @@ -15,8 +15,17 @@ defmodule Pleroma.Web.MastodonAPI.ConversationViewTest do user = insert(:user) other_user = insert(:user) + {:ok, parent} = CommonAPI.post(user, %{status: "parent"}) + {:ok, activity} = - CommonAPI.post(user, %{status: "hey @#{other_user.nickname}", visibility: "direct"}) + CommonAPI.post(user, %{ + status: "hey @#{other_user.nickname}", + visibility: "direct", + in_reply_to_id: parent.id + }) + + {:ok, _reply_activity} = + CommonAPI.post(user, %{status: "hu", visibility: "public", in_reply_to_id: parent.id}) [participation] = Participation.for_user_with_last_activity_id(user) diff --git a/test/web/mastodon_api/views/notification_view_test.exs b/test/web/mastodon_api/views/notification_view_test.exs index 9c399b2df..2f6a808f1 100644 --- a/test/web/mastodon_api/views/notification_view_test.exs +++ b/test/web/mastodon_api/views/notification_view_test.exs @@ -49,7 +49,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do expected = %{ id: to_string(notification.id), - pleroma: %{is_seen: false}, + pleroma: %{is_seen: false, is_muted: false}, type: "pleroma:chat_mention", account: AccountView.render("show.json", %{user: user, for: recipient}), chat_message: MessageReferenceView.render("show.json", %{chat_message_reference: cm_ref}), @@ -68,7 +68,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do expected = %{ id: to_string(notification.id), - pleroma: %{is_seen: false}, + pleroma: %{is_seen: false, is_muted: false}, type: "mention", account: AccountView.render("show.json", %{ @@ -92,7 +92,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do expected = %{ id: to_string(notification.id), - pleroma: %{is_seen: false}, + pleroma: %{is_seen: false, is_muted: false}, type: "favourite", account: AccountView.render("show.json", %{user: another_user, for: user}), status: StatusView.render("show.json", %{activity: create_activity, for: user}), @@ -112,7 +112,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do expected = %{ id: to_string(notification.id), - pleroma: %{is_seen: false}, + pleroma: %{is_seen: false, is_muted: false}, type: "reblog", account: AccountView.render("show.json", %{user: another_user, for: user}), status: StatusView.render("show.json", %{activity: reblog_activity, for: user}), @@ -130,7 +130,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do expected = %{ id: to_string(notification.id), - pleroma: %{is_seen: false}, + pleroma: %{is_seen: false, is_muted: false}, type: "follow", account: AccountView.render("show.json", %{user: follower, for: followed}), created_at: Utils.to_masto_date(notification.inserted_at) @@ -171,7 +171,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do expected = %{ id: to_string(notification.id), - pleroma: %{is_seen: false}, + pleroma: %{is_seen: false, is_muted: false}, type: "move", account: AccountView.render("show.json", %{user: old_user, for: follower}), target: AccountView.render("show.json", %{user: new_user, for: follower}), @@ -196,7 +196,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do expected = %{ id: to_string(notification.id), - pleroma: %{is_seen: false}, + pleroma: %{is_seen: false, is_muted: false}, type: "pleroma:emoji_reaction", emoji: "☕", account: AccountView.render("show.json", %{user: other_user, for: user}), @@ -206,4 +206,26 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do test_notifications_rendering([notification], user, [expected]) end + + test "muted notification" do + user = insert(:user) + another_user = insert(:user) + + {:ok, _} = Pleroma.UserRelationship.create_mute(user, 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) + + expected = %{ + id: to_string(notification.id), + pleroma: %{is_seen: true, is_muted: true}, + type: "favourite", + account: AccountView.render("show.json", %{user: another_user, for: user}), + status: StatusView.render("show.json", %{activity: create_activity, for: user}), + created_at: Utils.to_masto_date(notification.inserted_at) + } + + 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 76672f36c..b7e2f17ef 100644 --- a/test/web/mastodon_api/views/poll_view_test.exs +++ b/test/web/mastodon_api/views/poll_view_test.exs @@ -135,4 +135,33 @@ defmodule Pleroma.Web.MastodonAPI.PollViewTest do assert result[:expires_at] == nil assert result[:expired] == false end + + test "doesn't strips HTML tags" do + user = insert(:user) + + {:ok, activity} = + CommonAPI.post(user, %{ + status: "What's with the smug face?", + poll: %{ + options: [ + "<input type=\"date\">", + "<input type=\"date\" >", + "<input type=\"date\"/>", + "<input type=\"date\"></input>" + ], + expires_in: 20 + } + }) + + object = Object.normalize(activity) + + assert %{ + options: [ + %{title: "<input type=\"date\">", votes_count: 0}, + %{title: "<input type=\"date\" >", votes_count: 0}, + %{title: "<input type=\"date\"/>", votes_count: 0}, + %{title: "<input type=\"date\"></input>", votes_count: 0} + ] + } = PollView.render("show.json", %{object: object}) + end end diff --git a/test/web/mastodon_api/views/status_view_test.exs b/test/web/mastodon_api/views/status_view_test.exs index 5cbadf0fc..70d829979 100644 --- a/test/web/mastodon_api/views/status_view_test.exs +++ b/test/web/mastodon_api/views/status_view_test.exs @@ -56,6 +56,23 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do ] end + test "works correctly with badly formatted emojis" do + user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{status: "yo"}) + + activity + |> Object.normalize(false) + |> Object.update_data(%{"reactions" => %{"☕" => [user.ap_id], "x" => 1}}) + + activity = Activity.get_by_id(activity.id) + + status = StatusView.render("show.json", activity: activity, for: user) + + assert status[:pleroma][:emoji_reactions] == [ + %{name: "☕", count: 1, me: true} + ] + end + test "loads and returns the direct conversation id when given the `with_direct_conversation_id` option" do user = insert(:user) @@ -177,12 +194,13 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do id: to_string(note.id), uri: object_data["id"], url: Pleroma.Web.Router.Helpers.o_status_url(Pleroma.Web.Endpoint, :notice, note), - account: AccountView.render("show.json", %{user: user}), + account: AccountView.render("show.json", %{user: user, skip_visibility_check: true}), in_reply_to_id: nil, in_reply_to_account_id: nil, card: nil, reblog: nil, content: HTML.filter_tags(object_data["content"]), + text: nil, created_at: created_at, reblogs_count: 0, replies_count: 0, @@ -226,7 +244,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do expires_at: nil, direct_conversation_id: nil, thread_muted: false, - emoji_reactions: [] + emoji_reactions: [], + parent_visible: false } } @@ -498,6 +517,12 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do represented = StatusView.render("show.json", %{for: user, activity: activity}) assert represented[:id] == to_string(activity.id) + + assert represented[:url] == + "https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39" + + assert represented[:content] == + "<p><a href=\"https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39\">Mobilizon Launching Party</a></p><p>Mobilizon is now federated! 🎉</p><p></p><p>You can view this event from other instances if they are subscribed to mobilizon.org, and soon directly from Mastodon and Pleroma. It is possible that you may see some comments from other instances, including Mastodon ones, just below.</p><p></p><p>With a Mobilizon account on an instance, you may <strong>participate</strong> at events from other instances and <strong>add comments</strong> on events.</p><p></p><p>Of course, it's still <u>a work in progress</u>: if reports made from an instance on events and comments can be federated, you can't block people right now, and moderators actions are rather limited, but this <strong>will definitely get fixed over time</strong> until first stable version next year.</p><p></p><p>Anyway, if you want to come up with some feedback, head over to our forum or - if you feel you have technical skills and are familiar with it - on our Gitlab repository.</p><p></p><p>Also, to people that want to set Mobilizon themselves even though we really don't advise to do that for now, we have a little documentation but it's quite the early days and you'll probably need some help. No worries, you can chat with us on our Forum or though our Matrix channel.</p><p></p><p>Check our website for more informations and follow us on Twitter or Mastodon.</p>" end describe "build_tags/1" do @@ -620,4 +645,20 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do assert status.visibility == "list" end + + test "has a field for parent visibility" do + user = insert(:user) + poster = insert(:user) + + {:ok, invisible} = CommonAPI.post(poster, %{status: "hey", visibility: "private"}) + + {:ok, visible} = + CommonAPI.post(poster, %{status: "hey", visibility: "private", in_reply_to_id: invisible.id}) + + status = StatusView.render("show.json", activity: visible, for: user) + refute status.pleroma.parent_visible + + status = StatusView.render("show.json", activity: visible, for: poster) + assert status.pleroma.parent_visible + end end diff --git a/test/web/media_proxy/invalidation_test.exs b/test/web/media_proxy/invalidation_test.exs new file mode 100644 index 000000000..926ae74ca --- /dev/null +++ b/test/web/media_proxy/invalidation_test.exs @@ -0,0 +1,64 @@ +defmodule Pleroma.Web.MediaProxy.InvalidationTest do + use ExUnit.Case + use Pleroma.Tests.Helpers + + alias Pleroma.Config + alias Pleroma.Web.MediaProxy.Invalidation + + import ExUnit.CaptureLog + import Mock + import Tesla.Mock + + setup do: clear_config([:media_proxy]) + + setup do + on_exit(fn -> Cachex.clear(:banned_urls_cache) end) + end + + describe "Invalidation.Http" do + test "perform request to clear cache" do + Config.put([:media_proxy, :enabled], false) + Config.put([:media_proxy, :invalidation, :enabled], true) + Config.put([:media_proxy, :invalidation, :provider], Invalidation.Http) + + Config.put([Invalidation.Http], method: :purge, headers: [{"x-refresh", 1}]) + image_url = "http://example.com/media/example.jpg" + Pleroma.Web.MediaProxy.put_in_banned_urls(image_url) + + mock(fn + %{ + method: :purge, + url: "http://example.com/media/example.jpg", + headers: [{"x-refresh", 1}] + } -> + %Tesla.Env{status: 200} + end) + + assert capture_log(fn -> + assert Pleroma.Web.MediaProxy.in_banned_urls(image_url) + assert Invalidation.purge([image_url]) == {:ok, [image_url]} + assert Pleroma.Web.MediaProxy.in_banned_urls(image_url) + end) =~ "Running cache purge: [\"#{image_url}\"]" + end + end + + describe "Invalidation.Script" do + test "run script to clear cache" do + Config.put([:media_proxy, :enabled], false) + Config.put([:media_proxy, :invalidation, :enabled], true) + Config.put([:media_proxy, :invalidation, :provider], Invalidation.Script) + Config.put([Invalidation.Script], script_path: "purge-nginx") + + image_url = "http://example.com/media/example.jpg" + Pleroma.Web.MediaProxy.put_in_banned_urls(image_url) + + with_mocks [{System, [], [cmd: fn _, _ -> {"ok", 0} end]}] do + assert capture_log(fn -> + assert Pleroma.Web.MediaProxy.in_banned_urls(image_url) + assert Invalidation.purge([image_url]) == {:ok, [image_url]} + assert Pleroma.Web.MediaProxy.in_banned_urls(image_url) + end) =~ "Running cache purge: [\"#{image_url}\"]" + end + end + end +end diff --git a/test/web/media_proxy/invalidations/http_test.exs b/test/web/media_proxy/invalidations/http_test.exs index 8a3b4141c..a1bef5237 100644 --- a/test/web/media_proxy/invalidations/http_test.exs +++ b/test/web/media_proxy/invalidations/http_test.exs @@ -5,6 +5,10 @@ defmodule Pleroma.Web.MediaProxy.Invalidation.HttpTest do import ExUnit.CaptureLog import Tesla.Mock + setup do + on_exit(fn -> Cachex.clear(:banned_urls_cache) end) + end + test "logs hasn't error message when request is valid" do mock(fn %{method: :purge, url: "http://example.com/media/example.jpg"} -> @@ -14,8 +18,8 @@ defmodule Pleroma.Web.MediaProxy.Invalidation.HttpTest do refute capture_log(fn -> assert Invalidation.Http.purge( ["http://example.com/media/example.jpg"], - %{} - ) == {:ok, "success"} + [] + ) == {:ok, ["http://example.com/media/example.jpg"]} end) =~ "Error while cache purge" end @@ -28,8 +32,8 @@ defmodule Pleroma.Web.MediaProxy.Invalidation.HttpTest do assert capture_log(fn -> assert Invalidation.Http.purge( ["http://example.com/media/example1.jpg"], - %{} - ) == {:ok, "success"} + [] + ) == {:ok, ["http://example.com/media/example1.jpg"]} end) =~ "Error while cache purge: url - http://example.com/media/example1.jpg" end end diff --git a/test/web/media_proxy/invalidations/script_test.exs b/test/web/media_proxy/invalidations/script_test.exs index 1358963ab..51833ab18 100644 --- a/test/web/media_proxy/invalidations/script_test.exs +++ b/test/web/media_proxy/invalidations/script_test.exs @@ -4,17 +4,23 @@ defmodule Pleroma.Web.MediaProxy.Invalidation.ScriptTest do import ExUnit.CaptureLog + setup do + on_exit(fn -> Cachex.clear(:banned_urls_cache) end) + end + test "it logger error when script not found" do assert capture_log(fn -> assert Invalidation.Script.purge( ["http://example.com/media/example.jpg"], - %{script_path: "./example"} - ) == {:error, "\"%ErlangError{original: :enoent}\""} - end) =~ "Error while cache purge: \"%ErlangError{original: :enoent}\"" + script_path: "./example" + ) == {:error, "%ErlangError{original: :enoent}"} + end) =~ "Error while cache purge: %ErlangError{original: :enoent}" - assert Invalidation.Script.purge( - ["http://example.com/media/example.jpg"], - %{} - ) == {:error, "not found script path"} + capture_log(fn -> + assert Invalidation.Script.purge( + ["http://example.com/media/example.jpg"], + [] + ) == {:error, "\"not found script path\""} + end) end end diff --git a/test/web/media_proxy/media_proxy_controller_test.exs b/test/web/media_proxy/media_proxy_controller_test.exs index da79d38a5..d4db44c63 100644 --- a/test/web/media_proxy/media_proxy_controller_test.exs +++ b/test/web/media_proxy/media_proxy_controller_test.exs @@ -4,66 +4,118 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyControllerTest do use Pleroma.Web.ConnCase + import Mock - alias Pleroma.Config - setup do: clear_config(:media_proxy) - setup do: clear_config([Pleroma.Web.Endpoint, :secret_key_base]) + alias Pleroma.Web.MediaProxy + alias Pleroma.Web.MediaProxy.MediaProxyController + alias Plug.Conn + + setup do + on_exit(fn -> Cachex.clear(:banned_urls_cache) end) + end test "it returns 404 when MediaProxy disabled", %{conn: conn} do - Config.put([:media_proxy, :enabled], false) + clear_config([:media_proxy, :enabled], false) - assert %Plug.Conn{ + assert %Conn{ status: 404, resp_body: "Not Found" } = get(conn, "/proxy/hhgfh/eeeee") - assert %Plug.Conn{ + assert %Conn{ status: 404, resp_body: "Not Found" } = get(conn, "/proxy/hhgfh/eeee/fff") end - test "it returns 403 when signature invalidated", %{conn: conn} do - Config.put([:media_proxy, :enabled], true) - Config.put([Pleroma.Web.Endpoint, :secret_key_base], "00000000000") - path = URI.parse(Pleroma.Web.MediaProxy.encode_url("https://google.fn")).path - Config.put([Pleroma.Web.Endpoint, :secret_key_base], "000") - - assert %Plug.Conn{ - status: 403, - resp_body: "Forbidden" - } = get(conn, path) - - assert %Plug.Conn{ - status: 403, - resp_body: "Forbidden" - } = get(conn, "/proxy/hhgfh/eeee") - - assert %Plug.Conn{ - status: 403, - resp_body: "Forbidden" - } = get(conn, "/proxy/hhgfh/eeee/fff") - end + describe "" do + setup do + clear_config([:media_proxy, :enabled], true) + clear_config([Pleroma.Web.Endpoint, :secret_key_base], "00000000000") + [url: MediaProxy.encode_url("https://google.fn/test.png")] + end + + test "it returns 403 for invalid signature", %{conn: conn, url: url} do + Pleroma.Config.put([Pleroma.Web.Endpoint, :secret_key_base], "000") + %{path: path} = URI.parse(url) + + assert %Conn{ + status: 403, + resp_body: "Forbidden" + } = get(conn, path) + + assert %Conn{ + status: 403, + resp_body: "Forbidden" + } = get(conn, "/proxy/hhgfh/eeee") + + assert %Conn{ + status: 403, + resp_body: "Forbidden" + } = get(conn, "/proxy/hhgfh/eeee/fff") + end - test "redirects on valid url when filename invalidated", %{conn: conn} do - Config.put([:media_proxy, :enabled], true) - Config.put([Pleroma.Web.Endpoint, :secret_key_base], "00000000000") - url = Pleroma.Web.MediaProxy.encode_url("https://google.fn/test.png") - invalid_url = String.replace(url, "test.png", "test-file.png") - response = get(conn, invalid_url) - assert response.status == 302 - assert redirected_to(response) == url + test "redirects on valid url when filename is invalidated", %{conn: conn, url: url} do + invalid_url = String.replace(url, "test.png", "test-file.png") + response = get(conn, invalid_url) + assert response.status == 302 + assert redirected_to(response) == url + end + + test "it performs ReverseProxy.call with valid signature", %{conn: conn, url: url} do + with_mock Pleroma.ReverseProxy, + call: fn _conn, _url, _opts -> %Conn{status: :success} end do + assert %Conn{status: :success} = get(conn, url) + end + end + + test "it returns 404 when url is in banned_urls cache", %{conn: conn, url: url} do + MediaProxy.put_in_banned_urls("https://google.fn/test.png") + + with_mock Pleroma.ReverseProxy, + call: fn _conn, _url, _opts -> %Conn{status: :success} end do + assert %Conn{status: 404, resp_body: "Not Found"} = get(conn, url) + end + end end - test "it performs ReverseProxy.call when signature valid", %{conn: conn} do - Config.put([:media_proxy, :enabled], true) - Config.put([Pleroma.Web.Endpoint, :secret_key_base], "00000000000") - url = Pleroma.Web.MediaProxy.encode_url("https://google.fn/test.png") + describe "filename_matches/3" do + test "preserves the encoded or decoded path" do + assert MediaProxyController.filename_matches( + %{"filename" => "/Hello world.jpg"}, + "/Hello world.jpg", + "http://pleroma.social/Hello world.jpg" + ) == :ok + + assert MediaProxyController.filename_matches( + %{"filename" => "/Hello%20world.jpg"}, + "/Hello%20world.jpg", + "http://pleroma.social/Hello%20world.jpg" + ) == :ok + + assert MediaProxyController.filename_matches( + %{"filename" => "/my%2Flong%2Furl%2F2019%2F07%2FS.jpg"}, + "/my%2Flong%2Furl%2F2019%2F07%2FS.jpg", + "http://pleroma.social/my%2Flong%2Furl%2F2019%2F07%2FS.jpg" + ) == :ok + + assert MediaProxyController.filename_matches( + %{"filename" => "/my%2Flong%2Furl%2F2019%2F07%2FS.jp"}, + "/my%2Flong%2Furl%2F2019%2F07%2FS.jp", + "http://pleroma.social/my%2Flong%2Furl%2F2019%2F07%2FS.jpg" + ) == {:wrong_filename, "my%2Flong%2Furl%2F2019%2F07%2FS.jpg"} + end + + test "encoded url are tried to match for proxy as `conn.request_path` encodes the url" do + # conn.request_path will return encoded url + request_path = "/ANALYSE-DAI-_-LE-STABLECOIN-100-D%C3%89CENTRALIS%C3%89-BQ.jpg" - with_mock Pleroma.ReverseProxy, - call: fn _conn, _url, _opts -> %Plug.Conn{status: :success} end do - assert %Plug.Conn{status: :success} = get(conn, url) + assert MediaProxyController.filename_matches( + true, + request_path, + "https://mydomain.com/uploads/2019/07/ANALYSE-DAI-_-LE-STABLECOIN-100-DÉCENTRALISÉ-BQ.jpg" + ) == :ok end end end diff --git a/test/web/media_proxy/media_proxy_test.exs b/test/web/media_proxy/media_proxy_test.exs index 69d2a71a6..72885cfdd 100644 --- a/test/web/media_proxy/media_proxy_test.exs +++ b/test/web/media_proxy/media_proxy_test.exs @@ -5,38 +5,33 @@ defmodule Pleroma.Web.MediaProxyTest do use ExUnit.Case use Pleroma.Tests.Helpers - import Pleroma.Web.MediaProxy - alias Pleroma.Web.MediaProxy.MediaProxyController - setup do: clear_config([:media_proxy, :enabled]) - setup do: clear_config(Pleroma.Upload) + alias Pleroma.Web.Endpoint + alias Pleroma.Web.MediaProxy describe "when enabled" do - setup do - Pleroma.Config.put([:media_proxy, :enabled], true) - :ok - end + setup do: clear_config([:media_proxy, :enabled], true) test "ignores invalid url" do - assert url(nil) == nil - assert url("") == nil + assert MediaProxy.url(nil) == nil + assert MediaProxy.url("") == nil end test "ignores relative url" do - assert url("/local") == "/local" - assert url("/") == "/" + assert MediaProxy.url("/local") == "/local" + assert MediaProxy.url("/") == "/" end test "ignores local url" do - local_url = Pleroma.Web.Endpoint.url() <> "/hello" - local_root = Pleroma.Web.Endpoint.url() - assert url(local_url) == local_url - assert url(local_root) == local_root + local_url = Endpoint.url() <> "/hello" + local_root = Endpoint.url() + assert MediaProxy.url(local_url) == local_url + assert MediaProxy.url(local_root) == local_root end test "encodes and decodes URL" do url = "https://pleroma.soykaf.com/static/logo.png" - encoded = url(url) + encoded = MediaProxy.url(url) assert String.starts_with?( encoded, @@ -50,86 +45,44 @@ defmodule Pleroma.Web.MediaProxyTest do test "encodes and decodes URL without a path" do url = "https://pleroma.soykaf.com" - encoded = url(url) + encoded = MediaProxy.url(url) assert decode_result(encoded) == url end test "encodes and decodes URL without an extension" do url = "https://pleroma.soykaf.com/path/" - encoded = url(url) + encoded = MediaProxy.url(url) assert String.ends_with?(encoded, "/path") assert decode_result(encoded) == url end test "encodes and decodes URL and ignores query params for the path" do url = "https://pleroma.soykaf.com/static/logo.png?93939393939&bunny=true" - encoded = url(url) + encoded = MediaProxy.url(url) assert String.ends_with?(encoded, "/logo.png") assert decode_result(encoded) == url end test "validates signature" do - secret_key_base = Pleroma.Config.get([Pleroma.Web.Endpoint, :secret_key_base]) - - on_exit(fn -> - Pleroma.Config.put([Pleroma.Web.Endpoint, :secret_key_base], secret_key_base) - end) - - encoded = url("https://pleroma.social") + encoded = MediaProxy.url("https://pleroma.social") - Pleroma.Config.put( - [Pleroma.Web.Endpoint, :secret_key_base], + clear_config( + [Endpoint, :secret_key_base], "00000000000000000000000000000000000000000000000" ) [_, "proxy", sig, base64 | _] = URI.parse(encoded).path |> String.split("/") - assert decode_url(sig, base64) == {:error, :invalid_signature} - end - - test "filename_matches preserves the encoded or decoded path" do - assert MediaProxyController.filename_matches( - %{"filename" => "/Hello world.jpg"}, - "/Hello world.jpg", - "http://pleroma.social/Hello world.jpg" - ) == :ok - - assert MediaProxyController.filename_matches( - %{"filename" => "/Hello%20world.jpg"}, - "/Hello%20world.jpg", - "http://pleroma.social/Hello%20world.jpg" - ) == :ok - - assert MediaProxyController.filename_matches( - %{"filename" => "/my%2Flong%2Furl%2F2019%2F07%2FS.jpg"}, - "/my%2Flong%2Furl%2F2019%2F07%2FS.jpg", - "http://pleroma.social/my%2Flong%2Furl%2F2019%2F07%2FS.jpg" - ) == :ok - - assert MediaProxyController.filename_matches( - %{"filename" => "/my%2Flong%2Furl%2F2019%2F07%2FS.jp"}, - "/my%2Flong%2Furl%2F2019%2F07%2FS.jp", - "http://pleroma.social/my%2Flong%2Furl%2F2019%2F07%2FS.jpg" - ) == {:wrong_filename, "my%2Flong%2Furl%2F2019%2F07%2FS.jpg"} - end - - test "encoded url are tried to match for proxy as `conn.request_path` encodes the url" do - # conn.request_path will return encoded url - request_path = "/ANALYSE-DAI-_-LE-STABLECOIN-100-D%C3%89CENTRALIS%C3%89-BQ.jpg" - - assert MediaProxyController.filename_matches( - true, - request_path, - "https://mydomain.com/uploads/2019/07/ANALYSE-DAI-_-LE-STABLECOIN-100-DÉCENTRALISÉ-BQ.jpg" - ) == :ok + assert MediaProxy.decode_url(sig, base64) == {:error, :invalid_signature} end test "uses the configured base_url" do - clear_config([:media_proxy, :base_url], "https://cache.pleroma.social") + base_url = "https://cache.pleroma.social" + clear_config([:media_proxy, :base_url], base_url) url = "https://pleroma.soykaf.com/static/logo.png" - encoded = url(url) + encoded = MediaProxy.url(url) - assert String.starts_with?(encoded, Pleroma.Config.get([:media_proxy, :base_url])) + assert String.starts_with?(encoded, base_url) end # Some sites expect ASCII encoded characters in the URL to be preserved even if @@ -140,7 +93,7 @@ defmodule Pleroma.Web.MediaProxyTest do url = "https://pleroma.com/%20/%21/%22/%23/%24/%25/%26/%27/%28/%29/%2A/%2B/%2C/%2D/%2E/%2F/%30/%31/%32/%33/%34/%35/%36/%37/%38/%39/%3A/%3B/%3C/%3D/%3E/%3F/%40/%41/%42/%43/%44/%45/%46/%47/%48/%49/%4A/%4B/%4C/%4D/%4E/%4F/%50/%51/%52/%53/%54/%55/%56/%57/%58/%59/%5A/%5B/%5C/%5D/%5E/%5F/%60/%61/%62/%63/%64/%65/%66/%67/%68/%69/%6A/%6B/%6C/%6D/%6E/%6F/%70/%71/%72/%73/%74/%75/%76/%77/%78/%79/%7A/%7B/%7C/%7D/%7E/%7F/%80/%81/%82/%83/%84/%85/%86/%87/%88/%89/%8A/%8B/%8C/%8D/%8E/%8F/%90/%91/%92/%93/%94/%95/%96/%97/%98/%99/%9A/%9B/%9C/%9D/%9E/%9F/%C2%A0/%A1/%A2/%A3/%A4/%A5/%A6/%A7/%A8/%A9/%AA/%AB/%AC/%C2%AD/%AE/%AF/%B0/%B1/%B2/%B3/%B4/%B5/%B6/%B7/%B8/%B9/%BA/%BB/%BC/%BD/%BE/%BF/%C0/%C1/%C2/%C3/%C4/%C5/%C6/%C7/%C8/%C9/%CA/%CB/%CC/%CD/%CE/%CF/%D0/%D1/%D2/%D3/%D4/%D5/%D6/%D7/%D8/%D9/%DA/%DB/%DC/%DD/%DE/%DF/%E0/%E1/%E2/%E3/%E4/%E5/%E6/%E7/%E8/%E9/%EA/%EB/%EC/%ED/%EE/%EF/%F0/%F1/%F2/%F3/%F4/%F5/%F6/%F7/%F8/%F9/%FA/%FB/%FC/%FD/%FE/%FF" - encoded = url(url) + encoded = MediaProxy.url(url) assert decode_result(encoded) == url end @@ -151,56 +104,49 @@ defmodule Pleroma.Web.MediaProxyTest do url = "https://pleroma.com/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890-._~:/?#[]@!$&'()*+,;=|^`{}" - encoded = url(url) + encoded = MediaProxy.url(url) assert decode_result(encoded) == url end test "preserve unicode characters" do url = "https://ko.wikipedia.org/wiki/위키백과:대문" - encoded = url(url) + encoded = MediaProxy.url(url) assert decode_result(encoded) == url end end describe "when disabled" do - setup do - enabled = Pleroma.Config.get([:media_proxy, :enabled]) - - if enabled do - Pleroma.Config.put([:media_proxy, :enabled], false) - - on_exit(fn -> - Pleroma.Config.put([:media_proxy, :enabled], enabled) - :ok - end) - end - - :ok - end + setup do: clear_config([:media_proxy, :enabled], false) test "does not encode remote urls" do - assert url("https://google.fr") == "https://google.fr" + assert MediaProxy.url("https://google.fr") == "https://google.fr" end end defp decode_result(encoded) do [_, "proxy", sig, base64 | _] = URI.parse(encoded).path |> String.split("/") - {:ok, decoded} = decode_url(sig, base64) + {:ok, decoded} = MediaProxy.decode_url(sig, base64) decoded end describe "whitelist" do - setup do - Pleroma.Config.put([:media_proxy, :enabled], true) - :ok - end + setup do: clear_config([:media_proxy, :enabled], true) test "mediaproxy whitelist" do - Pleroma.Config.put([:media_proxy, :whitelist], ["google.com", "feld.me"]) + clear_config([:media_proxy, :whitelist], ["https://google.com", "https://feld.me"]) + url = "https://feld.me/foo.png" + + unencoded = MediaProxy.url(url) + assert unencoded == url + end + + # TODO: delete after removing support bare domains for media proxy whitelist + test "mediaproxy whitelist bare domains whitelist (deprecated)" do + clear_config([:media_proxy, :whitelist], ["google.com", "feld.me"]) url = "https://feld.me/foo.png" - unencoded = url(url) + unencoded = MediaProxy.url(url) assert unencoded == url end @@ -211,17 +157,17 @@ defmodule Pleroma.Web.MediaProxyTest do media_url = "https://mycdn.akamai.com" url = "#{media_url}/static/logo.png" - encoded = url(url) + encoded = MediaProxy.url(url) assert String.starts_with?(encoded, media_url) end test "ensure Pleroma.Upload base_url is always whitelisted" do media_url = "https://media.pleroma.social" - Pleroma.Config.put([Pleroma.Upload, :base_url], media_url) + clear_config([Pleroma.Upload, :base_url], media_url) url = "#{media_url}/static/logo.png" - encoded = url(url) + encoded = MediaProxy.url(url) assert String.starts_with?(encoded, media_url) end diff --git a/test/web/metadata/metadata_test.exs b/test/web/metadata/metadata_test.exs index 3f8b29e58..9d3121b7b 100644 --- a/test/web/metadata/metadata_test.exs +++ b/test/web/metadata/metadata_test.exs @@ -22,4 +22,13 @@ defmodule Pleroma.Web.MetadataTest do "<meta content=\"noindex, noarchive\" name=\"robots\">" end end + + describe "no metadata for private instances" do + test "for local user" do + clear_config([:instance, :public], false) + user = insert(:user, bio: "This is my secret fedi account bio") + + assert "" = Pleroma.Web.Metadata.build_tags(%{user: user}) + end + end end diff --git a/test/web/metadata/rel_me_test.exs b/test/web/metadata/rel_me_test.exs index 4107a8459..2293d6e13 100644 --- a/test/web/metadata/rel_me_test.exs +++ b/test/web/metadata/rel_me_test.exs @@ -9,13 +9,12 @@ defmodule Pleroma.Web.Metadata.Providers.RelMeTest do test "it renders all links with rel='me' from user bio" do bio = - ~s(<a href="https://some-link.com">https://some-link.com</a> <a rel="me" href="https://another-link.com">https://another-link.com</a> - <link href="http://some.com"> <link rel="me" href="http://some3.com>") + ~s(<a href="https://some-link.com">https://some-link.com</a> <a rel="me" href="https://another-link.com">https://another-link.com</a> <link href="http://some.com"> <link rel="me" href="http://some3.com">) user = insert(:user, %{bio: bio}) assert RelMe.build_tags(%{user: user}) == [ - {:link, [rel: "me", href: "http://some3.com>"], []}, + {:link, [rel: "me", href: "http://some3.com"], []}, {:link, [rel: "me", href: "https://another-link.com"], []} ] end diff --git a/test/web/node_info_test.exs b/test/web/node_info_test.exs index 00925caad..06b33607f 100644 --- a/test/web/node_info_test.exs +++ b/test/web/node_info_test.exs @@ -67,10 +67,10 @@ defmodule Pleroma.Web.NodeInfoTest do end test "returns fieldsLimits field", %{conn: conn} do - 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) + clear_config([:instance, :max_account_fields], 10) + clear_config([:instance, :max_remote_account_fields], 15) + clear_config([:instance, :account_field_name_length], 255) + clear_config([:instance, :account_field_value_length], 2048) response = conn @@ -84,8 +84,7 @@ defmodule Pleroma.Web.NodeInfoTest do end test "it returns the safe_dm_mentions feature if enabled", %{conn: conn} do - option = Config.get([:instance, :safe_dm_mentions]) - Config.put([:instance, :safe_dm_mentions], true) + clear_config([:instance, :safe_dm_mentions], true) response = conn @@ -102,8 +101,6 @@ defmodule Pleroma.Web.NodeInfoTest do |> json_response(:ok) refute "safe_dm_mentions" in response["metadata"]["features"] - - Config.put([:instance, :safe_dm_mentions], option) end describe "`metadata/federation/enabled`" do @@ -156,14 +153,11 @@ defmodule Pleroma.Web.NodeInfoTest do end test "it shows MRF transparency data if enabled", %{conn: conn} do - config = Config.get([:instance, :rewrite_policy]) - Config.put([:instance, :rewrite_policy], [Pleroma.Web.ActivityPub.MRF.SimplePolicy]) - - option = Config.get([:instance, :mrf_transparency]) - Config.put([:instance, :mrf_transparency], true) + clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.SimplePolicy]) + clear_config([:mrf, :transparency], true) simple_config = %{"reject" => ["example.com"]} - Config.put(:mrf_simple, simple_config) + clear_config(:mrf_simple, simple_config) response = conn @@ -171,26 +165,17 @@ defmodule Pleroma.Web.NodeInfoTest do |> json_response(:ok) assert response["metadata"]["federation"]["mrf_simple"] == simple_config - - 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 = Config.get([:instance, :rewrite_policy]) - Config.put([:instance, :rewrite_policy], [Pleroma.Web.ActivityPub.MRF.SimplePolicy]) - - option = Config.get([:instance, :mrf_transparency]) - Config.put([:instance, :mrf_transparency], true) - - exclusions = Config.get([:instance, :mrf_transparency_exclusions]) - Config.put([:instance, :mrf_transparency_exclusions], ["other.site"]) + clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.SimplePolicy]) + clear_config([:mrf, :transparency], true) + clear_config([:mrf, :transparency_exclusions], ["other.site"]) simple_config = %{"reject" => ["example.com", "other.site"]} - expected_config = %{"reject" => ["example.com"]} + clear_config(:mrf_simple, simple_config) - Config.put(:mrf_simple, simple_config) + expected_config = %{"reject" => ["example.com"]} response = conn @@ -199,10 +184,5 @@ defmodule Pleroma.Web.NodeInfoTest do assert response["metadata"]["federation"]["mrf_simple"] == expected_config assert response["metadata"]["federation"]["exclusions"] == true - - 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/app_test.exs b/test/web/oauth/app_test.exs index 899af648e..993a490e0 100644 --- a/test/web/oauth/app_test.exs +++ b/test/web/oauth/app_test.exs @@ -29,5 +29,16 @@ defmodule Pleroma.Web.OAuth.AppTest do assert exist_app.id == app.id assert exist_app.scopes == ["read", "write", "follow", "push"] end + + test "has unique client_id" do + insert(:oauth_app, client_name: "", redirect_uris: "", client_id: "boop") + + error = + catch_error(insert(:oauth_app, client_name: "", redirect_uris: "", client_id: "boop")) + + assert %Ecto.ConstraintError{} = error + assert error.constraint == "apps_client_id_index" + assert error.type == :unique + end end end diff --git a/test/web/oauth/ldap_authorization_test.exs b/test/web/oauth/ldap_authorization_test.exs index 011642c08..63b1c0eb8 100644 --- a/test/web/oauth/ldap_authorization_test.exs +++ b/test/web/oauth/ldap_authorization_test.exs @@ -7,7 +7,6 @@ defmodule Pleroma.Web.OAuth.LDAPAuthorizationTest do alias Pleroma.Repo alias Pleroma.Web.OAuth.Token import Pleroma.Factory - import ExUnit.CaptureLog import Mock @skip if !Code.ensure_loaded?(:eldap), do: :skip @@ -72,9 +71,7 @@ defmodule Pleroma.Web.OAuth.LDAPAuthorizationTest do equalityMatch: fn _type, _value -> :ok end, wholeSubtree: fn -> :ok end, search: fn _connection, _options -> - {:ok, - {:eldap_search_result, [{:eldap_entry, '', [{'mail', [to_charlist(user.email)]}]}], - []}} + {:ok, {:eldap_search_result, [{:eldap_entry, '', []}], []}} end, close: fn _connection -> send(self(), :close_connection) @@ -102,50 +99,6 @@ defmodule Pleroma.Web.OAuth.LDAPAuthorizationTest do end @tag @skip - test "falls back to the default authorization when LDAP is unavailable" do - password = "testpassword" - 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 - port = Pleroma.Config.get([:ldap, :port]) - - with_mocks [ - {:eldap, [], - [ - open: fn [^host], [{:port, ^port}, {:ssl, false} | _] -> {:error, 'connect failed'} end, - simple_bind: fn _connection, _dn, ^password -> :ok end, - close: fn _connection -> - send(self(), :close_connection) - :ok - end - ]} - ] do - log = - capture_log(fn -> - conn = - build_conn() - |> post("/oauth/token", %{ - "grant_type" => "password", - "username" => user.nickname, - "password" => password, - "client_id" => app.client_id, - "client_secret" => app.client_secret - }) - - assert %{"access_token" => token} = json_response(conn, 200) - - token = Repo.get_by(Token, token: token) - - assert token.user_id == user.id - end) - - assert log =~ "Could not open LDAP connection: 'connect failed'" - refute_received :close_connection - end - end - - @tag @skip test "disallow authorization for wrong LDAP credentials" do password = "testpassword" user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt(password)) diff --git a/test/web/oauth/oauth_controller_test.exs b/test/web/oauth/oauth_controller_test.exs index d389e4ce0..1200126b8 100644 --- a/test/web/oauth/oauth_controller_test.exs +++ b/test/web/oauth/oauth_controller_test.exs @@ -19,7 +19,10 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do key: "_test", signing_salt: "cooldude" ] - setup do: clear_config([:instance, :account_activation_required]) + setup do + clear_config([:instance, :account_activation_required]) + clear_config([:instance, :account_approval_required]) + end describe "in OAuth consumer mode, " do setup do @@ -995,6 +998,30 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do } end + test "rejects token exchange for valid credentials belonging to an unapproved user" do + password = "testpassword" + + user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt(password), approval_pending: true) + + refute Pleroma.User.account_status(user) == :active + + app = insert(:oauth_app) + + conn = + build_conn() + |> post("/oauth/token", %{ + "grant_type" => "password", + "username" => user.nickname, + "password" => password, + "client_id" => app.client_id, + "client_secret" => app.client_secret + }) + + assert resp = json_response(conn, 403) + assert %{"error" => _} = resp + refute Map.has_key?(resp, "access_token") + end + test "rejects an invalid authorization code" do app = insert(:oauth_app) diff --git a/test/web/oauth/token_test.exs b/test/web/oauth/token_test.exs index 40d71eb59..c88b9cc98 100644 --- a/test/web/oauth/token_test.exs +++ b/test/web/oauth/token_test.exs @@ -69,17 +69,4 @@ defmodule Pleroma.Web.OAuth.TokenTest do assert tokens == 2 end - - test "deletes expired tokens" do - insert(:oauth_token, valid_until: Timex.shift(Timex.now(), days: -3)) - insert(:oauth_token, valid_until: Timex.shift(Timex.now(), days: -3)) - t3 = insert(:oauth_token) - t4 = insert(:oauth_token, valid_until: Timex.shift(Timex.now(), minutes: 10)) - {tokens, _} = Token.delete_expired_tokens() - assert tokens == 2 - available_tokens = Pleroma.Repo.all(Token) - - token_ids = available_tokens |> Enum.map(& &1.id) - assert token_ids == [t3.id, t4.id] - end end diff --git a/test/web/pleroma_api/controllers/account_controller_test.exs b/test/web/pleroma_api/controllers/account_controller_test.exs index 103997c31..07909d48b 100644 --- a/test/web/pleroma_api/controllers/account_controller_test.exs +++ b/test/web/pleroma_api/controllers/account_controller_test.exs @@ -13,8 +13,6 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do import Pleroma.Factory import Swoosh.TestAssertions - @image "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7" - describe "POST /api/v1/pleroma/accounts/confirmation_resend" do setup do {:ok, user} = @@ -68,103 +66,6 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do end end - describe "PATCH /api/v1/pleroma/accounts/update_avatar" do - setup do: oauth_access(["write:accounts"]) - - test "user avatar can be set", %{user: user, conn: conn} do - avatar_image = File.read!("test/fixtures/avatar_data_uri") - - conn = - conn - |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: avatar_image}) - - user = refresh_record(user) - - assert %{ - "name" => _, - "type" => _, - "url" => [ - %{ - "href" => _, - "mediaType" => _, - "type" => _ - } - ] - } = user.avatar - - assert %{"url" => _} = json_response_and_validate_schema(conn, 200) - end - - test "user avatar can be reset", %{user: user, conn: conn} do - 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_and_validate_schema(conn, 200) - end - end - - describe "PATCH /api/v1/pleroma/accounts/update_banner" do - setup do: oauth_access(["write:accounts"]) - - test "can set profile banner", %{user: user, conn: conn} do - 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_and_validate_schema(conn, 200) - end - - test "can reset profile banner", %{user: user, conn: conn} do - 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_and_validate_schema(conn, 200) - end - end - - describe "PATCH /api/v1/pleroma/accounts/update_background" do - setup do: oauth_access(["write:accounts"]) - - test "background image can be set", %{user: user, conn: conn} do - 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_and_validate_schema(conn, 200) - end - - test "background image can be reset", %{user: user, conn: conn} do - 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_and_validate_schema(conn, 200) - end - end - describe "getting favorites timeline of specified user" do setup do [current_user, user] = insert_pair(:user, hide_favorites: false) diff --git a/test/web/pleroma_api/controllers/chat_controller_test.exs b/test/web/pleroma_api/controllers/chat_controller_test.exs index 82e16741d..7be5fe09c 100644 --- a/test/web/pleroma_api/controllers/chat_controller_test.exs +++ b/test/web/pleroma_api/controllers/chat_controller_test.exs @@ -267,6 +267,21 @@ defmodule Pleroma.Web.PleromaAPI.ChatControllerTest do describe "GET /api/v1/pleroma/chats" do setup do: oauth_access(["read:chats"]) + test "it does not return chats with deleted users", %{conn: conn, user: user} do + recipient = insert(:user) + {:ok, _} = Chat.get_or_create(user.id, recipient.ap_id) + + Pleroma.Repo.delete(recipient) + User.invalidate_cache(recipient) + + result = + conn + |> get("/api/v1/pleroma/chats") + |> json_response_and_validate_schema(200) + + assert length(result) == 0 + end + test "it does not return chats with users you blocked", %{conn: conn, user: user} do recipient = insert(:user) @@ -332,5 +347,27 @@ defmodule Pleroma.Web.PleromaAPI.ChatControllerTest do chat_1.id |> to_string() ] end + + test "it is not affected by :restrict_unauthenticated setting (issue #1973)", %{ + conn: conn, + user: user + } do + clear_config([:restrict_unauthenticated, :profiles, :local], true) + clear_config([:restrict_unauthenticated, :profiles, :remote], true) + + user2 = insert(:user) + user3 = insert(:user, local: false) + + {:ok, _chat_12} = Chat.get_or_create(user.id, user2.ap_id) + {:ok, _chat_13} = Chat.get_or_create(user.id, user3.ap_id) + + result = + conn + |> get("/api/v1/pleroma/chats") + |> json_response_and_validate_schema(200) + + account_ids = Enum.map(result, &get_in(&1, ["account", "id"])) + assert Enum.sort(account_ids) == Enum.sort([user2.id, user3.id]) + end end end diff --git a/test/web/pleroma_api/controllers/emoji_pack_controller_test.exs b/test/web/pleroma_api/controllers/emoji_pack_controller_test.exs index ee3d281a0..e113bb15f 100644 --- a/test/web/pleroma_api/controllers/emoji_pack_controller_test.exs +++ b/test/web/pleroma_api/controllers/emoji_pack_controller_test.exs @@ -14,6 +14,8 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do ) setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], false) + setup do: clear_config([:instance, :public], true) + setup do admin = insert(:user, is_admin: true) token = insert(:oauth_admin_token, user: admin) @@ -27,18 +29,63 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do {:ok, %{admin_conn: admin_conn}} end + test "GET /api/pleroma/emoji/packs when :public: false", %{conn: conn} do + Config.put([:instance, :public], false) + conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200) + end + test "GET /api/pleroma/emoji/packs", %{conn: conn} do resp = conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200) - shared = resp["test_pack"] - assert shared["files"] == %{"blank" => "blank.png"} + assert resp["count"] == 3 + + assert resp["packs"] + |> Map.keys() + |> length() == 3 + + shared = resp["packs"]["test_pack"] + assert shared["files"] == %{"blank" => "blank.png", "blank2" => "blank2.png"} assert Map.has_key?(shared["pack"], "download-sha256") assert shared["pack"]["can-download"] assert shared["pack"]["share-files"] - non_shared = resp["test_pack_nonshared"] + non_shared = resp["packs"]["test_pack_nonshared"] assert non_shared["pack"]["share-files"] == false assert non_shared["pack"]["can-download"] == false + + resp = + conn + |> get("/api/pleroma/emoji/packs?page_size=1") + |> json_response_and_validate_schema(200) + + assert resp["count"] == 3 + + packs = Map.keys(resp["packs"]) + + assert length(packs) == 1 + + [pack1] = packs + + resp = + conn + |> get("/api/pleroma/emoji/packs?page_size=1&page=2") + |> json_response_and_validate_schema(200) + + assert resp["count"] == 3 + packs = Map.keys(resp["packs"]) + assert length(packs) == 1 + [pack2] = packs + + resp = + conn + |> get("/api/pleroma/emoji/packs?page_size=1&page=3") + |> json_response_and_validate_schema(200) + + assert resp["count"] == 3 + packs = Map.keys(resp["packs"]) + assert length(packs) == 1 + [pack3] = packs + assert [pack1, pack2, pack3] |> Enum.uniq() |> length() == 3 end describe "GET /api/pleroma/emoji/packs/remote" do @@ -332,7 +379,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do Map.put( new_data, "fallback-src-sha256", - "74409E2674DAA06C072729C6C8426C4CB3B7E0B85ED77792DB7A436E11D76DAF" + "1967BB4E42BCC34BCC12D57BE7811D3B7BE52F965BCE45C87BD377B9499CE11D" ) assert ctx[:admin_conn] @@ -398,7 +445,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do assert admin_conn |> put_req_header("content-type", "multipart/form-data") |> post("/api/pleroma/emoji/packs/test_pack/files", %{ - shortcode: "blank2", + shortcode: "blank3", filename: "dir/blank.png", file: %Plug.Upload{ filename: "blank.png", @@ -407,7 +454,8 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do }) |> json_response_and_validate_schema(200) == %{ "blank" => "blank.png", - "blank2" => "dir/blank.png" + "blank2" => "blank2.png", + "blank3" => "dir/blank.png" } assert File.exists?("#{@emoji_path}/test_pack/dir/blank.png") @@ -431,7 +479,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do assert admin_conn |> put_req_header("content-type", "multipart/form-data") |> post("/api/pleroma/emoji/packs/test_pack/files", %{ - shortcode: "blank2", + shortcode: "blank3", filename: "dir/blank.png", file: %Plug.Upload{ filename: "blank.png", @@ -440,7 +488,8 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do }) |> json_response_and_validate_schema(200) == %{ "blank" => "blank.png", - "blank2" => "dir/blank.png" + "blank2" => "blank2.png", + "blank3" => "dir/blank.png" } assert File.exists?("#{@emoji_path}/test_pack/dir/blank.png") @@ -448,14 +497,15 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do assert admin_conn |> put_req_header("content-type", "multipart/form-data") |> patch("/api/pleroma/emoji/packs/test_pack/files", %{ - shortcode: "blank2", - new_shortcode: "blank3", + shortcode: "blank3", + new_shortcode: "blank4", new_filename: "dir_2/blank_3.png", force: true }) |> json_response_and_validate_schema(200) == %{ "blank" => "blank.png", - "blank3" => "dir_2/blank_3.png" + "blank2" => "blank2.png", + "blank4" => "dir_2/blank_3.png" } assert File.exists?("#{@emoji_path}/test_pack/dir_2/blank_3.png") @@ -481,7 +531,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do assert admin_conn |> put_req_header("content-type", "multipart/form-data") |> post("/api/pleroma/emoji/packs/not_loaded/files", %{ - shortcode: "blank2", + shortcode: "blank3", filename: "dir/blank.png", file: %Plug.Upload{ filename: "blank.png", @@ -535,7 +585,8 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do }) |> json_response_and_validate_schema(200) == %{ "blank" => "blank.png", - "blank4" => "dir/blank.png" + "blank4" => "dir/blank.png", + "blank2" => "blank2.png" } assert File.exists?("#{@emoji_path}/test_pack/dir/blank.png") @@ -549,7 +600,8 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do }) |> json_response_and_validate_schema(200) == %{ "blank3" => "dir_2/blank_3.png", - "blank" => "blank.png" + "blank" => "blank.png", + "blank2" => "blank2.png" } refute File.exists?("#{@emoji_path}/test_pack/dir/") @@ -557,7 +609,10 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do assert admin_conn |> delete("/api/pleroma/emoji/packs/test_pack/files?shortcode=blank3") - |> json_response_and_validate_schema(200) == %{"blank" => "blank.png"} + |> json_response_and_validate_schema(200) == %{ + "blank" => "blank.png", + "blank2" => "blank2.png" + } refute File.exists?("#{@emoji_path}/test_pack/dir_2/") @@ -581,7 +636,8 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do }) |> json_response_and_validate_schema(200) == %{ "blank_url" => "blank_url.png", - "blank" => "blank.png" + "blank" => "blank.png", + "blank2" => "blank2.png" } assert File.exists?("#{@emoji_path}/test_pack/blank_url.png") @@ -602,15 +658,16 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do }) |> json_response_and_validate_schema(200) == %{ "shortcode" => "shortcode.png", - "blank" => "blank.png" + "blank" => "blank.png", + "blank2" => "blank2.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") + |> delete("/api/pleroma/emoji/packs/test_pack/files?shortcode=blank3") |> json_response_and_validate_schema(:bad_request) == %{ - "error" => "Emoji \"blank2\" does not exist" + "error" => "Emoji \"blank3\" does not exist" } end @@ -618,12 +675,12 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do assert admin_conn |> put_req_header("content-type", "multipart/form-data") |> patch("/api/pleroma/emoji/packs/test_pack/files", %{ - shortcode: "blank2", - new_shortcode: "blank3", + shortcode: "blank3", + new_shortcode: "blank4", new_filename: "dir_2/blank_3.png" }) |> json_response_and_validate_schema(:bad_request) == %{ - "error" => "Emoji \"blank2\" does not exist" + "error" => "Emoji \"blank3\" does not exist" } end @@ -651,7 +708,8 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do assert Jason.decode!(File.read!("#{@emoji_path}/test_created/pack.json")) == %{ "pack" => %{}, - "files" => %{} + "files" => %{}, + "files_count" => 0 } assert admin_conn @@ -709,14 +767,14 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do resp = conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200) - refute Map.has_key?(resp, "test_pack_for_import") + refute Map.has_key?(resp["packs"], "test_pack_for_import") assert admin_conn |> get("/api/pleroma/emoji/packs/import") |> json_response_and_validate_schema(200) == ["test_pack_for_import"] resp = conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200) - assert resp["test_pack_for_import"]["files"] == %{"blank" => "blank.png"} + assert resp["packs"]["test_pack_for_import"]["files"] == %{"blank" => "blank.png"} File.rm!("#{@emoji_path}/test_pack_for_import/pack.json") refute File.exists?("#{@emoji_path}/test_pack_for_import/pack.json") @@ -736,7 +794,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do resp = conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200) - assert resp["test_pack_for_import"]["files"] == %{ + assert resp["packs"]["test_pack_for_import"]["files"] == %{ "blank" => "blank.png", "blank2" => "blank.png", "foo" => "blank.png" @@ -746,7 +804,8 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do describe "GET /api/pleroma/emoji/packs/:name" do test "shows pack.json", %{conn: conn} do assert %{ - "files" => %{"blank" => "blank.png"}, + "files" => files, + "files_count" => 2, "pack" => %{ "can-download" => true, "description" => "Test description", @@ -759,6 +818,28 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do conn |> get("/api/pleroma/emoji/packs/test_pack") |> json_response_and_validate_schema(200) + + assert files == %{"blank" => "blank.png", "blank2" => "blank2.png"} + + assert %{ + "files" => files, + "files_count" => 2 + } = + conn + |> get("/api/pleroma/emoji/packs/test_pack?page_size=1") + |> json_response_and_validate_schema(200) + + assert files |> Map.keys() |> length() == 1 + + assert %{ + "files" => files, + "files_count" => 2 + } = + conn + |> get("/api/pleroma/emoji/packs/test_pack?page_size=1&page=2") + |> json_response_and_validate_schema(200) + + assert files |> Map.keys() |> length() == 1 end test "non existing pack", %{conn: conn} do diff --git a/test/web/pleroma_api/controllers/emoji_reaction_controller_test.exs b/test/web/pleroma_api/controllers/emoji_reaction_controller_test.exs index e1bb5ebfe..3deab30d1 100644 --- a/test/web/pleroma_api/controllers/emoji_reaction_controller_test.exs +++ b/test/web/pleroma_api/controllers/emoji_reaction_controller_test.exs @@ -106,6 +106,23 @@ defmodule Pleroma.Web.PleromaAPI.EmojiReactionControllerTest do result end + test "GET /api/v1/pleroma/statuses/:id/reactions with :show_reactions disabled", %{conn: conn} do + clear_config([:instance, :show_reactions], false) + + user = insert(:user) + other_user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "#cofe"}) + {:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅") + + result = + conn + |> get("/api/v1/pleroma/statuses/#{activity.id}/reactions") + |> json_response_and_validate_schema(200) + + assert result == [] + end + test "GET /api/v1/pleroma/statuses/:id/reactions/:emoji", %{conn: conn} do user = insert(:user) other_user = insert(:user) diff --git a/test/web/pleroma_api/views/chat/message_reference_view_test.exs b/test/web/pleroma_api/views/chat/message_reference_view_test.exs index e5b165255..40dbae3cd 100644 --- a/test/web/pleroma_api/views/chat/message_reference_view_test.exs +++ b/test/web/pleroma_api/views/chat/message_reference_view_test.exs @@ -43,7 +43,17 @@ defmodule Pleroma.Web.PleromaAPI.Chat.MessageReferenceViewTest do assert chat_message[:unread] == false assert match?([%{shortcode: "firefox"}], chat_message[:emojis]) - {:ok, activity} = CommonAPI.post_chat_message(recipient, user, "gkgkgk", media_id: upload.id) + clear_config([:rich_media, :enabled], true) + + Tesla.Mock.mock(fn + %{url: "https://example.com/ogp"} -> + %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/ogp.html")} + end) + + {:ok, activity} = + CommonAPI.post_chat_message(recipient, user, "gkgkgk https://example.com/ogp", + media_id: upload.id + ) object = Object.normalize(activity) @@ -52,10 +62,11 @@ defmodule Pleroma.Web.PleromaAPI.Chat.MessageReferenceViewTest do chat_message_two = MessageReferenceView.render("show.json", chat_message_reference: cm_ref) assert chat_message_two[:id] == cm_ref.id - assert chat_message_two[:content] == "gkgkgk" + assert chat_message_two[:content] == object.data["content"] assert chat_message_two[:account_id] == recipient.id assert chat_message_two[:chat_id] == chat_message[:chat_id] assert chat_message_two[:attachment] assert chat_message_two[:unread] == true + assert chat_message_two[:card] end end diff --git a/test/web/pleroma_api/views/chat_view_test.exs b/test/web/pleroma_api/views/chat_view_test.exs index 14eecb1bd..02484b705 100644 --- a/test/web/pleroma_api/views/chat_view_test.exs +++ b/test/web/pleroma_api/views/chat_view_test.exs @@ -26,7 +26,8 @@ defmodule Pleroma.Web.PleromaAPI.ChatViewTest do assert represented_chat == %{ id: "#{chat.id}", - account: AccountView.render("show.json", user: recipient), + account: + AccountView.render("show.json", user: recipient, skip_visibility_check: true), unread: 0, last_message: nil, updated_at: Utils.to_masto_date(chat.updated_at) diff --git a/test/web/preload/instance_test.exs b/test/web/preload/instance_test.exs new file mode 100644 index 000000000..a46f28312 --- /dev/null +++ b/test/web/preload/instance_test.exs @@ -0,0 +1,48 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Preload.Providers.InstanceTest do + use Pleroma.DataCase + alias Pleroma.Web.Preload.Providers.Instance + + setup do: {:ok, Instance.generate_terms(nil)} + + test "it renders the info", %{"/api/v1/instance" => info} do + assert %{ + description: description, + email: "admin@example.com", + registrations: true + } = info + + assert String.equivalent?(description, "Pleroma: An efficient and flexible fediverse server") + end + + test "it renders the panel", %{"/instance/panel.html" => panel} do + assert String.contains?( + panel, + "<p>Welcome to <a href=\"https://pleroma.social\" target=\"_blank\">Pleroma!</a></p>" + ) + end + + test "it works with overrides" do + clear_config([:instance, :static_dir], "test/fixtures/preload_static") + + %{"/instance/panel.html" => panel} = Instance.generate_terms(nil) + + assert String.contains?( + panel, + "HEY!" + ) + end + + test "it renders the node_info", %{"/nodeinfo/2.0.json" => nodeinfo} do + %{ + metadata: metadata, + version: "2.0" + } = nodeinfo + + assert metadata.private == false + assert metadata.suggestions == %{enabled: false} + end +end diff --git a/test/web/preload/timeline_test.exs b/test/web/preload/timeline_test.exs new file mode 100644 index 000000000..3b1f2f1aa --- /dev/null +++ b/test/web/preload/timeline_test.exs @@ -0,0 +1,56 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Preload.Providers.TimelineTest do + use Pleroma.DataCase + import Pleroma.Factory + + alias Pleroma.Web.CommonAPI + alias Pleroma.Web.Preload.Providers.Timelines + + @public_url "/api/v1/timelines/public" + + describe "unauthenticated timeliness when restricted" do + setup do: clear_config([:restrict_unauthenticated, :timelines, :local], true) + setup do: clear_config([:restrict_unauthenticated, :timelines, :federated], true) + + test "return nothing" do + tl_data = Timelines.generate_terms(%{}) + + refute Map.has_key?(tl_data, "/api/v1/timelines/public") + end + end + + describe "unauthenticated timeliness when unrestricted" do + setup do: clear_config([:restrict_unauthenticated, :timelines, :local], false) + setup do: clear_config([:restrict_unauthenticated, :timelines, :federated], false) + + setup do: {:ok, user: insert(:user)} + + test "returns the timeline when not restricted" do + assert Timelines.generate_terms(%{}) + |> Map.has_key?(@public_url) + end + + test "returns public items", %{user: user} do + {:ok, _} = CommonAPI.post(user, %{status: "it's post 1!"}) + {:ok, _} = CommonAPI.post(user, %{status: "it's post 2!"}) + {:ok, _} = CommonAPI.post(user, %{status: "it's post 3!"}) + + assert Timelines.generate_terms(%{}) + |> Map.fetch!(@public_url) + |> Enum.count() == 3 + end + + test "does not return non-public items", %{user: user} do + {:ok, _} = CommonAPI.post(user, %{status: "it's post 1!", visibility: "unlisted"}) + {:ok, _} = CommonAPI.post(user, %{status: "it's post 2!", visibility: "direct"}) + {:ok, _} = CommonAPI.post(user, %{status: "it's post 3!"}) + + assert Timelines.generate_terms(%{}) + |> Map.fetch!(@public_url) + |> Enum.count() == 1 + end + end +end diff --git a/test/web/preload/user_test.exs b/test/web/preload/user_test.exs new file mode 100644 index 000000000..83f065e27 --- /dev/null +++ b/test/web/preload/user_test.exs @@ -0,0 +1,33 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Preload.Providers.UserTest do + use Pleroma.DataCase + import Pleroma.Factory + alias Pleroma.Web.Preload.Providers.User + + describe "returns empty when user doesn't exist" do + test "nil user specified" do + assert User.generate_terms(%{user: nil}) == %{} + end + + test "missing user specified" do + assert User.generate_terms(%{user: :not_a_user}) == %{} + end + end + + describe "specified user exists" do + setup do + user = insert(:user) + + terms = User.generate_terms(%{user: user}) + %{terms: terms, user: user} + end + + test "account is rendered", %{terms: terms, user: user} do + account = terms["/api/v1/accounts/#{user.id}"] + assert %{acct: user, username: user} = account + end + end +end diff --git a/test/web/push/impl_test.exs b/test/web/push/impl_test.exs index b48952b29..aeb5c1fbd 100644 --- a/test/web/push/impl_test.exs +++ b/test/web/push/impl_test.exs @@ -238,9 +238,11 @@ defmodule Pleroma.Web.Push.ImplTest do } end - test "hides details for notifications when privacy option enabled" do + test "hides contents of notifications when option enabled" do user = insert(:user, nickname: "Bob") - user2 = insert(:user, nickname: "Rob", notification_settings: %{privacy_option: true}) + + user2 = + insert(:user, nickname: "Rob", notification_settings: %{hide_notification_contents: true}) {:ok, activity} = CommonAPI.post(user, %{ @@ -284,9 +286,11 @@ defmodule Pleroma.Web.Push.ImplTest do } end - test "returns regular content for notifications with privacy option disabled" do + test "returns regular content when hiding contents option disabled" do user = insert(:user, nickname: "Bob") - user2 = insert(:user, nickname: "Rob", notification_settings: %{privacy_option: false}) + + user2 = + insert(:user, nickname: "Rob", notification_settings: %{hide_notification_contents: false}) {:ok, activity} = CommonAPI.post(user, %{ diff --git a/test/web/rich_media/aws_signed_url_test.exs b/test/web/rich_media/aws_signed_url_test.exs index b30f4400e..1ceae1a31 100644 --- a/test/web/rich_media/aws_signed_url_test.exs +++ b/test/web/rich_media/aws_signed_url_test.exs @@ -21,7 +21,7 @@ defmodule Pleroma.Web.RichMedia.TTL.AwsSignedUrlTest do expire_time = Timex.parse!(timestamp, "{ISO:Basic:Z}") |> Timex.to_unix() |> Kernel.+(valid_till) - assert expire_time == Pleroma.Web.RichMedia.Parser.TTL.AwsSignedUrl.ttl(metadata, url) + assert {:ok, expire_time} == Pleroma.Web.RichMedia.Parser.TTL.AwsSignedUrl.ttl(metadata, url) end test "s3 signed url is parsed and correct ttl is set for rich media" do @@ -55,7 +55,7 @@ defmodule Pleroma.Web.RichMedia.TTL.AwsSignedUrlTest do Cachex.put(:rich_media_cache, url, metadata) - Pleroma.Web.RichMedia.Parser.set_ttl_based_on_image({:ok, metadata}, url) + Pleroma.Web.RichMedia.Parser.set_ttl_based_on_image(metadata, url) {:ok, cache_ttl} = Cachex.ttl(:rich_media_cache, url) diff --git a/test/web/rich_media/parser_test.exs b/test/web/rich_media/parser_test.exs index 420a612c6..21ae35f8b 100644 --- a/test/web/rich_media/parser_test.exs +++ b/test/web/rich_media/parser_test.exs @@ -5,6 +5,8 @@ defmodule Pleroma.Web.RichMedia.ParserTest do use ExUnit.Case, async: true + alias Pleroma.Web.RichMedia.Parser + setup do Tesla.Mock.mock(fn %{ @@ -48,23 +50,27 @@ defmodule Pleroma.Web.RichMedia.ParserTest do %{method: :get, url: "http://example.com/empty"} -> %Tesla.Env{status: 200, body: "hello"} + + %{method: :get, url: "http://example.com/malformed"} -> + %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/malformed-data.html")} + + %{method: :get, url: "http://example.com/error"} -> + {:error, :overload} end) :ok end test "returns error when no metadata present" do - assert {:error, _} = Pleroma.Web.RichMedia.Parser.parse("http://example.com/empty") + assert {:error, _} = Parser.parse("http://example.com/empty") end test "doesn't just add a title" do - assert Pleroma.Web.RichMedia.Parser.parse("http://example.com/non-ogp") == - {:error, - "Found metadata was invalid or incomplete: %{\"url\" => \"http://example.com/non-ogp\"}"} + assert {:error, {:invalid_metadata, _}} = Parser.parse("http://example.com/non-ogp") end test "parses ogp" do - assert Pleroma.Web.RichMedia.Parser.parse("http://example.com/ogp") == + assert Parser.parse("http://example.com/ogp") == {:ok, %{ "image" => "http://ia.media-imdb.com/images/rock.jpg", @@ -77,7 +83,7 @@ defmodule Pleroma.Web.RichMedia.ParserTest do end test "falls back to <title> when ogp:title is missing" do - assert Pleroma.Web.RichMedia.Parser.parse("http://example.com/ogp-missing-title") == + assert Parser.parse("http://example.com/ogp-missing-title") == {:ok, %{ "image" => "http://ia.media-imdb.com/images/rock.jpg", @@ -90,7 +96,7 @@ defmodule Pleroma.Web.RichMedia.ParserTest do end test "parses twitter card" do - assert Pleroma.Web.RichMedia.Parser.parse("http://example.com/twitter-card") == + assert Parser.parse("http://example.com/twitter-card") == {:ok, %{ "card" => "summary", @@ -103,7 +109,7 @@ defmodule Pleroma.Web.RichMedia.ParserTest do end test "parses OEmbed" do - assert Pleroma.Web.RichMedia.Parser.parse("http://example.com/oembed") == + assert Parser.parse("http://example.com/oembed") == {:ok, %{ "author_name" => "bees", @@ -132,6 +138,10 @@ defmodule Pleroma.Web.RichMedia.ParserTest do end test "rejects invalid OGP data" do - assert {:error, _} = Pleroma.Web.RichMedia.Parser.parse("http://example.com/malformed") + assert {:error, _} = Parser.parse("http://example.com/malformed") + end + + test "returns error if getting page was not successful" do + assert {:error, :overload} = Parser.parse("http://example.com/error") end end diff --git a/test/web/static_fe/static_fe_controller_test.exs b/test/web/static_fe/static_fe_controller_test.exs index a49ab002f..1598bf675 100644 --- a/test/web/static_fe/static_fe_controller_test.exs +++ b/test/web/static_fe/static_fe_controller_test.exs @@ -87,6 +87,20 @@ defmodule Pleroma.Web.StaticFE.StaticFEControllerTest do assert html =~ "testing a thing!" end + test "redirects to json if requested", %{conn: conn, user: user} do + {:ok, activity} = CommonAPI.post(user, %{status: "testing a thing!"}) + + conn = + conn + |> put_req_header( + "accept", + "Accept: application/activity+json, application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\", text/html" + ) + |> get("/notice/#{activity.id}") + + assert redirected_to(conn, 302) =~ activity.data["object"] + end + test "filters HTML tags", %{conn: conn} do user = insert(:user) {:ok, activity} = CommonAPI.post(user, %{status: "<script>alert('xss')</script>"}) diff --git a/test/web/streamer/streamer_test.exs b/test/web/streamer/streamer_test.exs index 245f6e63f..d56d74464 100644 --- a/test/web/streamer/streamer_test.exs +++ b/test/web/streamer/streamer_test.exs @@ -116,6 +116,35 @@ defmodule Pleroma.Web.StreamerTest do refute Streamer.filtered_by_user?(user, announce) end + test "it does not stream announces of the user's own posts in the 'user' stream", %{ + user: user + } do + Streamer.get_topic_and_add_socket("user", user) + + other_user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{status: "hey"}) + {:ok, announce} = CommonAPI.repeat(activity.id, other_user) + + assert Streamer.filtered_by_user?(user, announce) + end + + test "it does stream notifications announces of the user's own posts in the 'user' stream", %{ + user: user + } do + Streamer.get_topic_and_add_socket("user", user) + + other_user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{status: "hey"}) + {:ok, announce} = CommonAPI.repeat(activity.id, other_user) + + notification = + Pleroma.Notification + |> Repo.get_by(%{user_id: user.id, activity_id: announce.id}) + |> Repo.preload(:activity) + + refute Streamer.filtered_by_user?(user, notification) + end + test "it streams boosts of mastodon user in the 'user' stream", %{user: user} do Streamer.get_topic_and_add_socket("user", user) diff --git a/test/web/twitter_api/password_controller_test.exs b/test/web/twitter_api/password_controller_test.exs index 231a46c67..a5e9e2178 100644 --- a/test/web/twitter_api/password_controller_test.exs +++ b/test/web/twitter_api/password_controller_test.exs @@ -37,7 +37,7 @@ defmodule Pleroma.Web.TwitterAPI.PasswordControllerTest do test "it returns HTTP 200", %{conn: conn} do user = insert(:user) {:ok, token} = PasswordResetToken.create_token(user) - {:ok, _access_token} = Token.create_token(insert(:oauth_app), user, %{}) + {:ok, _access_token} = Token.create(insert(:oauth_app), user, %{}) params = %{ "password" => "test", @@ -62,7 +62,7 @@ defmodule Pleroma.Web.TwitterAPI.PasswordControllerTest do user = insert(:user, password_reset_pending: true) {:ok, token} = PasswordResetToken.create_token(user) - {:ok, _access_token} = Token.create_token(insert(:oauth_app), user, %{}) + {:ok, _access_token} = Token.create(insert(:oauth_app), user, %{}) params = %{ "password" => "test", diff --git a/test/web/twitter_api/remote_follow_controller_test.exs b/test/web/twitter_api/remote_follow_controller_test.exs index f7e54c26a..3852c7ce9 100644 --- a/test/web/twitter_api/remote_follow_controller_test.exs +++ b/test/web/twitter_api/remote_follow_controller_test.exs @@ -227,7 +227,7 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do } ) - {:ok, %{token: token}} = MFA.Token.create_token(user) + {:ok, %{token: token}} = MFA.Token.create(user) user2 = insert(:user) otp_token = TOTP.generate_token(otp_secret) @@ -256,7 +256,7 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do } ) - {:ok, %{token: token}} = MFA.Token.create_token(user) + {:ok, %{token: token}} = MFA.Token.create(user) user2 = insert(:user) otp_token = TOTP.generate_token(TOTP.generate_secret()) diff --git a/test/web/twitter_api/twitter_api_test.exs b/test/web/twitter_api/twitter_api_test.exs index 368533292..20a45cb6f 100644 --- a/test/web/twitter_api/twitter_api_test.exs +++ b/test/web/twitter_api/twitter_api_test.exs @@ -4,11 +4,11 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do use Pleroma.DataCase + import Pleroma.Factory alias Pleroma.Repo alias Pleroma.Tests.ObanHelpers alias Pleroma.User alias Pleroma.UserInviteToken - alias Pleroma.Web.MastodonAPI.AccountView alias Pleroma.Web.TwitterAPI.TwitterAPI setup_all do @@ -27,13 +27,10 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do {:ok, user} = TwitterAPI.register_user(data) - fetched_user = User.get_cached_by_nickname("lain") - - assert AccountView.render("show.json", %{user: user}) == - AccountView.render("show.json", %{user: fetched_user}) + assert user == User.get_cached_by_nickname("lain") end - test "it registers a new user with empty string in bio and returns the user." do + test "it registers a new user with empty string in bio and returns the user" do data = %{ :username => "lain", :email => "lain@wired.jp", @@ -45,10 +42,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do {:ok, user} = TwitterAPI.register_user(data) - fetched_user = User.get_cached_by_nickname("lain") - - assert AccountView.render("show.json", %{user: user}) == - AccountView.render("show.json", %{user: fetched_user}) + assert user == User.get_cached_by_nickname("lain") end test "it sends confirmation email if :account_activation_required is specified in instance config" do @@ -85,6 +79,42 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do ) end + test "it sends an admin email if :account_approval_required is specified in instance config" do + admin = insert(:user, is_admin: true) + setting = Pleroma.Config.get([:instance, :account_approval_required]) + + unless setting do + Pleroma.Config.put([:instance, :account_approval_required], true) + on_exit(fn -> Pleroma.Config.put([:instance, :account_approval_required], setting) end) + end + + data = %{ + :username => "lain", + :email => "lain@wired.jp", + :fullname => "lain iwakura", + :bio => "", + :password => "bear", + :confirm => "bear", + :reason => "I love anime" + } + + {:ok, user} = TwitterAPI.register_user(data) + ObanHelpers.perform_all() + + assert user.approval_pending + + email = Pleroma.Emails.AdminEmail.new_unapproved_registration(admin, user) + + notify_email = Pleroma.Config.get([:instance, :notify_email]) + instance_name = Pleroma.Config.get([:instance, :name]) + + Swoosh.TestAssertions.assert_email_sent( + from: {instance_name, notify_email}, + to: {admin.name, admin.email}, + html_body: email.html_body + ) + end + test "it registers a new user and parses mentions in the bio" do data1 = %{ :username => "john", @@ -134,13 +164,10 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do {:ok, user} = TwitterAPI.register_user(data) - fetched_user = User.get_cached_by_nickname("vinny") - invite = Repo.get_by(UserInviteToken, token: invite.token) + assert user == User.get_cached_by_nickname("vinny") + invite = Repo.get_by(UserInviteToken, token: invite.token) assert invite.used == true - - assert AccountView.render("show.json", %{user: user}) == - AccountView.render("show.json", %{user: fetched_user}) end test "returns error on invalid token" do @@ -197,10 +224,8 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do check_fn = fn invite -> data = Map.put(data, :token, invite.token) {:ok, user} = TwitterAPI.register_user(data) - fetched_user = User.get_cached_by_nickname("vinny") - assert AccountView.render("show.json", %{user: user}) == - AccountView.render("show.json", %{user: fetched_user}) + assert user == User.get_cached_by_nickname("vinny") end {:ok, data: data, check_fn: check_fn} @@ -260,14 +285,11 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do } {:ok, user} = TwitterAPI.register_user(data) - fetched_user = User.get_cached_by_nickname("vinny") - invite = Repo.get_by(UserInviteToken, token: invite.token) + assert user == User.get_cached_by_nickname("vinny") + invite = Repo.get_by(UserInviteToken, token: invite.token) assert invite.used == true - assert AccountView.render("show.json", %{user: user}) == - AccountView.render("show.json", %{user: fetched_user}) - data = %{ :username => "GrimReaper", :email => "death@reapers.afterlife", @@ -302,13 +324,10 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do } {:ok, user} = TwitterAPI.register_user(data) - fetched_user = User.get_cached_by_nickname("vinny") - invite = Repo.get_by(UserInviteToken, token: invite.token) + assert user == User.get_cached_by_nickname("vinny") + invite = Repo.get_by(UserInviteToken, token: invite.token) refute invite.used - - assert AccountView.render("show.json", %{user: user}) == - AccountView.render("show.json", %{user: fetched_user}) end test "error after max uses" do @@ -327,13 +346,11 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do } {:ok, user} = TwitterAPI.register_user(data) - fetched_user = User.get_cached_by_nickname("vinny") + assert user == User.get_cached_by_nickname("vinny") + invite = Repo.get_by(UserInviteToken, token: invite.token) assert invite.used == true - assert AccountView.render("show.json", %{user: user}) == - AccountView.render("show.json", %{user: fetched_user}) - data = %{ :username => "GrimReaper", :email => "death@reapers.afterlife", diff --git a/test/web/twitter_api/util_controller_test.exs b/test/web/twitter_api/util_controller_test.exs index ad919d341..d164127ee 100644 --- a/test/web/twitter_api/util_controller_test.exs +++ b/test/web/twitter_api/util_controller_test.exs @@ -191,7 +191,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do test "it updates notification settings", %{user: user, conn: conn} do conn |> put("/api/pleroma/notification_settings", %{ - "followers" => false, + "block_from_strangers" => true, "bar" => 1 }) |> json_response(:ok) @@ -199,130 +199,25 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do user = refresh_record(user) assert %Pleroma.User.NotificationSetting{ - followers: false, - follows: true, - non_follows: true, - non_followers: true, - privacy_option: false + block_from_strangers: true, + hide_notification_contents: false } == user.notification_settings end - test "it updates notification privacy option", %{user: user, conn: conn} do + test "it updates notification settings to enable hiding contents", %{user: user, conn: conn} do conn - |> put("/api/pleroma/notification_settings", %{"privacy_option" => "1"}) + |> put("/api/pleroma/notification_settings", %{"hide_notification_contents" => "1"}) |> json_response(:ok) user = refresh_record(user) assert %Pleroma.User.NotificationSetting{ - followers: true, - follows: true, - non_follows: true, - non_followers: true, - privacy_option: true + block_from_strangers: false, + hide_notification_contents: true } == user.notification_settings end end - describe "GET /api/statusnet/config" do - test "it returns config in xml format", %{conn: conn} do - instance = Config.get(:instance) - - response = - conn - |> put_req_header("accept", "application/xml") - |> get("/api/statusnet/config") - |> response(:ok) - - assert response == - "<config>\n<site>\n<name>#{Keyword.get(instance, :name)}</name>\n<site>#{ - Pleroma.Web.base_url() - }</site>\n<textlimit>#{Keyword.get(instance, :limit)}</textlimit>\n<closed>#{ - !Keyword.get(instance, :registrations_open) - }</closed>\n</site>\n</config>\n" - end - - test "it returns config in json format", %{conn: conn} do - instance = Config.get(:instance) - Config.put([:instance, :managed_config], true) - Config.put([:instance, :registrations_open], false) - Config.put([:instance, :invites_enabled], true) - Config.put([:instance, :public], false) - Config.put([:frontend_configurations, :pleroma_fe], %{theme: "asuka-hospital"}) - - response = - conn - |> put_req_header("accept", "application/json") - |> get("/api/statusnet/config") - |> json_response(:ok) - - expected_data = %{ - "site" => %{ - "accountActivationRequired" => "0", - "closed" => "1", - "description" => Keyword.get(instance, :description), - "invitesEnabled" => "1", - "name" => Keyword.get(instance, :name), - "pleromafe" => %{"theme" => "asuka-hospital"}, - "private" => "1", - "safeDMMentionsEnabled" => "0", - "server" => Pleroma.Web.base_url(), - "textlimit" => to_string(Keyword.get(instance, :limit)), - "uploadlimit" => %{ - "avatarlimit" => to_string(Keyword.get(instance, :avatar_upload_limit)), - "backgroundlimit" => to_string(Keyword.get(instance, :background_upload_limit)), - "bannerlimit" => to_string(Keyword.get(instance, :banner_upload_limit)), - "uploadlimit" => to_string(Keyword.get(instance, :upload_limit)) - }, - "vapidPublicKey" => Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key) - } - } - - assert response == expected_data - end - - test "returns the state of safe_dm_mentions flag", %{conn: conn} do - Config.put([:instance, :safe_dm_mentions], true) - - response = - conn - |> get("/api/statusnet/config.json") - |> json_response(:ok) - - assert response["site"]["safeDMMentionsEnabled"] == "1" - - Config.put([:instance, :safe_dm_mentions], false) - - response = - conn - |> get("/api/statusnet/config.json") - |> json_response(:ok) - - assert response["site"]["safeDMMentionsEnabled"] == "0" - end - - test "it returns the managed config", %{conn: conn} do - Config.put([:instance, :managed_config], false) - Config.put([:frontend_configurations, :pleroma_fe], %{theme: "asuka-hospital"}) - - response = - conn - |> get("/api/statusnet/config.json") - |> json_response(:ok) - - refute response["site"]["pleromafe"] - - Config.put([:instance, :managed_config], true) - - response = - conn - |> get("/api/statusnet/config.json") - |> json_response(:ok) - - assert response["site"]["pleromafe"] == %{"theme" => "asuka-hospital"} - end - end - describe "GET /api/pleroma/frontend_configurations" do test "returns everything in :pleroma, :frontend_configurations", %{conn: conn} do config = [ @@ -451,28 +346,6 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do end end - describe "GET /api/statusnet/version" do - test "it returns version in xml format", %{conn: conn} do - response = - conn - |> put_req_header("accept", "application/xml") - |> get("/api/statusnet/version") - |> response(:ok) - - assert response == "<version>#{Pleroma.Application.named_version()}</version>" - end - - test "it returns version in json format", %{conn: conn} do - response = - conn - |> put_req_header("accept", "application/json") - |> get("/api/statusnet/version") - |> json_response(:ok) - - assert response == "#{Pleroma.Application.named_version()}" - end - end - describe "POST /main/ostatus - remote_subscribe/2" do setup do: clear_config([:instance, :federating], true) @@ -713,10 +586,16 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do end end - test "with proper permissions and valid password", %{conn: conn} do + test "with proper permissions and valid password", %{conn: conn, user: user} do conn = post(conn, "/api/pleroma/delete_account", %{"password" => "test"}) - + ObanHelpers.perform_all() assert json_response(conn, 200) == %{"status" => "success"} + + user = User.get_by_id(user.id) + assert user.deactivated == true + assert user.name == nil + assert user.bio == "" + assert user.password_hash == nil end end end diff --git a/test/web/web_finger/web_finger_test.exs b/test/web/web_finger/web_finger_test.exs index f4884e0a2..96fc0bbaa 100644 --- a/test/web/web_finger/web_finger_test.exs +++ b/test/web/web_finger/web_finger_test.exs @@ -40,6 +40,11 @@ defmodule Pleroma.Web.WebFingerTest do end describe "fingering" do + test "returns error for nonsensical input" do + assert {:error, _} = WebFinger.finger("bliblablu") + assert {:error, _} = WebFinger.finger("pleroma.social") + end + test "returns error when fails parse xml or json" do user = "invalid_content@social.heldscal.la" assert {:error, %Jason.DecodeError{}} = WebFinger.finger(user) |