diff options
| author | Egor Kislitsyn <egor@kislitsyn.com> | 2020-04-27 14:33:00 +0400 | 
|---|---|---|
| committer | Egor Kislitsyn <egor@kislitsyn.com> | 2020-04-27 14:33:00 +0400 | 
| commit | f1ca917bb0d733445657e0be92440dedc2f8d3c6 (patch) | |
| tree | 175db64bc7d9c32bc5c2f2e7b443daa040e4d65c /test | |
| parent | 1b06a27746ccbbdec77b7bc1571783a64ade4431 (diff) | |
| parent | 01cc93b6873b5c50c0fc54774a3b004bf660e46b (diff) | |
| download | pleroma-f1ca917bb0d733445657e0be92440dedc2f8d3c6.tar.gz pleroma-f1ca917bb0d733445657e0be92440dedc2f8d3c6.zip  | |
Merge branch 'develop' into openapi/account
Diffstat (limited to 'test')
| -rw-r--r-- | test/stats_test.exs (renamed from test/stat_test.exs) | 12 | ||||
| -rw-r--r-- | test/support/api_spec_helpers.ex | 57 | ||||
| -rw-r--r-- | test/support/conn_case.ex | 36 | ||||
| -rw-r--r-- | test/web/activity_pub/activity_pub_test.exs | 570 | ||||
| -rw-r--r-- | test/web/activity_pub/utils_test.exs | 5 | ||||
| -rw-r--r-- | test/web/api_spec/app_operation_test.exs | 45 | ||||
| -rw-r--r-- | test/web/api_spec/schema_examples_test.exs | 43 | ||||
| -rw-r--r-- | test/web/common_api/common_api_test.exs | 72 | ||||
| -rw-r--r-- | test/web/common_api/common_api_utils_test.exs | 20 | ||||
| -rw-r--r-- | test/web/mastodon_api/controllers/app_controller_test.exs | 4 | ||||
| -rw-r--r-- | test/web/mastodon_api/controllers/custom_emoji_controller_test.exs | 17 | ||||
| -rw-r--r-- | test/web/mastodon_api/controllers/domain_block_controller_test.exs | 28 | ||||
| -rw-r--r-- | test/web/mastodon_api/controllers/status_controller_test.exs | 11 | 
13 files changed, 739 insertions, 181 deletions
diff --git a/test/stat_test.exs b/test/stats_test.exs index bccc1c8d0..c1aeb2c7f 100644 --- a/test/stat_test.exs +++ b/test/stats_test.exs @@ -2,11 +2,21 @@  # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>  # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.StateTest do +defmodule Pleroma.StatsTest do    use Pleroma.DataCase    import Pleroma.Factory    alias Pleroma.Web.CommonAPI +  describe "user count" do +    test "it ignores internal users" do +      _user = insert(:user, local: true) +      _internal = insert(:user, local: true, nickname: nil) +      _internal = Pleroma.Web.ActivityPub.Relay.get_actor() + +      assert match?(%{stats: %{user_count: 1}}, Pleroma.Stats.calculate_stat_data()) +    end +  end +    describe "status visibility count" do      test "on new status" do        user = insert(:user) diff --git a/test/support/api_spec_helpers.ex b/test/support/api_spec_helpers.ex new file mode 100644 index 000000000..80c69c788 --- /dev/null +++ b/test/support/api_spec_helpers.ex @@ -0,0 +1,57 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Tests.ApiSpecHelpers do +  @moduledoc """ +  OpenAPI spec test helpers +  """ + +  import ExUnit.Assertions + +  alias OpenApiSpex.Cast.Error +  alias OpenApiSpex.Reference +  alias OpenApiSpex.Schema + +  def assert_schema(value, schema) do +    api_spec = Pleroma.Web.ApiSpec.spec() + +    case OpenApiSpex.cast_value(value, schema, api_spec) do +      {:ok, data} -> +        data + +      {:error, errors} -> +        errors = +          Enum.map(errors, fn error -> +            message = Error.message(error) +            path = Error.path_to_string(error) +            "#{message} at #{path}" +          end) + +        flunk( +          "Value does not conform to schema #{schema.title}: #{Enum.join(errors, "\n")}\n#{ +            inspect(value) +          }" +        ) +    end +  end + +  def resolve_schema(%Schema{} = schema), do: schema + +  def resolve_schema(%Reference{} = ref) do +    schemas = Pleroma.Web.ApiSpec.spec().components.schemas +    Reference.resolve_schema(ref, schemas) +  end + +  def api_operations do +    paths = Pleroma.Web.ApiSpec.spec().paths + +    Enum.flat_map(paths, fn {_, path_item} -> +      path_item +      |> Map.take([:delete, :get, :head, :options, :patch, :post, :put, :trace]) +      |> Map.values() +      |> Enum.reject(&is_nil/1) +      |> Enum.uniq() +    end) +  end +end diff --git a/test/support/conn_case.ex b/test/support/conn_case.ex index 36ce372c2..8099461cc 100644 --- a/test/support/conn_case.ex +++ b/test/support/conn_case.ex @@ -56,6 +56,42 @@ defmodule Pleroma.Web.ConnCase do          [conn: conn]        end +      defp json_response_and_validate_schema(conn, status \\ nil) do +        content_type = +          conn +          |> Plug.Conn.get_resp_header("content-type") +          |> List.first() +          |> String.split(";") +          |> List.first() + +        status = status || conn.status + +        %{private: %{open_api_spex: %{operation_id: op_id, operation_lookup: lookup, spec: spec}}} = +          conn + +        schema = lookup[op_id].responses[status].content[content_type].schema +        json = json_response(conn, status) + +        case OpenApiSpex.cast_value(json, schema, spec) do +          {:ok, _data} -> +            json + +          {:error, errors} -> +            errors = +              Enum.map(errors, fn error -> +                message = OpenApiSpex.Cast.Error.message(error) +                path = OpenApiSpex.Cast.Error.path_to_string(error) +                "#{message} at #{path}" +              end) + +            flunk( +              "Response does not conform to schema of #{op_id} operation: #{ +                Enum.join(errors, "\n") +              }\n#{inspect(json)}" +            ) +        end +      end +        defp ensure_federating_or_authenticated(conn, url, user) do          initial_setting = Config.get([:instance, :federating])          on_exit(fn -> Config.put([:instance, :federating], initial_setting) end) diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs index 6410df49b..edd7dfb22 100644 --- a/test/web/activity_pub/activity_pub_test.exs +++ b/test/web/activity_pub/activity_pub_test.exs @@ -994,72 +994,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      end    end -  describe "like an object" do -    test_with_mock "sends an activity to federation", Federator, [:passthrough], [] do -      Config.put([:instance, :federating], true) -      note_activity = insert(:note_activity) -      assert object_activity = Object.normalize(note_activity) - -      user = insert(:user) - -      {:ok, like_activity, _object} = ActivityPub.like(user, object_activity) -      assert called(Federator.publish(like_activity)) -    end - -    test "returns exist activity if object already liked" do -      note_activity = insert(:note_activity) -      assert object_activity = Object.normalize(note_activity) - -      user = insert(:user) - -      {:ok, like_activity, _object} = ActivityPub.like(user, object_activity) - -      {:ok, like_activity_exist, _object} = ActivityPub.like(user, object_activity) -      assert like_activity == like_activity_exist -    end - -    test "reverts like activity on error" do -      note_activity = insert(:note_activity) -      object = Object.normalize(note_activity) -      user = insert(:user) - -      with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do -        assert {:error, :reverted} = ActivityPub.like(user, object) -      end - -      assert Repo.aggregate(Activity, :count, :id) == 1 -      assert Repo.get(Object, object.id) == object -    end - -    test "adds a like activity to the db" do -      note_activity = insert(:note_activity) -      assert object = Object.normalize(note_activity) - -      user = insert(:user) -      user_two = insert(:user) - -      {:ok, like_activity, object} = ActivityPub.like(user, object) - -      assert like_activity.data["actor"] == user.ap_id -      assert like_activity.data["type"] == "Like" -      assert like_activity.data["object"] == object.data["id"] -      assert like_activity.data["to"] == [User.ap_followers(user), note_activity.data["actor"]] -      assert like_activity.data["context"] == object.data["context"] -      assert object.data["like_count"] == 1 -      assert object.data["likes"] == [user.ap_id] - -      # Just return the original activity if the user already liked it. -      {:ok, same_like_activity, object} = ActivityPub.like(user, object) - -      assert like_activity == same_like_activity -      assert object.data["likes"] == [user.ap_id] -      assert object.data["like_count"] == 1 - -      {:ok, _like_activity, object} = ActivityPub.like(user_two, object) -      assert object.data["like_count"] == 2 -    end -  end -    describe "unliking" do      test_with_mock "sends an activity to federation", Federator, [:passthrough], [] do        Config.put([:instance, :federating], true) @@ -1071,7 +1005,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do        {:ok, object} = ActivityPub.unlike(user, object)        refute called(Federator.publish()) -      {:ok, _like_activity, object} = ActivityPub.like(user, object) +      {:ok, _like_activity} = CommonAPI.favorite(user, note_activity.id) +      object = Object.get_by_id(object.id)        assert object.data["like_count"] == 1        {:ok, unlike_activity, _, object} = ActivityPub.unlike(user, object) @@ -1082,10 +1017,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      test "reverts unliking on error" do        note_activity = insert(:note_activity) -      object = Object.normalize(note_activity)        user = insert(:user) -      {:ok, like_activity, object} = ActivityPub.like(user, object) +      {:ok, like_activity} = CommonAPI.favorite(user, note_activity.id) +      object = Object.normalize(note_activity)        assert object.data["like_count"] == 1        with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do @@ -1106,7 +1041,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do        {:ok, object} = ActivityPub.unlike(user, object)        assert object.data["like_count"] == 0 -      {:ok, like_activity, object} = ActivityPub.like(user, object) +      {:ok, like_activity} = CommonAPI.favorite(user, note_activity.id) + +      object = Object.get_by_id(object.id)        assert object.data["like_count"] == 1        {:ok, unlike_activity, _, object} = ActivityPub.unlike(user, object) @@ -1973,4 +1910,497 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do                 ActivityPub.move(old_user, new_user)      end    end + +  test "doesn't retrieve replies activities with exclude_replies" do +    user = insert(:user) + +    {:ok, activity} = CommonAPI.post(user, %{"status" => "yeah"}) + +    {:ok, _reply} = +      CommonAPI.post(user, %{"status" => "yeah", "in_reply_to_status_id" => activity.id}) + +    [result] = ActivityPub.fetch_public_activities(%{"exclude_replies" => "true"}) + +    assert result.id == activity.id + +    assert length(ActivityPub.fetch_public_activities()) == 2 +  end + +  describe "replies filtering with public messages" do +    setup :public_messages + +    test "public timeline", %{users: %{u1: user}} do +      activities_ids = +        %{} +        |> Map.put("type", ["Create", "Announce"]) +        |> Map.put("local_only", false) +        |> Map.put("blocking_user", user) +        |> Map.put("muting_user", user) +        |> Map.put("reply_filtering_user", user) +        |> ActivityPub.fetch_public_activities() +        |> Enum.map(& &1.id) + +      assert length(activities_ids) == 16 +    end + +    test "public timeline with reply_visibility `following`", %{ +      users: %{u1: user}, +      u1: u1, +      u2: u2, +      u3: u3, +      u4: u4, +      activities: activities +    } do +      activities_ids = +        %{} +        |> Map.put("type", ["Create", "Announce"]) +        |> Map.put("local_only", false) +        |> Map.put("blocking_user", user) +        |> Map.put("muting_user", user) +        |> Map.put("reply_visibility", "following") +        |> Map.put("reply_filtering_user", user) +        |> ActivityPub.fetch_public_activities() +        |> Enum.map(& &1.id) + +      assert length(activities_ids) == 14 + +      visible_ids = +        Map.values(u1) ++ Map.values(u2) ++ Map.values(u4) ++ Map.values(activities) ++ [u3[:r1]] + +      assert Enum.all?(visible_ids, &(&1 in activities_ids)) +    end + +    test "public timeline with reply_visibility `self`", %{ +      users: %{u1: user}, +      u1: u1, +      u2: u2, +      u3: u3, +      u4: u4, +      activities: activities +    } do +      activities_ids = +        %{} +        |> Map.put("type", ["Create", "Announce"]) +        |> Map.put("local_only", false) +        |> Map.put("blocking_user", user) +        |> Map.put("muting_user", user) +        |> Map.put("reply_visibility", "self") +        |> Map.put("reply_filtering_user", user) +        |> ActivityPub.fetch_public_activities() +        |> Enum.map(& &1.id) + +      assert length(activities_ids) == 10 +      visible_ids = Map.values(u1) ++ [u2[:r1], u3[:r1], u4[:r1]] ++ Map.values(activities) +      assert Enum.all?(visible_ids, &(&1 in activities_ids)) +    end + +    test "home timeline", %{ +      users: %{u1: user}, +      activities: activities, +      u1: u1, +      u2: u2, +      u3: u3, +      u4: u4 +    } do +      params = +        %{} +        |> Map.put("type", ["Create", "Announce"]) +        |> Map.put("blocking_user", user) +        |> Map.put("muting_user", user) +        |> Map.put("user", user) +        |> Map.put("reply_filtering_user", user) + +      activities_ids = +        ActivityPub.fetch_activities([user.ap_id | User.following(user)], params) +        |> Enum.map(& &1.id) + +      assert length(activities_ids) == 13 + +      visible_ids = +        Map.values(u1) ++ +          Map.values(u3) ++ +          [ +            activities[:a1], +            activities[:a2], +            activities[:a4], +            u2[:r1], +            u2[:r3], +            u4[:r1], +            u4[:r2] +          ] + +      assert Enum.all?(visible_ids, &(&1 in activities_ids)) +    end + +    test "home timeline with reply_visibility `following`", %{ +      users: %{u1: user}, +      activities: activities, +      u1: u1, +      u2: u2, +      u3: u3, +      u4: u4 +    } do +      params = +        %{} +        |> Map.put("type", ["Create", "Announce"]) +        |> Map.put("blocking_user", user) +        |> Map.put("muting_user", user) +        |> Map.put("user", user) +        |> Map.put("reply_visibility", "following") +        |> Map.put("reply_filtering_user", user) + +      activities_ids = +        ActivityPub.fetch_activities([user.ap_id | User.following(user)], params) +        |> Enum.map(& &1.id) + +      assert length(activities_ids) == 11 + +      visible_ids = +        Map.values(u1) ++ +          [ +            activities[:a1], +            activities[:a2], +            activities[:a4], +            u2[:r1], +            u2[:r3], +            u3[:r1], +            u4[:r1], +            u4[:r2] +          ] + +      assert Enum.all?(visible_ids, &(&1 in activities_ids)) +    end + +    test "home timeline with reply_visibility `self`", %{ +      users: %{u1: user}, +      activities: activities, +      u1: u1, +      u2: u2, +      u3: u3, +      u4: u4 +    } do +      params = +        %{} +        |> Map.put("type", ["Create", "Announce"]) +        |> Map.put("blocking_user", user) +        |> Map.put("muting_user", user) +        |> Map.put("user", user) +        |> Map.put("reply_visibility", "self") +        |> Map.put("reply_filtering_user", user) + +      activities_ids = +        ActivityPub.fetch_activities([user.ap_id | User.following(user)], params) +        |> Enum.map(& &1.id) + +      assert length(activities_ids) == 9 + +      visible_ids = +        Map.values(u1) ++ +          [ +            activities[:a1], +            activities[:a2], +            activities[:a4], +            u2[:r1], +            u3[:r1], +            u4[:r1] +          ] + +      assert Enum.all?(visible_ids, &(&1 in activities_ids)) +    end +  end + +  describe "replies filtering with private messages" do +    setup :private_messages + +    test "public timeline", %{users: %{u1: user}} do +      activities_ids = +        %{} +        |> Map.put("type", ["Create", "Announce"]) +        |> Map.put("local_only", false) +        |> Map.put("blocking_user", user) +        |> Map.put("muting_user", user) +        |> Map.put("user", user) +        |> ActivityPub.fetch_public_activities() +        |> Enum.map(& &1.id) + +      assert activities_ids == [] +    end + +    test "public timeline with default reply_visibility `following`", %{users: %{u1: user}} do +      activities_ids = +        %{} +        |> Map.put("type", ["Create", "Announce"]) +        |> Map.put("local_only", false) +        |> Map.put("blocking_user", user) +        |> Map.put("muting_user", user) +        |> Map.put("reply_visibility", "following") +        |> Map.put("reply_filtering_user", user) +        |> Map.put("user", user) +        |> ActivityPub.fetch_public_activities() +        |> Enum.map(& &1.id) + +      assert activities_ids == [] +    end + +    test "public timeline with default reply_visibility `self`", %{users: %{u1: user}} do +      activities_ids = +        %{} +        |> Map.put("type", ["Create", "Announce"]) +        |> Map.put("local_only", false) +        |> Map.put("blocking_user", user) +        |> Map.put("muting_user", user) +        |> Map.put("reply_visibility", "self") +        |> Map.put("reply_filtering_user", user) +        |> Map.put("user", user) +        |> ActivityPub.fetch_public_activities() +        |> Enum.map(& &1.id) + +      assert activities_ids == [] +    end + +    test "home timeline", %{users: %{u1: user}} do +      params = +        %{} +        |> Map.put("type", ["Create", "Announce"]) +        |> Map.put("blocking_user", user) +        |> Map.put("muting_user", user) +        |> Map.put("user", user) + +      activities_ids = +        ActivityPub.fetch_activities([user.ap_id | User.following(user)], params) +        |> Enum.map(& &1.id) + +      assert length(activities_ids) == 12 +    end + +    test "home timeline with default reply_visibility `following`", %{users: %{u1: user}} do +      params = +        %{} +        |> Map.put("type", ["Create", "Announce"]) +        |> Map.put("blocking_user", user) +        |> Map.put("muting_user", user) +        |> Map.put("user", user) +        |> Map.put("reply_visibility", "following") +        |> Map.put("reply_filtering_user", user) + +      activities_ids = +        ActivityPub.fetch_activities([user.ap_id | User.following(user)], params) +        |> Enum.map(& &1.id) + +      assert length(activities_ids) == 12 +    end + +    test "home timeline with default reply_visibility `self`", %{ +      users: %{u1: user}, +      activities: activities, +      u1: u1, +      u2: u2, +      u3: u3, +      u4: u4 +    } do +      params = +        %{} +        |> Map.put("type", ["Create", "Announce"]) +        |> Map.put("blocking_user", user) +        |> Map.put("muting_user", user) +        |> Map.put("user", user) +        |> Map.put("reply_visibility", "self") +        |> Map.put("reply_filtering_user", user) + +      activities_ids = +        ActivityPub.fetch_activities([user.ap_id | User.following(user)], params) +        |> Enum.map(& &1.id) + +      assert length(activities_ids) == 10 + +      visible_ids = +        Map.values(u1) ++ Map.values(u4) ++ [u2[:r1], u3[:r1]] ++ Map.values(activities) + +      assert Enum.all?(visible_ids, &(&1 in activities_ids)) +    end +  end + +  defp public_messages(_) do +    [u1, u2, u3, u4] = insert_list(4, :user) +    {:ok, u1} = User.follow(u1, u2) +    {:ok, u2} = User.follow(u2, u1) +    {:ok, u1} = User.follow(u1, u4) +    {:ok, u4} = User.follow(u4, u1) + +    {:ok, u2} = User.follow(u2, u3) +    {:ok, u3} = User.follow(u3, u2) + +    {:ok, a1} = CommonAPI.post(u1, %{"status" => "Status"}) + +    {:ok, r1_1} = +      CommonAPI.post(u2, %{ +        "status" => "@#{u1.nickname} reply from u2 to u1", +        "in_reply_to_status_id" => a1.id +      }) + +    {:ok, r1_2} = +      CommonAPI.post(u3, %{ +        "status" => "@#{u1.nickname} reply from u3 to u1", +        "in_reply_to_status_id" => a1.id +      }) + +    {:ok, r1_3} = +      CommonAPI.post(u4, %{ +        "status" => "@#{u1.nickname} reply from u4 to u1", +        "in_reply_to_status_id" => a1.id +      }) + +    {:ok, a2} = CommonAPI.post(u2, %{"status" => "Status"}) + +    {:ok, r2_1} = +      CommonAPI.post(u1, %{ +        "status" => "@#{u2.nickname} reply from u1 to u2", +        "in_reply_to_status_id" => a2.id +      }) + +    {:ok, r2_2} = +      CommonAPI.post(u3, %{ +        "status" => "@#{u2.nickname} reply from u3 to u2", +        "in_reply_to_status_id" => a2.id +      }) + +    {:ok, r2_3} = +      CommonAPI.post(u4, %{ +        "status" => "@#{u2.nickname} reply from u4 to u2", +        "in_reply_to_status_id" => a2.id +      }) + +    {:ok, a3} = CommonAPI.post(u3, %{"status" => "Status"}) + +    {:ok, r3_1} = +      CommonAPI.post(u1, %{ +        "status" => "@#{u3.nickname} reply from u1 to u3", +        "in_reply_to_status_id" => a3.id +      }) + +    {:ok, r3_2} = +      CommonAPI.post(u2, %{ +        "status" => "@#{u3.nickname} reply from u2 to u3", +        "in_reply_to_status_id" => a3.id +      }) + +    {:ok, r3_3} = +      CommonAPI.post(u4, %{ +        "status" => "@#{u3.nickname} reply from u4 to u3", +        "in_reply_to_status_id" => a3.id +      }) + +    {:ok, a4} = CommonAPI.post(u4, %{"status" => "Status"}) + +    {:ok, r4_1} = +      CommonAPI.post(u1, %{ +        "status" => "@#{u4.nickname} reply from u1 to u4", +        "in_reply_to_status_id" => a4.id +      }) + +    {:ok, r4_2} = +      CommonAPI.post(u2, %{ +        "status" => "@#{u4.nickname} reply from u2 to u4", +        "in_reply_to_status_id" => a4.id +      }) + +    {:ok, r4_3} = +      CommonAPI.post(u3, %{ +        "status" => "@#{u4.nickname} reply from u3 to u4", +        "in_reply_to_status_id" => a4.id +      }) + +    {:ok, +     users: %{u1: u1, u2: u2, u3: u3, u4: u4}, +     activities: %{a1: a1.id, a2: a2.id, a3: a3.id, a4: a4.id}, +     u1: %{r1: r1_1.id, r2: r1_2.id, r3: r1_3.id}, +     u2: %{r1: r2_1.id, r2: r2_2.id, r3: r2_3.id}, +     u3: %{r1: r3_1.id, r2: r3_2.id, r3: r3_3.id}, +     u4: %{r1: r4_1.id, r2: r4_2.id, r3: r4_3.id}} +  end + +  defp private_messages(_) do +    [u1, u2, u3, u4] = insert_list(4, :user) +    {:ok, u1} = User.follow(u1, u2) +    {:ok, u2} = User.follow(u2, u1) +    {:ok, u1} = User.follow(u1, u3) +    {:ok, u3} = User.follow(u3, u1) +    {:ok, u1} = User.follow(u1, u4) +    {:ok, u4} = User.follow(u4, u1) + +    {:ok, u2} = User.follow(u2, u3) +    {:ok, u3} = User.follow(u3, u2) + +    {:ok, a1} = CommonAPI.post(u1, %{"status" => "Status", "visibility" => "private"}) + +    {:ok, r1_1} = +      CommonAPI.post(u2, %{ +        "status" => "@#{u1.nickname} reply from u2 to u1", +        "in_reply_to_status_id" => a1.id, +        "visibility" => "private" +      }) + +    {:ok, r1_2} = +      CommonAPI.post(u3, %{ +        "status" => "@#{u1.nickname} reply from u3 to u1", +        "in_reply_to_status_id" => a1.id, +        "visibility" => "private" +      }) + +    {:ok, r1_3} = +      CommonAPI.post(u4, %{ +        "status" => "@#{u1.nickname} reply from u4 to u1", +        "in_reply_to_status_id" => a1.id, +        "visibility" => "private" +      }) + +    {:ok, a2} = CommonAPI.post(u2, %{"status" => "Status", "visibility" => "private"}) + +    {:ok, r2_1} = +      CommonAPI.post(u1, %{ +        "status" => "@#{u2.nickname} reply from u1 to u2", +        "in_reply_to_status_id" => a2.id, +        "visibility" => "private" +      }) + +    {:ok, r2_2} = +      CommonAPI.post(u3, %{ +        "status" => "@#{u2.nickname} reply from u3 to u2", +        "in_reply_to_status_id" => a2.id, +        "visibility" => "private" +      }) + +    {:ok, a3} = CommonAPI.post(u3, %{"status" => "Status", "visibility" => "private"}) + +    {:ok, r3_1} = +      CommonAPI.post(u1, %{ +        "status" => "@#{u3.nickname} reply from u1 to u3", +        "in_reply_to_status_id" => a3.id, +        "visibility" => "private" +      }) + +    {:ok, r3_2} = +      CommonAPI.post(u2, %{ +        "status" => "@#{u3.nickname} reply from u2 to u3", +        "in_reply_to_status_id" => a3.id, +        "visibility" => "private" +      }) + +    {:ok, a4} = CommonAPI.post(u4, %{"status" => "Status", "visibility" => "private"}) + +    {:ok, r4_1} = +      CommonAPI.post(u1, %{ +        "status" => "@#{u4.nickname} reply from u1 to u4", +        "in_reply_to_status_id" => a4.id, +        "visibility" => "private" +      }) + +    {:ok, +     users: %{u1: u1, u2: u2, u3: u3, u4: u4}, +     activities: %{a1: a1.id, a2: a2.id, a3: a3.id, a4: a4.id}, +     u1: %{r1: r1_1.id, r2: r1_2.id, r3: r1_3.id}, +     u2: %{r1: r2_1.id, r2: r2_2.id}, +     u3: %{r1: r3_1.id, r2: r3_2.id}, +     u4: %{r1: r4_1.id}} +  end  end diff --git a/test/web/activity_pub/utils_test.exs b/test/web/activity_pub/utils_test.exs index e913a5148..b0bfed917 100644 --- a/test/web/activity_pub/utils_test.exs +++ b/test/web/activity_pub/utils_test.exs @@ -224,8 +224,7 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do        object = Object.normalize(activity)        {:ok, [vote], object} = CommonAPI.vote(other_user, object, [0]) -      vote_object = Object.normalize(vote) -      {:ok, _activity, _object} = ActivityPub.like(user, vote_object) +      {:ok, _activity} = CommonAPI.favorite(user, activity.id)        [fetched_vote] = Utils.get_existing_votes(other_user.ap_id, object)        assert fetched_vote.id == vote.id      end @@ -346,7 +345,7 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do        user = insert(:user)        refute Utils.get_existing_like(user.ap_id, object) -      {:ok, like_activity, _object} = ActivityPub.like(user, object) +      {:ok, like_activity} = CommonAPI.favorite(user, note_activity.id)        assert ^like_activity = Utils.get_existing_like(user.ap_id, object)      end diff --git a/test/web/api_spec/app_operation_test.exs b/test/web/api_spec/app_operation_test.exs deleted file mode 100644 index 5b96abb44..000000000 --- a/test/web/api_spec/app_operation_test.exs +++ /dev/null @@ -1,45 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ApiSpec.AppOperationTest do -  use Pleroma.Web.ConnCase, async: true - -  alias Pleroma.Web.ApiSpec -  alias Pleroma.Web.ApiSpec.Schemas.AppCreateRequest -  alias Pleroma.Web.ApiSpec.Schemas.AppCreateResponse - -  import OpenApiSpex.TestAssertions -  import Pleroma.Factory - -  test "AppCreateRequest example matches schema" do -    api_spec = ApiSpec.spec() -    schema = AppCreateRequest.schema() -    assert_schema(schema.example, "AppCreateRequest", api_spec) -  end - -  test "AppCreateResponse example matches schema" do -    api_spec = ApiSpec.spec() -    schema = AppCreateResponse.schema() -    assert_schema(schema.example, "AppCreateResponse", api_spec) -  end - -  test "AppController produces a AppCreateResponse", %{conn: conn} do -    api_spec = ApiSpec.spec() -    app_attrs = build(:oauth_app) - -    json = -      conn -      |> put_req_header("content-type", "application/json") -      |> post( -        "/api/v1/apps", -        Jason.encode!(%{ -          client_name: app_attrs.client_name, -          redirect_uris: app_attrs.redirect_uris -        }) -      ) -      |> json_response(200) - -    assert_schema(json, "AppCreateResponse", api_spec) -  end -end diff --git a/test/web/api_spec/schema_examples_test.exs b/test/web/api_spec/schema_examples_test.exs new file mode 100644 index 000000000..88b6f07cb --- /dev/null +++ b/test/web/api_spec/schema_examples_test.exs @@ -0,0 +1,43 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.SchemaExamplesTest do +  use ExUnit.Case, async: true +  import Pleroma.Tests.ApiSpecHelpers + +  @content_type "application/json" + +  for operation <- api_operations() do +    describe operation.operationId <> " Request Body" do +      if operation.requestBody do +        @media_type operation.requestBody.content[@content_type] +        @schema resolve_schema(@media_type.schema) + +        if @media_type.example do +          test "request body media type example matches schema" do +            assert_schema(@media_type.example, @schema) +          end +        end + +        if @schema.example do +          test "request body schema example matches schema" do +            assert_schema(@schema.example, @schema) +          end +        end +      end +    end + +    for {status, response} <- operation.responses do +      describe "#{operation.operationId} - #{status} Response" do +        @schema resolve_schema(response.content[@content_type].schema) + +        if @schema.example do +          test "example matches schema" do +            assert_schema(@schema.example, @schema) +          end +        end +      end +    end +  end +end diff --git a/test/web/common_api/common_api_test.exs b/test/web/common_api/common_api_test.exs index e130736ec..1758662b0 100644 --- a/test/web/common_api/common_api_test.exs +++ b/test/web/common_api/common_api_test.exs @@ -21,6 +21,60 @@ defmodule Pleroma.Web.CommonAPITest do    setup do: clear_config([:instance, :limit])    setup do: clear_config([:instance, :max_pinned_statuses]) +  test "favoriting race condition" do +    user = insert(:user) +    users_serial = insert_list(10, :user) +    users = insert_list(10, :user) + +    {:ok, activity} = CommonAPI.post(user, %{"status" => "."}) + +    users_serial +    |> Enum.map(fn user -> +      CommonAPI.favorite(user, activity.id) +    end) + +    object = Object.get_by_ap_id(activity.data["object"]) +    assert object.data["like_count"] == 10 + +    users +    |> Enum.map(fn user -> +      Task.async(fn -> +        CommonAPI.favorite(user, activity.id) +      end) +    end) +    |> Enum.map(&Task.await/1) + +    object = Object.get_by_ap_id(activity.data["object"]) +    assert object.data["like_count"] == 20 +  end + +  test "repeating race condition" do +    user = insert(:user) +    users_serial = insert_list(10, :user) +    users = insert_list(10, :user) + +    {:ok, activity} = CommonAPI.post(user, %{"status" => "."}) + +    users_serial +    |> Enum.map(fn user -> +      CommonAPI.repeat(activity.id, user) +    end) + +    object = Object.get_by_ap_id(activity.data["object"]) +    assert object.data["announcement_count"] == 10 + +    users +    |> Enum.map(fn user -> +      Task.async(fn -> +        CommonAPI.repeat(activity.id, user) +      end) +    end) +    |> Enum.map(&Task.await/1) + +    object = Object.get_by_ap_id(activity.data["object"]) +    assert object.data["announcement_count"] == 20 +  end +    test "when replying to a conversation / participation, it will set the correct context id even if no explicit reply_to is given" do      user = insert(:user)      {:ok, activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"}) @@ -256,6 +310,16 @@ defmodule Pleroma.Web.CommonAPITest do        {:ok, %Activity{}, _} = CommonAPI.repeat(activity.id, user)      end +    test "can't repeat a repeat" do +      user = insert(:user) +      other_user = insert(:user) +      {:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"}) + +      {:ok, %Activity{} = announce, _} = CommonAPI.repeat(activity.id, other_user) + +      refute match?({:ok, %Activity{}, _}, CommonAPI.repeat(announce.id, user)) +    end +      test "repeating a status privately" do        user = insert(:user)        other_user = insert(:user) @@ -285,8 +349,8 @@ defmodule Pleroma.Web.CommonAPITest do        other_user = insert(:user)        {:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"}) -      {:ok, %Activity{} = activity, object} = CommonAPI.repeat(activity.id, user) -      {:ok, ^activity, ^object} = CommonAPI.repeat(activity.id, user) +      {:ok, %Activity{} = announce, object} = CommonAPI.repeat(activity.id, user) +      {:ok, ^announce, ^object} = CommonAPI.repeat(activity.id, user)      end      test "favoriting a status twice returns ok, but without the like activity" do @@ -360,7 +424,9 @@ defmodule Pleroma.Web.CommonAPITest do        user = refresh_record(user) -      assert {:ok, ^activity} = CommonAPI.unpin(activity.id, user) +      id = activity.id + +      assert match?({:ok, %{id: ^id}}, CommonAPI.unpin(activity.id, user))        user = refresh_record(user) diff --git a/test/web/common_api/common_api_utils_test.exs b/test/web/common_api/common_api_utils_test.exs index b21445fe9..18a3b3b87 100644 --- a/test/web/common_api/common_api_utils_test.exs +++ b/test/web/common_api/common_api_utils_test.exs @@ -335,26 +335,6 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do      end    end -  describe "get_by_id_or_ap_id/1" do -    test "get activity by id" do -      activity = insert(:note_activity) -      %Pleroma.Activity{} = note = Utils.get_by_id_or_ap_id(activity.id) -      assert note.id == activity.id -    end - -    test "get activity by ap_id" do -      activity = insert(:note_activity) -      %Pleroma.Activity{} = note = Utils.get_by_id_or_ap_id(activity.data["object"]) -      assert note.id == activity.id -    end - -    test "get activity by object when type isn't `Create` " do -      activity = insert(:like_activity) -      %Pleroma.Activity{} = like = Utils.get_by_id_or_ap_id(activity.id) -      assert like.data["object"] == activity.data["object"] -    end -  end -    describe "to_master_date/1" do      test "removes microseconds from date (NaiveDateTime)" do        assert Utils.to_masto_date(~N[2015-01-23 23:50:07.123]) == "2015-01-23T23:50:07.000Z" diff --git a/test/web/mastodon_api/controllers/app_controller_test.exs b/test/web/mastodon_api/controllers/app_controller_test.exs index e7b11d14e..a0b8b126c 100644 --- a/test/web/mastodon_api/controllers/app_controller_test.exs +++ b/test/web/mastodon_api/controllers/app_controller_test.exs @@ -27,7 +27,7 @@ defmodule Pleroma.Web.MastodonAPI.AppControllerTest do        "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)      } -    assert expected == json_response(conn, 200) +    assert expected == json_response_and_validate_schema(conn, 200)    end    test "creates an oauth app", %{conn: conn} do @@ -55,6 +55,6 @@ defmodule Pleroma.Web.MastodonAPI.AppControllerTest do        "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)      } -    assert expected == json_response(conn, 200) +    assert expected == json_response_and_validate_schema(conn, 200)    end  end diff --git a/test/web/mastodon_api/controllers/custom_emoji_controller_test.exs b/test/web/mastodon_api/controllers/custom_emoji_controller_test.exs index 0b2ffa470..4222556a4 100644 --- a/test/web/mastodon_api/controllers/custom_emoji_controller_test.exs +++ b/test/web/mastodon_api/controllers/custom_emoji_controller_test.exs @@ -5,15 +5,13 @@  defmodule Pleroma.Web.MastodonAPI.CustomEmojiControllerTest do    use Pleroma.Web.ConnCase, async: true    alias Pleroma.Web.ApiSpec -  alias Pleroma.Web.ApiSpec.Schemas.CustomEmoji -  alias Pleroma.Web.ApiSpec.Schemas.CustomEmojisResponse    import OpenApiSpex.TestAssertions    test "with tags", %{conn: conn} do      assert resp =               conn               |> get("/api/v1/custom_emojis") -             |> json_response(200) +             |> json_response_and_validate_schema(200)      assert [emoji | _body] = resp      assert Map.has_key?(emoji, "shortcode") @@ -23,19 +21,6 @@ defmodule Pleroma.Web.MastodonAPI.CustomEmojiControllerTest do      assert Map.has_key?(emoji, "category")      assert Map.has_key?(emoji, "url")      assert Map.has_key?(emoji, "visible_in_picker") -    assert_schema(resp, "CustomEmojisResponse", ApiSpec.spec())      assert_schema(emoji, "CustomEmoji", ApiSpec.spec())    end - -  test "CustomEmoji example matches schema" do -    api_spec = ApiSpec.spec() -    schema = CustomEmoji.schema() -    assert_schema(schema.example, "CustomEmoji", api_spec) -  end - -  test "CustomEmojisResponse example matches schema" do -    api_spec = ApiSpec.spec() -    schema = CustomEmojisResponse.schema() -    assert_schema(schema.example, "CustomEmojisResponse", api_spec) -  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 d66190c90..01a24afcf 100644 --- a/test/web/mastodon_api/controllers/domain_block_controller_test.exs +++ b/test/web/mastodon_api/controllers/domain_block_controller_test.exs @@ -6,11 +6,8 @@ defmodule Pleroma.Web.MastodonAPI.DomainBlockControllerTest do    use Pleroma.Web.ConnCase    alias Pleroma.User -  alias Pleroma.Web.ApiSpec -  alias Pleroma.Web.ApiSpec.Schemas.DomainBlocksResponse    import Pleroma.Factory -  import OpenApiSpex.TestAssertions    test "blocking / unblocking a domain" do      %{user: user, conn: conn} = oauth_access(["write:blocks"]) @@ -21,7 +18,7 @@ defmodule Pleroma.Web.MastodonAPI.DomainBlockControllerTest do        |> put_req_header("content-type", "application/json")        |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"}) -    assert %{} = json_response(ret_conn, 200) +    assert %{} == json_response_and_validate_schema(ret_conn, 200)      user = User.get_cached_by_ap_id(user.ap_id)      assert User.blocks?(user, other_user) @@ -30,7 +27,7 @@ defmodule Pleroma.Web.MastodonAPI.DomainBlockControllerTest do        |> put_req_header("content-type", "application/json")        |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"}) -    assert %{} = json_response(ret_conn, 200) +    assert %{} == json_response_and_validate_schema(ret_conn, 200)      user = User.get_cached_by_ap_id(user.ap_id)      refute User.blocks?(user, other_user)    end @@ -41,21 +38,10 @@ defmodule Pleroma.Web.MastodonAPI.DomainBlockControllerTest do      {:ok, user} = User.block_domain(user, "bad.site")      {:ok, user} = User.block_domain(user, "even.worse.site") -    conn = -      conn -      |> assign(:user, user) -      |> get("/api/v1/domain_blocks") - -    domain_blocks = json_response(conn, 200) - -    assert "bad.site" in domain_blocks -    assert "even.worse.site" in domain_blocks -    assert_schema(domain_blocks, "DomainBlocksResponse", ApiSpec.spec()) -  end - -  test "DomainBlocksResponse example matches schema" do -    api_spec = ApiSpec.spec() -    schema = DomainBlocksResponse.schema() -    assert_schema(schema.example, "DomainBlocksResponse", api_spec) +    assert ["even.worse.site", "bad.site"] == +             conn +             |> assign(:user, user) +             |> get("/api/v1/domain_blocks") +             |> json_response_and_validate_schema(200)    end  end diff --git a/test/web/mastodon_api/controllers/status_controller_test.exs b/test/web/mastodon_api/controllers/status_controller_test.exs index 162f7b1b2..85068edd0 100644 --- a/test/web/mastodon_api/controllers/status_controller_test.exs +++ b/test/web/mastodon_api/controllers/status_controller_test.exs @@ -302,6 +302,17 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do        assert [] == Repo.all(Activity)      end +    test "ignores nil values", %{conn: conn} do +      conn = +        post(conn, "/api/v1/statuses", %{ +          "status" => "not scheduled", +          "scheduled_at" => nil +        }) + +      assert result = json_response(conn, 200) +      assert Activity.get_by_id(result["id"]) +    end +      test "creates a scheduled activity with a media attachment", %{user: user, conn: conn} do        scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)  | 
