diff options
Diffstat (limited to 'test')
24 files changed, 1323 insertions, 171 deletions
diff --git a/test/emoji_test.exs b/test/emoji_test.exs index 1fdbd0fdf..7bdf2b6fa 100644 --- a/test/emoji_test.exs +++ b/test/emoji_test.exs @@ -6,6 +6,14 @@ defmodule Pleroma.EmojiTest do use ExUnit.Case, async: true alias Pleroma.Emoji + describe "is_unicode_emoji?/1" do + test "tells if a string is an unicode emoji" do + refute Emoji.is_unicode_emoji?("X") + assert Emoji.is_unicode_emoji?("☂") + assert Emoji.is_unicode_emoji?("🥺") + end + end + describe "get_all/0" do setup do emoji_list = Emoji.get_all() diff --git a/test/fixtures/emoji-reaction.json b/test/fixtures/emoji-reaction.json new file mode 100644 index 000000000..3812e43ad --- /dev/null +++ b/test/fixtures/emoji-reaction.json @@ -0,0 +1,30 @@ +{ + "type": "EmojiReaction", + "signature": { + "type": "RsaSignature2017", + "signatureValue": "fdxMfQSMwbC6wP6sh6neS/vM5879K67yQkHTbiT5Npr5wAac0y6+o3Ij+41tN3rL6wfuGTosSBTHOtta6R4GCOOhCaCSLMZKypnp1VltCzLDoyrZELnYQIC8gpUXVmIycZbREk22qWUe/w7DAFaKK4UscBlHDzeDVcA0K3Se5Sluqi9/Zh+ldAnEzj/rSEPDjrtvf5wGNf3fHxbKSRKFt90JvKK6hS+vxKUhlRFDf6/SMETw+EhwJSNW4d10yMUakqUWsFv4Acq5LW7l+HpYMvlYY1FZhNde1+uonnCyuQDyvzkff8zwtEJmAXC4RivO/VVLa17SmqheJZfI8oluVg==", + "creator": "http://mastodon.example.org/users/admin#main-key", + "created": "2018-02-17T18:57:49Z" + }, + "object": "http://localtesting.pleroma.lol/objects/eb92579d-3417-42a8-8652-2492c2d4f454", + "content": "👌", + "nickname": "lain", + "id": "http://mastodon.example.org/users/admin#reactions/2", + "actor": "http://mastodon.example.org/users/admin", + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1", + { + "toot": "http://joinmastodon.org/ns#", + "sensitive": "as:sensitive", + "ostatus": "http://ostatus.org#", + "movedTo": "as:movedTo", + "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", + "inReplyToAtomUri": "ostatus:inReplyToAtomUri", + "conversation": "ostatus:conversation", + "atomUri": "ostatus:atomUri", + "Hashtag": "as:Hashtag", + "Emoji": "toot:Emoji" + } + ] +} diff --git a/test/fixtures/misskey-like.json b/test/fixtures/misskey-like.json new file mode 100644 index 000000000..84d56f473 --- /dev/null +++ b/test/fixtures/misskey-like.json @@ -0,0 +1,14 @@ +{ + "@context" : [ + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1", + {"Hashtag" : "as:Hashtag"} + ], + "_misskey_reaction" : "pudding", + "actor": "http://mastodon.example.org/users/admin", + "cc" : ["https://testing.pleroma.lol/users/lain"], + "id" : "https://misskey.xyz/75149198-2f45-46e4-930a-8b0538297075", + "nickname" : "lain", + "object" : "https://testing.pleroma.lol/objects/c331bbf7-2eb9-4801-a709-2a6103492a5a", + "type" : "Like" +} diff --git a/test/object/containment_test.exs b/test/object/containment_test.exs index 0dc2728b9..7636803a6 100644 --- a/test/object/containment_test.exs +++ b/test/object/containment_test.exs @@ -17,6 +17,16 @@ defmodule Pleroma.Object.ContainmentTest do end describe "general origin containment" do + test "works for completely actorless posts" do + assert :error == + Containment.contain_origin("https://glaceon.social/users/monorail", %{ + "deleted" => "2019-10-30T05:48:50.249606Z", + "formerType" => "Note", + "id" => "https://glaceon.social/users/monorail/statuses/103049757364029187", + "type" => "Tombstone" + }) + end + test "contain_origin_from_id() catches obvious spoofing attempts" do data = %{ "id" => "http://example.com/~alyssa/activities/1234.json" @@ -67,6 +77,20 @@ defmodule Pleroma.Object.ContainmentTest do end) =~ "[error] Could not decode user at fetch https://n1u.moe/users/rye" end + + test "contain_origin_from_id() gracefully handles cases where no ID is present" do + data = %{ + "type" => "Create", + "object" => %{ + "id" => "http://example.net/~alyssa/activities/1234", + "attributedTo" => "http://example.org/~alyssa" + }, + "actor" => "http://example.com/~bob" + } + + :error = + Containment.contain_origin_from_id("http://example.net/~alyssa/activities/1234", data) + end end describe "containment of children" do diff --git a/test/object_test.exs b/test/object_test.exs index dd228c32f..9247a6d84 100644 --- a/test/object_test.exs +++ b/test/object_test.exs @@ -124,6 +124,8 @@ defmodule Pleroma.ObjectTest do %Object{} = object = Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d") + Object.set_cache(object) + assert Enum.at(object.data["oneOf"], 0)["replies"]["totalItems"] == 4 assert Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 0 @@ -133,6 +135,8 @@ defmodule Pleroma.ObjectTest do }) updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: -1) + object_in_cache = Object.get_cached_by_ap_id(object.data["id"]) + assert updated_object == object_in_cache assert Enum.at(updated_object.data["oneOf"], 0)["replies"]["totalItems"] == 8 assert Enum.at(updated_object.data["oneOf"], 1)["replies"]["totalItems"] == 3 end @@ -141,6 +145,8 @@ defmodule Pleroma.ObjectTest do %Object{} = object = Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d") + Object.set_cache(object) + assert Enum.at(object.data["oneOf"], 0)["replies"]["totalItems"] == 4 assert Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 0 @@ -148,6 +154,8 @@ defmodule Pleroma.ObjectTest do mock_modified.(%Tesla.Env{status: 404, body: ""}) updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: -1) + object_in_cache = Object.get_cached_by_ap_id(object.data["id"]) + assert updated_object == object_in_cache assert Enum.at(updated_object.data["oneOf"], 0)["replies"]["totalItems"] == 4 assert Enum.at(updated_object.data["oneOf"], 1)["replies"]["totalItems"] == 0 end) =~ @@ -160,6 +168,8 @@ defmodule Pleroma.ObjectTest do %Object{} = object = Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d") + Object.set_cache(object) + assert Enum.at(object.data["oneOf"], 0)["replies"]["totalItems"] == 4 assert Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 0 @@ -169,6 +179,8 @@ defmodule Pleroma.ObjectTest do }) updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: 100) + object_in_cache = Object.get_cached_by_ap_id(object.data["id"]) + assert updated_object == object_in_cache assert Enum.at(updated_object.data["oneOf"], 0)["replies"]["totalItems"] == 4 assert Enum.at(updated_object.data["oneOf"], 1)["replies"]["totalItems"] == 0 end @@ -177,6 +189,8 @@ defmodule Pleroma.ObjectTest do %Object{} = object = Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d") + Object.set_cache(object) + assert Enum.at(object.data["oneOf"], 0)["replies"]["totalItems"] == 4 assert Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 0 @@ -192,6 +206,8 @@ defmodule Pleroma.ObjectTest do }) updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: -1) + object_in_cache = Object.get_cached_by_ap_id(object.data["id"]) + assert updated_object == object_in_cache assert Enum.at(updated_object.data["oneOf"], 0)["replies"]["totalItems"] == 8 assert Enum.at(updated_object.data["oneOf"], 1)["replies"]["totalItems"] == 3 diff --git a/test/plugs/admin_secret_authentication_plug_test.exs b/test/plugs/admin_secret_authentication_plug_test.exs index c94a62c10..506b1f609 100644 --- a/test/plugs/admin_secret_authentication_plug_test.exs +++ b/test/plugs/admin_secret_authentication_plug_test.exs @@ -22,21 +22,39 @@ defmodule Pleroma.Plugs.AdminSecretAuthenticationPlugTest do assert conn == ret_conn end - test "with secret set and given in the 'admin_token' parameter, it assigns an admin user", %{ - conn: conn - } do - Pleroma.Config.put(:admin_token, "password123") + describe "when secret set it assigns an admin user" do + test "with `admin_token` query parameter", %{conn: conn} do + Pleroma.Config.put(:admin_token, "password123") - conn = - %{conn | params: %{"admin_token" => "wrong_password"}} - |> AdminSecretAuthenticationPlug.call(%{}) + conn = + %{conn | params: %{"admin_token" => "wrong_password"}} + |> AdminSecretAuthenticationPlug.call(%{}) - refute conn.assigns[:user] + refute conn.assigns[:user] - conn = - %{conn | params: %{"admin_token" => "password123"}} - |> AdminSecretAuthenticationPlug.call(%{}) + conn = + %{conn | params: %{"admin_token" => "password123"}} + |> AdminSecretAuthenticationPlug.call(%{}) + + assert conn.assigns[:user].is_admin + end + + test "with `x-admin-token` HTTP header", %{conn: conn} do + Pleroma.Config.put(:admin_token, "☕️") + + conn = + conn + |> put_req_header("x-admin-token", "🥛") + |> AdminSecretAuthenticationPlug.call(%{}) + + refute conn.assigns[:user] + + conn = + conn + |> put_req_header("x-admin-token", "☕️") + |> AdminSecretAuthenticationPlug.call(%{}) - assert conn.assigns[:user].is_admin + assert conn.assigns[:user].is_admin + end end end diff --git a/test/plugs/cache_control_test.exs b/test/plugs/cache_control_test.exs index 69ce6cc7d..be78b3e1e 100644 --- a/test/plugs/cache_control_test.exs +++ b/test/plugs/cache_control_test.exs @@ -9,7 +9,7 @@ defmodule Pleroma.Web.CacheControlTest do test "Verify Cache-Control header on static assets", %{conn: conn} do conn = get(conn, "/index.html") - assert Conn.get_resp_header(conn, "cache-control") == ["public, no-cache"] + assert Conn.get_resp_header(conn, "cache-control") == ["public max-age=86400 must-revalidate"] end test "Verify Cache-Control header on the API", %{conn: conn} do diff --git a/test/plugs/rate_limiter_test.exs b/test/plugs/rate_limiter_test.exs index 395095079..49f63c424 100644 --- a/test/plugs/rate_limiter_test.exs +++ b/test/plugs/rate_limiter_test.exs @@ -12,163 +12,196 @@ defmodule Pleroma.Plugs.RateLimiterTest do # Note: each example must work with separate buckets in order to prevent concurrency issues - test "init/1" do - limiter_name = :test_init - Pleroma.Config.put([:rate_limit, limiter_name], {1, 1}) + describe "config" do + test "config is required for plug to work" do + limiter_name = :test_init + Pleroma.Config.put([:rate_limit, limiter_name], {1, 1}) - assert {limiter_name, {1, 1}, []} == RateLimiter.init(limiter_name) - assert nil == RateLimiter.init(:foo) - end + assert %{limits: {1, 1}, name: :test_init, opts: [name: :test_init]} == + RateLimiter.init(name: limiter_name) - test "ip/1" do - assert "127.0.0.1" == RateLimiter.ip(%{remote_ip: {127, 0, 0, 1}}) - end + assert nil == RateLimiter.init(name: :foo) + end - test "it restricts by opts" do - limiter_name = :test_opts - scale = 1000 - limit = 5 + test "it restricts based on config values" do + limiter_name = :test_opts + scale = 80 + limit = 5 - Pleroma.Config.put([:rate_limit, limiter_name], {scale, limit}) + Pleroma.Config.put([:rate_limit, limiter_name], {scale, limit}) - opts = RateLimiter.init(limiter_name) - conn = conn(:get, "/") - bucket_name = "#{limiter_name}:#{RateLimiter.ip(conn)}" + opts = RateLimiter.init(name: limiter_name) + conn = conn(:get, "/") - conn = RateLimiter.call(conn, opts) - assert {1, 4, _, _, _} = ExRated.inspect_bucket(bucket_name, scale, limit) + for i <- 1..5 do + conn = RateLimiter.call(conn, opts) + assert {^i, _} = RateLimiter.inspect_bucket(conn, limiter_name, opts) + Process.sleep(10) + end - conn = RateLimiter.call(conn, opts) - assert {2, 3, _, _, _} = ExRated.inspect_bucket(bucket_name, scale, limit) + conn = RateLimiter.call(conn, opts) + assert %{"error" => "Throttled"} = Phoenix.ConnTest.json_response(conn, :too_many_requests) + assert conn.halted - conn = RateLimiter.call(conn, opts) - assert {3, 2, _, _, _} = ExRated.inspect_bucket(bucket_name, scale, limit) + Process.sleep(50) - conn = RateLimiter.call(conn, opts) - assert {4, 1, _, _, _} = ExRated.inspect_bucket(bucket_name, scale, limit) + conn = conn(:get, "/") - conn = RateLimiter.call(conn, opts) - assert {5, 0, to_reset, _, _} = ExRated.inspect_bucket(bucket_name, scale, limit) + conn = RateLimiter.call(conn, opts) + assert {1, 4} = RateLimiter.inspect_bucket(conn, limiter_name, opts) - conn = RateLimiter.call(conn, opts) + refute conn.status == Plug.Conn.Status.code(:too_many_requests) + refute conn.resp_body + refute conn.halted + end + end - assert %{"error" => "Throttled"} = Phoenix.ConnTest.json_response(conn, :too_many_requests) - assert conn.halted + describe "options" do + test "`bucket_name` option overrides default bucket name" do + limiter_name = :test_bucket_name - Process.sleep(to_reset) + Pleroma.Config.put([:rate_limit, limiter_name], {1000, 5}) - conn = conn(:get, "/") + base_bucket_name = "#{limiter_name}:group1" + opts = RateLimiter.init(name: limiter_name, bucket_name: base_bucket_name) - conn = RateLimiter.call(conn, opts) - assert {1, 4, _, _, _} = ExRated.inspect_bucket(bucket_name, scale, limit) + conn = conn(:get, "/") - refute conn.status == Plug.Conn.Status.code(:too_many_requests) - refute conn.resp_body - refute conn.halted - end + RateLimiter.call(conn, opts) + assert {1, 4} = RateLimiter.inspect_bucket(conn, base_bucket_name, opts) + assert {:err, :not_found} = RateLimiter.inspect_bucket(conn, limiter_name, opts) + end - test "`bucket_name` option overrides default bucket name" do - limiter_name = :test_bucket_name - scale = 1000 - limit = 5 + test "`params` option allows different queries to be tracked independently" do + limiter_name = :test_params + Pleroma.Config.put([:rate_limit, limiter_name], {1000, 5}) - Pleroma.Config.put([:rate_limit, limiter_name], {scale, limit}) - base_bucket_name = "#{limiter_name}:group1" - opts = RateLimiter.init({limiter_name, bucket_name: base_bucket_name}) + opts = RateLimiter.init(name: limiter_name, params: ["id"]) - conn = conn(:get, "/") - default_bucket_name = "#{limiter_name}:#{RateLimiter.ip(conn)}" - customized_bucket_name = "#{base_bucket_name}:#{RateLimiter.ip(conn)}" + conn = conn(:get, "/?id=1") + conn = Plug.Conn.fetch_query_params(conn) + conn_2 = conn(:get, "/?id=2") - RateLimiter.call(conn, opts) - assert {1, 4, _, _, _} = ExRated.inspect_bucket(customized_bucket_name, scale, limit) - assert {0, 5, _, _, _} = ExRated.inspect_bucket(default_bucket_name, scale, limit) - end - - test "`params` option appends specified params' values to bucket name" do - limiter_name = :test_params - scale = 1000 - limit = 5 + RateLimiter.call(conn, opts) + assert {1, 4} = RateLimiter.inspect_bucket(conn, limiter_name, opts) + assert {0, 5} = RateLimiter.inspect_bucket(conn_2, limiter_name, opts) + end - Pleroma.Config.put([:rate_limit, limiter_name], {scale, limit}) - opts = RateLimiter.init({limiter_name, params: ["id"]}) - id = "1" + test "it supports combination of options modifying bucket name" do + limiter_name = :test_options_combo + Pleroma.Config.put([:rate_limit, limiter_name], {1000, 5}) - conn = conn(:get, "/?id=#{id}") - conn = Plug.Conn.fetch_query_params(conn) + base_bucket_name = "#{limiter_name}:group1" + opts = RateLimiter.init(name: limiter_name, bucket_name: base_bucket_name, params: ["id"]) + id = "100" - default_bucket_name = "#{limiter_name}:#{RateLimiter.ip(conn)}" - parametrized_bucket_name = "#{limiter_name}:#{id}:#{RateLimiter.ip(conn)}" + conn = conn(:get, "/?id=#{id}") + conn = Plug.Conn.fetch_query_params(conn) + conn_2 = conn(:get, "/?id=#{101}") - RateLimiter.call(conn, opts) - assert {1, 4, _, _, _} = ExRated.inspect_bucket(parametrized_bucket_name, scale, limit) - assert {0, 5, _, _, _} = ExRated.inspect_bucket(default_bucket_name, scale, limit) + RateLimiter.call(conn, opts) + assert {1, 4} = RateLimiter.inspect_bucket(conn, base_bucket_name, opts) + assert {0, 5} = RateLimiter.inspect_bucket(conn_2, base_bucket_name, opts) + end end - test "it supports combination of options modifying bucket name" do - limiter_name = :test_options_combo - scale = 1000 - limit = 5 + describe "unauthenticated users" do + test "are restricted based on remote IP" do + limiter_name = :test_unauthenticated + Pleroma.Config.put([:rate_limit, limiter_name], [{1000, 5}, {1, 10}]) + + opts = RateLimiter.init(name: limiter_name) - Pleroma.Config.put([:rate_limit, limiter_name], {scale, limit}) - base_bucket_name = "#{limiter_name}:group1" - opts = RateLimiter.init({limiter_name, bucket_name: base_bucket_name, params: ["id"]}) - id = "100" + conn = %{conn(:get, "/") | remote_ip: {127, 0, 0, 2}} + conn_2 = %{conn(:get, "/") | remote_ip: {127, 0, 0, 3}} - conn = conn(:get, "/?id=#{id}") - conn = Plug.Conn.fetch_query_params(conn) + for i <- 1..5 do + conn = RateLimiter.call(conn, opts) + assert {^i, _} = RateLimiter.inspect_bucket(conn, limiter_name, opts) + refute conn.halted + end - default_bucket_name = "#{limiter_name}:#{RateLimiter.ip(conn)}" - parametrized_bucket_name = "#{base_bucket_name}:#{id}:#{RateLimiter.ip(conn)}" + conn = RateLimiter.call(conn, opts) - RateLimiter.call(conn, opts) - assert {1, 4, _, _, _} = ExRated.inspect_bucket(parametrized_bucket_name, scale, limit) - assert {0, 5, _, _, _} = ExRated.inspect_bucket(default_bucket_name, scale, limit) + assert %{"error" => "Throttled"} = Phoenix.ConnTest.json_response(conn, :too_many_requests) + assert conn.halted + + conn_2 = RateLimiter.call(conn_2, opts) + assert {1, 4} = RateLimiter.inspect_bucket(conn_2, limiter_name, opts) + + refute conn_2.status == Plug.Conn.Status.code(:too_many_requests) + refute conn_2.resp_body + refute conn_2.halted + end end - test "optional limits for authenticated users" do - limiter_name = :test_authenticated - Ecto.Adapters.SQL.Sandbox.checkout(Pleroma.Repo) + describe "authenticated users" do + setup do + Ecto.Adapters.SQL.Sandbox.checkout(Pleroma.Repo) + + :ok + end + + test "can have limits seperate from unauthenticated connections" do + limiter_name = :test_authenticated + + scale = 1000 + limit = 5 + Pleroma.Config.put([:rate_limit, limiter_name], [{1, 10}, {scale, limit}]) + + opts = RateLimiter.init(name: limiter_name) - scale = 1000 - limit = 5 - Pleroma.Config.put([:rate_limit, limiter_name], [{1, 10}, {scale, limit}]) + user = insert(:user) + conn = conn(:get, "/") |> assign(:user, user) - opts = RateLimiter.init(limiter_name) + for i <- 1..5 do + conn = RateLimiter.call(conn, opts) + assert {^i, _} = RateLimiter.inspect_bucket(conn, limiter_name, opts) + refute conn.halted + end - user = insert(:user) - conn = conn(:get, "/") |> assign(:user, user) - bucket_name = "#{limiter_name}:#{user.id}" + conn = RateLimiter.call(conn, opts) - conn = RateLimiter.call(conn, opts) - assert {1, 4, _, _, _} = ExRated.inspect_bucket(bucket_name, scale, limit) + assert %{"error" => "Throttled"} = Phoenix.ConnTest.json_response(conn, :too_many_requests) + assert conn.halted - conn = RateLimiter.call(conn, opts) - assert {2, 3, _, _, _} = ExRated.inspect_bucket(bucket_name, scale, limit) + Process.sleep(1550) - conn = RateLimiter.call(conn, opts) - assert {3, 2, _, _, _} = ExRated.inspect_bucket(bucket_name, scale, limit) + conn = conn(:get, "/") |> assign(:user, user) + conn = RateLimiter.call(conn, opts) + assert {1, 4} = RateLimiter.inspect_bucket(conn, limiter_name, opts) - conn = RateLimiter.call(conn, opts) - assert {4, 1, _, _, _} = ExRated.inspect_bucket(bucket_name, scale, limit) + refute conn.status == Plug.Conn.Status.code(:too_many_requests) + refute conn.resp_body + refute conn.halted + end - conn = RateLimiter.call(conn, opts) - assert {5, 0, to_reset, _, _} = ExRated.inspect_bucket(bucket_name, scale, limit) + test "diffrerent users are counted independently" do + limiter_name = :test_authenticated + Pleroma.Config.put([:rate_limit, limiter_name], [{1, 10}, {1000, 5}]) - conn = RateLimiter.call(conn, opts) + opts = RateLimiter.init(name: limiter_name) - assert %{"error" => "Throttled"} = Phoenix.ConnTest.json_response(conn, :too_many_requests) - assert conn.halted + user = insert(:user) + conn = conn(:get, "/") |> assign(:user, user) - Process.sleep(to_reset) + user_2 = insert(:user) + conn_2 = conn(:get, "/") |> assign(:user, user_2) - conn = conn(:get, "/") |> assign(:user, user) + for i <- 1..5 do + conn = RateLimiter.call(conn, opts) + assert {^i, _} = RateLimiter.inspect_bucket(conn, limiter_name, opts) + end - conn = RateLimiter.call(conn, opts) - assert {1, 4, _, _, _} = ExRated.inspect_bucket(bucket_name, scale, limit) + conn = RateLimiter.call(conn, opts) + assert %{"error" => "Throttled"} = Phoenix.ConnTest.json_response(conn, :too_many_requests) + assert conn.halted - refute conn.status == Plug.Conn.Status.code(:too_many_requests) - refute conn.resp_body - refute conn.halted + conn_2 = RateLimiter.call(conn_2, opts) + assert {1, 4} = RateLimiter.inspect_bucket(conn_2, limiter_name, opts) + refute conn_2.status == Plug.Conn.Status.code(:too_many_requests) + refute conn_2.resp_body + refute conn_2.halted + end end end diff --git a/test/plugs/user_enabled_plug_test.exs b/test/plugs/user_enabled_plug_test.exs index 996a7d77b..a4035bf0e 100644 --- a/test/plugs/user_enabled_plug_test.exs +++ b/test/plugs/user_enabled_plug_test.exs @@ -16,6 +16,23 @@ defmodule Pleroma.Plugs.UserEnabledPlugTest do assert ret_conn == conn end + test "with a user that's not confirmed and a config requiring confirmation, it removes that user", + %{conn: conn} do + old = Pleroma.Config.get([:instance, :account_activation_required]) + Pleroma.Config.put([:instance, :account_activation_required], true) + + user = insert(:user, confirmation_pending: true) + + conn = + conn + |> assign(:user, user) + |> UserEnabledPlug.call(%{}) + + assert conn.assigns.user == nil + + Pleroma.Config.put([:instance, :account_activation_required], old) + end + test "with a user that is deactivated, it removes that user", %{conn: conn} do user = insert(:user, deactivated: true) diff --git a/test/user_test.exs b/test/user_test.exs index 485106b75..e6302b525 100644 --- a/test/user_test.exs +++ b/test/user_test.exs @@ -167,9 +167,10 @@ defmodule Pleroma.UserTest do {:ok, user} = User.follow(user, followed) user = User.get_cached_by_id(user.id) - followed = User.get_cached_by_ap_id(followed.ap_id) + assert followed.follower_count == 1 + assert user.following_count == 1 assert User.ap_followers(followed) in User.following(user) end @@ -971,12 +972,14 @@ defmodule Pleroma.UserTest do user2 = insert(:user) {:ok, user2} = User.follow(user2, user) + assert user2.following_count == 1 assert User.following_count(user2) == 1 {:ok, _user} = User.deactivate(user) info = User.get_cached_user_info(user2) + assert refresh_record(user2).following_count == 0 assert info.following_count == 0 assert User.following_count(user2) == 0 assert [] = User.get_friends(user2) @@ -1214,6 +1217,13 @@ defmodule Pleroma.UserTest do refute User.auth_active?(local_user) assert User.auth_active?(confirmed_user) assert User.auth_active?(remote_user) + + # also shows unactive for deactivated users + + deactivated_but_confirmed = + insert(:user, local: true, confirmation_pending: false, deactivated: true) + + refute User.auth_active?(deactivated_but_confirmed) end describe "superuser?/1" do diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs index f29b8cc74..d437ad456 100644 --- a/test/web/activity_pub/activity_pub_test.exs +++ b/test/web/activity_pub/activity_pub_test.exs @@ -734,56 +734,54 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do end test "retrieves a maximum of 20 activities" do - activities = ActivityBuilder.insert_list(30) - last_expected = List.last(activities) + ActivityBuilder.insert_list(10) + expected_activities = ActivityBuilder.insert_list(20) activities = ActivityPub.fetch_public_activities() - last = List.last(activities) + assert collect_ids(activities) == collect_ids(expected_activities) assert length(activities) == 20 - assert last == last_expected end test "retrieves ids starting from a since_id" do activities = ActivityBuilder.insert_list(30) - later_activities = ActivityBuilder.insert_list(10) + expected_activities = ActivityBuilder.insert_list(10) since_id = List.last(activities).id - last_expected = List.last(later_activities) activities = ActivityPub.fetch_public_activities(%{"since_id" => since_id}) - last = List.last(activities) + assert collect_ids(activities) == collect_ids(expected_activities) assert length(activities) == 10 - assert last == last_expected end test "retrieves ids up to max_id" do - _first_activities = ActivityBuilder.insert_list(10) - activities = ActivityBuilder.insert_list(20) - later_activities = ActivityBuilder.insert_list(10) - max_id = List.first(later_activities).id - last_expected = List.last(activities) + ActivityBuilder.insert_list(10) + expected_activities = ActivityBuilder.insert_list(20) + + %{id: max_id} = + 10 + |> ActivityBuilder.insert_list() + |> List.first() activities = ActivityPub.fetch_public_activities(%{"max_id" => max_id}) - last = List.last(activities) assert length(activities) == 20 - assert last == last_expected + assert collect_ids(activities) == collect_ids(expected_activities) end test "paginates via offset/limit" do - _first_activities = ActivityBuilder.insert_list(10) - activities = ActivityBuilder.insert_list(10) - _later_activities = ActivityBuilder.insert_list(10) - first_expected = List.first(activities) + _first_part_activities = ActivityBuilder.insert_list(10) + second_part_activities = ActivityBuilder.insert_list(10) + + later_activities = ActivityBuilder.insert_list(10) activities = ActivityPub.fetch_public_activities(%{"page" => "2", "page_size" => "20"}, :offset) - first = List.first(activities) - assert length(activities) == 20 - assert first == first_expected + + assert collect_ids(activities) == + collect_ids(second_part_activities) ++ collect_ids(later_activities) end test "doesn't return reblogs for users for whom reblogs have been muted" do @@ -814,6 +812,78 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do end end + describe "react to an object" do + test_with_mock "sends an activity to federation", Pleroma.Web.Federator, [:passthrough], [] do + Pleroma.Config.put([:instance, :federating], true) + user = insert(:user) + reactor = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{"status" => "YASSSS queen slay"}) + assert object = Object.normalize(activity) + + {:ok, reaction_activity, _object} = ActivityPub.react_with_emoji(reactor, object, "🔥") + + assert called(Pleroma.Web.Federator.publish(reaction_activity)) + end + + test "adds an emoji reaction activity to the db" do + user = insert(:user) + reactor = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{"status" => "YASSSS queen slay"}) + assert object = Object.normalize(activity) + + {:ok, reaction_activity, object} = ActivityPub.react_with_emoji(reactor, object, "🔥") + + assert reaction_activity + + assert reaction_activity.data["actor"] == reactor.ap_id + assert reaction_activity.data["type"] == "EmojiReaction" + assert reaction_activity.data["content"] == "🔥" + assert reaction_activity.data["object"] == object.data["id"] + assert reaction_activity.data["to"] == [User.ap_followers(reactor), activity.data["actor"]] + assert reaction_activity.data["context"] == object.data["context"] + assert object.data["reaction_count"] == 1 + assert object.data["reactions"]["🔥"] == [reactor.ap_id] + end + end + + describe "unreacting to an object" do + test_with_mock "sends an activity to federation", Pleroma.Web.Federator, [:passthrough], [] do + Pleroma.Config.put([:instance, :federating], true) + user = insert(:user) + reactor = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{"status" => "YASSSS queen slay"}) + assert object = Object.normalize(activity) + + {:ok, reaction_activity, _object} = ActivityPub.react_with_emoji(reactor, object, "🔥") + + assert called(Pleroma.Web.Federator.publish(reaction_activity)) + + {:ok, unreaction_activity, _object} = + ActivityPub.unreact_with_emoji(reactor, reaction_activity.data["id"]) + + assert called(Pleroma.Web.Federator.publish(unreaction_activity)) + end + + test "adds an undo activity to the db" do + user = insert(:user) + reactor = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{"status" => "YASSSS queen slay"}) + assert object = Object.normalize(activity) + + {:ok, reaction_activity, _object} = ActivityPub.react_with_emoji(reactor, object, "🔥") + + {:ok, unreaction_activity, _object} = + ActivityPub.unreact_with_emoji(reactor, reaction_activity.data["id"]) + + assert unreaction_activity.actor == reactor.ap_id + assert unreaction_activity.data["object"] == reaction_activity.data["id"] + + object = Object.get_by_ap_id(object.data["id"]) + assert object.data["reaction_count"] == 0 + assert object.data["reactions"] == %{} + end + end + describe "like an object" do test_with_mock "sends an activity to federation", Pleroma.Web.Federator, [:passthrough], [] do Pleroma.Config.put([:instance, :federating], true) diff --git a/test/web/activity_pub/mrf/object_age_policy_test.exs b/test/web/activity_pub/mrf/object_age_policy_test.exs new file mode 100644 index 000000000..643609da4 --- /dev/null +++ b/test/web/activity_pub/mrf/object_age_policy_test.exs @@ -0,0 +1,105 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicyTest do + use Pleroma.DataCase + alias Pleroma.Config + alias Pleroma.User + alias Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy + alias Pleroma.Web.ActivityPub.Visibility + + clear_config([:mrf_object_age]) do + Config.put(:mrf_object_age, + threshold: 172_800, + actions: [:delist, :strip_followers] + ) + end + + setup_all do + Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + + describe "with reject action" do + test "it rejects an old post" do + Config.put([:mrf_object_age, :actions], [:reject]) + + data = + File.read!("test/fixtures/mastodon-post-activity.json") + |> Poison.decode!() + + {:reject, _} = ObjectAgePolicy.filter(data) + end + + test "it allows a new post" do + Config.put([:mrf_object_age, :actions], [:reject]) + + data = + File.read!("test/fixtures/mastodon-post-activity.json") + |> Poison.decode!() + |> Map.put("published", DateTime.utc_now() |> DateTime.to_iso8601()) + + {:ok, _} = ObjectAgePolicy.filter(data) + end + end + + describe "with delist action" do + test "it delists an old post" do + Config.put([:mrf_object_age, :actions], [:delist]) + + data = + File.read!("test/fixtures/mastodon-post-activity.json") + |> Poison.decode!() + + {: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 allows a new post" do + Config.put([:mrf_object_age, :actions], [:delist]) + + data = + File.read!("test/fixtures/mastodon-post-activity.json") + |> Poison.decode!() + |> Map.put("published", DateTime.utc_now() |> DateTime.to_iso8601()) + + {:ok, _user} = User.get_or_fetch_by_ap_id(data["actor"]) + + {:ok, ^data} = ObjectAgePolicy.filter(data) + end + end + + describe "with strip_followers action" do + test "it strips followers collections from an old post" do + Config.put([:mrf_object_age, :actions], [:strip_followers]) + + data = + File.read!("test/fixtures/mastodon-post-activity.json") + |> Poison.decode!() + + {: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 allows a new post" do + Config.put([:mrf_object_age, :actions], [:strip_followers]) + + data = + File.read!("test/fixtures/mastodon-post-activity.json") + |> Poison.decode!() + |> Map.put("published", DateTime.utc_now() |> DateTime.to_iso8601()) + + {:ok, _u} = User.get_or_fetch_by_ap_id(data["actor"]) + + {:ok, ^data} = ObjectAgePolicy.filter(data) + end + end +end diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs index 4645eb39d..0bdd514e9 100644 --- a/test/web/activity_pub/transmogrifier_test.exs +++ b/test/web/activity_pub/transmogrifier_test.exs @@ -339,6 +339,80 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do assert data["object"] == activity.data["object"] end + test "it works for incoming misskey likes, turning them into EmojiReactions" do + user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{"status" => "hello"}) + + data = + File.read!("test/fixtures/misskey-like.json") + |> Poison.decode!() + |> Map.put("object", activity.data["object"]) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["actor"] == data["actor"] + assert data["type"] == "EmojiReaction" + assert data["id"] == data["id"] + assert data["object"] == activity.data["object"] + assert data["content"] == "🍮" + end + + test "it works for incoming misskey likes that contain unicode emojis, turning them into EmojiReactions" do + user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{"status" => "hello"}) + + data = + File.read!("test/fixtures/misskey-like.json") + |> Poison.decode!() + |> Map.put("object", activity.data["object"]) + |> Map.put("_misskey_reaction", "⭐") + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["actor"] == data["actor"] + assert data["type"] == "EmojiReaction" + assert data["id"] == data["id"] + assert data["object"] == activity.data["object"] + assert data["content"] == "⭐" + end + + test "it works for incoming emoji reactions" do + user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{"status" => "hello"}) + + data = + File.read!("test/fixtures/emoji-reaction.json") + |> Poison.decode!() + |> Map.put("object", activity.data["object"]) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["actor"] == "http://mastodon.example.org/users/admin" + assert data["type"] == "EmojiReaction" + assert data["id"] == "http://mastodon.example.org/users/admin#reactions/2" + assert data["object"] == activity.data["object"] + assert data["content"] == "👌" + end + + test "it works for incoming emoji reaction undos" do + user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{"status" => "hello"}) + {:ok, reaction_activity, _object} = CommonAPI.react_with_emoji(activity.id, user, "👌") + + data = + File.read!("test/fixtures/mastodon-undo-like.json") + |> Poison.decode!() + |> Map.put("object", reaction_activity.data["id"]) + |> Map.put("actor", user.ap_id) + + {:ok, activity} = Transmogrifier.handle_incoming(data) + + assert activity.actor == user.ap_id + assert activity.data["id"] == data["id"] + assert activity.data["type"] == "Undo" + end + test "it returns an error for incoming unlikes wihout a like activity" do user = insert(:user) {:ok, activity} = CommonAPI.post(user, %{"status" => "leave a like pls"}) @@ -553,6 +627,20 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do refute Map.has_key?(object.data, "likes") end + test "it strips internal reactions" do + user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{"status" => "#cofe"}) + {:ok, _, _} = CommonAPI.react_with_emoji(activity.id, user, "📢") + + %{object: object} = Activity.get_by_id_with_object(activity.id) + assert Map.has_key?(object.data, "reactions") + assert Map.has_key?(object.data, "reaction_count") + + object_data = Transmogrifier.strip_internal_fields(object.data) + refute Map.has_key?(object_data, "reactions") + 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!() diff --git a/test/web/activity_pub/utils_test.exs b/test/web/activity_pub/utils_test.exs index 586eb1d2f..1feb076ba 100644 --- a/test/web/activity_pub/utils_test.exs +++ b/test/web/activity_pub/utils_test.exs @@ -636,4 +636,47 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do assert updated_object.data["announcement_count"] == 1 end end + + describe "get_reports_grouped_by_status/1" do + setup do + [reporter, target_user] = insert_pair(:user) + first_status = insert(:note_activity, user: target_user) + second_status = insert(:note_activity, user: target_user) + + CommonAPI.report(reporter, %{ + "account_id" => target_user.id, + "comment" => "I feel offended", + "status_ids" => [first_status.id] + }) + + CommonAPI.report(reporter, %{ + "account_id" => target_user.id, + "comment" => "I feel offended2", + "status_ids" => [second_status.id] + }) + + data = [%{activity: first_status.data["id"]}, %{activity: second_status.data["id"]}] + + {:ok, + %{ + first_status: first_status, + second_status: second_status, + data: data + }} + end + + test "works for deprecated reports format", %{ + first_status: first_status, + second_status: second_status, + data: data + } do + groups = Utils.get_reports_grouped_by_status(data).groups + + first_group = Enum.find(groups, &(&1.status.id == first_status.data["id"])) + second_group = Enum.find(groups, &(&1.status.id == second_status.data["id"])) + + assert first_group.status.id == first_status.data["id"] + assert second_group.status.id == second_status.data["id"] + end + end end diff --git a/test/web/admin_api/admin_api_controller_test.exs b/test/web/admin_api/admin_api_controller_test.exs index 045c87e95..3a4c4d65c 100644 --- a/test/web/admin_api/admin_api_controller_test.exs +++ b/test/web/admin_api/admin_api_controller_test.exs @@ -1312,7 +1312,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do end end - describe "PUT /api/pleroma/admin/reports/:id" do + describe "PATCH /api/pleroma/admin/reports" do setup %{conn: conn} do admin = insert(:user, is_admin: true) [reporter, target_user] = insert_pair(:user) @@ -1325,16 +1325,32 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do "status_ids" => [activity.id] }) - %{conn: assign(conn, :user, admin), id: report_id, admin: admin} + {:ok, %{id: second_report_id}} = + CommonAPI.report(reporter, %{ + "account_id" => target_user.id, + "comment" => "I feel very offended", + "status_ids" => [activity.id] + }) + + %{ + conn: assign(conn, :user, admin), + id: report_id, + admin: admin, + second_report_id: second_report_id + } end test "mark report as resolved", %{conn: conn, id: id, admin: admin} do - response = - conn - |> put("/api/pleroma/admin/reports/#{id}", %{"state" => "resolved"}) - |> json_response(:ok) + conn + |> patch("/api/pleroma/admin/reports", %{ + "reports" => [ + %{"state" => "resolved", "id" => id} + ] + }) + |> json_response(:no_content) - assert response["state"] == "resolved" + activity = Activity.get_by_id(id) + assert activity.data["state"] == "resolved" log_entry = Repo.one(ModerationLog) @@ -1343,12 +1359,16 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do end test "closes report", %{conn: conn, id: id, admin: admin} do - response = - conn - |> put("/api/pleroma/admin/reports/#{id}", %{"state" => "closed"}) - |> json_response(:ok) + conn + |> patch("/api/pleroma/admin/reports", %{ + "reports" => [ + %{"state" => "closed", "id" => id} + ] + }) + |> json_response(:no_content) - assert response["state"] == "closed" + activity = Activity.get_by_id(id) + assert activity.data["state"] == "closed" log_entry = Repo.one(ModerationLog) @@ -1359,17 +1379,54 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do test "returns 400 when state is unknown", %{conn: conn, id: id} do conn = conn - |> put("/api/pleroma/admin/reports/#{id}", %{"state" => "test"}) + |> patch("/api/pleroma/admin/reports", %{ + "reports" => [ + %{"state" => "test", "id" => id} + ] + }) - assert json_response(conn, :bad_request) == "Unsupported state" + assert hd(json_response(conn, :bad_request))["error"] == "Unsupported state" end test "returns 404 when report is not exist", %{conn: conn} do conn = conn - |> put("/api/pleroma/admin/reports/test", %{"state" => "closed"}) + |> patch("/api/pleroma/admin/reports", %{ + "reports" => [ + %{"state" => "closed", "id" => "test"} + ] + }) - assert json_response(conn, :not_found) == "Not found" + assert hd(json_response(conn, :bad_request))["error"] == "not_found" + end + + test "updates state of multiple reports", %{ + conn: conn, + id: id, + admin: admin, + second_report_id: second_report_id + } do + conn + |> patch("/api/pleroma/admin/reports", %{ + "reports" => [ + %{"state" => "resolved", "id" => id}, + %{"state" => "closed", "id" => second_report_id} + ] + }) + |> json_response(:no_content) + + activity = Activity.get_by_id(id) + second_activity = Activity.get_by_id(second_report_id) + assert activity.data["state"] == "resolved" + assert second_activity.data["state"] == "closed" + + [first_log_entry, second_log_entry] = Repo.all(ModerationLog) + + assert ModerationLog.get_log_entry_message(first_log_entry) == + "@#{admin.nickname} updated report ##{id} with 'resolved' state" + + assert ModerationLog.get_log_entry_message(second_log_entry) == + "@#{admin.nickname} updated report ##{second_report_id} with 'closed' state" end end @@ -1492,7 +1549,145 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do end end - # + describe "GET /api/pleroma/admin/grouped_reports" do + setup %{conn: conn} do + admin = insert(:user, is_admin: true) + [reporter, target_user] = insert_pair(:user) + + date1 = (DateTime.to_unix(DateTime.utc_now()) + 1000) |> DateTime.from_unix!() + date2 = (DateTime.to_unix(DateTime.utc_now()) + 2000) |> DateTime.from_unix!() + date3 = (DateTime.to_unix(DateTime.utc_now()) + 3000) |> DateTime.from_unix!() + + first_status = + insert(:note_activity, user: target_user, data_attrs: %{"published" => date1}) + + second_status = + insert(:note_activity, user: target_user, data_attrs: %{"published" => date2}) + + third_status = + insert(:note_activity, user: target_user, data_attrs: %{"published" => date3}) + + {:ok, first_report} = + CommonAPI.report(reporter, %{ + "account_id" => target_user.id, + "status_ids" => [first_status.id, second_status.id, third_status.id] + }) + + {:ok, second_report} = + CommonAPI.report(reporter, %{ + "account_id" => target_user.id, + "status_ids" => [first_status.id, second_status.id] + }) + + {:ok, third_report} = + CommonAPI.report(reporter, %{ + "account_id" => target_user.id, + "status_ids" => [first_status.id] + }) + + %{ + conn: assign(conn, :user, admin), + first_status: Activity.get_by_ap_id_with_object(first_status.data["id"]), + second_status: Activity.get_by_ap_id_with_object(second_status.data["id"]), + third_status: Activity.get_by_ap_id_with_object(third_status.data["id"]), + first_status_reports: [first_report, second_report, third_report], + second_status_reports: [first_report, second_report], + third_status_reports: [first_report], + target_user: target_user, + reporter: reporter + } + end + + test "returns reports grouped by status", %{ + conn: conn, + first_status: first_status, + second_status: second_status, + third_status: third_status, + first_status_reports: first_status_reports, + second_status_reports: second_status_reports, + third_status_reports: third_status_reports, + target_user: target_user, + reporter: reporter + } do + response = + conn + |> get("/api/pleroma/admin/grouped_reports") + |> json_response(:ok) + + assert length(response["reports"]) == 3 + + first_group = + Enum.find(response["reports"], &(&1["status"]["id"] == first_status.data["id"])) + + second_group = + Enum.find(response["reports"], &(&1["status"]["id"] == second_status.data["id"])) + + third_group = + Enum.find(response["reports"], &(&1["status"]["id"] == third_status.data["id"])) + + assert length(first_group["reports"]) == 3 + assert length(second_group["reports"]) == 2 + assert length(third_group["reports"]) == 1 + + assert first_group["date"] == + Enum.max_by(first_status_reports, fn act -> + NaiveDateTime.from_iso8601!(act.data["published"]) + end).data["published"] + + assert first_group["status"] == %{ + "id" => first_status.data["id"], + "content" => first_status.object.data["content"], + "published" => first_status.object.data["published"] + } + + assert first_group["account"]["id"] == target_user.id + + assert length(first_group["actors"]) == 1 + assert hd(first_group["actors"])["id"] == reporter.id + + assert Enum.map(first_group["reports"], & &1["id"]) -- + Enum.map(first_status_reports, & &1.id) == [] + + assert second_group["date"] == + Enum.max_by(second_status_reports, fn act -> + NaiveDateTime.from_iso8601!(act.data["published"]) + end).data["published"] + + assert second_group["status"] == %{ + "id" => second_status.data["id"], + "content" => second_status.object.data["content"], + "published" => second_status.object.data["published"] + } + + assert second_group["account"]["id"] == target_user.id + + assert length(second_group["actors"]) == 1 + assert hd(second_group["actors"])["id"] == reporter.id + + assert Enum.map(second_group["reports"], & &1["id"]) -- + Enum.map(second_status_reports, & &1.id) == [] + + assert third_group["date"] == + Enum.max_by(third_status_reports, fn act -> + NaiveDateTime.from_iso8601!(act.data["published"]) + end).data["published"] + + assert third_group["status"] == %{ + "id" => third_status.data["id"], + "content" => third_status.object.data["content"], + "published" => third_status.object.data["published"] + } + + assert third_group["account"]["id"] == target_user.id + + assert length(third_group["actors"]) == 1 + assert hd(third_group["actors"])["id"] == reporter.id + + assert Enum.map(third_group["reports"], & &1["id"]) -- + Enum.map(third_status_reports, & &1.id) == [] + end + end + describe "POST /api/pleroma/admin/reports/:id/respond" do setup %{conn: conn} do admin = insert(:user, is_admin: true) @@ -2269,6 +2464,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do Pleroma.Config.put([:instance, :dynamic_configuration], true) end + clear_config([:feed, :post_title]) do + Pleroma.Config.put([:feed, :post_title], %{max_length: 100, omission: "…"}) + end + test "transfer settings to DB and to file", %{conn: conn, admin: admin} do assert Pleroma.Repo.all(Pleroma.Web.AdminAPI.Config) == [] conn = get(conn, "/api/pleroma/admin/config/migrate_to_db") @@ -2565,7 +2764,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do conn = build_conn() |> assign(:user, admin) - |> patch("/api/pleroma/admin/users/#{user.nickname}/force_password_reset") + |> patch("/api/pleroma/admin/users/force_password_reset", %{nicknames: [user.nickname]}) assert json_response(conn, 204) == "" diff --git a/test/web/common_api/common_api_test.exs b/test/web/common_api/common_api_test.exs index 8e6fbd7f0..138488d44 100644 --- a/test/web/common_api/common_api_test.exs +++ b/test/web/common_api/common_api_test.exs @@ -227,6 +227,33 @@ defmodule Pleroma.Web.CommonAPITest do end describe "reactions" do + test "reacting to a status with an emoji" 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, "👍") + + assert reaction.data["actor"] == user.ap_id + assert reaction.data["content"] == "👍" + + # TODO: test error case. + end + + test "unreacting to a status with an emoji" do + user = insert(:user) + other_user = insert(:user) + + {:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"}) + {:ok, reaction, _} = CommonAPI.react_with_emoji(activity.id, user, "👍") + + {:ok, unreaction, _} = CommonAPI.unreact_with_emoji(activity.id, user, "👍") + + assert unreaction.data["type"] == "Undo" + assert unreaction.data["object"] == reaction.data["id"] + end + test "repeating a status" do user = insert(:user) other_user = insert(:user) @@ -441,6 +468,35 @@ defmodule Pleroma.Web.CommonAPITest do assert CommonAPI.update_report_state(report_id, "test") == {:error, "Unsupported state"} end + + test "updates state of multiple reports" do + [reporter, target_user] = insert_pair(:user) + activity = insert(:note_activity, user: target_user) + + {:ok, %Activity{id: first_report_id}} = + CommonAPI.report(reporter, %{ + "account_id" => target_user.id, + "comment" => "I feel offended", + "status_ids" => [activity.id] + }) + + {:ok, %Activity{id: second_report_id}} = + CommonAPI.report(reporter, %{ + "account_id" => target_user.id, + "comment" => "I feel very offended!", + "status_ids" => [activity.id] + }) + + {:ok, report_ids} = + CommonAPI.update_report_state([first_report_id, second_report_id], "resolved") + + first_report = Activity.get_by_id(first_report_id) + second_report = Activity.get_by_id(second_report_id) + + assert report_ids -- [first_report_id, second_report_id] == [] + assert first_report.data["state"] == "resolved" + assert second_report.data["state"] == "resolved" + end end describe "reblog muting" do diff --git a/test/web/feed/feed_controller_test.exs b/test/web/feed/feed_controller_test.exs index 1f44eae20..6f61acf43 100644 --- a/test/web/feed/feed_controller_test.exs +++ b/test/web/feed/feed_controller_test.exs @@ -6,16 +6,25 @@ defmodule Pleroma.Web.Feed.FeedControllerTest do use Pleroma.Web.ConnCase import Pleroma.Factory + import SweetXml alias Pleroma.Object alias Pleroma.User + clear_config([:feed]) + test "gets a feed", %{conn: conn} do + Pleroma.Config.put( + [:feed, :post_title], + %{max_length: 10, omission: "..."} + ) + activity = insert(:note_activity) note = insert(:note, data: %{ + "content" => "This is :moominmamma: note ", "attachment" => [ %{ "url" => [%{"mediaType" => "image/png", "href" => "https://pleroma.gov/image.png"}] @@ -26,15 +35,30 @@ defmodule Pleroma.Web.Feed.FeedControllerTest do ) note_activity = insert(:note_activity, note: note) - object = Object.normalize(note_activity) user = User.get_cached_by_ap_id(note_activity.data["actor"]) - conn = + note2 = + insert(:note, + user: user, + data: %{"content" => "42 This is :moominmamma: note ", "inReplyTo" => activity.data["id"]} + ) + + _note_activity2 = insert(:note_activity, note: note2) + object = Object.normalize(note_activity) + + resp = conn |> put_req_header("content-type", "application/atom+xml") |> get("/users/#{user.nickname}/feed.atom") + |> response(200) + + activity_titles = + resp + |> SweetXml.parse() + |> SweetXml.xpath(~x"//entry/title/text()"l) - assert response(conn, 200) =~ object.data["content"] + assert activity_titles == ['42 This...', 'This is...'] + assert resp =~ object.data["content"] end test "returns 404 for a missing feed", %{conn: conn} do diff --git a/test/web/mastodon_api/controllers/conversation_controller_test.exs b/test/web/mastodon_api/controllers/conversation_controller_test.exs index 542af4944..2a1223b18 100644 --- a/test/web/mastodon_api/controllers/conversation_controller_test.exs +++ b/test/web/mastodon_api/controllers/conversation_controller_test.exs @@ -59,6 +59,59 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do assert User.get_cached_by_id(user_one.id).unread_conversation_count == 0 end + test "filters conversations by recipients", %{conn: conn} do + user_one = insert(:user) + user_two = insert(:user) + user_three = insert(:user) + + {:ok, direct1} = + CommonAPI.post(user_one, %{ + "status" => "Hi @#{user_two.nickname}!", + "visibility" => "direct" + }) + + {:ok, _direct2} = + CommonAPI.post(user_one, %{ + "status" => "Hi @#{user_three.nickname}!", + "visibility" => "direct" + }) + + {:ok, direct3} = + CommonAPI.post(user_one, %{ + "status" => "Hi @#{user_two.nickname}, @#{user_three.nickname}!", + "visibility" => "direct" + }) + + {:ok, _direct4} = + CommonAPI.post(user_two, %{ + "status" => "Hi @#{user_three.nickname}!", + "visibility" => "direct" + }) + + {:ok, direct5} = + CommonAPI.post(user_two, %{ + "status" => "Hi @#{user_one.nickname}!", + "visibility" => "direct" + }) + + [conversation1, conversation2] = + conn + |> assign(:user, user_one) + |> get("/api/v1/conversations", %{"recipients" => [user_two.id]}) + |> json_response(200) + + assert conversation1["last_status"]["id"] == direct5.id + assert conversation2["last_status"]["id"] == direct1.id + + [conversation1] = + conn + |> assign(:user, user_one) + |> get("/api/v1/conversations", %{"recipients" => [user_two.id, user_three.id]}) + |> json_response(200) + + assert conversation1["last_status"]["id"] == direct3.id + end + test "updates the last_status on reply", %{conn: conn} do user_one = insert(:user) user_two = insert(:user) diff --git a/test/web/mastodon_api/controllers/filter_controller_test.exs b/test/web/mastodon_api/controllers/filter_controller_test.exs index 5d5b56c8e..550689788 100644 --- a/test/web/mastodon_api/controllers/filter_controller_test.exs +++ b/test/web/mastodon_api/controllers/filter_controller_test.exs @@ -3,7 +3,7 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.FilterControllerTest do - use Pleroma.Web.ConnCase, async: true + use Pleroma.Web.ConnCase alias Pleroma.Web.MastodonAPI.FilterView diff --git a/test/web/mastodon_api/controllers/scheduled_activity_controller_test.exs b/test/web/mastodon_api/controllers/scheduled_activity_controller_test.exs index 9ad6a4fa7..ae5fee2bc 100644 --- a/test/web/mastodon_api/controllers/scheduled_activity_controller_test.exs +++ b/test/web/mastodon_api/controllers/scheduled_activity_controller_test.exs @@ -3,7 +3,7 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.ScheduledActivityControllerTest do - use Pleroma.Web.ConnCase, async: true + use Pleroma.Web.ConnCase alias Pleroma.Repo alias Pleroma.ScheduledActivity diff --git a/test/web/node_info_test.exs b/test/web/node_info_test.exs index a3281b25b..9a574a38d 100644 --- a/test/web/node_info_test.exs +++ b/test/web/node_info_test.exs @@ -61,6 +61,33 @@ defmodule Pleroma.Web.NodeInfoTest do assert Pleroma.Application.repository() == result["software"]["repository"] end + test "returns fieldsLimits field", %{conn: conn} do + max_account_fields = Pleroma.Config.get([:instance, :max_account_fields]) + max_remote_account_fields = Pleroma.Config.get([:instance, :max_remote_account_fields]) + account_field_name_length = Pleroma.Config.get([:instance, :account_field_name_length]) + account_field_value_length = Pleroma.Config.get([:instance, :account_field_value_length]) + + Pleroma.Config.put([:instance, :max_account_fields], 10) + Pleroma.Config.put([:instance, :max_remote_account_fields], 15) + Pleroma.Config.put([:instance, :account_field_name_length], 255) + Pleroma.Config.put([:instance, :account_field_value_length], 2048) + + response = + conn + |> get("/nodeinfo/2.1.json") + |> json_response(:ok) + + assert response["metadata"]["fieldsLimits"]["maxFields"] == 10 + assert response["metadata"]["fieldsLimits"]["maxRemoteFields"] == 15 + assert response["metadata"]["fieldsLimits"]["nameLength"] == 255 + assert response["metadata"]["fieldsLimits"]["valueLength"] == 2048 + + Pleroma.Config.put([:instance, :max_account_fields], max_account_fields) + Pleroma.Config.put([:instance, :max_remote_account_fields], max_remote_account_fields) + Pleroma.Config.put([:instance, :account_field_name_length], account_field_name_length) + Pleroma.Config.put([:instance, :account_field_value_length], account_field_value_length) + end + test "it returns the safe_dm_mentions feature if enabled", %{conn: conn} do option = Pleroma.Config.get([:instance, :safe_dm_mentions]) Pleroma.Config.put([:instance, :safe_dm_mentions], true) @@ -84,6 +111,30 @@ defmodule Pleroma.Web.NodeInfoTest do Pleroma.Config.put([:instance, :safe_dm_mentions], option) end + test "it shows if federation is enabled/disabled", %{conn: conn} do + original = Pleroma.Config.get([:instance, :federating]) + + Pleroma.Config.put([:instance, :federating], true) + + response = + conn + |> get("/nodeinfo/2.1.json") + |> json_response(:ok) + + assert response["metadata"]["federation"]["enabled"] == true + + Pleroma.Config.put([:instance, :federating], false) + + response = + conn + |> get("/nodeinfo/2.1.json") + |> json_response(:ok) + + assert response["metadata"]["federation"]["enabled"] == false + + Pleroma.Config.put([:instance, :federating], original) + end + test "it shows MRF transparency data if enabled", %{conn: conn} do config = Pleroma.Config.get([:instance, :rewrite_policy]) Pleroma.Config.put([:instance, :rewrite_policy], [Pleroma.Web.ActivityPub.MRF.SimplePolicy]) diff --git a/test/web/oauth/oauth_controller_test.exs b/test/web/oauth/oauth_controller_test.exs index ad8d79083..beb995cd8 100644 --- a/test/web/oauth/oauth_controller_test.exs +++ b/test/web/oauth/oauth_controller_test.exs @@ -469,6 +469,29 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do assert html_response(conn, 200) =~ ~s(type="submit") end + test "renders authentication page if user is already authenticated but user request with another client", + %{ + app: app, + conn: conn + } do + token = insert(:oauth_token, app_id: app.id) + + conn = + conn + |> put_session(:oauth_token, token.token) + |> get( + "/oauth/authorize", + %{ + "response_type" => "code", + "client_id" => "another_client_id", + "redirect_uri" => OAuthController.default_redirect_uri(app), + "scope" => "read" + } + ) + + assert html_response(conn, 200) =~ ~s(type="submit") + end + test "with existing authentication and non-OOB `redirect_uri`, redirects to app with `token` and `state` params", %{ app: app, diff --git a/test/web/pleroma_api/controllers/pleroma_api_controller_test.exs b/test/web/pleroma_api/controllers/pleroma_api_controller_test.exs index 0c83edb56..b1b59beed 100644 --- a/test/web/pleroma_api/controllers/pleroma_api_controller_test.exs +++ b/test/web/pleroma_api/controllers/pleroma_api_controller_test.exs @@ -7,12 +7,72 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIControllerTest do alias Pleroma.Conversation.Participation alias Pleroma.Notification + alias Pleroma.Object alias Pleroma.Repo alias Pleroma.User alias Pleroma.Web.CommonAPI import Pleroma.Factory + test "POST /api/v1/pleroma/statuses/:id/react_with_emoji", %{conn: conn} do + user = insert(:user) + other_user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{"status" => "#cofe"}) + + result = + conn + |> assign(:user, other_user) + |> post("/api/v1/pleroma/statuses/#{activity.id}/react_with_emoji", %{"emoji" => "☕"}) + + assert %{"id" => id} = json_response(result, 200) + assert to_string(activity.id) == id + end + + test "POST /api/v1/pleroma/statuses/:id/unreact_with_emoji", %{conn: conn} do + user = insert(:user) + other_user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{"status" => "#cofe"}) + {:ok, activity, _object} = CommonAPI.react_with_emoji(activity.id, other_user, "☕") + + result = + conn + |> assign(:user, other_user) + |> post("/api/v1/pleroma/statuses/#{activity.id}/unreact_with_emoji", %{"emoji" => "☕"}) + + assert %{"id" => id} = json_response(result, 200) + assert to_string(activity.id) == id + + object = Object.normalize(activity) + + assert object.data["reaction_count"] == 0 + end + + test "GET /api/v1/pleroma/statuses/:id/emoji_reactions_by", %{conn: conn} do + user = insert(:user) + other_user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{"status" => "#cofe"}) + + result = + conn + |> get("/api/v1/pleroma/statuses/#{activity.id}/emoji_reactions_by") + |> json_response(200) + + assert result == %{} + + {:ok, _, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅") + + result = + conn + |> get("/api/v1/pleroma/statuses/#{activity.id}/emoji_reactions_by") + |> json_response(200) + + [represented_user] = result["🎅"] + assert represented_user["id"] == other_user.id + end + test "/api/v1/pleroma/conversations/:id", %{conn: conn} do user = insert(:user) other_user = insert(:user) diff --git a/test/web/static_fe/static_fe_controller_test.exs b/test/web/static_fe/static_fe_controller_test.exs new file mode 100644 index 000000000..2ce8f9fa3 --- /dev/null +++ b/test/web/static_fe/static_fe_controller_test.exs @@ -0,0 +1,210 @@ +defmodule Pleroma.Web.StaticFE.StaticFEControllerTest do + use Pleroma.Web.ConnCase + alias Pleroma.Activity + alias Pleroma.Web.ActivityPub.Transmogrifier + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + clear_config_all([:static_fe, :enabled]) do + Pleroma.Config.put([:static_fe, :enabled], true) + end + + describe "user profile page" do + test "just the profile as HTML", %{conn: conn} do + user = insert(:user) + + conn = + conn + |> put_req_header("accept", "text/html") + |> get("/users/#{user.nickname}") + + assert html_response(conn, 200) =~ user.nickname + end + + test "renders json unless there's an html accept header", %{conn: conn} do + user = insert(:user) + + conn = + conn + |> put_req_header("accept", "application/json") + |> get("/users/#{user.nickname}") + + assert json_response(conn, 200) + end + + test "404 when user not found", %{conn: conn} do + conn = + conn + |> put_req_header("accept", "text/html") + |> get("/users/limpopo") + + assert html_response(conn, 404) =~ "not found" + end + + test "profile does not include private messages", %{conn: conn} do + user = insert(:user) + CommonAPI.post(user, %{"status" => "public"}) + CommonAPI.post(user, %{"status" => "private", "visibility" => "private"}) + + conn = + conn + |> put_req_header("accept", "text/html") + |> get("/users/#{user.nickname}") + + html = html_response(conn, 200) + + assert html =~ ">public<" + refute html =~ ">private<" + end + + test "pagination", %{conn: conn} do + user = insert(:user) + Enum.map(1..30, fn i -> CommonAPI.post(user, %{"status" => "test#{i}"}) end) + + conn = + conn + |> put_req_header("accept", "text/html") + |> get("/users/#{user.nickname}") + + html = html_response(conn, 200) + + assert html =~ ">test30<" + assert html =~ ">test11<" + refute html =~ ">test10<" + refute html =~ ">test1<" + end + + test "pagination, page 2", %{conn: conn} do + user = insert(:user) + activities = Enum.map(1..30, fn i -> CommonAPI.post(user, %{"status" => "test#{i}"}) end) + {:ok, a11} = Enum.at(activities, 11) + + conn = + conn + |> put_req_header("accept", "text/html") + |> get("/users/#{user.nickname}?max_id=#{a11.id}") + + html = html_response(conn, 200) + + assert html =~ ">test1<" + assert html =~ ">test10<" + refute html =~ ">test20<" + refute html =~ ">test29<" + end + end + + describe "notice rendering" do + test "single notice page", %{conn: conn} do + user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{"status" => "testing a thing!"}) + + conn = + conn + |> put_req_header("accept", "text/html") + |> get("/notice/#{activity.id}") + + html = html_response(conn, 200) + assert html =~ "<header>" + assert html =~ user.nickname + assert html =~ "testing a thing!" + end + + test "shows the whole thread", %{conn: conn} do + user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{"status" => "space: the final frontier"}) + + CommonAPI.post(user, %{ + "status" => "these are the voyages or something", + "in_reply_to_status_id" => activity.id + }) + + conn = + conn + |> put_req_header("accept", "text/html") + |> get("/notice/#{activity.id}") + + html = html_response(conn, 200) + assert html =~ "the final frontier" + assert html =~ "voyages" + end + + test "redirect by AP object ID", %{conn: conn} do + user = insert(:user) + + {:ok, %Activity{data: %{"object" => object_url}}} = + CommonAPI.post(user, %{"status" => "beam me up"}) + + conn = + conn + |> put_req_header("accept", "text/html") + |> get(URI.parse(object_url).path) + + assert html_response(conn, 302) =~ "redirected" + end + + test "redirect by activity ID", %{conn: conn} do + user = insert(:user) + + {:ok, %Activity{data: %{"id" => id}}} = + CommonAPI.post(user, %{"status" => "I'm a doctor, not a devops!"}) + + conn = + conn + |> put_req_header("accept", "text/html") + |> get(URI.parse(id).path) + + assert html_response(conn, 302) =~ "redirected" + end + + test "404 when notice not found", %{conn: conn} do + conn = + conn + |> put_req_header("accept", "text/html") + |> get("/notice/88c9c317") + + assert html_response(conn, 404) =~ "not found" + end + + test "404 for private status", %{conn: conn} do + user = insert(:user) + + {:ok, activity} = + CommonAPI.post(user, %{"status" => "don't show me!", "visibility" => "private"}) + + conn = + conn + |> put_req_header("accept", "text/html") + |> get("/notice/#{activity.id}") + + assert html_response(conn, 404) =~ "not found" + end + + test "302 for remote cached status", %{conn: conn} do + user = insert(:user) + + message = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "to" => user.follower_address, + "cc" => "https://www.w3.org/ns/activitystreams#Public", + "type" => "Create", + "object" => %{ + "content" => "blah blah blah", + "type" => "Note", + "attributedTo" => user.ap_id, + "inReplyTo" => nil + }, + "actor" => user.ap_id + } + + assert {:ok, activity} = Transmogrifier.handle_incoming(message) + + conn = + conn + |> put_req_header("accept", "text/html") + |> get("/notice/#{activity.id}") + + assert html_response(conn, 302) =~ "redirected" + end + end +end |