diff options
| author | Mark Felder <feld@FreeBSD.org> | 2020-06-30 15:09:03 -0500 | 
|---|---|---|
| committer | Mark Felder <feld@FreeBSD.org> | 2020-06-30 15:09:03 -0500 | 
| commit | 0883a706dc376fdfb7de9df1366803e87c8e7c98 (patch) | |
| tree | dc43f7b09dddd3300efd7b3ebbc0e82e60a978c5 /test/web | |
| parent | 954acdda2072cac343409b3d17d831b86ac6a18c (diff) | |
| parent | b9e6ad571ac5925431466d6e6b27c0b372bb7727 (diff) | |
| download | pleroma-0883a706dc376fdfb7de9df1366803e87c8e7c98.tar.gz pleroma-0883a706dc376fdfb7de9df1366803e87c8e7c98.zip | |
Merge branch 'develop' into activation-meta
Diffstat (limited to 'test/web')
61 files changed, 5853 insertions, 2912 deletions
| diff --git a/test/web/activity_pub/activity_pub_controller_test.exs b/test/web/activity_pub/activity_pub_controller_test.exs index 24edab41a..e722f7c04 100644 --- a/test/web/activity_pub/activity_pub_controller_test.exs +++ b/test/web/activity_pub/activity_pub_controller_test.exs @@ -536,6 +536,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do        assert_receive {:mix_shell, :info, ["relay.mastodon.host"]}      end +    @tag capture_log: true      test "without valid signature, " <>             "it only accepts Create activities and requires enabled federation",           %{conn: conn} do @@ -648,11 +649,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do      test "it accepts announces with to as string instead of array", %{conn: conn} do        user = insert(:user) +      {:ok, post} = CommonAPI.post(user, %{status: "hey"}) +      announcer = insert(:user, local: false) +        data = %{          "@context" => "https://www.w3.org/ns/activitystreams", -        "actor" => "http://mastodon.example.org/users/admin", -        "id" => "http://mastodon.example.org/users/admin/statuses/19512778738411822/activity", -        "object" => "https://mastodon.social/users/emelie/statuses/101849165031453009", +        "actor" => announcer.ap_id, +        "id" => "#{announcer.ap_id}/statuses/19512778738411822/activity", +        "object" => post.data["object"],          "to" => "https://www.w3.org/ns/activitystreams#Public",          "cc" => [user.ap_id],          "type" => "Announce" @@ -804,17 +808,63 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do    end    describe "GET /users/:nickname/outbox" do +    test "it paginates correctly", %{conn: conn} do +      user = insert(:user) +      conn = assign(conn, :user, user) +      outbox_endpoint = user.ap_id <> "/outbox" + +      _posts = +        for i <- 0..25 do +          {:ok, activity} = CommonAPI.post(user, %{status: "post #{i}"}) +          activity +        end + +      result = +        conn +        |> put_req_header("accept", "application/activity+json") +        |> get(outbox_endpoint <> "?page=true") +        |> json_response(200) + +      result_ids = Enum.map(result["orderedItems"], fn x -> x["id"] end) +      assert length(result["orderedItems"]) == 20 +      assert length(result_ids) == 20 +      assert result["next"] +      assert String.starts_with?(result["next"], outbox_endpoint) + +      result_next = +        conn +        |> put_req_header("accept", "application/activity+json") +        |> get(result["next"]) +        |> json_response(200) + +      result_next_ids = Enum.map(result_next["orderedItems"], fn x -> x["id"] end) +      assert length(result_next["orderedItems"]) == 6 +      assert length(result_next_ids) == 6 +      refute Enum.find(result_next_ids, fn x -> x in result_ids end) +      refute Enum.find(result_ids, fn x -> x in result_next_ids end) +      assert String.starts_with?(result["id"], outbox_endpoint) + +      result_next_again = +        conn +        |> put_req_header("accept", "application/activity+json") +        |> get(result_next["id"]) +        |> json_response(200) + +      assert result_next == result_next_again +    end +      test "it returns 200 even if there're no activities", %{conn: conn} do        user = insert(:user) +      outbox_endpoint = user.ap_id <> "/outbox"        conn =          conn          |> assign(:user, user)          |> put_req_header("accept", "application/activity+json") -        |> get("/users/#{user.nickname}/outbox") +        |> get(outbox_endpoint)        result = json_response(conn, 200) -      assert user.ap_id <> "/outbox" == result["id"] +      assert outbox_endpoint == result["id"]      end      test "it returns a note activity in a collection", %{conn: conn} do diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs index 3dcb62873..575e0c5db 100644 --- a/test/web/activity_pub/activity_pub_test.exs +++ b/test/web/activity_pub/activity_pub_test.exs @@ -82,30 +82,28 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do        {:ok, private_activity} = CommonAPI.post(user, %{status: ".", visibility: "private"}) -      activities = -        ActivityPub.fetch_activities([], %{:visibility => "direct", "actor_id" => user.ap_id}) +      activities = ActivityPub.fetch_activities([], %{visibility: "direct", actor_id: user.ap_id})        assert activities == [direct_activity]        activities = -        ActivityPub.fetch_activities([], %{:visibility => "unlisted", "actor_id" => user.ap_id}) +        ActivityPub.fetch_activities([], %{visibility: "unlisted", actor_id: user.ap_id})        assert activities == [unlisted_activity]        activities = -        ActivityPub.fetch_activities([], %{:visibility => "private", "actor_id" => user.ap_id}) +        ActivityPub.fetch_activities([], %{visibility: "private", actor_id: user.ap_id})        assert activities == [private_activity] -      activities = -        ActivityPub.fetch_activities([], %{:visibility => "public", "actor_id" => user.ap_id}) +      activities = ActivityPub.fetch_activities([], %{visibility: "public", actor_id: user.ap_id})        assert activities == [public_activity]        activities =          ActivityPub.fetch_activities([], %{ -          :visibility => ~w[private public], -          "actor_id" => user.ap_id +          visibility: ~w[private public], +          actor_id: user.ap_id          })        assert activities == [public_activity, private_activity] @@ -126,8 +124,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do        activities =          ActivityPub.fetch_activities([], %{ -          "exclude_visibilities" => "direct", -          "actor_id" => user.ap_id +          exclude_visibilities: "direct", +          actor_id: user.ap_id          })        assert public_activity in activities @@ -137,8 +135,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do        activities =          ActivityPub.fetch_activities([], %{ -          "exclude_visibilities" => "unlisted", -          "actor_id" => user.ap_id +          exclude_visibilities: "unlisted", +          actor_id: user.ap_id          })        assert public_activity in activities @@ -148,8 +146,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do        activities =          ActivityPub.fetch_activities([], %{ -          "exclude_visibilities" => "private", -          "actor_id" => user.ap_id +          exclude_visibilities: "private", +          actor_id: user.ap_id          })        assert public_activity in activities @@ -159,8 +157,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do        activities =          ActivityPub.fetch_activities([], %{ -          "exclude_visibilities" => "public", -          "actor_id" => user.ap_id +          exclude_visibilities: "public", +          actor_id: user.ap_id          })        refute public_activity in activities @@ -193,23 +191,22 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do        {:ok, status_two} = CommonAPI.post(user, %{status: ". #essais"})        {:ok, status_three} = CommonAPI.post(user, %{status: ". #test #reject"}) -      fetch_one = ActivityPub.fetch_activities([], %{"type" => "Create", "tag" => "test"}) +      fetch_one = ActivityPub.fetch_activities([], %{type: "Create", tag: "test"}) -      fetch_two = -        ActivityPub.fetch_activities([], %{"type" => "Create", "tag" => ["test", "essais"]}) +      fetch_two = ActivityPub.fetch_activities([], %{type: "Create", tag: ["test", "essais"]})        fetch_three =          ActivityPub.fetch_activities([], %{ -          "type" => "Create", -          "tag" => ["test", "essais"], -          "tag_reject" => ["reject"] +          type: "Create", +          tag: ["test", "essais"], +          tag_reject: ["reject"]          })        fetch_four =          ActivityPub.fetch_activities([], %{ -          "type" => "Create", -          "tag" => ["test"], -          "tag_all" => ["test", "reject"] +          type: "Create", +          tag: ["test"], +          tag_all: ["test", "reject"]          })        assert fetch_one == [status_one, status_three] @@ -375,7 +372,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do        _listen_activity_2 = insert(:listen)        _listen_activity_3 = insert(:listen) -      timeline = ActivityPub.fetch_activities([], %{"type" => ["Listen"]}) +      timeline = ActivityPub.fetch_activities([], %{type: ["Listen"]})        assert length(timeline) == 3      end @@ -507,7 +504,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do        {:ok, _user_relationship} = User.block(user, %{ap_id: activity_five.data["actor"]}) -      activities = ActivityPub.fetch_activities_for_context("2hu", %{"blocking_user" => user}) +      activities = ActivityPub.fetch_activities_for_context("2hu", %{blocking_user: user})        assert activities == [activity_two, activity]      end    end @@ -520,8 +517,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      booster = insert(:user)      {:ok, _user_relationship} = User.block(user, %{ap_id: activity_one.data["actor"]}) -    activities = -      ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true}) +    activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})      assert Enum.member?(activities, activity_two)      assert Enum.member?(activities, activity_three) @@ -529,8 +525,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      {:ok, _user_block} = User.unblock(user, %{ap_id: activity_one.data["actor"]}) -    activities = -      ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true}) +    activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})      assert Enum.member?(activities, activity_two)      assert Enum.member?(activities, activity_three) @@ -541,16 +536,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      %Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)      activity_three = Activity.get_by_id(activity_three.id) -    activities = -      ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true}) +    activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})      assert Enum.member?(activities, activity_two)      refute Enum.member?(activities, activity_three)      refute Enum.member?(activities, boost_activity)      assert Enum.member?(activities, activity_one) -    activities = -      ActivityPub.fetch_activities([], %{"blocking_user" => nil, "skip_preload" => true}) +    activities = ActivityPub.fetch_activities([], %{blocking_user: nil, skip_preload: true})      assert Enum.member?(activities, activity_two)      assert Enum.member?(activities, activity_three) @@ -573,7 +566,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      {:ok, activity_four} = CommonAPI.post(blockee, %{status: "hey! @#{blocker.nickname}"}) -    activities = ActivityPub.fetch_activities([], %{"blocking_user" => blocker}) +    activities = ActivityPub.fetch_activities([], %{blocking_user: blocker})      assert Enum.member?(activities, activity_one)      refute Enum.member?(activities, activity_two) @@ -581,7 +574,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      refute Enum.member?(activities, activity_four)    end -  test "doesn't return announce activities concerning blocked users" do +  test "doesn't return announce activities with blocked users in 'to'" do      blocker = insert(:user)      blockee = insert(:user)      friend = insert(:user) @@ -595,7 +588,40 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      {:ok, activity_three} = CommonAPI.repeat(activity_two.id, friend)      activities = -      ActivityPub.fetch_activities([], %{"blocking_user" => blocker}) +      ActivityPub.fetch_activities([], %{blocking_user: blocker}) +      |> Enum.map(fn act -> act.id end) + +    assert Enum.member?(activities, activity_one.id) +    refute Enum.member?(activities, activity_two.id) +    refute Enum.member?(activities, activity_three.id) +  end + +  test "doesn't return announce activities with blocked users in 'cc'" do +    blocker = insert(:user) +    blockee = insert(:user) +    friend = insert(:user) + +    {:ok, _user_relationship} = User.block(blocker, blockee) + +    {:ok, activity_one} = CommonAPI.post(friend, %{status: "hey!"}) + +    {:ok, activity_two} = CommonAPI.post(blockee, %{status: "hey! @#{friend.nickname}"}) + +    assert object = Pleroma.Object.normalize(activity_two) + +    data = %{ +      "actor" => friend.ap_id, +      "object" => object.data["id"], +      "context" => object.data["context"], +      "type" => "Announce", +      "to" => ["https://www.w3.org/ns/activitystreams#Public"], +      "cc" => [blockee.ap_id] +    } + +    assert {:ok, activity_three} = ActivityPub.insert(data) + +    activities = +      ActivityPub.fetch_activities([], %{blocking_user: blocker})        |> Enum.map(fn act -> act.id end)      assert Enum.member?(activities, activity_one.id) @@ -611,8 +637,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      user = insert(:user)      {:ok, user} = User.block_domain(user, domain) -    activities = -      ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true}) +    activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})      refute activity in activities @@ -620,8 +645,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      ActivityPub.follow(user, followed_user)      {:ok, repeat_activity} = CommonAPI.repeat(activity.id, followed_user) -    activities = -      ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true}) +    activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})      refute repeat_activity in activities    end @@ -641,8 +665,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      note = insert(:note, %{data: %{"actor" => domain_user.ap_id}})      activity = insert(:note_activity, %{note: note}) -    activities = -      ActivityPub.fetch_activities([], %{"blocking_user" => blocker, "skip_preload" => true}) +    activities = ActivityPub.fetch_activities([], %{blocking_user: blocker, skip_preload: true})      assert activity in activities @@ -653,8 +676,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      bad_activity = insert(:note_activity, %{note: bad_note})      {:ok, repeat_activity} = CommonAPI.repeat(bad_activity.id, domain_user) -    activities = -      ActivityPub.fetch_activities([], %{"blocking_user" => blocker, "skip_preload" => true}) +    activities = ActivityPub.fetch_activities([], %{blocking_user: blocker, skip_preload: true})      refute repeat_activity in activities    end @@ -669,8 +691,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      activity_one_actor = User.get_by_ap_id(activity_one.data["actor"])      {:ok, _user_relationships} = User.mute(user, activity_one_actor) -    activities = -      ActivityPub.fetch_activities([], %{"muting_user" => user, "skip_preload" => true}) +    activities = ActivityPub.fetch_activities([], %{muting_user: user, skip_preload: true})      assert Enum.member?(activities, activity_two)      assert Enum.member?(activities, activity_three) @@ -679,9 +700,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      # Calling with 'with_muted' will deliver muted activities, too.      activities =        ActivityPub.fetch_activities([], %{ -        "muting_user" => user, -        "with_muted" => true, -        "skip_preload" => true +        muting_user: user, +        with_muted: true, +        skip_preload: true        })      assert Enum.member?(activities, activity_two) @@ -690,8 +711,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      {:ok, _user_mute} = User.unmute(user, activity_one_actor) -    activities = -      ActivityPub.fetch_activities([], %{"muting_user" => user, "skip_preload" => true}) +    activities = ActivityPub.fetch_activities([], %{muting_user: user, skip_preload: true})      assert Enum.member?(activities, activity_two)      assert Enum.member?(activities, activity_three) @@ -703,15 +723,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      %Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)      activity_three = Activity.get_by_id(activity_three.id) -    activities = -      ActivityPub.fetch_activities([], %{"muting_user" => user, "skip_preload" => true}) +    activities = ActivityPub.fetch_activities([], %{muting_user: user, skip_preload: true})      assert Enum.member?(activities, activity_two)      refute Enum.member?(activities, activity_three)      refute Enum.member?(activities, boost_activity)      assert Enum.member?(activities, activity_one) -    activities = ActivityPub.fetch_activities([], %{"muting_user" => nil, "skip_preload" => true}) +    activities = ActivityPub.fetch_activities([], %{muting_user: nil, skip_preload: true})      assert Enum.member?(activities, activity_two)      assert Enum.member?(activities, activity_three) @@ -727,7 +746,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      {:ok, _activity_two} = CommonAPI.add_mute(user, activity_two) -    assert [_activity_one] = ActivityPub.fetch_activities([], %{"muting_user" => user}) +    assert [_activity_one] = ActivityPub.fetch_activities([], %{muting_user: user})    end    test "returns thread muted activities when with_muted is set" do @@ -739,7 +758,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      {:ok, _activity_two} = CommonAPI.add_mute(user, activity_two)      assert [_activity_two, _activity_one] = -             ActivityPub.fetch_activities([], %{"muting_user" => user, "with_muted" => true}) +             ActivityPub.fetch_activities([], %{muting_user: user, with_muted: true})    end    test "does include announces on request" do @@ -761,7 +780,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      {:ok, expected_activity} = ActivityBuilder.insert(%{"type" => "Create"}, %{:user => user})      {:ok, _} = ActivityBuilder.insert(%{"type" => "Announce"}, %{:user => user}) -    [activity] = ActivityPub.fetch_user_activities(user, nil, %{"exclude_reblogs" => "true"}) +    [activity] = ActivityPub.fetch_user_activities(user, nil, %{exclude_reblogs: true})      assert activity == expected_activity    end @@ -804,7 +823,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do        expected_activities = ActivityBuilder.insert_list(10)        since_id = List.last(activities).id -      activities = ActivityPub.fetch_public_activities(%{"since_id" => since_id}) +      activities = ActivityPub.fetch_public_activities(%{since_id: since_id})        assert collect_ids(activities) == collect_ids(expected_activities)        assert length(activities) == 10 @@ -819,7 +838,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do          |> ActivityBuilder.insert_list()          |> List.first() -      activities = ActivityPub.fetch_public_activities(%{"max_id" => max_id}) +      activities = ActivityPub.fetch_public_activities(%{max_id: max_id})        assert length(activities) == 20        assert collect_ids(activities) == collect_ids(expected_activities) @@ -831,8 +850,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do        later_activities = ActivityBuilder.insert_list(10) -      activities = -        ActivityPub.fetch_public_activities(%{"page" => "2", "page_size" => "20"}, :offset) +      activities = ActivityPub.fetch_public_activities(%{page: "2", page_size: "20"}, :offset)        assert length(activities) == 20 @@ -848,7 +866,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do        {:ok, activity} = CommonAPI.repeat(activity.id, booster) -      activities = ActivityPub.fetch_activities([], %{"muting_user" => user}) +      activities = ActivityPub.fetch_activities([], %{muting_user: user})        refute Enum.any?(activities, fn %{id: id} -> id == activity.id end)      end @@ -862,7 +880,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do        {:ok, activity} = CommonAPI.repeat(activity.id, booster) -      activities = ActivityPub.fetch_activities([], %{"muting_user" => user}) +      activities = ActivityPub.fetch_activities([], %{muting_user: user})        assert Enum.any?(activities, fn %{id: id} -> id == activity.id end)      end @@ -974,54 +992,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      end    end -  describe "blocking" do -    test "reverts block activity on error" do -      [blocker, blocked] = insert_list(2, :user) - -      with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do -        assert {:error, :reverted} = ActivityPub.block(blocker, blocked) -      end - -      assert Repo.aggregate(Activity, :count, :id) == 0 -      assert Repo.aggregate(Object, :count, :id) == 0 -    end - -    test "creates a block activity" do -      clear_config([:instance, :federating], true) -      blocker = insert(:user) -      blocked = insert(:user) - -      with_mock Pleroma.Web.Federator, -        publish: fn _ -> nil end do -        {:ok, activity} = ActivityPub.block(blocker, blocked) - -        assert activity.data["type"] == "Block" -        assert activity.data["actor"] == blocker.ap_id -        assert activity.data["object"] == blocked.ap_id - -        assert called(Pleroma.Web.Federator.publish(activity)) -      end -    end - -    test "works with outgoing blocks disabled, but doesn't federate" do -      clear_config([:instance, :federating], true) -      clear_config([:activitypub, :outgoing_blocks], false) -      blocker = insert(:user) -      blocked = insert(:user) - -      with_mock Pleroma.Web.Federator, -        publish: fn _ -> nil end do -        {:ok, activity} = ActivityPub.block(blocker, blocked) - -        assert activity.data["type"] == "Block" -        assert activity.data["actor"] == blocker.ap_id -        assert activity.data["object"] == blocked.ap_id - -        refute called(Pleroma.Web.Federator.publish(:_)) -      end -    end -  end -    describe "timeline post-processing" do      test "it filters broken threads" do        user1 = insert(:user) @@ -1066,7 +1036,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do        assert length(activities) == 3        activities = -        ActivityPub.fetch_activities([user1.ap_id | User.following(user1)], %{"user" => user1}) +        ActivityPub.fetch_activities([user1.ap_id | User.following(user1)], %{user: user1})          |> Enum.map(fn a -> a.id end)        assert [public_activity.id, private_activity_1.id] == activities @@ -1074,52 +1044,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      end    end -  describe "update" do -    setup do: clear_config([:instance, :max_pinned_statuses]) - -    test "it creates an update activity with the new user data" do -      user = insert(:user) -      {:ok, user} = User.ensure_keys_present(user) -      user_data = Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: user}) - -      {:ok, update} = -        ActivityPub.update(%{ -          actor: user_data["id"], -          to: [user.follower_address], -          cc: [], -          object: user_data -        }) - -      assert update.data["actor"] == user.ap_id -      assert update.data["to"] == [user.follower_address] -      assert embedded_object = update.data["object"] -      assert embedded_object["id"] == user_data["id"] -      assert embedded_object["type"] == user_data["type"] -    end -  end - -  test "returned pinned statuses" do -    Config.put([:instance, :max_pinned_statuses], 3) -    user = insert(:user) - -    {:ok, activity_one} = CommonAPI.post(user, %{status: "HI!!!"}) -    {:ok, activity_two} = CommonAPI.post(user, %{status: "HI!!!"}) -    {:ok, activity_three} = CommonAPI.post(user, %{status: "HI!!!"}) - -    CommonAPI.pin(activity_one.id, user) -    user = refresh_record(user) - -    CommonAPI.pin(activity_two.id, user) -    user = refresh_record(user) - -    CommonAPI.pin(activity_three.id, user) -    user = refresh_record(user) - -    activities = ActivityPub.fetch_user_activities(user, nil, %{"pinned" => "true"}) - -    assert 3 = length(activities) -  end -    describe "flag/1" do      setup do        reporter = insert(:user) @@ -1226,7 +1150,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      activity = Repo.preload(activity, :bookmark)      activity = %Activity{activity | thread_muted?: !!activity.thread_muted?} -    assert ActivityPub.fetch_activities([], %{"user" => user}) == [activity] +    assert ActivityPub.fetch_activities([], %{user: user}) == [activity]    end    def data_uri do @@ -1400,7 +1324,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do        assert Enum.map(result, & &1.id) == [a1.id, a5.id, a3.id, a4.id] -      result = ActivityPub.fetch_favourites(user, %{"limit" => 2}) +      result = ActivityPub.fetch_favourites(user, %{limit: 2})        assert Enum.map(result, & &1.id) == [a1.id, a5.id]      end    end @@ -1470,7 +1394,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      {:ok, _reply} = CommonAPI.post(user, %{status: "yeah", in_reply_to_status_id: activity.id}) -    [result] = ActivityPub.fetch_public_activities(%{"exclude_replies" => "true"}) +    [result] = ActivityPub.fetch_public_activities(%{exclude_replies: true})      assert result.id == activity.id @@ -1483,11 +1407,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      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) +        |> 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) @@ -1504,12 +1428,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      } 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(: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) @@ -1531,12 +1455,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      } 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(: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) @@ -1555,11 +1479,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      } 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) +        |> 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) @@ -1593,12 +1517,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      } 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) +        |> 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) @@ -1632,12 +1556,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      } 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) +        |> 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) @@ -1658,6 +1582,40 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do        assert Enum.all?(visible_ids, &(&1 in activities_ids))      end + +    test "filtering out announces where the user is the actor of the announced message" do +      user = insert(:user) +      other_user = insert(:user) +      third_user = insert(:user) +      User.follow(user, other_user) + +      {:ok, post} = CommonAPI.post(user, %{status: "yo"}) +      {:ok, other_post} = CommonAPI.post(third_user, %{status: "yo"}) +      {:ok, _announce} = CommonAPI.repeat(post.id, other_user) +      {:ok, _announce} = CommonAPI.repeat(post.id, third_user) +      {:ok, announce} = CommonAPI.repeat(other_post.id, other_user) + +      params = %{ +        type: ["Announce"] +      } + +      results = +        [user.ap_id | User.following(user)] +        |> ActivityPub.fetch_activities(params) + +      assert length(results) == 3 + +      params = %{ +        type: ["Announce"], +        announce_filtering_user: user +      } + +      [result] = +        [user.ap_id | User.following(user)] +        |> ActivityPub.fetch_activities(params) + +      assert result.id == announce.id +    end    end    describe "replies filtering with private messages" do @@ -1666,11 +1624,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      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) +        |> 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) @@ -1680,13 +1638,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      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) +        |> 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) @@ -1696,13 +1654,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      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) +        |> 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) @@ -1712,10 +1670,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      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) +        |> 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) @@ -1727,12 +1685,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      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) +        |> 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) @@ -1751,12 +1709,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      } 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) +        |> 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) @@ -2001,4 +1959,20 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do               end) =~ "Follower/Following counter update for #{user.ap_id} failed"      end    end + +  describe "global activity expiration" do +    setup do: clear_config([:mrf, :policies]) + +    test "creates an activity expiration for local Create activities" do +      Pleroma.Config.put( +        [:mrf, :policies], +        Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy +      ) + +      {:ok, %{id: id_create}} = ActivityBuilder.insert(%{"type" => "Create", "context" => "3hu"}) +      {:ok, _follow} = ActivityBuilder.insert(%{"type" => "Follow", "context" => "3hu"}) + +      assert [%{activity_id: ^id_create}] = Pleroma.ActivityExpiration |> Repo.all() +    end +  end  end diff --git a/test/web/activity_pub/mrf/activity_expiration_policy_test.exs b/test/web/activity_pub/mrf/activity_expiration_policy_test.exs new file mode 100644 index 000000000..8babf49e7 --- /dev/null +++ b/test/web/activity_pub/mrf/activity_expiration_policy_test.exs @@ -0,0 +1,77 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicyTest do +  use ExUnit.Case, async: true +  alias Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy + +  @id Pleroma.Web.Endpoint.url() <> "/activities/cofe" + +  test "adds `expires_at` property" do +    assert {:ok, %{"type" => "Create", "expires_at" => expires_at}} = +             ActivityExpirationPolicy.filter(%{ +               "id" => @id, +               "type" => "Create", +               "object" => %{"type" => "Note"} +             }) + +    assert Timex.diff(expires_at, NaiveDateTime.utc_now(), :days) == 364 +  end + +  test "keeps existing `expires_at` if it less than the config setting" do +    expires_at = NaiveDateTime.utc_now() |> Timex.shift(days: 1) + +    assert {:ok, %{"type" => "Create", "expires_at" => ^expires_at}} = +             ActivityExpirationPolicy.filter(%{ +               "id" => @id, +               "type" => "Create", +               "expires_at" => expires_at, +               "object" => %{"type" => "Note"} +             }) +  end + +  test "overwrites existing `expires_at` if it greater than the config setting" do +    too_distant_future = NaiveDateTime.utc_now() |> Timex.shift(years: 2) + +    assert {:ok, %{"type" => "Create", "expires_at" => expires_at}} = +             ActivityExpirationPolicy.filter(%{ +               "id" => @id, +               "type" => "Create", +               "expires_at" => too_distant_future, +               "object" => %{"type" => "Note"} +             }) + +    assert Timex.diff(expires_at, NaiveDateTime.utc_now(), :days) == 364 +  end + +  test "ignores remote activities" do +    assert {:ok, activity} = +             ActivityExpirationPolicy.filter(%{ +               "id" => "https://example.com/123", +               "type" => "Create", +               "object" => %{"type" => "Note"} +             }) + +    refute Map.has_key?(activity, "expires_at") +  end + +  test "ignores non-Create/Note activities" do +    assert {:ok, activity} = +             ActivityExpirationPolicy.filter(%{ +               "id" => "https://example.com/123", +               "type" => "Follow" +             }) + +    refute Map.has_key?(activity, "expires_at") + +    assert {:ok, activity} = +             ActivityExpirationPolicy.filter(%{ +               "id" => "https://example.com/123", +               "type" => "Create", +               "object" => %{"type" => "Cofe"} +             }) + +    refute Map.has_key?(activity, "expires_at") +  end +end diff --git a/test/web/activity_pub/mrf/anti_link_spam_policy_test.exs b/test/web/activity_pub/mrf/anti_link_spam_policy_test.exs index 1a13699be..6867c9853 100644 --- a/test/web/activity_pub/mrf/anti_link_spam_policy_test.exs +++ b/test/web/activity_pub/mrf/anti_link_spam_policy_test.exs @@ -33,7 +33,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicyTest do    describe "with new user" do      test "it allows posts without links" do -      user = insert(:user) +      user = insert(:user, local: false)        assert user.note_count == 0 @@ -45,7 +45,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicyTest do      end      test "it disallows posts with links" do -      user = insert(:user) +      user = insert(:user, local: false)        assert user.note_count == 0 @@ -55,6 +55,18 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicyTest do        {:reject, _} = AntiLinkSpamPolicy.filter(message)      end + +    test "it allows posts with links for local users" do +      user = insert(:user) + +      assert user.note_count == 0 + +      message = +        @linkful_message +        |> Map.put("actor", user.ap_id) + +      {:ok, _message} = AntiLinkSpamPolicy.filter(message) +    end    end    describe "with old user" do diff --git a/test/web/activity_pub/mrf/hellthread_policy_test.exs b/test/web/activity_pub/mrf/hellthread_policy_test.exs index 95ef0b168..6e9daa7f9 100644 --- a/test/web/activity_pub/mrf/hellthread_policy_test.exs +++ b/test/web/activity_pub/mrf/hellthread_policy_test.exs @@ -8,6 +8,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicyTest do    import Pleroma.Web.ActivityPub.MRF.HellthreadPolicy +  alias Pleroma.Web.CommonAPI +    setup do      user = insert(:user) @@ -20,7 +22,10 @@ defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicyTest do          "https://instance.tld/users/user1",          "https://instance.tld/users/user2",          "https://instance.tld/users/user3" -      ] +      ], +      "object" => %{ +        "type" => "Note" +      }      }      [user: user, message: message] @@ -28,6 +33,17 @@ defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicyTest do    setup do: clear_config(:mrf_hellthread) +  test "doesn't die on chat messages" do +    Pleroma.Config.put([:mrf_hellthread], %{delist_threshold: 2, reject_threshold: 0}) + +    user = insert(:user) +    other_user = insert(:user) + +    {:ok, activity} = CommonAPI.post_chat_message(user, other_user, "moin") + +    assert {:ok, _} = filter(activity.data) +  end +    describe "reject" do      test "rejects the message if the recipient count is above reject_threshold", %{        message: message diff --git a/test/web/activity_pub/mrf/mrf_test.exs b/test/web/activity_pub/mrf/mrf_test.exs index c941066f2..a63b25423 100644 --- a/test/web/activity_pub/mrf/mrf_test.exs +++ b/test/web/activity_pub/mrf/mrf_test.exs @@ -60,8 +60,6 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do    end    describe "describe/0" do -    setup do: clear_config([:instance, :rewrite_policy]) -      test "it works as expected with noop policy" do        expected = %{          mrf_policies: ["NoOpPolicy"], @@ -72,7 +70,7 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do      end      test "it works as expected with mock policy" do -      Pleroma.Config.put([:instance, :rewrite_policy], [MRFModuleMock]) +      clear_config([:mrf, :policies], [MRFModuleMock])        expected = %{          mrf_policies: ["MRFModuleMock"], diff --git a/test/web/activity_pub/mrf/user_allowlist_policy_test.exs b/test/web/activity_pub/mrf/user_allowlist_policy_test.exs index 724bae058..ba1b69658 100644 --- a/test/web/activity_pub/mrf/user_allowlist_policy_test.exs +++ b/test/web/activity_pub/mrf/user_allowlist_policy_test.exs @@ -7,7 +7,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.UserAllowListPolicyTest do    alias Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy -  setup do: clear_config([:mrf_user_allowlist, :localhost]) +  setup do: clear_config(:mrf_user_allowlist)    test "pass filter if allow list is empty" do      actor = insert(:user) @@ -17,14 +17,14 @@ defmodule Pleroma.Web.ActivityPub.MRF.UserAllowListPolicyTest do    test "pass filter if allow list isn't empty and user in allow list" do      actor = insert(:user) -    Pleroma.Config.put([:mrf_user_allowlist, :localhost], [actor.ap_id, "test-ap-id"]) +    Pleroma.Config.put([:mrf_user_allowlist], %{"localhost" => [actor.ap_id, "test-ap-id"]})      message = %{"actor" => actor.ap_id}      assert UserAllowListPolicy.filter(message) == {:ok, message}    end    test "rejected if allow list isn't empty and user not in allow list" do      actor = insert(:user) -    Pleroma.Config.put([:mrf_user_allowlist, :localhost], ["test-ap-id"]) +    Pleroma.Config.put([:mrf_user_allowlist], %{"localhost" => ["test-ap-id"]})      message = %{"actor" => actor.ap_id}      assert UserAllowListPolicy.filter(message) == {:reject, nil}    end diff --git a/test/web/activity_pub/object_validator_test.exs b/test/web/activity_pub/object_validator_test.exs index 7953eecf2..f38bf7e08 100644 --- a/test/web/activity_pub/object_validator_test.exs +++ b/test/web/activity_pub/object_validator_test.exs @@ -2,14 +2,264 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidatorTest do    use Pleroma.DataCase    alias Pleroma.Object +  alias Pleroma.Web.ActivityPub.ActivityPub    alias Pleroma.Web.ActivityPub.Builder    alias Pleroma.Web.ActivityPub.ObjectValidator +  alias Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator    alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator    alias Pleroma.Web.ActivityPub.Utils    alias Pleroma.Web.CommonAPI    import Pleroma.Factory +  describe "attachments" do +    test "works with honkerific attachments" do +      attachment = %{ +        "mediaType" => "", +        "name" => "", +        "summary" => "298p3RG7j27tfsZ9RQ.jpg", +        "type" => "Document", +        "url" => "https://honk.tedunangst.com/d/298p3RG7j27tfsZ9RQ.jpg" +      } + +      assert {:ok, attachment} = +               AttachmentValidator.cast_and_validate(attachment) +               |> Ecto.Changeset.apply_action(:insert) + +      assert attachment.mediaType == "application/octet-stream" +    end + +    test "it turns mastodon attachments into our attachments" do +      attachment = %{ +        "url" => +          "http://mastodon.example.org/system/media_attachments/files/000/000/002/original/334ce029e7bfb920.jpg", +        "type" => "Document", +        "name" => nil, +        "mediaType" => "image/jpeg" +      } + +      {:ok, attachment} = +        AttachmentValidator.cast_and_validate(attachment) +        |> Ecto.Changeset.apply_action(:insert) + +      assert [ +               %{ +                 href: +                   "http://mastodon.example.org/system/media_attachments/files/000/000/002/original/334ce029e7bfb920.jpg", +                 type: "Link", +                 mediaType: "image/jpeg" +               } +             ] = attachment.url + +      assert attachment.mediaType == "image/jpeg" +    end + +    test "it handles our own uploads" do +      user = insert(:user) + +      file = %Plug.Upload{ +        content_type: "image/jpg", +        path: Path.absname("test/fixtures/image.jpg"), +        filename: "an_image.jpg" +      } + +      {:ok, attachment} = ActivityPub.upload(file, actor: user.ap_id) + +      {:ok, attachment} = +        attachment.data +        |> AttachmentValidator.cast_and_validate() +        |> Ecto.Changeset.apply_action(:insert) + +      assert attachment.mediaType == "image/jpeg" +    end +  end + +  describe "chat message create activities" do +    test "it is invalid if the object already exists" do +      user = insert(:user) +      recipient = insert(:user) +      {:ok, activity} = CommonAPI.post_chat_message(user, recipient, "hey") +      object = Object.normalize(activity, false) + +      {:ok, create_data, _} = Builder.create(user, object.data, [recipient.ap_id]) + +      {:error, cng} = ObjectValidator.validate(create_data, []) + +      assert {:object, {"The object to create already exists", []}} in cng.errors +    end + +    test "it is invalid if the object data has a different `to` or `actor` field" do +      user = insert(:user) +      recipient = insert(:user) +      {:ok, object_data, _} = Builder.chat_message(recipient, user.ap_id, "Hey") + +      {:ok, create_data, _} = Builder.create(user, object_data, [recipient.ap_id]) + +      {:error, cng} = ObjectValidator.validate(create_data, []) + +      assert {:to, {"Recipients don't match with object recipients", []}} in cng.errors +      assert {:actor, {"Actor doesn't match with object actor", []}} in cng.errors +    end +  end + +  describe "chat messages" do +    setup do +      clear_config([:instance, :remote_limit]) +      user = insert(:user) +      recipient = insert(:user, local: false) + +      {:ok, valid_chat_message, _} = Builder.chat_message(user, recipient.ap_id, "hey :firefox:") + +      %{user: user, recipient: recipient, valid_chat_message: valid_chat_message} +    end + +    test "let's through some basic html", %{user: user, recipient: recipient} do +      {:ok, valid_chat_message, _} = +        Builder.chat_message( +          user, +          recipient.ap_id, +          "hey <a href='https://example.org'>example</a> <script>alert('uguu')</script>" +        ) + +      assert {:ok, object, _meta} = ObjectValidator.validate(valid_chat_message, []) + +      assert object["content"] == +               "hey <a href=\"https://example.org\">example</a> alert('uguu')" +    end + +    test "validates for a basic object we build", %{valid_chat_message: valid_chat_message} do +      assert {:ok, object, _meta} = ObjectValidator.validate(valid_chat_message, []) + +      assert Map.put(valid_chat_message, "attachment", nil) == object +    end + +    test "validates for a basic object with an attachment", %{ +      valid_chat_message: valid_chat_message, +      user: user +    } do +      file = %Plug.Upload{ +        content_type: "image/jpg", +        path: Path.absname("test/fixtures/image.jpg"), +        filename: "an_image.jpg" +      } + +      {:ok, attachment} = ActivityPub.upload(file, actor: user.ap_id) + +      valid_chat_message = +        valid_chat_message +        |> Map.put("attachment", attachment.data) + +      assert {:ok, object, _meta} = ObjectValidator.validate(valid_chat_message, []) + +      assert object["attachment"] +    end + +    test "validates for a basic object with an attachment in an array", %{ +      valid_chat_message: valid_chat_message, +      user: user +    } do +      file = %Plug.Upload{ +        content_type: "image/jpg", +        path: Path.absname("test/fixtures/image.jpg"), +        filename: "an_image.jpg" +      } + +      {:ok, attachment} = ActivityPub.upload(file, actor: user.ap_id) + +      valid_chat_message = +        valid_chat_message +        |> Map.put("attachment", [attachment.data]) + +      assert {:ok, object, _meta} = ObjectValidator.validate(valid_chat_message, []) + +      assert object["attachment"] +    end + +    test "validates for a basic object with an attachment but without content", %{ +      valid_chat_message: valid_chat_message, +      user: user +    } do +      file = %Plug.Upload{ +        content_type: "image/jpg", +        path: Path.absname("test/fixtures/image.jpg"), +        filename: "an_image.jpg" +      } + +      {:ok, attachment} = ActivityPub.upload(file, actor: user.ap_id) + +      valid_chat_message = +        valid_chat_message +        |> Map.put("attachment", attachment.data) +        |> Map.delete("content") + +      assert {:ok, object, _meta} = ObjectValidator.validate(valid_chat_message, []) + +      assert object["attachment"] +    end + +    test "does not validate if the message has no content", %{ +      valid_chat_message: valid_chat_message +    } do +      contentless = +        valid_chat_message +        |> Map.delete("content") + +      refute match?({:ok, _object, _meta}, ObjectValidator.validate(contentless, [])) +    end + +    test "does not validate if the message is longer than the remote_limit", %{ +      valid_chat_message: valid_chat_message +    } do +      Pleroma.Config.put([:instance, :remote_limit], 2) +      refute match?({:ok, _object, _meta}, ObjectValidator.validate(valid_chat_message, [])) +    end + +    test "does not validate if the recipient is blocking the actor", %{ +      valid_chat_message: valid_chat_message, +      user: user, +      recipient: recipient +    } do +      Pleroma.User.block(recipient, user) +      refute match?({:ok, _object, _meta}, ObjectValidator.validate(valid_chat_message, [])) +    end + +    test "does not validate if the actor or the recipient is not in our system", %{ +      valid_chat_message: valid_chat_message +    } do +      chat_message = +        valid_chat_message +        |> Map.put("actor", "https://raymoo.com/raymoo") + +      {:error, _} = ObjectValidator.validate(chat_message, []) + +      chat_message = +        valid_chat_message +        |> Map.put("to", ["https://raymoo.com/raymoo"]) + +      {:error, _} = ObjectValidator.validate(chat_message, []) +    end + +    test "does not validate for a message with multiple recipients", %{ +      valid_chat_message: valid_chat_message, +      user: user, +      recipient: recipient +    } do +      chat_message = +        valid_chat_message +        |> Map.put("to", [user.ap_id, recipient.ap_id]) + +      assert {:error, _} = ObjectValidator.validate(chat_message, []) +    end + +    test "does not validate if it doesn't concern local users" do +      user = insert(:user, local: false) +      recipient = insert(:user, local: false) + +      {:ok, valid_chat_message, _} = Builder.chat_message(user, recipient.ap_id, "hey") +      assert {:error, _} = ObjectValidator.validate(valid_chat_message, []) +    end +  end +    describe "EmojiReacts" do      setup do        user = insert(:user) @@ -372,4 +622,63 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidatorTest do        assert {:actor, {"can not announce this object publicly", []}} in cng.errors      end    end + +  describe "updates" do +    setup do +      user = insert(:user) + +      object = %{ +        "id" => user.ap_id, +        "name" => "A new name", +        "summary" => "A new bio" +      } + +      {:ok, valid_update, []} = Builder.update(user, object) + +      %{user: user, valid_update: valid_update} +    end + +    test "validates a basic object", %{valid_update: valid_update} do +      assert {:ok, _update, []} = ObjectValidator.validate(valid_update, []) +    end + +    test "returns an error if the object can't be updated by the actor", %{ +      valid_update: valid_update +    } do +      other_user = insert(:user) + +      update = +        valid_update +        |> Map.put("actor", other_user.ap_id) + +      assert {:error, _cng} = ObjectValidator.validate(update, []) +    end +  end + +  describe "blocks" do +    setup do +      user = insert(:user, local: false) +      blocked = insert(:user) + +      {:ok, valid_block, []} = Builder.block(user, blocked) + +      %{user: user, valid_block: valid_block} +    end + +    test "validates a basic object", %{ +      valid_block: valid_block +    } do +      assert {:ok, _block, []} = ObjectValidator.validate(valid_block, []) +    end + +    test "returns an error if we don't know the blocked user", %{ +      valid_block: valid_block +    } do +      block = +        valid_block +        |> Map.put("object", "https://gensokyo.2hu/users/raymoo") + +      assert {:error, _cng} = ObjectValidator.validate(block, []) +    end +  end  end diff --git a/test/web/activity_pub/object_validators/types/date_time_test.exs b/test/web/activity_pub/object_validators/types/date_time_test.exs index 3e17a9497..43be8e936 100644 --- a/test/web/activity_pub/object_validators/types/date_time_test.exs +++ b/test/web/activity_pub/object_validators/types/date_time_test.exs @@ -1,5 +1,5 @@  defmodule Pleroma.Web.ActivityPub.ObjectValidators.Types.DateTimeTest do -  alias Pleroma.Web.ActivityPub.ObjectValidators.Types.DateTime +  alias Pleroma.EctoType.ActivityPub.ObjectValidators.DateTime    use Pleroma.DataCase    test "it validates an xsd:Datetime" do diff --git a/test/web/activity_pub/object_validators/types/object_id_test.exs b/test/web/activity_pub/object_validators/types/object_id_test.exs index 834213182..e0ab76379 100644 --- a/test/web/activity_pub/object_validators/types/object_id_test.exs +++ b/test/web/activity_pub/object_validators/types/object_id_test.exs @@ -1,5 +1,9 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only +  defmodule Pleroma.Web.ObjectValidators.Types.ObjectIDTest do -  alias Pleroma.Web.ActivityPub.ObjectValidators.Types.ObjectID +  alias Pleroma.EctoType.ActivityPub.ObjectValidators.ObjectID    use Pleroma.DataCase    @uris [ diff --git a/test/web/activity_pub/object_validators/types/recipients_test.exs b/test/web/activity_pub/object_validators/types/recipients_test.exs index f278f039b..053916bdd 100644 --- a/test/web/activity_pub/object_validators/types/recipients_test.exs +++ b/test/web/activity_pub/object_validators/types/recipients_test.exs @@ -1,5 +1,5 @@  defmodule Pleroma.Web.ObjectValidators.Types.RecipientsTest do -  alias Pleroma.Web.ActivityPub.ObjectValidators.Types.Recipients +  alias Pleroma.EctoType.ActivityPub.ObjectValidators.Recipients    use Pleroma.DataCase    test "it asserts that all elements of the list are object ids" do diff --git a/test/web/activity_pub/object_validators/types/safe_text_test.exs b/test/web/activity_pub/object_validators/types/safe_text_test.exs new file mode 100644 index 000000000..9c08606f6 --- /dev/null +++ b/test/web/activity_pub/object_validators/types/safe_text_test.exs @@ -0,0 +1,30 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.ObjectValidators.Types.SafeTextTest do +  use Pleroma.DataCase + +  alias Pleroma.EctoType.ActivityPub.ObjectValidators.SafeText + +  test "it lets normal text go through" do +    text = "hey how are you" +    assert {:ok, text} == SafeText.cast(text) +  end + +  test "it removes html tags from text" do +    text = "hey look xss <script>alert('foo')</script>" +    assert {:ok, "hey look xss alert('foo')"} == SafeText.cast(text) +  end + +  test "it keeps basic html tags" do +    text = "hey <a href='http://gensokyo.2hu'>look</a> xss <script>alert('foo')</script>" + +    assert {:ok, "hey <a href=\"http://gensokyo.2hu\">look</a> xss alert('foo')"} == +             SafeText.cast(text) +  end + +  test "errors for non-text" do +    assert :error == SafeText.cast(1) +  end +end diff --git a/test/web/activity_pub/pipeline_test.exs b/test/web/activity_pub/pipeline_test.exs index 26557720b..8deb64501 100644 --- a/test/web/activity_pub/pipeline_test.exs +++ b/test/web/activity_pub/pipeline_test.exs @@ -33,7 +33,10 @@ defmodule Pleroma.Web.ActivityPub.PipelineTest do          {            Pleroma.Web.ActivityPub.SideEffects,            [], -          [handle: fn o, m -> {:ok, o, m} end] +          [ +            handle: fn o, m -> {:ok, o, m} end, +            handle_after_transaction: fn m -> m end +          ]          },          {            Pleroma.Web.Federator, @@ -71,7 +74,7 @@ defmodule Pleroma.Web.ActivityPub.PipelineTest do          {            Pleroma.Web.ActivityPub.SideEffects,            [], -          [handle: fn o, m -> {:ok, o, m} end] +          [handle: fn o, m -> {:ok, o, m} end, handle_after_transaction: fn m -> m end]          },          {            Pleroma.Web.Federator, @@ -110,7 +113,7 @@ defmodule Pleroma.Web.ActivityPub.PipelineTest do          {            Pleroma.Web.ActivityPub.SideEffects,            [], -          [handle: fn o, m -> {:ok, o, m} end] +          [handle: fn o, m -> {:ok, o, m} end, handle_after_transaction: fn m -> m end]          },          {            Pleroma.Web.Federator, diff --git a/test/web/activity_pub/side_effects_test.exs b/test/web/activity_pub/side_effects_test.exs index a80104ea7..af27c34b4 100644 --- a/test/web/activity_pub/side_effects_test.exs +++ b/test/web/activity_pub/side_effects_test.exs @@ -7,6 +7,8 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do    use Pleroma.DataCase    alias Pleroma.Activity +  alias Pleroma.Chat +  alias Pleroma.Chat.MessageReference    alias Pleroma.Notification    alias Pleroma.Object    alias Pleroma.Repo @@ -20,6 +22,114 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do    import Pleroma.Factory    import Mock +  describe "handle_after_transaction" do +    test "it streams out notifications and streams" do +      author = insert(:user, local: true) +      recipient = insert(:user, local: true) + +      {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey") + +      {:ok, create_activity_data, _meta} = +        Builder.create(author, chat_message_data["id"], [recipient.ap_id]) + +      {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false) + +      {:ok, _create_activity, meta} = +        SideEffects.handle(create_activity, local: false, object_data: chat_message_data) + +      assert [notification] = meta[:notifications] + +      with_mocks([ +        { +          Pleroma.Web.Streamer, +          [], +          [ +            stream: fn _, _ -> nil end +          ] +        }, +        { +          Pleroma.Web.Push, +          [], +          [ +            send: fn _ -> nil end +          ] +        } +      ]) do +        SideEffects.handle_after_transaction(meta) + +        assert called(Pleroma.Web.Streamer.stream(["user", "user:notification"], notification)) +        assert called(Pleroma.Web.Streamer.stream(["user", "user:pleroma_chat"], :_)) +        assert called(Pleroma.Web.Push.send(notification)) +      end +    end +  end + +  describe "blocking users" do +    setup do +      user = insert(:user) +      blocked = insert(:user) +      User.follow(blocked, user) +      User.follow(user, blocked) + +      {:ok, block_data, []} = Builder.block(user, blocked) +      {:ok, block, _meta} = ActivityPub.persist(block_data, local: true) + +      %{user: user, blocked: blocked, block: block} +    end + +    test "it unfollows and blocks", %{user: user, blocked: blocked, block: block} do +      assert User.following?(user, blocked) +      assert User.following?(blocked, user) + +      {:ok, _, _} = SideEffects.handle(block) + +      refute User.following?(user, blocked) +      refute User.following?(blocked, user) +      assert User.blocks?(user, blocked) +    end + +    test "it blocks but does not unfollow if the relevant setting is set", %{ +      user: user, +      blocked: blocked, +      block: block +    } do +      clear_config([:activitypub, :unfollow_blocked], false) +      assert User.following?(user, blocked) +      assert User.following?(blocked, user) + +      {:ok, _, _} = SideEffects.handle(block) + +      refute User.following?(user, blocked) +      assert User.following?(blocked, user) +      assert User.blocks?(user, blocked) +    end +  end + +  describe "update users" do +    setup do +      user = insert(:user) +      {:ok, update_data, []} = Builder.update(user, %{"id" => user.ap_id, "name" => "new name!"}) +      {:ok, update, _meta} = ActivityPub.persist(update_data, local: true) + +      %{user: user, update_data: update_data, update: update} +    end + +    test "it updates the user", %{user: user, update: update} do +      {:ok, _, _} = SideEffects.handle(update) +      user = User.get_by_id(user.id) +      assert user.name == "new name!" +    end + +    test "it uses a given changeset to update", %{user: user, update: update} do +      changeset = Ecto.Changeset.change(user, %{default_scope: "direct"}) + +      assert user.default_scope == "public" +      {:ok, _, _} = SideEffects.handle(update, user_update_changeset: changeset) +      user = User.get_by_id(user.id) +      assert user.default_scope == "direct" +    end +  end +    describe "delete objects" do      setup do        user = insert(:user) @@ -173,8 +283,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do        {:ok, like} = CommonAPI.favorite(user, post.id)        {:ok, reaction} = CommonAPI.react_with_emoji(post.id, user, "👍")        {:ok, announce} = CommonAPI.repeat(post.id, user) -      {:ok, block} = ActivityPub.block(user, poster) -      User.block(user, poster) +      {:ok, block} = CommonAPI.block(user, poster)        {:ok, undo_data, _meta} = Builder.undo(user, like)        {:ok, like_undo, _meta} = ActivityPub.persist(undo_data, local: true) @@ -290,6 +399,147 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do      end    end +  describe "creation of ChatMessages" do +    test "notifies the recipient" do +      author = insert(:user, local: false) +      recipient = insert(:user, local: true) + +      {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey") + +      {:ok, create_activity_data, _meta} = +        Builder.create(author, chat_message_data["id"], [recipient.ap_id]) + +      {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false) + +      {:ok, _create_activity, _meta} = +        SideEffects.handle(create_activity, local: false, object_data: chat_message_data) + +      assert Repo.get_by(Notification, user_id: recipient.id, activity_id: create_activity.id) +    end + +    test "it streams the created ChatMessage" do +      author = insert(:user, local: true) +      recipient = insert(:user, local: true) + +      {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey") + +      {:ok, create_activity_data, _meta} = +        Builder.create(author, chat_message_data["id"], [recipient.ap_id]) + +      {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false) + +      {:ok, _create_activity, meta} = +        SideEffects.handle(create_activity, local: false, object_data: chat_message_data) + +      assert [_, _] = meta[:streamables] +    end + +    test "it creates a Chat and MessageReferences for the local users and bumps the unread count, except for the author" do +      author = insert(:user, local: true) +      recipient = insert(:user, local: true) + +      {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey") + +      {:ok, create_activity_data, _meta} = +        Builder.create(author, chat_message_data["id"], [recipient.ap_id]) + +      {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false) + +      with_mocks([ +        { +          Pleroma.Web.Streamer, +          [], +          [ +            stream: fn _, _ -> nil end +          ] +        }, +        { +          Pleroma.Web.Push, +          [], +          [ +            send: fn _ -> nil end +          ] +        } +      ]) do +        {:ok, _create_activity, meta} = +          SideEffects.handle(create_activity, local: false, object_data: chat_message_data) + +        # The notification gets created +        assert [notification] = meta[:notifications] +        assert notification.activity_id == create_activity.id + +        # But it is not sent out +        refute called(Pleroma.Web.Streamer.stream(["user", "user:notification"], notification)) +        refute called(Pleroma.Web.Push.send(notification)) + +        # Same for the user chat stream +        assert [{topics, _}, _] = meta[:streamables] +        assert topics == ["user", "user:pleroma_chat"] +        refute called(Pleroma.Web.Streamer.stream(["user", "user:pleroma_chat"], :_)) + +        chat = Chat.get(author.id, recipient.ap_id) + +        [cm_ref] = MessageReference.for_chat_query(chat) |> Repo.all() + +        assert cm_ref.object.data["content"] == "hey" +        assert cm_ref.unread == false + +        chat = Chat.get(recipient.id, author.ap_id) + +        [cm_ref] = MessageReference.for_chat_query(chat) |> Repo.all() + +        assert cm_ref.object.data["content"] == "hey" +        assert cm_ref.unread == true +      end +    end + +    test "it creates a Chat for the local users and bumps the unread count" do +      author = insert(:user, local: false) +      recipient = insert(:user, local: true) + +      {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey") + +      {:ok, create_activity_data, _meta} = +        Builder.create(author, chat_message_data["id"], [recipient.ap_id]) + +      {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false) + +      {:ok, _create_activity, _meta} = +        SideEffects.handle(create_activity, local: false, object_data: chat_message_data) + +      # An object is created +      assert Object.get_by_ap_id(chat_message_data["id"]) + +      # The remote user won't get a chat +      chat = Chat.get(author.id, recipient.ap_id) +      refute chat + +      # The local user will get a chat +      chat = Chat.get(recipient.id, author.ap_id) +      assert chat + +      author = insert(:user, local: true) +      recipient = insert(:user, local: true) + +      {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey") + +      {:ok, create_activity_data, _meta} = +        Builder.create(author, chat_message_data["id"], [recipient.ap_id]) + +      {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false) + +      {:ok, _create_activity, _meta} = +        SideEffects.handle(create_activity, local: false, object_data: chat_message_data) + +      # Both users are local and get the chat +      chat = Chat.get(author.id, recipient.ap_id) +      assert chat + +      chat = Chat.get(recipient.id, author.ap_id) +      assert chat +    end +  end +    describe "announce objects" do      setup do        poster = insert(:user) diff --git a/test/web/activity_pub/transmogrifier/block_handling_test.exs b/test/web/activity_pub/transmogrifier/block_handling_test.exs new file mode 100644 index 000000000..71f1a0ed5 --- /dev/null +++ b/test/web/activity_pub/transmogrifier/block_handling_test.exs @@ -0,0 +1,63 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.Transmogrifier.BlockHandlingTest do +  use Pleroma.DataCase + +  alias Pleroma.Activity +  alias Pleroma.User +  alias Pleroma.Web.ActivityPub.Transmogrifier + +  import Pleroma.Factory + +  test "it works for incoming blocks" do +    user = insert(:user) + +    data = +      File.read!("test/fixtures/mastodon-block-activity.json") +      |> Poison.decode!() +      |> Map.put("object", user.ap_id) + +    blocker = insert(:user, ap_id: data["actor"]) + +    {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + +    assert data["type"] == "Block" +    assert data["object"] == user.ap_id +    assert data["actor"] == "http://mastodon.example.org/users/admin" + +    assert User.blocks?(blocker, user) +  end + +  test "incoming blocks successfully tear down any follow relationship" do +    blocker = insert(:user) +    blocked = insert(:user) + +    data = +      File.read!("test/fixtures/mastodon-block-activity.json") +      |> Poison.decode!() +      |> Map.put("object", blocked.ap_id) +      |> Map.put("actor", blocker.ap_id) + +    {:ok, blocker} = User.follow(blocker, blocked) +    {:ok, blocked} = User.follow(blocked, blocker) + +    assert User.following?(blocker, blocked) +    assert User.following?(blocked, blocker) + +    {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + +    assert data["type"] == "Block" +    assert data["object"] == blocked.ap_id +    assert data["actor"] == blocker.ap_id + +    blocker = User.get_cached_by_ap_id(data["actor"]) +    blocked = User.get_cached_by_ap_id(data["object"]) + +    assert User.blocks?(blocker, blocked) + +    refute User.following?(blocker, blocked) +    refute User.following?(blocked, blocker) +  end +end diff --git a/test/web/activity_pub/transmogrifier/chat_message_test.exs b/test/web/activity_pub/transmogrifier/chat_message_test.exs new file mode 100644 index 000000000..d6736dc3e --- /dev/null +++ b/test/web/activity_pub/transmogrifier/chat_message_test.exs @@ -0,0 +1,153 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.Transmogrifier.ChatMessageTest do +  use Pleroma.DataCase + +  import Pleroma.Factory + +  alias Pleroma.Activity +  alias Pleroma.Chat +  alias Pleroma.Object +  alias Pleroma.Web.ActivityPub.Transmogrifier + +  describe "handle_incoming" do +    test "handles chonks with attachment" do +      data = %{ +        "@context" => "https://www.w3.org/ns/activitystreams", +        "actor" => "https://honk.tedunangst.com/u/tedu", +        "id" => "https://honk.tedunangst.com/u/tedu/honk/x6gt8X8PcyGkQcXxzg1T", +        "object" => %{ +          "attachment" => [ +            %{ +              "mediaType" => "image/jpeg", +              "name" => "298p3RG7j27tfsZ9RQ.jpg", +              "summary" => "298p3RG7j27tfsZ9RQ.jpg", +              "type" => "Document", +              "url" => "https://honk.tedunangst.com/d/298p3RG7j27tfsZ9RQ.jpg" +            } +          ], +          "attributedTo" => "https://honk.tedunangst.com/u/tedu", +          "content" => "", +          "id" => "https://honk.tedunangst.com/u/tedu/chonk/26L4wl5yCbn4dr4y1b", +          "published" => "2020-05-18T01:13:03Z", +          "to" => [ +            "https://dontbulling.me/users/lain" +          ], +          "type" => "ChatMessage" +        }, +        "published" => "2020-05-18T01:13:03Z", +        "to" => [ +          "https://dontbulling.me/users/lain" +        ], +        "type" => "Create" +      } + +      _user = insert(:user, ap_id: data["actor"]) +      _user = insert(:user, ap_id: hd(data["to"])) + +      assert {:ok, _activity} = Transmogrifier.handle_incoming(data) +    end + +    test "it rejects messages that don't contain content" do +      data = +        File.read!("test/fixtures/create-chat-message.json") +        |> Poison.decode!() + +      object = +        data["object"] +        |> Map.delete("content") + +      data = +        data +        |> Map.put("object", object) + +      _author = +        insert(:user, ap_id: data["actor"], local: false, last_refreshed_at: DateTime.utc_now()) + +      _recipient = +        insert(:user, +          ap_id: List.first(data["to"]), +          local: true, +          last_refreshed_at: DateTime.utc_now() +        ) + +      {:error, _} = Transmogrifier.handle_incoming(data) +    end + +    test "it rejects messages that don't concern local users" do +      data = +        File.read!("test/fixtures/create-chat-message.json") +        |> Poison.decode!() + +      _author = +        insert(:user, ap_id: data["actor"], local: false, last_refreshed_at: DateTime.utc_now()) + +      _recipient = +        insert(:user, +          ap_id: List.first(data["to"]), +          local: false, +          last_refreshed_at: DateTime.utc_now() +        ) + +      {:error, _} = Transmogrifier.handle_incoming(data) +    end + +    test "it rejects messages where the `to` field of activity and object don't match" do +      data = +        File.read!("test/fixtures/create-chat-message.json") +        |> Poison.decode!() + +      author = insert(:user, ap_id: data["actor"]) +      _recipient = insert(:user, ap_id: List.first(data["to"])) + +      data = +        data +        |> Map.put("to", author.ap_id) + +      assert match?({:error, _}, Transmogrifier.handle_incoming(data)) +      refute Object.get_by_ap_id(data["object"]["id"]) +    end + +    test "it fetches the actor if they aren't in our system" do +      Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end) + +      data = +        File.read!("test/fixtures/create-chat-message.json") +        |> Poison.decode!() +        |> Map.put("actor", "http://mastodon.example.org/users/admin") +        |> put_in(["object", "actor"], "http://mastodon.example.org/users/admin") + +      _recipient = insert(:user, ap_id: List.first(data["to"]), local: true) + +      {:ok, %Activity{} = _activity} = Transmogrifier.handle_incoming(data) +    end + +    test "it inserts it and creates a chat" do +      data = +        File.read!("test/fixtures/create-chat-message.json") +        |> Poison.decode!() + +      author = +        insert(:user, ap_id: data["actor"], local: false, last_refreshed_at: DateTime.utc_now()) + +      recipient = insert(:user, ap_id: List.first(data["to"]), local: true) + +      {:ok, %Activity{} = activity} = Transmogrifier.handle_incoming(data) +      assert activity.local == false + +      assert activity.actor == author.ap_id +      assert activity.recipients == [recipient.ap_id, author.ap_id] + +      %Object{} = object = Object.get_by_ap_id(activity.data["object"]) + +      assert object +      assert object.data["content"] == "You expected a cute girl? Too bad. alert('XSS')" +      assert match?(%{"firefox" => _}, object.data["emoji"]) + +      refute Chat.get(author.id, recipient.ap_id) +      assert Chat.get(recipient.id, author.ap_id) +    end +  end +end diff --git a/test/web/activity_pub/transmogrifier/follow_handling_test.exs b/test/web/activity_pub/transmogrifier/follow_handling_test.exs index 967389fae..06c39eed6 100644 --- a/test/web/activity_pub/transmogrifier/follow_handling_test.exs +++ b/test/web/activity_pub/transmogrifier/follow_handling_test.exs @@ -5,6 +5,7 @@  defmodule Pleroma.Web.ActivityPub.Transmogrifier.FollowHandlingTest do    use Pleroma.DataCase    alias Pleroma.Activity +  alias Pleroma.Notification    alias Pleroma.Repo    alias Pleroma.User    alias Pleroma.Web.ActivityPub.Transmogrifier @@ -12,6 +13,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.FollowHandlingTest do    import Pleroma.Factory    import Ecto.Query +  import Mock    setup_all do      Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) @@ -57,9 +59,12 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.FollowHandlingTest do        activity = Repo.get(Activity, activity.id)        assert activity.data["state"] == "accept"        assert User.following?(User.get_cached_by_ap_id(data["actor"]), user) + +      [notification] = Notification.for_user(user) +      assert notification.type == "follow"      end -    test "with locked accounts, it does not create a follow or an accept" do +    test "with locked accounts, it does create a Follow, but not an Accept" do        user = insert(:user, locked: true)        data = @@ -81,6 +86,9 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.FollowHandlingTest do          |> Repo.all()        assert Enum.empty?(accepts) + +      [notification] = Notification.for_user(user) +      assert notification.type == "follow_request"      end      test "it works for follow requests when you are already followed, creating a new accept activity" do @@ -144,6 +152,23 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.FollowHandlingTest do        assert activity.data["state"] == "reject"      end +    test "it rejects incoming follow requests if the following errors for some reason" do +      user = insert(:user) + +      data = +        File.read!("test/fixtures/mastodon-follow-activity.json") +        |> Poison.decode!() +        |> Map.put("object", user.ap_id) + +      with_mock Pleroma.User, [:passthrough], follow: fn _, _ -> {:error, :testing} end do +        {:ok, %Activity{data: %{"id" => id}}} = Transmogrifier.handle_incoming(data) + +        %Activity{} = activity = Activity.get_by_ap_id(id) + +        assert activity.data["state"] == "reject" +      end +    end +      test "it works for incoming follow requests from hubzilla" do        user = insert(:user) diff --git a/test/web/activity_pub/transmogrifier/user_update_handling_test.exs b/test/web/activity_pub/transmogrifier/user_update_handling_test.exs new file mode 100644 index 000000000..64636656c --- /dev/null +++ b/test/web/activity_pub/transmogrifier/user_update_handling_test.exs @@ -0,0 +1,159 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.Transmogrifier.UserUpdateHandlingTest do +  use Pleroma.DataCase + +  alias Pleroma.Activity +  alias Pleroma.User +  alias Pleroma.Web.ActivityPub.Transmogrifier + +  import Pleroma.Factory + +  test "it works for incoming update activities" do +    user = insert(:user, local: false) + +    update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!() + +    object = +      update_data["object"] +      |> Map.put("actor", user.ap_id) +      |> Map.put("id", user.ap_id) + +    update_data = +      update_data +      |> Map.put("actor", user.ap_id) +      |> Map.put("object", object) + +    {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(update_data) + +    assert data["id"] == update_data["id"] + +    user = User.get_cached_by_ap_id(data["actor"]) +    assert user.name == "gargle" + +    assert user.avatar["url"] == [ +             %{ +               "href" => +                 "https://cd.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg" +             } +           ] + +    assert user.banner["url"] == [ +             %{ +               "href" => +                 "https://cd.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png" +             } +           ] + +    assert user.bio == "<p>Some bio</p>" +  end + +  test "it works with alsoKnownAs" do +    %{ap_id: actor} = insert(:user, local: false) + +    assert User.get_cached_by_ap_id(actor).also_known_as == [] + +    {:ok, _activity} = +      "test/fixtures/mastodon-update.json" +      |> File.read!() +      |> Poison.decode!() +      |> Map.put("actor", actor) +      |> Map.update!("object", fn object -> +        object +        |> Map.put("actor", actor) +        |> Map.put("id", actor) +        |> Map.put("alsoKnownAs", [ +          "http://mastodon.example.org/users/foo", +          "http://example.org/users/bar" +        ]) +      end) +      |> Transmogrifier.handle_incoming() + +    assert User.get_cached_by_ap_id(actor).also_known_as == [ +             "http://mastodon.example.org/users/foo", +             "http://example.org/users/bar" +           ] +  end + +  test "it works with custom profile fields" do +    user = insert(:user, local: false) + +    assert user.fields == [] + +    update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!() + +    object = +      update_data["object"] +      |> Map.put("actor", user.ap_id) +      |> Map.put("id", user.ap_id) + +    update_data = +      update_data +      |> Map.put("actor", user.ap_id) +      |> Map.put("object", object) + +    {:ok, _update_activity} = Transmogrifier.handle_incoming(update_data) + +    user = User.get_cached_by_ap_id(user.ap_id) + +    assert user.fields == [ +             %{"name" => "foo", "value" => "updated"}, +             %{"name" => "foo1", "value" => "updated"} +           ] + +    Pleroma.Config.put([:instance, :max_remote_account_fields], 2) + +    update_data = +      update_data +      |> put_in(["object", "attachment"], [ +        %{"name" => "foo", "type" => "PropertyValue", "value" => "bar"}, +        %{"name" => "foo11", "type" => "PropertyValue", "value" => "bar11"}, +        %{"name" => "foo22", "type" => "PropertyValue", "value" => "bar22"} +      ]) +      |> Map.put("id", update_data["id"] <> ".") + +    {:ok, _} = Transmogrifier.handle_incoming(update_data) + +    user = User.get_cached_by_ap_id(user.ap_id) + +    assert user.fields == [ +             %{"name" => "foo", "value" => "updated"}, +             %{"name" => "foo1", "value" => "updated"} +           ] + +    update_data = +      update_data +      |> put_in(["object", "attachment"], []) +      |> Map.put("id", update_data["id"] <> ".") + +    {:ok, _} = Transmogrifier.handle_incoming(update_data) + +    user = User.get_cached_by_ap_id(user.ap_id) + +    assert user.fields == [] +  end + +  test "it works for incoming update activities which lock the account" do +    user = insert(:user, local: false) + +    update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!() + +    object = +      update_data["object"] +      |> Map.put("actor", user.ap_id) +      |> Map.put("id", user.ap_id) +      |> Map.put("manuallyApprovesFollowers", true) + +    update_data = +      update_data +      |> Map.put("actor", user.ap_id) +      |> Map.put("object", object) + +    {:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(update_data) + +    user = User.get_cached_by_ap_id(user.ap_id) +    assert user.locked == true +  end +end diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs index 94d8552e8..6a53fd3f0 100644 --- a/test/web/activity_pub/transmogrifier_test.exs +++ b/test/web/activity_pub/transmogrifier_test.exs @@ -401,162 +401,6 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do        refute Map.has_key?(object_data, "reaction_count")      end -    test "it works for incoming update activities" do -      data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!() - -      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) -      update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!() - -      object = -        update_data["object"] -        |> Map.put("actor", data["actor"]) -        |> Map.put("id", data["actor"]) - -      update_data = -        update_data -        |> Map.put("actor", data["actor"]) -        |> Map.put("object", object) - -      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(update_data) - -      assert data["id"] == update_data["id"] - -      user = User.get_cached_by_ap_id(data["actor"]) -      assert user.name == "gargle" - -      assert user.avatar["url"] == [ -               %{ -                 "href" => -                   "https://cd.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg" -               } -             ] - -      assert user.banner["url"] == [ -               %{ -                 "href" => -                   "https://cd.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png" -               } -             ] - -      assert user.bio == "<p>Some bio</p>" -    end - -    test "it works with alsoKnownAs" do -      {:ok, %Activity{data: %{"actor" => actor}}} = -        "test/fixtures/mastodon-post-activity.json" -        |> File.read!() -        |> Poison.decode!() -        |> Transmogrifier.handle_incoming() - -      assert User.get_cached_by_ap_id(actor).also_known_as == ["http://example.org/users/foo"] - -      {:ok, _activity} = -        "test/fixtures/mastodon-update.json" -        |> File.read!() -        |> Poison.decode!() -        |> Map.put("actor", actor) -        |> Map.update!("object", fn object -> -          object -          |> Map.put("actor", actor) -          |> Map.put("id", actor) -          |> Map.put("alsoKnownAs", [ -            "http://mastodon.example.org/users/foo", -            "http://example.org/users/bar" -          ]) -        end) -        |> Transmogrifier.handle_incoming() - -      assert User.get_cached_by_ap_id(actor).also_known_as == [ -               "http://mastodon.example.org/users/foo", -               "http://example.org/users/bar" -             ] -    end - -    test "it works with custom profile fields" do -      {:ok, activity} = -        "test/fixtures/mastodon-post-activity.json" -        |> File.read!() -        |> Poison.decode!() -        |> Transmogrifier.handle_incoming() - -      user = User.get_cached_by_ap_id(activity.actor) - -      assert user.fields == [ -               %{"name" => "foo", "value" => "bar"}, -               %{"name" => "foo1", "value" => "bar1"} -             ] - -      update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!() - -      object = -        update_data["object"] -        |> Map.put("actor", user.ap_id) -        |> Map.put("id", user.ap_id) - -      update_data = -        update_data -        |> Map.put("actor", user.ap_id) -        |> Map.put("object", object) - -      {:ok, _update_activity} = Transmogrifier.handle_incoming(update_data) - -      user = User.get_cached_by_ap_id(user.ap_id) - -      assert user.fields == [ -               %{"name" => "foo", "value" => "updated"}, -               %{"name" => "foo1", "value" => "updated"} -             ] - -      Pleroma.Config.put([:instance, :max_remote_account_fields], 2) - -      update_data = -        put_in(update_data, ["object", "attachment"], [ -          %{"name" => "foo", "type" => "PropertyValue", "value" => "bar"}, -          %{"name" => "foo11", "type" => "PropertyValue", "value" => "bar11"}, -          %{"name" => "foo22", "type" => "PropertyValue", "value" => "bar22"} -        ]) - -      {:ok, _} = Transmogrifier.handle_incoming(update_data) - -      user = User.get_cached_by_ap_id(user.ap_id) - -      assert user.fields == [ -               %{"name" => "foo", "value" => "updated"}, -               %{"name" => "foo1", "value" => "updated"} -             ] - -      update_data = put_in(update_data, ["object", "attachment"], []) - -      {:ok, _} = Transmogrifier.handle_incoming(update_data) - -      user = User.get_cached_by_ap_id(user.ap_id) - -      assert user.fields == [] -    end - -    test "it works for incoming update activities which lock the account" do -      data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!() - -      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) -      update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!() - -      object = -        update_data["object"] -        |> Map.put("actor", data["actor"]) -        |> Map.put("id", data["actor"]) -        |> Map.put("manuallyApprovesFollowers", true) - -      update_data = -        update_data -        |> Map.put("actor", data["actor"]) -        |> Map.put("object", object) - -      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(update_data) - -      user = User.get_cached_by_ap_id(data["actor"]) -      assert user.locked == true -    end -      test "it works for incomming unfollows with an existing follow" do        user = insert(:user) @@ -601,56 +445,6 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do        assert [^pending_follower] = User.get_follow_requests(user)      end -    test "it works for incoming blocks" do -      user = insert(:user) - -      data = -        File.read!("test/fixtures/mastodon-block-activity.json") -        |> Poison.decode!() -        |> Map.put("object", user.ap_id) - -      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - -      assert data["type"] == "Block" -      assert data["object"] == user.ap_id -      assert data["actor"] == "http://mastodon.example.org/users/admin" - -      blocker = User.get_cached_by_ap_id(data["actor"]) - -      assert User.blocks?(blocker, user) -    end - -    test "incoming blocks successfully tear down any follow relationship" do -      blocker = insert(:user) -      blocked = insert(:user) - -      data = -        File.read!("test/fixtures/mastodon-block-activity.json") -        |> Poison.decode!() -        |> Map.put("object", blocked.ap_id) -        |> Map.put("actor", blocker.ap_id) - -      {:ok, blocker} = User.follow(blocker, blocked) -      {:ok, blocked} = User.follow(blocked, blocker) - -      assert User.following?(blocker, blocked) -      assert User.following?(blocked, blocker) - -      {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - -      assert data["type"] == "Block" -      assert data["object"] == blocked.ap_id -      assert data["actor"] == blocker.ap_id - -      blocker = User.get_cached_by_ap_id(data["actor"]) -      blocked = User.get_cached_by_ap_id(data["object"]) - -      assert User.blocks?(blocker, blocked) - -      refute User.following?(blocker, blocked) -      refute User.following?(blocked, blocker) -    end -      test "it works for incoming accepts which were pre-accepted" do        follower = insert(:user)        followed = insert(:user) @@ -1571,9 +1365,6 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do        assert modified_object["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873" -      assert modified_object["conversation"] == -               "tag:shitposter.club,2017-05-05:objectType=thread:nonce=3c16e9c2681f6d26" -        assert modified_object["context"] ==                 "tag:shitposter.club,2017-05-05:objectType=thread:nonce=3c16e9c2681f6d26"      end diff --git a/test/web/activity_pub/utils_test.exs b/test/web/activity_pub/utils_test.exs index 15f03f193..2f9ecb5a3 100644 --- a/test/web/activity_pub/utils_test.exs +++ b/test/web/activity_pub/utils_test.exs @@ -27,16 +27,6 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do      end    end -  describe "fetch the latest Block" do -    test "fetches the latest Block activity" do -      blocker = insert(:user) -      blocked = insert(:user) -      {:ok, activity} = ActivityPub.block(blocker, blocked) - -      assert activity == Utils.fetch_latest_block(blocker, blocked) -    end -  end -    describe "determine_explicit_mentions()" do      test "works with an object that has mentions" do        object = %{ @@ -344,9 +334,9 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do        user1 = insert(:user)        user2 = insert(:user) -      assert {:ok, %Activity{} = _} = ActivityPub.block(user1, user2) -      assert {:ok, %Activity{} = _} = ActivityPub.block(user1, user2) -      assert {:ok, %Activity{} = activity} = ActivityPub.block(user1, user2) +      assert {:ok, %Activity{} = _} = CommonAPI.block(user1, user2) +      assert {:ok, %Activity{} = _} = CommonAPI.block(user1, user2) +      assert {:ok, %Activity{} = activity} = CommonAPI.block(user1, user2)        assert Utils.fetch_latest_block(user1, user2) == activity      end diff --git a/test/web/activity_pub/views/user_view_test.exs b/test/web/activity_pub/views/user_view_test.exs index 20b0f223c..bec15a996 100644 --- a/test/web/activity_pub/views/user_view_test.exs +++ b/test/web/activity_pub/views/user_view_test.exs @@ -158,35 +158,4 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do        assert %{"totalItems" => 1} = UserView.render("following.json", %{user: user})      end    end - -  test "activity collection page aginates correctly" do -    user = insert(:user) - -    posts = -      for i <- 0..25 do -        {:ok, activity} = CommonAPI.post(user, %{status: "post #{i}"}) -        activity -      end - -    # outbox sorts chronologically, newest first, with ten per page -    posts = Enum.reverse(posts) - -    %{"next" => next_url} = -      UserView.render("activity_collection_page.json", %{ -        iri: "#{user.ap_id}/outbox", -        activities: Enum.take(posts, 10) -      }) - -    next_id = Enum.at(posts, 9).id -    assert next_url =~ next_id - -    %{"next" => next_url} = -      UserView.render("activity_collection_page.json", %{ -        iri: "#{user.ap_id}/outbox", -        activities: Enum.take(Enum.drop(posts, 10), 10) -      }) - -    next_id = Enum.at(posts, 19).id -    assert next_url =~ next_id -  end  end diff --git a/test/web/admin_api/controllers/admin_api_controller_test.exs b/test/web/admin_api/controllers/admin_api_controller_test.exs index ead840186..48fb108ec 100644 --- a/test/web/admin_api/controllers/admin_api_controller_test.exs +++ b/test/web/admin_api/controllers/admin_api_controller_test.exs @@ -12,15 +12,12 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do    alias Pleroma.Activity    alias Pleroma.Config -  alias Pleroma.ConfigDB    alias Pleroma.HTML    alias Pleroma.MFA    alias Pleroma.ModerationLog    alias Pleroma.Repo -  alias Pleroma.ReportNote    alias Pleroma.Tests.ObanHelpers    alias Pleroma.User -  alias Pleroma.UserInviteToken    alias Pleroma.Web    alias Pleroma.Web.ActivityPub.Relay    alias Pleroma.Web.CommonAPI @@ -340,7 +337,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do          "tags" => [],          "avatar" => User.avatar_url(user) |> MediaProxy.url(),          "display_name" => HTML.strip_tags(user.name || user.nickname), -        "confirmation_pending" => false +        "confirmation_pending" => false, +        "url" => user.ap_id        }        assert expected == json_response(conn, 200) @@ -588,122 +586,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do      end    end -  describe "POST /api/pleroma/admin/email_invite, with valid config" do -    setup do: clear_config([:instance, :registrations_open], false) -    setup do: clear_config([:instance, :invites_enabled], true) - -    test "sends invitation and returns 204", %{admin: admin, conn: conn} do -      recipient_email = "foo@bar.com" -      recipient_name = "J. D." - -      conn = -        post( -          conn, -          "/api/pleroma/admin/users/email_invite?email=#{recipient_email}&name=#{recipient_name}" -        ) - -      assert json_response(conn, :no_content) - -      token_record = List.last(Repo.all(Pleroma.UserInviteToken)) -      assert token_record -      refute token_record.used - -      notify_email = Config.get([:instance, :notify_email]) -      instance_name = Config.get([:instance, :name]) - -      email = -        Pleroma.Emails.UserEmail.user_invitation_email( -          admin, -          token_record, -          recipient_email, -          recipient_name -        ) - -      Swoosh.TestAssertions.assert_email_sent( -        from: {instance_name, notify_email}, -        to: {recipient_name, recipient_email}, -        html_body: email.html_body -      ) -    end - -    test "it returns 403 if requested by a non-admin" do -      non_admin_user = insert(:user) -      token = insert(:oauth_token, user: non_admin_user) - -      conn = -        build_conn() -        |> assign(:user, non_admin_user) -        |> assign(:token, token) -        |> post("/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD") - -      assert json_response(conn, :forbidden) -    end - -    test "email with +", %{conn: conn, admin: admin} do -      recipient_email = "foo+bar@baz.com" - -      conn -      |> put_req_header("content-type", "application/json;charset=utf-8") -      |> post("/api/pleroma/admin/users/email_invite", %{email: recipient_email}) -      |> json_response(:no_content) - -      token_record = -        Pleroma.UserInviteToken -        |> Repo.all() -        |> List.last() - -      assert token_record -      refute token_record.used - -      notify_email = Config.get([:instance, :notify_email]) -      instance_name = Config.get([:instance, :name]) - -      email = -        Pleroma.Emails.UserEmail.user_invitation_email( -          admin, -          token_record, -          recipient_email -        ) - -      Swoosh.TestAssertions.assert_email_sent( -        from: {instance_name, notify_email}, -        to: recipient_email, -        html_body: email.html_body -      ) -    end -  end - -  describe "POST /api/pleroma/admin/users/email_invite, with invalid config" do -    setup do: clear_config([:instance, :registrations_open]) -    setup do: clear_config([:instance, :invites_enabled]) - -    test "it returns 500 if `invites_enabled` is not enabled", %{conn: conn} do -      Config.put([:instance, :registrations_open], false) -      Config.put([:instance, :invites_enabled], false) - -      conn = post(conn, "/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD") - -      assert json_response(conn, :bad_request) == -               %{ -                 "error" => -                   "To send invites you need to set the `invites_enabled` option to true." -               } -    end - -    test "it returns 500 if `registrations_open` is enabled", %{conn: conn} do -      Config.put([:instance, :registrations_open], true) -      Config.put([:instance, :invites_enabled], true) - -      conn = post(conn, "/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD") - -      assert json_response(conn, :bad_request) == -               %{ -                 "error" => -                   "To send invites you need to set the `registrations_open` option to false." -               } -    end -  end -    test "/api/pleroma/admin/users/:nickname/password_reset", %{conn: conn} do      user = insert(:user) @@ -733,7 +615,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do              "tags" => [],              "avatar" => User.avatar_url(admin) |> MediaProxy.url(),              "display_name" => HTML.strip_tags(admin.name || admin.nickname), -            "confirmation_pending" => false +            "confirmation_pending" => false, +            "url" => admin.ap_id            },            %{              "deactivated" => user.deactivated, @@ -744,7 +627,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do              "tags" => ["foo", "bar"],              "avatar" => User.avatar_url(user) |> MediaProxy.url(),              "display_name" => HTML.strip_tags(user.name || user.nickname), -            "confirmation_pending" => false +            "confirmation_pending" => false, +            "url" => user.ap_id            }          ]          |> Enum.sort_by(& &1["nickname"]) @@ -757,8 +641,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do      end      test "pagination works correctly with service users", %{conn: conn} do -      service1 = insert(:user, ap_id: Web.base_url() <> "/relay") -      service2 = insert(:user, ap_id: Web.base_url() <> "/internal/fetch") +      service1 = User.get_or_create_service_actor_by_ap_id(Web.base_url() <> "/meido", "meido") +        insert_list(25, :user)        assert %{"count" => 26, "page_size" => 10, "users" => users1} = @@ -767,8 +651,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do                 |> json_response(200)        assert Enum.count(users1) == 10 -      assert service1 not in [users1] -      assert service2 not in [users1] +      assert service1 not in users1        assert %{"count" => 26, "page_size" => 10, "users" => users2} =                 conn @@ -776,8 +659,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do                 |> json_response(200)        assert Enum.count(users2) == 10 -      assert service1 not in [users2] -      assert service2 not in [users2] +      assert service1 not in users2        assert %{"count" => 26, "page_size" => 10, "users" => users3} =                 conn @@ -785,8 +667,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do                 |> json_response(200)        assert Enum.count(users3) == 6 -      assert service1 not in [users3] -      assert service2 not in [users3] +      assert service1 not in users3      end      test "renders empty array for the second page", %{conn: conn} do @@ -819,7 +700,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do                     "tags" => [],                     "avatar" => User.avatar_url(user) |> MediaProxy.url(),                     "display_name" => HTML.strip_tags(user.name || user.nickname), -                   "confirmation_pending" => false +                   "confirmation_pending" => false, +                   "url" => user.ap_id                   }                 ]               } @@ -844,7 +726,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do                     "tags" => [],                     "avatar" => User.avatar_url(user) |> MediaProxy.url(),                     "display_name" => HTML.strip_tags(user.name || user.nickname), -                   "confirmation_pending" => false +                   "confirmation_pending" => false, +                   "url" => user.ap_id                   }                 ]               } @@ -869,7 +752,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do                     "tags" => [],                     "avatar" => User.avatar_url(user) |> MediaProxy.url(),                     "display_name" => HTML.strip_tags(user.name || user.nickname), -                   "confirmation_pending" => false +                   "confirmation_pending" => false, +                   "url" => user.ap_id                   }                 ]               } @@ -894,7 +778,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do                     "tags" => [],                     "avatar" => User.avatar_url(user) |> MediaProxy.url(),                     "display_name" => HTML.strip_tags(user.name || user.nickname), -                   "confirmation_pending" => false +                   "confirmation_pending" => false, +                   "url" => user.ap_id                   }                 ]               } @@ -919,7 +804,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do                     "tags" => [],                     "avatar" => User.avatar_url(user) |> MediaProxy.url(),                     "display_name" => HTML.strip_tags(user.name || user.nickname), -                   "confirmation_pending" => false +                   "confirmation_pending" => false, +                   "url" => user.ap_id                   }                 ]               } @@ -944,7 +830,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do                     "tags" => [],                     "avatar" => User.avatar_url(user) |> MediaProxy.url(),                     "display_name" => HTML.strip_tags(user.name || user.nickname), -                   "confirmation_pending" => false +                   "confirmation_pending" => false, +                   "url" => user.ap_id                   }                 ]               } @@ -964,7 +851,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do                     "tags" => [],                     "avatar" => User.avatar_url(user2) |> MediaProxy.url(),                     "display_name" => HTML.strip_tags(user2.name || user2.nickname), -                   "confirmation_pending" => false +                   "confirmation_pending" => false, +                   "url" => user2.ap_id                   }                 ]               } @@ -996,7 +884,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do                     "tags" => [],                     "avatar" => User.avatar_url(user) |> MediaProxy.url(),                     "display_name" => HTML.strip_tags(user.name || user.nickname), -                   "confirmation_pending" => false +                   "confirmation_pending" => false, +                   "url" => user.ap_id                   }                 ]               } @@ -1021,7 +910,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do              "tags" => [],              "avatar" => User.avatar_url(user) |> MediaProxy.url(),              "display_name" => HTML.strip_tags(user.name || user.nickname), -            "confirmation_pending" => false +            "confirmation_pending" => false, +            "url" => user.ap_id            },            %{              "deactivated" => admin.deactivated, @@ -1032,7 +922,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do              "tags" => [],              "avatar" => User.avatar_url(admin) |> MediaProxy.url(),              "display_name" => HTML.strip_tags(admin.name || admin.nickname), -            "confirmation_pending" => false +            "confirmation_pending" => false, +            "url" => admin.ap_id            },            %{              "deactivated" => false, @@ -1043,7 +934,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do              "tags" => [],              "avatar" => User.avatar_url(old_admin) |> MediaProxy.url(),              "display_name" => HTML.strip_tags(old_admin.name || old_admin.nickname), -            "confirmation_pending" => false +            "confirmation_pending" => false, +            "url" => old_admin.ap_id            }          ]          |> Enum.sort_by(& &1["nickname"]) @@ -1073,7 +965,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do              "tags" => [],              "avatar" => User.avatar_url(admin) |> MediaProxy.url(),              "display_name" => HTML.strip_tags(admin.name || admin.nickname), -            "confirmation_pending" => false +            "confirmation_pending" => false, +            "url" => admin.ap_id            },            %{              "deactivated" => false, @@ -1084,7 +977,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do              "tags" => [],              "avatar" => User.avatar_url(second_admin) |> MediaProxy.url(),              "display_name" => HTML.strip_tags(second_admin.name || second_admin.nickname), -            "confirmation_pending" => false +            "confirmation_pending" => false, +            "url" => second_admin.ap_id            }          ]          |> Enum.sort_by(& &1["nickname"]) @@ -1116,7 +1010,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do                     "tags" => [],                     "avatar" => User.avatar_url(moderator) |> MediaProxy.url(),                     "display_name" => HTML.strip_tags(moderator.name || moderator.nickname), -                   "confirmation_pending" => false +                   "confirmation_pending" => false, +                   "url" => moderator.ap_id                   }                 ]               } @@ -1141,7 +1036,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do              "tags" => ["first"],              "avatar" => User.avatar_url(user1) |> MediaProxy.url(),              "display_name" => HTML.strip_tags(user1.name || user1.nickname), -            "confirmation_pending" => false +            "confirmation_pending" => false, +            "url" => user1.ap_id            },            %{              "deactivated" => false, @@ -1152,7 +1048,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do              "tags" => ["second"],              "avatar" => User.avatar_url(user2) |> MediaProxy.url(),              "display_name" => HTML.strip_tags(user2.name || user2.nickname), -            "confirmation_pending" => false +            "confirmation_pending" => false, +            "url" => user2.ap_id            }          ]          |> Enum.sort_by(& &1["nickname"]) @@ -1191,7 +1088,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do                     "tags" => [],                     "avatar" => User.avatar_url(user) |> MediaProxy.url(),                     "display_name" => HTML.strip_tags(user.name || user.nickname), -                   "confirmation_pending" => false +                   "confirmation_pending" => false, +                   "url" => user.ap_id                   }                 ]               } @@ -1215,7 +1113,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do                     "tags" => [],                     "avatar" => User.avatar_url(admin) |> MediaProxy.url(),                     "display_name" => HTML.strip_tags(admin.name || admin.nickname), -                   "confirmation_pending" => false +                   "confirmation_pending" => false, +                   "url" => admin.ap_id                   }                 ]               } @@ -1277,7 +1176,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do                 "tags" => [],                 "avatar" => User.avatar_url(user) |> MediaProxy.url(),                 "display_name" => HTML.strip_tags(user.name || user.nickname), -               "confirmation_pending" => false +               "confirmation_pending" => false, +               "url" => user.ap_id               }      log_entry = Repo.one(ModerationLog) @@ -1318,1561 +1218,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do      end    end -  describe "POST /api/pleroma/admin/users/invite_token" do -    test "without options", %{conn: conn} do -      conn = post(conn, "/api/pleroma/admin/users/invite_token") - -      invite_json = json_response(conn, 200) -      invite = UserInviteToken.find_by_token!(invite_json["token"]) -      refute invite.used -      refute invite.expires_at -      refute invite.max_use -      assert invite.invite_type == "one_time" -    end - -    test "with expires_at", %{conn: conn} do -      conn = -        post(conn, "/api/pleroma/admin/users/invite_token", %{ -          "expires_at" => Date.to_string(Date.utc_today()) -        }) - -      invite_json = json_response(conn, 200) -      invite = UserInviteToken.find_by_token!(invite_json["token"]) - -      refute invite.used -      assert invite.expires_at == Date.utc_today() -      refute invite.max_use -      assert invite.invite_type == "date_limited" -    end - -    test "with max_use", %{conn: conn} do -      conn = post(conn, "/api/pleroma/admin/users/invite_token", %{"max_use" => 150}) - -      invite_json = json_response(conn, 200) -      invite = UserInviteToken.find_by_token!(invite_json["token"]) -      refute invite.used -      refute invite.expires_at -      assert invite.max_use == 150 -      assert invite.invite_type == "reusable" -    end - -    test "with max use and expires_at", %{conn: conn} do -      conn = -        post(conn, "/api/pleroma/admin/users/invite_token", %{ -          "max_use" => 150, -          "expires_at" => Date.to_string(Date.utc_today()) -        }) - -      invite_json = json_response(conn, 200) -      invite = UserInviteToken.find_by_token!(invite_json["token"]) -      refute invite.used -      assert invite.expires_at == Date.utc_today() -      assert invite.max_use == 150 -      assert invite.invite_type == "reusable_date_limited" -    end -  end - -  describe "GET /api/pleroma/admin/users/invites" do -    test "no invites", %{conn: conn} do -      conn = get(conn, "/api/pleroma/admin/users/invites") - -      assert json_response(conn, 200) == %{"invites" => []} -    end - -    test "with invite", %{conn: conn} do -      {:ok, invite} = UserInviteToken.create_invite() - -      conn = get(conn, "/api/pleroma/admin/users/invites") - -      assert json_response(conn, 200) == %{ -               "invites" => [ -                 %{ -                   "expires_at" => nil, -                   "id" => invite.id, -                   "invite_type" => "one_time", -                   "max_use" => nil, -                   "token" => invite.token, -                   "used" => false, -                   "uses" => 0 -                 } -               ] -             } -    end -  end - -  describe "POST /api/pleroma/admin/users/revoke_invite" do -    test "with token", %{conn: conn} do -      {:ok, invite} = UserInviteToken.create_invite() - -      conn = post(conn, "/api/pleroma/admin/users/revoke_invite", %{"token" => invite.token}) - -      assert json_response(conn, 200) == %{ -               "expires_at" => nil, -               "id" => invite.id, -               "invite_type" => "one_time", -               "max_use" => nil, -               "token" => invite.token, -               "used" => true, -               "uses" => 0 -             } -    end - -    test "with invalid token", %{conn: conn} do -      conn = post(conn, "/api/pleroma/admin/users/revoke_invite", %{"token" => "foo"}) - -      assert json_response(conn, :not_found) == %{"error" => "Not found"} -    end -  end - -  describe "GET /api/pleroma/admin/reports/:id" do -    test "returns report by its id", %{conn: conn} do -      [reporter, target_user] = insert_pair(:user) -      activity = insert(:note_activity, user: target_user) - -      {:ok, %{id: report_id}} = -        CommonAPI.report(reporter, %{ -          account_id: target_user.id, -          comment: "I feel offended", -          status_ids: [activity.id] -        }) - -      response = -        conn -        |> get("/api/pleroma/admin/reports/#{report_id}") -        |> json_response(:ok) - -      assert response["id"] == report_id -    end - -    test "returns 404 when report id is invalid", %{conn: conn} do -      conn = get(conn, "/api/pleroma/admin/reports/test") - -      assert json_response(conn, :not_found) == %{"error" => "Not found"} -    end -  end - -  describe "PATCH /api/pleroma/admin/reports" do -    setup do -      [reporter, target_user] = insert_pair(:user) -      activity = insert(:note_activity, user: target_user) - -      {:ok, %{id: report_id}} = -        CommonAPI.report(reporter, %{ -          account_id: target_user.id, -          comment: "I feel offended", -          status_ids: [activity.id] -        }) - -      {:ok, %{id: second_report_id}} = -        CommonAPI.report(reporter, %{ -          account_id: target_user.id, -          comment: "I feel very offended", -          status_ids: [activity.id] -        }) - -      %{ -        id: report_id, -        second_report_id: second_report_id -      } -    end - -    test "requires admin:write:reports scope", %{conn: conn, id: id, admin: admin} do -      read_token = insert(:oauth_token, user: admin, scopes: ["admin:read"]) -      write_token = insert(:oauth_token, user: admin, scopes: ["admin:write:reports"]) - -      response = -        conn -        |> assign(:token, read_token) -        |> patch("/api/pleroma/admin/reports", %{ -          "reports" => [%{"state" => "resolved", "id" => id}] -        }) -        |> json_response(403) - -      assert response == %{ -               "error" => "Insufficient permissions: admin:write:reports." -             } - -      conn -      |> assign(:token, write_token) -      |> patch("/api/pleroma/admin/reports", %{ -        "reports" => [%{"state" => "resolved", "id" => id}] -      }) -      |> json_response(:no_content) -    end - -    test "mark report as resolved", %{conn: conn, id: id, admin: admin} do -      conn -      |> patch("/api/pleroma/admin/reports", %{ -        "reports" => [ -          %{"state" => "resolved", "id" => id} -        ] -      }) -      |> json_response(:no_content) - -      activity = Activity.get_by_id(id) -      assert activity.data["state"] == "resolved" - -      log_entry = Repo.one(ModerationLog) - -      assert ModerationLog.get_log_entry_message(log_entry) == -               "@#{admin.nickname} updated report ##{id} with 'resolved' state" -    end - -    test "closes report", %{conn: conn, id: id, admin: admin} do -      conn -      |> patch("/api/pleroma/admin/reports", %{ -        "reports" => [ -          %{"state" => "closed", "id" => id} -        ] -      }) -      |> json_response(:no_content) - -      activity = Activity.get_by_id(id) -      assert activity.data["state"] == "closed" - -      log_entry = Repo.one(ModerationLog) - -      assert ModerationLog.get_log_entry_message(log_entry) == -               "@#{admin.nickname} updated report ##{id} with 'closed' state" -    end - -    test "returns 400 when state is unknown", %{conn: conn, id: id} do -      conn = -        conn -        |> patch("/api/pleroma/admin/reports", %{ -          "reports" => [ -            %{"state" => "test", "id" => id} -          ] -        }) - -      assert hd(json_response(conn, :bad_request))["error"] == "Unsupported state" -    end - -    test "returns 404 when report is not exist", %{conn: conn} do -      conn = -        conn -        |> patch("/api/pleroma/admin/reports", %{ -          "reports" => [ -            %{"state" => "closed", "id" => "test"} -          ] -        }) - -      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 - -  describe "GET /api/pleroma/admin/reports" do -    test "returns empty response when no reports created", %{conn: conn} do -      response = -        conn -        |> get("/api/pleroma/admin/reports") -        |> json_response(:ok) - -      assert Enum.empty?(response["reports"]) -      assert response["total"] == 0 -    end - -    test "returns reports", %{conn: conn} do -      [reporter, target_user] = insert_pair(:user) -      activity = insert(:note_activity, user: target_user) - -      {:ok, %{id: report_id}} = -        CommonAPI.report(reporter, %{ -          account_id: target_user.id, -          comment: "I feel offended", -          status_ids: [activity.id] -        }) - -      response = -        conn -        |> get("/api/pleroma/admin/reports") -        |> json_response(:ok) - -      [report] = response["reports"] - -      assert length(response["reports"]) == 1 -      assert report["id"] == report_id - -      assert response["total"] == 1 -    end - -    test "returns reports with specified state", %{conn: conn} do -      [reporter, target_user] = insert_pair(:user) -      activity = insert(:note_activity, user: target_user) - -      {:ok, %{id: first_report_id}} = -        CommonAPI.report(reporter, %{ -          account_id: target_user.id, -          comment: "I feel offended", -          status_ids: [activity.id] -        }) - -      {:ok, %{id: second_report_id}} = -        CommonAPI.report(reporter, %{ -          account_id: target_user.id, -          comment: "I don't like this user" -        }) - -      CommonAPI.update_report_state(second_report_id, "closed") - -      response = -        conn -        |> get("/api/pleroma/admin/reports", %{ -          "state" => "open" -        }) -        |> json_response(:ok) - -      [open_report] = response["reports"] - -      assert length(response["reports"]) == 1 -      assert open_report["id"] == first_report_id - -      assert response["total"] == 1 - -      response = -        conn -        |> get("/api/pleroma/admin/reports", %{ -          "state" => "closed" -        }) -        |> json_response(:ok) - -      [closed_report] = response["reports"] - -      assert length(response["reports"]) == 1 -      assert closed_report["id"] == second_report_id - -      assert response["total"] == 1 - -      response = -        conn -        |> get("/api/pleroma/admin/reports", %{ -          "state" => "resolved" -        }) -        |> json_response(:ok) - -      assert Enum.empty?(response["reports"]) -      assert response["total"] == 0 -    end - -    test "returns 403 when requested by a non-admin" do -      user = insert(:user) -      token = insert(:oauth_token, user: user) - -      conn = -        build_conn() -        |> assign(:user, user) -        |> assign(:token, token) -        |> get("/api/pleroma/admin/reports") - -      assert json_response(conn, :forbidden) == -               %{"error" => "User is not an admin or OAuth admin scope is not granted."} -    end - -    test "returns 403 when requested by anonymous" do -      conn = get(build_conn(), "/api/pleroma/admin/reports") - -      assert json_response(conn, :forbidden) == %{"error" => "Invalid credentials."} -    end -  end - -  describe "GET /api/pleroma/admin/config" do -    setup do: clear_config(:configurable_from_database, true) - -    test "when configuration from database is off", %{conn: conn} do -      Config.put(:configurable_from_database, false) -      conn = get(conn, "/api/pleroma/admin/config") - -      assert json_response(conn, 400) == -               %{ -                 "error" => "To use this endpoint you need to enable configuration from database." -               } -    end - -    test "with settings only in db", %{conn: conn} do -      config1 = insert(:config) -      config2 = insert(:config) - -      conn = get(conn, "/api/pleroma/admin/config", %{"only_db" => true}) - -      %{ -        "configs" => [ -          %{ -            "group" => ":pleroma", -            "key" => key1, -            "value" => _ -          }, -          %{ -            "group" => ":pleroma", -            "key" => key2, -            "value" => _ -          } -        ] -      } = json_response(conn, 200) - -      assert key1 == config1.key -      assert key2 == config2.key -    end - -    test "db is added to settings that are in db", %{conn: conn} do -      _config = insert(:config, key: ":instance", value: ConfigDB.to_binary(name: "Some name")) - -      %{"configs" => configs} = -        conn -        |> get("/api/pleroma/admin/config") -        |> json_response(200) - -      [instance_config] = -        Enum.filter(configs, fn %{"group" => group, "key" => key} -> -          group == ":pleroma" and key == ":instance" -        end) - -      assert instance_config["db"] == [":name"] -    end - -    test "merged default setting with db settings", %{conn: conn} do -      config1 = insert(:config) -      config2 = insert(:config) - -      config3 = -        insert(:config, -          value: ConfigDB.to_binary(k1: :v1, k2: :v2) -        ) - -      %{"configs" => configs} = -        conn -        |> get("/api/pleroma/admin/config") -        |> json_response(200) - -      assert length(configs) > 3 - -      received_configs = -        Enum.filter(configs, fn %{"group" => group, "key" => key} -> -          group == ":pleroma" and key in [config1.key, config2.key, config3.key] -        end) - -      assert length(received_configs) == 3 - -      db_keys = -        config3.value -        |> ConfigDB.from_binary() -        |> Keyword.keys() -        |> ConfigDB.convert() - -      Enum.each(received_configs, fn %{"value" => value, "db" => db} -> -        assert db in [[config1.key], [config2.key], db_keys] - -        assert value in [ -                 ConfigDB.from_binary_with_convert(config1.value), -                 ConfigDB.from_binary_with_convert(config2.value), -                 ConfigDB.from_binary_with_convert(config3.value) -               ] -      end) -    end - -    test "subkeys with full update right merge", %{conn: conn} do -      config1 = -        insert(:config, -          key: ":emoji", -          value: ConfigDB.to_binary(groups: [a: 1, b: 2], key: [a: 1]) -        ) - -      config2 = -        insert(:config, -          key: ":assets", -          value: ConfigDB.to_binary(mascots: [a: 1, b: 2], key: [a: 1]) -        ) - -      %{"configs" => configs} = -        conn -        |> get("/api/pleroma/admin/config") -        |> json_response(200) - -      vals = -        Enum.filter(configs, fn %{"group" => group, "key" => key} -> -          group == ":pleroma" and key in [config1.key, config2.key] -        end) - -      emoji = Enum.find(vals, fn %{"key" => key} -> key == ":emoji" end) -      assets = Enum.find(vals, fn %{"key" => key} -> key == ":assets" end) - -      emoji_val = ConfigDB.transform_with_out_binary(emoji["value"]) -      assets_val = ConfigDB.transform_with_out_binary(assets["value"]) - -      assert emoji_val[:groups] == [a: 1, b: 2] -      assert assets_val[:mascots] == [a: 1, b: 2] -    end -  end - -  test "POST /api/pleroma/admin/config error", %{conn: conn} do -    conn = post(conn, "/api/pleroma/admin/config", %{"configs" => []}) - -    assert json_response(conn, 400) == -             %{"error" => "To use this endpoint you need to enable configuration from database."} -  end - -  describe "POST /api/pleroma/admin/config" do -    setup do -      http = Application.get_env(:pleroma, :http) - -      on_exit(fn -> -        Application.delete_env(:pleroma, :key1) -        Application.delete_env(:pleroma, :key2) -        Application.delete_env(:pleroma, :key3) -        Application.delete_env(:pleroma, :key4) -        Application.delete_env(:pleroma, :keyaa1) -        Application.delete_env(:pleroma, :keyaa2) -        Application.delete_env(:pleroma, Pleroma.Web.Endpoint.NotReal) -        Application.delete_env(:pleroma, Pleroma.Captcha.NotReal) -        Application.put_env(:pleroma, :http, http) -        Application.put_env(:tesla, :adapter, Tesla.Mock) -        Restarter.Pleroma.refresh() -      end) -    end - -    setup do: clear_config(:configurable_from_database, true) - -    @tag capture_log: true -    test "create new config setting in db", %{conn: conn} do -      ueberauth = Application.get_env(:ueberauth, Ueberauth) -      on_exit(fn -> Application.put_env(:ueberauth, Ueberauth, ueberauth) end) - -      conn = -        post(conn, "/api/pleroma/admin/config", %{ -          configs: [ -            %{group: ":pleroma", key: ":key1", value: "value1"}, -            %{ -              group: ":ueberauth", -              key: "Ueberauth", -              value: [%{"tuple" => [":consumer_secret", "aaaa"]}] -            }, -            %{ -              group: ":pleroma", -              key: ":key2", -              value: %{ -                ":nested_1" => "nested_value1", -                ":nested_2" => [ -                  %{":nested_22" => "nested_value222"}, -                  %{":nested_33" => %{":nested_44" => "nested_444"}} -                ] -              } -            }, -            %{ -              group: ":pleroma", -              key: ":key3", -              value: [ -                %{"nested_3" => ":nested_3", "nested_33" => "nested_33"}, -                %{"nested_4" => true} -              ] -            }, -            %{ -              group: ":pleroma", -              key: ":key4", -              value: %{":nested_5" => ":upload", "endpoint" => "https://example.com"} -            }, -            %{ -              group: ":idna", -              key: ":key5", -              value: %{"tuple" => ["string", "Pleroma.Captcha.NotReal", []]} -            } -          ] -        }) - -      assert json_response(conn, 200) == %{ -               "configs" => [ -                 %{ -                   "group" => ":pleroma", -                   "key" => ":key1", -                   "value" => "value1", -                   "db" => [":key1"] -                 }, -                 %{ -                   "group" => ":ueberauth", -                   "key" => "Ueberauth", -                   "value" => [%{"tuple" => [":consumer_secret", "aaaa"]}], -                   "db" => [":consumer_secret"] -                 }, -                 %{ -                   "group" => ":pleroma", -                   "key" => ":key2", -                   "value" => %{ -                     ":nested_1" => "nested_value1", -                     ":nested_2" => [ -                       %{":nested_22" => "nested_value222"}, -                       %{":nested_33" => %{":nested_44" => "nested_444"}} -                     ] -                   }, -                   "db" => [":key2"] -                 }, -                 %{ -                   "group" => ":pleroma", -                   "key" => ":key3", -                   "value" => [ -                     %{"nested_3" => ":nested_3", "nested_33" => "nested_33"}, -                     %{"nested_4" => true} -                   ], -                   "db" => [":key3"] -                 }, -                 %{ -                   "group" => ":pleroma", -                   "key" => ":key4", -                   "value" => %{"endpoint" => "https://example.com", ":nested_5" => ":upload"}, -                   "db" => [":key4"] -                 }, -                 %{ -                   "group" => ":idna", -                   "key" => ":key5", -                   "value" => %{"tuple" => ["string", "Pleroma.Captcha.NotReal", []]}, -                   "db" => [":key5"] -                 } -               ] -             } - -      assert Application.get_env(:pleroma, :key1) == "value1" - -      assert Application.get_env(:pleroma, :key2) == %{ -               nested_1: "nested_value1", -               nested_2: [ -                 %{nested_22: "nested_value222"}, -                 %{nested_33: %{nested_44: "nested_444"}} -               ] -             } - -      assert Application.get_env(:pleroma, :key3) == [ -               %{"nested_3" => :nested_3, "nested_33" => "nested_33"}, -               %{"nested_4" => true} -             ] - -      assert Application.get_env(:pleroma, :key4) == %{ -               "endpoint" => "https://example.com", -               nested_5: :upload -             } - -      assert Application.get_env(:idna, :key5) == {"string", Pleroma.Captcha.NotReal, []} -    end - -    test "save configs setting without explicit key", %{conn: conn} do -      level = Application.get_env(:quack, :level) -      meta = Application.get_env(:quack, :meta) -      webhook_url = Application.get_env(:quack, :webhook_url) - -      on_exit(fn -> -        Application.put_env(:quack, :level, level) -        Application.put_env(:quack, :meta, meta) -        Application.put_env(:quack, :webhook_url, webhook_url) -      end) - -      conn = -        post(conn, "/api/pleroma/admin/config", %{ -          configs: [ -            %{ -              group: ":quack", -              key: ":level", -              value: ":info" -            }, -            %{ -              group: ":quack", -              key: ":meta", -              value: [":none"] -            }, -            %{ -              group: ":quack", -              key: ":webhook_url", -              value: "https://hooks.slack.com/services/KEY" -            } -          ] -        }) - -      assert json_response(conn, 200) == %{ -               "configs" => [ -                 %{ -                   "group" => ":quack", -                   "key" => ":level", -                   "value" => ":info", -                   "db" => [":level"] -                 }, -                 %{ -                   "group" => ":quack", -                   "key" => ":meta", -                   "value" => [":none"], -                   "db" => [":meta"] -                 }, -                 %{ -                   "group" => ":quack", -                   "key" => ":webhook_url", -                   "value" => "https://hooks.slack.com/services/KEY", -                   "db" => [":webhook_url"] -                 } -               ] -             } - -      assert Application.get_env(:quack, :level) == :info -      assert Application.get_env(:quack, :meta) == [:none] -      assert Application.get_env(:quack, :webhook_url) == "https://hooks.slack.com/services/KEY" -    end - -    test "saving config with partial update", %{conn: conn} do -      config = insert(:config, key: ":key1", value: :erlang.term_to_binary(key1: 1, key2: 2)) - -      conn = -        post(conn, "/api/pleroma/admin/config", %{ -          configs: [ -            %{group: config.group, key: config.key, value: [%{"tuple" => [":key3", 3]}]} -          ] -        }) - -      assert json_response(conn, 200) == %{ -               "configs" => [ -                 %{ -                   "group" => ":pleroma", -                   "key" => ":key1", -                   "value" => [ -                     %{"tuple" => [":key1", 1]}, -                     %{"tuple" => [":key2", 2]}, -                     %{"tuple" => [":key3", 3]} -                   ], -                   "db" => [":key1", ":key2", ":key3"] -                 } -               ] -             } -    end - -    test "saving config which need pleroma reboot", %{conn: conn} do -      chat = Config.get(:chat) -      on_exit(fn -> Config.put(:chat, chat) end) - -      assert post( -               conn, -               "/api/pleroma/admin/config", -               %{ -                 configs: [ -                   %{group: ":pleroma", key: ":chat", value: [%{"tuple" => [":enabled", true]}]} -                 ] -               } -             ) -             |> json_response(200) == %{ -               "configs" => [ -                 %{ -                   "db" => [":enabled"], -                   "group" => ":pleroma", -                   "key" => ":chat", -                   "value" => [%{"tuple" => [":enabled", true]}] -                 } -               ], -               "need_reboot" => true -             } - -      configs = -        conn -        |> get("/api/pleroma/admin/config") -        |> json_response(200) - -      assert configs["need_reboot"] - -      capture_log(fn -> -        assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{} -      end) =~ "pleroma restarted" - -      configs = -        conn -        |> get("/api/pleroma/admin/config") -        |> json_response(200) - -      assert configs["need_reboot"] == false -    end - -    test "update setting which need reboot, don't change reboot flag until reboot", %{conn: conn} do -      chat = Config.get(:chat) -      on_exit(fn -> Config.put(:chat, chat) end) - -      assert post( -               conn, -               "/api/pleroma/admin/config", -               %{ -                 configs: [ -                   %{group: ":pleroma", key: ":chat", value: [%{"tuple" => [":enabled", true]}]} -                 ] -               } -             ) -             |> json_response(200) == %{ -               "configs" => [ -                 %{ -                   "db" => [":enabled"], -                   "group" => ":pleroma", -                   "key" => ":chat", -                   "value" => [%{"tuple" => [":enabled", true]}] -                 } -               ], -               "need_reboot" => true -             } - -      assert post(conn, "/api/pleroma/admin/config", %{ -               configs: [ -                 %{group: ":pleroma", key: ":key1", value: [%{"tuple" => [":key3", 3]}]} -               ] -             }) -             |> json_response(200) == %{ -               "configs" => [ -                 %{ -                   "group" => ":pleroma", -                   "key" => ":key1", -                   "value" => [ -                     %{"tuple" => [":key3", 3]} -                   ], -                   "db" => [":key3"] -                 } -               ], -               "need_reboot" => true -             } - -      capture_log(fn -> -        assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{} -      end) =~ "pleroma restarted" - -      configs = -        conn -        |> get("/api/pleroma/admin/config") -        |> json_response(200) - -      assert configs["need_reboot"] == false -    end - -    test "saving config with nested merge", %{conn: conn} do -      config = -        insert(:config, key: ":key1", value: :erlang.term_to_binary(key1: 1, key2: [k1: 1, k2: 2])) - -      conn = -        post(conn, "/api/pleroma/admin/config", %{ -          configs: [ -            %{ -              group: config.group, -              key: config.key, -              value: [ -                %{"tuple" => [":key3", 3]}, -                %{ -                  "tuple" => [ -                    ":key2", -                    [ -                      %{"tuple" => [":k2", 1]}, -                      %{"tuple" => [":k3", 3]} -                    ] -                  ] -                } -              ] -            } -          ] -        }) - -      assert json_response(conn, 200) == %{ -               "configs" => [ -                 %{ -                   "group" => ":pleroma", -                   "key" => ":key1", -                   "value" => [ -                     %{"tuple" => [":key1", 1]}, -                     %{"tuple" => [":key3", 3]}, -                     %{ -                       "tuple" => [ -                         ":key2", -                         [ -                           %{"tuple" => [":k1", 1]}, -                           %{"tuple" => [":k2", 1]}, -                           %{"tuple" => [":k3", 3]} -                         ] -                       ] -                     } -                   ], -                   "db" => [":key1", ":key3", ":key2"] -                 } -               ] -             } -    end - -    test "saving special atoms", %{conn: conn} do -      conn = -        post(conn, "/api/pleroma/admin/config", %{ -          "configs" => [ -            %{ -              "group" => ":pleroma", -              "key" => ":key1", -              "value" => [ -                %{ -                  "tuple" => [ -                    ":ssl_options", -                    [%{"tuple" => [":versions", [":tlsv1", ":tlsv1.1", ":tlsv1.2"]]}] -                  ] -                } -              ] -            } -          ] -        }) - -      assert json_response(conn, 200) == %{ -               "configs" => [ -                 %{ -                   "group" => ":pleroma", -                   "key" => ":key1", -                   "value" => [ -                     %{ -                       "tuple" => [ -                         ":ssl_options", -                         [%{"tuple" => [":versions", [":tlsv1", ":tlsv1.1", ":tlsv1.2"]]}] -                       ] -                     } -                   ], -                   "db" => [":ssl_options"] -                 } -               ] -             } - -      assert Application.get_env(:pleroma, :key1) == [ -               ssl_options: [versions: [:tlsv1, :"tlsv1.1", :"tlsv1.2"]] -             ] -    end - -    test "saving full setting if value is in full_key_update list", %{conn: conn} do -      backends = Application.get_env(:logger, :backends) -      on_exit(fn -> Application.put_env(:logger, :backends, backends) end) - -      config = -        insert(:config, -          group: ":logger", -          key: ":backends", -          value: :erlang.term_to_binary([]) -        ) - -      Pleroma.Config.TransferTask.load_and_update_env([], false) - -      assert Application.get_env(:logger, :backends) == [] - -      conn = -        post(conn, "/api/pleroma/admin/config", %{ -          configs: [ -            %{ -              group: config.group, -              key: config.key, -              value: [":console"] -            } -          ] -        }) - -      assert json_response(conn, 200) == %{ -               "configs" => [ -                 %{ -                   "group" => ":logger", -                   "key" => ":backends", -                   "value" => [ -                     ":console" -                   ], -                   "db" => [":backends"] -                 } -               ] -             } - -      assert Application.get_env(:logger, :backends) == [ -               :console -             ] -    end - -    test "saving full setting if value is not keyword", %{conn: conn} do -      config = -        insert(:config, -          group: ":tesla", -          key: ":adapter", -          value: :erlang.term_to_binary(Tesla.Adapter.Hackey) -        ) - -      conn = -        post(conn, "/api/pleroma/admin/config", %{ -          configs: [ -            %{group: config.group, key: config.key, value: "Tesla.Adapter.Httpc"} -          ] -        }) - -      assert json_response(conn, 200) == %{ -               "configs" => [ -                 %{ -                   "group" => ":tesla", -                   "key" => ":adapter", -                   "value" => "Tesla.Adapter.Httpc", -                   "db" => [":adapter"] -                 } -               ] -             } -    end - -    test "update config setting & delete with fallback to default value", %{ -      conn: conn, -      admin: admin, -      token: token -    } do -      ueberauth = Application.get_env(:ueberauth, Ueberauth) -      config1 = insert(:config, key: ":keyaa1") -      config2 = insert(:config, key: ":keyaa2") - -      config3 = -        insert(:config, -          group: ":ueberauth", -          key: "Ueberauth" -        ) - -      conn = -        post(conn, "/api/pleroma/admin/config", %{ -          configs: [ -            %{group: config1.group, key: config1.key, value: "another_value"}, -            %{group: config2.group, key: config2.key, value: "another_value"} -          ] -        }) - -      assert json_response(conn, 200) == %{ -               "configs" => [ -                 %{ -                   "group" => ":pleroma", -                   "key" => config1.key, -                   "value" => "another_value", -                   "db" => [":keyaa1"] -                 }, -                 %{ -                   "group" => ":pleroma", -                   "key" => config2.key, -                   "value" => "another_value", -                   "db" => [":keyaa2"] -                 } -               ] -             } - -      assert Application.get_env(:pleroma, :keyaa1) == "another_value" -      assert Application.get_env(:pleroma, :keyaa2) == "another_value" -      assert Application.get_env(:ueberauth, Ueberauth) == ConfigDB.from_binary(config3.value) - -      conn = -        build_conn() -        |> assign(:user, admin) -        |> assign(:token, token) -        |> post("/api/pleroma/admin/config", %{ -          configs: [ -            %{group: config2.group, key: config2.key, delete: true}, -            %{ -              group: ":ueberauth", -              key: "Ueberauth", -              delete: true -            } -          ] -        }) - -      assert json_response(conn, 200) == %{ -               "configs" => [] -             } - -      assert Application.get_env(:ueberauth, Ueberauth) == ueberauth -      refute Keyword.has_key?(Application.get_all_env(:pleroma), :keyaa2) -    end - -    test "common config example", %{conn: conn} do -      conn = -        post(conn, "/api/pleroma/admin/config", %{ -          configs: [ -            %{ -              "group" => ":pleroma", -              "key" => "Pleroma.Captcha.NotReal", -              "value" => [ -                %{"tuple" => [":enabled", false]}, -                %{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]}, -                %{"tuple" => [":seconds_valid", 60]}, -                %{"tuple" => [":path", ""]}, -                %{"tuple" => [":key1", nil]}, -                %{"tuple" => [":partial_chain", "&:hackney_connect.partial_chain/1"]}, -                %{"tuple" => [":regex1", "~r/https:\/\/example.com/"]}, -                %{"tuple" => [":regex2", "~r/https:\/\/example.com/u"]}, -                %{"tuple" => [":regex3", "~r/https:\/\/example.com/i"]}, -                %{"tuple" => [":regex4", "~r/https:\/\/example.com/s"]}, -                %{"tuple" => [":name", "Pleroma"]} -              ] -            } -          ] -        }) - -      assert Config.get([Pleroma.Captcha.NotReal, :name]) == "Pleroma" - -      assert json_response(conn, 200) == %{ -               "configs" => [ -                 %{ -                   "group" => ":pleroma", -                   "key" => "Pleroma.Captcha.NotReal", -                   "value" => [ -                     %{"tuple" => [":enabled", false]}, -                     %{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]}, -                     %{"tuple" => [":seconds_valid", 60]}, -                     %{"tuple" => [":path", ""]}, -                     %{"tuple" => [":key1", nil]}, -                     %{"tuple" => [":partial_chain", "&:hackney_connect.partial_chain/1"]}, -                     %{"tuple" => [":regex1", "~r/https:\\/\\/example.com/"]}, -                     %{"tuple" => [":regex2", "~r/https:\\/\\/example.com/u"]}, -                     %{"tuple" => [":regex3", "~r/https:\\/\\/example.com/i"]}, -                     %{"tuple" => [":regex4", "~r/https:\\/\\/example.com/s"]}, -                     %{"tuple" => [":name", "Pleroma"]} -                   ], -                   "db" => [ -                     ":enabled", -                     ":method", -                     ":seconds_valid", -                     ":path", -                     ":key1", -                     ":partial_chain", -                     ":regex1", -                     ":regex2", -                     ":regex3", -                     ":regex4", -                     ":name" -                   ] -                 } -               ] -             } -    end - -    test "tuples with more than two values", %{conn: conn} do -      conn = -        post(conn, "/api/pleroma/admin/config", %{ -          configs: [ -            %{ -              "group" => ":pleroma", -              "key" => "Pleroma.Web.Endpoint.NotReal", -              "value" => [ -                %{ -                  "tuple" => [ -                    ":http", -                    [ -                      %{ -                        "tuple" => [ -                          ":key2", -                          [ -                            %{ -                              "tuple" => [ -                                ":_", -                                [ -                                  %{ -                                    "tuple" => [ -                                      "/api/v1/streaming", -                                      "Pleroma.Web.MastodonAPI.WebsocketHandler", -                                      [] -                                    ] -                                  }, -                                  %{ -                                    "tuple" => [ -                                      "/websocket", -                                      "Phoenix.Endpoint.CowboyWebSocket", -                                      %{ -                                        "tuple" => [ -                                          "Phoenix.Transports.WebSocket", -                                          %{ -                                            "tuple" => [ -                                              "Pleroma.Web.Endpoint", -                                              "Pleroma.Web.UserSocket", -                                              [] -                                            ] -                                          } -                                        ] -                                      } -                                    ] -                                  }, -                                  %{ -                                    "tuple" => [ -                                      ":_", -                                      "Phoenix.Endpoint.Cowboy2Handler", -                                      %{"tuple" => ["Pleroma.Web.Endpoint", []]} -                                    ] -                                  } -                                ] -                              ] -                            } -                          ] -                        ] -                      } -                    ] -                  ] -                } -              ] -            } -          ] -        }) - -      assert json_response(conn, 200) == %{ -               "configs" => [ -                 %{ -                   "group" => ":pleroma", -                   "key" => "Pleroma.Web.Endpoint.NotReal", -                   "value" => [ -                     %{ -                       "tuple" => [ -                         ":http", -                         [ -                           %{ -                             "tuple" => [ -                               ":key2", -                               [ -                                 %{ -                                   "tuple" => [ -                                     ":_", -                                     [ -                                       %{ -                                         "tuple" => [ -                                           "/api/v1/streaming", -                                           "Pleroma.Web.MastodonAPI.WebsocketHandler", -                                           [] -                                         ] -                                       }, -                                       %{ -                                         "tuple" => [ -                                           "/websocket", -                                           "Phoenix.Endpoint.CowboyWebSocket", -                                           %{ -                                             "tuple" => [ -                                               "Phoenix.Transports.WebSocket", -                                               %{ -                                                 "tuple" => [ -                                                   "Pleroma.Web.Endpoint", -                                                   "Pleroma.Web.UserSocket", -                                                   [] -                                                 ] -                                               } -                                             ] -                                           } -                                         ] -                                       }, -                                       %{ -                                         "tuple" => [ -                                           ":_", -                                           "Phoenix.Endpoint.Cowboy2Handler", -                                           %{"tuple" => ["Pleroma.Web.Endpoint", []]} -                                         ] -                                       } -                                     ] -                                   ] -                                 } -                               ] -                             ] -                           } -                         ] -                       ] -                     } -                   ], -                   "db" => [":http"] -                 } -               ] -             } -    end - -    test "settings with nesting map", %{conn: conn} do -      conn = -        post(conn, "/api/pleroma/admin/config", %{ -          configs: [ -            %{ -              "group" => ":pleroma", -              "key" => ":key1", -              "value" => [ -                %{"tuple" => [":key2", "some_val"]}, -                %{ -                  "tuple" => [ -                    ":key3", -                    %{ -                      ":max_options" => 20, -                      ":max_option_chars" => 200, -                      ":min_expiration" => 0, -                      ":max_expiration" => 31_536_000, -                      "nested" => %{ -                        ":max_options" => 20, -                        ":max_option_chars" => 200, -                        ":min_expiration" => 0, -                        ":max_expiration" => 31_536_000 -                      } -                    } -                  ] -                } -              ] -            } -          ] -        }) - -      assert json_response(conn, 200) == -               %{ -                 "configs" => [ -                   %{ -                     "group" => ":pleroma", -                     "key" => ":key1", -                     "value" => [ -                       %{"tuple" => [":key2", "some_val"]}, -                       %{ -                         "tuple" => [ -                           ":key3", -                           %{ -                             ":max_expiration" => 31_536_000, -                             ":max_option_chars" => 200, -                             ":max_options" => 20, -                             ":min_expiration" => 0, -                             "nested" => %{ -                               ":max_expiration" => 31_536_000, -                               ":max_option_chars" => 200, -                               ":max_options" => 20, -                               ":min_expiration" => 0 -                             } -                           } -                         ] -                       } -                     ], -                     "db" => [":key2", ":key3"] -                   } -                 ] -               } -    end - -    test "value as map", %{conn: conn} do -      conn = -        post(conn, "/api/pleroma/admin/config", %{ -          configs: [ -            %{ -              "group" => ":pleroma", -              "key" => ":key1", -              "value" => %{"key" => "some_val"} -            } -          ] -        }) - -      assert json_response(conn, 200) == -               %{ -                 "configs" => [ -                   %{ -                     "group" => ":pleroma", -                     "key" => ":key1", -                     "value" => %{"key" => "some_val"}, -                     "db" => [":key1"] -                   } -                 ] -               } -    end - -    test "queues key as atom", %{conn: conn} do -      conn = -        post(conn, "/api/pleroma/admin/config", %{ -          configs: [ -            %{ -              "group" => ":oban", -              "key" => ":queues", -              "value" => [ -                %{"tuple" => [":federator_incoming", 50]}, -                %{"tuple" => [":federator_outgoing", 50]}, -                %{"tuple" => [":web_push", 50]}, -                %{"tuple" => [":mailer", 10]}, -                %{"tuple" => [":transmogrifier", 20]}, -                %{"tuple" => [":scheduled_activities", 10]}, -                %{"tuple" => [":background", 5]} -              ] -            } -          ] -        }) - -      assert json_response(conn, 200) == %{ -               "configs" => [ -                 %{ -                   "group" => ":oban", -                   "key" => ":queues", -                   "value" => [ -                     %{"tuple" => [":federator_incoming", 50]}, -                     %{"tuple" => [":federator_outgoing", 50]}, -                     %{"tuple" => [":web_push", 50]}, -                     %{"tuple" => [":mailer", 10]}, -                     %{"tuple" => [":transmogrifier", 20]}, -                     %{"tuple" => [":scheduled_activities", 10]}, -                     %{"tuple" => [":background", 5]} -                   ], -                   "db" => [ -                     ":federator_incoming", -                     ":federator_outgoing", -                     ":web_push", -                     ":mailer", -                     ":transmogrifier", -                     ":scheduled_activities", -                     ":background" -                   ] -                 } -               ] -             } -    end - -    test "delete part of settings by atom subkeys", %{conn: conn} do -      config = -        insert(:config, -          key: ":keyaa1", -          value: :erlang.term_to_binary(subkey1: "val1", subkey2: "val2", subkey3: "val3") -        ) - -      conn = -        post(conn, "/api/pleroma/admin/config", %{ -          configs: [ -            %{ -              group: config.group, -              key: config.key, -              subkeys: [":subkey1", ":subkey3"], -              delete: true -            } -          ] -        }) - -      assert json_response(conn, 200) == %{ -               "configs" => [ -                 %{ -                   "group" => ":pleroma", -                   "key" => ":keyaa1", -                   "value" => [%{"tuple" => [":subkey2", "val2"]}], -                   "db" => [":subkey2"] -                 } -               ] -             } -    end - -    test "proxy tuple localhost", %{conn: conn} do -      conn = -        post(conn, "/api/pleroma/admin/config", %{ -          configs: [ -            %{ -              group: ":pleroma", -              key: ":http", -              value: [ -                %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}]} -              ] -            } -          ] -        }) - -      assert %{ -               "configs" => [ -                 %{ -                   "group" => ":pleroma", -                   "key" => ":http", -                   "value" => value, -                   "db" => db -                 } -               ] -             } = json_response(conn, 200) - -      assert %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}]} in value -      assert ":proxy_url" in db -    end - -    test "proxy tuple domain", %{conn: conn} do -      conn = -        post(conn, "/api/pleroma/admin/config", %{ -          configs: [ -            %{ -              group: ":pleroma", -              key: ":http", -              value: [ -                %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}]} -              ] -            } -          ] -        }) - -      assert %{ -               "configs" => [ -                 %{ -                   "group" => ":pleroma", -                   "key" => ":http", -                   "value" => value, -                   "db" => db -                 } -               ] -             } = json_response(conn, 200) - -      assert %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}]} in value -      assert ":proxy_url" in db -    end - -    test "proxy tuple ip", %{conn: conn} do -      conn = -        post(conn, "/api/pleroma/admin/config", %{ -          configs: [ -            %{ -              group: ":pleroma", -              key: ":http", -              value: [ -                %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}]} -              ] -            } -          ] -        }) - -      assert %{ -               "configs" => [ -                 %{ -                   "group" => ":pleroma", -                   "key" => ":http", -                   "value" => value, -                   "db" => db -                 } -               ] -             } = json_response(conn, 200) - -      assert %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}]} in value -      assert ":proxy_url" in db -    end - -    @tag capture_log: true -    test "doesn't set keys not in the whitelist", %{conn: conn} do -      clear_config(:database_config_whitelist, [ -        {:pleroma, :key1}, -        {:pleroma, :key2}, -        {:pleroma, Pleroma.Captcha.NotReal}, -        {:not_real} -      ]) - -      post(conn, "/api/pleroma/admin/config", %{ -        configs: [ -          %{group: ":pleroma", key: ":key1", value: "value1"}, -          %{group: ":pleroma", key: ":key2", value: "value2"}, -          %{group: ":pleroma", key: ":key3", value: "value3"}, -          %{group: ":pleroma", key: "Pleroma.Web.Endpoint.NotReal", value: "value4"}, -          %{group: ":pleroma", key: "Pleroma.Captcha.NotReal", value: "value5"}, -          %{group: ":not_real", key: ":anything", value: "value6"} -        ] -      }) - -      assert Application.get_env(:pleroma, :key1) == "value1" -      assert Application.get_env(:pleroma, :key2) == "value2" -      assert Application.get_env(:pleroma, :key3) == nil -      assert Application.get_env(:pleroma, Pleroma.Web.Endpoint.NotReal) == nil -      assert Application.get_env(:pleroma, Pleroma.Captcha.NotReal) == "value5" -      assert Application.get_env(:not_real, :anything) == "value6" -    end -  end -    describe "GET /api/pleroma/admin/restart" do      setup do: clear_config(:configurable_from_database, true) @@ -3254,14 +1599,14 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do        assert patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{                 "actor_type" => "Application"               }) -             |> json_response(200) == %{"errors" => %{"actor_type" => "is invalid"}} +             |> json_response(400) == %{"errors" => %{"actor_type" => "is invalid"}}      end      test "update non existing user", %{conn: conn} do        assert patch(conn, "/api/pleroma/admin/users/non-existing/credentials", %{                 "password" => "new_password"               }) -             |> json_response(200) == %{"error" => "Unable to update user."} +             |> json_response(404) == %{"error" => "Not found"}      end    end @@ -3281,57 +1626,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do      end    end -  describe "relays" do -    test "POST /relay", %{conn: conn, admin: admin} do -      conn = -        post(conn, "/api/pleroma/admin/relay", %{ -          relay_url: "http://mastodon.example.org/users/admin" -        }) - -      assert json_response(conn, 200) == "http://mastodon.example.org/users/admin" - -      log_entry = Repo.one(ModerationLog) - -      assert ModerationLog.get_log_entry_message(log_entry) == -               "@#{admin.nickname} followed relay: http://mastodon.example.org/users/admin" -    end - -    test "GET /relay", %{conn: conn} do -      relay_user = Pleroma.Web.ActivityPub.Relay.get_actor() - -      ["http://mastodon.example.org/users/admin", "https://mstdn.io/users/mayuutann"] -      |> Enum.each(fn ap_id -> -        {:ok, user} = User.get_or_fetch_by_ap_id(ap_id) -        User.follow(relay_user, user) -      end) - -      conn = get(conn, "/api/pleroma/admin/relay") - -      assert json_response(conn, 200)["relays"] -- ["mastodon.example.org", "mstdn.io"] == [] -    end - -    test "DELETE /relay", %{conn: conn, admin: admin} do -      post(conn, "/api/pleroma/admin/relay", %{ -        relay_url: "http://mastodon.example.org/users/admin" -      }) - -      conn = -        delete(conn, "/api/pleroma/admin/relay", %{ -          relay_url: "http://mastodon.example.org/users/admin" -        }) - -      assert json_response(conn, 200) == "http://mastodon.example.org/users/admin" - -      [log_entry_one, log_entry_two] = Repo.all(ModerationLog) - -      assert ModerationLog.get_log_entry_message(log_entry_one) == -               "@#{admin.nickname} followed relay: http://mastodon.example.org/users/admin" - -      assert ModerationLog.get_log_entry_message(log_entry_two) == -               "@#{admin.nickname} unfollowed relay: http://mastodon.example.org/users/admin" -    end -  end -    describe "instances" do      test "GET /instances/:instance/statuses", %{conn: conn} do        user = insert(:user, local: false, nickname: "archaeme@archae.me") @@ -3421,116 +1715,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do      end    end -  describe "POST /reports/:id/notes" do -    setup %{conn: conn, admin: admin} do -      [reporter, target_user] = insert_pair(:user) -      activity = insert(:note_activity, user: target_user) - -      {:ok, %{id: report_id}} = -        CommonAPI.report(reporter, %{ -          account_id: target_user.id, -          comment: "I feel offended", -          status_ids: [activity.id] -        }) - -      post(conn, "/api/pleroma/admin/reports/#{report_id}/notes", %{ -        content: "this is disgusting!" -      }) - -      post(conn, "/api/pleroma/admin/reports/#{report_id}/notes", %{ -        content: "this is disgusting2!" -      }) - -      %{ -        admin_id: admin.id, -        report_id: report_id -      } -    end - -    test "it creates report note", %{admin_id: admin_id, report_id: report_id} do -      [note, _] = Repo.all(ReportNote) - -      assert %{ -               activity_id: ^report_id, -               content: "this is disgusting!", -               user_id: ^admin_id -             } = note -    end - -    test "it returns reports with notes", %{conn: conn, admin: admin} do -      conn = get(conn, "/api/pleroma/admin/reports") - -      response = json_response(conn, 200) -      notes = hd(response["reports"])["notes"] -      [note, _] = notes - -      assert note["user"]["nickname"] == admin.nickname -      assert note["content"] == "this is disgusting!" -      assert note["created_at"] -      assert response["total"] == 1 -    end - -    test "it deletes the note", %{conn: conn, report_id: report_id} do -      assert ReportNote |> Repo.all() |> length() == 2 - -      [note, _] = Repo.all(ReportNote) - -      delete(conn, "/api/pleroma/admin/reports/#{report_id}/notes/#{note.id}") - -      assert ReportNote |> Repo.all() |> length() == 1 -    end -  end - -  describe "GET /api/pleroma/admin/config/descriptions" do -    test "structure", %{conn: conn} do -      admin = insert(:user, is_admin: true) - -      conn = -        assign(conn, :user, admin) -        |> get("/api/pleroma/admin/config/descriptions") - -      assert [child | _others] = json_response(conn, 200) - -      assert child["children"] -      assert child["key"] -      assert String.starts_with?(child["group"], ":") -      assert child["description"] -    end - -    test "filters by database configuration whitelist", %{conn: conn} do -      clear_config(:database_config_whitelist, [ -        {:pleroma, :instance}, -        {:pleroma, :activitypub}, -        {:pleroma, Pleroma.Upload}, -        {:esshd} -      ]) - -      admin = insert(:user, is_admin: true) - -      conn = -        assign(conn, :user, admin) -        |> get("/api/pleroma/admin/config/descriptions") - -      children = json_response(conn, 200) - -      assert length(children) == 4 - -      assert Enum.count(children, fn c -> c["group"] == ":pleroma" end) == 3 - -      instance = Enum.find(children, fn c -> c["key"] == ":instance" end) -      assert instance["children"] - -      activitypub = Enum.find(children, fn c -> c["key"] == ":activitypub" end) -      assert activitypub["children"] - -      web_endpoint = Enum.find(children, fn c -> c["key"] == "Pleroma.Upload" end) -      assert web_endpoint["children"] - -      esshd = Enum.find(children, fn c -> c["group"] == ":esshd" end) -      assert esshd["children"] -    end -  end -    describe "/api/pleroma/admin/stats" do      test "status visibility count", %{conn: conn} do        admin = insert(:user, is_admin: true) @@ -3548,190 +1732,25 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do        assert %{"direct" => 0, "private" => 0, "public" => 1, "unlisted" => 2} =                 response["status_visibility"]      end -  end - -  describe "POST /api/pleroma/admin/oauth_app" do -    test "errors", %{conn: conn} do -      response = conn |> post("/api/pleroma/admin/oauth_app", %{}) |> json_response(200) - -      assert response == %{"name" => "can't be blank", "redirect_uris" => "can't be blank"} -    end - -    test "success", %{conn: conn} do -      base_url = Web.base_url() -      app_name = "Trusted app" - -      response = -        conn -        |> post("/api/pleroma/admin/oauth_app", %{ -          name: app_name, -          redirect_uris: base_url -        }) -        |> json_response(200) - -      assert %{ -               "client_id" => _, -               "client_secret" => _, -               "name" => ^app_name, -               "redirect_uri" => ^base_url, -               "trusted" => false -             } = response -    end - -    test "with trusted", %{conn: conn} do -      base_url = Web.base_url() -      app_name = "Trusted app" - -      response = -        conn -        |> post("/api/pleroma/admin/oauth_app", %{ -          name: app_name, -          redirect_uris: base_url, -          trusted: true -        }) -        |> json_response(200) - -      assert %{ -               "client_id" => _, -               "client_secret" => _, -               "name" => ^app_name, -               "redirect_uri" => ^base_url, -               "trusted" => true -             } = response -    end -  end - -  describe "GET /api/pleroma/admin/oauth_app" do -    setup do -      app = insert(:oauth_app) -      {:ok, app: app} -    end - -    test "list", %{conn: conn} do -      response = -        conn -        |> get("/api/pleroma/admin/oauth_app") -        |> json_response(200) - -      assert %{"apps" => apps, "count" => count, "page_size" => _} = response - -      assert length(apps) == count -    end - -    test "with page size", %{conn: conn} do -      insert(:oauth_app) -      page_size = 1 - -      response = -        conn -        |> get("/api/pleroma/admin/oauth_app", %{page_size: to_string(page_size)}) -        |> json_response(200) - -      assert %{"apps" => apps, "count" => _, "page_size" => ^page_size} = response - -      assert length(apps) == page_size -    end - -    test "search by client name", %{conn: conn, app: app} do -      response = -        conn -        |> get("/api/pleroma/admin/oauth_app", %{name: app.client_name}) -        |> json_response(200) - -      assert %{"apps" => [returned], "count" => _, "page_size" => _} = response - -      assert returned["client_id"] == app.client_id -      assert returned["name"] == app.client_name -    end - -    test "search by client id", %{conn: conn, app: app} do -      response = -        conn -        |> get("/api/pleroma/admin/oauth_app", %{client_id: app.client_id}) -        |> json_response(200) - -      assert %{"apps" => [returned], "count" => _, "page_size" => _} = response - -      assert returned["client_id"] == app.client_id -      assert returned["name"] == app.client_name -    end - -    test "only trusted", %{conn: conn} do -      app = insert(:oauth_app, trusted: true) -      response = -        conn -        |> get("/api/pleroma/admin/oauth_app", %{trusted: true}) -        |> json_response(200) - -      assert %{"apps" => [returned], "count" => _, "page_size" => _} = response - -      assert returned["client_id"] == app.client_id -      assert returned["name"] == app.client_name -    end -  end - -  describe "DELETE /api/pleroma/admin/oauth_app/:id" do -    test "with id", %{conn: conn} do -      app = insert(:oauth_app) - -      response = -        conn -        |> delete("/api/pleroma/admin/oauth_app/" <> to_string(app.id)) -        |> json_response(:no_content) - -      assert response == "" -    end - -    test "with non existance id", %{conn: conn} do -      response = -        conn -        |> delete("/api/pleroma/admin/oauth_app/0") -        |> json_response(:bad_request) - -      assert response == "" -    end -  end - -  describe "PATCH /api/pleroma/admin/oauth_app/:id" do -    test "with id", %{conn: conn} do -      app = insert(:oauth_app) +    test "by instance", %{conn: conn} do +      admin = insert(:user, is_admin: true) +      user1 = insert(:user) +      instance2 = "instance2.tld" +      user2 = insert(:user, %{ap_id: "https://#{instance2}/@actor"}) -      name = "another name" -      url = "https://example.com" -      scopes = ["admin"] -      id = app.id -      website = "http://website.com" +      CommonAPI.post(user1, %{visibility: "public", status: "hey"}) +      CommonAPI.post(user2, %{visibility: "unlisted", status: "hey"}) +      CommonAPI.post(user2, %{visibility: "private", status: "hey"})        response =          conn -        |> patch("/api/pleroma/admin/oauth_app/" <> to_string(app.id), %{ -          name: name, -          trusted: true, -          redirect_uris: url, -          scopes: scopes, -          website: website -        }) +        |> assign(:user, admin) +        |> get("/api/pleroma/admin/stats", instance: instance2)          |> json_response(200) -      assert %{ -               "client_id" => _, -               "client_secret" => _, -               "id" => ^id, -               "name" => ^name, -               "redirect_uri" => ^url, -               "trusted" => true, -               "website" => ^website -             } = response -    end - -    test "without id", %{conn: conn} do -      response = -        conn -        |> patch("/api/pleroma/admin/oauth_app/0") -        |> json_response(:bad_request) - -      assert response == "" +      assert %{"direct" => 0, "private" => 1, "public" => 0, "unlisted" => 1} = +               response["status_visibility"]      end    end  end diff --git a/test/web/admin_api/controllers/config_controller_test.exs b/test/web/admin_api/controllers/config_controller_test.exs new file mode 100644 index 000000000..064ef9bc7 --- /dev/null +++ b/test/web/admin_api/controllers/config_controller_test.exs @@ -0,0 +1,1388 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do +  use Pleroma.Web.ConnCase, async: true + +  import ExUnit.CaptureLog +  import Pleroma.Factory + +  alias Pleroma.Config +  alias Pleroma.ConfigDB + +  setup do +    admin = insert(:user, is_admin: true) +    token = insert(:oauth_admin_token, user: admin) + +    conn = +      build_conn() +      |> assign(:user, admin) +      |> assign(:token, token) + +    {:ok, %{admin: admin, token: token, conn: conn}} +  end + +  describe "GET /api/pleroma/admin/config" do +    setup do: clear_config(:configurable_from_database, true) + +    test "when configuration from database is off", %{conn: conn} do +      Config.put(:configurable_from_database, false) +      conn = get(conn, "/api/pleroma/admin/config") + +      assert json_response_and_validate_schema(conn, 400) == +               %{ +                 "error" => "To use this endpoint you need to enable configuration from database." +               } +    end + +    test "with settings only in db", %{conn: conn} do +      config1 = insert(:config) +      config2 = insert(:config) + +      conn = get(conn, "/api/pleroma/admin/config?only_db=true") + +      %{ +        "configs" => [ +          %{ +            "group" => ":pleroma", +            "key" => key1, +            "value" => _ +          }, +          %{ +            "group" => ":pleroma", +            "key" => key2, +            "value" => _ +          } +        ] +      } = json_response_and_validate_schema(conn, 200) + +      assert key1 == inspect(config1.key) +      assert key2 == inspect(config2.key) +    end + +    test "db is added to settings that are in db", %{conn: conn} do +      _config = insert(:config, key: ":instance", value: [name: "Some name"]) + +      %{"configs" => configs} = +        conn +        |> get("/api/pleroma/admin/config") +        |> json_response_and_validate_schema(200) + +      [instance_config] = +        Enum.filter(configs, fn %{"group" => group, "key" => key} -> +          group == ":pleroma" and key == ":instance" +        end) + +      assert instance_config["db"] == [":name"] +    end + +    test "merged default setting with db settings", %{conn: conn} do +      config1 = insert(:config) +      config2 = insert(:config) + +      config3 = +        insert(:config, +          value: [k1: :v1, k2: :v2] +        ) + +      %{"configs" => configs} = +        conn +        |> get("/api/pleroma/admin/config") +        |> json_response_and_validate_schema(200) + +      assert length(configs) > 3 + +      saved_configs = [config1, config2, config3] +      keys = Enum.map(saved_configs, &inspect(&1.key)) + +      received_configs = +        Enum.filter(configs, fn %{"group" => group, "key" => key} -> +          group == ":pleroma" and key in keys +        end) + +      assert length(received_configs) == 3 + +      db_keys = +        config3.value +        |> Keyword.keys() +        |> ConfigDB.to_json_types() + +      keys = Enum.map(saved_configs -- [config3], &inspect(&1.key)) + +      values = Enum.map(saved_configs, &ConfigDB.to_json_types(&1.value)) + +      mapset_keys = MapSet.new(keys ++ db_keys) + +      Enum.each(received_configs, fn %{"value" => value, "db" => db} -> +        db = MapSet.new(db) +        assert MapSet.subset?(db, mapset_keys) + +        assert value in values +      end) +    end + +    test "subkeys with full update right merge", %{conn: conn} do +      insert(:config, +        key: ":emoji", +        value: [groups: [a: 1, b: 2], key: [a: 1]] +      ) + +      insert(:config, +        key: ":assets", +        value: [mascots: [a: 1, b: 2], key: [a: 1]] +      ) + +      %{"configs" => configs} = +        conn +        |> get("/api/pleroma/admin/config") +        |> json_response_and_validate_schema(200) + +      vals = +        Enum.filter(configs, fn %{"group" => group, "key" => key} -> +          group == ":pleroma" and key in [":emoji", ":assets"] +        end) + +      emoji = Enum.find(vals, fn %{"key" => key} -> key == ":emoji" end) +      assets = Enum.find(vals, fn %{"key" => key} -> key == ":assets" end) + +      emoji_val = ConfigDB.to_elixir_types(emoji["value"]) +      assets_val = ConfigDB.to_elixir_types(assets["value"]) + +      assert emoji_val[:groups] == [a: 1, b: 2] +      assert assets_val[:mascots] == [a: 1, b: 2] +    end +  end + +  test "POST /api/pleroma/admin/config error", %{conn: conn} do +    conn = +      conn +      |> put_req_header("content-type", "application/json") +      |> post("/api/pleroma/admin/config", %{"configs" => []}) + +    assert json_response_and_validate_schema(conn, 400) == +             %{"error" => "To use this endpoint you need to enable configuration from database."} +  end + +  describe "POST /api/pleroma/admin/config" do +    setup do +      http = Application.get_env(:pleroma, :http) + +      on_exit(fn -> +        Application.delete_env(:pleroma, :key1) +        Application.delete_env(:pleroma, :key2) +        Application.delete_env(:pleroma, :key3) +        Application.delete_env(:pleroma, :key4) +        Application.delete_env(:pleroma, :keyaa1) +        Application.delete_env(:pleroma, :keyaa2) +        Application.delete_env(:pleroma, Pleroma.Web.Endpoint.NotReal) +        Application.delete_env(:pleroma, Pleroma.Captcha.NotReal) +        Application.put_env(:pleroma, :http, http) +        Application.put_env(:tesla, :adapter, Tesla.Mock) +        Restarter.Pleroma.refresh() +      end) +    end + +    setup do: clear_config(:configurable_from_database, true) + +    @tag capture_log: true +    test "create new config setting in db", %{conn: conn} do +      ueberauth = Application.get_env(:ueberauth, Ueberauth) +      on_exit(fn -> Application.put_env(:ueberauth, Ueberauth, ueberauth) end) + +      conn = +        conn +        |> put_req_header("content-type", "application/json") +        |> post("/api/pleroma/admin/config", %{ +          configs: [ +            %{group: ":pleroma", key: ":key1", value: "value1"}, +            %{ +              group: ":ueberauth", +              key: "Ueberauth", +              value: [%{"tuple" => [":consumer_secret", "aaaa"]}] +            }, +            %{ +              group: ":pleroma", +              key: ":key2", +              value: %{ +                ":nested_1" => "nested_value1", +                ":nested_2" => [ +                  %{":nested_22" => "nested_value222"}, +                  %{":nested_33" => %{":nested_44" => "nested_444"}} +                ] +              } +            }, +            %{ +              group: ":pleroma", +              key: ":key3", +              value: [ +                %{"nested_3" => ":nested_3", "nested_33" => "nested_33"}, +                %{"nested_4" => true} +              ] +            }, +            %{ +              group: ":pleroma", +              key: ":key4", +              value: %{":nested_5" => ":upload", "endpoint" => "https://example.com"} +            }, +            %{ +              group: ":idna", +              key: ":key5", +              value: %{"tuple" => ["string", "Pleroma.Captcha.NotReal", []]} +            } +          ] +        }) + +      assert json_response_and_validate_schema(conn, 200) == %{ +               "configs" => [ +                 %{ +                   "group" => ":pleroma", +                   "key" => ":key1", +                   "value" => "value1", +                   "db" => [":key1"] +                 }, +                 %{ +                   "group" => ":ueberauth", +                   "key" => "Ueberauth", +                   "value" => [%{"tuple" => [":consumer_secret", "aaaa"]}], +                   "db" => [":consumer_secret"] +                 }, +                 %{ +                   "group" => ":pleroma", +                   "key" => ":key2", +                   "value" => %{ +                     ":nested_1" => "nested_value1", +                     ":nested_2" => [ +                       %{":nested_22" => "nested_value222"}, +                       %{":nested_33" => %{":nested_44" => "nested_444"}} +                     ] +                   }, +                   "db" => [":key2"] +                 }, +                 %{ +                   "group" => ":pleroma", +                   "key" => ":key3", +                   "value" => [ +                     %{"nested_3" => ":nested_3", "nested_33" => "nested_33"}, +                     %{"nested_4" => true} +                   ], +                   "db" => [":key3"] +                 }, +                 %{ +                   "group" => ":pleroma", +                   "key" => ":key4", +                   "value" => %{"endpoint" => "https://example.com", ":nested_5" => ":upload"}, +                   "db" => [":key4"] +                 }, +                 %{ +                   "group" => ":idna", +                   "key" => ":key5", +                   "value" => %{"tuple" => ["string", "Pleroma.Captcha.NotReal", []]}, +                   "db" => [":key5"] +                 } +               ], +               "need_reboot" => false +             } + +      assert Application.get_env(:pleroma, :key1) == "value1" + +      assert Application.get_env(:pleroma, :key2) == %{ +               nested_1: "nested_value1", +               nested_2: [ +                 %{nested_22: "nested_value222"}, +                 %{nested_33: %{nested_44: "nested_444"}} +               ] +             } + +      assert Application.get_env(:pleroma, :key3) == [ +               %{"nested_3" => :nested_3, "nested_33" => "nested_33"}, +               %{"nested_4" => true} +             ] + +      assert Application.get_env(:pleroma, :key4) == %{ +               "endpoint" => "https://example.com", +               nested_5: :upload +             } + +      assert Application.get_env(:idna, :key5) == {"string", Pleroma.Captcha.NotReal, []} +    end + +    test "save configs setting without explicit key", %{conn: conn} do +      level = Application.get_env(:quack, :level) +      meta = Application.get_env(:quack, :meta) +      webhook_url = Application.get_env(:quack, :webhook_url) + +      on_exit(fn -> +        Application.put_env(:quack, :level, level) +        Application.put_env(:quack, :meta, meta) +        Application.put_env(:quack, :webhook_url, webhook_url) +      end) + +      conn = +        conn +        |> put_req_header("content-type", "application/json") +        |> post("/api/pleroma/admin/config", %{ +          configs: [ +            %{ +              group: ":quack", +              key: ":level", +              value: ":info" +            }, +            %{ +              group: ":quack", +              key: ":meta", +              value: [":none"] +            }, +            %{ +              group: ":quack", +              key: ":webhook_url", +              value: "https://hooks.slack.com/services/KEY" +            } +          ] +        }) + +      assert json_response_and_validate_schema(conn, 200) == %{ +               "configs" => [ +                 %{ +                   "group" => ":quack", +                   "key" => ":level", +                   "value" => ":info", +                   "db" => [":level"] +                 }, +                 %{ +                   "group" => ":quack", +                   "key" => ":meta", +                   "value" => [":none"], +                   "db" => [":meta"] +                 }, +                 %{ +                   "group" => ":quack", +                   "key" => ":webhook_url", +                   "value" => "https://hooks.slack.com/services/KEY", +                   "db" => [":webhook_url"] +                 } +               ], +               "need_reboot" => false +             } + +      assert Application.get_env(:quack, :level) == :info +      assert Application.get_env(:quack, :meta) == [:none] +      assert Application.get_env(:quack, :webhook_url) == "https://hooks.slack.com/services/KEY" +    end + +    test "saving config with partial update", %{conn: conn} do +      insert(:config, key: ":key1", value: :erlang.term_to_binary(key1: 1, key2: 2)) + +      conn = +        conn +        |> put_req_header("content-type", "application/json") +        |> post("/api/pleroma/admin/config", %{ +          configs: [ +            %{group: ":pleroma", key: ":key1", value: [%{"tuple" => [":key3", 3]}]} +          ] +        }) + +      assert json_response_and_validate_schema(conn, 200) == %{ +               "configs" => [ +                 %{ +                   "group" => ":pleroma", +                   "key" => ":key1", +                   "value" => [ +                     %{"tuple" => [":key1", 1]}, +                     %{"tuple" => [":key2", 2]}, +                     %{"tuple" => [":key3", 3]} +                   ], +                   "db" => [":key1", ":key2", ":key3"] +                 } +               ], +               "need_reboot" => false +             } +    end + +    test "saving config which need pleroma reboot", %{conn: conn} do +      chat = Config.get(:chat) +      on_exit(fn -> Config.put(:chat, chat) end) + +      assert conn +             |> put_req_header("content-type", "application/json") +             |> post( +               "/api/pleroma/admin/config", +               %{ +                 configs: [ +                   %{group: ":pleroma", key: ":chat", value: [%{"tuple" => [":enabled", true]}]} +                 ] +               } +             ) +             |> json_response_and_validate_schema(200) == %{ +               "configs" => [ +                 %{ +                   "db" => [":enabled"], +                   "group" => ":pleroma", +                   "key" => ":chat", +                   "value" => [%{"tuple" => [":enabled", true]}] +                 } +               ], +               "need_reboot" => true +             } + +      configs = +        conn +        |> get("/api/pleroma/admin/config") +        |> json_response_and_validate_schema(200) + +      assert configs["need_reboot"] + +      capture_log(fn -> +        assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == +                 %{} +      end) =~ "pleroma restarted" + +      configs = +        conn +        |> get("/api/pleroma/admin/config") +        |> json_response_and_validate_schema(200) + +      assert configs["need_reboot"] == false +    end + +    test "update setting which need reboot, don't change reboot flag until reboot", %{conn: conn} do +      chat = Config.get(:chat) +      on_exit(fn -> Config.put(:chat, chat) end) + +      assert conn +             |> put_req_header("content-type", "application/json") +             |> post( +               "/api/pleroma/admin/config", +               %{ +                 configs: [ +                   %{group: ":pleroma", key: ":chat", value: [%{"tuple" => [":enabled", true]}]} +                 ] +               } +             ) +             |> json_response_and_validate_schema(200) == %{ +               "configs" => [ +                 %{ +                   "db" => [":enabled"], +                   "group" => ":pleroma", +                   "key" => ":chat", +                   "value" => [%{"tuple" => [":enabled", true]}] +                 } +               ], +               "need_reboot" => true +             } + +      assert conn +             |> put_req_header("content-type", "application/json") +             |> post("/api/pleroma/admin/config", %{ +               configs: [ +                 %{group: ":pleroma", key: ":key1", value: [%{"tuple" => [":key3", 3]}]} +               ] +             }) +             |> json_response_and_validate_schema(200) == %{ +               "configs" => [ +                 %{ +                   "group" => ":pleroma", +                   "key" => ":key1", +                   "value" => [ +                     %{"tuple" => [":key3", 3]} +                   ], +                   "db" => [":key3"] +                 } +               ], +               "need_reboot" => true +             } + +      capture_log(fn -> +        assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == +                 %{} +      end) =~ "pleroma restarted" + +      configs = +        conn +        |> get("/api/pleroma/admin/config") +        |> json_response_and_validate_schema(200) + +      assert configs["need_reboot"] == false +    end + +    test "saving config with nested merge", %{conn: conn} do +      insert(:config, key: :key1, value: [key1: 1, key2: [k1: 1, k2: 2]]) + +      conn = +        conn +        |> put_req_header("content-type", "application/json") +        |> post("/api/pleroma/admin/config", %{ +          configs: [ +            %{ +              group: ":pleroma", +              key: ":key1", +              value: [ +                %{"tuple" => [":key3", 3]}, +                %{ +                  "tuple" => [ +                    ":key2", +                    [ +                      %{"tuple" => [":k2", 1]}, +                      %{"tuple" => [":k3", 3]} +                    ] +                  ] +                } +              ] +            } +          ] +        }) + +      assert json_response_and_validate_schema(conn, 200) == %{ +               "configs" => [ +                 %{ +                   "group" => ":pleroma", +                   "key" => ":key1", +                   "value" => [ +                     %{"tuple" => [":key1", 1]}, +                     %{"tuple" => [":key3", 3]}, +                     %{ +                       "tuple" => [ +                         ":key2", +                         [ +                           %{"tuple" => [":k1", 1]}, +                           %{"tuple" => [":k2", 1]}, +                           %{"tuple" => [":k3", 3]} +                         ] +                       ] +                     } +                   ], +                   "db" => [":key1", ":key3", ":key2"] +                 } +               ], +               "need_reboot" => false +             } +    end + +    test "saving special atoms", %{conn: conn} do +      conn = +        conn +        |> put_req_header("content-type", "application/json") +        |> post("/api/pleroma/admin/config", %{ +          "configs" => [ +            %{ +              "group" => ":pleroma", +              "key" => ":key1", +              "value" => [ +                %{ +                  "tuple" => [ +                    ":ssl_options", +                    [%{"tuple" => [":versions", [":tlsv1", ":tlsv1.1", ":tlsv1.2"]]}] +                  ] +                } +              ] +            } +          ] +        }) + +      assert json_response_and_validate_schema(conn, 200) == %{ +               "configs" => [ +                 %{ +                   "group" => ":pleroma", +                   "key" => ":key1", +                   "value" => [ +                     %{ +                       "tuple" => [ +                         ":ssl_options", +                         [%{"tuple" => [":versions", [":tlsv1", ":tlsv1.1", ":tlsv1.2"]]}] +                       ] +                     } +                   ], +                   "db" => [":ssl_options"] +                 } +               ], +               "need_reboot" => false +             } + +      assert Application.get_env(:pleroma, :key1) == [ +               ssl_options: [versions: [:tlsv1, :"tlsv1.1", :"tlsv1.2"]] +             ] +    end + +    test "saving full setting if value is in full_key_update list", %{conn: conn} do +      backends = Application.get_env(:logger, :backends) +      on_exit(fn -> Application.put_env(:logger, :backends, backends) end) + +      insert(:config, +        group: :logger, +        key: :backends, +        value: [] +      ) + +      Pleroma.Config.TransferTask.load_and_update_env([], false) + +      assert Application.get_env(:logger, :backends) == [] + +      conn = +        conn +        |> put_req_header("content-type", "application/json") +        |> post("/api/pleroma/admin/config", %{ +          configs: [ +            %{ +              group: ":logger", +              key: ":backends", +              value: [":console"] +            } +          ] +        }) + +      assert json_response_and_validate_schema(conn, 200) == %{ +               "configs" => [ +                 %{ +                   "group" => ":logger", +                   "key" => ":backends", +                   "value" => [ +                     ":console" +                   ], +                   "db" => [":backends"] +                 } +               ], +               "need_reboot" => false +             } + +      assert Application.get_env(:logger, :backends) == [ +               :console +             ] +    end + +    test "saving full setting if value is not keyword", %{conn: conn} do +      insert(:config, +        group: :tesla, +        key: :adapter, +        value: Tesla.Adapter.Hackey +      ) + +      conn = +        conn +        |> put_req_header("content-type", "application/json") +        |> post("/api/pleroma/admin/config", %{ +          configs: [ +            %{group: ":tesla", key: ":adapter", value: "Tesla.Adapter.Httpc"} +          ] +        }) + +      assert json_response_and_validate_schema(conn, 200) == %{ +               "configs" => [ +                 %{ +                   "group" => ":tesla", +                   "key" => ":adapter", +                   "value" => "Tesla.Adapter.Httpc", +                   "db" => [":adapter"] +                 } +               ], +               "need_reboot" => false +             } +    end + +    test "update config setting & delete with fallback to default value", %{ +      conn: conn, +      admin: admin, +      token: token +    } do +      ueberauth = Application.get_env(:ueberauth, Ueberauth) +      insert(:config, key: :keyaa1) +      insert(:config, key: :keyaa2) + +      config3 = +        insert(:config, +          group: :ueberauth, +          key: Ueberauth +        ) + +      conn = +        conn +        |> put_req_header("content-type", "application/json") +        |> post("/api/pleroma/admin/config", %{ +          configs: [ +            %{group: ":pleroma", key: ":keyaa1", value: "another_value"}, +            %{group: ":pleroma", key: ":keyaa2", value: "another_value"} +          ] +        }) + +      assert json_response_and_validate_schema(conn, 200) == %{ +               "configs" => [ +                 %{ +                   "group" => ":pleroma", +                   "key" => ":keyaa1", +                   "value" => "another_value", +                   "db" => [":keyaa1"] +                 }, +                 %{ +                   "group" => ":pleroma", +                   "key" => ":keyaa2", +                   "value" => "another_value", +                   "db" => [":keyaa2"] +                 } +               ], +               "need_reboot" => false +             } + +      assert Application.get_env(:pleroma, :keyaa1) == "another_value" +      assert Application.get_env(:pleroma, :keyaa2) == "another_value" +      assert Application.get_env(:ueberauth, Ueberauth) == config3.value + +      conn = +        build_conn() +        |> assign(:user, admin) +        |> assign(:token, token) +        |> put_req_header("content-type", "application/json") +        |> post("/api/pleroma/admin/config", %{ +          configs: [ +            %{group: ":pleroma", key: ":keyaa2", delete: true}, +            %{ +              group: ":ueberauth", +              key: "Ueberauth", +              delete: true +            } +          ] +        }) + +      assert json_response_and_validate_schema(conn, 200) == %{ +               "configs" => [], +               "need_reboot" => false +             } + +      assert Application.get_env(:ueberauth, Ueberauth) == ueberauth +      refute Keyword.has_key?(Application.get_all_env(:pleroma), :keyaa2) +    end + +    test "common config example", %{conn: conn} do +      conn = +        conn +        |> put_req_header("content-type", "application/json") +        |> post("/api/pleroma/admin/config", %{ +          configs: [ +            %{ +              "group" => ":pleroma", +              "key" => "Pleroma.Captcha.NotReal", +              "value" => [ +                %{"tuple" => [":enabled", false]}, +                %{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]}, +                %{"tuple" => [":seconds_valid", 60]}, +                %{"tuple" => [":path", ""]}, +                %{"tuple" => [":key1", nil]}, +                %{"tuple" => [":partial_chain", "&:hackney_connect.partial_chain/1"]}, +                %{"tuple" => [":regex1", "~r/https:\/\/example.com/"]}, +                %{"tuple" => [":regex2", "~r/https:\/\/example.com/u"]}, +                %{"tuple" => [":regex3", "~r/https:\/\/example.com/i"]}, +                %{"tuple" => [":regex4", "~r/https:\/\/example.com/s"]}, +                %{"tuple" => [":name", "Pleroma"]} +              ] +            } +          ] +        }) + +      assert Config.get([Pleroma.Captcha.NotReal, :name]) == "Pleroma" + +      assert json_response_and_validate_schema(conn, 200) == %{ +               "configs" => [ +                 %{ +                   "group" => ":pleroma", +                   "key" => "Pleroma.Captcha.NotReal", +                   "value" => [ +                     %{"tuple" => [":enabled", false]}, +                     %{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]}, +                     %{"tuple" => [":seconds_valid", 60]}, +                     %{"tuple" => [":path", ""]}, +                     %{"tuple" => [":key1", nil]}, +                     %{"tuple" => [":partial_chain", "&:hackney_connect.partial_chain/1"]}, +                     %{"tuple" => [":regex1", "~r/https:\\/\\/example.com/"]}, +                     %{"tuple" => [":regex2", "~r/https:\\/\\/example.com/u"]}, +                     %{"tuple" => [":regex3", "~r/https:\\/\\/example.com/i"]}, +                     %{"tuple" => [":regex4", "~r/https:\\/\\/example.com/s"]}, +                     %{"tuple" => [":name", "Pleroma"]} +                   ], +                   "db" => [ +                     ":enabled", +                     ":method", +                     ":seconds_valid", +                     ":path", +                     ":key1", +                     ":partial_chain", +                     ":regex1", +                     ":regex2", +                     ":regex3", +                     ":regex4", +                     ":name" +                   ] +                 } +               ], +               "need_reboot" => false +             } +    end + +    test "tuples with more than two values", %{conn: conn} do +      conn = +        conn +        |> put_req_header("content-type", "application/json") +        |> post("/api/pleroma/admin/config", %{ +          configs: [ +            %{ +              "group" => ":pleroma", +              "key" => "Pleroma.Web.Endpoint.NotReal", +              "value" => [ +                %{ +                  "tuple" => [ +                    ":http", +                    [ +                      %{ +                        "tuple" => [ +                          ":key2", +                          [ +                            %{ +                              "tuple" => [ +                                ":_", +                                [ +                                  %{ +                                    "tuple" => [ +                                      "/api/v1/streaming", +                                      "Pleroma.Web.MastodonAPI.WebsocketHandler", +                                      [] +                                    ] +                                  }, +                                  %{ +                                    "tuple" => [ +                                      "/websocket", +                                      "Phoenix.Endpoint.CowboyWebSocket", +                                      %{ +                                        "tuple" => [ +                                          "Phoenix.Transports.WebSocket", +                                          %{ +                                            "tuple" => [ +                                              "Pleroma.Web.Endpoint", +                                              "Pleroma.Web.UserSocket", +                                              [] +                                            ] +                                          } +                                        ] +                                      } +                                    ] +                                  }, +                                  %{ +                                    "tuple" => [ +                                      ":_", +                                      "Phoenix.Endpoint.Cowboy2Handler", +                                      %{"tuple" => ["Pleroma.Web.Endpoint", []]} +                                    ] +                                  } +                                ] +                              ] +                            } +                          ] +                        ] +                      } +                    ] +                  ] +                } +              ] +            } +          ] +        }) + +      assert json_response_and_validate_schema(conn, 200) == %{ +               "configs" => [ +                 %{ +                   "group" => ":pleroma", +                   "key" => "Pleroma.Web.Endpoint.NotReal", +                   "value" => [ +                     %{ +                       "tuple" => [ +                         ":http", +                         [ +                           %{ +                             "tuple" => [ +                               ":key2", +                               [ +                                 %{ +                                   "tuple" => [ +                                     ":_", +                                     [ +                                       %{ +                                         "tuple" => [ +                                           "/api/v1/streaming", +                                           "Pleroma.Web.MastodonAPI.WebsocketHandler", +                                           [] +                                         ] +                                       }, +                                       %{ +                                         "tuple" => [ +                                           "/websocket", +                                           "Phoenix.Endpoint.CowboyWebSocket", +                                           %{ +                                             "tuple" => [ +                                               "Phoenix.Transports.WebSocket", +                                               %{ +                                                 "tuple" => [ +                                                   "Pleroma.Web.Endpoint", +                                                   "Pleroma.Web.UserSocket", +                                                   [] +                                                 ] +                                               } +                                             ] +                                           } +                                         ] +                                       }, +                                       %{ +                                         "tuple" => [ +                                           ":_", +                                           "Phoenix.Endpoint.Cowboy2Handler", +                                           %{"tuple" => ["Pleroma.Web.Endpoint", []]} +                                         ] +                                       } +                                     ] +                                   ] +                                 } +                               ] +                             ] +                           } +                         ] +                       ] +                     } +                   ], +                   "db" => [":http"] +                 } +               ], +               "need_reboot" => false +             } +    end + +    test "settings with nesting map", %{conn: conn} do +      conn = +        conn +        |> put_req_header("content-type", "application/json") +        |> post("/api/pleroma/admin/config", %{ +          configs: [ +            %{ +              "group" => ":pleroma", +              "key" => ":key1", +              "value" => [ +                %{"tuple" => [":key2", "some_val"]}, +                %{ +                  "tuple" => [ +                    ":key3", +                    %{ +                      ":max_options" => 20, +                      ":max_option_chars" => 200, +                      ":min_expiration" => 0, +                      ":max_expiration" => 31_536_000, +                      "nested" => %{ +                        ":max_options" => 20, +                        ":max_option_chars" => 200, +                        ":min_expiration" => 0, +                        ":max_expiration" => 31_536_000 +                      } +                    } +                  ] +                } +              ] +            } +          ] +        }) + +      assert json_response_and_validate_schema(conn, 200) == +               %{ +                 "configs" => [ +                   %{ +                     "group" => ":pleroma", +                     "key" => ":key1", +                     "value" => [ +                       %{"tuple" => [":key2", "some_val"]}, +                       %{ +                         "tuple" => [ +                           ":key3", +                           %{ +                             ":max_expiration" => 31_536_000, +                             ":max_option_chars" => 200, +                             ":max_options" => 20, +                             ":min_expiration" => 0, +                             "nested" => %{ +                               ":max_expiration" => 31_536_000, +                               ":max_option_chars" => 200, +                               ":max_options" => 20, +                               ":min_expiration" => 0 +                             } +                           } +                         ] +                       } +                     ], +                     "db" => [":key2", ":key3"] +                   } +                 ], +                 "need_reboot" => false +               } +    end + +    test "value as map", %{conn: conn} do +      conn = +        conn +        |> put_req_header("content-type", "application/json") +        |> post("/api/pleroma/admin/config", %{ +          configs: [ +            %{ +              "group" => ":pleroma", +              "key" => ":key1", +              "value" => %{"key" => "some_val"} +            } +          ] +        }) + +      assert json_response_and_validate_schema(conn, 200) == +               %{ +                 "configs" => [ +                   %{ +                     "group" => ":pleroma", +                     "key" => ":key1", +                     "value" => %{"key" => "some_val"}, +                     "db" => [":key1"] +                   } +                 ], +                 "need_reboot" => false +               } +    end + +    test "queues key as atom", %{conn: conn} do +      conn = +        conn +        |> put_req_header("content-type", "application/json") +        |> post("/api/pleroma/admin/config", %{ +          configs: [ +            %{ +              "group" => ":oban", +              "key" => ":queues", +              "value" => [ +                %{"tuple" => [":federator_incoming", 50]}, +                %{"tuple" => [":federator_outgoing", 50]}, +                %{"tuple" => [":web_push", 50]}, +                %{"tuple" => [":mailer", 10]}, +                %{"tuple" => [":transmogrifier", 20]}, +                %{"tuple" => [":scheduled_activities", 10]}, +                %{"tuple" => [":background", 5]} +              ] +            } +          ] +        }) + +      assert json_response_and_validate_schema(conn, 200) == %{ +               "configs" => [ +                 %{ +                   "group" => ":oban", +                   "key" => ":queues", +                   "value" => [ +                     %{"tuple" => [":federator_incoming", 50]}, +                     %{"tuple" => [":federator_outgoing", 50]}, +                     %{"tuple" => [":web_push", 50]}, +                     %{"tuple" => [":mailer", 10]}, +                     %{"tuple" => [":transmogrifier", 20]}, +                     %{"tuple" => [":scheduled_activities", 10]}, +                     %{"tuple" => [":background", 5]} +                   ], +                   "db" => [ +                     ":federator_incoming", +                     ":federator_outgoing", +                     ":web_push", +                     ":mailer", +                     ":transmogrifier", +                     ":scheduled_activities", +                     ":background" +                   ] +                 } +               ], +               "need_reboot" => false +             } +    end + +    test "delete part of settings by atom subkeys", %{conn: conn} do +      insert(:config, +        key: :keyaa1, +        value: [subkey1: "val1", subkey2: "val2", subkey3: "val3"] +      ) + +      conn = +        conn +        |> put_req_header("content-type", "application/json") +        |> post("/api/pleroma/admin/config", %{ +          configs: [ +            %{ +              group: ":pleroma", +              key: ":keyaa1", +              subkeys: [":subkey1", ":subkey3"], +              delete: true +            } +          ] +        }) + +      assert json_response_and_validate_schema(conn, 200) == %{ +               "configs" => [ +                 %{ +                   "group" => ":pleroma", +                   "key" => ":keyaa1", +                   "value" => [%{"tuple" => [":subkey2", "val2"]}], +                   "db" => [":subkey2"] +                 } +               ], +               "need_reboot" => false +             } +    end + +    test "proxy tuple localhost", %{conn: conn} do +      conn = +        conn +        |> put_req_header("content-type", "application/json") +        |> post("/api/pleroma/admin/config", %{ +          configs: [ +            %{ +              group: ":pleroma", +              key: ":http", +              value: [ +                %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}]} +              ] +            } +          ] +        }) + +      assert %{ +               "configs" => [ +                 %{ +                   "group" => ":pleroma", +                   "key" => ":http", +                   "value" => value, +                   "db" => db +                 } +               ] +             } = json_response_and_validate_schema(conn, 200) + +      assert %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}]} in value +      assert ":proxy_url" in db +    end + +    test "proxy tuple domain", %{conn: conn} do +      conn = +        conn +        |> put_req_header("content-type", "application/json") +        |> post("/api/pleroma/admin/config", %{ +          configs: [ +            %{ +              group: ":pleroma", +              key: ":http", +              value: [ +                %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}]} +              ] +            } +          ] +        }) + +      assert %{ +               "configs" => [ +                 %{ +                   "group" => ":pleroma", +                   "key" => ":http", +                   "value" => value, +                   "db" => db +                 } +               ] +             } = json_response_and_validate_schema(conn, 200) + +      assert %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}]} in value +      assert ":proxy_url" in db +    end + +    test "proxy tuple ip", %{conn: conn} do +      conn = +        conn +        |> put_req_header("content-type", "application/json") +        |> post("/api/pleroma/admin/config", %{ +          configs: [ +            %{ +              group: ":pleroma", +              key: ":http", +              value: [ +                %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}]} +              ] +            } +          ] +        }) + +      assert %{ +               "configs" => [ +                 %{ +                   "group" => ":pleroma", +                   "key" => ":http", +                   "value" => value, +                   "db" => db +                 } +               ] +             } = json_response_and_validate_schema(conn, 200) + +      assert %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}]} in value +      assert ":proxy_url" in db +    end + +    @tag capture_log: true +    test "doesn't set keys not in the whitelist", %{conn: conn} do +      clear_config(:database_config_whitelist, [ +        {:pleroma, :key1}, +        {:pleroma, :key2}, +        {:pleroma, Pleroma.Captcha.NotReal}, +        {:not_real} +      ]) + +      conn +      |> put_req_header("content-type", "application/json") +      |> post("/api/pleroma/admin/config", %{ +        configs: [ +          %{group: ":pleroma", key: ":key1", value: "value1"}, +          %{group: ":pleroma", key: ":key2", value: "value2"}, +          %{group: ":pleroma", key: ":key3", value: "value3"}, +          %{group: ":pleroma", key: "Pleroma.Web.Endpoint.NotReal", value: "value4"}, +          %{group: ":pleroma", key: "Pleroma.Captcha.NotReal", value: "value5"}, +          %{group: ":not_real", key: ":anything", value: "value6"} +        ] +      }) + +      assert Application.get_env(:pleroma, :key1) == "value1" +      assert Application.get_env(:pleroma, :key2) == "value2" +      assert Application.get_env(:pleroma, :key3) == nil +      assert Application.get_env(:pleroma, Pleroma.Web.Endpoint.NotReal) == nil +      assert Application.get_env(:pleroma, Pleroma.Captcha.NotReal) == "value5" +      assert Application.get_env(:not_real, :anything) == "value6" +    end + +    test "args for Pleroma.Upload.Filter.Mogrify with custom tuples", %{conn: conn} do +      clear_config(Pleroma.Upload.Filter.Mogrify) + +      assert conn +             |> put_req_header("content-type", "application/json") +             |> post("/api/pleroma/admin/config", %{ +               configs: [ +                 %{ +                   group: ":pleroma", +                   key: "Pleroma.Upload.Filter.Mogrify", +                   value: [ +                     %{"tuple" => [":args", ["auto-orient", "strip"]]} +                   ] +                 } +               ] +             }) +             |> json_response_and_validate_schema(200) == %{ +               "configs" => [ +                 %{ +                   "group" => ":pleroma", +                   "key" => "Pleroma.Upload.Filter.Mogrify", +                   "value" => [ +                     %{"tuple" => [":args", ["auto-orient", "strip"]]} +                   ], +                   "db" => [":args"] +                 } +               ], +               "need_reboot" => false +             } + +      assert Config.get(Pleroma.Upload.Filter.Mogrify) == [args: ["auto-orient", "strip"]] + +      assert conn +             |> put_req_header("content-type", "application/json") +             |> post("/api/pleroma/admin/config", %{ +               configs: [ +                 %{ +                   group: ":pleroma", +                   key: "Pleroma.Upload.Filter.Mogrify", +                   value: [ +                     %{ +                       "tuple" => [ +                         ":args", +                         [ +                           "auto-orient", +                           "strip", +                           "{\"implode\", \"1\"}", +                           "{\"resize\", \"3840x1080>\"}" +                         ] +                       ] +                     } +                   ] +                 } +               ] +             }) +             |> json_response(200) == %{ +               "configs" => [ +                 %{ +                   "group" => ":pleroma", +                   "key" => "Pleroma.Upload.Filter.Mogrify", +                   "value" => [ +                     %{ +                       "tuple" => [ +                         ":args", +                         [ +                           "auto-orient", +                           "strip", +                           "{\"implode\", \"1\"}", +                           "{\"resize\", \"3840x1080>\"}" +                         ] +                       ] +                     } +                   ], +                   "db" => [":args"] +                 } +               ], +               "need_reboot" => false +             } + +      assert Config.get(Pleroma.Upload.Filter.Mogrify) == [ +               args: ["auto-orient", "strip", {"implode", "1"}, {"resize", "3840x1080>"}] +             ] +    end +  end + +  describe "GET /api/pleroma/admin/config/descriptions" do +    test "structure", %{conn: conn} do +      admin = insert(:user, is_admin: true) + +      conn = +        assign(conn, :user, admin) +        |> get("/api/pleroma/admin/config/descriptions") + +      assert [child | _others] = json_response_and_validate_schema(conn, 200) + +      assert child["children"] +      assert child["key"] +      assert String.starts_with?(child["group"], ":") +      assert child["description"] +    end + +    test "filters by database configuration whitelist", %{conn: conn} do +      clear_config(:database_config_whitelist, [ +        {:pleroma, :instance}, +        {:pleroma, :activitypub}, +        {:pleroma, Pleroma.Upload}, +        {:esshd} +      ]) + +      admin = insert(:user, is_admin: true) + +      conn = +        assign(conn, :user, admin) +        |> get("/api/pleroma/admin/config/descriptions") + +      children = json_response_and_validate_schema(conn, 200) + +      assert length(children) == 4 + +      assert Enum.count(children, fn c -> c["group"] == ":pleroma" end) == 3 + +      instance = Enum.find(children, fn c -> c["key"] == ":instance" end) +      assert instance["children"] + +      activitypub = Enum.find(children, fn c -> c["key"] == ":activitypub" end) +      assert activitypub["children"] + +      web_endpoint = Enum.find(children, fn c -> c["key"] == "Pleroma.Upload" end) +      assert web_endpoint["children"] + +      esshd = Enum.find(children, fn c -> c["group"] == ":esshd" end) +      assert esshd["children"] +    end +  end +end diff --git a/test/web/admin_api/controllers/invite_controller_test.exs b/test/web/admin_api/controllers/invite_controller_test.exs new file mode 100644 index 000000000..ab186c5e7 --- /dev/null +++ b/test/web/admin_api/controllers/invite_controller_test.exs @@ -0,0 +1,281 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.AdminAPI.InviteControllerTest do +  use Pleroma.Web.ConnCase, async: true + +  import Pleroma.Factory + +  alias Pleroma.Config +  alias Pleroma.Repo +  alias Pleroma.UserInviteToken + +  setup do +    admin = insert(:user, is_admin: true) +    token = insert(:oauth_admin_token, user: admin) + +    conn = +      build_conn() +      |> assign(:user, admin) +      |> assign(:token, token) + +    {:ok, %{admin: admin, token: token, conn: conn}} +  end + +  describe "POST /api/pleroma/admin/users/email_invite, with valid config" do +    setup do: clear_config([:instance, :registrations_open], false) +    setup do: clear_config([:instance, :invites_enabled], true) + +    test "sends invitation and returns 204", %{admin: admin, conn: conn} do +      recipient_email = "foo@bar.com" +      recipient_name = "J. D." + +      conn = +        conn +        |> put_req_header("content-type", "application/json;charset=utf-8") +        |> post("/api/pleroma/admin/users/email_invite", %{ +          email: recipient_email, +          name: recipient_name +        }) + +      assert json_response_and_validate_schema(conn, :no_content) + +      token_record = List.last(Repo.all(Pleroma.UserInviteToken)) +      assert token_record +      refute token_record.used + +      notify_email = Config.get([:instance, :notify_email]) +      instance_name = Config.get([:instance, :name]) + +      email = +        Pleroma.Emails.UserEmail.user_invitation_email( +          admin, +          token_record, +          recipient_email, +          recipient_name +        ) + +      Swoosh.TestAssertions.assert_email_sent( +        from: {instance_name, notify_email}, +        to: {recipient_name, recipient_email}, +        html_body: email.html_body +      ) +    end + +    test "it returns 403 if requested by a non-admin" do +      non_admin_user = insert(:user) +      token = insert(:oauth_token, user: non_admin_user) + +      conn = +        build_conn() +        |> assign(:user, non_admin_user) +        |> assign(:token, token) +        |> put_req_header("content-type", "application/json;charset=utf-8") +        |> post("/api/pleroma/admin/users/email_invite", %{ +          email: "foo@bar.com", +          name: "JD" +        }) + +      assert json_response(conn, :forbidden) +    end + +    test "email with +", %{conn: conn, admin: admin} do +      recipient_email = "foo+bar@baz.com" + +      conn +      |> put_req_header("content-type", "application/json;charset=utf-8") +      |> post("/api/pleroma/admin/users/email_invite", %{email: recipient_email}) +      |> json_response_and_validate_schema(:no_content) + +      token_record = +        Pleroma.UserInviteToken +        |> Repo.all() +        |> List.last() + +      assert token_record +      refute token_record.used + +      notify_email = Config.get([:instance, :notify_email]) +      instance_name = Config.get([:instance, :name]) + +      email = +        Pleroma.Emails.UserEmail.user_invitation_email( +          admin, +          token_record, +          recipient_email +        ) + +      Swoosh.TestAssertions.assert_email_sent( +        from: {instance_name, notify_email}, +        to: recipient_email, +        html_body: email.html_body +      ) +    end +  end + +  describe "POST /api/pleroma/admin/users/email_invite, with invalid config" do +    setup do: clear_config([:instance, :registrations_open]) +    setup do: clear_config([:instance, :invites_enabled]) + +    test "it returns 500 if `invites_enabled` is not enabled", %{conn: conn} do +      Config.put([:instance, :registrations_open], false) +      Config.put([:instance, :invites_enabled], false) + +      conn = +        conn +        |> put_req_header("content-type", "application/json") +        |> post("/api/pleroma/admin/users/email_invite", %{ +          email: "foo@bar.com", +          name: "JD" +        }) + +      assert json_response_and_validate_schema(conn, :bad_request) == +               %{ +                 "error" => +                   "To send invites you need to set the `invites_enabled` option to true." +               } +    end + +    test "it returns 500 if `registrations_open` is enabled", %{conn: conn} do +      Config.put([:instance, :registrations_open], true) +      Config.put([:instance, :invites_enabled], true) + +      conn = +        conn +        |> put_req_header("content-type", "application/json") +        |> post("/api/pleroma/admin/users/email_invite", %{ +          email: "foo@bar.com", +          name: "JD" +        }) + +      assert json_response_and_validate_schema(conn, :bad_request) == +               %{ +                 "error" => +                   "To send invites you need to set the `registrations_open` option to false." +               } +    end +  end + +  describe "POST /api/pleroma/admin/users/invite_token" do +    test "without options", %{conn: conn} do +      conn = +        conn +        |> put_req_header("content-type", "application/json") +        |> post("/api/pleroma/admin/users/invite_token") + +      invite_json = json_response_and_validate_schema(conn, 200) +      invite = UserInviteToken.find_by_token!(invite_json["token"]) +      refute invite.used +      refute invite.expires_at +      refute invite.max_use +      assert invite.invite_type == "one_time" +    end + +    test "with expires_at", %{conn: conn} do +      conn = +        conn +        |> put_req_header("content-type", "application/json") +        |> post("/api/pleroma/admin/users/invite_token", %{ +          "expires_at" => Date.to_string(Date.utc_today()) +        }) + +      invite_json = json_response_and_validate_schema(conn, 200) +      invite = UserInviteToken.find_by_token!(invite_json["token"]) + +      refute invite.used +      assert invite.expires_at == Date.utc_today() +      refute invite.max_use +      assert invite.invite_type == "date_limited" +    end + +    test "with max_use", %{conn: conn} do +      conn = +        conn +        |> put_req_header("content-type", "application/json") +        |> post("/api/pleroma/admin/users/invite_token", %{"max_use" => 150}) + +      invite_json = json_response_and_validate_schema(conn, 200) +      invite = UserInviteToken.find_by_token!(invite_json["token"]) +      refute invite.used +      refute invite.expires_at +      assert invite.max_use == 150 +      assert invite.invite_type == "reusable" +    end + +    test "with max use and expires_at", %{conn: conn} do +      conn = +        conn +        |> put_req_header("content-type", "application/json") +        |> post("/api/pleroma/admin/users/invite_token", %{ +          "max_use" => 150, +          "expires_at" => Date.to_string(Date.utc_today()) +        }) + +      invite_json = json_response_and_validate_schema(conn, 200) +      invite = UserInviteToken.find_by_token!(invite_json["token"]) +      refute invite.used +      assert invite.expires_at == Date.utc_today() +      assert invite.max_use == 150 +      assert invite.invite_type == "reusable_date_limited" +    end +  end + +  describe "GET /api/pleroma/admin/users/invites" do +    test "no invites", %{conn: conn} do +      conn = get(conn, "/api/pleroma/admin/users/invites") + +      assert json_response_and_validate_schema(conn, 200) == %{"invites" => []} +    end + +    test "with invite", %{conn: conn} do +      {:ok, invite} = UserInviteToken.create_invite() + +      conn = get(conn, "/api/pleroma/admin/users/invites") + +      assert json_response_and_validate_schema(conn, 200) == %{ +               "invites" => [ +                 %{ +                   "expires_at" => nil, +                   "id" => invite.id, +                   "invite_type" => "one_time", +                   "max_use" => nil, +                   "token" => invite.token, +                   "used" => false, +                   "uses" => 0 +                 } +               ] +             } +    end +  end + +  describe "POST /api/pleroma/admin/users/revoke_invite" do +    test "with token", %{conn: conn} do +      {:ok, invite} = UserInviteToken.create_invite() + +      conn = +        conn +        |> put_req_header("content-type", "application/json") +        |> post("/api/pleroma/admin/users/revoke_invite", %{"token" => invite.token}) + +      assert json_response_and_validate_schema(conn, 200) == %{ +               "expires_at" => nil, +               "id" => invite.id, +               "invite_type" => "one_time", +               "max_use" => nil, +               "token" => invite.token, +               "used" => true, +               "uses" => 0 +             } +    end + +    test "with invalid token", %{conn: conn} do +      conn = +        conn +        |> put_req_header("content-type", "application/json") +        |> post("/api/pleroma/admin/users/revoke_invite", %{"token" => "foo"}) + +      assert json_response_and_validate_schema(conn, :not_found) == %{"error" => "Not found"} +    end +  end +end diff --git a/test/web/admin_api/controllers/media_proxy_cache_controller_test.exs b/test/web/admin_api/controllers/media_proxy_cache_controller_test.exs new file mode 100644 index 000000000..5ab6cb78a --- /dev/null +++ b/test/web/admin_api/controllers/media_proxy_cache_controller_test.exs @@ -0,0 +1,145 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.AdminAPI.MediaProxyCacheControllerTest do +  use Pleroma.Web.ConnCase + +  import Pleroma.Factory +  import Mock + +  alias Pleroma.Web.MediaProxy + +  setup do: clear_config([:media_proxy]) + +  setup do +    on_exit(fn -> Cachex.clear(:banned_urls_cache) end) +  end + +  setup do +    admin = insert(:user, is_admin: true) +    token = insert(:oauth_admin_token, user: admin) + +    conn = +      build_conn() +      |> assign(:user, admin) +      |> assign(:token, token) + +    Config.put([:media_proxy, :enabled], true) +    Config.put([:media_proxy, :invalidation, :enabled], true) +    Config.put([:media_proxy, :invalidation, :provider], MediaProxy.Invalidation.Script) + +    {:ok, %{admin: admin, token: token, conn: conn}} +  end + +  describe "GET /api/pleroma/admin/media_proxy_caches" do +    test "shows banned MediaProxy URLs", %{conn: conn} do +      MediaProxy.put_in_banned_urls([ +        "http://localhost:4001/media/a688346.jpg", +        "http://localhost:4001/media/fb1f4d.jpg" +      ]) + +      MediaProxy.put_in_banned_urls("http://localhost:4001/media/gb1f44.jpg") +      MediaProxy.put_in_banned_urls("http://localhost:4001/media/tb13f47.jpg") +      MediaProxy.put_in_banned_urls("http://localhost:4001/media/wb1f46.jpg") + +      response = +        conn +        |> get("/api/pleroma/admin/media_proxy_caches?page_size=2") +        |> json_response_and_validate_schema(200) + +      assert response["urls"] == [ +               "http://localhost:4001/media/fb1f4d.jpg", +               "http://localhost:4001/media/a688346.jpg" +             ] + +      response = +        conn +        |> get("/api/pleroma/admin/media_proxy_caches?page_size=2&page=2") +        |> json_response_and_validate_schema(200) + +      assert response["urls"] == [ +               "http://localhost:4001/media/gb1f44.jpg", +               "http://localhost:4001/media/tb13f47.jpg" +             ] + +      response = +        conn +        |> get("/api/pleroma/admin/media_proxy_caches?page_size=2&page=3") +        |> json_response_and_validate_schema(200) + +      assert response["urls"] == ["http://localhost:4001/media/wb1f46.jpg"] +    end +  end + +  describe "POST /api/pleroma/admin/media_proxy_caches/delete" do +    test "deleted MediaProxy URLs from banned", %{conn: conn} do +      MediaProxy.put_in_banned_urls([ +        "http://localhost:4001/media/a688346.jpg", +        "http://localhost:4001/media/fb1f4d.jpg" +      ]) + +      response = +        conn +        |> put_req_header("content-type", "application/json") +        |> post("/api/pleroma/admin/media_proxy_caches/delete", %{ +          urls: ["http://localhost:4001/media/a688346.jpg"] +        }) +        |> json_response_and_validate_schema(200) + +      assert response["urls"] == ["http://localhost:4001/media/a688346.jpg"] +      refute MediaProxy.in_banned_urls("http://localhost:4001/media/a688346.jpg") +      assert MediaProxy.in_banned_urls("http://localhost:4001/media/fb1f4d.jpg") +    end +  end + +  describe "POST /api/pleroma/admin/media_proxy_caches/purge" do +    test "perform invalidates cache of MediaProxy", %{conn: conn} do +      urls = [ +        "http://example.com/media/a688346.jpg", +        "http://example.com/media/fb1f4d.jpg" +      ] + +      with_mocks [ +        {MediaProxy.Invalidation.Script, [], +         [ +           purge: fn _, _ -> {"ok", 0} end +         ]} +      ] do +        response = +          conn +          |> put_req_header("content-type", "application/json") +          |> post("/api/pleroma/admin/media_proxy_caches/purge", %{urls: urls, ban: false}) +          |> json_response_and_validate_schema(200) + +        assert response["urls"] == urls + +        refute MediaProxy.in_banned_urls("http://example.com/media/a688346.jpg") +        refute MediaProxy.in_banned_urls("http://example.com/media/fb1f4d.jpg") +      end +    end + +    test "perform invalidates cache of MediaProxy and adds url to banned", %{conn: conn} do +      urls = [ +        "http://example.com/media/a688346.jpg", +        "http://example.com/media/fb1f4d.jpg" +      ] + +      with_mocks [{MediaProxy.Invalidation.Script, [], [purge: fn _, _ -> {"ok", 0} end]}] do +        response = +          conn +          |> put_req_header("content-type", "application/json") +          |> post("/api/pleroma/admin/media_proxy_caches/purge", %{ +            urls: urls, +            ban: true +          }) +          |> json_response_and_validate_schema(200) + +        assert response["urls"] == urls + +        assert MediaProxy.in_banned_urls("http://example.com/media/a688346.jpg") +        assert MediaProxy.in_banned_urls("http://example.com/media/fb1f4d.jpg") +      end +    end +  end +end diff --git a/test/web/admin_api/controllers/oauth_app_controller_test.exs b/test/web/admin_api/controllers/oauth_app_controller_test.exs new file mode 100644 index 000000000..ed7c4172c --- /dev/null +++ b/test/web/admin_api/controllers/oauth_app_controller_test.exs @@ -0,0 +1,220 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.AdminAPI.OAuthAppControllerTest do +  use Pleroma.Web.ConnCase, async: true +  use Oban.Testing, repo: Pleroma.Repo + +  import Pleroma.Factory + +  alias Pleroma.Config +  alias Pleroma.Web + +  setup do +    admin = insert(:user, is_admin: true) +    token = insert(:oauth_admin_token, user: admin) + +    conn = +      build_conn() +      |> assign(:user, admin) +      |> assign(:token, token) + +    {:ok, %{admin: admin, token: token, conn: conn}} +  end + +  describe "POST /api/pleroma/admin/oauth_app" do +    test "errors", %{conn: conn} do +      response = +        conn +        |> put_req_header("content-type", "application/json") +        |> post("/api/pleroma/admin/oauth_app", %{}) +        |> json_response_and_validate_schema(400) + +      assert %{ +               "error" => "Missing field: name. Missing field: redirect_uris." +             } = response +    end + +    test "success", %{conn: conn} do +      base_url = Web.base_url() +      app_name = "Trusted app" + +      response = +        conn +        |> put_req_header("content-type", "application/json") +        |> post("/api/pleroma/admin/oauth_app", %{ +          name: app_name, +          redirect_uris: base_url +        }) +        |> json_response_and_validate_schema(200) + +      assert %{ +               "client_id" => _, +               "client_secret" => _, +               "name" => ^app_name, +               "redirect_uri" => ^base_url, +               "trusted" => false +             } = response +    end + +    test "with trusted", %{conn: conn} do +      base_url = Web.base_url() +      app_name = "Trusted app" + +      response = +        conn +        |> put_req_header("content-type", "application/json") +        |> post("/api/pleroma/admin/oauth_app", %{ +          name: app_name, +          redirect_uris: base_url, +          trusted: true +        }) +        |> json_response_and_validate_schema(200) + +      assert %{ +               "client_id" => _, +               "client_secret" => _, +               "name" => ^app_name, +               "redirect_uri" => ^base_url, +               "trusted" => true +             } = response +    end +  end + +  describe "GET /api/pleroma/admin/oauth_app" do +    setup do +      app = insert(:oauth_app) +      {:ok, app: app} +    end + +    test "list", %{conn: conn} do +      response = +        conn +        |> get("/api/pleroma/admin/oauth_app") +        |> json_response_and_validate_schema(200) + +      assert %{"apps" => apps, "count" => count, "page_size" => _} = response + +      assert length(apps) == count +    end + +    test "with page size", %{conn: conn} do +      insert(:oauth_app) +      page_size = 1 + +      response = +        conn +        |> get("/api/pleroma/admin/oauth_app?page_size=#{page_size}") +        |> json_response_and_validate_schema(200) + +      assert %{"apps" => apps, "count" => _, "page_size" => ^page_size} = response + +      assert length(apps) == page_size +    end + +    test "search by client name", %{conn: conn, app: app} do +      response = +        conn +        |> get("/api/pleroma/admin/oauth_app?name=#{app.client_name}") +        |> json_response_and_validate_schema(200) + +      assert %{"apps" => [returned], "count" => _, "page_size" => _} = response + +      assert returned["client_id"] == app.client_id +      assert returned["name"] == app.client_name +    end + +    test "search by client id", %{conn: conn, app: app} do +      response = +        conn +        |> get("/api/pleroma/admin/oauth_app?client_id=#{app.client_id}") +        |> json_response_and_validate_schema(200) + +      assert %{"apps" => [returned], "count" => _, "page_size" => _} = response + +      assert returned["client_id"] == app.client_id +      assert returned["name"] == app.client_name +    end + +    test "only trusted", %{conn: conn} do +      app = insert(:oauth_app, trusted: true) + +      response = +        conn +        |> get("/api/pleroma/admin/oauth_app?trusted=true") +        |> json_response_and_validate_schema(200) + +      assert %{"apps" => [returned], "count" => _, "page_size" => _} = response + +      assert returned["client_id"] == app.client_id +      assert returned["name"] == app.client_name +    end +  end + +  describe "DELETE /api/pleroma/admin/oauth_app/:id" do +    test "with id", %{conn: conn} do +      app = insert(:oauth_app) + +      response = +        conn +        |> delete("/api/pleroma/admin/oauth_app/" <> to_string(app.id)) +        |> json_response_and_validate_schema(:no_content) + +      assert response == "" +    end + +    test "with non existance id", %{conn: conn} do +      response = +        conn +        |> delete("/api/pleroma/admin/oauth_app/0") +        |> json_response_and_validate_schema(:bad_request) + +      assert response == "" +    end +  end + +  describe "PATCH /api/pleroma/admin/oauth_app/:id" do +    test "with id", %{conn: conn} do +      app = insert(:oauth_app) + +      name = "another name" +      url = "https://example.com" +      scopes = ["admin"] +      id = app.id +      website = "http://website.com" + +      response = +        conn +        |> put_req_header("content-type", "application/json") +        |> patch("/api/pleroma/admin/oauth_app/#{id}", %{ +          name: name, +          trusted: true, +          redirect_uris: url, +          scopes: scopes, +          website: website +        }) +        |> json_response_and_validate_schema(200) + +      assert %{ +               "client_id" => _, +               "client_secret" => _, +               "id" => ^id, +               "name" => ^name, +               "redirect_uri" => ^url, +               "trusted" => true, +               "website" => ^website +             } = response +    end + +    test "without id", %{conn: conn} do +      response = +        conn +        |> put_req_header("content-type", "application/json") +        |> patch("/api/pleroma/admin/oauth_app/0") +        |> json_response_and_validate_schema(:bad_request) + +      assert response == "" +    end +  end +end diff --git a/test/web/admin_api/controllers/relay_controller_test.exs b/test/web/admin_api/controllers/relay_controller_test.exs new file mode 100644 index 000000000..64086adc5 --- /dev/null +++ b/test/web/admin_api/controllers/relay_controller_test.exs @@ -0,0 +1,92 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.AdminAPI.RelayControllerTest do +  use Pleroma.Web.ConnCase + +  import Pleroma.Factory + +  alias Pleroma.Config +  alias Pleroma.ModerationLog +  alias Pleroma.Repo +  alias Pleroma.User + +  setup_all do +    Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) + +    :ok +  end + +  setup do +    admin = insert(:user, is_admin: true) +    token = insert(:oauth_admin_token, user: admin) + +    conn = +      build_conn() +      |> assign(:user, admin) +      |> assign(:token, token) + +    {:ok, %{admin: admin, token: token, conn: conn}} +  end + +  describe "relays" do +    test "POST /relay", %{conn: conn, admin: admin} do +      conn = +        conn +        |> put_req_header("content-type", "application/json") +        |> post("/api/pleroma/admin/relay", %{ +          relay_url: "http://mastodon.example.org/users/admin" +        }) + +      assert json_response_and_validate_schema(conn, 200) == +               "http://mastodon.example.org/users/admin" + +      log_entry = Repo.one(ModerationLog) + +      assert ModerationLog.get_log_entry_message(log_entry) == +               "@#{admin.nickname} followed relay: http://mastodon.example.org/users/admin" +    end + +    test "GET /relay", %{conn: conn} do +      relay_user = Pleroma.Web.ActivityPub.Relay.get_actor() + +      ["http://mastodon.example.org/users/admin", "https://mstdn.io/users/mayuutann"] +      |> Enum.each(fn ap_id -> +        {:ok, user} = User.get_or_fetch_by_ap_id(ap_id) +        User.follow(relay_user, user) +      end) + +      conn = get(conn, "/api/pleroma/admin/relay") + +      assert json_response_and_validate_schema(conn, 200)["relays"] -- +               ["mastodon.example.org", "mstdn.io"] == [] +    end + +    test "DELETE /relay", %{conn: conn, admin: admin} do +      conn +      |> put_req_header("content-type", "application/json") +      |> post("/api/pleroma/admin/relay", %{ +        relay_url: "http://mastodon.example.org/users/admin" +      }) + +      conn = +        conn +        |> put_req_header("content-type", "application/json") +        |> delete("/api/pleroma/admin/relay", %{ +          relay_url: "http://mastodon.example.org/users/admin" +        }) + +      assert json_response_and_validate_schema(conn, 200) == +               "http://mastodon.example.org/users/admin" + +      [log_entry_one, log_entry_two] = Repo.all(ModerationLog) + +      assert ModerationLog.get_log_entry_message(log_entry_one) == +               "@#{admin.nickname} followed relay: http://mastodon.example.org/users/admin" + +      assert ModerationLog.get_log_entry_message(log_entry_two) == +               "@#{admin.nickname} unfollowed relay: http://mastodon.example.org/users/admin" +    end +  end +end diff --git a/test/web/admin_api/controllers/report_controller_test.exs b/test/web/admin_api/controllers/report_controller_test.exs new file mode 100644 index 000000000..940bce340 --- /dev/null +++ b/test/web/admin_api/controllers/report_controller_test.exs @@ -0,0 +1,374 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.AdminAPI.ReportControllerTest do +  use Pleroma.Web.ConnCase + +  import Pleroma.Factory + +  alias Pleroma.Activity +  alias Pleroma.Config +  alias Pleroma.ModerationLog +  alias Pleroma.Repo +  alias Pleroma.ReportNote +  alias Pleroma.Web.CommonAPI + +  setup do +    admin = insert(:user, is_admin: true) +    token = insert(:oauth_admin_token, user: admin) + +    conn = +      build_conn() +      |> assign(:user, admin) +      |> assign(:token, token) + +    {:ok, %{admin: admin, token: token, conn: conn}} +  end + +  describe "GET /api/pleroma/admin/reports/:id" do +    test "returns report by its id", %{conn: conn} do +      [reporter, target_user] = insert_pair(:user) +      activity = insert(:note_activity, user: target_user) + +      {:ok, %{id: report_id}} = +        CommonAPI.report(reporter, %{ +          account_id: target_user.id, +          comment: "I feel offended", +          status_ids: [activity.id] +        }) + +      response = +        conn +        |> get("/api/pleroma/admin/reports/#{report_id}") +        |> json_response_and_validate_schema(:ok) + +      assert response["id"] == report_id +    end + +    test "returns 404 when report id is invalid", %{conn: conn} do +      conn = get(conn, "/api/pleroma/admin/reports/test") + +      assert json_response_and_validate_schema(conn, :not_found) == %{"error" => "Not found"} +    end +  end + +  describe "PATCH /api/pleroma/admin/reports" do +    setup do +      [reporter, target_user] = insert_pair(:user) +      activity = insert(:note_activity, user: target_user) + +      {:ok, %{id: report_id}} = +        CommonAPI.report(reporter, %{ +          account_id: target_user.id, +          comment: "I feel offended", +          status_ids: [activity.id] +        }) + +      {:ok, %{id: second_report_id}} = +        CommonAPI.report(reporter, %{ +          account_id: target_user.id, +          comment: "I feel very offended", +          status_ids: [activity.id] +        }) + +      %{ +        id: report_id, +        second_report_id: second_report_id +      } +    end + +    test "requires admin:write:reports scope", %{conn: conn, id: id, admin: admin} do +      read_token = insert(:oauth_token, user: admin, scopes: ["admin:read"]) +      write_token = insert(:oauth_token, user: admin, scopes: ["admin:write:reports"]) + +      response = +        conn +        |> assign(:token, read_token) +        |> put_req_header("content-type", "application/json") +        |> patch("/api/pleroma/admin/reports", %{ +          "reports" => [%{"state" => "resolved", "id" => id}] +        }) +        |> json_response_and_validate_schema(403) + +      assert response == %{ +               "error" => "Insufficient permissions: admin:write:reports." +             } + +      conn +      |> assign(:token, write_token) +      |> put_req_header("content-type", "application/json") +      |> patch("/api/pleroma/admin/reports", %{ +        "reports" => [%{"state" => "resolved", "id" => id}] +      }) +      |> json_response_and_validate_schema(:no_content) +    end + +    test "mark report as resolved", %{conn: conn, id: id, admin: admin} do +      conn +      |> put_req_header("content-type", "application/json") +      |> patch("/api/pleroma/admin/reports", %{ +        "reports" => [ +          %{"state" => "resolved", "id" => id} +        ] +      }) +      |> json_response_and_validate_schema(:no_content) + +      activity = Activity.get_by_id(id) +      assert activity.data["state"] == "resolved" + +      log_entry = Repo.one(ModerationLog) + +      assert ModerationLog.get_log_entry_message(log_entry) == +               "@#{admin.nickname} updated report ##{id} with 'resolved' state" +    end + +    test "closes report", %{conn: conn, id: id, admin: admin} do +      conn +      |> put_req_header("content-type", "application/json") +      |> patch("/api/pleroma/admin/reports", %{ +        "reports" => [ +          %{"state" => "closed", "id" => id} +        ] +      }) +      |> json_response_and_validate_schema(:no_content) + +      activity = Activity.get_by_id(id) +      assert activity.data["state"] == "closed" + +      log_entry = Repo.one(ModerationLog) + +      assert ModerationLog.get_log_entry_message(log_entry) == +               "@#{admin.nickname} updated report ##{id} with 'closed' state" +    end + +    test "returns 400 when state is unknown", %{conn: conn, id: id} do +      conn = +        conn +        |> put_req_header("content-type", "application/json") +        |> patch("/api/pleroma/admin/reports", %{ +          "reports" => [ +            %{"state" => "test", "id" => id} +          ] +        }) + +      assert "Unsupported state" = +               hd(json_response_and_validate_schema(conn, :bad_request))["error"] +    end + +    test "returns 404 when report is not exist", %{conn: conn} do +      conn = +        conn +        |> put_req_header("content-type", "application/json") +        |> patch("/api/pleroma/admin/reports", %{ +          "reports" => [ +            %{"state" => "closed", "id" => "test"} +          ] +        }) + +      assert hd(json_response_and_validate_schema(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 +      |> put_req_header("content-type", "application/json") +      |> patch("/api/pleroma/admin/reports", %{ +        "reports" => [ +          %{"state" => "resolved", "id" => id}, +          %{"state" => "closed", "id" => second_report_id} +        ] +      }) +      |> json_response_and_validate_schema(: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 + +  describe "GET /api/pleroma/admin/reports" do +    test "returns empty response when no reports created", %{conn: conn} do +      response = +        conn +        |> get("/api/pleroma/admin/reports") +        |> json_response_and_validate_schema(:ok) + +      assert Enum.empty?(response["reports"]) +      assert response["total"] == 0 +    end + +    test "returns reports", %{conn: conn} do +      [reporter, target_user] = insert_pair(:user) +      activity = insert(:note_activity, user: target_user) + +      {:ok, %{id: report_id}} = +        CommonAPI.report(reporter, %{ +          account_id: target_user.id, +          comment: "I feel offended", +          status_ids: [activity.id] +        }) + +      response = +        conn +        |> get("/api/pleroma/admin/reports") +        |> json_response_and_validate_schema(:ok) + +      [report] = response["reports"] + +      assert length(response["reports"]) == 1 +      assert report["id"] == report_id + +      assert response["total"] == 1 +    end + +    test "returns reports with specified state", %{conn: conn} do +      [reporter, target_user] = insert_pair(:user) +      activity = insert(:note_activity, user: target_user) + +      {:ok, %{id: first_report_id}} = +        CommonAPI.report(reporter, %{ +          account_id: target_user.id, +          comment: "I feel offended", +          status_ids: [activity.id] +        }) + +      {:ok, %{id: second_report_id}} = +        CommonAPI.report(reporter, %{ +          account_id: target_user.id, +          comment: "I don't like this user" +        }) + +      CommonAPI.update_report_state(second_report_id, "closed") + +      response = +        conn +        |> get("/api/pleroma/admin/reports?state=open") +        |> json_response_and_validate_schema(:ok) + +      assert [open_report] = response["reports"] + +      assert length(response["reports"]) == 1 +      assert open_report["id"] == first_report_id + +      assert response["total"] == 1 + +      response = +        conn +        |> get("/api/pleroma/admin/reports?state=closed") +        |> json_response_and_validate_schema(:ok) + +      assert [closed_report] = response["reports"] + +      assert length(response["reports"]) == 1 +      assert closed_report["id"] == second_report_id + +      assert response["total"] == 1 + +      assert %{"total" => 0, "reports" => []} == +               conn +               |> get("/api/pleroma/admin/reports?state=resolved", %{ +                 "" => "" +               }) +               |> json_response_and_validate_schema(:ok) +    end + +    test "returns 403 when requested by a non-admin" do +      user = insert(:user) +      token = insert(:oauth_token, user: user) + +      conn = +        build_conn() +        |> assign(:user, user) +        |> assign(:token, token) +        |> get("/api/pleroma/admin/reports") + +      assert json_response(conn, :forbidden) == +               %{"error" => "User is not an admin or OAuth admin scope is not granted."} +    end + +    test "returns 403 when requested by anonymous" do +      conn = get(build_conn(), "/api/pleroma/admin/reports") + +      assert json_response(conn, :forbidden) == %{ +               "error" => "Invalid credentials." +             } +    end +  end + +  describe "POST /api/pleroma/admin/reports/:id/notes" do +    setup %{conn: conn, admin: admin} do +      [reporter, target_user] = insert_pair(:user) +      activity = insert(:note_activity, user: target_user) + +      {:ok, %{id: report_id}} = +        CommonAPI.report(reporter, %{ +          account_id: target_user.id, +          comment: "I feel offended", +          status_ids: [activity.id] +        }) + +      conn +      |> put_req_header("content-type", "application/json") +      |> post("/api/pleroma/admin/reports/#{report_id}/notes", %{ +        content: "this is disgusting!" +      }) + +      conn +      |> put_req_header("content-type", "application/json") +      |> post("/api/pleroma/admin/reports/#{report_id}/notes", %{ +        content: "this is disgusting2!" +      }) + +      %{ +        admin_id: admin.id, +        report_id: report_id +      } +    end + +    test "it creates report note", %{admin_id: admin_id, report_id: report_id} do +      assert [note, _] = Repo.all(ReportNote) + +      assert %{ +               activity_id: ^report_id, +               content: "this is disgusting!", +               user_id: ^admin_id +             } = note +    end + +    test "it returns reports with notes", %{conn: conn, admin: admin} do +      conn = get(conn, "/api/pleroma/admin/reports") + +      response = json_response_and_validate_schema(conn, 200) +      notes = hd(response["reports"])["notes"] +      [note, _] = notes + +      assert note["user"]["nickname"] == admin.nickname +      assert note["content"] == "this is disgusting!" +      assert note["created_at"] +      assert response["total"] == 1 +    end + +    test "it deletes the note", %{conn: conn, report_id: report_id} do +      assert ReportNote |> Repo.all() |> length() == 2 +      assert [note, _] = Repo.all(ReportNote) + +      delete(conn, "/api/pleroma/admin/reports/#{report_id}/notes/#{note.id}") + +      assert ReportNote |> Repo.all() |> length() == 1 +    end +  end +end diff --git a/test/web/admin_api/controllers/status_controller_test.exs b/test/web/admin_api/controllers/status_controller_test.exs index 124d8dc2e..eff78fb0a 100644 --- a/test/web/admin_api/controllers/status_controller_test.exs +++ b/test/web/admin_api/controllers/status_controller_test.exs @@ -42,6 +42,14 @@ defmodule Pleroma.Web.AdminAPI.StatusControllerTest do          |> json_response_and_validate_schema(200)        assert response["id"] == activity.id + +      account = response["account"] +      actor = User.get_by_ap_id(activity.actor) + +      assert account["id"] == actor.id +      assert account["nickname"] == actor.nickname +      assert account["deactivated"] == actor.deactivated +      assert account["confirmation_pending"] == actor.confirmation_pending      end    end diff --git a/test/web/common_api/common_api_test.exs b/test/web/common_api/common_api_test.exs index 2291f76dd..fc3bb845d 100644 --- a/test/web/common_api/common_api_test.exs +++ b/test/web/common_api/common_api_test.exs @@ -5,7 +5,9 @@  defmodule Pleroma.Web.CommonAPITest do    use Pleroma.DataCase    alias Pleroma.Activity +  alias Pleroma.Chat    alias Pleroma.Conversation.Participation +  alias Pleroma.Notification    alias Pleroma.Object    alias Pleroma.User    alias Pleroma.Web.ActivityPub.ActivityPub @@ -23,6 +25,196 @@ defmodule Pleroma.Web.CommonAPITest do    setup do: clear_config([:instance, :limit])    setup do: clear_config([:instance, :max_pinned_statuses]) +  describe "blocking" do +    setup do +      blocker = insert(:user) +      blocked = insert(:user) +      User.follow(blocker, blocked) +      User.follow(blocked, blocker) +      %{blocker: blocker, blocked: blocked} +    end + +    test "it blocks and federates", %{blocker: blocker, blocked: blocked} do +      clear_config([:instance, :federating], true) + +      with_mock Pleroma.Web.Federator, +        publish: fn _ -> nil end do +        assert {:ok, block} = CommonAPI.block(blocker, blocked) + +        assert block.local +        assert User.blocks?(blocker, blocked) +        refute User.following?(blocker, blocked) +        refute User.following?(blocked, blocker) + +        assert called(Pleroma.Web.Federator.publish(block)) +      end +    end + +    test "it blocks and does not federate if outgoing blocks are disabled", %{ +      blocker: blocker, +      blocked: blocked +    } do +      clear_config([:instance, :federating], true) +      clear_config([:activitypub, :outgoing_blocks], false) + +      with_mock Pleroma.Web.Federator, +        publish: fn _ -> nil end do +        assert {:ok, block} = CommonAPI.block(blocker, blocked) + +        assert block.local +        assert User.blocks?(blocker, blocked) +        refute User.following?(blocker, blocked) +        refute User.following?(blocked, blocker) + +        refute called(Pleroma.Web.Federator.publish(block)) +      end +    end +  end + +  describe "posting chat messages" do +    setup do: clear_config([:instance, :chat_limit]) + +    test "it posts a chat message without content but with an attachment" do +      author = insert(:user) +      recipient = insert(:user) + +      file = %Plug.Upload{ +        content_type: "image/jpg", +        path: Path.absname("test/fixtures/image.jpg"), +        filename: "an_image.jpg" +      } + +      {:ok, upload} = ActivityPub.upload(file, actor: author.ap_id) + +      with_mocks([ +        { +          Pleroma.Web.Streamer, +          [], +          [ +            stream: fn _, _ -> +              nil +            end +          ] +        }, +        { +          Pleroma.Web.Push, +          [], +          [ +            send: fn _ -> nil end +          ] +        } +      ]) do +        {:ok, activity} = +          CommonAPI.post_chat_message( +            author, +            recipient, +            nil, +            media_id: upload.id +          ) + +        notification = +          Notification.for_user_and_activity(recipient, activity) +          |> Repo.preload(:activity) + +        assert called(Pleroma.Web.Push.send(notification)) +        assert called(Pleroma.Web.Streamer.stream(["user", "user:notification"], notification)) +        assert called(Pleroma.Web.Streamer.stream(["user", "user:pleroma_chat"], :_)) + +        assert activity +      end +    end + +    test "it adds html newlines" do +      author = insert(:user) +      recipient = insert(:user) + +      other_user = insert(:user) + +      {:ok, activity} = +        CommonAPI.post_chat_message( +          author, +          recipient, +          "uguu\nuguuu" +        ) + +      assert other_user.ap_id not in activity.recipients + +      object = Object.normalize(activity, false) + +      assert object.data["content"] == "uguu<br/>uguuu" +    end + +    test "it linkifies" do +      author = insert(:user) +      recipient = insert(:user) + +      other_user = insert(:user) + +      {:ok, activity} = +        CommonAPI.post_chat_message( +          author, +          recipient, +          "https://example.org is the site of @#{other_user.nickname} #2hu" +        ) + +      assert other_user.ap_id not in activity.recipients + +      object = Object.normalize(activity, false) + +      assert object.data["content"] == +               "<a href=\"https://example.org\" rel=\"ugc\">https://example.org</a> is the site of <span class=\"h-card\"><a class=\"u-url mention\" data-user=\"#{ +                 other_user.id +               }\" href=\"#{other_user.ap_id}\" rel=\"ugc\">@<span>#{other_user.nickname}</span></a></span> <a class=\"hashtag\" data-tag=\"2hu\" href=\"http://localhost:4001/tag/2hu\">#2hu</a>" +    end + +    test "it posts a chat message" do +      author = insert(:user) +      recipient = insert(:user) + +      {:ok, activity} = +        CommonAPI.post_chat_message( +          author, +          recipient, +          "a test message <script>alert('uuu')</script> :firefox:" +        ) + +      assert activity.data["type"] == "Create" +      assert activity.local +      object = Object.normalize(activity) + +      assert object.data["type"] == "ChatMessage" +      assert object.data["to"] == [recipient.ap_id] + +      assert object.data["content"] == +               "a test message <script>alert('uuu')</script> :firefox:" + +      assert object.data["emoji"] == %{ +               "firefox" => "http://localhost:4001/emoji/Firefox.gif" +             } + +      assert Chat.get(author.id, recipient.ap_id) +      assert Chat.get(recipient.id, author.ap_id) + +      assert :ok == Pleroma.Web.Federator.perform(:publish, activity) +    end + +    test "it reject messages over the local limit" do +      Pleroma.Config.put([:instance, :chat_limit], 2) + +      author = insert(:user) +      recipient = insert(:user) + +      {:error, message} = +        CommonAPI.post_chat_message( +          author, +          recipient, +          "123" +        ) + +      assert message == :content_too_long +    end +  end +    describe "unblocking" do      test "it works even without an existing block activity" do        blocked = insert(:user) diff --git a/test/web/fallback_test.exs b/test/web/fallback_test.exs index 3919ef93a..a65865860 100644 --- a/test/web/fallback_test.exs +++ b/test/web/fallback_test.exs @@ -6,22 +6,56 @@ defmodule Pleroma.Web.FallbackTest do    use Pleroma.Web.ConnCase    import Pleroma.Factory -  test "GET /registration/:token", %{conn: conn} do -    assert conn -           |> get("/registration/foo") -           |> html_response(200) =~ "<!--server-generated-meta-->" +  describe "neither preloaded data nor metadata attached to" do +    test "GET /registration/:token", %{conn: conn} do +      response = get(conn, "/registration/foo") + +      assert html_response(response, 200) =~ "<!--server-generated-meta-->" +    end + +    test "GET /*path", %{conn: conn} do +      assert conn +             |> get("/foo") +             |> html_response(200) =~ "<!--server-generated-meta-->" +    end    end -  test "GET /:maybe_nickname_or_id", %{conn: conn} do -    user = insert(:user) +  describe "preloaded data and metadata attached to" do +    test "GET /:maybe_nickname_or_id", %{conn: conn} do +      user = insert(:user) +      user_missing = get(conn, "/foo") +      user_present = get(conn, "/#{user.nickname}") -    assert conn -           |> get("/foo") -           |> html_response(200) =~ "<!--server-generated-meta-->" +      assert(html_response(user_missing, 200) =~ "<!--server-generated-meta-->") +      refute html_response(user_present, 200) =~ "<!--server-generated-meta-->" +      assert html_response(user_present, 200) =~ "initial-results" +    end -    refute conn -           |> get("/" <> user.nickname) -           |> html_response(200) =~ "<!--server-generated-meta-->" +    test "GET /*path", %{conn: conn} do +      assert conn +             |> get("/foo") +             |> html_response(200) =~ "<!--server-generated-meta-->" + +      refute conn +             |> get("/foo/bar") +             |> html_response(200) =~ "<!--server-generated-meta-->" +    end +  end + +  describe "preloaded data is attached to" do +    test "GET /main/public", %{conn: conn} do +      public_page = get(conn, "/main/public") + +      refute html_response(public_page, 200) =~ "<!--server-generated-meta-->" +      assert html_response(public_page, 200) =~ "initial-results" +    end + +    test "GET /main/all", %{conn: conn} do +      public_page = get(conn, "/main/all") + +      refute html_response(public_page, 200) =~ "<!--server-generated-meta-->" +      assert html_response(public_page, 200) =~ "initial-results" +    end    end    test "GET /api*path", %{conn: conn} do @@ -34,16 +68,6 @@ defmodule Pleroma.Web.FallbackTest do      assert redirected_to(get(conn, "/pleroma/admin")) =~ "/pleroma/admin/"    end -  test "GET /*path", %{conn: conn} do -    assert conn -           |> get("/foo") -           |> html_response(200) =~ "<!--server-generated-meta-->" - -    assert conn -           |> get("/foo/bar") -           |> html_response(200) =~ "<!--server-generated-meta-->" -  end -    test "OPTIONS /*path", %{conn: conn} do      assert conn             |> options("/foo") diff --git a/test/web/federator_test.exs b/test/web/federator_test.exs index de90aa6e0..592fdccd1 100644 --- a/test/web/federator_test.exs +++ b/test/web/federator_test.exs @@ -23,7 +23,7 @@ defmodule Pleroma.Web.FederatorTest do    setup_all do: clear_config([:instance, :federating], true)    setup do: clear_config([:instance, :allow_relay]) -  setup do: clear_config([:instance, :rewrite_policy]) +  setup do: clear_config([:mrf, :policies])    setup do: clear_config([:mrf_keyword])    describe "Publish an activity" do @@ -158,7 +158,7 @@ defmodule Pleroma.Web.FederatorTest do        Pleroma.Config.put([:mrf_keyword, :reject], ["lain"])        Pleroma.Config.put( -        [:instance, :rewrite_policy], +        [:mrf, :policies],          Pleroma.Web.ActivityPub.MRF.KeywordPolicy        ) diff --git a/test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs b/test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs index 696228203..31f0edf97 100644 --- a/test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs +++ b/test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs @@ -8,6 +8,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do    use Pleroma.Web.ConnCase +  import Mock    import Pleroma.Factory    setup do: clear_config([:instance, :max_account_fields]) @@ -52,33 +53,39 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do        user = Repo.get(User, user_data["id"]) -      res_conn = -        conn -        |> assign(:user, user) -        |> patch("/api/v1/accounts/update_credentials", %{ -          "pleroma_settings_store" => %{ -            masto_fe: %{ -              theme: "blub" +      clear_config([:instance, :federating], true) + +      with_mock Pleroma.Web.Federator, +        publish: fn _activity -> :ok end do +        res_conn = +          conn +          |> assign(:user, user) +          |> patch("/api/v1/accounts/update_credentials", %{ +            "pleroma_settings_store" => %{ +              masto_fe: %{ +                theme: "blub" +              }              } -          } -        }) +          }) -      assert user_data = json_response_and_validate_schema(res_conn, 200) +        assert user_data = json_response_and_validate_schema(res_conn, 200) -      assert user_data["pleroma"]["settings_store"] == -               %{ -                 "pleroma_fe" => %{"theme" => "bla"}, -                 "masto_fe" => %{"theme" => "blub"} -               } +        assert user_data["pleroma"]["settings_store"] == +                 %{ +                   "pleroma_fe" => %{"theme" => "bla"}, +                   "masto_fe" => %{"theme" => "blub"} +                 } + +        assert_called(Pleroma.Web.Federator.publish(:_)) +      end      end      test "updates the user's bio", %{conn: conn} do        user2 = insert(:user) -      conn = -        patch(conn, "/api/v1/accounts/update_credentials", %{ -          "note" => "I drink #cofe with @#{user2.nickname}\n\nsuya.." -        }) +      raw_bio = "I drink #cofe with @#{user2.nickname}\n\nsuya.." + +      conn = patch(conn, "/api/v1/accounts/update_credentials", %{"note" => raw_bio})        assert user_data = json_response_and_validate_schema(conn, 200) @@ -86,6 +93,12 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do                 ~s(I drink <a class="hashtag" data-tag="cofe" href="http://localhost:4001/tag/cofe">#cofe</a> with <span class="h-card"><a class="u-url mention" data-user="#{                   user2.id                 }" href="#{user2.ap_id}" rel="ugc">@<span>#{user2.nickname}</span></a></span><br/><br/>suya..) + +      assert user_data["source"]["note"] == raw_bio + +      user = Repo.get(User, user_data["id"]) + +      assert user.raw_bio == raw_bio      end      test "updates the user's locking status", %{conn: conn} do @@ -203,10 +216,20 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do          filename: "an_image.jpg"        } -      conn = patch(conn, "/api/v1/accounts/update_credentials", %{"avatar" => new_avatar}) +      res = +        conn +        |> patch("/api/v1/accounts/update_credentials", %{"avatar" => new_avatar}) -      assert user_response = json_response_and_validate_schema(conn, 200) +      assert user_response = json_response_and_validate_schema(res, 200)        assert user_response["avatar"] != User.avatar_url(user) + +      # Also removes it +      res = +        conn +        |> patch("/api/v1/accounts/update_credentials", %{"avatar" => nil}) + +      assert user_response = json_response_and_validate_schema(res, 200) +      assert user_response["avatar"] == User.avatar_url(user)      end      test "updates the user's banner", %{user: user, conn: conn} do @@ -216,10 +239,21 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do          filename: "an_image.jpg"        } -      conn = patch(conn, "/api/v1/accounts/update_credentials", %{"header" => new_header}) +      res = +        conn +        |> patch("/api/v1/accounts/update_credentials", %{"header" => new_header}) -      assert user_response = json_response_and_validate_schema(conn, 200) +      assert user_response = json_response_and_validate_schema(res, 200)        assert user_response["header"] != User.banner_url(user) + +      # Also removes it + +      res = +        conn +        |> patch("/api/v1/accounts/update_credentials", %{"header" => nil}) + +      assert user_response = json_response_and_validate_schema(res, 200) +      assert user_response["header"] == User.banner_url(user)      end      test "updates the user's background", %{conn: conn} do @@ -229,13 +263,25 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do          filename: "an_image.jpg"        } -      conn = -        patch(conn, "/api/v1/accounts/update_credentials", %{ +      res = +        conn +        |> patch("/api/v1/accounts/update_credentials", %{            "pleroma_background_image" => new_header          }) -      assert user_response = json_response_and_validate_schema(conn, 200) +      assert user_response = json_response_and_validate_schema(res, 200)        assert user_response["pleroma"]["background_image"] + +      # Also removes it + +      res = +        conn +        |> patch("/api/v1/accounts/update_credentials", %{ +          "pleroma_background_image" => nil +        }) + +      assert user_response = json_response_and_validate_schema(res, 200) +      refute user_response["pleroma"]["background_image"]      end      test "requires 'write:accounts' permission" do @@ -387,4 +433,71 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do                 |> json_response_and_validate_schema(403)      end    end + +  describe "Mark account as bot" do +    setup do: oauth_access(["write:accounts"]) +    setup :request_content_type + +    test "changing actor_type to Service makes account a bot", %{conn: conn} do +      account = +        conn +        |> patch("/api/v1/accounts/update_credentials", %{actor_type: "Service"}) +        |> json_response_and_validate_schema(200) + +      assert account["bot"] +      assert account["source"]["pleroma"]["actor_type"] == "Service" +    end + +    test "changing actor_type to Person makes account a human", %{conn: conn} do +      account = +        conn +        |> patch("/api/v1/accounts/update_credentials", %{actor_type: "Person"}) +        |> json_response_and_validate_schema(200) + +      refute account["bot"] +      assert account["source"]["pleroma"]["actor_type"] == "Person" +    end + +    test "changing actor_type to Application causes error", %{conn: conn} do +      response = +        conn +        |> patch("/api/v1/accounts/update_credentials", %{actor_type: "Application"}) +        |> json_response_and_validate_schema(403) + +      assert %{"error" => "Invalid request"} == response +    end + +    test "changing bot field to true changes actor_type to Service", %{conn: conn} do +      account = +        conn +        |> patch("/api/v1/accounts/update_credentials", %{bot: "true"}) +        |> json_response_and_validate_schema(200) + +      assert account["bot"] +      assert account["source"]["pleroma"]["actor_type"] == "Service" +    end + +    test "changing bot field to false changes actor_type to Person", %{conn: conn} do +      account = +        conn +        |> patch("/api/v1/accounts/update_credentials", %{bot: "false"}) +        |> json_response_and_validate_schema(200) + +      refute account["bot"] +      assert account["source"]["pleroma"]["actor_type"] == "Person" +    end + +    test "actor_type field has a higher priority than bot", %{conn: conn} do +      account = +        conn +        |> patch("/api/v1/accounts/update_credentials", %{ +          actor_type: "Person", +          bot: "true" +        }) +        |> json_response_and_validate_schema(200) + +      refute account["bot"] +      assert account["source"]["pleroma"]["actor_type"] == "Person" +    end +  end  end diff --git a/test/web/mastodon_api/controllers/account_controller_test.exs b/test/web/mastodon_api/controllers/account_controller_test.exs index 1ce97378d..260ad2306 100644 --- a/test/web/mastodon_api/controllers/account_controller_test.exs +++ b/test/web/mastodon_api/controllers/account_controller_test.exs @@ -127,6 +127,15 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do                 |> get("/api/v1/accounts/internal.fetch")                 |> json_response_and_validate_schema(404)      end + +    test "returns 404 for deactivated user", %{conn: conn} do +      user = insert(:user, deactivated: true) + +      assert %{"error" => "Can't find user"} = +               conn +               |> get("/api/v1/accounts/#{user.id}") +               |> json_response_and_validate_schema(:not_found) +    end    end    defp local_and_remote_users do @@ -143,15 +152,15 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do      setup do: clear_config([:restrict_unauthenticated, :profiles, :remote], true)      test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do -      assert %{"error" => "Can't find user"} == +      assert %{"error" => "This API requires an authenticated user"} ==                 conn                 |> get("/api/v1/accounts/#{local.id}") -               |> json_response_and_validate_schema(:not_found) +               |> json_response_and_validate_schema(:unauthorized) -      assert %{"error" => "Can't find user"} == +      assert %{"error" => "This API requires an authenticated user"} ==                 conn                 |> get("/api/v1/accounts/#{remote.id}") -               |> json_response_and_validate_schema(:not_found) +               |> json_response_and_validate_schema(:unauthorized)      end      test "if user is authenticated", %{local: local, remote: remote} do @@ -173,8 +182,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do      test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do        res_conn = get(conn, "/api/v1/accounts/#{local.id}") -      assert json_response_and_validate_schema(res_conn, :not_found) == %{ -               "error" => "Can't find user" +      assert json_response_and_validate_schema(res_conn, :unauthorized) == %{ +               "error" => "This API requires an authenticated user"               }        res_conn = get(conn, "/api/v1/accounts/#{remote.id}") @@ -203,8 +212,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do        res_conn = get(conn, "/api/v1/accounts/#{remote.id}") -      assert json_response_and_validate_schema(res_conn, :not_found) == %{ -               "error" => "Can't find user" +      assert json_response_and_validate_schema(res_conn, :unauthorized) == %{ +               "error" => "This API requires an authenticated user"               }      end @@ -249,6 +258,24 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do        assert id == announce.id      end +    test "deactivated user", %{conn: conn} do +      user = insert(:user, deactivated: true) + +      assert %{"error" => "Can't find user"} == +               conn +               |> get("/api/v1/accounts/#{user.id}/statuses") +               |> json_response_and_validate_schema(:not_found) +    end + +    test "returns 404 when user is invisible", %{conn: conn} do +      user = insert(:user, %{invisible: true}) + +      assert %{"error" => "Can't find user"} = +               conn +               |> get("/api/v1/accounts/#{user.id}") +               |> json_response_and_validate_schema(404) +    end +      test "respects blocks", %{user: user_one, conn: conn} do        user_two = insert(:user)        user_three = insert(:user) @@ -350,9 +377,10 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do        assert json_response_and_validate_schema(conn, 200) == []      end -    test "gets an users media", %{conn: conn} do +    test "gets an users media, excludes reblogs", %{conn: conn} do        note = insert(:note_activity)        user = User.get_cached_by_ap_id(note.data["actor"]) +      other_user = insert(:user)        file = %Plug.Upload{          content_type: "image/jpg", @@ -364,6 +392,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do        {:ok, %{id: image_post_id}} = CommonAPI.post(user, %{status: "cofe", media_ids: [media_id]}) +      {:ok, %{id: media_id}} = ActivityPub.upload(file, actor: other_user.ap_id) + +      {:ok, %{id: other_image_post_id}} = +        CommonAPI.post(other_user, %{status: "cofe2", media_ids: [media_id]}) + +      {:ok, _announce} = CommonAPI.repeat(other_image_post_id, user) +        conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?only_media=true")        assert [%{"id" => ^image_post_id}] = json_response_and_validate_schema(conn, 200) @@ -422,15 +457,15 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do      setup do: clear_config([:restrict_unauthenticated, :profiles, :remote], true)      test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do -      assert %{"error" => "Can't find user"} == +      assert %{"error" => "This API requires an authenticated user"} ==                 conn                 |> get("/api/v1/accounts/#{local.id}/statuses") -               |> json_response_and_validate_schema(:not_found) +               |> json_response_and_validate_schema(:unauthorized) -      assert %{"error" => "Can't find user"} == +      assert %{"error" => "This API requires an authenticated user"} ==                 conn                 |> get("/api/v1/accounts/#{remote.id}/statuses") -               |> json_response_and_validate_schema(:not_found) +               |> json_response_and_validate_schema(:unauthorized)      end      test "if user is authenticated", %{local: local, remote: remote} do @@ -451,10 +486,10 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do      setup do: clear_config([:restrict_unauthenticated, :profiles, :local], true)      test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do -      assert %{"error" => "Can't find user"} == +      assert %{"error" => "This API requires an authenticated user"} ==                 conn                 |> get("/api/v1/accounts/#{local.id}/statuses") -               |> json_response_and_validate_schema(:not_found) +               |> json_response_and_validate_schema(:unauthorized)        res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses")        assert length(json_response_and_validate_schema(res_conn, 200)) == 1 @@ -481,10 +516,10 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do        res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses")        assert length(json_response_and_validate_schema(res_conn, 200)) == 1 -      assert %{"error" => "Can't find user"} == +      assert %{"error" => "This API requires an authenticated user"} ==                 conn                 |> get("/api/v1/accounts/#{remote.id}/statuses") -               |> json_response_and_validate_schema(:not_found) +               |> json_response_and_validate_schema(:unauthorized)      end      test "if user is authenticated", %{local: local, remote: remote} do @@ -745,7 +780,6 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do        assert %{"id" => _id, "muting" => true, "muting_notifications" => true} =                 conn -               |> put_req_header("content-type", "application/json")                 |> post("/api/v1/accounts/#{other_user.id}/mute")                 |> json_response_and_validate_schema(200) diff --git a/test/web/mastodon_api/controllers/conversation_controller_test.exs b/test/web/mastodon_api/controllers/conversation_controller_test.exs index 693ba51e5..3e21e6bf1 100644 --- a/test/web/mastodon_api/controllers/conversation_controller_test.exs +++ b/test/web/mastodon_api/controllers/conversation_controller_test.exs @@ -12,84 +12,88 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do    setup do: oauth_access(["read:statuses"]) -  test "returns a list of conversations", %{user: user_one, conn: conn} do -    user_two = insert(:user) -    user_three = insert(:user) - -    {:ok, user_two} = User.follow(user_two, user_one) - -    assert User.get_cached_by_id(user_two.id).unread_conversation_count == 0 - -    {:ok, direct} = -      CommonAPI.post(user_one, %{ -        status: "Hi @#{user_two.nickname}, @#{user_three.nickname}!", -        visibility: "direct" -      }) - -    assert User.get_cached_by_id(user_two.id).unread_conversation_count == 1 - -    {:ok, _follower_only} = -      CommonAPI.post(user_one, %{ -        status: "Hi @#{user_two.nickname}!", -        visibility: "private" -      }) - -    res_conn = get(conn, "/api/v1/conversations") - -    assert response = json_response_and_validate_schema(res_conn, 200) - -    assert [ -             %{ -               "id" => res_id, -               "accounts" => res_accounts, -               "last_status" => res_last_status, -               "unread" => unread -             } -           ] = response - -    account_ids = Enum.map(res_accounts, & &1["id"]) -    assert length(res_accounts) == 2 -    assert user_two.id in account_ids -    assert user_three.id in account_ids -    assert is_binary(res_id) -    assert unread == false -    assert res_last_status["id"] == direct.id -    assert User.get_cached_by_id(user_one.id).unread_conversation_count == 0 +  describe "returns a list of conversations" do +    setup(%{user: user_one, conn: conn}) do +      user_two = insert(:user) +      user_three = insert(:user) + +      {:ok, user_two} = User.follow(user_two, user_one) + +      {:ok, %{user: user_one, user_two: user_two, user_three: user_three, conn: conn}} +    end + +    test "returns correct conversations", %{ +      user: user_one, +      user_two: user_two, +      user_three: user_three, +      conn: conn +    } do +      assert User.get_cached_by_id(user_two.id).unread_conversation_count == 0 +      {:ok, direct} = create_direct_message(user_one, [user_two, user_three]) + +      assert User.get_cached_by_id(user_two.id).unread_conversation_count == 1 + +      {:ok, _follower_only} = +        CommonAPI.post(user_one, %{ +          status: "Hi @#{user_two.nickname}!", +          visibility: "private" +        }) + +      res_conn = get(conn, "/api/v1/conversations") + +      assert response = json_response_and_validate_schema(res_conn, 200) + +      assert [ +               %{ +                 "id" => res_id, +                 "accounts" => res_accounts, +                 "last_status" => res_last_status, +                 "unread" => unread +               } +             ] = response + +      account_ids = Enum.map(res_accounts, & &1["id"]) +      assert length(res_accounts) == 2 +      assert user_two.id in account_ids +      assert user_three.id in account_ids +      assert is_binary(res_id) +      assert unread == false +      assert res_last_status["id"] == direct.id +      assert User.get_cached_by_id(user_one.id).unread_conversation_count == 0 +    end + +    test "observes limit params", %{ +      user: user_one, +      user_two: user_two, +      user_three: user_three, +      conn: conn +    } do +      {:ok, _} = create_direct_message(user_one, [user_two, user_three]) +      {:ok, _} = create_direct_message(user_two, [user_one, user_three]) +      {:ok, _} = create_direct_message(user_three, [user_two, user_one]) + +      res_conn = get(conn, "/api/v1/conversations?limit=1") + +      assert response = json_response_and_validate_schema(res_conn, 200) + +      assert Enum.count(response) == 1 + +      res_conn = get(conn, "/api/v1/conversations?limit=2") + +      assert response = json_response_and_validate_schema(res_conn, 200) + +      assert Enum.count(response) == 2 +    end    end    test "filters conversations by recipients", %{user: user_one, conn: conn} do      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" -      }) +    {:ok, direct1} = create_direct_message(user_one, [user_two]) +    {:ok, _direct2} = create_direct_message(user_one, [user_three]) +    {:ok, direct3} = create_direct_message(user_one, [user_two, user_three]) +    {:ok, _direct4} = create_direct_message(user_two, [user_three]) +    {:ok, direct5} = create_direct_message(user_two, [user_one])      assert [conversation1, conversation2] =               conn @@ -109,12 +113,7 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do    test "updates the last_status on reply", %{user: user_one, conn: conn} do      user_two = insert(:user) - -    {:ok, direct} = -      CommonAPI.post(user_one, %{ -        status: "Hi @#{user_two.nickname}", -        visibility: "direct" -      }) +    {:ok, direct} = create_direct_message(user_one, [user_two])      {:ok, direct_reply} =        CommonAPI.post(user_two, %{ @@ -133,12 +132,7 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do    test "the user marks a conversation as read", %{user: user_one, conn: conn} do      user_two = insert(:user) - -    {:ok, direct} = -      CommonAPI.post(user_one, %{ -        status: "Hi @#{user_two.nickname}", -        visibility: "direct" -      }) +    {:ok, direct} = create_direct_message(user_one, [user_two])      assert User.get_cached_by_id(user_one.id).unread_conversation_count == 0      assert User.get_cached_by_id(user_two.id).unread_conversation_count == 1 @@ -194,15 +188,22 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do    test "(vanilla) Mastodon frontend behaviour", %{user: user_one, conn: conn} do      user_two = insert(:user) - -    {:ok, direct} = -      CommonAPI.post(user_one, %{ -        status: "Hi @#{user_two.nickname}!", -        visibility: "direct" -      }) +    {:ok, direct} = create_direct_message(user_one, [user_two])      res_conn = get(conn, "/api/v1/statuses/#{direct.id}/context")      assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200)    end + +  defp create_direct_message(sender, recips) do +    hellos = +      recips +      |> Enum.map(fn s -> "@#{s.nickname}" end) +      |> Enum.join(", ") + +    CommonAPI.post(sender, %{ +      status: "Hi #{hellos}!", +      visibility: "direct" +    }) +  end  end diff --git a/test/web/mastodon_api/controllers/notification_controller_test.exs b/test/web/mastodon_api/controllers/notification_controller_test.exs index e278d61f5..70ef0e8b5 100644 --- a/test/web/mastodon_api/controllers/notification_controller_test.exs +++ b/test/web/mastodon_api/controllers/notification_controller_test.exs @@ -54,6 +54,27 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do      assert response == expected_response    end +  test "by default, does not contain pleroma:chat_mention" do +    %{user: user, conn: conn} = oauth_access(["read:notifications"]) +    other_user = insert(:user) + +    {:ok, _activity} = CommonAPI.post_chat_message(other_user, user, "hey") + +    result = +      conn +      |> get("/api/v1/notifications") +      |> json_response_and_validate_schema(200) + +    assert [] == result + +    result = +      conn +      |> get("/api/v1/notifications?include_types[]=pleroma:chat_mention") +      |> json_response_and_validate_schema(200) + +    assert [_] = result +  end +    test "getting a single notification" do      %{user: user, conn: conn} = oauth_access(["read:notifications"])      other_user = insert(:user) @@ -292,6 +313,33 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do        assert public_activity.id in activity_ids        refute unlisted_activity.id in activity_ids      end + +    test "doesn't return less than the requested amount of records when the user's reply is liked" do +      user = insert(:user) +      %{user: other_user, conn: conn} = oauth_access(["read:notifications"]) + +      {:ok, mention} = +        CommonAPI.post(user, %{status: "@#{other_user.nickname}", visibility: "public"}) + +      {:ok, activity} = CommonAPI.post(user, %{status: ".", visibility: "public"}) + +      {:ok, reply} = +        CommonAPI.post(other_user, %{ +          status: ".", +          visibility: "public", +          in_reply_to_status_id: activity.id +        }) + +      {:ok, _favorite} = CommonAPI.favorite(user, reply.id) + +      activity_ids = +        conn +        |> get("/api/v1/notifications?exclude_visibilities[]=direct&limit=2") +        |> json_response_and_validate_schema(200) +        |> Enum.map(& &1["status"]["id"]) + +      assert [reply.id, mention.id] == activity_ids +    end    end    test "filters notifications using exclude_types" do diff --git a/test/web/mastodon_api/controllers/search_controller_test.exs b/test/web/mastodon_api/controllers/search_controller_test.exs index 7d0cafccc..826f37fbc 100644 --- a/test/web/mastodon_api/controllers/search_controller_test.exs +++ b/test/web/mastodon_api/controllers/search_controller_test.exs @@ -71,10 +71,102 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do          get(conn, "/api/v2/search?q=天子")          |> json_response_and_validate_schema(200) +      assert results["hashtags"] == [ +               %{"name" => "天子", "url" => "#{Web.base_url()}/tag/天子"} +             ] +        [status] = results["statuses"]        assert status["id"] == to_string(activity.id)      end +    test "constructs hashtags from search query", %{conn: conn} do +      results = +        conn +        |> get("/api/v2/search?#{URI.encode_query(%{q: "some text with #explicit #hashtags"})}") +        |> json_response_and_validate_schema(200) + +      assert results["hashtags"] == [ +               %{"name" => "explicit", "url" => "#{Web.base_url()}/tag/explicit"}, +               %{"name" => "hashtags", "url" => "#{Web.base_url()}/tag/hashtags"} +             ] + +      results = +        conn +        |> get("/api/v2/search?#{URI.encode_query(%{q: "john doe JOHN DOE"})}") +        |> json_response_and_validate_schema(200) + +      assert results["hashtags"] == [ +               %{"name" => "john", "url" => "#{Web.base_url()}/tag/john"}, +               %{"name" => "doe", "url" => "#{Web.base_url()}/tag/doe"}, +               %{"name" => "JohnDoe", "url" => "#{Web.base_url()}/tag/JohnDoe"} +             ] + +      results = +        conn +        |> get("/api/v2/search?#{URI.encode_query(%{q: "accident-prone"})}") +        |> json_response_and_validate_schema(200) + +      assert results["hashtags"] == [ +               %{"name" => "accident", "url" => "#{Web.base_url()}/tag/accident"}, +               %{"name" => "prone", "url" => "#{Web.base_url()}/tag/prone"}, +               %{"name" => "AccidentProne", "url" => "#{Web.base_url()}/tag/AccidentProne"} +             ] + +      results = +        conn +        |> get("/api/v2/search?#{URI.encode_query(%{q: "https://shpposter.club/users/shpuld"})}") +        |> json_response_and_validate_schema(200) + +      assert results["hashtags"] == [ +               %{"name" => "shpuld", "url" => "#{Web.base_url()}/tag/shpuld"} +             ] + +      results = +        conn +        |> get( +          "/api/v2/search?#{ +            URI.encode_query(%{ +              q: +                "https://www.washingtonpost.com/sports/2020/06/10/" <> +                  "nascar-ban-display-confederate-flag-all-events-properties/" +            }) +          }" +        ) +        |> json_response_and_validate_schema(200) + +      assert results["hashtags"] == [ +               %{"name" => "nascar", "url" => "#{Web.base_url()}/tag/nascar"}, +               %{"name" => "ban", "url" => "#{Web.base_url()}/tag/ban"}, +               %{"name" => "display", "url" => "#{Web.base_url()}/tag/display"}, +               %{"name" => "confederate", "url" => "#{Web.base_url()}/tag/confederate"}, +               %{"name" => "flag", "url" => "#{Web.base_url()}/tag/flag"}, +               %{"name" => "all", "url" => "#{Web.base_url()}/tag/all"}, +               %{"name" => "events", "url" => "#{Web.base_url()}/tag/events"}, +               %{"name" => "properties", "url" => "#{Web.base_url()}/tag/properties"}, +               %{ +                 "name" => "NascarBanDisplayConfederateFlagAllEventsProperties", +                 "url" => +                   "#{Web.base_url()}/tag/NascarBanDisplayConfederateFlagAllEventsProperties" +               } +             ] +    end + +    test "supports pagination of hashtags search results", %{conn: conn} do +      results = +        conn +        |> get( +          "/api/v2/search?#{ +            URI.encode_query(%{q: "#some #text #with #hashtags", limit: 2, offset: 1}) +          }" +        ) +        |> json_response_and_validate_schema(200) + +      assert results["hashtags"] == [ +               %{"name" => "text", "url" => "#{Web.base_url()}/tag/text"}, +               %{"name" => "with", "url" => "#{Web.base_url()}/tag/with"} +             ] +    end +      test "excludes a blocked users from search results", %{conn: conn} do        user = insert(:user)        user_smith = insert(:user, %{nickname: "Agent", name: "I love 2hu"}) @@ -179,7 +271,7 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do        [account | _] = results["accounts"]        assert account["id"] == to_string(user_three.id) -      assert results["hashtags"] == [] +      assert results["hashtags"] == ["2hu"]        [status] = results["statuses"]        assert status["id"] == to_string(activity.id) diff --git a/test/web/mastodon_api/controllers/status_controller_test.exs b/test/web/mastodon_api/controllers/status_controller_test.exs index 700c82e4f..a98e939e8 100644 --- a/test/web/mastodon_api/controllers/status_controller_test.exs +++ b/test/web/mastodon_api/controllers/status_controller_test.exs @@ -1541,14 +1541,49 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do             } = response    end +  test "favorites paginate correctly" do +    %{user: user, conn: conn} = oauth_access(["read:favourites"]) +    other_user = insert(:user) +    {:ok, first_post} = CommonAPI.post(other_user, %{status: "bla"}) +    {:ok, second_post} = CommonAPI.post(other_user, %{status: "bla"}) +    {:ok, third_post} = CommonAPI.post(other_user, %{status: "bla"}) + +    {:ok, _first_favorite} = CommonAPI.favorite(user, third_post.id) +    {:ok, _second_favorite} = CommonAPI.favorite(user, first_post.id) +    {:ok, third_favorite} = CommonAPI.favorite(user, second_post.id) + +    result = +      conn +      |> get("/api/v1/favourites?limit=1") + +    assert [%{"id" => post_id}] = json_response_and_validate_schema(result, 200) +    assert post_id == second_post.id + +    # Using the header for pagination works correctly +    [next, _] = get_resp_header(result, "link") |> hd() |> String.split(", ") +    [_, max_id] = Regex.run(~r/max_id=([^&]+)/, next) + +    assert max_id == third_favorite.id + +    result = +      conn +      |> get("/api/v1/favourites?max_id=#{max_id}") + +    assert [%{"id" => first_post_id}, %{"id" => third_post_id}] = +             json_response_and_validate_schema(result, 200) + +    assert first_post_id == first_post.id +    assert third_post_id == third_post.id +  end +    test "returns the favorites of a user" do      %{user: user, conn: conn} = oauth_access(["read:favourites"])      other_user = insert(:user)      {:ok, _} = CommonAPI.post(other_user, %{status: "bla"}) -    {:ok, activity} = CommonAPI.post(other_user, %{status: "traps are happy"}) +    {:ok, activity} = CommonAPI.post(other_user, %{status: "trees are happy"}) -    {:ok, _} = CommonAPI.favorite(user, activity.id) +    {:ok, last_like} = CommonAPI.favorite(user, activity.id)      first_conn = get(conn, "/api/v1/favourites") @@ -1566,9 +1601,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do      {:ok, _} = CommonAPI.favorite(user, second_activity.id) -    last_like = status["id"] - -    second_conn = get(conn, "/api/v1/favourites?since_id=#{last_like}") +    second_conn = get(conn, "/api/v1/favourites?since_id=#{last_like.id}")      assert [second_status] = json_response_and_validate_schema(second_conn, 200)      assert second_status["id"] == to_string(second_activity.id) diff --git a/test/web/mastodon_api/controllers/subscription_controller_test.exs b/test/web/mastodon_api/controllers/subscription_controller_test.exs index 4aa260663..d36bb1ae8 100644 --- a/test/web/mastodon_api/controllers/subscription_controller_test.exs +++ b/test/web/mastodon_api/controllers/subscription_controller_test.exs @@ -58,7 +58,9 @@ defmodule Pleroma.Web.MastodonAPI.SubscriptionControllerTest do        result =          conn          |> post("/api/v1/push/subscription", %{ -          "data" => %{"alerts" => %{"mention" => true, "test" => true}}, +          "data" => %{ +            "alerts" => %{"mention" => true, "test" => true, "pleroma:chat_mention" => true} +          },            "subscription" => @sub          })          |> json_response_and_validate_schema(200) @@ -66,7 +68,7 @@ defmodule Pleroma.Web.MastodonAPI.SubscriptionControllerTest do        [subscription] = Pleroma.Repo.all(Subscription)        assert %{ -               "alerts" => %{"mention" => true}, +               "alerts" => %{"mention" => true, "pleroma:chat_mention" => true},                 "endpoint" => subscription.endpoint,                 "id" => to_string(subscription.id),                 "server_key" => @server_key diff --git a/test/web/mastodon_api/controllers/timeline_controller_test.exs b/test/web/mastodon_api/controllers/timeline_controller_test.exs index 2375ac8e8..f069390c1 100644 --- a/test/web/mastodon_api/controllers/timeline_controller_test.exs +++ b/test/web/mastodon_api/controllers/timeline_controller_test.exs @@ -60,9 +60,9 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do    describe "public" do      @tag capture_log: true      test "the public timeline", %{conn: conn} do -      following = insert(:user) +      user = insert(:user) -      {:ok, _activity} = CommonAPI.post(following, %{status: "test"}) +      {:ok, activity} = CommonAPI.post(user, %{status: "test"})        _activity = insert(:note_activity, local: false) @@ -77,6 +77,13 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do        conn = get(build_conn(), "/api/v1/timelines/public?local=1")        assert [%{"content" => "test"}] = json_response_and_validate_schema(conn, :ok) + +      # does not contain repeats +      {:ok, _} = CommonAPI.repeat(activity.id, user) + +      conn = get(build_conn(), "/api/v1/timelines/public?local=true") + +      assert [_] = json_response_and_validate_schema(conn, :ok)      end      test "the public timeline includes only public statuses for an authenticated user" do @@ -90,6 +97,49 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do        res_conn = get(conn, "/api/v1/timelines/public")        assert length(json_response_and_validate_schema(res_conn, 200)) == 1      end + +    test "doesn't return replies if follower is posting with blocked user" do +      %{conn: conn, user: blocker} = oauth_access(["read:statuses"]) +      [blockee, friend] = insert_list(2, :user) +      {:ok, blocker} = User.follow(blocker, friend) +      {:ok, _} = User.block(blocker, blockee) + +      conn = assign(conn, :user, blocker) + +      {:ok, %{id: activity_id} = activity} = CommonAPI.post(friend, %{status: "hey!"}) + +      {:ok, reply_from_blockee} = +        CommonAPI.post(blockee, %{status: "heya", in_reply_to_status_id: activity}) + +      {:ok, _reply_from_friend} = +        CommonAPI.post(friend, %{status: "status", in_reply_to_status_id: reply_from_blockee}) + +      res_conn = get(conn, "/api/v1/timelines/public") +      [%{"id" => ^activity_id}] = json_response_and_validate_schema(res_conn, 200) +    end + +    test "doesn't return replies if follow is posting with users from blocked domain" do +      %{conn: conn, user: blocker} = oauth_access(["read:statuses"]) +      friend = insert(:user) +      blockee = insert(:user, ap_id: "https://example.com/users/blocked") +      {:ok, blocker} = User.follow(blocker, friend) +      {:ok, blocker} = User.block_domain(blocker, "example.com") + +      conn = assign(conn, :user, blocker) + +      {:ok, %{id: activity_id} = activity} = CommonAPI.post(friend, %{status: "hey!"}) + +      {:ok, reply_from_blockee} = +        CommonAPI.post(blockee, %{status: "heya", in_reply_to_status_id: activity}) + +      {:ok, _reply_from_friend} = +        CommonAPI.post(friend, %{status: "status", in_reply_to_status_id: reply_from_blockee}) + +      res_conn = get(conn, "/api/v1/timelines/public") + +      activities = json_response_and_validate_schema(res_conn, 200) +      [%{"id" => ^activity_id}] = activities +    end    end    defp local_and_remote_activities do diff --git a/test/web/mastodon_api/views/account_view_test.exs b/test/web/mastodon_api/views/account_view_test.exs index f91333e5c..80b1f734c 100644 --- a/test/web/mastodon_api/views/account_view_test.exs +++ b/test/web/mastodon_api/views/account_view_test.exs @@ -33,7 +33,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do          bio:            "<script src=\"invalid-html\"></script><span>valid html</span>. a<br>b<br/>c<br >d<br />f '&<>\"",          inserted_at: ~N[2017-08-15 15:47:06.597036], -        emoji: %{"karjalanpiirakka" => "/file.png"} +        emoji: %{"karjalanpiirakka" => "/file.png"}, +        raw_bio: "valid html. a\nb\nc\nd\nf '&<>\""        })      expected = %{ @@ -72,6 +73,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do          fields: []        },        pleroma: %{ +        ap_id: user.ap_id,          background_image: "https://example.com/images/asuka_hospital.png",          confirmation_pending: false,          tags: [], @@ -148,6 +150,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do          fields: []        },        pleroma: %{ +        ap_id: user.ap_id,          background_image: nil,          confirmation_pending: false,          tags: [], diff --git a/test/web/mastodon_api/views/conversation_view_test.exs b/test/web/mastodon_api/views/conversation_view_test.exs index 6f84366f8..2e8203c9b 100644 --- a/test/web/mastodon_api/views/conversation_view_test.exs +++ b/test/web/mastodon_api/views/conversation_view_test.exs @@ -15,8 +15,17 @@ defmodule Pleroma.Web.MastodonAPI.ConversationViewTest do      user = insert(:user)      other_user = insert(:user) +    {:ok, parent} = CommonAPI.post(user, %{status: "parent"}) +      {:ok, activity} = -      CommonAPI.post(user, %{status: "hey @#{other_user.nickname}", visibility: "direct"}) +      CommonAPI.post(user, %{ +        status: "hey @#{other_user.nickname}", +        visibility: "direct", +        in_reply_to_id: parent.id +      }) + +    {:ok, _reply_activity} = +      CommonAPI.post(user, %{status: "hu", visibility: "public", in_reply_to_id: parent.id})      [participation] = Participation.for_user_with_last_activity_id(user) diff --git a/test/web/mastodon_api/views/notification_view_test.exs b/test/web/mastodon_api/views/notification_view_test.exs index f15be1df1..8e0e58538 100644 --- a/test/web/mastodon_api/views/notification_view_test.exs +++ b/test/web/mastodon_api/views/notification_view_test.exs @@ -6,7 +6,10 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do    use Pleroma.DataCase    alias Pleroma.Activity +  alias Pleroma.Chat +  alias Pleroma.Chat.MessageReference    alias Pleroma.Notification +  alias Pleroma.Object    alias Pleroma.Repo    alias Pleroma.User    alias Pleroma.Web.CommonAPI @@ -14,6 +17,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do    alias Pleroma.Web.MastodonAPI.AccountView    alias Pleroma.Web.MastodonAPI.NotificationView    alias Pleroma.Web.MastodonAPI.StatusView +  alias Pleroma.Web.PleromaAPI.Chat.MessageReferenceView    import Pleroma.Factory    defp test_notifications_rendering(notifications, user, expected_result) do @@ -31,6 +35,30 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do      assert expected_result == result    end +  test "ChatMessage notification" do +    user = insert(:user) +    recipient = insert(:user) +    {:ok, activity} = CommonAPI.post_chat_message(user, recipient, "what's up my dude") + +    {:ok, [notification]} = Notification.create_notifications(activity) + +    object = Object.normalize(activity) +    chat = Chat.get(recipient.id, user.ap_id) + +    cm_ref = MessageReference.for_chat_and_object(chat, object) + +    expected = %{ +      id: to_string(notification.id), +      pleroma: %{is_seen: false, is_muted: false}, +      type: "pleroma:chat_mention", +      account: AccountView.render("show.json", %{user: user, for: recipient}), +      chat_message: MessageReferenceView.render("show.json", %{chat_message_reference: cm_ref}), +      created_at: Utils.to_masto_date(notification.inserted_at) +    } + +    test_notifications_rendering([notification], recipient, [expected]) +  end +    test "Mention notification" do      user = insert(:user)      mentioned_user = insert(:user) @@ -40,7 +68,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do      expected = %{        id: to_string(notification.id), -      pleroma: %{is_seen: false}, +      pleroma: %{is_seen: false, is_muted: false},        type: "mention",        account:          AccountView.render("show.json", %{ @@ -64,7 +92,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do      expected = %{        id: to_string(notification.id), -      pleroma: %{is_seen: false}, +      pleroma: %{is_seen: false, is_muted: false},        type: "favourite",        account: AccountView.render("show.json", %{user: another_user, for: user}),        status: StatusView.render("show.json", %{activity: create_activity, for: user}), @@ -84,7 +112,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do      expected = %{        id: to_string(notification.id), -      pleroma: %{is_seen: false}, +      pleroma: %{is_seen: false, is_muted: false},        type: "reblog",        account: AccountView.render("show.json", %{user: another_user, for: user}),        status: StatusView.render("show.json", %{activity: reblog_activity, for: user}), @@ -102,7 +130,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do      expected = %{        id: to_string(notification.id), -      pleroma: %{is_seen: false}, +      pleroma: %{is_seen: false, is_muted: false},        type: "follow",        account: AccountView.render("show.json", %{user: follower, for: followed}),        created_at: Utils.to_masto_date(notification.inserted_at) @@ -111,9 +139,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do      test_notifications_rendering([notification], followed, [expected])      User.perform(:delete, follower) -    notification = Notification |> Repo.one() |> Repo.preload(:activity) - -    test_notifications_rendering([notification], followed, []) +    refute Repo.one(Notification)    end    @tag capture_log: true @@ -145,7 +171,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do      expected = %{        id: to_string(notification.id), -      pleroma: %{is_seen: false}, +      pleroma: %{is_seen: false, is_muted: false},        type: "move",        account: AccountView.render("show.json", %{user: old_user, for: follower}),        target: AccountView.render("show.json", %{user: new_user, for: follower}), @@ -170,7 +196,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do      expected = %{        id: to_string(notification.id), -      pleroma: %{is_seen: false}, +      pleroma: %{is_seen: false, is_muted: false},        type: "pleroma:emoji_reaction",        emoji: "☕",        account: AccountView.render("show.json", %{user: other_user, for: user}), @@ -180,4 +206,26 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do      test_notifications_rendering([notification], user, [expected])    end + +  test "muted notification" do +    user = insert(:user) +    another_user = insert(:user) + +    {:ok, _} = Pleroma.UserRelationship.create_mute(user, another_user) +    {:ok, create_activity} = CommonAPI.post(user, %{status: "hey"}) +    {:ok, favorite_activity} = CommonAPI.favorite(another_user, create_activity.id) +    {:ok, [notification]} = Notification.create_notifications(favorite_activity) +    create_activity = Activity.get_by_id(create_activity.id) + +    expected = %{ +      id: to_string(notification.id), +      pleroma: %{is_seen: false, is_muted: true}, +      type: "favourite", +      account: AccountView.render("show.json", %{user: another_user, for: user}), +      status: StatusView.render("show.json", %{activity: create_activity, for: user}), +      created_at: Utils.to_masto_date(notification.inserted_at) +    } + +    test_notifications_rendering([notification], user, [expected]) +  end  end diff --git a/test/web/mastodon_api/views/status_view_test.exs b/test/web/mastodon_api/views/status_view_test.exs index 5cbadf0fc..f90a0c273 100644 --- a/test/web/mastodon_api/views/status_view_test.exs +++ b/test/web/mastodon_api/views/status_view_test.exs @@ -226,7 +226,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do          expires_at: nil,          direct_conversation_id: nil,          thread_muted: false, -        emoji_reactions: [] +        emoji_reactions: [], +        parent_visible: false        }      } @@ -620,4 +621,20 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do      assert status.visibility == "list"    end + +  test "has a field for parent visibility" do +    user = insert(:user) +    poster = insert(:user) + +    {:ok, invisible} = CommonAPI.post(poster, %{status: "hey", visibility: "private"}) + +    {:ok, visible} = +      CommonAPI.post(poster, %{status: "hey", visibility: "private", in_reply_to_id: invisible.id}) + +    status = StatusView.render("show.json", activity: visible, for: user) +    refute status.pleroma.parent_visible + +    status = StatusView.render("show.json", activity: visible, for: poster) +    assert status.pleroma.parent_visible +  end  end diff --git a/test/web/media_proxy/invalidation_test.exs b/test/web/media_proxy/invalidation_test.exs new file mode 100644 index 000000000..926ae74ca --- /dev/null +++ b/test/web/media_proxy/invalidation_test.exs @@ -0,0 +1,64 @@ +defmodule Pleroma.Web.MediaProxy.InvalidationTest do +  use ExUnit.Case +  use Pleroma.Tests.Helpers + +  alias Pleroma.Config +  alias Pleroma.Web.MediaProxy.Invalidation + +  import ExUnit.CaptureLog +  import Mock +  import Tesla.Mock + +  setup do: clear_config([:media_proxy]) + +  setup do +    on_exit(fn -> Cachex.clear(:banned_urls_cache) end) +  end + +  describe "Invalidation.Http" do +    test "perform request to clear cache" do +      Config.put([:media_proxy, :enabled], false) +      Config.put([:media_proxy, :invalidation, :enabled], true) +      Config.put([:media_proxy, :invalidation, :provider], Invalidation.Http) + +      Config.put([Invalidation.Http], method: :purge, headers: [{"x-refresh", 1}]) +      image_url = "http://example.com/media/example.jpg" +      Pleroma.Web.MediaProxy.put_in_banned_urls(image_url) + +      mock(fn +        %{ +          method: :purge, +          url: "http://example.com/media/example.jpg", +          headers: [{"x-refresh", 1}] +        } -> +          %Tesla.Env{status: 200} +      end) + +      assert capture_log(fn -> +               assert Pleroma.Web.MediaProxy.in_banned_urls(image_url) +               assert Invalidation.purge([image_url]) == {:ok, [image_url]} +               assert Pleroma.Web.MediaProxy.in_banned_urls(image_url) +             end) =~ "Running cache purge: [\"#{image_url}\"]" +    end +  end + +  describe "Invalidation.Script" do +    test "run script to clear cache" do +      Config.put([:media_proxy, :enabled], false) +      Config.put([:media_proxy, :invalidation, :enabled], true) +      Config.put([:media_proxy, :invalidation, :provider], Invalidation.Script) +      Config.put([Invalidation.Script], script_path: "purge-nginx") + +      image_url = "http://example.com/media/example.jpg" +      Pleroma.Web.MediaProxy.put_in_banned_urls(image_url) + +      with_mocks [{System, [], [cmd: fn _, _ -> {"ok", 0} end]}] do +        assert capture_log(fn -> +                 assert Pleroma.Web.MediaProxy.in_banned_urls(image_url) +                 assert Invalidation.purge([image_url]) == {:ok, [image_url]} +                 assert Pleroma.Web.MediaProxy.in_banned_urls(image_url) +               end) =~ "Running cache purge: [\"#{image_url}\"]" +      end +    end +  end +end diff --git a/test/web/media_proxy/invalidations/http_test.exs b/test/web/media_proxy/invalidations/http_test.exs index 8a3b4141c..a1bef5237 100644 --- a/test/web/media_proxy/invalidations/http_test.exs +++ b/test/web/media_proxy/invalidations/http_test.exs @@ -5,6 +5,10 @@ defmodule Pleroma.Web.MediaProxy.Invalidation.HttpTest do    import ExUnit.CaptureLog    import Tesla.Mock +  setup do +    on_exit(fn -> Cachex.clear(:banned_urls_cache) end) +  end +    test "logs hasn't error message when request is valid" do      mock(fn        %{method: :purge, url: "http://example.com/media/example.jpg"} -> @@ -14,8 +18,8 @@ defmodule Pleroma.Web.MediaProxy.Invalidation.HttpTest do      refute capture_log(fn ->               assert Invalidation.Http.purge(                        ["http://example.com/media/example.jpg"], -                      %{} -                    ) == {:ok, "success"} +                      [] +                    ) == {:ok, ["http://example.com/media/example.jpg"]}             end) =~ "Error while cache purge"    end @@ -28,8 +32,8 @@ defmodule Pleroma.Web.MediaProxy.Invalidation.HttpTest do      assert capture_log(fn ->               assert Invalidation.Http.purge(                        ["http://example.com/media/example1.jpg"], -                      %{} -                    ) == {:ok, "success"} +                      [] +                    ) == {:ok, ["http://example.com/media/example1.jpg"]}             end) =~ "Error while cache purge: url - http://example.com/media/example1.jpg"    end  end diff --git a/test/web/media_proxy/invalidations/script_test.exs b/test/web/media_proxy/invalidations/script_test.exs index 1358963ab..51833ab18 100644 --- a/test/web/media_proxy/invalidations/script_test.exs +++ b/test/web/media_proxy/invalidations/script_test.exs @@ -4,17 +4,23 @@ defmodule Pleroma.Web.MediaProxy.Invalidation.ScriptTest do    import ExUnit.CaptureLog +  setup do +    on_exit(fn -> Cachex.clear(:banned_urls_cache) end) +  end +    test "it logger error when script not found" do      assert capture_log(fn ->               assert Invalidation.Script.purge(                        ["http://example.com/media/example.jpg"], -                      %{script_path: "./example"} -                    ) == {:error, "\"%ErlangError{original: :enoent}\""} -           end) =~ "Error while cache purge: \"%ErlangError{original: :enoent}\"" +                      script_path: "./example" +                    ) == {:error, "%ErlangError{original: :enoent}"} +           end) =~ "Error while cache purge: %ErlangError{original: :enoent}" -    assert Invalidation.Script.purge( -             ["http://example.com/media/example.jpg"], -             %{} -           ) == {:error, "not found script path"} +    capture_log(fn -> +      assert Invalidation.Script.purge( +               ["http://example.com/media/example.jpg"], +               [] +             ) == {:error, "\"not found script path\""} +    end)    end  end diff --git a/test/web/media_proxy/media_proxy_controller_test.exs b/test/web/media_proxy/media_proxy_controller_test.exs index da79d38a5..d61cef83b 100644 --- a/test/web/media_proxy/media_proxy_controller_test.exs +++ b/test/web/media_proxy/media_proxy_controller_test.exs @@ -10,6 +10,10 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyControllerTest do    setup do: clear_config(:media_proxy)    setup do: clear_config([Pleroma.Web.Endpoint, :secret_key_base]) +  setup do +    on_exit(fn -> Cachex.clear(:banned_urls_cache) end) +  end +    test "it returns 404 when MediaProxy disabled", %{conn: conn} do      Config.put([:media_proxy, :enabled], false) @@ -66,4 +70,16 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyControllerTest do        assert %Plug.Conn{status: :success} = get(conn, url)      end    end + +  test "it returns 404 when url contains in banned_urls cache", %{conn: conn} do +    Config.put([:media_proxy, :enabled], true) +    Config.put([Pleroma.Web.Endpoint, :secret_key_base], "00000000000") +    url = Pleroma.Web.MediaProxy.encode_url("https://google.fn/test.png") +    Pleroma.Web.MediaProxy.put_in_banned_urls("https://google.fn/test.png") + +    with_mock Pleroma.ReverseProxy, +      call: fn _conn, _url, _opts -> %Plug.Conn{status: :success} end do +      assert %Plug.Conn{status: 404, resp_body: "Not Found"} = get(conn, url) +    end +  end  end diff --git a/test/web/node_info_test.exs b/test/web/node_info_test.exs index 9bcc07b37..06b33607f 100644 --- a/test/web/node_info_test.exs +++ b/test/web/node_info_test.exs @@ -67,10 +67,10 @@ defmodule Pleroma.Web.NodeInfoTest do    end    test "returns fieldsLimits field", %{conn: conn} do -    Config.put([:instance, :max_account_fields], 10) -    Config.put([:instance, :max_remote_account_fields], 15) -    Config.put([:instance, :account_field_name_length], 255) -    Config.put([:instance, :account_field_value_length], 2048) +    clear_config([:instance, :max_account_fields], 10) +    clear_config([:instance, :max_remote_account_fields], 15) +    clear_config([:instance, :account_field_name_length], 255) +    clear_config([:instance, :account_field_value_length], 2048)      response =        conn @@ -84,8 +84,7 @@ defmodule Pleroma.Web.NodeInfoTest do    end    test "it returns the safe_dm_mentions feature if enabled", %{conn: conn} do -    option = Config.get([:instance, :safe_dm_mentions]) -    Config.put([:instance, :safe_dm_mentions], true) +    clear_config([:instance, :safe_dm_mentions], true)      response =        conn @@ -102,8 +101,6 @@ defmodule Pleroma.Web.NodeInfoTest do        |> json_response(:ok)      refute "safe_dm_mentions" in response["metadata"]["features"] - -    Config.put([:instance, :safe_dm_mentions], option)    end    describe "`metadata/federation/enabled`" do @@ -145,7 +142,8 @@ defmodule Pleroma.Web.NodeInfoTest do        "shareable_emoji_packs",        "multifetch",        "pleroma_emoji_reactions", -      "pleroma:api/v1/notifications:include_types_filter" +      "pleroma:api/v1/notifications:include_types_filter", +      "pleroma_chat_messages"      ]      assert MapSet.subset?( @@ -155,14 +153,11 @@ defmodule Pleroma.Web.NodeInfoTest do    end    test "it shows MRF transparency data if enabled", %{conn: conn} do -    config = Config.get([:instance, :rewrite_policy]) -    Config.put([:instance, :rewrite_policy], [Pleroma.Web.ActivityPub.MRF.SimplePolicy]) - -    option = Config.get([:instance, :mrf_transparency]) -    Config.put([:instance, :mrf_transparency], true) +    clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.SimplePolicy]) +    clear_config([:mrf, :transparency], true)      simple_config = %{"reject" => ["example.com"]} -    Config.put(:mrf_simple, simple_config) +    clear_config(:mrf_simple, simple_config)      response =        conn @@ -170,26 +165,17 @@ defmodule Pleroma.Web.NodeInfoTest do        |> json_response(:ok)      assert response["metadata"]["federation"]["mrf_simple"] == simple_config - -    Config.put([:instance, :rewrite_policy], config) -    Config.put([:instance, :mrf_transparency], option) -    Config.put(:mrf_simple, %{})    end    test "it performs exclusions from MRF transparency data if configured", %{conn: conn} do -    config = Config.get([:instance, :rewrite_policy]) -    Config.put([:instance, :rewrite_policy], [Pleroma.Web.ActivityPub.MRF.SimplePolicy]) - -    option = Config.get([:instance, :mrf_transparency]) -    Config.put([:instance, :mrf_transparency], true) - -    exclusions = Config.get([:instance, :mrf_transparency_exclusions]) -    Config.put([:instance, :mrf_transparency_exclusions], ["other.site"]) +    clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.SimplePolicy]) +    clear_config([:mrf, :transparency], true) +    clear_config([:mrf, :transparency_exclusions], ["other.site"])      simple_config = %{"reject" => ["example.com", "other.site"]} -    expected_config = %{"reject" => ["example.com"]} +    clear_config(:mrf_simple, simple_config) -    Config.put(:mrf_simple, simple_config) +    expected_config = %{"reject" => ["example.com"]}      response =        conn @@ -198,10 +184,5 @@ defmodule Pleroma.Web.NodeInfoTest do      assert response["metadata"]["federation"]["mrf_simple"] == expected_config      assert response["metadata"]["federation"]["exclusions"] == true - -    Config.put([:instance, :rewrite_policy], config) -    Config.put([:instance, :mrf_transparency], option) -    Config.put([:instance, :mrf_transparency_exclusions], exclusions) -    Config.put(:mrf_simple, %{})    end  end diff --git a/test/web/pleroma_api/controllers/chat_controller_test.exs b/test/web/pleroma_api/controllers/chat_controller_test.exs new file mode 100644 index 000000000..82e16741d --- /dev/null +++ b/test/web/pleroma_api/controllers/chat_controller_test.exs @@ -0,0 +1,336 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only +defmodule Pleroma.Web.PleromaAPI.ChatControllerTest do +  use Pleroma.Web.ConnCase, async: true + +  alias Pleroma.Chat +  alias Pleroma.Chat.MessageReference +  alias Pleroma.Object +  alias Pleroma.User +  alias Pleroma.Web.ActivityPub.ActivityPub +  alias Pleroma.Web.CommonAPI + +  import Pleroma.Factory + +  describe "POST /api/v1/pleroma/chats/:id/messages/:message_id/read" do +    setup do: oauth_access(["write:chats"]) + +    test "it marks one message as read", %{conn: conn, user: user} do +      other_user = insert(:user) + +      {:ok, create} = CommonAPI.post_chat_message(other_user, user, "sup") +      {:ok, _create} = CommonAPI.post_chat_message(other_user, user, "sup part 2") +      {:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id) +      object = Object.normalize(create, false) +      cm_ref = MessageReference.for_chat_and_object(chat, object) + +      assert cm_ref.unread == true + +      result = +        conn +        |> post("/api/v1/pleroma/chats/#{chat.id}/messages/#{cm_ref.id}/read") +        |> json_response_and_validate_schema(200) + +      assert result["unread"] == false + +      cm_ref = MessageReference.for_chat_and_object(chat, object) + +      assert cm_ref.unread == false +    end +  end + +  describe "POST /api/v1/pleroma/chats/:id/read" do +    setup do: oauth_access(["write:chats"]) + +    test "given a `last_read_id`, it marks everything until then as read", %{ +      conn: conn, +      user: user +    } do +      other_user = insert(:user) + +      {:ok, create} = CommonAPI.post_chat_message(other_user, user, "sup") +      {:ok, _create} = CommonAPI.post_chat_message(other_user, user, "sup part 2") +      {:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id) +      object = Object.normalize(create, false) +      cm_ref = MessageReference.for_chat_and_object(chat, object) + +      assert cm_ref.unread == true + +      result = +        conn +        |> put_req_header("content-type", "application/json") +        |> post("/api/v1/pleroma/chats/#{chat.id}/read", %{"last_read_id" => cm_ref.id}) +        |> json_response_and_validate_schema(200) + +      assert result["unread"] == 1 + +      cm_ref = MessageReference.for_chat_and_object(chat, object) + +      assert cm_ref.unread == false +    end +  end + +  describe "POST /api/v1/pleroma/chats/:id/messages" do +    setup do: oauth_access(["write:chats"]) + +    test "it posts a message to the chat", %{conn: conn, user: user} do +      other_user = insert(:user) + +      {:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id) + +      result = +        conn +        |> put_req_header("content-type", "application/json") +        |> post("/api/v1/pleroma/chats/#{chat.id}/messages", %{"content" => "Hallo!!"}) +        |> json_response_and_validate_schema(200) + +      assert result["content"] == "Hallo!!" +      assert result["chat_id"] == chat.id |> to_string() +    end + +    test "it fails if there is no content", %{conn: conn, user: user} do +      other_user = insert(:user) + +      {:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id) + +      result = +        conn +        |> put_req_header("content-type", "application/json") +        |> post("/api/v1/pleroma/chats/#{chat.id}/messages") +        |> json_response_and_validate_schema(400) + +      assert result +    end + +    test "it works with an attachment", %{conn: conn, user: user} do +      file = %Plug.Upload{ +        content_type: "image/jpg", +        path: Path.absname("test/fixtures/image.jpg"), +        filename: "an_image.jpg" +      } + +      {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id) + +      other_user = insert(:user) + +      {:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id) + +      result = +        conn +        |> put_req_header("content-type", "application/json") +        |> post("/api/v1/pleroma/chats/#{chat.id}/messages", %{ +          "media_id" => to_string(upload.id) +        }) +        |> json_response_and_validate_schema(200) + +      assert result["attachment"] +    end +  end + +  describe "DELETE /api/v1/pleroma/chats/:id/messages/:message_id" do +    setup do: oauth_access(["write:chats"]) + +    test "it deletes a message from the chat", %{conn: conn, user: user} do +      recipient = insert(:user) + +      {:ok, message} = +        CommonAPI.post_chat_message(user, recipient, "Hello darkness my old friend") + +      {:ok, other_message} = CommonAPI.post_chat_message(recipient, user, "nico nico ni") + +      object = Object.normalize(message, false) + +      chat = Chat.get(user.id, recipient.ap_id) + +      cm_ref = MessageReference.for_chat_and_object(chat, object) + +      # Deleting your own message removes the message and the reference +      result = +        conn +        |> put_req_header("content-type", "application/json") +        |> delete("/api/v1/pleroma/chats/#{chat.id}/messages/#{cm_ref.id}") +        |> json_response_and_validate_schema(200) + +      assert result["id"] == cm_ref.id +      refute MessageReference.get_by_id(cm_ref.id) +      assert %{data: %{"type" => "Tombstone"}} = Object.get_by_id(object.id) + +      # Deleting other people's messages just removes the reference +      object = Object.normalize(other_message, false) +      cm_ref = MessageReference.for_chat_and_object(chat, object) + +      result = +        conn +        |> put_req_header("content-type", "application/json") +        |> delete("/api/v1/pleroma/chats/#{chat.id}/messages/#{cm_ref.id}") +        |> json_response_and_validate_schema(200) + +      assert result["id"] == cm_ref.id +      refute MessageReference.get_by_id(cm_ref.id) +      assert Object.get_by_id(object.id) +    end +  end + +  describe "GET /api/v1/pleroma/chats/:id/messages" do +    setup do: oauth_access(["read:chats"]) + +    test "it paginates", %{conn: conn, user: user} do +      recipient = insert(:user) + +      Enum.each(1..30, fn _ -> +        {:ok, _} = CommonAPI.post_chat_message(user, recipient, "hey") +      end) + +      chat = Chat.get(user.id, recipient.ap_id) + +      result = +        conn +        |> get("/api/v1/pleroma/chats/#{chat.id}/messages") +        |> json_response_and_validate_schema(200) + +      assert length(result) == 20 + +      result = +        conn +        |> get("/api/v1/pleroma/chats/#{chat.id}/messages?max_id=#{List.last(result)["id"]}") +        |> json_response_and_validate_schema(200) + +      assert length(result) == 10 +    end + +    test "it returns the messages for a given chat", %{conn: conn, user: user} do +      other_user = insert(:user) +      third_user = insert(:user) + +      {:ok, _} = CommonAPI.post_chat_message(user, other_user, "hey") +      {:ok, _} = CommonAPI.post_chat_message(user, third_user, "hey") +      {:ok, _} = CommonAPI.post_chat_message(user, other_user, "how are you?") +      {:ok, _} = CommonAPI.post_chat_message(other_user, user, "fine, how about you?") + +      chat = Chat.get(user.id, other_user.ap_id) + +      result = +        conn +        |> get("/api/v1/pleroma/chats/#{chat.id}/messages") +        |> json_response_and_validate_schema(200) + +      result +      |> Enum.each(fn message -> +        assert message["chat_id"] == chat.id |> to_string() +      end) + +      assert length(result) == 3 + +      # Trying to get the chat of a different user +      result = +        conn +        |> assign(:user, other_user) +        |> get("/api/v1/pleroma/chats/#{chat.id}/messages") + +      assert result |> json_response(404) +    end +  end + +  describe "POST /api/v1/pleroma/chats/by-account-id/:id" do +    setup do: oauth_access(["write:chats"]) + +    test "it creates or returns a chat", %{conn: conn} do +      other_user = insert(:user) + +      result = +        conn +        |> post("/api/v1/pleroma/chats/by-account-id/#{other_user.id}") +        |> json_response_and_validate_schema(200) + +      assert result["id"] +    end +  end + +  describe "GET /api/v1/pleroma/chats/:id" do +    setup do: oauth_access(["read:chats"]) + +    test "it returns a chat", %{conn: conn, user: user} do +      other_user = insert(:user) + +      {:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id) + +      result = +        conn +        |> get("/api/v1/pleroma/chats/#{chat.id}") +        |> json_response_and_validate_schema(200) + +      assert result["id"] == to_string(chat.id) +    end +  end + +  describe "GET /api/v1/pleroma/chats" do +    setup do: oauth_access(["read:chats"]) + +    test "it does not return chats with users you blocked", %{conn: conn, user: user} do +      recipient = insert(:user) + +      {:ok, _} = Chat.get_or_create(user.id, recipient.ap_id) + +      result = +        conn +        |> get("/api/v1/pleroma/chats") +        |> json_response_and_validate_schema(200) + +      assert length(result) == 1 + +      User.block(user, recipient) + +      result = +        conn +        |> get("/api/v1/pleroma/chats") +        |> json_response_and_validate_schema(200) + +      assert length(result) == 0 +    end + +    test "it returns all chats", %{conn: conn, user: user} do +      Enum.each(1..30, fn _ -> +        recipient = insert(:user) +        {:ok, _} = Chat.get_or_create(user.id, recipient.ap_id) +      end) + +      result = +        conn +        |> get("/api/v1/pleroma/chats") +        |> json_response_and_validate_schema(200) + +      assert length(result) == 30 +    end + +    test "it return a list of chats the current user is participating in, in descending order of updates", +         %{conn: conn, user: user} do +      har = insert(:user) +      jafnhar = insert(:user) +      tridi = insert(:user) + +      {:ok, chat_1} = Chat.get_or_create(user.id, har.ap_id) +      :timer.sleep(1000) +      {:ok, _chat_2} = Chat.get_or_create(user.id, jafnhar.ap_id) +      :timer.sleep(1000) +      {:ok, chat_3} = Chat.get_or_create(user.id, tridi.ap_id) +      :timer.sleep(1000) + +      # bump the second one +      {:ok, chat_2} = Chat.bump_or_create(user.id, jafnhar.ap_id) + +      result = +        conn +        |> get("/api/v1/pleroma/chats") +        |> json_response_and_validate_schema(200) + +      ids = Enum.map(result, & &1["id"]) + +      assert ids == [ +               chat_2.id |> to_string(), +               chat_3.id |> to_string(), +               chat_1.id |> to_string() +             ] +    end +  end +end diff --git a/test/web/pleroma_api/controllers/emoji_pack_controller_test.exs b/test/web/pleroma_api/controllers/emoji_pack_controller_test.exs index ee3d281a0..df58a5eb6 100644 --- a/test/web/pleroma_api/controllers/emoji_pack_controller_test.exs +++ b/test/web/pleroma_api/controllers/emoji_pack_controller_test.exs @@ -30,15 +30,55 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do    test "GET /api/pleroma/emoji/packs", %{conn: conn} do      resp = conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200) -    shared = resp["test_pack"] -    assert shared["files"] == %{"blank" => "blank.png"} +    assert resp["count"] == 3 + +    assert resp["packs"] +           |> Map.keys() +           |> length() == 3 + +    shared = resp["packs"]["test_pack"] +    assert shared["files"] == %{"blank" => "blank.png", "blank2" => "blank2.png"}      assert Map.has_key?(shared["pack"], "download-sha256")      assert shared["pack"]["can-download"]      assert shared["pack"]["share-files"] -    non_shared = resp["test_pack_nonshared"] +    non_shared = resp["packs"]["test_pack_nonshared"]      assert non_shared["pack"]["share-files"] == false      assert non_shared["pack"]["can-download"] == false + +    resp = +      conn +      |> get("/api/pleroma/emoji/packs?page_size=1") +      |> json_response_and_validate_schema(200) + +    assert resp["count"] == 3 + +    packs = Map.keys(resp["packs"]) + +    assert length(packs) == 1 + +    [pack1] = packs + +    resp = +      conn +      |> get("/api/pleroma/emoji/packs?page_size=1&page=2") +      |> json_response_and_validate_schema(200) + +    assert resp["count"] == 3 +    packs = Map.keys(resp["packs"]) +    assert length(packs) == 1 +    [pack2] = packs + +    resp = +      conn +      |> get("/api/pleroma/emoji/packs?page_size=1&page=3") +      |> json_response_and_validate_schema(200) + +    assert resp["count"] == 3 +    packs = Map.keys(resp["packs"]) +    assert length(packs) == 1 +    [pack3] = packs +    assert [pack1, pack2, pack3] |> Enum.uniq() |> length() == 3    end    describe "GET /api/pleroma/emoji/packs/remote" do @@ -332,7 +372,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do          Map.put(            new_data,            "fallback-src-sha256", -          "74409E2674DAA06C072729C6C8426C4CB3B7E0B85ED77792DB7A436E11D76DAF" +          "1967BB4E42BCC34BCC12D57BE7811D3B7BE52F965BCE45C87BD377B9499CE11D"          )        assert ctx[:admin_conn] @@ -398,7 +438,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do        assert admin_conn               |> put_req_header("content-type", "multipart/form-data")               |> post("/api/pleroma/emoji/packs/test_pack/files", %{ -               shortcode: "blank2", +               shortcode: "blank3",                 filename: "dir/blank.png",                 file: %Plug.Upload{                   filename: "blank.png", @@ -407,7 +447,8 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do               })               |> json_response_and_validate_schema(200) == %{                 "blank" => "blank.png", -               "blank2" => "dir/blank.png" +               "blank2" => "blank2.png", +               "blank3" => "dir/blank.png"               }        assert File.exists?("#{@emoji_path}/test_pack/dir/blank.png") @@ -431,7 +472,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do        assert admin_conn               |> put_req_header("content-type", "multipart/form-data")               |> post("/api/pleroma/emoji/packs/test_pack/files", %{ -               shortcode: "blank2", +               shortcode: "blank3",                 filename: "dir/blank.png",                 file: %Plug.Upload{                   filename: "blank.png", @@ -440,7 +481,8 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do               })               |> json_response_and_validate_schema(200) == %{                 "blank" => "blank.png", -               "blank2" => "dir/blank.png" +               "blank2" => "blank2.png", +               "blank3" => "dir/blank.png"               }        assert File.exists?("#{@emoji_path}/test_pack/dir/blank.png") @@ -448,14 +490,15 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do        assert admin_conn               |> put_req_header("content-type", "multipart/form-data")               |> patch("/api/pleroma/emoji/packs/test_pack/files", %{ -               shortcode: "blank2", -               new_shortcode: "blank3", +               shortcode: "blank3", +               new_shortcode: "blank4",                 new_filename: "dir_2/blank_3.png",                 force: true               })               |> json_response_and_validate_schema(200) == %{                 "blank" => "blank.png", -               "blank3" => "dir_2/blank_3.png" +               "blank2" => "blank2.png", +               "blank4" => "dir_2/blank_3.png"               }        assert File.exists?("#{@emoji_path}/test_pack/dir_2/blank_3.png") @@ -481,7 +524,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do        assert admin_conn               |> put_req_header("content-type", "multipart/form-data")               |> post("/api/pleroma/emoji/packs/not_loaded/files", %{ -               shortcode: "blank2", +               shortcode: "blank3",                 filename: "dir/blank.png",                 file: %Plug.Upload{                   filename: "blank.png", @@ -535,7 +578,8 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do               })               |> json_response_and_validate_schema(200) == %{                 "blank" => "blank.png", -               "blank4" => "dir/blank.png" +               "blank4" => "dir/blank.png", +               "blank2" => "blank2.png"               }        assert File.exists?("#{@emoji_path}/test_pack/dir/blank.png") @@ -549,7 +593,8 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do               })               |> json_response_and_validate_schema(200) == %{                 "blank3" => "dir_2/blank_3.png", -               "blank" => "blank.png" +               "blank" => "blank.png", +               "blank2" => "blank2.png"               }        refute File.exists?("#{@emoji_path}/test_pack/dir/") @@ -557,7 +602,10 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do        assert admin_conn               |> delete("/api/pleroma/emoji/packs/test_pack/files?shortcode=blank3") -             |> json_response_and_validate_schema(200) == %{"blank" => "blank.png"} +             |> json_response_and_validate_schema(200) == %{ +               "blank" => "blank.png", +               "blank2" => "blank2.png" +             }        refute File.exists?("#{@emoji_path}/test_pack/dir_2/") @@ -581,7 +629,8 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do               })               |> json_response_and_validate_schema(200) == %{                 "blank_url" => "blank_url.png", -               "blank" => "blank.png" +               "blank" => "blank.png", +               "blank2" => "blank2.png"               }        assert File.exists?("#{@emoji_path}/test_pack/blank_url.png") @@ -602,15 +651,16 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do               })               |> json_response_and_validate_schema(200) == %{                 "shortcode" => "shortcode.png", -               "blank" => "blank.png" +               "blank" => "blank.png", +               "blank2" => "blank2.png"               }      end      test "remove non existing shortcode in pack.json", %{admin_conn: admin_conn} do        assert admin_conn -             |> delete("/api/pleroma/emoji/packs/test_pack/files?shortcode=blank2") +             |> delete("/api/pleroma/emoji/packs/test_pack/files?shortcode=blank3")               |> json_response_and_validate_schema(:bad_request) == %{ -               "error" => "Emoji \"blank2\" does not exist" +               "error" => "Emoji \"blank3\" does not exist"               }      end @@ -618,12 +668,12 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do        assert admin_conn               |> put_req_header("content-type", "multipart/form-data")               |> patch("/api/pleroma/emoji/packs/test_pack/files", %{ -               shortcode: "blank2", -               new_shortcode: "blank3", +               shortcode: "blank3", +               new_shortcode: "blank4",                 new_filename: "dir_2/blank_3.png"               })               |> json_response_and_validate_schema(:bad_request) == %{ -               "error" => "Emoji \"blank2\" does not exist" +               "error" => "Emoji \"blank3\" does not exist"               }      end @@ -651,7 +701,8 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do        assert Jason.decode!(File.read!("#{@emoji_path}/test_created/pack.json")) == %{                 "pack" => %{}, -               "files" => %{} +               "files" => %{}, +               "files_count" => 0               }        assert admin_conn @@ -709,14 +760,14 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do      resp = conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200) -    refute Map.has_key?(resp, "test_pack_for_import") +    refute Map.has_key?(resp["packs"], "test_pack_for_import")      assert admin_conn             |> get("/api/pleroma/emoji/packs/import")             |> json_response_and_validate_schema(200) == ["test_pack_for_import"]      resp = conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200) -    assert resp["test_pack_for_import"]["files"] == %{"blank" => "blank.png"} +    assert resp["packs"]["test_pack_for_import"]["files"] == %{"blank" => "blank.png"}      File.rm!("#{@emoji_path}/test_pack_for_import/pack.json")      refute File.exists?("#{@emoji_path}/test_pack_for_import/pack.json") @@ -736,7 +787,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do      resp = conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200) -    assert resp["test_pack_for_import"]["files"] == %{ +    assert resp["packs"]["test_pack_for_import"]["files"] == %{               "blank" => "blank.png",               "blank2" => "blank.png",               "foo" => "blank.png" @@ -746,7 +797,8 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do    describe "GET /api/pleroma/emoji/packs/:name" do      test "shows pack.json", %{conn: conn} do        assert %{ -               "files" => %{"blank" => "blank.png"}, +               "files" => files, +               "files_count" => 2,                 "pack" => %{                   "can-download" => true,                   "description" => "Test description", @@ -759,6 +811,28 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do                 conn                 |> get("/api/pleroma/emoji/packs/test_pack")                 |> json_response_and_validate_schema(200) + +      assert files == %{"blank" => "blank.png", "blank2" => "blank2.png"} + +      assert %{ +               "files" => files, +               "files_count" => 2 +             } = +               conn +               |> get("/api/pleroma/emoji/packs/test_pack?page_size=1") +               |> json_response_and_validate_schema(200) + +      assert files |> Map.keys() |> length() == 1 + +      assert %{ +               "files" => files, +               "files_count" => 2 +             } = +               conn +               |> get("/api/pleroma/emoji/packs/test_pack?page_size=1&page=2") +               |> json_response_and_validate_schema(200) + +      assert files |> Map.keys() |> length() == 1      end      test "non existing pack", %{conn: conn} do diff --git a/test/web/pleroma_api/views/chat/message_reference_view_test.exs b/test/web/pleroma_api/views/chat/message_reference_view_test.exs new file mode 100644 index 000000000..e5b165255 --- /dev/null +++ b/test/web/pleroma_api/views/chat/message_reference_view_test.exs @@ -0,0 +1,61 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaAPI.Chat.MessageReferenceViewTest do +  use Pleroma.DataCase + +  alias Pleroma.Chat +  alias Pleroma.Chat.MessageReference +  alias Pleroma.Object +  alias Pleroma.Web.ActivityPub.ActivityPub +  alias Pleroma.Web.CommonAPI +  alias Pleroma.Web.PleromaAPI.Chat.MessageReferenceView + +  import Pleroma.Factory + +  test "it displays a chat message" do +    user = insert(:user) +    recipient = insert(:user) + +    file = %Plug.Upload{ +      content_type: "image/jpg", +      path: Path.absname("test/fixtures/image.jpg"), +      filename: "an_image.jpg" +    } + +    {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id) +    {:ok, activity} = CommonAPI.post_chat_message(user, recipient, "kippis :firefox:") + +    chat = Chat.get(user.id, recipient.ap_id) + +    object = Object.normalize(activity) + +    cm_ref = MessageReference.for_chat_and_object(chat, object) + +    chat_message = MessageReferenceView.render("show.json", chat_message_reference: cm_ref) + +    assert chat_message[:id] == cm_ref.id +    assert chat_message[:content] == "kippis :firefox:" +    assert chat_message[:account_id] == user.id +    assert chat_message[:chat_id] +    assert chat_message[:created_at] +    assert chat_message[:unread] == false +    assert match?([%{shortcode: "firefox"}], chat_message[:emojis]) + +    {:ok, activity} = CommonAPI.post_chat_message(recipient, user, "gkgkgk", media_id: upload.id) + +    object = Object.normalize(activity) + +    cm_ref = MessageReference.for_chat_and_object(chat, object) + +    chat_message_two = MessageReferenceView.render("show.json", chat_message_reference: cm_ref) + +    assert chat_message_two[:id] == cm_ref.id +    assert chat_message_two[:content] == "gkgkgk" +    assert chat_message_two[:account_id] == recipient.id +    assert chat_message_two[:chat_id] == chat_message[:chat_id] +    assert chat_message_two[:attachment] +    assert chat_message_two[:unread] == true +  end +end diff --git a/test/web/pleroma_api/views/chat_view_test.exs b/test/web/pleroma_api/views/chat_view_test.exs new file mode 100644 index 000000000..14eecb1bd --- /dev/null +++ b/test/web/pleroma_api/views/chat_view_test.exs @@ -0,0 +1,48 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaAPI.ChatViewTest do +  use Pleroma.DataCase + +  alias Pleroma.Chat +  alias Pleroma.Chat.MessageReference +  alias Pleroma.Object +  alias Pleroma.Web.CommonAPI +  alias Pleroma.Web.CommonAPI.Utils +  alias Pleroma.Web.MastodonAPI.AccountView +  alias Pleroma.Web.PleromaAPI.Chat.MessageReferenceView +  alias Pleroma.Web.PleromaAPI.ChatView + +  import Pleroma.Factory + +  test "it represents a chat" do +    user = insert(:user) +    recipient = insert(:user) + +    {:ok, chat} = Chat.get_or_create(user.id, recipient.ap_id) + +    represented_chat = ChatView.render("show.json", chat: chat) + +    assert represented_chat == %{ +             id: "#{chat.id}", +             account: AccountView.render("show.json", user: recipient), +             unread: 0, +             last_message: nil, +             updated_at: Utils.to_masto_date(chat.updated_at) +           } + +    {:ok, chat_message_creation} = CommonAPI.post_chat_message(user, recipient, "hello") + +    chat_message = Object.normalize(chat_message_creation, false) + +    {:ok, chat} = Chat.get_or_create(user.id, recipient.ap_id) + +    represented_chat = ChatView.render("show.json", chat: chat) + +    cm_ref = MessageReference.for_chat_and_object(chat, chat_message) + +    assert represented_chat[:last_message] == +             MessageReferenceView.render("show.json", chat_message_reference: cm_ref) +  end +end diff --git a/test/web/preload/instance_test.exs b/test/web/preload/instance_test.exs new file mode 100644 index 000000000..a46f28312 --- /dev/null +++ b/test/web/preload/instance_test.exs @@ -0,0 +1,48 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Preload.Providers.InstanceTest do +  use Pleroma.DataCase +  alias Pleroma.Web.Preload.Providers.Instance + +  setup do: {:ok, Instance.generate_terms(nil)} + +  test "it renders the info", %{"/api/v1/instance" => info} do +    assert %{ +             description: description, +             email: "admin@example.com", +             registrations: true +           } = info + +    assert String.equivalent?(description, "Pleroma: An efficient and flexible fediverse server") +  end + +  test "it renders the panel", %{"/instance/panel.html" => panel} do +    assert String.contains?( +             panel, +             "<p>Welcome to <a href=\"https://pleroma.social\" target=\"_blank\">Pleroma!</a></p>" +           ) +  end + +  test "it works with overrides" do +    clear_config([:instance, :static_dir], "test/fixtures/preload_static") + +    %{"/instance/panel.html" => panel} = Instance.generate_terms(nil) + +    assert String.contains?( +             panel, +             "HEY!" +           ) +  end + +  test "it renders the node_info", %{"/nodeinfo/2.0.json" => nodeinfo} do +    %{ +      metadata: metadata, +      version: "2.0" +    } = nodeinfo + +    assert metadata.private == false +    assert metadata.suggestions == %{enabled: false} +  end +end diff --git a/test/web/preload/status_net_test.exs b/test/web/preload/status_net_test.exs new file mode 100644 index 000000000..df7acdb11 --- /dev/null +++ b/test/web/preload/status_net_test.exs @@ -0,0 +1,15 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Preload.Providers.StatusNetTest do +  use Pleroma.DataCase +  alias Pleroma.Web.Preload.Providers.StatusNet + +  setup do: {:ok, StatusNet.generate_terms(nil)} + +  test "it renders the info", %{"/api/statusnet/config.json" => info} do +    assert {:ok, res} = Jason.decode(info) +    assert res["site"] +  end +end diff --git a/test/web/preload/timeline_test.exs b/test/web/preload/timeline_test.exs new file mode 100644 index 000000000..fea95a6a4 --- /dev/null +++ b/test/web/preload/timeline_test.exs @@ -0,0 +1,74 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Preload.Providers.TimelineTest do +  use Pleroma.DataCase +  import Pleroma.Factory + +  alias Pleroma.Web.CommonAPI +  alias Pleroma.Web.Preload.Providers.Timelines + +  @public_url "/api/v1/timelines/public" + +  describe "unauthenticated timeliness when restricted" do +    setup do +      svd_config = Pleroma.Config.get([:restrict_unauthenticated, :timelines]) +      Pleroma.Config.put([:restrict_unauthenticated, :timelines], %{local: true, federated: true}) + +      on_exit(fn -> +        Pleroma.Config.put([:restrict_unauthenticated, :timelines], svd_config) +      end) + +      :ok +    end + +    test "return nothing" do +      tl_data = Timelines.generate_terms(%{}) + +      refute Map.has_key?(tl_data, "/api/v1/timelines/public") +    end +  end + +  describe "unauthenticated timeliness when unrestricted" do +    setup do +      svd_config = Pleroma.Config.get([:restrict_unauthenticated, :timelines]) + +      Pleroma.Config.put([:restrict_unauthenticated, :timelines], %{ +        local: false, +        federated: false +      }) + +      on_exit(fn -> +        Pleroma.Config.put([:restrict_unauthenticated, :timelines], svd_config) +      end) + +      {:ok, user: insert(:user)} +    end + +    test "returns the timeline when not restricted" do +      assert Timelines.generate_terms(%{}) +             |> Map.has_key?(@public_url) +    end + +    test "returns public items", %{user: user} do +      {:ok, _} = CommonAPI.post(user, %{status: "it's post 1!"}) +      {:ok, _} = CommonAPI.post(user, %{status: "it's post 2!"}) +      {:ok, _} = CommonAPI.post(user, %{status: "it's post 3!"}) + +      assert Timelines.generate_terms(%{}) +             |> Map.fetch!(@public_url) +             |> Enum.count() == 3 +    end + +    test "does not return non-public items", %{user: user} do +      {:ok, _} = CommonAPI.post(user, %{status: "it's post 1!", visibility: "unlisted"}) +      {:ok, _} = CommonAPI.post(user, %{status: "it's post 2!", visibility: "direct"}) +      {:ok, _} = CommonAPI.post(user, %{status: "it's post 3!"}) + +      assert Timelines.generate_terms(%{}) +             |> Map.fetch!(@public_url) +             |> Enum.count() == 1 +    end +  end +end diff --git a/test/web/preload/user_test.exs b/test/web/preload/user_test.exs new file mode 100644 index 000000000..83f065e27 --- /dev/null +++ b/test/web/preload/user_test.exs @@ -0,0 +1,33 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Preload.Providers.UserTest do +  use Pleroma.DataCase +  import Pleroma.Factory +  alias Pleroma.Web.Preload.Providers.User + +  describe "returns empty when user doesn't exist" do +    test "nil user specified" do +      assert User.generate_terms(%{user: nil}) == %{} +    end + +    test "missing user specified" do +      assert User.generate_terms(%{user: :not_a_user}) == %{} +    end +  end + +  describe "specified user exists" do +    setup do +      user = insert(:user) + +      terms = User.generate_terms(%{user: user}) +      %{terms: terms, user: user} +    end + +    test "account is rendered", %{terms: terms, user: user} do +      account = terms["/api/v1/accounts/#{user.id}"] +      assert %{acct: user, username: user} = account +    end +  end +end diff --git a/test/web/push/impl_test.exs b/test/web/push/impl_test.exs index a826b24c9..b48952b29 100644 --- a/test/web/push/impl_test.exs +++ b/test/web/push/impl_test.exs @@ -5,8 +5,10 @@  defmodule Pleroma.Web.Push.ImplTest do    use Pleroma.DataCase +  alias Pleroma.Notification    alias Pleroma.Object    alias Pleroma.User +  alias Pleroma.Web.ActivityPub.ActivityPub    alias Pleroma.Web.CommonAPI    alias Pleroma.Web.Push.Impl    alias Pleroma.Web.Push.Subscription @@ -60,7 +62,8 @@ defmodule Pleroma.Web.Push.ImplTest do      notif =        insert(:notification,          user: user, -        activity: activity +        activity: activity, +        type: "mention"        )      assert Impl.perform(notif) == {:ok, [:ok, :ok]} @@ -126,7 +129,7 @@ defmodule Pleroma.Web.Push.ImplTest do             ) ==               "@Bob: Lorem ipsum dolor sit amet, consectetur  adipiscing elit. Fusce sagittis fini..." -    assert Impl.format_title(%{activity: activity}) == +    assert Impl.format_title(%{activity: activity, type: "mention"}) ==               "New Mention"    end @@ -136,9 +139,10 @@ defmodule Pleroma.Web.Push.ImplTest do      {:ok, _, _, activity} = CommonAPI.follow(user, other_user)      object = Object.normalize(activity, false) -    assert Impl.format_body(%{activity: activity}, user, object) == "@Bob has followed you" +    assert Impl.format_body(%{activity: activity, type: "follow"}, user, object) == +             "@Bob has followed you" -    assert Impl.format_title(%{activity: activity}) == +    assert Impl.format_title(%{activity: activity, type: "follow"}) ==               "New Follower"    end @@ -157,7 +161,7 @@ defmodule Pleroma.Web.Push.ImplTest do      assert Impl.format_body(%{activity: announce_activity}, user, object) ==               "@#{user.nickname} repeated: Lorem ipsum dolor sit amet, consectetur  adipiscing elit. Fusce sagittis fini..." -    assert Impl.format_title(%{activity: announce_activity}) == +    assert Impl.format_title(%{activity: announce_activity, type: "reblog"}) ==               "New Repeat"    end @@ -173,9 +177,10 @@ defmodule Pleroma.Web.Push.ImplTest do      {:ok, activity} = CommonAPI.favorite(user, activity.id)      object = Object.normalize(activity) -    assert Impl.format_body(%{activity: activity}, user, object) == "@Bob has favorited your post" +    assert Impl.format_body(%{activity: activity, type: "favourite"}, user, object) == +             "@Bob has favorited your post" -    assert Impl.format_title(%{activity: activity}) == +    assert Impl.format_title(%{activity: activity, type: "favourite"}) ==               "New Favorite"    end @@ -193,6 +198,46 @@ defmodule Pleroma.Web.Push.ImplTest do    end    describe "build_content/3" do +    test "builds content for chat messages" do +      user = insert(:user) +      recipient = insert(:user) + +      {:ok, chat} = CommonAPI.post_chat_message(user, recipient, "hey") +      object = Object.normalize(chat, false) +      [notification] = Notification.for_user(recipient) + +      res = Impl.build_content(notification, user, object) + +      assert res == %{ +               body: "@#{user.nickname}: hey", +               title: "New Chat Message" +             } +    end + +    test "builds content for chat messages with no content" do +      user = insert(:user) +      recipient = insert(:user) + +      file = %Plug.Upload{ +        content_type: "image/jpg", +        path: Path.absname("test/fixtures/image.jpg"), +        filename: "an_image.jpg" +      } + +      {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id) + +      {:ok, chat} = CommonAPI.post_chat_message(user, recipient, nil, media_id: upload.id) +      object = Object.normalize(chat, false) +      [notification] = Notification.for_user(recipient) + +      res = Impl.build_content(notification, user, object) + +      assert res == %{ +               body: "@#{user.nickname}: (Attachment)", +               title: "New Chat Message" +             } +    end +      test "hides details for notifications when privacy option enabled" do        user = insert(:user, nickname: "Bob")        user2 = insert(:user, nickname: "Rob", notification_settings: %{privacy_option: true}) @@ -218,7 +263,7 @@ defmodule Pleroma.Web.Push.ImplTest do            status: "<Lorem ipsum dolor sit amet."          }) -      notif = insert(:notification, user: user2, activity: activity) +      notif = insert(:notification, user: user2, activity: activity, type: "mention")        actor = User.get_cached_by_ap_id(notif.activity.data["actor"])        object = Object.normalize(activity) @@ -229,7 +274,7 @@ defmodule Pleroma.Web.Push.ImplTest do        {:ok, activity} = CommonAPI.favorite(user, activity.id) -      notif = insert(:notification, user: user2, activity: activity) +      notif = insert(:notification, user: user2, activity: activity, type: "favourite")        actor = User.get_cached_by_ap_id(notif.activity.data["actor"])        object = Object.normalize(activity) @@ -268,7 +313,7 @@ defmodule Pleroma.Web.Push.ImplTest do              "<span>Lorem ipsum dolor sit amet</span>, consectetur :firefox: adipiscing elit. Fusce sagittis finibus turpis."          }) -      notif = insert(:notification, user: user2, activity: activity) +      notif = insert(:notification, user: user2, activity: activity, type: "mention")        actor = User.get_cached_by_ap_id(notif.activity.data["actor"])        object = Object.normalize(activity) @@ -281,7 +326,7 @@ defmodule Pleroma.Web.Push.ImplTest do        {:ok, activity} = CommonAPI.favorite(user, activity.id) -      notif = insert(:notification, user: user2, activity: activity) +      notif = insert(:notification, user: user2, activity: activity, type: "favourite")        actor = User.get_cached_by_ap_id(notif.activity.data["actor"])        object = Object.normalize(activity) diff --git a/test/web/rich_media/parser_test.exs b/test/web/rich_media/parser_test.exs index e54a13bc8..420a612c6 100644 --- a/test/web/rich_media/parser_test.exs +++ b/test/web/rich_media/parser_test.exs @@ -60,19 +60,19 @@ defmodule Pleroma.Web.RichMedia.ParserTest do    test "doesn't just add a title" do      assert Pleroma.Web.RichMedia.Parser.parse("http://example.com/non-ogp") ==               {:error, -              "Found metadata was invalid or incomplete: %{url: \"http://example.com/non-ogp\"}"} +              "Found metadata was invalid or incomplete: %{\"url\" => \"http://example.com/non-ogp\"}"}    end    test "parses ogp" do      assert Pleroma.Web.RichMedia.Parser.parse("http://example.com/ogp") ==               {:ok,                %{ -                image: "http://ia.media-imdb.com/images/rock.jpg", -                title: "The Rock", -                description: +                "image" => "http://ia.media-imdb.com/images/rock.jpg", +                "title" => "The Rock", +                "description" =>                    "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.", -                type: "video.movie", -                url: "http://example.com/ogp" +                "type" => "video.movie", +                "url" => "http://example.com/ogp"                }}    end @@ -80,12 +80,12 @@ defmodule Pleroma.Web.RichMedia.ParserTest do      assert Pleroma.Web.RichMedia.Parser.parse("http://example.com/ogp-missing-title") ==               {:ok,                %{ -                image: "http://ia.media-imdb.com/images/rock.jpg", -                title: "The Rock (1996)", -                description: +                "image" => "http://ia.media-imdb.com/images/rock.jpg", +                "title" => "The Rock (1996)", +                "description" =>                    "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.", -                type: "video.movie", -                url: "http://example.com/ogp-missing-title" +                "type" => "video.movie", +                "url" => "http://example.com/ogp-missing-title"                }}    end @@ -93,12 +93,12 @@ defmodule Pleroma.Web.RichMedia.ParserTest do      assert Pleroma.Web.RichMedia.Parser.parse("http://example.com/twitter-card") ==               {:ok,                %{ -                card: "summary", -                site: "@flickr", -                image: "https://farm6.staticflickr.com/5510/14338202952_93595258ff_z.jpg", -                title: "Small Island Developing States Photo Submission", -                description: "View the album on Flickr.", -                url: "http://example.com/twitter-card" +                "card" => "summary", +                "site" => "@flickr", +                "image" => "https://farm6.staticflickr.com/5510/14338202952_93595258ff_z.jpg", +                "title" => "Small Island Developing States Photo Submission", +                "description" => "View the album on Flickr.", +                "url" => "http://example.com/twitter-card"                }}    end @@ -106,27 +106,28 @@ defmodule Pleroma.Web.RichMedia.ParserTest do      assert Pleroma.Web.RichMedia.Parser.parse("http://example.com/oembed") ==               {:ok,                %{ -                author_name: "bees", -                author_url: "https://www.flickr.com/photos/bees/", -                cache_age: 3600, -                flickr_type: "photo", -                height: "768", -                html: +                "author_name" => "bees", +                "author_url" => "https://www.flickr.com/photos/bees/", +                "cache_age" => 3600, +                "flickr_type" => "photo", +                "height" => "768", +                "html" =>                    "<a data-flickr-embed=\"true\" href=\"https://www.flickr.com/photos/bees/2362225867/\" title=\"Bacon Lollys by bees, on Flickr\"><img src=\"https://farm4.staticflickr.com/3040/2362225867_4a87ab8baf_b.jpg\" width=\"1024\" height=\"768\" alt=\"Bacon Lollys\"></a><script async src=\"https://embedr.flickr.com/assets/client-code.js\" charset=\"utf-8\"></script>", -                license: "All Rights Reserved", -                license_id: 0, -                provider_name: "Flickr", -                provider_url: "https://www.flickr.com/", -                thumbnail_height: 150, -                thumbnail_url: "https://farm4.staticflickr.com/3040/2362225867_4a87ab8baf_q.jpg", -                thumbnail_width: 150, -                title: "Bacon Lollys", -                type: "photo", -                url: "http://example.com/oembed", -                version: "1.0", -                web_page: "https://www.flickr.com/photos/bees/2362225867/", -                web_page_short_url: "https://flic.kr/p/4AK2sc", -                width: "1024" +                "license" => "All Rights Reserved", +                "license_id" => 0, +                "provider_name" => "Flickr", +                "provider_url" => "https://www.flickr.com/", +                "thumbnail_height" => 150, +                "thumbnail_url" => +                  "https://farm4.staticflickr.com/3040/2362225867_4a87ab8baf_q.jpg", +                "thumbnail_width" => 150, +                "title" => "Bacon Lollys", +                "type" => "photo", +                "url" => "http://example.com/oembed", +                "version" => "1.0", +                "web_page" => "https://www.flickr.com/photos/bees/2362225867/", +                "web_page_short_url" => "https://flic.kr/p/4AK2sc", +                "width" => "1024"                }}    end diff --git a/test/web/rich_media/parsers/twitter_card_test.exs b/test/web/rich_media/parsers/twitter_card_test.exs index 87c767c15..219f005a2 100644 --- a/test/web/rich_media/parsers/twitter_card_test.exs +++ b/test/web/rich_media/parsers/twitter_card_test.exs @@ -7,8 +7,7 @@ defmodule Pleroma.Web.RichMedia.Parsers.TwitterCardTest do    alias Pleroma.Web.RichMedia.Parsers.TwitterCard    test "returns error when html not contains twitter card" do -    assert TwitterCard.parse([{"html", [], [{"head", [], []}, {"body", [], []}]}], %{}) == -             {:error, "No twitter card metadata found"} +    assert TwitterCard.parse([{"html", [], [{"head", [], []}, {"body", [], []}]}], %{}) == %{}    end    test "parses twitter card with only name attributes" do @@ -17,15 +16,21 @@ defmodule Pleroma.Web.RichMedia.Parsers.TwitterCardTest do        |> Floki.parse_document!()      assert TwitterCard.parse(html, %{}) == -             {:ok, -              %{ -                "app:id:googleplay": "com.nytimes.android", -                "app:name:googleplay": "NYTimes", -                "app:url:googleplay": "nytimes://reader/id/100000006583622", -                site: nil, -                title: -                  "She Was Arrested at 14. Then Her Photo Went to a Facial Recognition Database. - The New York Times" -              }} +             %{ +               "app:id:googleplay" => "com.nytimes.android", +               "app:name:googleplay" => "NYTimes", +               "app:url:googleplay" => "nytimes://reader/id/100000006583622", +               "site" => nil, +               "description" => +                 "With little oversight, the N.Y.P.D. has been using powerful surveillance technology on photos of children and teenagers.", +               "image" => +                 "https://static01.nyt.com/images/2019/08/01/nyregion/01nypd-juveniles-promo/01nypd-juveniles-promo-facebookJumbo.jpg", +               "type" => "article", +               "url" => +                 "https://www.nytimes.com/2019/08/01/nyregion/nypd-facial-recognition-children-teenagers.html", +               "title" => +                 "She Was Arrested at 14. Then Her Photo Went to a Facial Recognition Database." +             }    end    test "parses twitter card with only property attributes" do @@ -34,19 +39,19 @@ defmodule Pleroma.Web.RichMedia.Parsers.TwitterCardTest do        |> Floki.parse_document!()      assert TwitterCard.parse(html, %{}) == -             {:ok, -              %{ -                card: "summary_large_image", -                description: -                  "With little oversight, the N.Y.P.D. has been using powerful surveillance technology on photos of children and teenagers.", -                image: -                  "https://static01.nyt.com/images/2019/08/01/nyregion/01nypd-juveniles-promo/01nypd-juveniles-promo-videoSixteenByNineJumbo1600.jpg", -                "image:alt": "", -                title: -                  "She Was Arrested at 14. Then Her Photo Went to a Facial Recognition Database.", -                url: -                  "https://www.nytimes.com/2019/08/01/nyregion/nypd-facial-recognition-children-teenagers.html" -              }} +             %{ +               "card" => "summary_large_image", +               "description" => +                 "With little oversight, the N.Y.P.D. has been using powerful surveillance technology on photos of children and teenagers.", +               "image" => +                 "https://static01.nyt.com/images/2019/08/01/nyregion/01nypd-juveniles-promo/01nypd-juveniles-promo-videoSixteenByNineJumbo1600.jpg", +               "image:alt" => "", +               "title" => +                 "She Was Arrested at 14. Then Her Photo Went to a Facial Recognition Database.", +               "url" => +                 "https://www.nytimes.com/2019/08/01/nyregion/nypd-facial-recognition-children-teenagers.html", +               "type" => "article" +             }    end    test "parses twitter card with name & property attributes" do @@ -55,23 +60,23 @@ defmodule Pleroma.Web.RichMedia.Parsers.TwitterCardTest do        |> Floki.parse_document!()      assert TwitterCard.parse(html, %{}) == -             {:ok, -              %{ -                "app:id:googleplay": "com.nytimes.android", -                "app:name:googleplay": "NYTimes", -                "app:url:googleplay": "nytimes://reader/id/100000006583622", -                card: "summary_large_image", -                description: -                  "With little oversight, the N.Y.P.D. has been using powerful surveillance technology on photos of children and teenagers.", -                image: -                  "https://static01.nyt.com/images/2019/08/01/nyregion/01nypd-juveniles-promo/01nypd-juveniles-promo-videoSixteenByNineJumbo1600.jpg", -                "image:alt": "", -                site: nil, -                title: -                  "She Was Arrested at 14. Then Her Photo Went to a Facial Recognition Database.", -                url: -                  "https://www.nytimes.com/2019/08/01/nyregion/nypd-facial-recognition-children-teenagers.html" -              }} +             %{ +               "app:id:googleplay" => "com.nytimes.android", +               "app:name:googleplay" => "NYTimes", +               "app:url:googleplay" => "nytimes://reader/id/100000006583622", +               "card" => "summary_large_image", +               "description" => +                 "With little oversight, the N.Y.P.D. has been using powerful surveillance technology on photos of children and teenagers.", +               "image" => +                 "https://static01.nyt.com/images/2019/08/01/nyregion/01nypd-juveniles-promo/01nypd-juveniles-promo-videoSixteenByNineJumbo1600.jpg", +               "image:alt" => "", +               "site" => nil, +               "title" => +                 "She Was Arrested at 14. Then Her Photo Went to a Facial Recognition Database.", +               "url" => +                 "https://www.nytimes.com/2019/08/01/nyregion/nypd-facial-recognition-children-teenagers.html", +               "type" => "article" +             }    end    test "respect only first title tag on the page" do @@ -84,14 +89,17 @@ defmodule Pleroma.Web.RichMedia.Parsers.TwitterCardTest do        File.read!("test/fixtures/margaret-corbin-grave-west-point.html") |> Floki.parse_document!()      assert TwitterCard.parse(html, %{}) == -             {:ok, -              %{ -                site: "@atlasobscura", -                title: -                  "The Missing Grave of Margaret Corbin, Revolutionary War Veteran - Atlas Obscura", -                card: "summary_large_image", -                image: image_path -              }} +             %{ +               "site" => "@atlasobscura", +               "title" => "The Missing Grave of Margaret Corbin, Revolutionary War Veteran", +               "card" => "summary_large_image", +               "image" => image_path, +               "description" => +                 "She's the only woman veteran honored with a monument at West Point. But where was she buried?", +               "site_name" => "Atlas Obscura", +               "type" => "article", +               "url" => "http://www.atlasobscura.com/articles/margaret-corbin-grave-west-point" +             }    end    test "takes first founded title in html head if there is html markup error" do @@ -100,14 +108,20 @@ defmodule Pleroma.Web.RichMedia.Parsers.TwitterCardTest do        |> Floki.parse_document!()      assert TwitterCard.parse(html, %{}) == -             {:ok, -              %{ -                site: nil, -                title: -                  "She Was Arrested at 14. Then Her Photo Went to a Facial Recognition Database. - The New York Times", -                "app:id:googleplay": "com.nytimes.android", -                "app:name:googleplay": "NYTimes", -                "app:url:googleplay": "nytimes://reader/id/100000006583622" -              }} +             %{ +               "site" => nil, +               "title" => +                 "She Was Arrested at 14. Then Her Photo Went to a Facial Recognition Database.", +               "app:id:googleplay" => "com.nytimes.android", +               "app:name:googleplay" => "NYTimes", +               "app:url:googleplay" => "nytimes://reader/id/100000006583622", +               "description" => +                 "With little oversight, the N.Y.P.D. has been using powerful surveillance technology on photos of children and teenagers.", +               "image" => +                 "https://static01.nyt.com/images/2019/08/01/nyregion/01nypd-juveniles-promo/01nypd-juveniles-promo-facebookJumbo.jpg", +               "type" => "article", +               "url" => +                 "https://www.nytimes.com/2019/08/01/nyregion/nypd-facial-recognition-children-teenagers.html" +             }    end  end diff --git a/test/web/streamer/streamer_test.exs b/test/web/streamer/streamer_test.exs index 4cf640ce8..245f6e63f 100644 --- a/test/web/streamer/streamer_test.exs +++ b/test/web/streamer/streamer_test.exs @@ -7,11 +7,15 @@ defmodule Pleroma.Web.StreamerTest do    import Pleroma.Factory +  alias Pleroma.Chat +  alias Pleroma.Chat.MessageReference    alias Pleroma.Conversation.Participation    alias Pleroma.List +  alias Pleroma.Object    alias Pleroma.User    alias Pleroma.Web.CommonAPI    alias Pleroma.Web.Streamer +  alias Pleroma.Web.StreamerView    @moduletag needs_streamer: true, capture_log: true @@ -124,7 +128,7 @@ defmodule Pleroma.Web.StreamerTest do          |> Map.put("object", activity.data["object"])          |> Map.put("actor", user.ap_id) -      {:ok, %Pleroma.Activity{data: data, local: false} = announce} = +      {:ok, %Pleroma.Activity{data: _data, local: false} = announce} =          Pleroma.Web.ActivityPub.Transmogrifier.handle_incoming(data)        assert_receive {:render_with_user, Pleroma.Web.StreamerView, "update.json", ^announce} @@ -145,6 +149,57 @@ defmodule Pleroma.Web.StreamerTest do        refute Streamer.filtered_by_user?(user, notify)      end +    test "it sends chat messages to the 'user:pleroma_chat' stream", %{user: user} do +      other_user = insert(:user) + +      {:ok, create_activity} = CommonAPI.post_chat_message(other_user, user, "hey cirno") +      object = Object.normalize(create_activity, false) +      chat = Chat.get(user.id, other_user.ap_id) +      cm_ref = MessageReference.for_chat_and_object(chat, object) +      cm_ref = %{cm_ref | chat: chat, object: object} + +      Streamer.get_topic_and_add_socket("user:pleroma_chat", user) +      Streamer.stream("user:pleroma_chat", {user, cm_ref}) + +      text = StreamerView.render("chat_update.json", %{chat_message_reference: cm_ref}) + +      assert text =~ "hey cirno" +      assert_receive {:text, ^text} +    end + +    test "it sends chat messages to the 'user' stream", %{user: user} do +      other_user = insert(:user) + +      {:ok, create_activity} = CommonAPI.post_chat_message(other_user, user, "hey cirno") +      object = Object.normalize(create_activity, false) +      chat = Chat.get(user.id, other_user.ap_id) +      cm_ref = MessageReference.for_chat_and_object(chat, object) +      cm_ref = %{cm_ref | chat: chat, object: object} + +      Streamer.get_topic_and_add_socket("user", user) +      Streamer.stream("user", {user, cm_ref}) + +      text = StreamerView.render("chat_update.json", %{chat_message_reference: cm_ref}) + +      assert text =~ "hey cirno" +      assert_receive {:text, ^text} +    end + +    test "it sends chat message notifications to the 'user:notification' stream", %{user: user} do +      other_user = insert(:user) + +      {:ok, create_activity} = CommonAPI.post_chat_message(other_user, user, "hey") + +      notify = +        Repo.get_by(Pleroma.Notification, user_id: user.id, activity_id: create_activity.id) +        |> Repo.preload(:activity) + +      Streamer.get_topic_and_add_socket("user:notification", user) +      Streamer.stream("user:notification", notify) +      assert_receive {:render_with_user, _, _, ^notify} +      refute Streamer.filtered_by_user?(user, notify) +    end +      test "it doesn't send notify to the 'user:notification' stream when a user is blocked", %{        user: user      } do | 
