diff options
Diffstat (limited to 'test/web/activity_pub/transmogrifier')
5 files changed, 573 insertions, 1 deletions
diff --git a/test/web/activity_pub/transmogrifier/announce_handling_test.exs b/test/web/activity_pub/transmogrifier/announce_handling_test.exs new file mode 100644 index 000000000..e895636b5 --- /dev/null +++ b/test/web/activity_pub/transmogrifier/announce_handling_test.exs @@ -0,0 +1,172 @@ +# 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.AnnounceHandlingTest do + use Pleroma.DataCase + + alias Pleroma.Activity + alias Pleroma.Object + alias Pleroma.Web.ActivityPub.Transmogrifier + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + test "it works for incoming honk announces" do + user = insert(:user, ap_id: "https://honktest/u/test", local: false) + other_user = insert(:user) + {:ok, post} = CommonAPI.post(other_user, %{status: "bonkeronk"}) + + announce = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "actor" => "https://honktest/u/test", + "id" => "https://honktest/u/test/bonk/1793M7B9MQ48847vdx", + "object" => post.data["object"], + "published" => "2019-06-25T19:33:58Z", + "to" => "https://www.w3.org/ns/activitystreams#Public", + "type" => "Announce" + } + + {:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(announce) + + object = Object.get_by_ap_id(post.data["object"]) + + assert length(object.data["announcements"]) == 1 + assert user.ap_id in object.data["announcements"] + end + + test "it works for incoming announces with actor being inlined (kroeg)" do + data = File.read!("test/fixtures/kroeg-announce-with-inline-actor.json") |> Poison.decode!() + + _user = insert(:user, local: false, ap_id: data["actor"]["id"]) + other_user = insert(:user) + + {:ok, post} = CommonAPI.post(other_user, %{status: "kroegeroeg"}) + + data = + data + |> put_in(["object", "id"], post.data["object"]) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["actor"] == "https://puckipedia.com/" + end + + test "it works for incoming announces, fetching the announced object" do + data = + File.read!("test/fixtures/mastodon-announce.json") + |> Poison.decode!() + |> Map.put("object", "http://mastodon.example.org/users/admin/statuses/99541947525187367") + + Tesla.Mock.mock(fn + %{method: :get} -> + %Tesla.Env{status: 200, body: File.read!("test/fixtures/mastodon-note-object.json")} + end) + + _user = insert(:user, local: false, ap_id: data["actor"]) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["actor"] == "http://mastodon.example.org/users/admin" + assert data["type"] == "Announce" + + assert data["id"] == + "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity" + + assert data["object"] == + "http://mastodon.example.org/users/admin/statuses/99541947525187367" + + assert(Activity.get_create_by_object_ap_id(data["object"])) + end + + @tag capture_log: true + test "it works for incoming announces with an existing activity" do + user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{status: "hey"}) + + data = + File.read!("test/fixtures/mastodon-announce.json") + |> Poison.decode!() + |> Map.put("object", activity.data["object"]) + + _user = insert(:user, local: false, ap_id: data["actor"]) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["actor"] == "http://mastodon.example.org/users/admin" + assert data["type"] == "Announce" + + assert data["id"] == + "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity" + + assert data["object"] == activity.data["object"] + + assert Activity.get_create_by_object_ap_id(data["object"]).id == activity.id + end + + # Ignore inlined activities for now + @tag skip: true + test "it works for incoming announces with an inlined activity" do + data = + File.read!("test/fixtures/mastodon-announce-private.json") + |> Poison.decode!() + + _user = + insert(:user, + local: false, + ap_id: data["actor"], + follower_address: data["actor"] <> "/followers" + ) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["actor"] == "http://mastodon.example.org/users/admin" + assert data["type"] == "Announce" + + assert data["id"] == + "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity" + + object = Object.normalize(data["object"]) + + assert object.data["id"] == "http://mastodon.example.org/@admin/99541947525187368" + assert object.data["content"] == "this is a private toot" + end + + @tag capture_log: true + test "it rejects incoming announces with an inlined activity from another origin" do + Tesla.Mock.mock(fn + %{method: :get} -> %Tesla.Env{status: 404, body: ""} + end) + + data = + File.read!("test/fixtures/bogus-mastodon-announce.json") + |> Poison.decode!() + + _user = insert(:user, local: false, ap_id: data["actor"]) + + assert {:error, e} = Transmogrifier.handle_incoming(data) + end + + test "it does not clobber the addressing on announce activities" do + user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{status: "hey"}) + + data = + File.read!("test/fixtures/mastodon-announce.json") + |> Poison.decode!() + |> Map.put("object", Object.normalize(activity).data["id"]) + |> Map.put("to", ["http://mastodon.example.org/users/admin/followers"]) + |> Map.put("cc", []) + + _user = + insert(:user, + local: false, + ap_id: data["actor"], + follower_address: "http://mastodon.example.org/users/admin/followers" + ) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["to"] == ["http://mastodon.example.org/users/admin/followers"] + end +end diff --git a/test/web/activity_pub/transmogrifier/block_handling_test.exs b/test/web/activity_pub/transmogrifier/block_handling_test.exs new file mode 100644 index 000000000..71f1a0ed5 --- /dev/null +++ b/test/web/activity_pub/transmogrifier/block_handling_test.exs @@ -0,0 +1,63 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.Transmogrifier.BlockHandlingTest do + use Pleroma.DataCase + + alias Pleroma.Activity + alias Pleroma.User + alias Pleroma.Web.ActivityPub.Transmogrifier + + import Pleroma.Factory + + test "it works for incoming blocks" do + user = insert(:user) + + data = + File.read!("test/fixtures/mastodon-block-activity.json") + |> Poison.decode!() + |> Map.put("object", user.ap_id) + + blocker = insert(:user, ap_id: data["actor"]) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["type"] == "Block" + assert data["object"] == user.ap_id + assert data["actor"] == "http://mastodon.example.org/users/admin" + + assert User.blocks?(blocker, user) + end + + test "incoming blocks successfully tear down any follow relationship" do + blocker = insert(:user) + blocked = insert(:user) + + data = + File.read!("test/fixtures/mastodon-block-activity.json") + |> Poison.decode!() + |> Map.put("object", blocked.ap_id) + |> Map.put("actor", blocker.ap_id) + + {:ok, blocker} = User.follow(blocker, blocked) + {:ok, blocked} = User.follow(blocked, blocker) + + assert User.following?(blocker, blocked) + assert User.following?(blocked, blocker) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["type"] == "Block" + assert data["object"] == blocked.ap_id + assert data["actor"] == blocker.ap_id + + blocker = User.get_cached_by_ap_id(data["actor"]) + blocked = User.get_cached_by_ap_id(data["object"]) + + assert User.blocks?(blocker, blocked) + + refute User.following?(blocker, blocked) + refute User.following?(blocked, blocker) + end +end diff --git a/test/web/activity_pub/transmogrifier/chat_message_test.exs b/test/web/activity_pub/transmogrifier/chat_message_test.exs 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 |