diff options
Diffstat (limited to 'test/web/activity_pub')
62 files changed, 0 insertions, 12432 deletions
diff --git a/test/web/activity_pub/activity_pub_controller_test.exs b/test/web/activity_pub/activity_pub_controller_test.exs deleted file mode 100644 index 57988dc1e..000000000 --- a/test/web/activity_pub/activity_pub_controller_test.exs +++ /dev/null @@ -1,1534 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do - use Pleroma.Web.ConnCase - use Oban.Testing, repo: Pleroma.Repo - - alias Pleroma.Activity - alias Pleroma.Config - alias Pleroma.Delivery - alias Pleroma.Instances - alias Pleroma.Object - alias Pleroma.Tests.ObanHelpers - alias Pleroma.User - alias Pleroma.Web.ActivityPub.ActivityPub - alias Pleroma.Web.ActivityPub.ObjectView - alias Pleroma.Web.ActivityPub.Relay - alias Pleroma.Web.ActivityPub.UserView - alias Pleroma.Web.ActivityPub.Utils - alias Pleroma.Web.CommonAPI - alias Pleroma.Web.Endpoint - alias Pleroma.Workers.ReceiverWorker - - import Pleroma.Factory - - require Pleroma.Constants - - setup_all do - Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - - setup do: clear_config([:instance, :federating], true) - - describe "/relay" do - setup do: clear_config([:instance, :allow_relay]) - - test "with the relay active, it returns the relay user", %{conn: conn} do - res = - conn - |> get(activity_pub_path(conn, :relay)) - |> json_response(200) - - assert res["id"] =~ "/relay" - end - - test "with the relay disabled, it returns 404", %{conn: conn} do - Config.put([:instance, :allow_relay], false) - - conn - |> get(activity_pub_path(conn, :relay)) - |> json_response(404) - end - - test "on non-federating instance, it returns 404", %{conn: conn} do - Config.put([:instance, :federating], false) - user = insert(:user) - - conn - |> assign(:user, user) - |> get(activity_pub_path(conn, :relay)) - |> json_response(404) - end - end - - describe "/internal/fetch" do - test "it returns the internal fetch user", %{conn: conn} do - res = - conn - |> get(activity_pub_path(conn, :internal_fetch)) - |> json_response(200) - - assert res["id"] =~ "/fetch" - end - - test "on non-federating instance, it returns 404", %{conn: conn} do - Config.put([:instance, :federating], false) - user = insert(:user) - - conn - |> assign(:user, user) - |> get(activity_pub_path(conn, :internal_fetch)) - |> json_response(404) - end - end - - describe "/users/:nickname" do - test "it returns a json representation of the user with accept application/json", %{ - conn: conn - } do - user = insert(:user) - - conn = - conn - |> put_req_header("accept", "application/json") - |> get("/users/#{user.nickname}") - - user = User.get_cached_by_id(user.id) - - assert json_response(conn, 200) == UserView.render("user.json", %{user: user}) - end - - test "it returns a json representation of the user with accept application/activity+json", %{ - conn: conn - } do - user = insert(:user) - - conn = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/users/#{user.nickname}") - - user = User.get_cached_by_id(user.id) - - assert json_response(conn, 200) == UserView.render("user.json", %{user: user}) - end - - test "it returns a json representation of the user with accept application/ld+json", %{ - conn: conn - } do - user = insert(:user) - - conn = - conn - |> put_req_header( - "accept", - "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"" - ) - |> get("/users/#{user.nickname}") - - user = User.get_cached_by_id(user.id) - - assert json_response(conn, 200) == UserView.render("user.json", %{user: user}) - end - - test "it returns 404 for remote users", %{ - conn: conn - } do - user = insert(:user, local: false, nickname: "remoteuser@example.com") - - conn = - conn - |> put_req_header("accept", "application/json") - |> get("/users/#{user.nickname}.json") - - assert json_response(conn, 404) - end - - test "it returns error when user is not found", %{conn: conn} do - response = - conn - |> put_req_header("accept", "application/json") - |> get("/users/jimm") - |> json_response(404) - - assert response == "Not found" - end - - test "it requires authentication if instance is NOT federating", %{ - conn: conn - } do - user = insert(:user) - - conn = - put_req_header( - conn, - "accept", - "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"" - ) - - ensure_federating_or_authenticated(conn, "/users/#{user.nickname}.json", user) - end - end - - describe "mastodon compatibility routes" do - test "it returns a json representation of the object with accept application/json", %{ - conn: conn - } do - {:ok, object} = - %{ - "type" => "Note", - "content" => "hey", - "id" => Endpoint.url() <> "/users/raymoo/statuses/999999999", - "actor" => Endpoint.url() <> "/users/raymoo", - "to" => [Pleroma.Constants.as_public()] - } - |> Object.create() - - conn = - conn - |> put_req_header("accept", "application/json") - |> get("/users/raymoo/statuses/999999999") - - assert json_response(conn, 200) == ObjectView.render("object.json", %{object: object}) - end - - test "it returns a json representation of the activity with accept application/json", %{ - conn: conn - } do - {:ok, object} = - %{ - "type" => "Note", - "content" => "hey", - "id" => Endpoint.url() <> "/users/raymoo/statuses/999999999", - "actor" => Endpoint.url() <> "/users/raymoo", - "to" => [Pleroma.Constants.as_public()] - } - |> Object.create() - - {:ok, activity, _} = - %{ - "id" => object.data["id"] <> "/activity", - "type" => "Create", - "object" => object.data["id"], - "actor" => object.data["actor"], - "to" => object.data["to"] - } - |> ActivityPub.persist(local: true) - - conn = - conn - |> put_req_header("accept", "application/json") - |> get("/users/raymoo/statuses/999999999/activity") - - assert json_response(conn, 200) == ObjectView.render("object.json", %{object: activity}) - end - end - - describe "/objects/:uuid" do - test "it returns a json representation of the object with accept application/json", %{ - conn: conn - } do - note = insert(:note) - uuid = String.split(note.data["id"], "/") |> List.last() - - conn = - conn - |> put_req_header("accept", "application/json") - |> get("/objects/#{uuid}") - - assert json_response(conn, 200) == ObjectView.render("object.json", %{object: note}) - end - - test "it returns a json representation of the object with accept application/activity+json", - %{conn: conn} do - note = insert(:note) - uuid = String.split(note.data["id"], "/") |> List.last() - - conn = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/objects/#{uuid}") - - assert json_response(conn, 200) == ObjectView.render("object.json", %{object: note}) - end - - test "it returns a json representation of the object with accept application/ld+json", %{ - conn: conn - } do - note = insert(:note) - uuid = String.split(note.data["id"], "/") |> List.last() - - conn = - conn - |> put_req_header( - "accept", - "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"" - ) - |> get("/objects/#{uuid}") - - assert json_response(conn, 200) == ObjectView.render("object.json", %{object: note}) - end - - test "it returns 404 for non-public messages", %{conn: conn} do - note = insert(:direct_note) - uuid = String.split(note.data["id"], "/") |> List.last() - - conn = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/objects/#{uuid}") - - assert json_response(conn, 404) - end - - test "it returns 404 for tombstone objects", %{conn: conn} do - tombstone = insert(:tombstone) - uuid = String.split(tombstone.data["id"], "/") |> List.last() - - conn = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/objects/#{uuid}") - - assert json_response(conn, 404) - end - - test "it caches a response", %{conn: conn} do - note = insert(:note) - uuid = String.split(note.data["id"], "/") |> List.last() - - conn1 = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/objects/#{uuid}") - - assert json_response(conn1, :ok) - assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"})) - - conn2 = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/objects/#{uuid}") - - assert json_response(conn1, :ok) == json_response(conn2, :ok) - assert Enum.any?(conn2.resp_headers, &(&1 == {"x-cache", "HIT from Pleroma"})) - end - - test "cached purged after object deletion", %{conn: conn} do - note = insert(:note) - uuid = String.split(note.data["id"], "/") |> List.last() - - conn1 = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/objects/#{uuid}") - - assert json_response(conn1, :ok) - assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"})) - - Object.delete(note) - - conn2 = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/objects/#{uuid}") - - assert "Not found" == json_response(conn2, :not_found) - end - - test "it requires authentication if instance is NOT federating", %{ - conn: conn - } do - user = insert(:user) - note = insert(:note) - uuid = String.split(note.data["id"], "/") |> List.last() - - conn = put_req_header(conn, "accept", "application/activity+json") - - ensure_federating_or_authenticated(conn, "/objects/#{uuid}", user) - end - end - - describe "/activities/:uuid" do - test "it returns a json representation of the activity", %{conn: conn} do - activity = insert(:note_activity) - uuid = String.split(activity.data["id"], "/") |> List.last() - - conn = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/activities/#{uuid}") - - assert json_response(conn, 200) == ObjectView.render("object.json", %{object: activity}) - end - - test "it returns 404 for non-public activities", %{conn: conn} do - activity = insert(:direct_note_activity) - uuid = String.split(activity.data["id"], "/") |> List.last() - - conn = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/activities/#{uuid}") - - assert json_response(conn, 404) - end - - test "it caches a response", %{conn: conn} do - activity = insert(:note_activity) - uuid = String.split(activity.data["id"], "/") |> List.last() - - conn1 = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/activities/#{uuid}") - - assert json_response(conn1, :ok) - assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"})) - - conn2 = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/activities/#{uuid}") - - assert json_response(conn1, :ok) == json_response(conn2, :ok) - assert Enum.any?(conn2.resp_headers, &(&1 == {"x-cache", "HIT from Pleroma"})) - end - - test "cached purged after activity deletion", %{conn: conn} do - user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{status: "cofe"}) - - uuid = String.split(activity.data["id"], "/") |> List.last() - - conn1 = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/activities/#{uuid}") - - assert json_response(conn1, :ok) - assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"})) - - Activity.delete_all_by_object_ap_id(activity.object.data["id"]) - - conn2 = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/activities/#{uuid}") - - assert "Not found" == json_response(conn2, :not_found) - end - - test "it requires authentication if instance is NOT federating", %{ - conn: conn - } do - user = insert(:user) - activity = insert(:note_activity) - uuid = String.split(activity.data["id"], "/") |> List.last() - - conn = put_req_header(conn, "accept", "application/activity+json") - - ensure_federating_or_authenticated(conn, "/activities/#{uuid}", user) - end - end - - describe "/inbox" do - test "it inserts an incoming activity into the database", %{conn: conn} do - data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!() - - conn = - conn - |> assign(:valid_signature, true) - |> put_req_header("content-type", "application/activity+json") - |> post("/inbox", data) - - assert "ok" == json_response(conn, 200) - - ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) - assert Activity.get_by_ap_id(data["id"]) - end - - @tag capture_log: true - test "it inserts an incoming activity into the database" <> - "even if we can't fetch the user but have it in our db", - %{conn: conn} do - user = - insert(:user, - ap_id: "https://mastodon.example.org/users/raymoo", - ap_enabled: true, - local: false, - last_refreshed_at: nil - ) - - data = - File.read!("test/fixtures/mastodon-post-activity.json") - |> Poison.decode!() - |> Map.put("actor", user.ap_id) - |> put_in(["object", "attridbutedTo"], user.ap_id) - - conn = - conn - |> assign(:valid_signature, true) - |> put_req_header("content-type", "application/activity+json") - |> post("/inbox", data) - - assert "ok" == json_response(conn, 200) - - ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) - assert Activity.get_by_ap_id(data["id"]) - end - - test "it clears `unreachable` federation status of the sender", %{conn: conn} do - data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!() - - sender_url = data["actor"] - Instances.set_consistently_unreachable(sender_url) - refute Instances.reachable?(sender_url) - - conn = - conn - |> assign(:valid_signature, true) - |> put_req_header("content-type", "application/activity+json") - |> post("/inbox", data) - - assert "ok" == json_response(conn, 200) - assert Instances.reachable?(sender_url) - end - - test "accept follow activity", %{conn: conn} do - Pleroma.Config.put([:instance, :federating], true) - relay = Relay.get_actor() - - assert {:ok, %Activity{} = activity} = Relay.follow("https://relay.mastodon.host/actor") - - followed_relay = Pleroma.User.get_by_ap_id("https://relay.mastodon.host/actor") - relay = refresh_record(relay) - - accept = - File.read!("test/fixtures/relay/accept-follow.json") - |> String.replace("{{ap_id}}", relay.ap_id) - |> String.replace("{{activity_id}}", activity.data["id"]) - - assert "ok" == - conn - |> assign(:valid_signature, true) - |> put_req_header("content-type", "application/activity+json") - |> post("/inbox", accept) - |> json_response(200) - - ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) - - assert Pleroma.FollowingRelationship.following?( - relay, - followed_relay - ) - - Mix.shell(Mix.Shell.Process) - - on_exit(fn -> - Mix.shell(Mix.Shell.IO) - end) - - :ok = Mix.Tasks.Pleroma.Relay.run(["list"]) - assert_receive {:mix_shell, :info, ["https://relay.mastodon.host/actor"]} - end - - @tag capture_log: true - test "without valid signature, " <> - "it only accepts Create activities and requires enabled federation", - %{conn: conn} do - data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!() - non_create_data = File.read!("test/fixtures/mastodon-announce.json") |> Poison.decode!() - - conn = put_req_header(conn, "content-type", "application/activity+json") - - Config.put([:instance, :federating], false) - - conn - |> post("/inbox", data) - |> json_response(403) - - conn - |> post("/inbox", non_create_data) - |> json_response(403) - - Config.put([:instance, :federating], true) - - ret_conn = post(conn, "/inbox", data) - assert "ok" == json_response(ret_conn, 200) - - conn - |> post("/inbox", non_create_data) - |> json_response(400) - end - end - - describe "/users/:nickname/inbox" do - setup do - data = - File.read!("test/fixtures/mastodon-post-activity.json") - |> Poison.decode!() - - [data: data] - end - - test "it inserts an incoming activity into the database", %{conn: conn, data: data} do - user = insert(:user) - data = Map.put(data, "bcc", [user.ap_id]) - - conn = - conn - |> assign(:valid_signature, true) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{user.nickname}/inbox", data) - - assert "ok" == json_response(conn, 200) - ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) - assert Activity.get_by_ap_id(data["id"]) - end - - test "it accepts messages with to as string instead of array", %{conn: conn, data: data} do - user = insert(:user) - - data = - Map.put(data, "to", user.ap_id) - |> Map.delete("cc") - - conn = - conn - |> assign(:valid_signature, true) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{user.nickname}/inbox", data) - - assert "ok" == json_response(conn, 200) - ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) - assert Activity.get_by_ap_id(data["id"]) - end - - test "it accepts messages with cc as string instead of array", %{conn: conn, data: data} do - user = insert(:user) - - data = - Map.put(data, "cc", user.ap_id) - |> Map.delete("to") - - conn = - conn - |> assign(:valid_signature, true) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{user.nickname}/inbox", data) - - assert "ok" == json_response(conn, 200) - ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) - %Activity{} = activity = Activity.get_by_ap_id(data["id"]) - assert user.ap_id in activity.recipients - end - - test "it accepts messages with bcc as string instead of array", %{conn: conn, data: data} do - user = insert(:user) - - data = - Map.put(data, "bcc", user.ap_id) - |> Map.delete("to") - |> Map.delete("cc") - - conn = - conn - |> assign(:valid_signature, true) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{user.nickname}/inbox", data) - - assert "ok" == json_response(conn, 200) - ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) - assert Activity.get_by_ap_id(data["id"]) - end - - 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" => 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" - } - - conn = - conn - |> assign(:valid_signature, true) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{user.nickname}/inbox", data) - - assert "ok" == json_response(conn, 200) - ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) - %Activity{} = activity = Activity.get_by_ap_id(data["id"]) - assert "https://www.w3.org/ns/activitystreams#Public" in activity.recipients - end - - test "it accepts messages from actors that are followed by the user", %{ - conn: conn, - data: data - } do - recipient = insert(:user) - actor = insert(:user, %{ap_id: "http://mastodon.example.org/users/actor"}) - - {:ok, recipient} = User.follow(recipient, actor) - - object = - data["object"] - |> Map.put("attributedTo", actor.ap_id) - - data = - data - |> Map.put("actor", actor.ap_id) - |> Map.put("object", object) - - conn = - conn - |> assign(:valid_signature, true) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{recipient.nickname}/inbox", data) - - assert "ok" == json_response(conn, 200) - ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) - assert Activity.get_by_ap_id(data["id"]) - end - - test "it rejects reads from other users", %{conn: conn} do - user = insert(:user) - other_user = insert(:user) - - conn = - conn - |> assign(:user, other_user) - |> put_req_header("accept", "application/activity+json") - |> get("/users/#{user.nickname}/inbox") - - assert json_response(conn, 403) - end - - test "it returns a note activity in a collection", %{conn: conn} do - note_activity = insert(:direct_note_activity) - note_object = Object.normalize(note_activity) - user = User.get_cached_by_ap_id(hd(note_activity.data["to"])) - - conn = - conn - |> assign(:user, user) - |> put_req_header("accept", "application/activity+json") - |> get("/users/#{user.nickname}/inbox?page=true") - - assert response(conn, 200) =~ note_object.data["content"] - end - - test "it clears `unreachable` federation status of the sender", %{conn: conn, data: data} do - user = insert(:user) - data = Map.put(data, "bcc", [user.ap_id]) - - sender_host = URI.parse(data["actor"]).host - Instances.set_consistently_unreachable(sender_host) - refute Instances.reachable?(sender_host) - - conn = - conn - |> assign(:valid_signature, true) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{user.nickname}/inbox", data) - - assert "ok" == json_response(conn, 200) - assert Instances.reachable?(sender_host) - end - - test "it removes all follower collections but actor's", %{conn: conn} do - [actor, recipient] = insert_pair(:user) - - data = - File.read!("test/fixtures/activitypub-client-post-activity.json") - |> Poison.decode!() - - object = Map.put(data["object"], "attributedTo", actor.ap_id) - - data = - data - |> Map.put("id", Utils.generate_object_id()) - |> Map.put("actor", actor.ap_id) - |> Map.put("object", object) - |> Map.put("cc", [ - recipient.follower_address, - actor.follower_address - ]) - |> Map.put("to", [ - recipient.ap_id, - recipient.follower_address, - "https://www.w3.org/ns/activitystreams#Public" - ]) - - conn - |> assign(:valid_signature, true) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{recipient.nickname}/inbox", data) - |> json_response(200) - - ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) - - activity = Activity.get_by_ap_id(data["id"]) - - assert activity.id - assert actor.follower_address in activity.recipients - assert actor.follower_address in activity.data["cc"] - - refute recipient.follower_address in activity.recipients - refute recipient.follower_address in activity.data["cc"] - refute recipient.follower_address in activity.data["to"] - end - - test "it requires authentication", %{conn: conn} do - user = insert(:user) - conn = put_req_header(conn, "accept", "application/activity+json") - - ret_conn = get(conn, "/users/#{user.nickname}/inbox") - assert json_response(ret_conn, 403) - - ret_conn = - conn - |> assign(:user, user) - |> get("/users/#{user.nickname}/inbox") - - assert json_response(ret_conn, 200) - end - 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(outbox_endpoint) - - result = json_response(conn, 200) - assert outbox_endpoint == result["id"] - end - - test "it returns a note activity in a collection", %{conn: conn} do - note_activity = insert(:note_activity) - note_object = Object.normalize(note_activity) - user = User.get_cached_by_ap_id(note_activity.data["actor"]) - - conn = - conn - |> assign(:user, user) - |> put_req_header("accept", "application/activity+json") - |> get("/users/#{user.nickname}/outbox?page=true") - - assert response(conn, 200) =~ note_object.data["content"] - end - - test "it returns an announce activity in a collection", %{conn: conn} do - announce_activity = insert(:announce_activity) - user = User.get_cached_by_ap_id(announce_activity.data["actor"]) - - conn = - conn - |> assign(:user, user) - |> put_req_header("accept", "application/activity+json") - |> get("/users/#{user.nickname}/outbox?page=true") - - assert response(conn, 200) =~ announce_activity.data["object"] - end - - test "it requires authentication if instance is NOT federating", %{ - conn: conn - } do - user = insert(:user) - conn = put_req_header(conn, "accept", "application/activity+json") - - ensure_federating_or_authenticated(conn, "/users/#{user.nickname}/outbox", user) - end - end - - describe "POST /users/:nickname/outbox (C2S)" do - setup do - [ - activity: %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "type" => "Create", - "object" => %{"type" => "Note", "content" => "AP C2S test"}, - "to" => "https://www.w3.org/ns/activitystreams#Public", - "cc" => [] - } - ] - end - - test "it rejects posts from other users / unauthenticated users", %{ - conn: conn, - activity: activity - } do - user = insert(:user) - other_user = insert(:user) - conn = put_req_header(conn, "content-type", "application/activity+json") - - conn - |> post("/users/#{user.nickname}/outbox", activity) - |> json_response(403) - - conn - |> assign(:user, other_user) - |> post("/users/#{user.nickname}/outbox", activity) - |> json_response(403) - end - - test "it inserts an incoming create activity into the database", %{ - conn: conn, - activity: activity - } do - user = insert(:user) - - result = - conn - |> assign(:user, user) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{user.nickname}/outbox", activity) - |> json_response(201) - - assert Activity.get_by_ap_id(result["id"]) - assert result["object"] - assert %Object{data: object} = Object.normalize(result["object"]) - assert object["content"] == activity["object"]["content"] - end - - test "it rejects anything beyond 'Note' creations", %{conn: conn, activity: activity} do - user = insert(:user) - - activity = - activity - |> put_in(["object", "type"], "Benis") - - _result = - conn - |> assign(:user, user) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{user.nickname}/outbox", activity) - |> json_response(400) - end - - test "it inserts an incoming sensitive activity into the database", %{ - conn: conn, - activity: activity - } do - user = insert(:user) - conn = assign(conn, :user, user) - object = Map.put(activity["object"], "sensitive", true) - activity = Map.put(activity, "object", object) - - response = - conn - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{user.nickname}/outbox", activity) - |> json_response(201) - - assert Activity.get_by_ap_id(response["id"]) - assert response["object"] - assert %Object{data: response_object} = Object.normalize(response["object"]) - assert response_object["sensitive"] == true - assert response_object["content"] == activity["object"]["content"] - - representation = - conn - |> put_req_header("accept", "application/activity+json") - |> get(response["id"]) - |> json_response(200) - - assert representation["object"]["sensitive"] == true - end - - test "it rejects an incoming activity with bogus type", %{conn: conn, activity: activity} do - user = insert(:user) - activity = Map.put(activity, "type", "BadType") - - conn = - conn - |> assign(:user, user) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{user.nickname}/outbox", activity) - - assert json_response(conn, 400) - end - - test "it erects a tombstone when receiving a delete activity", %{conn: conn} do - note_activity = insert(:note_activity) - note_object = Object.normalize(note_activity) - user = User.get_cached_by_ap_id(note_activity.data["actor"]) - - data = %{ - type: "Delete", - object: %{ - id: note_object.data["id"] - } - } - - conn = - conn - |> assign(:user, user) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{user.nickname}/outbox", data) - - result = json_response(conn, 201) - assert Activity.get_by_ap_id(result["id"]) - - assert object = Object.get_by_ap_id(note_object.data["id"]) - assert object.data["type"] == "Tombstone" - end - - test "it rejects delete activity of object from other actor", %{conn: conn} do - note_activity = insert(:note_activity) - note_object = Object.normalize(note_activity) - user = insert(:user) - - data = %{ - type: "Delete", - object: %{ - id: note_object.data["id"] - } - } - - conn = - conn - |> assign(:user, user) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{user.nickname}/outbox", data) - - assert json_response(conn, 400) - end - - test "it increases like count when receiving a like action", %{conn: conn} do - note_activity = insert(:note_activity) - note_object = Object.normalize(note_activity) - user = User.get_cached_by_ap_id(note_activity.data["actor"]) - - data = %{ - type: "Like", - object: %{ - id: note_object.data["id"] - } - } - - conn = - conn - |> assign(:user, user) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{user.nickname}/outbox", data) - - result = json_response(conn, 201) - assert Activity.get_by_ap_id(result["id"]) - - assert object = Object.get_by_ap_id(note_object.data["id"]) - assert object.data["like_count"] == 1 - end - - test "it doesn't spreads faulty attributedTo or actor fields", %{ - conn: conn, - activity: activity - } do - reimu = insert(:user, nickname: "reimu") - cirno = insert(:user, nickname: "cirno") - - assert reimu.ap_id - assert cirno.ap_id - - activity = - activity - |> put_in(["object", "actor"], reimu.ap_id) - |> put_in(["object", "attributedTo"], reimu.ap_id) - |> put_in(["actor"], reimu.ap_id) - |> put_in(["attributedTo"], reimu.ap_id) - - _reimu_outbox = - conn - |> assign(:user, cirno) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{reimu.nickname}/outbox", activity) - |> json_response(403) - - cirno_outbox = - conn - |> assign(:user, cirno) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{cirno.nickname}/outbox", activity) - |> json_response(201) - - assert cirno_outbox["attributedTo"] == nil - assert cirno_outbox["actor"] == cirno.ap_id - - assert cirno_object = Object.normalize(cirno_outbox["object"]) - assert cirno_object.data["actor"] == cirno.ap_id - assert cirno_object.data["attributedTo"] == cirno.ap_id - end - end - - describe "/relay/followers" do - test "it returns relay followers", %{conn: conn} do - relay_actor = Relay.get_actor() - user = insert(:user) - User.follow(user, relay_actor) - - result = - conn - |> get("/relay/followers") - |> json_response(200) - - assert result["first"]["orderedItems"] == [user.ap_id] - end - - test "on non-federating instance, it returns 404", %{conn: conn} do - Config.put([:instance, :federating], false) - user = insert(:user) - - conn - |> assign(:user, user) - |> get("/relay/followers") - |> json_response(404) - end - end - - describe "/relay/following" do - test "it returns relay following", %{conn: conn} do - result = - conn - |> get("/relay/following") - |> json_response(200) - - assert result["first"]["orderedItems"] == [] - end - - test "on non-federating instance, it returns 404", %{conn: conn} do - Config.put([:instance, :federating], false) - user = insert(:user) - - conn - |> assign(:user, user) - |> get("/relay/following") - |> json_response(404) - end - end - - describe "/users/:nickname/followers" do - test "it returns the followers in a collection", %{conn: conn} do - user = insert(:user) - user_two = insert(:user) - User.follow(user, user_two) - - result = - conn - |> assign(:user, user_two) - |> get("/users/#{user_two.nickname}/followers") - |> json_response(200) - - assert result["first"]["orderedItems"] == [user.ap_id] - end - - test "it returns a uri if the user has 'hide_followers' set", %{conn: conn} do - user = insert(:user) - user_two = insert(:user, hide_followers: true) - User.follow(user, user_two) - - result = - conn - |> assign(:user, user) - |> get("/users/#{user_two.nickname}/followers") - |> json_response(200) - - assert is_binary(result["first"]) - end - - test "it returns a 403 error on pages, if the user has 'hide_followers' set and the request is from another user", - %{conn: conn} do - user = insert(:user) - other_user = insert(:user, hide_followers: true) - - result = - conn - |> assign(:user, user) - |> get("/users/#{other_user.nickname}/followers?page=1") - - assert result.status == 403 - assert result.resp_body == "" - end - - test "it renders the page, if the user has 'hide_followers' set and the request is authenticated with the same user", - %{conn: conn} do - user = insert(:user, hide_followers: true) - other_user = insert(:user) - {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user) - - result = - conn - |> assign(:user, user) - |> get("/users/#{user.nickname}/followers?page=1") - |> json_response(200) - - assert result["totalItems"] == 1 - assert result["orderedItems"] == [other_user.ap_id] - end - - test "it works for more than 10 users", %{conn: conn} do - user = insert(:user) - - Enum.each(1..15, fn _ -> - other_user = insert(:user) - User.follow(other_user, user) - end) - - result = - conn - |> assign(:user, user) - |> get("/users/#{user.nickname}/followers") - |> json_response(200) - - assert length(result["first"]["orderedItems"]) == 10 - assert result["first"]["totalItems"] == 15 - assert result["totalItems"] == 15 - - result = - conn - |> assign(:user, user) - |> get("/users/#{user.nickname}/followers?page=2") - |> json_response(200) - - assert length(result["orderedItems"]) == 5 - assert result["totalItems"] == 15 - end - - test "does not require authentication", %{conn: conn} do - user = insert(:user) - - conn - |> get("/users/#{user.nickname}/followers") - |> json_response(200) - end - end - - describe "/users/:nickname/following" do - test "it returns the following in a collection", %{conn: conn} do - user = insert(:user) - user_two = insert(:user) - User.follow(user, user_two) - - result = - conn - |> assign(:user, user) - |> get("/users/#{user.nickname}/following") - |> json_response(200) - - assert result["first"]["orderedItems"] == [user_two.ap_id] - end - - test "it returns a uri if the user has 'hide_follows' set", %{conn: conn} do - user = insert(:user) - user_two = insert(:user, hide_follows: true) - User.follow(user, user_two) - - result = - conn - |> assign(:user, user) - |> get("/users/#{user_two.nickname}/following") - |> json_response(200) - - assert is_binary(result["first"]) - end - - test "it returns a 403 error on pages, if the user has 'hide_follows' set and the request is from another user", - %{conn: conn} do - user = insert(:user) - user_two = insert(:user, hide_follows: true) - - result = - conn - |> assign(:user, user) - |> get("/users/#{user_two.nickname}/following?page=1") - - assert result.status == 403 - assert result.resp_body == "" - end - - test "it renders the page, if the user has 'hide_follows' set and the request is authenticated with the same user", - %{conn: conn} do - user = insert(:user, hide_follows: true) - other_user = insert(:user) - {:ok, user, _other_user, _activity} = CommonAPI.follow(user, other_user) - - result = - conn - |> assign(:user, user) - |> get("/users/#{user.nickname}/following?page=1") - |> json_response(200) - - assert result["totalItems"] == 1 - assert result["orderedItems"] == [other_user.ap_id] - end - - test "it works for more than 10 users", %{conn: conn} do - user = insert(:user) - - Enum.each(1..15, fn _ -> - user = User.get_cached_by_id(user.id) - other_user = insert(:user) - User.follow(user, other_user) - end) - - result = - conn - |> assign(:user, user) - |> get("/users/#{user.nickname}/following") - |> json_response(200) - - assert length(result["first"]["orderedItems"]) == 10 - assert result["first"]["totalItems"] == 15 - assert result["totalItems"] == 15 - - result = - conn - |> assign(:user, user) - |> get("/users/#{user.nickname}/following?page=2") - |> json_response(200) - - assert length(result["orderedItems"]) == 5 - assert result["totalItems"] == 15 - end - - test "does not require authentication", %{conn: conn} do - user = insert(:user) - - conn - |> get("/users/#{user.nickname}/following") - |> json_response(200) - end - end - - describe "delivery tracking" do - test "it tracks a signed object fetch", %{conn: conn} do - user = insert(:user, local: false) - activity = insert(:note_activity) - object = Object.normalize(activity) - - object_path = String.trim_leading(object.data["id"], Pleroma.Web.Endpoint.url()) - - conn - |> put_req_header("accept", "application/activity+json") - |> assign(:user, user) - |> get(object_path) - |> json_response(200) - - assert Delivery.get(object.id, user.id) - end - - test "it tracks a signed activity fetch", %{conn: conn} do - user = insert(:user, local: false) - activity = insert(:note_activity) - object = Object.normalize(activity) - - activity_path = String.trim_leading(activity.data["id"], Pleroma.Web.Endpoint.url()) - - conn - |> put_req_header("accept", "application/activity+json") - |> assign(:user, user) - |> get(activity_path) - |> json_response(200) - - assert Delivery.get(object.id, user.id) - end - - test "it tracks a signed object fetch when the json is cached", %{conn: conn} do - user = insert(:user, local: false) - other_user = insert(:user, local: false) - activity = insert(:note_activity) - object = Object.normalize(activity) - - object_path = String.trim_leading(object.data["id"], Pleroma.Web.Endpoint.url()) - - conn - |> put_req_header("accept", "application/activity+json") - |> assign(:user, user) - |> get(object_path) - |> json_response(200) - - build_conn() - |> put_req_header("accept", "application/activity+json") - |> assign(:user, other_user) - |> get(object_path) - |> json_response(200) - - assert Delivery.get(object.id, user.id) - assert Delivery.get(object.id, other_user.id) - end - - test "it tracks a signed activity fetch when the json is cached", %{conn: conn} do - user = insert(:user, local: false) - other_user = insert(:user, local: false) - activity = insert(:note_activity) - object = Object.normalize(activity) - - activity_path = String.trim_leading(activity.data["id"], Pleroma.Web.Endpoint.url()) - - conn - |> put_req_header("accept", "application/activity+json") - |> assign(:user, user) - |> get(activity_path) - |> json_response(200) - - build_conn() - |> put_req_header("accept", "application/activity+json") - |> assign(:user, other_user) - |> get(activity_path) - |> json_response(200) - - assert Delivery.get(object.id, user.id) - assert Delivery.get(object.id, other_user.id) - end - end - - describe "Additional ActivityPub C2S endpoints" do - test "GET /api/ap/whoami", %{conn: conn} do - user = insert(:user) - - conn = - conn - |> assign(:user, user) - |> get("/api/ap/whoami") - - user = User.get_cached_by_id(user.id) - - assert UserView.render("user.json", %{user: user}) == json_response(conn, 200) - - conn - |> get("/api/ap/whoami") - |> json_response(403) - end - - setup do: clear_config([:media_proxy]) - setup do: clear_config([Pleroma.Upload]) - - test "POST /api/ap/upload_media", %{conn: conn} do - user = insert(:user) - - desc = "Description of the image" - - image = %Plug.Upload{ - content_type: "image/jpg", - path: Path.absname("test/fixtures/image.jpg"), - filename: "an_image.jpg" - } - - object = - conn - |> assign(:user, user) - |> post("/api/ap/upload_media", %{"file" => image, "description" => desc}) - |> json_response(:created) - - assert object["name"] == desc - assert object["type"] == "Document" - assert object["actor"] == user.ap_id - assert [%{"href" => object_href, "mediaType" => object_mediatype}] = object["url"] - assert is_binary(object_href) - assert object_mediatype == "image/jpeg" - - activity_request = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "type" => "Create", - "object" => %{ - "type" => "Note", - "content" => "AP C2S test, attachment", - "attachment" => [object] - }, - "to" => "https://www.w3.org/ns/activitystreams#Public", - "cc" => [] - } - - activity_response = - conn - |> assign(:user, user) - |> post("/users/#{user.nickname}/outbox", activity_request) - |> json_response(:created) - - assert activity_response["id"] - assert activity_response["object"] - assert activity_response["actor"] == user.ap_id - - assert %Object{data: %{"attachment" => [attachment]}} = - Object.normalize(activity_response["object"]) - - assert attachment["type"] == "Document" - assert attachment["name"] == desc - - assert [ - %{ - "href" => ^object_href, - "type" => "Link", - "mediaType" => ^object_mediatype - } - ] = attachment["url"] - - # Fails if unauthenticated - conn - |> post("/api/ap/upload_media", %{"file" => image, "description" => desc}) - |> json_response(403) - end - end -end diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs deleted file mode 100644 index 03f968aaf..000000000 --- a/test/web/activity_pub/activity_pub_test.exs +++ /dev/null @@ -1,2128 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.ActivityPubTest do - use Pleroma.DataCase - use Oban.Testing, repo: Pleroma.Repo - - alias Pleroma.Activity - alias Pleroma.Builders.ActivityBuilder - alias Pleroma.Config - alias Pleroma.Notification - alias Pleroma.Object - alias Pleroma.User - alias Pleroma.Web.ActivityPub.ActivityPub - alias Pleroma.Web.ActivityPub.Utils - alias Pleroma.Web.AdminAPI.AccountView - alias Pleroma.Web.CommonAPI - - import ExUnit.CaptureLog - import Mock - import Pleroma.Factory - import Tesla.Mock - - setup do - mock(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - - setup do: clear_config([:instance, :federating]) - - describe "streaming out participations" do - test "it streams them out" do - user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"}) - - {:ok, conversation} = Pleroma.Conversation.create_or_bump_for(activity) - - participations = - conversation.participations - |> Repo.preload(:user) - - with_mock Pleroma.Web.Streamer, - stream: fn _, _ -> nil end do - ActivityPub.stream_out_participations(conversation.participations) - - assert called(Pleroma.Web.Streamer.stream("participation", participations)) - end - end - - test "streams them out on activity creation" do - user_one = insert(:user) - user_two = insert(:user) - - with_mock Pleroma.Web.Streamer, - stream: fn _, _ -> nil end do - {:ok, activity} = - CommonAPI.post(user_one, %{ - status: "@#{user_two.nickname}", - visibility: "direct" - }) - - conversation = - activity.data["context"] - |> Pleroma.Conversation.get_for_ap_id() - |> Repo.preload(participations: :user) - - assert called(Pleroma.Web.Streamer.stream("participation", conversation.participations)) - end - end - end - - describe "fetching restricted by visibility" do - test "it restricts by the appropriate visibility" do - user = insert(:user) - - {:ok, public_activity} = CommonAPI.post(user, %{status: ".", visibility: "public"}) - - {:ok, direct_activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"}) - - {:ok, unlisted_activity} = CommonAPI.post(user, %{status: ".", visibility: "unlisted"}) - - {:ok, private_activity} = CommonAPI.post(user, %{status: ".", visibility: "private"}) - - 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}) - - assert activities == [unlisted_activity] - - activities = - 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}) - - assert activities == [public_activity] - - activities = - ActivityPub.fetch_activities([], %{ - visibility: ~w[private public], - actor_id: user.ap_id - }) - - assert activities == [public_activity, private_activity] - end - end - - describe "fetching excluded by visibility" do - test "it excludes by the appropriate visibility" do - user = insert(:user) - - {:ok, public_activity} = CommonAPI.post(user, %{status: ".", visibility: "public"}) - - {:ok, direct_activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"}) - - {:ok, unlisted_activity} = CommonAPI.post(user, %{status: ".", visibility: "unlisted"}) - - {:ok, private_activity} = CommonAPI.post(user, %{status: ".", visibility: "private"}) - - activities = - ActivityPub.fetch_activities([], %{ - exclude_visibilities: "direct", - actor_id: user.ap_id - }) - - assert public_activity in activities - assert unlisted_activity in activities - assert private_activity in activities - refute direct_activity in activities - - activities = - ActivityPub.fetch_activities([], %{ - exclude_visibilities: "unlisted", - actor_id: user.ap_id - }) - - assert public_activity in activities - refute unlisted_activity in activities - assert private_activity in activities - assert direct_activity in activities - - activities = - ActivityPub.fetch_activities([], %{ - exclude_visibilities: "private", - actor_id: user.ap_id - }) - - assert public_activity in activities - assert unlisted_activity in activities - refute private_activity in activities - assert direct_activity in activities - - activities = - ActivityPub.fetch_activities([], %{ - exclude_visibilities: "public", - actor_id: user.ap_id - }) - - refute public_activity in activities - assert unlisted_activity in activities - assert private_activity in activities - assert direct_activity in activities - end - end - - describe "building a user from his ap id" do - test "it returns a user" do - user_id = "http://mastodon.example.org/users/admin" - {:ok, user} = ActivityPub.make_user_from_ap_id(user_id) - assert user.ap_id == user_id - assert user.nickname == "admin@mastodon.example.org" - assert user.ap_enabled - assert user.follower_address == "http://mastodon.example.org/users/admin/followers" - end - - test "it returns a user that is invisible" do - user_id = "http://mastodon.example.org/users/relay" - {:ok, user} = ActivityPub.make_user_from_ap_id(user_id) - assert User.invisible?(user) - end - - test "it returns a user that accepts chat messages" do - user_id = "http://mastodon.example.org/users/admin" - {:ok, user} = ActivityPub.make_user_from_ap_id(user_id) - - assert user.accepts_chat_messages - end - end - - test "it fetches the appropriate tag-restricted posts" do - user = insert(:user) - - {:ok, status_one} = CommonAPI.post(user, %{status: ". #test"}) - {:ok, status_two} = CommonAPI.post(user, %{status: ". #essais"}) - {:ok, status_three} = CommonAPI.post(user, %{status: ". #test #reject"}) - - fetch_one = ActivityPub.fetch_activities([], %{type: "Create", tag: "test"}) - - fetch_two = ActivityPub.fetch_activities([], %{type: "Create", tag: ["test", "essais"]}) - - fetch_three = - ActivityPub.fetch_activities([], %{ - type: "Create", - tag: ["test", "essais"], - tag_reject: ["reject"] - }) - - fetch_four = - ActivityPub.fetch_activities([], %{ - type: "Create", - tag: ["test"], - tag_all: ["test", "reject"] - }) - - assert fetch_one == [status_one, status_three] - assert fetch_two == [status_one, status_two, status_three] - assert fetch_three == [status_one, status_two] - assert fetch_four == [status_three] - end - - describe "insertion" do - test "drops activities beyond a certain limit" do - limit = Config.get([:instance, :remote_limit]) - - random_text = - :crypto.strong_rand_bytes(limit + 1) - |> Base.encode64() - |> binary_part(0, limit + 1) - - data = %{ - "ok" => true, - "object" => %{ - "content" => random_text - } - } - - assert {:error, {:remote_limit_error, _}} = ActivityPub.insert(data) - end - - test "doesn't drop activities with content being null" do - user = insert(:user) - - data = %{ - "actor" => user.ap_id, - "to" => [], - "object" => %{ - "actor" => user.ap_id, - "to" => [], - "type" => "Note", - "content" => nil - } - } - - assert {:ok, _} = ActivityPub.insert(data) - end - - test "returns the activity if one with the same id is already in" do - activity = insert(:note_activity) - {:ok, new_activity} = ActivityPub.insert(activity.data) - - assert activity.id == new_activity.id - end - - test "inserts a given map into the activity database, giving it an id if it has none." do - user = insert(:user) - - data = %{ - "actor" => user.ap_id, - "to" => [], - "object" => %{ - "actor" => user.ap_id, - "to" => [], - "type" => "Note", - "content" => "hey" - } - } - - {:ok, %Activity{} = activity} = ActivityPub.insert(data) - assert activity.data["ok"] == data["ok"] - assert is_binary(activity.data["id"]) - - given_id = "bla" - - data = %{ - "id" => given_id, - "actor" => user.ap_id, - "to" => [], - "context" => "blabla", - "object" => %{ - "actor" => user.ap_id, - "to" => [], - "type" => "Note", - "content" => "hey" - } - } - - {:ok, %Activity{} = activity} = ActivityPub.insert(data) - assert activity.data["ok"] == data["ok"] - assert activity.data["id"] == given_id - assert activity.data["context"] == "blabla" - assert activity.data["context_id"] - end - - test "adds a context when none is there" do - user = insert(:user) - - data = %{ - "actor" => user.ap_id, - "to" => [], - "object" => %{ - "actor" => user.ap_id, - "to" => [], - "type" => "Note", - "content" => "hey" - } - } - - {:ok, %Activity{} = activity} = ActivityPub.insert(data) - object = Pleroma.Object.normalize(activity) - - assert is_binary(activity.data["context"]) - assert is_binary(object.data["context"]) - assert activity.data["context_id"] - assert object.data["context_id"] - end - - test "adds an id to a given object if it lacks one and is a note and inserts it to the object database" do - user = insert(:user) - - data = %{ - "actor" => user.ap_id, - "to" => [], - "object" => %{ - "actor" => user.ap_id, - "to" => [], - "type" => "Note", - "content" => "hey" - } - } - - {:ok, %Activity{} = activity} = ActivityPub.insert(data) - assert object = Object.normalize(activity) - assert is_binary(object.data["id"]) - end - end - - describe "listen activities" do - test "does not increase user note count" do - user = insert(:user) - - {:ok, activity} = - ActivityPub.listen(%{ - to: ["https://www.w3.org/ns/activitystreams#Public"], - actor: user, - context: "", - object: %{ - "actor" => user.ap_id, - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "artist" => "lain", - "title" => "lain radio episode 1", - "length" => 180_000, - "type" => "Audio" - } - }) - - assert activity.actor == user.ap_id - - user = User.get_cached_by_id(user.id) - assert user.note_count == 0 - end - - test "can be fetched into a timeline" do - _listen_activity_1 = insert(:listen) - _listen_activity_2 = insert(:listen) - _listen_activity_3 = insert(:listen) - - timeline = ActivityPub.fetch_activities([], %{type: ["Listen"]}) - - assert length(timeline) == 3 - end - end - - describe "create activities" do - test "it reverts create" do - user = insert(:user) - - with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do - assert {:error, :reverted} = - ActivityPub.create(%{ - to: ["user1", "user2"], - actor: user, - context: "", - object: %{ - "to" => ["user1", "user2"], - "type" => "Note", - "content" => "testing" - } - }) - end - - assert Repo.aggregate(Activity, :count, :id) == 0 - assert Repo.aggregate(Object, :count, :id) == 0 - end - - test "removes doubled 'to' recipients" do - user = insert(:user) - - {:ok, activity} = - ActivityPub.create(%{ - to: ["user1", "user1", "user2"], - actor: user, - context: "", - object: %{ - "to" => ["user1", "user1", "user2"], - "type" => "Note", - "content" => "testing" - } - }) - - assert activity.data["to"] == ["user1", "user2"] - assert activity.actor == user.ap_id - assert activity.recipients == ["user1", "user2", user.ap_id] - end - - test "increases user note count only for public activities" do - user = insert(:user) - - {:ok, _} = - CommonAPI.post(User.get_cached_by_id(user.id), %{ - status: "1", - visibility: "public" - }) - - {:ok, _} = - CommonAPI.post(User.get_cached_by_id(user.id), %{ - status: "2", - visibility: "unlisted" - }) - - {:ok, _} = - CommonAPI.post(User.get_cached_by_id(user.id), %{ - status: "2", - visibility: "private" - }) - - {:ok, _} = - CommonAPI.post(User.get_cached_by_id(user.id), %{ - status: "3", - visibility: "direct" - }) - - user = User.get_cached_by_id(user.id) - assert user.note_count == 2 - end - - test "increases replies count" do - user = insert(:user) - user2 = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "1", visibility: "public"}) - ap_id = activity.data["id"] - reply_data = %{status: "1", in_reply_to_status_id: activity.id} - - # public - {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, :visibility, "public")) - assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id) - assert object.data["repliesCount"] == 1 - - # unlisted - {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, :visibility, "unlisted")) - assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id) - assert object.data["repliesCount"] == 2 - - # private - {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, :visibility, "private")) - assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id) - assert object.data["repliesCount"] == 2 - - # direct - {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, :visibility, "direct")) - assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id) - assert object.data["repliesCount"] == 2 - end - end - - describe "fetch activities for recipients" do - test "retrieve the activities for certain recipients" do - {:ok, activity_one} = ActivityBuilder.insert(%{"to" => ["someone"]}) - {:ok, activity_two} = ActivityBuilder.insert(%{"to" => ["someone_else"]}) - {:ok, _activity_three} = ActivityBuilder.insert(%{"to" => ["noone"]}) - - activities = ActivityPub.fetch_activities(["someone", "someone_else"]) - assert length(activities) == 2 - assert activities == [activity_one, activity_two] - end - end - - describe "fetch activities in context" do - test "retrieves activities that have a given context" do - {:ok, activity} = ActivityBuilder.insert(%{"type" => "Create", "context" => "2hu"}) - {:ok, activity_two} = ActivityBuilder.insert(%{"type" => "Create", "context" => "2hu"}) - {:ok, _activity_three} = ActivityBuilder.insert(%{"type" => "Create", "context" => "3hu"}) - {:ok, _activity_four} = ActivityBuilder.insert(%{"type" => "Announce", "context" => "2hu"}) - activity_five = insert(:note_activity) - user = insert(:user) - - {:ok, _user_relationship} = User.block(user, %{ap_id: activity_five.data["actor"]}) - - activities = ActivityPub.fetch_activities_for_context("2hu", %{blocking_user: user}) - assert activities == [activity_two, activity] - end - - test "doesn't return activities with filtered words" do - user = insert(:user) - user_two = insert(:user) - insert(:filter, user: user, phrase: "test", hide: true) - - {:ok, %{id: id1, data: %{"context" => context}}} = CommonAPI.post(user, %{status: "1"}) - - {:ok, %{id: id2}} = CommonAPI.post(user_two, %{status: "2", in_reply_to_status_id: id1}) - - {:ok, %{id: id3} = user_activity} = - CommonAPI.post(user, %{status: "3 test?", in_reply_to_status_id: id2}) - - {:ok, %{id: id4} = filtered_activity} = - CommonAPI.post(user_two, %{status: "4 test!", in_reply_to_status_id: id3}) - - {:ok, _} = CommonAPI.post(user, %{status: "5", in_reply_to_status_id: id4}) - - activities = - context - |> ActivityPub.fetch_activities_for_context(%{user: user}) - |> Enum.map(& &1.id) - - assert length(activities) == 4 - assert user_activity.id in activities - refute filtered_activity.id in activities - end - end - - test "doesn't return blocked activities" do - activity_one = insert(:note_activity) - activity_two = insert(:note_activity) - activity_three = insert(:note_activity) - user = insert(:user) - 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}) - - assert Enum.member?(activities, activity_two) - assert Enum.member?(activities, activity_three) - refute Enum.member?(activities, activity_one) - - {:ok, _user_block} = User.unblock(user, %{ap_id: activity_one.data["actor"]}) - - activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true}) - - assert Enum.member?(activities, activity_two) - assert Enum.member?(activities, activity_three) - assert Enum.member?(activities, activity_one) - - {:ok, _user_relationship} = User.block(user, %{ap_id: activity_three.data["actor"]}) - {:ok, %{data: %{"object" => id}}} = CommonAPI.repeat(activity_three.id, booster) - %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}) - - 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}) - - assert Enum.member?(activities, activity_two) - assert Enum.member?(activities, activity_three) - assert Enum.member?(activities, boost_activity) - assert Enum.member?(activities, activity_one) - end - - test "doesn't return transitive interactions concerning blocked users" 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(friend, %{status: "hey! @#{blockee.nickname}"}) - - {:ok, activity_three} = CommonAPI.post(blockee, %{status: "hey! @#{friend.nickname}"}) - - {:ok, activity_four} = CommonAPI.post(blockee, %{status: "hey! @#{blocker.nickname}"}) - - activities = ActivityPub.fetch_activities([], %{blocking_user: blocker}) - - assert Enum.member?(activities, activity_one) - refute Enum.member?(activities, activity_two) - refute Enum.member?(activities, activity_three) - refute Enum.member?(activities, activity_four) - end - - test "doesn't return announce activities with blocked users in 'to'" 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}"}) - - {:ok, activity_three} = CommonAPI.repeat(activity_two.id, friend) - - activities = - 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) - refute Enum.member?(activities, activity_two.id) - refute Enum.member?(activities, activity_three.id) - end - - test "doesn't return activities from blocked domains" do - domain = "dogwhistle.zone" - domain_user = insert(:user, %{ap_id: "https://#{domain}/@pundit"}) - note = insert(:note, %{data: %{"actor" => domain_user.ap_id}}) - activity = insert(:note_activity, %{note: note}) - user = insert(:user) - {:ok, user} = User.block_domain(user, domain) - - activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true}) - - refute activity in activities - - followed_user = insert(:user) - CommonAPI.follow(user, followed_user) - {:ok, repeat_activity} = CommonAPI.repeat(activity.id, followed_user) - - activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true}) - - refute repeat_activity in activities - end - - test "does return activities from followed users on blocked domains" do - domain = "meanies.social" - domain_user = insert(:user, %{ap_id: "https://#{domain}/@pundit"}) - blocker = insert(:user) - - {:ok, blocker} = User.follow(blocker, domain_user) - {:ok, blocker} = User.block_domain(blocker, domain) - - assert User.following?(blocker, domain_user) - assert User.blocks_domain?(blocker, domain_user) - refute User.blocks?(blocker, domain_user) - - 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}) - - assert activity in activities - - # And check that if the guy we DO follow boosts someone else from their domain, - # that should be hidden - another_user = insert(:user, %{ap_id: "https://#{domain}/@meanie2"}) - bad_note = insert(:note, %{data: %{"actor" => another_user.ap_id}}) - 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}) - - refute repeat_activity in activities - end - - test "doesn't return muted activities" do - activity_one = insert(:note_activity) - activity_two = insert(:note_activity) - activity_three = insert(:note_activity) - user = insert(:user) - booster = insert(:user) - - 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}) - - assert Enum.member?(activities, activity_two) - assert Enum.member?(activities, activity_three) - refute Enum.member?(activities, activity_one) - - # Calling with 'with_muted' will deliver muted activities, too. - activities = - ActivityPub.fetch_activities([], %{ - muting_user: user, - with_muted: true, - skip_preload: true - }) - - assert Enum.member?(activities, activity_two) - assert Enum.member?(activities, activity_three) - assert Enum.member?(activities, activity_one) - - {:ok, _user_mute} = User.unmute(user, activity_one_actor) - - activities = ActivityPub.fetch_activities([], %{muting_user: user, skip_preload: true}) - - assert Enum.member?(activities, activity_two) - assert Enum.member?(activities, activity_three) - assert Enum.member?(activities, activity_one) - - activity_three_actor = User.get_by_ap_id(activity_three.data["actor"]) - {:ok, _user_relationships} = User.mute(user, activity_three_actor) - {:ok, %{data: %{"object" => id}}} = CommonAPI.repeat(activity_three.id, booster) - %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}) - - 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}) - - assert Enum.member?(activities, activity_two) - assert Enum.member?(activities, activity_three) - assert Enum.member?(activities, boost_activity) - assert Enum.member?(activities, activity_one) - end - - test "doesn't return thread muted activities" do - user = insert(:user) - _activity_one = insert(:note_activity) - note_two = insert(:note, data: %{"context" => "suya.."}) - activity_two = insert(:note_activity, note: note_two) - - {:ok, _activity_two} = CommonAPI.add_mute(user, activity_two) - - assert [_activity_one] = ActivityPub.fetch_activities([], %{muting_user: user}) - end - - test "returns thread muted activities when with_muted is set" do - user = insert(:user) - _activity_one = insert(:note_activity) - note_two = insert(:note, data: %{"context" => "suya.."}) - activity_two = insert(:note_activity, note: note_two) - - {:ok, _activity_two} = CommonAPI.add_mute(user, activity_two) - - assert [_activity_two, _activity_one] = - ActivityPub.fetch_activities([], %{muting_user: user, with_muted: true}) - end - - test "does include announces on request" do - activity_three = insert(:note_activity) - user = insert(:user) - booster = insert(:user) - - {:ok, user} = User.follow(user, booster) - - {:ok, announce} = CommonAPI.repeat(activity_three.id, booster) - - [announce_activity] = ActivityPub.fetch_activities([user.ap_id | User.following(user)]) - - assert announce_activity.id == announce.id - end - - test "excludes reblogs on request" do - user = insert(:user) - {: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}) - - assert activity == expected_activity - end - - describe "irreversible filters" do - setup do - user = insert(:user) - user_two = insert(:user) - - insert(:filter, user: user_two, phrase: "cofe", hide: true) - insert(:filter, user: user_two, phrase: "ok boomer", hide: true) - insert(:filter, user: user_two, phrase: "test", hide: false) - - params = %{ - type: ["Create", "Announce"], - user: user_two - } - - {:ok, %{user: user, user_two: user_two, params: params}} - end - - test "it returns statuses if they don't contain exact filter words", %{ - user: user, - params: params - } do - {:ok, _} = CommonAPI.post(user, %{status: "hey"}) - {:ok, _} = CommonAPI.post(user, %{status: "got cofefe?"}) - {:ok, _} = CommonAPI.post(user, %{status: "I am not a boomer"}) - {:ok, _} = CommonAPI.post(user, %{status: "ok boomers"}) - {:ok, _} = CommonAPI.post(user, %{status: "ccofee is not a word"}) - {:ok, _} = CommonAPI.post(user, %{status: "this is a test"}) - - activities = ActivityPub.fetch_activities([], params) - - assert Enum.count(activities) == 6 - end - - test "it does not filter user's own statuses", %{user_two: user_two, params: params} do - {:ok, _} = CommonAPI.post(user_two, %{status: "Give me some cofe!"}) - {:ok, _} = CommonAPI.post(user_two, %{status: "ok boomer"}) - - activities = ActivityPub.fetch_activities([], params) - - assert Enum.count(activities) == 2 - end - - test "it excludes statuses with filter words", %{user: user, params: params} do - {:ok, _} = CommonAPI.post(user, %{status: "Give me some cofe!"}) - {:ok, _} = CommonAPI.post(user, %{status: "ok boomer"}) - {:ok, _} = CommonAPI.post(user, %{status: "is it a cOfE?"}) - {:ok, _} = CommonAPI.post(user, %{status: "cofe is all I need"}) - {:ok, _} = CommonAPI.post(user, %{status: "— ok BOOMER\n"}) - - activities = ActivityPub.fetch_activities([], params) - - assert Enum.empty?(activities) - end - - test "it returns all statuses if user does not have any filters" do - another_user = insert(:user) - {:ok, _} = CommonAPI.post(another_user, %{status: "got cofe?"}) - {:ok, _} = CommonAPI.post(another_user, %{status: "test!"}) - - activities = - ActivityPub.fetch_activities([], %{ - type: ["Create", "Announce"], - user: another_user - }) - - assert Enum.count(activities) == 2 - end - end - - describe "public fetch activities" do - test "doesn't retrieve unlisted activities" do - user = insert(:user) - - {:ok, _unlisted_activity} = CommonAPI.post(user, %{status: "yeah", visibility: "unlisted"}) - - {:ok, listed_activity} = CommonAPI.post(user, %{status: "yeah"}) - - [activity] = ActivityPub.fetch_public_activities() - - assert activity == listed_activity - end - - test "retrieves public activities" do - _activities = ActivityPub.fetch_public_activities() - - %{public: public} = ActivityBuilder.public_and_non_public() - - activities = ActivityPub.fetch_public_activities() - assert length(activities) == 1 - assert Enum.at(activities, 0) == public - end - - test "retrieves a maximum of 20 activities" do - ActivityBuilder.insert_list(10) - expected_activities = ActivityBuilder.insert_list(20) - - activities = ActivityPub.fetch_public_activities() - - assert collect_ids(activities) == collect_ids(expected_activities) - assert length(activities) == 20 - end - - test "retrieves ids starting from a since_id" do - activities = ActivityBuilder.insert_list(30) - expected_activities = ActivityBuilder.insert_list(10) - since_id = List.last(activities).id - - activities = ActivityPub.fetch_public_activities(%{since_id: since_id}) - - assert collect_ids(activities) == collect_ids(expected_activities) - assert length(activities) == 10 - end - - test "retrieves ids up to max_id" do - ActivityBuilder.insert_list(10) - expected_activities = ActivityBuilder.insert_list(20) - - %{id: max_id} = - 10 - |> ActivityBuilder.insert_list() - |> List.first() - - activities = ActivityPub.fetch_public_activities(%{max_id: max_id}) - - assert length(activities) == 20 - assert collect_ids(activities) == collect_ids(expected_activities) - end - - test "paginates via offset/limit" do - _first_part_activities = ActivityBuilder.insert_list(10) - second_part_activities = ActivityBuilder.insert_list(10) - - later_activities = ActivityBuilder.insert_list(10) - - activities = ActivityPub.fetch_public_activities(%{page: "2", page_size: "20"}, :offset) - - assert length(activities) == 20 - - assert collect_ids(activities) == - collect_ids(second_part_activities) ++ collect_ids(later_activities) - end - - test "doesn't return reblogs for users for whom reblogs have been muted" do - activity = insert(:note_activity) - user = insert(:user) - booster = insert(:user) - {:ok, _reblog_mute} = CommonAPI.hide_reblogs(user, booster) - - {:ok, activity} = CommonAPI.repeat(activity.id, booster) - - activities = ActivityPub.fetch_activities([], %{muting_user: user}) - - refute Enum.any?(activities, fn %{id: id} -> id == activity.id end) - end - - test "returns reblogs for users for whom reblogs have not been muted" do - activity = insert(:note_activity) - user = insert(:user) - booster = insert(:user) - {:ok, _reblog_mute} = CommonAPI.hide_reblogs(user, booster) - {:ok, _reblog_mute} = CommonAPI.show_reblogs(user, booster) - - {:ok, activity} = CommonAPI.repeat(activity.id, booster) - - activities = ActivityPub.fetch_activities([], %{muting_user: user}) - - assert Enum.any?(activities, fn %{id: id} -> id == activity.id end) - end - end - - describe "uploading files" do - setup do - test_file = %Plug.Upload{ - content_type: "image/jpg", - path: Path.absname("test/fixtures/image.jpg"), - filename: "an_image.jpg" - } - - %{test_file: test_file} - end - - test "sets a description if given", %{test_file: file} do - {:ok, %Object{} = object} = ActivityPub.upload(file, description: "a cool file") - assert object.data["name"] == "a cool file" - end - - test "it sets the default description depending on the configuration", %{test_file: file} do - clear_config([Pleroma.Upload, :default_description]) - - Pleroma.Config.put([Pleroma.Upload, :default_description], nil) - {:ok, %Object{} = object} = ActivityPub.upload(file) - assert object.data["name"] == "" - - Pleroma.Config.put([Pleroma.Upload, :default_description], :filename) - {:ok, %Object{} = object} = ActivityPub.upload(file) - assert object.data["name"] == "an_image.jpg" - - Pleroma.Config.put([Pleroma.Upload, :default_description], "unnamed attachment") - {:ok, %Object{} = object} = ActivityPub.upload(file) - assert object.data["name"] == "unnamed attachment" - end - - test "copies the file to the configured folder", %{test_file: file} do - clear_config([Pleroma.Upload, :default_description], :filename) - {:ok, %Object{} = object} = ActivityPub.upload(file) - assert object.data["name"] == "an_image.jpg" - end - - test "works with base64 encoded images" do - file = %{ - img: data_uri() - } - - {:ok, %Object{}} = ActivityPub.upload(file) - end - end - - describe "fetch the latest Follow" do - test "fetches the latest Follow activity" do - %Activity{data: %{"type" => "Follow"}} = activity = insert(:follow_activity) - follower = Repo.get_by(User, ap_id: activity.data["actor"]) - followed = Repo.get_by(User, ap_id: activity.data["object"]) - - assert activity == Utils.fetch_latest_follow(follower, followed) - end - end - - describe "unfollowing" do - test "it reverts unfollow activity" do - follower = insert(:user) - followed = insert(:user) - - {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed) - - with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do - assert {:error, :reverted} = ActivityPub.unfollow(follower, followed) - end - - activity = Activity.get_by_id(follow_activity.id) - assert activity.data["type"] == "Follow" - assert activity.data["actor"] == follower.ap_id - - assert activity.data["object"] == followed.ap_id - end - - test "creates an undo activity for the last follow" do - follower = insert(:user) - followed = insert(:user) - - {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed) - {:ok, activity} = ActivityPub.unfollow(follower, followed) - - assert activity.data["type"] == "Undo" - assert activity.data["actor"] == follower.ap_id - - embedded_object = activity.data["object"] - assert is_map(embedded_object) - assert embedded_object["type"] == "Follow" - assert embedded_object["object"] == followed.ap_id - assert embedded_object["id"] == follow_activity.data["id"] - end - - test "creates an undo activity for a pending follow request" do - follower = insert(:user) - followed = insert(:user, %{locked: true}) - - {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed) - {:ok, activity} = ActivityPub.unfollow(follower, followed) - - assert activity.data["type"] == "Undo" - assert activity.data["actor"] == follower.ap_id - - embedded_object = activity.data["object"] - assert is_map(embedded_object) - assert embedded_object["type"] == "Follow" - assert embedded_object["object"] == followed.ap_id - assert embedded_object["id"] == follow_activity.data["id"] - end - end - - describe "timeline post-processing" do - test "it filters broken threads" do - user1 = insert(:user) - user2 = insert(:user) - user3 = insert(:user) - - {:ok, user1} = User.follow(user1, user3) - assert User.following?(user1, user3) - - {:ok, user2} = User.follow(user2, user3) - assert User.following?(user2, user3) - - {:ok, user3} = User.follow(user3, user2) - assert User.following?(user3, user2) - - {:ok, public_activity} = CommonAPI.post(user3, %{status: "hi 1"}) - - {:ok, private_activity_1} = CommonAPI.post(user3, %{status: "hi 2", visibility: "private"}) - - {:ok, private_activity_2} = - CommonAPI.post(user2, %{ - status: "hi 3", - visibility: "private", - in_reply_to_status_id: private_activity_1.id - }) - - {:ok, private_activity_3} = - CommonAPI.post(user3, %{ - status: "hi 4", - visibility: "private", - in_reply_to_status_id: private_activity_2.id - }) - - activities = - ActivityPub.fetch_activities([user1.ap_id | User.following(user1)]) - |> Enum.map(fn a -> a.id end) - - private_activity_1 = Activity.get_by_ap_id_with_object(private_activity_1.data["id"]) - - assert [public_activity.id, private_activity_1.id, private_activity_3.id] == activities - - assert length(activities) == 3 - - activities = - 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 - assert length(activities) == 2 - end - end - - describe "flag/1" do - setup do - reporter = insert(:user) - target_account = insert(:user) - content = "foobar" - {:ok, activity} = CommonAPI.post(target_account, %{status: content}) - context = Utils.generate_context_id() - - reporter_ap_id = reporter.ap_id - target_ap_id = target_account.ap_id - activity_ap_id = activity.data["id"] - - activity_with_object = Activity.get_by_ap_id_with_object(activity_ap_id) - - {:ok, - %{ - reporter: reporter, - context: context, - target_account: target_account, - reported_activity: activity, - content: content, - activity_ap_id: activity_ap_id, - activity_with_object: activity_with_object, - reporter_ap_id: reporter_ap_id, - target_ap_id: target_ap_id - }} - end - - test "it can create a Flag activity", - %{ - reporter: reporter, - context: context, - target_account: target_account, - reported_activity: reported_activity, - content: content, - activity_ap_id: activity_ap_id, - activity_with_object: activity_with_object, - reporter_ap_id: reporter_ap_id, - target_ap_id: target_ap_id - } do - assert {:ok, activity} = - ActivityPub.flag(%{ - actor: reporter, - context: context, - account: target_account, - statuses: [reported_activity], - content: content - }) - - note_obj = %{ - "type" => "Note", - "id" => activity_ap_id, - "content" => content, - "published" => activity_with_object.object.data["published"], - "actor" => - AccountView.render("show.json", %{user: target_account, skip_visibility_check: true}) - } - - assert %Activity{ - actor: ^reporter_ap_id, - data: %{ - "type" => "Flag", - "content" => ^content, - "context" => ^context, - "object" => [^target_ap_id, ^note_obj] - } - } = activity - end - - test_with_mock "strips status data from Flag, before federating it", - %{ - reporter: reporter, - context: context, - target_account: target_account, - reported_activity: reported_activity, - content: content - }, - Utils, - [:passthrough], - [] do - {:ok, activity} = - ActivityPub.flag(%{ - actor: reporter, - context: context, - account: target_account, - statuses: [reported_activity], - content: content - }) - - new_data = - put_in(activity.data, ["object"], [target_account.ap_id, reported_activity.data["id"]]) - - assert_called(Utils.maybe_federate(%{activity | data: new_data})) - end - end - - test "fetch_activities/2 returns activities addressed to a list " do - user = insert(:user) - member = insert(:user) - {:ok, list} = Pleroma.List.create("foo", user) - {:ok, list} = Pleroma.List.follow(list, member) - - {:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"}) - - activity = Repo.preload(activity, :bookmark) - activity = %Activity{activity | thread_muted?: !!activity.thread_muted?} - - assert ActivityPub.fetch_activities([], %{user: user}) == [activity] - end - - def data_uri do - File.read!("test/fixtures/avatar_data_uri") - end - - describe "fetch_activities_bounded" do - test "fetches private posts for followed users" do - user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{ - status: "thought I looked cute might delete later :3", - visibility: "private" - }) - - [result] = ActivityPub.fetch_activities_bounded([user.follower_address], []) - assert result.id == activity.id - end - - test "fetches only public posts for other users" do - user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{status: "#cofe", visibility: "public"}) - - {:ok, _private_activity} = - CommonAPI.post(user, %{ - status: "why is tenshi eating a corndog so cute?", - visibility: "private" - }) - - [result] = ActivityPub.fetch_activities_bounded([], [user.follower_address]) - assert result.id == activity.id - end - end - - describe "fetch_follow_information_for_user" do - test "syncronizes following/followers counters" do - user = - insert(:user, - local: false, - follower_address: "http://localhost:4001/users/fuser2/followers", - following_address: "http://localhost:4001/users/fuser2/following" - ) - - {:ok, info} = ActivityPub.fetch_follow_information_for_user(user) - assert info.follower_count == 527 - assert info.following_count == 267 - end - - test "detects hidden followers" do - mock(fn env -> - case env.url do - "http://localhost:4001/users/masto_closed/followers?page=1" -> - %Tesla.Env{status: 403, body: ""} - - _ -> - apply(HttpRequestMock, :request, [env]) - end - end) - - user = - insert(:user, - local: false, - follower_address: "http://localhost:4001/users/masto_closed/followers", - following_address: "http://localhost:4001/users/masto_closed/following" - ) - - {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user) - assert follow_info.hide_followers == true - assert follow_info.hide_follows == false - end - - test "detects hidden follows" do - mock(fn env -> - case env.url do - "http://localhost:4001/users/masto_closed/following?page=1" -> - %Tesla.Env{status: 403, body: ""} - - _ -> - apply(HttpRequestMock, :request, [env]) - end - end) - - user = - insert(:user, - local: false, - follower_address: "http://localhost:4001/users/masto_closed/followers", - following_address: "http://localhost:4001/users/masto_closed/following" - ) - - {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user) - assert follow_info.hide_followers == false - assert follow_info.hide_follows == true - end - - test "detects hidden follows/followers for friendica" do - user = - insert(:user, - local: false, - follower_address: "http://localhost:8080/followers/fuser3", - following_address: "http://localhost:8080/following/fuser3" - ) - - {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user) - assert follow_info.hide_followers == true - assert follow_info.follower_count == 296 - assert follow_info.following_count == 32 - assert follow_info.hide_follows == true - end - - test "doesn't crash when follower and following counters are hidden" do - mock(fn env -> - case env.url do - "http://localhost:4001/users/masto_hidden_counters/following" -> - json(%{ - "@context" => "https://www.w3.org/ns/activitystreams", - "id" => "http://localhost:4001/users/masto_hidden_counters/followers" - }) - - "http://localhost:4001/users/masto_hidden_counters/following?page=1" -> - %Tesla.Env{status: 403, body: ""} - - "http://localhost:4001/users/masto_hidden_counters/followers" -> - json(%{ - "@context" => "https://www.w3.org/ns/activitystreams", - "id" => "http://localhost:4001/users/masto_hidden_counters/following" - }) - - "http://localhost:4001/users/masto_hidden_counters/followers?page=1" -> - %Tesla.Env{status: 403, body: ""} - end - end) - - user = - insert(:user, - local: false, - follower_address: "http://localhost:4001/users/masto_hidden_counters/followers", - following_address: "http://localhost:4001/users/masto_hidden_counters/following" - ) - - {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user) - - assert follow_info.hide_followers == true - assert follow_info.follower_count == 0 - assert follow_info.hide_follows == true - assert follow_info.following_count == 0 - end - end - - describe "fetch_favourites/3" do - test "returns a favourite activities sorted by adds to favorite" do - user = insert(:user) - other_user = insert(:user) - user1 = insert(:user) - user2 = insert(:user) - {:ok, a1} = CommonAPI.post(user1, %{status: "bla"}) - {:ok, _a2} = CommonAPI.post(user2, %{status: "traps are happy"}) - {:ok, a3} = CommonAPI.post(user2, %{status: "Trees Are "}) - {:ok, a4} = CommonAPI.post(user2, %{status: "Agent Smith "}) - {:ok, a5} = CommonAPI.post(user1, %{status: "Red or Blue "}) - - {:ok, _} = CommonAPI.favorite(user, a4.id) - {:ok, _} = CommonAPI.favorite(other_user, a3.id) - {:ok, _} = CommonAPI.favorite(user, a3.id) - {:ok, _} = CommonAPI.favorite(other_user, a5.id) - {:ok, _} = CommonAPI.favorite(user, a5.id) - {:ok, _} = CommonAPI.favorite(other_user, a4.id) - {:ok, _} = CommonAPI.favorite(user, a1.id) - {:ok, _} = CommonAPI.favorite(other_user, a1.id) - result = ActivityPub.fetch_favourites(user) - - assert Enum.map(result, & &1.id) == [a1.id, a5.id, a3.id, a4.id] - - result = ActivityPub.fetch_favourites(user, %{limit: 2}) - assert Enum.map(result, & &1.id) == [a1.id, a5.id] - end - end - - describe "Move activity" do - test "create" do - %{ap_id: old_ap_id} = old_user = insert(:user) - %{ap_id: new_ap_id} = new_user = insert(:user, also_known_as: [old_ap_id]) - follower = insert(:user) - follower_move_opted_out = insert(:user, allow_following_move: false) - - User.follow(follower, old_user) - User.follow(follower_move_opted_out, old_user) - - assert User.following?(follower, old_user) - assert User.following?(follower_move_opted_out, old_user) - - assert {:ok, activity} = ActivityPub.move(old_user, new_user) - - assert %Activity{ - actor: ^old_ap_id, - data: %{ - "actor" => ^old_ap_id, - "object" => ^old_ap_id, - "target" => ^new_ap_id, - "type" => "Move" - }, - local: true - } = activity - - params = %{ - "op" => "move_following", - "origin_id" => old_user.id, - "target_id" => new_user.id - } - - assert_enqueued(worker: Pleroma.Workers.BackgroundWorker, args: params) - - Pleroma.Workers.BackgroundWorker.perform(%Oban.Job{args: params}) - - refute User.following?(follower, old_user) - assert User.following?(follower, new_user) - - assert User.following?(follower_move_opted_out, old_user) - refute User.following?(follower_move_opted_out, new_user) - - activity = %Activity{activity | object: nil} - - assert [%Notification{activity: ^activity}] = Notification.for_user(follower) - - assert [%Notification{activity: ^activity}] = Notification.for_user(follower_move_opted_out) - end - - test "old user must be in the new user's `also_known_as` list" do - old_user = insert(:user) - new_user = insert(:user) - - assert {:error, "Target account must have the origin in `alsoKnownAs`"} = - ActivityPub.move(old_user, new_user) - end - end - - test "doesn't retrieve replies activities with exclude_replies" do - user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "yeah"}) - - {:ok, _reply} = CommonAPI.post(user, %{status: "yeah", in_reply_to_status_id: activity.id}) - - [result] = ActivityPub.fetch_public_activities(%{exclude_replies: true}) - - assert result.id == activity.id - - assert length(ActivityPub.fetch_public_activities()) == 2 - end - - describe "replies filtering with public messages" do - setup :public_messages - - test "public timeline", %{users: %{u1: user}} do - activities_ids = - %{} - |> Map.put(:type, ["Create", "Announce"]) - |> Map.put(:local_only, false) - |> Map.put(:blocking_user, user) - |> Map.put(:muting_user, user) - |> Map.put(:reply_filtering_user, user) - |> ActivityPub.fetch_public_activities() - |> Enum.map(& &1.id) - - assert length(activities_ids) == 16 - end - - test "public timeline with reply_visibility `following`", %{ - users: %{u1: user}, - u1: u1, - u2: u2, - u3: u3, - u4: u4, - activities: activities - } do - activities_ids = - %{} - |> Map.put(:type, ["Create", "Announce"]) - |> Map.put(:local_only, false) - |> Map.put(:blocking_user, user) - |> Map.put(:muting_user, user) - |> Map.put(:reply_visibility, "following") - |> Map.put(:reply_filtering_user, user) - |> ActivityPub.fetch_public_activities() - |> Enum.map(& &1.id) - - assert length(activities_ids) == 14 - - visible_ids = - Map.values(u1) ++ Map.values(u2) ++ Map.values(u4) ++ Map.values(activities) ++ [u3[:r1]] - - assert Enum.all?(visible_ids, &(&1 in activities_ids)) - end - - test "public timeline with reply_visibility `self`", %{ - users: %{u1: user}, - u1: u1, - u2: u2, - u3: u3, - u4: u4, - activities: activities - } do - activities_ids = - %{} - |> Map.put(:type, ["Create", "Announce"]) - |> Map.put(:local_only, false) - |> Map.put(:blocking_user, user) - |> Map.put(:muting_user, user) - |> Map.put(:reply_visibility, "self") - |> Map.put(:reply_filtering_user, user) - |> ActivityPub.fetch_public_activities() - |> Enum.map(& &1.id) - - assert length(activities_ids) == 10 - visible_ids = Map.values(u1) ++ [u2[:r1], u3[:r1], u4[:r1]] ++ Map.values(activities) - assert Enum.all?(visible_ids, &(&1 in activities_ids)) - end - - test "home timeline", %{ - users: %{u1: user}, - activities: activities, - u1: u1, - u2: u2, - u3: u3, - u4: u4 - } do - params = - %{} - |> Map.put(:type, ["Create", "Announce"]) - |> Map.put(:blocking_user, user) - |> Map.put(:muting_user, user) - |> Map.put(:user, user) - |> Map.put(:reply_filtering_user, user) - - activities_ids = - ActivityPub.fetch_activities([user.ap_id | User.following(user)], params) - |> Enum.map(& &1.id) - - assert length(activities_ids) == 13 - - visible_ids = - Map.values(u1) ++ - Map.values(u3) ++ - [ - activities[:a1], - activities[:a2], - activities[:a4], - u2[:r1], - u2[:r3], - u4[:r1], - u4[:r2] - ] - - assert Enum.all?(visible_ids, &(&1 in activities_ids)) - end - - test "home timeline with reply_visibility `following`", %{ - users: %{u1: user}, - activities: activities, - u1: u1, - u2: u2, - u3: u3, - u4: u4 - } do - params = - %{} - |> Map.put(:type, ["Create", "Announce"]) - |> Map.put(:blocking_user, user) - |> Map.put(:muting_user, user) - |> Map.put(:user, user) - |> Map.put(:reply_visibility, "following") - |> Map.put(:reply_filtering_user, user) - - activities_ids = - ActivityPub.fetch_activities([user.ap_id | User.following(user)], params) - |> Enum.map(& &1.id) - - assert length(activities_ids) == 11 - - visible_ids = - Map.values(u1) ++ - [ - activities[:a1], - activities[:a2], - activities[:a4], - u2[:r1], - u2[:r3], - u3[:r1], - u4[:r1], - u4[:r2] - ] - - assert Enum.all?(visible_ids, &(&1 in activities_ids)) - end - - test "home timeline with reply_visibility `self`", %{ - users: %{u1: user}, - activities: activities, - u1: u1, - u2: u2, - u3: u3, - u4: u4 - } do - params = - %{} - |> Map.put(:type, ["Create", "Announce"]) - |> Map.put(:blocking_user, user) - |> Map.put(:muting_user, user) - |> Map.put(:user, user) - |> Map.put(:reply_visibility, "self") - |> Map.put(:reply_filtering_user, user) - - activities_ids = - ActivityPub.fetch_activities([user.ap_id | User.following(user)], params) - |> Enum.map(& &1.id) - - assert length(activities_ids) == 9 - - visible_ids = - Map.values(u1) ++ - [ - activities[:a1], - activities[:a2], - activities[:a4], - u2[:r1], - u3[:r1], - u4[:r1] - ] - - assert Enum.all?(visible_ids, &(&1 in activities_ids)) - end - - 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 - setup :private_messages - - test "public timeline", %{users: %{u1: user}} do - activities_ids = - %{} - |> Map.put(:type, ["Create", "Announce"]) - |> Map.put(:local_only, false) - |> Map.put(:blocking_user, user) - |> Map.put(:muting_user, user) - |> Map.put(:user, user) - |> ActivityPub.fetch_public_activities() - |> Enum.map(& &1.id) - - assert activities_ids == [] - end - - test "public timeline with default reply_visibility `following`", %{users: %{u1: user}} do - activities_ids = - %{} - |> Map.put(:type, ["Create", "Announce"]) - |> Map.put(:local_only, false) - |> Map.put(:blocking_user, user) - |> Map.put(:muting_user, user) - |> Map.put(:reply_visibility, "following") - |> Map.put(:reply_filtering_user, user) - |> Map.put(:user, user) - |> ActivityPub.fetch_public_activities() - |> Enum.map(& &1.id) - - assert activities_ids == [] - end - - test "public timeline with default reply_visibility `self`", %{users: %{u1: user}} do - activities_ids = - %{} - |> Map.put(:type, ["Create", "Announce"]) - |> Map.put(:local_only, false) - |> Map.put(:blocking_user, user) - |> Map.put(:muting_user, user) - |> Map.put(:reply_visibility, "self") - |> Map.put(:reply_filtering_user, user) - |> Map.put(:user, user) - |> ActivityPub.fetch_public_activities() - |> Enum.map(& &1.id) - - assert activities_ids == [] - end - - test "home timeline", %{users: %{u1: user}} do - params = - %{} - |> Map.put(:type, ["Create", "Announce"]) - |> Map.put(:blocking_user, user) - |> Map.put(:muting_user, user) - |> Map.put(:user, user) - - activities_ids = - ActivityPub.fetch_activities([user.ap_id | User.following(user)], params) - |> Enum.map(& &1.id) - - assert length(activities_ids) == 12 - end - - test "home timeline with default reply_visibility `following`", %{users: %{u1: user}} do - params = - %{} - |> Map.put(:type, ["Create", "Announce"]) - |> Map.put(:blocking_user, user) - |> Map.put(:muting_user, user) - |> Map.put(:user, user) - |> Map.put(:reply_visibility, "following") - |> Map.put(:reply_filtering_user, user) - - activities_ids = - ActivityPub.fetch_activities([user.ap_id | User.following(user)], params) - |> Enum.map(& &1.id) - - assert length(activities_ids) == 12 - end - - test "home timeline with default reply_visibility `self`", %{ - users: %{u1: user}, - activities: activities, - u1: u1, - u2: u2, - u3: u3, - u4: u4 - } do - params = - %{} - |> Map.put(:type, ["Create", "Announce"]) - |> Map.put(:blocking_user, user) - |> Map.put(:muting_user, user) - |> Map.put(:user, user) - |> Map.put(:reply_visibility, "self") - |> Map.put(:reply_filtering_user, user) - - activities_ids = - ActivityPub.fetch_activities([user.ap_id | User.following(user)], params) - |> Enum.map(& &1.id) - - assert length(activities_ids) == 10 - - visible_ids = - Map.values(u1) ++ Map.values(u4) ++ [u2[:r1], u3[:r1]] ++ Map.values(activities) - - assert Enum.all?(visible_ids, &(&1 in activities_ids)) - end - end - - defp public_messages(_) do - [u1, u2, u3, u4] = insert_list(4, :user) - {:ok, u1} = User.follow(u1, u2) - {:ok, u2} = User.follow(u2, u1) - {:ok, u1} = User.follow(u1, u4) - {:ok, u4} = User.follow(u4, u1) - - {:ok, u2} = User.follow(u2, u3) - {:ok, u3} = User.follow(u3, u2) - - {:ok, a1} = CommonAPI.post(u1, %{status: "Status"}) - - {:ok, r1_1} = - CommonAPI.post(u2, %{ - status: "@#{u1.nickname} reply from u2 to u1", - in_reply_to_status_id: a1.id - }) - - {:ok, r1_2} = - CommonAPI.post(u3, %{ - status: "@#{u1.nickname} reply from u3 to u1", - in_reply_to_status_id: a1.id - }) - - {:ok, r1_3} = - CommonAPI.post(u4, %{ - status: "@#{u1.nickname} reply from u4 to u1", - in_reply_to_status_id: a1.id - }) - - {:ok, a2} = CommonAPI.post(u2, %{status: "Status"}) - - {:ok, r2_1} = - CommonAPI.post(u1, %{ - status: "@#{u2.nickname} reply from u1 to u2", - in_reply_to_status_id: a2.id - }) - - {:ok, r2_2} = - CommonAPI.post(u3, %{ - status: "@#{u2.nickname} reply from u3 to u2", - in_reply_to_status_id: a2.id - }) - - {:ok, r2_3} = - CommonAPI.post(u4, %{ - status: "@#{u2.nickname} reply from u4 to u2", - in_reply_to_status_id: a2.id - }) - - {:ok, a3} = CommonAPI.post(u3, %{status: "Status"}) - - {:ok, r3_1} = - CommonAPI.post(u1, %{ - status: "@#{u3.nickname} reply from u1 to u3", - in_reply_to_status_id: a3.id - }) - - {:ok, r3_2} = - CommonAPI.post(u2, %{ - status: "@#{u3.nickname} reply from u2 to u3", - in_reply_to_status_id: a3.id - }) - - {:ok, r3_3} = - CommonAPI.post(u4, %{ - status: "@#{u3.nickname} reply from u4 to u3", - in_reply_to_status_id: a3.id - }) - - {:ok, a4} = CommonAPI.post(u4, %{status: "Status"}) - - {:ok, r4_1} = - CommonAPI.post(u1, %{ - status: "@#{u4.nickname} reply from u1 to u4", - in_reply_to_status_id: a4.id - }) - - {:ok, r4_2} = - CommonAPI.post(u2, %{ - status: "@#{u4.nickname} reply from u2 to u4", - in_reply_to_status_id: a4.id - }) - - {:ok, r4_3} = - CommonAPI.post(u3, %{ - status: "@#{u4.nickname} reply from u3 to u4", - in_reply_to_status_id: a4.id - }) - - {:ok, - users: %{u1: u1, u2: u2, u3: u3, u4: u4}, - activities: %{a1: a1.id, a2: a2.id, a3: a3.id, a4: a4.id}, - u1: %{r1: r1_1.id, r2: r1_2.id, r3: r1_3.id}, - u2: %{r1: r2_1.id, r2: r2_2.id, r3: r2_3.id}, - u3: %{r1: r3_1.id, r2: r3_2.id, r3: r3_3.id}, - u4: %{r1: r4_1.id, r2: r4_2.id, r3: r4_3.id}} - end - - defp private_messages(_) do - [u1, u2, u3, u4] = insert_list(4, :user) - {:ok, u1} = User.follow(u1, u2) - {:ok, u2} = User.follow(u2, u1) - {:ok, u1} = User.follow(u1, u3) - {:ok, u3} = User.follow(u3, u1) - {:ok, u1} = User.follow(u1, u4) - {:ok, u4} = User.follow(u4, u1) - - {:ok, u2} = User.follow(u2, u3) - {:ok, u3} = User.follow(u3, u2) - - {:ok, a1} = CommonAPI.post(u1, %{status: "Status", visibility: "private"}) - - {:ok, r1_1} = - CommonAPI.post(u2, %{ - status: "@#{u1.nickname} reply from u2 to u1", - in_reply_to_status_id: a1.id, - visibility: "private" - }) - - {:ok, r1_2} = - CommonAPI.post(u3, %{ - status: "@#{u1.nickname} reply from u3 to u1", - in_reply_to_status_id: a1.id, - visibility: "private" - }) - - {:ok, r1_3} = - CommonAPI.post(u4, %{ - status: "@#{u1.nickname} reply from u4 to u1", - in_reply_to_status_id: a1.id, - visibility: "private" - }) - - {:ok, a2} = CommonAPI.post(u2, %{status: "Status", visibility: "private"}) - - {:ok, r2_1} = - CommonAPI.post(u1, %{ - status: "@#{u2.nickname} reply from u1 to u2", - in_reply_to_status_id: a2.id, - visibility: "private" - }) - - {:ok, r2_2} = - CommonAPI.post(u3, %{ - status: "@#{u2.nickname} reply from u3 to u2", - in_reply_to_status_id: a2.id, - visibility: "private" - }) - - {:ok, a3} = CommonAPI.post(u3, %{status: "Status", visibility: "private"}) - - {:ok, r3_1} = - CommonAPI.post(u1, %{ - status: "@#{u3.nickname} reply from u1 to u3", - in_reply_to_status_id: a3.id, - visibility: "private" - }) - - {:ok, r3_2} = - CommonAPI.post(u2, %{ - status: "@#{u3.nickname} reply from u2 to u3", - in_reply_to_status_id: a3.id, - visibility: "private" - }) - - {:ok, a4} = CommonAPI.post(u4, %{status: "Status", visibility: "private"}) - - {:ok, r4_1} = - CommonAPI.post(u1, %{ - status: "@#{u4.nickname} reply from u1 to u4", - in_reply_to_status_id: a4.id, - visibility: "private" - }) - - {:ok, - users: %{u1: u1, u2: u2, u3: u3, u4: u4}, - activities: %{a1: a1.id, a2: a2.id, a3: a3.id, a4: a4.id}, - u1: %{r1: r1_1.id, r2: r1_2.id, r3: r1_3.id}, - u2: %{r1: r2_1.id, r2: r2_2.id}, - u3: %{r1: r3_1.id, r2: r3_2.id}, - u4: %{r1: r4_1.id}} - end - - describe "maybe_update_follow_information/1" do - setup do - clear_config([:instance, :external_user_synchronization], true) - - user = %{ - local: false, - ap_id: "https://gensokyo.2hu/users/raymoo", - following_address: "https://gensokyo.2hu/users/following", - follower_address: "https://gensokyo.2hu/users/followers", - type: "Person" - } - - %{user: user} - end - - test "logs an error when it can't fetch the info", %{user: user} do - assert capture_log(fn -> - ActivityPub.maybe_update_follow_information(user) - end) =~ "Follower/Following counter update for #{user.ap_id} failed" - end - - test "just returns the input if the user type is Application", %{ - user: user - } do - user = - user - |> Map.put(:type, "Application") - - refute capture_log(fn -> - assert ^user = ActivityPub.maybe_update_follow_information(user) - end) =~ "Follower/Following counter update for #{user.ap_id} failed" - end - - test "it just returns the input if the user has no following/follower addresses", %{ - user: user - } do - user = - user - |> Map.put(:following_address, nil) - |> Map.put(:follower_address, nil) - - refute capture_log(fn -> - assert ^user = ActivityPub.maybe_update_follow_information(user) - 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 - - describe "handling of clashing nicknames" do - test "renames an existing user with a clashing nickname and a different ap id" do - orig_user = - insert( - :user, - local: false, - nickname: "admin@mastodon.example.org", - ap_id: "http://mastodon.example.org/users/harinezumigari" - ) - - %{ - nickname: orig_user.nickname, - ap_id: orig_user.ap_id <> "part_2" - } - |> ActivityPub.maybe_handle_clashing_nickname() - - user = User.get_by_id(orig_user.id) - - assert user.nickname == "#{orig_user.id}.admin@mastodon.example.org" - end - - test "does nothing with a clashing nickname and the same ap id" do - orig_user = - insert( - :user, - local: false, - nickname: "admin@mastodon.example.org", - ap_id: "http://mastodon.example.org/users/harinezumigari" - ) - - %{ - nickname: orig_user.nickname, - ap_id: orig_user.ap_id - } - |> ActivityPub.maybe_handle_clashing_nickname() - - user = User.get_by_id(orig_user.id) - - assert user.nickname == orig_user.nickname - end - end -end diff --git a/test/web/activity_pub/mrf/activity_expiration_policy_test.exs b/test/web/activity_pub/mrf/activity_expiration_policy_test.exs deleted file mode 100644 index f25cf8b12..000000000 --- a/test/web/activity_pub/mrf/activity_expiration_policy_test.exs +++ /dev/null @@ -1,84 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicyTest do - use ExUnit.Case, async: true - alias Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy - - @id Pleroma.Web.Endpoint.url() <> "/activities/cofe" - @local_actor Pleroma.Web.Endpoint.url() <> "/users/cofe" - - test "adds `expires_at` property" do - assert {:ok, %{"type" => "Create", "expires_at" => expires_at}} = - ActivityExpirationPolicy.filter(%{ - "id" => @id, - "actor" => @local_actor, - "type" => "Create", - "object" => %{"type" => "Note"} - }) - - assert Timex.diff(expires_at, NaiveDateTime.utc_now(), :days) == 364 - 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, - "actor" => @local_actor, - "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, - "actor" => @local_actor, - "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", - "actor" => "https://example.com/users/cofe", - "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", - "actor" => "https://example.com/users/cofe", - "type" => "Follow" - }) - - refute Map.has_key?(activity, "expires_at") - - assert {:ok, activity} = - ActivityExpirationPolicy.filter(%{ - "id" => "https://example.com/123", - "actor" => "https://example.com/users/cofe", - "type" => "Create", - "object" => %{"type" => "Cofe"} - }) - - refute Map.has_key?(activity, "expires_at") - end -end diff --git a/test/web/activity_pub/mrf/anti_followbot_policy_test.exs b/test/web/activity_pub/mrf/anti_followbot_policy_test.exs deleted file mode 100644 index 3c795f5ac..000000000 --- a/test/web/activity_pub/mrf/anti_followbot_policy_test.exs +++ /dev/null @@ -1,72 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicyTest do - use Pleroma.DataCase - import Pleroma.Factory - - alias Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicy - - describe "blocking based on attributes" do - test "matches followbots by nickname" do - actor = insert(:user, %{nickname: "followbot@example.com"}) - target = insert(:user) - - message = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "type" => "Follow", - "actor" => actor.ap_id, - "object" => target.ap_id, - "id" => "https://example.com/activities/1234" - } - - assert {:reject, "[AntiFollowbotPolicy]" <> _} = AntiFollowbotPolicy.filter(message) - end - - test "matches followbots by display name" do - actor = insert(:user, %{name: "Federation Bot"}) - target = insert(:user) - - message = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "type" => "Follow", - "actor" => actor.ap_id, - "object" => target.ap_id, - "id" => "https://example.com/activities/1234" - } - - assert {:reject, "[AntiFollowbotPolicy]" <> _} = AntiFollowbotPolicy.filter(message) - end - end - - test "it allows non-followbots" do - actor = insert(:user) - target = insert(:user) - - message = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "type" => "Follow", - "actor" => actor.ap_id, - "object" => target.ap_id, - "id" => "https://example.com/activities/1234" - } - - {:ok, _} = AntiFollowbotPolicy.filter(message) - end - - test "it gracefully handles nil display names" do - actor = insert(:user, %{name: nil}) - target = insert(:user) - - message = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "type" => "Follow", - "actor" => actor.ap_id, - "object" => target.ap_id, - "id" => "https://example.com/activities/1234" - } - - {:ok, _} = AntiFollowbotPolicy.filter(message) - end -end diff --git a/test/web/activity_pub/mrf/anti_link_spam_policy_test.exs b/test/web/activity_pub/mrf/anti_link_spam_policy_test.exs deleted file mode 100644 index 6867c9853..000000000 --- a/test/web/activity_pub/mrf/anti_link_spam_policy_test.exs +++ /dev/null @@ -1,166 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicyTest do - use Pleroma.DataCase - import Pleroma.Factory - import ExUnit.CaptureLog - - alias Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy - - @linkless_message %{ - "type" => "Create", - "object" => %{ - "content" => "hi world!" - } - } - - @linkful_message %{ - "type" => "Create", - "object" => %{ - "content" => "<a href='https://example.com'>hi world!</a>" - } - } - - @response_message %{ - "type" => "Create", - "object" => %{ - "name" => "yes", - "type" => "Answer" - } - } - - describe "with new user" do - test "it allows posts without links" do - user = insert(:user, local: false) - - assert user.note_count == 0 - - message = - @linkless_message - |> Map.put("actor", user.ap_id) - - {:ok, _message} = AntiLinkSpamPolicy.filter(message) - end - - test "it disallows posts with links" do - user = insert(:user, local: false) - - assert user.note_count == 0 - - message = - @linkful_message - |> Map.put("actor", user.ap_id) - - {: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 - test "it allows posts without links" do - user = insert(:user, note_count: 1) - - assert user.note_count == 1 - - message = - @linkless_message - |> Map.put("actor", user.ap_id) - - {:ok, _message} = AntiLinkSpamPolicy.filter(message) - end - - test "it allows posts with links" do - user = insert(:user, note_count: 1) - - assert user.note_count == 1 - - message = - @linkful_message - |> Map.put("actor", user.ap_id) - - {:ok, _message} = AntiLinkSpamPolicy.filter(message) - end - end - - describe "with followed new user" do - test "it allows posts without links" do - user = insert(:user, follower_count: 1) - - assert user.follower_count == 1 - - message = - @linkless_message - |> Map.put("actor", user.ap_id) - - {:ok, _message} = AntiLinkSpamPolicy.filter(message) - end - - test "it allows posts with links" do - user = insert(:user, follower_count: 1) - - assert user.follower_count == 1 - - message = - @linkful_message - |> Map.put("actor", user.ap_id) - - {:ok, _message} = AntiLinkSpamPolicy.filter(message) - end - end - - describe "with unknown actors" do - setup do - Tesla.Mock.mock(fn - %{method: :get, url: "http://invalid.actor"} -> - %Tesla.Env{status: 500, body: ""} - end) - - :ok - end - - test "it rejects posts without links" do - message = - @linkless_message - |> Map.put("actor", "http://invalid.actor") - - assert capture_log(fn -> - {:reject, _} = AntiLinkSpamPolicy.filter(message) - end) =~ "[error] Could not decode user at fetch http://invalid.actor" - end - - test "it rejects posts with links" do - message = - @linkful_message - |> Map.put("actor", "http://invalid.actor") - - assert capture_log(fn -> - {:reject, _} = AntiLinkSpamPolicy.filter(message) - end) =~ "[error] Could not decode user at fetch http://invalid.actor" - end - end - - describe "with contentless-objects" do - test "it does not reject them or error out" do - user = insert(:user, note_count: 1) - - message = - @response_message - |> Map.put("actor", user.ap_id) - - {:ok, _message} = AntiLinkSpamPolicy.filter(message) - end - end -end diff --git a/test/web/activity_pub/mrf/ensure_re_prepended_test.exs b/test/web/activity_pub/mrf/ensure_re_prepended_test.exs deleted file mode 100644 index 9a283f27d..000000000 --- a/test/web/activity_pub/mrf/ensure_re_prepended_test.exs +++ /dev/null @@ -1,92 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrependedTest do - use Pleroma.DataCase - - alias Pleroma.Activity - alias Pleroma.Object - alias Pleroma.Web.ActivityPub.MRF.EnsureRePrepended - - describe "rewrites summary" do - test "it adds `re:` to summary object when child summary and parent summary equal" do - message = %{ - "type" => "Create", - "object" => %{ - "summary" => "object-summary", - "inReplyTo" => %Activity{object: %Object{data: %{"summary" => "object-summary"}}} - } - } - - assert {:ok, res} = EnsureRePrepended.filter(message) - assert res["object"]["summary"] == "re: object-summary" - end - - test "it adds `re:` to summary object when child summary containts re-subject of parent summary " do - message = %{ - "type" => "Create", - "object" => %{ - "summary" => "object-summary", - "inReplyTo" => %Activity{object: %Object{data: %{"summary" => "re: object-summary"}}} - } - } - - assert {:ok, res} = EnsureRePrepended.filter(message) - assert res["object"]["summary"] == "re: object-summary" - end - end - - describe "skip filter" do - test "it skip if type isn't 'Create'" do - message = %{ - "type" => "Annotation", - "object" => %{"summary" => "object-summary"} - } - - assert {:ok, res} = EnsureRePrepended.filter(message) - assert res == message - end - - test "it skip if summary is empty" do - message = %{ - "type" => "Create", - "object" => %{ - "inReplyTo" => %Activity{object: %Object{data: %{"summary" => "summary"}}} - } - } - - assert {:ok, res} = EnsureRePrepended.filter(message) - assert res == message - end - - test "it skip if inReplyTo is empty" do - message = %{"type" => "Create", "object" => %{"summary" => "summary"}} - assert {:ok, res} = EnsureRePrepended.filter(message) - assert res == message - end - - test "it skip if parent and child summary isn't equal" do - message = %{ - "type" => "Create", - "object" => %{ - "summary" => "object-summary", - "inReplyTo" => %Activity{object: %Object{data: %{"summary" => "summary"}}} - } - } - - assert {:ok, res} = EnsureRePrepended.filter(message) - assert res == message - end - - test "it skips if the object is only a reference" do - message = %{ - "type" => "Create", - "object" => "somereference" - } - - assert {:ok, res} = EnsureRePrepended.filter(message) - assert res == message - end - end -end diff --git a/test/web/activity_pub/mrf/hellthread_policy_test.exs b/test/web/activity_pub/mrf/hellthread_policy_test.exs deleted file mode 100644 index 26f5bcdaa..000000000 --- a/test/web/activity_pub/mrf/hellthread_policy_test.exs +++ /dev/null @@ -1,92 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicyTest do - use Pleroma.DataCase - import Pleroma.Factory - - import Pleroma.Web.ActivityPub.MRF.HellthreadPolicy - - alias Pleroma.Web.CommonAPI - - setup do - user = insert(:user) - - message = %{ - "actor" => user.ap_id, - "cc" => [user.follower_address], - "type" => "Create", - "to" => [ - "https://www.w3.org/ns/activitystreams#Public", - "https://instance.tld/users/user1", - "https://instance.tld/users/user2", - "https://instance.tld/users/user3" - ], - "object" => %{ - "type" => "Note" - } - } - - [user: user, message: message] - end - - setup do: clear_config(:mrf_hellthread) - - test "doesn't die on chat messages" do - Pleroma.Config.put([:mrf_hellthread], %{delist_threshold: 2, reject_threshold: 0}) - - user = insert(:user) - other_user = insert(:user) - - {:ok, activity} = CommonAPI.post_chat_message(user, other_user, "moin") - - assert {:ok, _} = filter(activity.data) - end - - describe "reject" do - test "rejects the message if the recipient count is above reject_threshold", %{ - message: message - } do - Pleroma.Config.put([:mrf_hellthread], %{delist_threshold: 0, reject_threshold: 2}) - - assert {:reject, "[HellthreadPolicy] 3 recipients is over the limit of 2"} == - filter(message) - end - - test "does not reject the message if the recipient count is below reject_threshold", %{ - message: message - } do - Pleroma.Config.put([:mrf_hellthread], %{delist_threshold: 0, reject_threshold: 3}) - - assert {:ok, ^message} = filter(message) - end - end - - describe "delist" do - test "delists the message if the recipient count is above delist_threshold", %{ - user: user, - message: message - } do - Pleroma.Config.put([:mrf_hellthread], %{delist_threshold: 2, reject_threshold: 0}) - - {:ok, message} = filter(message) - assert user.follower_address in message["to"] - assert "https://www.w3.org/ns/activitystreams#Public" in message["cc"] - end - - test "does not delist the message if the recipient count is below delist_threshold", %{ - message: message - } do - Pleroma.Config.put([:mrf_hellthread], %{delist_threshold: 4, reject_threshold: 0}) - - assert {:ok, ^message} = filter(message) - end - end - - test "excludes follower collection and public URI from threshold count", %{message: message} do - Pleroma.Config.put([:mrf_hellthread], %{delist_threshold: 0, reject_threshold: 3}) - - assert {:ok, ^message} = filter(message) - end -end diff --git a/test/web/activity_pub/mrf/keyword_policy_test.exs b/test/web/activity_pub/mrf/keyword_policy_test.exs deleted file mode 100644 index b3d0f3d90..000000000 --- a/test/web/activity_pub/mrf/keyword_policy_test.exs +++ /dev/null @@ -1,225 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicyTest do - use Pleroma.DataCase - - alias Pleroma.Web.ActivityPub.MRF.KeywordPolicy - - setup do: clear_config(:mrf_keyword) - - setup do - Pleroma.Config.put([:mrf_keyword], %{reject: [], federated_timeline_removal: [], replace: []}) - end - - describe "rejecting based on keywords" do - test "rejects if string matches in content" do - Pleroma.Config.put([:mrf_keyword, :reject], ["pun"]) - - message = %{ - "type" => "Create", - "object" => %{ - "content" => "just a daily reminder that compLAINer is a good pun", - "summary" => "" - } - } - - assert {:reject, "[KeywordPolicy] Matches with rejected keyword"} = - KeywordPolicy.filter(message) - end - - test "rejects if string matches in summary" do - Pleroma.Config.put([:mrf_keyword, :reject], ["pun"]) - - message = %{ - "type" => "Create", - "object" => %{ - "summary" => "just a daily reminder that compLAINer is a good pun", - "content" => "" - } - } - - assert {:reject, "[KeywordPolicy] Matches with rejected keyword"} = - KeywordPolicy.filter(message) - end - - test "rejects if regex matches in content" do - Pleroma.Config.put([:mrf_keyword, :reject], [~r/comp[lL][aA][iI][nN]er/]) - - assert true == - Enum.all?(["complainer", "compLainer", "compLAiNer", "compLAINer"], fn content -> - message = %{ - "type" => "Create", - "object" => %{ - "content" => "just a daily reminder that #{content} is a good pun", - "summary" => "" - } - } - - {:reject, "[KeywordPolicy] Matches with rejected keyword"} == - KeywordPolicy.filter(message) - end) - end - - test "rejects if regex matches in summary" do - Pleroma.Config.put([:mrf_keyword, :reject], [~r/comp[lL][aA][iI][nN]er/]) - - assert true == - Enum.all?(["complainer", "compLainer", "compLAiNer", "compLAINer"], fn content -> - message = %{ - "type" => "Create", - "object" => %{ - "summary" => "just a daily reminder that #{content} is a good pun", - "content" => "" - } - } - - {:reject, "[KeywordPolicy] Matches with rejected keyword"} == - KeywordPolicy.filter(message) - end) - end - end - - describe "delisting from ftl based on keywords" do - test "delists if string matches in content" do - Pleroma.Config.put([:mrf_keyword, :federated_timeline_removal], ["pun"]) - - message = %{ - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "type" => "Create", - "object" => %{ - "content" => "just a daily reminder that compLAINer is a good pun", - "summary" => "" - } - } - - {:ok, result} = KeywordPolicy.filter(message) - assert ["https://www.w3.org/ns/activitystreams#Public"] == result["cc"] - refute ["https://www.w3.org/ns/activitystreams#Public"] == result["to"] - end - - test "delists if string matches in summary" do - Pleroma.Config.put([:mrf_keyword, :federated_timeline_removal], ["pun"]) - - message = %{ - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "type" => "Create", - "object" => %{ - "summary" => "just a daily reminder that compLAINer is a good pun", - "content" => "" - } - } - - {:ok, result} = KeywordPolicy.filter(message) - assert ["https://www.w3.org/ns/activitystreams#Public"] == result["cc"] - refute ["https://www.w3.org/ns/activitystreams#Public"] == result["to"] - end - - test "delists if regex matches in content" do - Pleroma.Config.put([:mrf_keyword, :federated_timeline_removal], [~r/comp[lL][aA][iI][nN]er/]) - - assert true == - Enum.all?(["complainer", "compLainer", "compLAiNer", "compLAINer"], fn content -> - message = %{ - "type" => "Create", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "object" => %{ - "content" => "just a daily reminder that #{content} is a good pun", - "summary" => "" - } - } - - {:ok, result} = KeywordPolicy.filter(message) - - ["https://www.w3.org/ns/activitystreams#Public"] == result["cc"] and - not (["https://www.w3.org/ns/activitystreams#Public"] == result["to"]) - end) - end - - test "delists if regex matches in summary" do - Pleroma.Config.put([:mrf_keyword, :federated_timeline_removal], [~r/comp[lL][aA][iI][nN]er/]) - - assert true == - Enum.all?(["complainer", "compLainer", "compLAiNer", "compLAINer"], fn content -> - message = %{ - "type" => "Create", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "object" => %{ - "summary" => "just a daily reminder that #{content} is a good pun", - "content" => "" - } - } - - {:ok, result} = KeywordPolicy.filter(message) - - ["https://www.w3.org/ns/activitystreams#Public"] == result["cc"] and - not (["https://www.w3.org/ns/activitystreams#Public"] == result["to"]) - end) - end - end - - describe "replacing keywords" do - test "replaces keyword if string matches in content" do - Pleroma.Config.put([:mrf_keyword, :replace], [{"opensource", "free software"}]) - - message = %{ - "type" => "Create", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "object" => %{"content" => "ZFS is opensource", "summary" => ""} - } - - {:ok, %{"object" => %{"content" => result}}} = KeywordPolicy.filter(message) - assert result == "ZFS is free software" - end - - test "replaces keyword if string matches in summary" do - Pleroma.Config.put([:mrf_keyword, :replace], [{"opensource", "free software"}]) - - message = %{ - "type" => "Create", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "object" => %{"summary" => "ZFS is opensource", "content" => ""} - } - - {:ok, %{"object" => %{"summary" => result}}} = KeywordPolicy.filter(message) - assert result == "ZFS is free software" - end - - test "replaces keyword if regex matches in content" do - Pleroma.Config.put([:mrf_keyword, :replace], [ - {~r/open(-|\s)?source\s?(software)?/, "free software"} - ]) - - assert true == - Enum.all?(["opensource", "open-source", "open source"], fn content -> - message = %{ - "type" => "Create", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "object" => %{"content" => "ZFS is #{content}", "summary" => ""} - } - - {:ok, %{"object" => %{"content" => result}}} = KeywordPolicy.filter(message) - result == "ZFS is free software" - end) - end - - test "replaces keyword if regex matches in summary" do - Pleroma.Config.put([:mrf_keyword, :replace], [ - {~r/open(-|\s)?source\s?(software)?/, "free software"} - ]) - - assert true == - Enum.all?(["opensource", "open-source", "open source"], fn content -> - message = %{ - "type" => "Create", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "object" => %{"summary" => "ZFS is #{content}", "content" => ""} - } - - {:ok, %{"object" => %{"summary" => result}}} = KeywordPolicy.filter(message) - result == "ZFS is free software" - end) - end - end -end diff --git a/test/web/activity_pub/mrf/mediaproxy_warming_policy_test.exs b/test/web/activity_pub/mrf/mediaproxy_warming_policy_test.exs deleted file mode 100644 index 313d59a66..000000000 --- a/test/web/activity_pub/mrf/mediaproxy_warming_policy_test.exs +++ /dev/null @@ -1,51 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicyTest do - use Pleroma.DataCase - - alias Pleroma.HTTP - alias Pleroma.Tests.ObanHelpers - alias Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy - - import Mock - - @message %{ - "type" => "Create", - "object" => %{ - "type" => "Note", - "content" => "content", - "attachment" => [ - %{"url" => [%{"href" => "http://example.com/image.jpg"}]} - ] - } - } - - test "it prefetches media proxy URIs" do - with_mock HTTP, get: fn _, _, _ -> {:ok, []} end do - MediaProxyWarmingPolicy.filter(@message) - - ObanHelpers.perform_all() - # Performing jobs which has been just enqueued - ObanHelpers.perform_all() - - assert called(HTTP.get(:_, :_, :_)) - end - end - - test "it does nothing when no attachments are present" do - object = - @message["object"] - |> Map.delete("attachment") - - message = - @message - |> Map.put("object", object) - - with_mock HTTP, get: fn _, _, _ -> {:ok, []} end do - MediaProxyWarmingPolicy.filter(message) - refute called(HTTP.get(:_, :_, :_)) - end - end -end diff --git a/test/web/activity_pub/mrf/mention_policy_test.exs b/test/web/activity_pub/mrf/mention_policy_test.exs deleted file mode 100644 index 220309cc9..000000000 --- a/test/web/activity_pub/mrf/mention_policy_test.exs +++ /dev/null @@ -1,96 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.MRF.MentionPolicyTest do - use Pleroma.DataCase - - alias Pleroma.Web.ActivityPub.MRF.MentionPolicy - - setup do: clear_config(:mrf_mention) - - test "pass filter if allow list is empty" do - Pleroma.Config.delete([:mrf_mention]) - - message = %{ - "type" => "Create", - "to" => ["https://example.com/ok"], - "cc" => ["https://example.com/blocked"] - } - - assert MentionPolicy.filter(message) == {:ok, message} - end - - describe "allow" do - test "empty" do - Pleroma.Config.put([:mrf_mention], %{actors: ["https://example.com/blocked"]}) - - message = %{ - "type" => "Create" - } - - assert MentionPolicy.filter(message) == {:ok, message} - end - - test "to" do - Pleroma.Config.put([:mrf_mention], %{actors: ["https://example.com/blocked"]}) - - message = %{ - "type" => "Create", - "to" => ["https://example.com/ok"] - } - - assert MentionPolicy.filter(message) == {:ok, message} - end - - test "cc" do - Pleroma.Config.put([:mrf_mention], %{actors: ["https://example.com/blocked"]}) - - message = %{ - "type" => "Create", - "cc" => ["https://example.com/ok"] - } - - assert MentionPolicy.filter(message) == {:ok, message} - end - - test "both" do - Pleroma.Config.put([:mrf_mention], %{actors: ["https://example.com/blocked"]}) - - message = %{ - "type" => "Create", - "to" => ["https://example.com/ok"], - "cc" => ["https://example.com/ok2"] - } - - assert MentionPolicy.filter(message) == {:ok, message} - end - end - - describe "deny" do - test "to" do - Pleroma.Config.put([:mrf_mention], %{actors: ["https://example.com/blocked"]}) - - message = %{ - "type" => "Create", - "to" => ["https://example.com/blocked"] - } - - assert MentionPolicy.filter(message) == - {:reject, "[MentionPolicy] Rejected for mention of https://example.com/blocked"} - end - - test "cc" do - Pleroma.Config.put([:mrf_mention], %{actors: ["https://example.com/blocked"]}) - - message = %{ - "type" => "Create", - "to" => ["https://example.com/ok"], - "cc" => ["https://example.com/blocked"] - } - - assert MentionPolicy.filter(message) == - {:reject, "[MentionPolicy] Rejected for mention of https://example.com/blocked"} - end - end -end diff --git a/test/web/activity_pub/mrf/mrf_test.exs b/test/web/activity_pub/mrf/mrf_test.exs deleted file mode 100644 index a63b25423..000000000 --- a/test/web/activity_pub/mrf/mrf_test.exs +++ /dev/null @@ -1,84 +0,0 @@ -defmodule Pleroma.Web.ActivityPub.MRFTest do - use ExUnit.Case, async: true - use Pleroma.Tests.Helpers - alias Pleroma.Web.ActivityPub.MRF - - test "subdomains_regex/1" do - assert MRF.subdomains_regex(["unsafe.tld", "*.unsafe.tld"]) == [ - ~r/^unsafe.tld$/i, - ~r/^(.*\.)*unsafe.tld$/i - ] - end - - describe "subdomain_match/2" do - test "common domains" do - regexes = MRF.subdomains_regex(["unsafe.tld", "unsafe2.tld"]) - - assert regexes == [~r/^unsafe.tld$/i, ~r/^unsafe2.tld$/i] - - assert MRF.subdomain_match?(regexes, "unsafe.tld") - assert MRF.subdomain_match?(regexes, "unsafe2.tld") - - refute MRF.subdomain_match?(regexes, "example.com") - end - - test "wildcard domains with one subdomain" do - regexes = MRF.subdomains_regex(["*.unsafe.tld"]) - - assert regexes == [~r/^(.*\.)*unsafe.tld$/i] - - assert MRF.subdomain_match?(regexes, "unsafe.tld") - assert MRF.subdomain_match?(regexes, "sub.unsafe.tld") - refute MRF.subdomain_match?(regexes, "anotherunsafe.tld") - refute MRF.subdomain_match?(regexes, "unsafe.tldanother") - end - - test "wildcard domains with two subdomains" do - regexes = MRF.subdomains_regex(["*.unsafe.tld"]) - - assert regexes == [~r/^(.*\.)*unsafe.tld$/i] - - assert MRF.subdomain_match?(regexes, "unsafe.tld") - assert MRF.subdomain_match?(regexes, "sub.sub.unsafe.tld") - refute MRF.subdomain_match?(regexes, "sub.anotherunsafe.tld") - refute MRF.subdomain_match?(regexes, "sub.unsafe.tldanother") - end - - test "matches are case-insensitive" do - regexes = MRF.subdomains_regex(["UnSafe.TLD", "UnSAFE2.Tld"]) - - assert regexes == [~r/^UnSafe.TLD$/i, ~r/^UnSAFE2.Tld$/i] - - assert MRF.subdomain_match?(regexes, "UNSAFE.TLD") - assert MRF.subdomain_match?(regexes, "UNSAFE2.TLD") - assert MRF.subdomain_match?(regexes, "unsafe.tld") - assert MRF.subdomain_match?(regexes, "unsafe2.tld") - - refute MRF.subdomain_match?(regexes, "EXAMPLE.COM") - refute MRF.subdomain_match?(regexes, "example.com") - end - end - - describe "describe/0" do - test "it works as expected with noop policy" do - expected = %{ - mrf_policies: ["NoOpPolicy"], - exclusions: false - } - - {:ok, ^expected} = MRF.describe() - end - - test "it works as expected with mock policy" do - clear_config([:mrf, :policies], [MRFModuleMock]) - - expected = %{ - mrf_policies: ["MRFModuleMock"], - mrf_module_mock: "some config data", - exclusions: false - } - - {:ok, ^expected} = MRF.describe() - end - end -end diff --git a/test/web/activity_pub/mrf/no_placeholder_text_policy_test.exs b/test/web/activity_pub/mrf/no_placeholder_text_policy_test.exs deleted file mode 100644 index 64ea61dd4..000000000 --- a/test/web/activity_pub/mrf/no_placeholder_text_policy_test.exs +++ /dev/null @@ -1,37 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.MRF.NoPlaceholderTextPolicyTest do - use Pleroma.DataCase - alias Pleroma.Web.ActivityPub.MRF.NoPlaceholderTextPolicy - - test "it clears content object" do - message = %{ - "type" => "Create", - "object" => %{"content" => ".", "attachment" => "image"} - } - - assert {:ok, res} = NoPlaceholderTextPolicy.filter(message) - assert res["object"]["content"] == "" - - message = put_in(message, ["object", "content"], "<p>.</p>") - assert {:ok, res} = NoPlaceholderTextPolicy.filter(message) - assert res["object"]["content"] == "" - end - - @messages [ - %{ - "type" => "Create", - "object" => %{"content" => "test", "attachment" => "image"} - }, - %{"type" => "Create", "object" => %{"content" => "."}}, - %{"type" => "Create", "object" => %{"content" => "<p>.</p>"}} - ] - test "it skips filter" do - Enum.each(@messages, fn message -> - assert {:ok, res} = NoPlaceholderTextPolicy.filter(message) - assert res == message - end) - end -end diff --git a/test/web/activity_pub/mrf/normalize_markup_test.exs b/test/web/activity_pub/mrf/normalize_markup_test.exs deleted file mode 100644 index 9b39c45bd..000000000 --- a/test/web/activity_pub/mrf/normalize_markup_test.exs +++ /dev/null @@ -1,42 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.MRF.NormalizeMarkupTest do - use Pleroma.DataCase - alias Pleroma.Web.ActivityPub.MRF.NormalizeMarkup - - @html_sample """ - <b>this is in bold</b> - <p>this is a paragraph</p> - this is a linebreak<br /> - this is a link with allowed "rel" attribute: <a href="http://example.com/" rel="tag">example.com</a> - this is a link with not allowed "rel" attribute: <a href="http://example.com/" rel="tag noallowed">example.com</a> - this is an image: <img src="http://example.com/image.jpg"><br /> - <script>alert('hacked')</script> - """ - - test "it filter html tags" do - expected = """ - <b>this is in bold</b> - <p>this is a paragraph</p> - this is a linebreak<br/> - this is a link with allowed "rel" attribute: <a href="http://example.com/" rel="tag">example.com</a> - this is a link with not allowed "rel" attribute: <a href="http://example.com/">example.com</a> - this is an image: <img src="http://example.com/image.jpg"/><br/> - alert('hacked') - """ - - message = %{"type" => "Create", "object" => %{"content" => @html_sample}} - - assert {:ok, res} = NormalizeMarkup.filter(message) - assert res["object"]["content"] == expected - end - - test "it skips filter if type isn't `Create`" do - message = %{"type" => "Note", "object" => %{}} - - assert {:ok, res} = NormalizeMarkup.filter(message) - assert res == message - end -end diff --git a/test/web/activity_pub/mrf/object_age_policy_test.exs b/test/web/activity_pub/mrf/object_age_policy_test.exs deleted file mode 100644 index cf6acc9a2..000000000 --- a/test/web/activity_pub/mrf/object_age_policy_test.exs +++ /dev/null @@ -1,148 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicyTest do - use Pleroma.DataCase - alias Pleroma.Config - alias Pleroma.User - alias Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy - alias Pleroma.Web.ActivityPub.Visibility - - setup do: - clear_config(:mrf_object_age, - threshold: 172_800, - actions: [:delist, :strip_followers] - ) - - setup_all do - Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - - defp get_old_message do - File.read!("test/fixtures/mastodon-post-activity.json") - |> Poison.decode!() - end - - defp get_new_message do - old_message = get_old_message() - - new_object = - old_message - |> Map.get("object") - |> Map.put("published", DateTime.utc_now() |> DateTime.to_iso8601()) - - old_message - |> Map.put("object", new_object) - end - - describe "with reject action" do - test "works with objects with empty to or cc fields" do - Config.put([:mrf_object_age, :actions], [:reject]) - - data = - get_old_message() - |> Map.put("cc", nil) - |> Map.put("to", nil) - - assert match?({:reject, _}, ObjectAgePolicy.filter(data)) - end - - test "it rejects an old post" do - Config.put([:mrf_object_age, :actions], [:reject]) - - data = get_old_message() - - assert match?({:reject, _}, ObjectAgePolicy.filter(data)) - end - - test "it allows a new post" do - Config.put([:mrf_object_age, :actions], [:reject]) - - data = get_new_message() - - assert match?({:ok, _}, ObjectAgePolicy.filter(data)) - end - end - - describe "with delist action" do - test "works with objects with empty to or cc fields" do - Config.put([:mrf_object_age, :actions], [:delist]) - - data = - get_old_message() - |> Map.put("cc", nil) - |> Map.put("to", nil) - - {:ok, _u} = User.get_or_fetch_by_ap_id(data["actor"]) - - {:ok, data} = ObjectAgePolicy.filter(data) - - assert Visibility.get_visibility(%{data: data}) == "unlisted" - end - - test "it delists an old post" do - Config.put([:mrf_object_age, :actions], [:delist]) - - data = get_old_message() - - {:ok, _u} = User.get_or_fetch_by_ap_id(data["actor"]) - - {:ok, data} = ObjectAgePolicy.filter(data) - - assert Visibility.get_visibility(%{data: data}) == "unlisted" - end - - test "it allows a new post" do - Config.put([:mrf_object_age, :actions], [:delist]) - - data = get_new_message() - - {:ok, _user} = User.get_or_fetch_by_ap_id(data["actor"]) - - assert match?({:ok, ^data}, ObjectAgePolicy.filter(data)) - end - end - - describe "with strip_followers action" do - test "works with objects with empty to or cc fields" do - Config.put([:mrf_object_age, :actions], [:strip_followers]) - - data = - get_old_message() - |> Map.put("cc", nil) - |> Map.put("to", nil) - - {:ok, user} = User.get_or_fetch_by_ap_id(data["actor"]) - - {:ok, data} = ObjectAgePolicy.filter(data) - - refute user.follower_address in data["to"] - refute user.follower_address in data["cc"] - end - - test "it strips followers collections from an old post" do - Config.put([:mrf_object_age, :actions], [:strip_followers]) - - data = get_old_message() - - {:ok, user} = User.get_or_fetch_by_ap_id(data["actor"]) - - {:ok, data} = ObjectAgePolicy.filter(data) - - refute user.follower_address in data["to"] - refute user.follower_address in data["cc"] - end - - test "it allows a new post" do - Config.put([:mrf_object_age, :actions], [:strip_followers]) - - data = get_new_message() - - {:ok, _u} = User.get_or_fetch_by_ap_id(data["actor"]) - - assert match?({:ok, ^data}, ObjectAgePolicy.filter(data)) - end - end -end diff --git a/test/web/activity_pub/mrf/reject_non_public_test.exs b/test/web/activity_pub/mrf/reject_non_public_test.exs deleted file mode 100644 index 58b46b9a2..000000000 --- a/test/web/activity_pub/mrf/reject_non_public_test.exs +++ /dev/null @@ -1,100 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublicTest do - use Pleroma.DataCase - import Pleroma.Factory - - alias Pleroma.Web.ActivityPub.MRF.RejectNonPublic - - setup do: clear_config([:mrf_rejectnonpublic]) - - describe "public message" do - test "it's allowed when address is public" do - actor = insert(:user, follower_address: "test-address") - - message = %{ - "actor" => actor.ap_id, - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => ["https://www.w3.org/ns/activitystreams#Publid"], - "type" => "Create" - } - - assert {:ok, message} = RejectNonPublic.filter(message) - end - - test "it's allowed when cc address contain public address" do - actor = insert(:user, follower_address: "test-address") - - message = %{ - "actor" => actor.ap_id, - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => ["https://www.w3.org/ns/activitystreams#Publid"], - "type" => "Create" - } - - assert {:ok, message} = RejectNonPublic.filter(message) - end - end - - describe "followers message" do - test "it's allowed when addrer of message in the follower addresses of user and it enabled in config" do - actor = insert(:user, follower_address: "test-address") - - message = %{ - "actor" => actor.ap_id, - "to" => ["test-address"], - "cc" => ["https://www.w3.org/ns/activitystreams#Publid"], - "type" => "Create" - } - - Pleroma.Config.put([:mrf_rejectnonpublic, :allow_followersonly], true) - assert {:ok, message} = RejectNonPublic.filter(message) - end - - test "it's rejected when addrer of message in the follower addresses of user and it disabled in config" do - actor = insert(:user, follower_address: "test-address") - - message = %{ - "actor" => actor.ap_id, - "to" => ["test-address"], - "cc" => ["https://www.w3.org/ns/activitystreams#Publid"], - "type" => "Create" - } - - Pleroma.Config.put([:mrf_rejectnonpublic, :allow_followersonly], false) - assert {:reject, _} = RejectNonPublic.filter(message) - end - end - - describe "direct message" do - test "it's allows when direct messages are allow" do - actor = insert(:user) - - message = %{ - "actor" => actor.ap_id, - "to" => ["https://www.w3.org/ns/activitystreams#Publid"], - "cc" => ["https://www.w3.org/ns/activitystreams#Publid"], - "type" => "Create" - } - - Pleroma.Config.put([:mrf_rejectnonpublic, :allow_direct], true) - assert {:ok, message} = RejectNonPublic.filter(message) - end - - test "it's reject when direct messages aren't allow" do - actor = insert(:user) - - message = %{ - "actor" => actor.ap_id, - "to" => ["https://www.w3.org/ns/activitystreams#Publid~~~"], - "cc" => ["https://www.w3.org/ns/activitystreams#Publid"], - "type" => "Create" - } - - Pleroma.Config.put([:mrf_rejectnonpublic, :allow_direct], false) - assert {:reject, _} = RejectNonPublic.filter(message) - end - end -end diff --git a/test/web/activity_pub/mrf/simple_policy_test.exs b/test/web/activity_pub/mrf/simple_policy_test.exs deleted file mode 100644 index d7dde62c4..000000000 --- a/test/web/activity_pub/mrf/simple_policy_test.exs +++ /dev/null @@ -1,539 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do - use Pleroma.DataCase - import Pleroma.Factory - alias Pleroma.Config - alias Pleroma.Web.ActivityPub.MRF.SimplePolicy - alias Pleroma.Web.CommonAPI - - setup do: - clear_config(:mrf_simple, - media_removal: [], - media_nsfw: [], - federated_timeline_removal: [], - report_removal: [], - reject: [], - followers_only: [], - accept: [], - avatar_removal: [], - banner_removal: [], - reject_deletes: [] - ) - - describe "when :media_removal" do - test "is empty" do - Config.put([:mrf_simple, :media_removal], []) - media_message = build_media_message() - local_message = build_local_message() - - assert SimplePolicy.filter(media_message) == {:ok, media_message} - assert SimplePolicy.filter(local_message) == {:ok, local_message} - end - - test "has a matching host" do - Config.put([:mrf_simple, :media_removal], ["remote.instance"]) - media_message = build_media_message() - local_message = build_local_message() - - assert SimplePolicy.filter(media_message) == - {:ok, - media_message - |> Map.put("object", Map.delete(media_message["object"], "attachment"))} - - assert SimplePolicy.filter(local_message) == {:ok, local_message} - end - - test "match with wildcard domain" do - Config.put([:mrf_simple, :media_removal], ["*.remote.instance"]) - media_message = build_media_message() - local_message = build_local_message() - - assert SimplePolicy.filter(media_message) == - {:ok, - media_message - |> Map.put("object", Map.delete(media_message["object"], "attachment"))} - - assert SimplePolicy.filter(local_message) == {:ok, local_message} - end - end - - describe "when :media_nsfw" do - test "is empty" do - Config.put([:mrf_simple, :media_nsfw], []) - media_message = build_media_message() - local_message = build_local_message() - - assert SimplePolicy.filter(media_message) == {:ok, media_message} - assert SimplePolicy.filter(local_message) == {:ok, local_message} - end - - test "has a matching host" do - Config.put([:mrf_simple, :media_nsfw], ["remote.instance"]) - media_message = build_media_message() - local_message = build_local_message() - - assert SimplePolicy.filter(media_message) == - {:ok, - media_message - |> put_in(["object", "tag"], ["foo", "nsfw"]) - |> put_in(["object", "sensitive"], true)} - - assert SimplePolicy.filter(local_message) == {:ok, local_message} - end - - test "match with wildcard domain" do - Config.put([:mrf_simple, :media_nsfw], ["*.remote.instance"]) - media_message = build_media_message() - local_message = build_local_message() - - assert SimplePolicy.filter(media_message) == - {:ok, - media_message - |> put_in(["object", "tag"], ["foo", "nsfw"]) - |> put_in(["object", "sensitive"], true)} - - assert SimplePolicy.filter(local_message) == {:ok, local_message} - end - end - - defp build_media_message do - %{ - "actor" => "https://remote.instance/users/bob", - "type" => "Create", - "object" => %{ - "attachment" => [%{}], - "tag" => ["foo"], - "sensitive" => false - } - } - end - - describe "when :report_removal" do - test "is empty" do - Config.put([:mrf_simple, :report_removal], []) - report_message = build_report_message() - local_message = build_local_message() - - assert SimplePolicy.filter(report_message) == {:ok, report_message} - assert SimplePolicy.filter(local_message) == {:ok, local_message} - end - - test "has a matching host" do - Config.put([:mrf_simple, :report_removal], ["remote.instance"]) - report_message = build_report_message() - local_message = build_local_message() - - assert {:reject, _} = SimplePolicy.filter(report_message) - assert SimplePolicy.filter(local_message) == {:ok, local_message} - end - - test "match with wildcard domain" do - Config.put([:mrf_simple, :report_removal], ["*.remote.instance"]) - report_message = build_report_message() - local_message = build_local_message() - - assert {:reject, _} = SimplePolicy.filter(report_message) - assert SimplePolicy.filter(local_message) == {:ok, local_message} - end - end - - defp build_report_message do - %{ - "actor" => "https://remote.instance/users/bob", - "type" => "Flag" - } - end - - describe "when :federated_timeline_removal" do - test "is empty" do - Config.put([:mrf_simple, :federated_timeline_removal], []) - {_, ftl_message} = build_ftl_actor_and_message() - local_message = build_local_message() - - assert SimplePolicy.filter(ftl_message) == {:ok, ftl_message} - assert SimplePolicy.filter(local_message) == {:ok, local_message} - end - - test "has a matching host" do - {actor, ftl_message} = build_ftl_actor_and_message() - - ftl_message_actor_host = - ftl_message - |> Map.fetch!("actor") - |> URI.parse() - |> Map.fetch!(:host) - - Config.put([:mrf_simple, :federated_timeline_removal], [ftl_message_actor_host]) - local_message = build_local_message() - - assert {:ok, ftl_message} = SimplePolicy.filter(ftl_message) - assert actor.follower_address in ftl_message["to"] - refute actor.follower_address in ftl_message["cc"] - refute "https://www.w3.org/ns/activitystreams#Public" in ftl_message["to"] - assert "https://www.w3.org/ns/activitystreams#Public" in ftl_message["cc"] - - assert SimplePolicy.filter(local_message) == {:ok, local_message} - end - - test "match with wildcard domain" do - {actor, ftl_message} = build_ftl_actor_and_message() - - ftl_message_actor_host = - ftl_message - |> Map.fetch!("actor") - |> URI.parse() - |> Map.fetch!(:host) - - Config.put([:mrf_simple, :federated_timeline_removal], ["*." <> ftl_message_actor_host]) - local_message = build_local_message() - - assert {:ok, ftl_message} = SimplePolicy.filter(ftl_message) - assert actor.follower_address in ftl_message["to"] - refute actor.follower_address in ftl_message["cc"] - refute "https://www.w3.org/ns/activitystreams#Public" in ftl_message["to"] - assert "https://www.w3.org/ns/activitystreams#Public" in ftl_message["cc"] - - assert SimplePolicy.filter(local_message) == {:ok, local_message} - end - - test "has a matching host but only as:Public in to" do - {_actor, ftl_message} = build_ftl_actor_and_message() - - ftl_message_actor_host = - ftl_message - |> Map.fetch!("actor") - |> URI.parse() - |> Map.fetch!(:host) - - ftl_message = Map.put(ftl_message, "cc", []) - - Config.put([:mrf_simple, :federated_timeline_removal], [ftl_message_actor_host]) - - assert {:ok, ftl_message} = SimplePolicy.filter(ftl_message) - refute "https://www.w3.org/ns/activitystreams#Public" in ftl_message["to"] - assert "https://www.w3.org/ns/activitystreams#Public" in ftl_message["cc"] - end - end - - defp build_ftl_actor_and_message do - actor = insert(:user) - - {actor, - %{ - "actor" => actor.ap_id, - "to" => ["https://www.w3.org/ns/activitystreams#Public", "http://foo.bar/baz"], - "cc" => [actor.follower_address, "http://foo.bar/qux"] - }} - end - - describe "when :reject" do - test "is empty" do - Config.put([:mrf_simple, :reject], []) - - remote_message = build_remote_message() - - assert SimplePolicy.filter(remote_message) == {:ok, remote_message} - end - - test "activity has a matching host" do - Config.put([:mrf_simple, :reject], ["remote.instance"]) - - remote_message = build_remote_message() - - assert {:reject, _} = SimplePolicy.filter(remote_message) - end - - test "activity matches with wildcard domain" do - Config.put([:mrf_simple, :reject], ["*.remote.instance"]) - - remote_message = build_remote_message() - - assert {:reject, _} = SimplePolicy.filter(remote_message) - end - - test "actor has a matching host" do - Config.put([:mrf_simple, :reject], ["remote.instance"]) - - remote_user = build_remote_user() - - assert {:reject, _} = SimplePolicy.filter(remote_user) - end - end - - describe "when :followers_only" do - test "is empty" do - Config.put([:mrf_simple, :followers_only], []) - {_, ftl_message} = build_ftl_actor_and_message() - local_message = build_local_message() - - assert SimplePolicy.filter(ftl_message) == {:ok, ftl_message} - assert SimplePolicy.filter(local_message) == {:ok, local_message} - end - - test "has a matching host" do - actor = insert(:user) - following_user = insert(:user) - non_following_user = insert(:user) - - {:ok, _, _, _} = CommonAPI.follow(following_user, actor) - - activity = %{ - "actor" => actor.ap_id, - "to" => [ - "https://www.w3.org/ns/activitystreams#Public", - following_user.ap_id, - non_following_user.ap_id - ], - "cc" => [actor.follower_address, "http://foo.bar/qux"] - } - - dm_activity = %{ - "actor" => actor.ap_id, - "to" => [ - following_user.ap_id, - non_following_user.ap_id - ], - "cc" => [] - } - - actor_domain = - activity - |> Map.fetch!("actor") - |> URI.parse() - |> Map.fetch!(:host) - - Config.put([:mrf_simple, :followers_only], [actor_domain]) - - assert {:ok, new_activity} = SimplePolicy.filter(activity) - assert actor.follower_address in new_activity["cc"] - assert following_user.ap_id in new_activity["to"] - refute "https://www.w3.org/ns/activitystreams#Public" in new_activity["to"] - refute "https://www.w3.org/ns/activitystreams#Public" in new_activity["cc"] - refute non_following_user.ap_id in new_activity["to"] - refute non_following_user.ap_id in new_activity["cc"] - - assert {:ok, new_dm_activity} = SimplePolicy.filter(dm_activity) - assert new_dm_activity["to"] == [following_user.ap_id] - assert new_dm_activity["cc"] == [] - end - end - - describe "when :accept" do - test "is empty" do - Config.put([:mrf_simple, :accept], []) - - local_message = build_local_message() - remote_message = build_remote_message() - - assert SimplePolicy.filter(local_message) == {:ok, local_message} - assert SimplePolicy.filter(remote_message) == {:ok, remote_message} - end - - test "is not empty but activity doesn't have a matching host" do - Config.put([:mrf_simple, :accept], ["non.matching.remote"]) - - local_message = build_local_message() - remote_message = build_remote_message() - - assert SimplePolicy.filter(local_message) == {:ok, local_message} - assert {:reject, _} = SimplePolicy.filter(remote_message) - end - - test "activity has a matching host" do - Config.put([:mrf_simple, :accept], ["remote.instance"]) - - local_message = build_local_message() - remote_message = build_remote_message() - - assert SimplePolicy.filter(local_message) == {:ok, local_message} - assert SimplePolicy.filter(remote_message) == {:ok, remote_message} - end - - test "activity matches with wildcard domain" do - Config.put([:mrf_simple, :accept], ["*.remote.instance"]) - - local_message = build_local_message() - remote_message = build_remote_message() - - assert SimplePolicy.filter(local_message) == {:ok, local_message} - assert SimplePolicy.filter(remote_message) == {:ok, remote_message} - end - - test "actor has a matching host" do - Config.put([:mrf_simple, :accept], ["remote.instance"]) - - remote_user = build_remote_user() - - assert SimplePolicy.filter(remote_user) == {:ok, remote_user} - end - end - - describe "when :avatar_removal" do - test "is empty" do - Config.put([:mrf_simple, :avatar_removal], []) - - remote_user = build_remote_user() - - assert SimplePolicy.filter(remote_user) == {:ok, remote_user} - end - - test "is not empty but it doesn't have a matching host" do - Config.put([:mrf_simple, :avatar_removal], ["non.matching.remote"]) - - remote_user = build_remote_user() - - assert SimplePolicy.filter(remote_user) == {:ok, remote_user} - end - - test "has a matching host" do - Config.put([:mrf_simple, :avatar_removal], ["remote.instance"]) - - remote_user = build_remote_user() - {:ok, filtered} = SimplePolicy.filter(remote_user) - - refute filtered["icon"] - end - - test "match with wildcard domain" do - Config.put([:mrf_simple, :avatar_removal], ["*.remote.instance"]) - - remote_user = build_remote_user() - {:ok, filtered} = SimplePolicy.filter(remote_user) - - refute filtered["icon"] - end - end - - describe "when :banner_removal" do - test "is empty" do - Config.put([:mrf_simple, :banner_removal], []) - - remote_user = build_remote_user() - - assert SimplePolicy.filter(remote_user) == {:ok, remote_user} - end - - test "is not empty but it doesn't have a matching host" do - Config.put([:mrf_simple, :banner_removal], ["non.matching.remote"]) - - remote_user = build_remote_user() - - assert SimplePolicy.filter(remote_user) == {:ok, remote_user} - end - - test "has a matching host" do - Config.put([:mrf_simple, :banner_removal], ["remote.instance"]) - - remote_user = build_remote_user() - {:ok, filtered} = SimplePolicy.filter(remote_user) - - refute filtered["image"] - end - - test "match with wildcard domain" do - Config.put([:mrf_simple, :banner_removal], ["*.remote.instance"]) - - remote_user = build_remote_user() - {:ok, filtered} = SimplePolicy.filter(remote_user) - - refute filtered["image"] - end - end - - describe "when :reject_deletes is empty" do - setup do: Config.put([:mrf_simple, :reject_deletes], []) - - test "it accepts deletions even from rejected servers" do - Config.put([:mrf_simple, :reject], ["remote.instance"]) - - deletion_message = build_remote_deletion_message() - - assert SimplePolicy.filter(deletion_message) == {:ok, deletion_message} - end - - test "it accepts deletions even from non-whitelisted servers" do - Config.put([:mrf_simple, :accept], ["non.matching.remote"]) - - deletion_message = build_remote_deletion_message() - - assert SimplePolicy.filter(deletion_message) == {:ok, deletion_message} - end - end - - describe "when :reject_deletes is not empty but it doesn't have a matching host" do - setup do: Config.put([:mrf_simple, :reject_deletes], ["non.matching.remote"]) - - test "it accepts deletions even from rejected servers" do - Config.put([:mrf_simple, :reject], ["remote.instance"]) - - deletion_message = build_remote_deletion_message() - - assert SimplePolicy.filter(deletion_message) == {:ok, deletion_message} - end - - test "it accepts deletions even from non-whitelisted servers" do - Config.put([:mrf_simple, :accept], ["non.matching.remote"]) - - deletion_message = build_remote_deletion_message() - - assert SimplePolicy.filter(deletion_message) == {:ok, deletion_message} - end - end - - describe "when :reject_deletes has a matching host" do - setup do: Config.put([:mrf_simple, :reject_deletes], ["remote.instance"]) - - test "it rejects the deletion" do - deletion_message = build_remote_deletion_message() - - assert {:reject, _} = SimplePolicy.filter(deletion_message) - end - end - - describe "when :reject_deletes match with wildcard domain" do - setup do: Config.put([:mrf_simple, :reject_deletes], ["*.remote.instance"]) - - test "it rejects the deletion" do - deletion_message = build_remote_deletion_message() - - assert {:reject, _} = SimplePolicy.filter(deletion_message) - end - end - - defp build_local_message do - %{ - "actor" => "#{Pleroma.Web.base_url()}/users/alice", - "to" => [], - "cc" => [] - } - end - - defp build_remote_message do - %{"actor" => "https://remote.instance/users/bob"} - end - - defp build_remote_user do - %{ - "id" => "https://remote.instance/users/bob", - "icon" => %{ - "url" => "http://example.com/image.jpg", - "type" => "Image" - }, - "image" => %{ - "url" => "http://example.com/image.jpg", - "type" => "Image" - }, - "type" => "Person" - } - end - - defp build_remote_deletion_message do - %{ - "type" => "Delete", - "actor" => "https://remote.instance/users/bob" - } - end -end diff --git a/test/web/activity_pub/mrf/steal_emoji_policy_test.exs b/test/web/activity_pub/mrf/steal_emoji_policy_test.exs deleted file mode 100644 index 3f8222736..000000000 --- a/test/web/activity_pub/mrf/steal_emoji_policy_test.exs +++ /dev/null @@ -1,68 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicyTest do - use Pleroma.DataCase - - alias Pleroma.Config - alias Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy - - setup_all do - Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - - setup do - emoji_path = Path.join(Config.get([:instance, :static_dir]), "emoji/stolen") - File.rm_rf!(emoji_path) - File.mkdir!(emoji_path) - - Pleroma.Emoji.reload() - - on_exit(fn -> - File.rm_rf!(emoji_path) - end) - - :ok - end - - test "does nothing by default" do - installed_emoji = Pleroma.Emoji.get_all() |> Enum.map(fn {k, _} -> k end) - refute "firedfox" in installed_emoji - - message = %{ - "type" => "Create", - "object" => %{ - "emoji" => [{"firedfox", "https://example.org/emoji/firedfox.png"}], - "actor" => "https://example.org/users/admin" - } - } - - assert {:ok, message} == StealEmojiPolicy.filter(message) - - installed_emoji = Pleroma.Emoji.get_all() |> Enum.map(fn {k, _} -> k end) - refute "firedfox" in installed_emoji - end - - test "Steals emoji on unknown shortcode from allowed remote host" do - installed_emoji = Pleroma.Emoji.get_all() |> Enum.map(fn {k, _} -> k end) - refute "firedfox" in installed_emoji - - message = %{ - "type" => "Create", - "object" => %{ - "emoji" => [{"firedfox", "https://example.org/emoji/firedfox.png"}], - "actor" => "https://example.org/users/admin" - } - } - - clear_config([:mrf_steal_emoji, :hosts], ["example.org"]) - clear_config([:mrf_steal_emoji, :size_limit], 284_468) - - assert {:ok, message} == StealEmojiPolicy.filter(message) - - installed_emoji = Pleroma.Emoji.get_all() |> Enum.map(fn {k, _} -> k end) - assert "firedfox" in installed_emoji - end -end diff --git a/test/web/activity_pub/mrf/subchain_policy_test.exs b/test/web/activity_pub/mrf/subchain_policy_test.exs deleted file mode 100644 index fff66cb7e..000000000 --- a/test/web/activity_pub/mrf/subchain_policy_test.exs +++ /dev/null @@ -1,33 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.MRF.SubchainPolicyTest do - use Pleroma.DataCase - - alias Pleroma.Web.ActivityPub.MRF.DropPolicy - alias Pleroma.Web.ActivityPub.MRF.SubchainPolicy - - @message %{ - "actor" => "https://banned.com", - "type" => "Create", - "object" => %{"content" => "hi"} - } - setup do: clear_config([:mrf_subchain, :match_actor]) - - test "it matches and processes subchains when the actor matches a configured target" do - Pleroma.Config.put([:mrf_subchain, :match_actor], %{ - ~r/^https:\/\/banned.com/s => [DropPolicy] - }) - - {:reject, _} = SubchainPolicy.filter(@message) - end - - test "it doesn't match and process subchains when the actor doesn't match a configured target" do - Pleroma.Config.put([:mrf_subchain, :match_actor], %{ - ~r/^https:\/\/borked.com/s => [DropPolicy] - }) - - {:ok, _message} = SubchainPolicy.filter(@message) - end -end diff --git a/test/web/activity_pub/mrf/tag_policy_test.exs b/test/web/activity_pub/mrf/tag_policy_test.exs deleted file mode 100644 index 6ff71d640..000000000 --- a/test/web/activity_pub/mrf/tag_policy_test.exs +++ /dev/null @@ -1,123 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.MRF.TagPolicyTest do - use Pleroma.DataCase - import Pleroma.Factory - - alias Pleroma.Web.ActivityPub.MRF.TagPolicy - @public "https://www.w3.org/ns/activitystreams#Public" - - describe "mrf_tag:disable-any-subscription" do - test "rejects message" do - actor = insert(:user, tags: ["mrf_tag:disable-any-subscription"]) - message = %{"object" => actor.ap_id, "type" => "Follow", "actor" => actor.ap_id} - assert {:reject, _} = TagPolicy.filter(message) - end - end - - describe "mrf_tag:disable-remote-subscription" do - test "rejects non-local follow requests" do - actor = insert(:user, tags: ["mrf_tag:disable-remote-subscription"]) - follower = insert(:user, tags: ["mrf_tag:disable-remote-subscription"], local: false) - message = %{"object" => actor.ap_id, "type" => "Follow", "actor" => follower.ap_id} - assert {:reject, _} = TagPolicy.filter(message) - end - - test "allows non-local follow requests" do - actor = insert(:user, tags: ["mrf_tag:disable-remote-subscription"]) - follower = insert(:user, tags: ["mrf_tag:disable-remote-subscription"], local: true) - message = %{"object" => actor.ap_id, "type" => "Follow", "actor" => follower.ap_id} - assert {:ok, message} = TagPolicy.filter(message) - end - end - - describe "mrf_tag:sandbox" do - test "removes from public timelines" do - actor = insert(:user, tags: ["mrf_tag:sandbox"]) - - message = %{ - "actor" => actor.ap_id, - "type" => "Create", - "object" => %{}, - "to" => [@public, "f"], - "cc" => [@public, "d"] - } - - except_message = %{ - "actor" => actor.ap_id, - "type" => "Create", - "object" => %{"to" => ["f", actor.follower_address], "cc" => ["d"]}, - "to" => ["f", actor.follower_address], - "cc" => ["d"] - } - - assert TagPolicy.filter(message) == {:ok, except_message} - end - end - - describe "mrf_tag:force-unlisted" do - test "removes from the federated timeline" do - actor = insert(:user, tags: ["mrf_tag:force-unlisted"]) - - message = %{ - "actor" => actor.ap_id, - "type" => "Create", - "object" => %{}, - "to" => [@public, "f"], - "cc" => [actor.follower_address, "d"] - } - - except_message = %{ - "actor" => actor.ap_id, - "type" => "Create", - "object" => %{"to" => ["f", actor.follower_address], "cc" => ["d", @public]}, - "to" => ["f", actor.follower_address], - "cc" => ["d", @public] - } - - assert TagPolicy.filter(message) == {:ok, except_message} - end - end - - describe "mrf_tag:media-strip" do - test "removes attachments" do - actor = insert(:user, tags: ["mrf_tag:media-strip"]) - - message = %{ - "actor" => actor.ap_id, - "type" => "Create", - "object" => %{"attachment" => ["file1"]} - } - - except_message = %{ - "actor" => actor.ap_id, - "type" => "Create", - "object" => %{} - } - - assert TagPolicy.filter(message) == {:ok, except_message} - end - end - - describe "mrf_tag:media-force-nsfw" do - test "Mark as sensitive on presence of attachments" do - actor = insert(:user, tags: ["mrf_tag:media-force-nsfw"]) - - message = %{ - "actor" => actor.ap_id, - "type" => "Create", - "object" => %{"tag" => ["test"], "attachment" => ["file1"]} - } - - except_message = %{ - "actor" => actor.ap_id, - "type" => "Create", - "object" => %{"tag" => ["test", "nsfw"], "attachment" => ["file1"], "sensitive" => true} - } - - assert TagPolicy.filter(message) == {:ok, except_message} - end - end -end diff --git a/test/web/activity_pub/mrf/user_allowlist_policy_test.exs b/test/web/activity_pub/mrf/user_allowlist_policy_test.exs deleted file mode 100644 index 8e1ad5bc8..000000000 --- a/test/web/activity_pub/mrf/user_allowlist_policy_test.exs +++ /dev/null @@ -1,31 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Web.ActivityPub.MRF.UserAllowListPolicyTest do - use Pleroma.DataCase - import Pleroma.Factory - - alias Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy - - setup do: clear_config(:mrf_user_allowlist) - - test "pass filter if allow list is empty" do - actor = insert(:user) - message = %{"actor" => actor.ap_id} - assert UserAllowListPolicy.filter(message) == {:ok, message} - end - - 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"]}) - 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"]}) - message = %{"actor" => actor.ap_id} - assert {:reject, _} = UserAllowListPolicy.filter(message) - end -end diff --git a/test/web/activity_pub/mrf/vocabulary_policy_test.exs b/test/web/activity_pub/mrf/vocabulary_policy_test.exs deleted file mode 100644 index 2bceb67ee..000000000 --- a/test/web/activity_pub/mrf/vocabulary_policy_test.exs +++ /dev/null @@ -1,106 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicyTest do - use Pleroma.DataCase - - alias Pleroma.Web.ActivityPub.MRF.VocabularyPolicy - - describe "accept" do - setup do: clear_config([:mrf_vocabulary, :accept]) - - test "it accepts based on parent activity type" do - Pleroma.Config.put([:mrf_vocabulary, :accept], ["Like"]) - - message = %{ - "type" => "Like", - "object" => "whatever" - } - - {:ok, ^message} = VocabularyPolicy.filter(message) - end - - test "it accepts based on child object type" do - Pleroma.Config.put([:mrf_vocabulary, :accept], ["Create", "Note"]) - - message = %{ - "type" => "Create", - "object" => %{ - "type" => "Note", - "content" => "whatever" - } - } - - {:ok, ^message} = VocabularyPolicy.filter(message) - end - - test "it does not accept disallowed child objects" do - Pleroma.Config.put([:mrf_vocabulary, :accept], ["Create", "Note"]) - - message = %{ - "type" => "Create", - "object" => %{ - "type" => "Article", - "content" => "whatever" - } - } - - {:reject, _} = VocabularyPolicy.filter(message) - end - - test "it does not accept disallowed parent types" do - Pleroma.Config.put([:mrf_vocabulary, :accept], ["Announce", "Note"]) - - message = %{ - "type" => "Create", - "object" => %{ - "type" => "Note", - "content" => "whatever" - } - } - - {:reject, _} = VocabularyPolicy.filter(message) - end - end - - describe "reject" do - setup do: clear_config([:mrf_vocabulary, :reject]) - - test "it rejects based on parent activity type" do - Pleroma.Config.put([:mrf_vocabulary, :reject], ["Like"]) - - message = %{ - "type" => "Like", - "object" => "whatever" - } - - {:reject, _} = VocabularyPolicy.filter(message) - end - - test "it rejects based on child object type" do - Pleroma.Config.put([:mrf_vocabulary, :reject], ["Note"]) - - message = %{ - "type" => "Create", - "object" => %{ - "type" => "Note", - "content" => "whatever" - } - } - - {:reject, _} = VocabularyPolicy.filter(message) - end - - test "it passes through objects that aren't disallowed" do - Pleroma.Config.put([:mrf_vocabulary, :reject], ["Like"]) - - message = %{ - "type" => "Announce", - "object" => "whatever" - } - - {:ok, ^message} = VocabularyPolicy.filter(message) - end - end -end diff --git a/test/web/activity_pub/object_validators/accept_validation_test.exs b/test/web/activity_pub/object_validators/accept_validation_test.exs deleted file mode 100644 index d6111ba41..000000000 --- a/test/web/activity_pub/object_validators/accept_validation_test.exs +++ /dev/null @@ -1,56 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.ObjectValidators.AcceptValidationTest do - use Pleroma.DataCase - - alias Pleroma.Web.ActivityPub.Builder - alias Pleroma.Web.ActivityPub.ObjectValidator - alias Pleroma.Web.ActivityPub.Pipeline - - import Pleroma.Factory - - setup do - follower = insert(:user) - followed = insert(:user, local: false) - - {:ok, follow_data, _} = Builder.follow(follower, followed) - {:ok, follow_activity, _} = Pipeline.common_pipeline(follow_data, local: true) - - {:ok, accept_data, _} = Builder.accept(followed, follow_activity) - - %{accept_data: accept_data, followed: followed} - end - - test "it validates a basic 'accept'", %{accept_data: accept_data} do - assert {:ok, _, _} = ObjectValidator.validate(accept_data, []) - end - - test "it fails when the actor doesn't exist", %{accept_data: accept_data} do - accept_data = - accept_data - |> Map.put("actor", "https://gensokyo.2hu/users/raymoo") - - assert {:error, _} = ObjectValidator.validate(accept_data, []) - end - - test "it fails when the accepted activity doesn't exist", %{accept_data: accept_data} do - accept_data = - accept_data - |> Map.put("object", "https://gensokyo.2hu/users/raymoo/follows/1") - - assert {:error, _} = ObjectValidator.validate(accept_data, []) - end - - test "for an accepted follow, it only validates if the actor of the accept is the followed actor", - %{accept_data: accept_data} do - stranger = insert(:user) - - accept_data = - accept_data - |> Map.put("actor", stranger.ap_id) - - assert {:error, _} = ObjectValidator.validate(accept_data, []) - end -end diff --git a/test/web/activity_pub/object_validators/announce_validation_test.exs b/test/web/activity_pub/object_validators/announce_validation_test.exs deleted file mode 100644 index 623342f76..000000000 --- a/test/web/activity_pub/object_validators/announce_validation_test.exs +++ /dev/null @@ -1,106 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnnouncValidationTest do - use Pleroma.DataCase - - alias Pleroma.Object - alias Pleroma.Web.ActivityPub.Builder - alias Pleroma.Web.ActivityPub.ObjectValidator - alias Pleroma.Web.CommonAPI - - import Pleroma.Factory - - describe "announces" do - setup do - user = insert(:user) - announcer = insert(:user) - {:ok, post_activity} = CommonAPI.post(user, %{status: "uguu"}) - - object = Object.normalize(post_activity, false) - {:ok, valid_announce, []} = Builder.announce(announcer, object) - - %{ - valid_announce: valid_announce, - user: user, - post_activity: post_activity, - announcer: announcer - } - end - - test "returns ok for a valid announce", %{valid_announce: valid_announce} do - assert {:ok, _object, _meta} = ObjectValidator.validate(valid_announce, []) - end - - test "returns an error if the object can't be found", %{valid_announce: valid_announce} do - without_object = - valid_announce - |> Map.delete("object") - - {:error, cng} = ObjectValidator.validate(without_object, []) - - assert {:object, {"can't be blank", [validation: :required]}} in cng.errors - - nonexisting_object = - valid_announce - |> Map.put("object", "https://gensokyo.2hu/objects/99999999") - - {:error, cng} = ObjectValidator.validate(nonexisting_object, []) - - assert {:object, {"can't find object", []}} in cng.errors - end - - test "returns an error if we don't have the actor", %{valid_announce: valid_announce} do - nonexisting_actor = - valid_announce - |> Map.put("actor", "https://gensokyo.2hu/users/raymoo") - - {:error, cng} = ObjectValidator.validate(nonexisting_actor, []) - - assert {:actor, {"can't find user", []}} in cng.errors - end - - test "returns an error if the actor already announced the object", %{ - valid_announce: valid_announce, - announcer: announcer, - post_activity: post_activity - } do - _announce = CommonAPI.repeat(post_activity.id, announcer) - - {:error, cng} = ObjectValidator.validate(valid_announce, []) - - assert {:actor, {"already announced this object", []}} in cng.errors - assert {:object, {"already announced by this actor", []}} in cng.errors - end - - test "returns an error if the actor can't announce the object", %{ - announcer: announcer, - user: user - } do - {:ok, post_activity} = - CommonAPI.post(user, %{status: "a secret post", visibility: "private"}) - - object = Object.normalize(post_activity, false) - - # Another user can't announce it - {:ok, announce, []} = Builder.announce(announcer, object, public: false) - - {:error, cng} = ObjectValidator.validate(announce, []) - - assert {:actor, {"can not announce this object", []}} in cng.errors - - # The actor of the object can announce it - {:ok, announce, []} = Builder.announce(user, object, public: false) - - assert {:ok, _, _} = ObjectValidator.validate(announce, []) - - # The actor of the object can not announce it publicly - {:ok, announce, []} = Builder.announce(user, object, public: true) - - {:error, cng} = ObjectValidator.validate(announce, []) - - assert {:actor, {"can not announce this object publicly", []}} in cng.errors - end - end -end diff --git a/test/web/activity_pub/object_validators/attachment_validator_test.exs b/test/web/activity_pub/object_validators/attachment_validator_test.exs deleted file mode 100644 index 558bb3131..000000000 --- a/test/web/activity_pub/object_validators/attachment_validator_test.exs +++ /dev/null @@ -1,74 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidatorTest do - use Pleroma.DataCase - - alias Pleroma.Web.ActivityPub.ActivityPub - alias Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator - - import Pleroma.Factory - - describe "attachments" do - test "works with honkerific attachments" do - attachment = %{ - "mediaType" => "", - "name" => "", - "summary" => "298p3RG7j27tfsZ9RQ.jpg", - "type" => "Document", - "url" => "https://honk.tedunangst.com/d/298p3RG7j27tfsZ9RQ.jpg" - } - - assert {:ok, attachment} = - AttachmentValidator.cast_and_validate(attachment) - |> Ecto.Changeset.apply_action(:insert) - - assert attachment.mediaType == "application/octet-stream" - end - - test "it turns mastodon attachments into our attachments" do - attachment = %{ - "url" => - "http://mastodon.example.org/system/media_attachments/files/000/000/002/original/334ce029e7bfb920.jpg", - "type" => "Document", - "name" => nil, - "mediaType" => "image/jpeg" - } - - {:ok, attachment} = - AttachmentValidator.cast_and_validate(attachment) - |> Ecto.Changeset.apply_action(:insert) - - assert [ - %{ - href: - "http://mastodon.example.org/system/media_attachments/files/000/000/002/original/334ce029e7bfb920.jpg", - type: "Link", - mediaType: "image/jpeg" - } - ] = attachment.url - - assert attachment.mediaType == "image/jpeg" - end - - test "it handles our own uploads" do - user = insert(:user) - - file = %Plug.Upload{ - content_type: "image/jpg", - path: Path.absname("test/fixtures/image.jpg"), - filename: "an_image.jpg" - } - - {:ok, attachment} = ActivityPub.upload(file, actor: user.ap_id) - - {:ok, attachment} = - attachment.data - |> AttachmentValidator.cast_and_validate() - |> Ecto.Changeset.apply_action(:insert) - - assert attachment.mediaType == "image/jpeg" - end - end -end diff --git a/test/web/activity_pub/object_validators/block_validation_test.exs b/test/web/activity_pub/object_validators/block_validation_test.exs deleted file mode 100644 index c08d4b2e8..000000000 --- a/test/web/activity_pub/object_validators/block_validation_test.exs +++ /dev/null @@ -1,39 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.ObjectValidators.BlockValidationTest do - use Pleroma.DataCase - - alias Pleroma.Web.ActivityPub.Builder - alias Pleroma.Web.ActivityPub.ObjectValidator - - import Pleroma.Factory - - describe "blocks" do - setup do - user = insert(:user, local: false) - blocked = insert(:user) - - {:ok, valid_block, []} = Builder.block(user, blocked) - - %{user: user, valid_block: valid_block} - end - - test "validates a basic object", %{ - valid_block: valid_block - } do - assert {:ok, _block, []} = ObjectValidator.validate(valid_block, []) - end - - test "returns an error if we don't know the blocked user", %{ - valid_block: valid_block - } do - block = - valid_block - |> Map.put("object", "https://gensokyo.2hu/users/raymoo") - - assert {:error, _cng} = ObjectValidator.validate(block, []) - end - end -end diff --git a/test/web/activity_pub/object_validators/chat_validation_test.exs b/test/web/activity_pub/object_validators/chat_validation_test.exs deleted file mode 100644 index 16e4808e5..000000000 --- a/test/web/activity_pub/object_validators/chat_validation_test.exs +++ /dev/null @@ -1,212 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.ObjectValidators.ChatValidationTest do - use Pleroma.DataCase - alias Pleroma.Object - alias Pleroma.Web.ActivityPub.ActivityPub - alias Pleroma.Web.ActivityPub.Builder - alias Pleroma.Web.ActivityPub.ObjectValidator - alias Pleroma.Web.CommonAPI - - import Pleroma.Factory - - describe "chat message create activities" do - test "it is invalid if the object already exists" do - user = insert(:user) - recipient = insert(:user) - {:ok, activity} = CommonAPI.post_chat_message(user, recipient, "hey") - object = Object.normalize(activity, false) - - {:ok, create_data, _} = Builder.create(user, object.data, [recipient.ap_id]) - - {:error, cng} = ObjectValidator.validate(create_data, []) - - assert {:object, {"The object to create already exists", []}} in cng.errors - end - - test "it is invalid if the object data has a different `to` or `actor` field" do - user = insert(:user) - recipient = insert(:user) - {:ok, object_data, _} = Builder.chat_message(recipient, user.ap_id, "Hey") - - {:ok, create_data, _} = Builder.create(user, object_data, [recipient.ap_id]) - - {:error, cng} = ObjectValidator.validate(create_data, []) - - assert {:to, {"Recipients don't match with object recipients", []}} in cng.errors - assert {:actor, {"Actor doesn't match with object actor", []}} in cng.errors - end - end - - describe "chat messages" do - setup do - clear_config([:instance, :remote_limit]) - user = insert(:user) - recipient = insert(:user, local: false) - - {:ok, valid_chat_message, _} = Builder.chat_message(user, recipient.ap_id, "hey :firefox:") - - %{user: user, recipient: recipient, valid_chat_message: valid_chat_message} - end - - test "let's through some basic html", %{user: user, recipient: recipient} do - {:ok, valid_chat_message, _} = - Builder.chat_message( - user, - recipient.ap_id, - "hey <a href='https://example.org'>example</a> <script>alert('uguu')</script>" - ) - - assert {:ok, object, _meta} = ObjectValidator.validate(valid_chat_message, []) - - assert object["content"] == - "hey <a href=\"https://example.org\">example</a> alert('uguu')" - end - - test "validates for a basic object we build", %{valid_chat_message: valid_chat_message} do - assert {:ok, object, _meta} = ObjectValidator.validate(valid_chat_message, []) - - assert Map.put(valid_chat_message, "attachment", nil) == object - assert match?(%{"firefox" => _}, object["emoji"]) - end - - test "validates for a basic object with an attachment", %{ - valid_chat_message: valid_chat_message, - user: user - } do - file = %Plug.Upload{ - content_type: "image/jpg", - path: Path.absname("test/fixtures/image.jpg"), - filename: "an_image.jpg" - } - - {:ok, attachment} = ActivityPub.upload(file, actor: user.ap_id) - - valid_chat_message = - valid_chat_message - |> Map.put("attachment", attachment.data) - - assert {:ok, object, _meta} = ObjectValidator.validate(valid_chat_message, []) - - assert object["attachment"] - end - - test "validates for a basic object with an attachment in an array", %{ - valid_chat_message: valid_chat_message, - user: user - } do - file = %Plug.Upload{ - content_type: "image/jpg", - path: Path.absname("test/fixtures/image.jpg"), - filename: "an_image.jpg" - } - - {:ok, attachment} = ActivityPub.upload(file, actor: user.ap_id) - - valid_chat_message = - valid_chat_message - |> Map.put("attachment", [attachment.data]) - - assert {:ok, object, _meta} = ObjectValidator.validate(valid_chat_message, []) - - assert object["attachment"] - end - - test "validates for a basic object with an attachment but without content", %{ - valid_chat_message: valid_chat_message, - user: user - } do - file = %Plug.Upload{ - content_type: "image/jpg", - path: Path.absname("test/fixtures/image.jpg"), - filename: "an_image.jpg" - } - - {:ok, attachment} = ActivityPub.upload(file, actor: user.ap_id) - - valid_chat_message = - valid_chat_message - |> Map.put("attachment", attachment.data) - |> Map.delete("content") - - assert {:ok, object, _meta} = ObjectValidator.validate(valid_chat_message, []) - - assert object["attachment"] - end - - test "does not validate if the message has no content", %{ - valid_chat_message: valid_chat_message - } do - contentless = - valid_chat_message - |> Map.delete("content") - - refute match?({:ok, _object, _meta}, ObjectValidator.validate(contentless, [])) - end - - test "does not validate if the message is longer than the remote_limit", %{ - valid_chat_message: valid_chat_message - } do - Pleroma.Config.put([:instance, :remote_limit], 2) - refute match?({:ok, _object, _meta}, ObjectValidator.validate(valid_chat_message, [])) - end - - test "does not validate if the recipient is blocking the actor", %{ - valid_chat_message: valid_chat_message, - user: user, - recipient: recipient - } do - Pleroma.User.block(recipient, user) - refute match?({:ok, _object, _meta}, ObjectValidator.validate(valid_chat_message, [])) - end - - test "does not validate if the recipient is not accepting chat messages", %{ - valid_chat_message: valid_chat_message, - recipient: recipient - } do - recipient - |> Ecto.Changeset.change(%{accepts_chat_messages: false}) - |> Pleroma.Repo.update!() - - refute match?({:ok, _object, _meta}, ObjectValidator.validate(valid_chat_message, [])) - end - - test "does not validate if the actor or the recipient is not in our system", %{ - valid_chat_message: valid_chat_message - } do - chat_message = - valid_chat_message - |> Map.put("actor", "https://raymoo.com/raymoo") - - {:error, _} = ObjectValidator.validate(chat_message, []) - - chat_message = - valid_chat_message - |> Map.put("to", ["https://raymoo.com/raymoo"]) - - {:error, _} = ObjectValidator.validate(chat_message, []) - end - - test "does not validate for a message with multiple recipients", %{ - valid_chat_message: valid_chat_message, - user: user, - recipient: recipient - } do - chat_message = - valid_chat_message - |> Map.put("to", [user.ap_id, recipient.ap_id]) - - assert {:error, _} = ObjectValidator.validate(chat_message, []) - end - - test "does not validate if it doesn't concern local users" do - user = insert(:user, local: false) - recipient = insert(:user, local: false) - - {:ok, valid_chat_message, _} = Builder.chat_message(user, recipient.ap_id, "hey") - assert {:error, _} = ObjectValidator.validate(valid_chat_message, []) - end - end -end diff --git a/test/web/activity_pub/object_validators/delete_validation_test.exs b/test/web/activity_pub/object_validators/delete_validation_test.exs deleted file mode 100644 index 02683b899..000000000 --- a/test/web/activity_pub/object_validators/delete_validation_test.exs +++ /dev/null @@ -1,106 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidationTest do - use Pleroma.DataCase - - alias Pleroma.Object - alias Pleroma.Web.ActivityPub.Builder - alias Pleroma.Web.ActivityPub.ObjectValidator - alias Pleroma.Web.CommonAPI - - import Pleroma.Factory - - describe "deletes" do - setup do - user = insert(:user) - {:ok, post_activity} = CommonAPI.post(user, %{status: "cancel me daddy"}) - - {:ok, valid_post_delete, _} = Builder.delete(user, post_activity.data["object"]) - {:ok, valid_user_delete, _} = Builder.delete(user, user.ap_id) - - %{user: user, valid_post_delete: valid_post_delete, valid_user_delete: valid_user_delete} - end - - test "it is valid for a post deletion", %{valid_post_delete: valid_post_delete} do - {:ok, valid_post_delete, _} = ObjectValidator.validate(valid_post_delete, []) - - assert valid_post_delete["deleted_activity_id"] - end - - test "it is invalid if the object isn't in a list of certain types", %{ - valid_post_delete: valid_post_delete - } do - object = Object.get_by_ap_id(valid_post_delete["object"]) - - data = - object.data - |> Map.put("type", "Like") - - {:ok, _object} = - object - |> Ecto.Changeset.change(%{data: data}) - |> Object.update_and_set_cache() - - {:error, cng} = ObjectValidator.validate(valid_post_delete, []) - assert {:object, {"object not in allowed types", []}} in cng.errors - end - - test "it is valid for a user deletion", %{valid_user_delete: valid_user_delete} do - assert match?({:ok, _, _}, ObjectValidator.validate(valid_user_delete, [])) - end - - test "it's invalid if the id is missing", %{valid_post_delete: valid_post_delete} do - no_id = - valid_post_delete - |> Map.delete("id") - - {:error, cng} = ObjectValidator.validate(no_id, []) - - assert {:id, {"can't be blank", [validation: :required]}} in cng.errors - end - - test "it's invalid if the object doesn't exist", %{valid_post_delete: valid_post_delete} do - missing_object = - valid_post_delete - |> Map.put("object", "http://does.not/exist") - - {:error, cng} = ObjectValidator.validate(missing_object, []) - - assert {:object, {"can't find object", []}} in cng.errors - end - - test "it's invalid if the actor of the object and the actor of delete are from different domains", - %{valid_post_delete: valid_post_delete} do - valid_user = insert(:user) - - valid_other_actor = - valid_post_delete - |> Map.put("actor", valid_user.ap_id) - - assert match?({:ok, _, _}, ObjectValidator.validate(valid_other_actor, [])) - - invalid_other_actor = - valid_post_delete - |> Map.put("actor", "https://gensokyo.2hu/users/raymoo") - - {:error, cng} = ObjectValidator.validate(invalid_other_actor, []) - - assert {:actor, {"is not allowed to modify object", []}} in cng.errors - end - - test "it's valid if the actor of the object is a local superuser", - %{valid_post_delete: valid_post_delete} do - user = - insert(:user, local: true, is_moderator: true, ap_id: "https://gensokyo.2hu/users/raymoo") - - valid_other_actor = - valid_post_delete - |> Map.put("actor", user.ap_id) - - {:ok, _, meta} = ObjectValidator.validate(valid_other_actor, []) - assert meta[:do_not_federate] - end - end -end diff --git a/test/web/activity_pub/object_validators/emoji_react_validation_test.exs b/test/web/activity_pub/object_validators/emoji_react_validation_test.exs deleted file mode 100644 index 582e6d785..000000000 --- a/test/web/activity_pub/object_validators/emoji_react_validation_test.exs +++ /dev/null @@ -1,53 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactHandlingTest do - use Pleroma.DataCase - - alias Pleroma.Web.ActivityPub.Builder - alias Pleroma.Web.ActivityPub.ObjectValidator - alias Pleroma.Web.CommonAPI - - import Pleroma.Factory - - describe "EmojiReacts" do - setup do - user = insert(:user) - {:ok, post_activity} = CommonAPI.post(user, %{status: "uguu"}) - - object = Pleroma.Object.get_by_ap_id(post_activity.data["object"]) - - {:ok, valid_emoji_react, []} = Builder.emoji_react(user, object, "👌") - - %{user: user, post_activity: post_activity, valid_emoji_react: valid_emoji_react} - end - - test "it validates a valid EmojiReact", %{valid_emoji_react: valid_emoji_react} do - assert {:ok, _, _} = ObjectValidator.validate(valid_emoji_react, []) - end - - test "it is not valid without a 'content' field", %{valid_emoji_react: valid_emoji_react} do - without_content = - valid_emoji_react - |> Map.delete("content") - - {:error, cng} = ObjectValidator.validate(without_content, []) - - refute cng.valid? - assert {:content, {"can't be blank", [validation: :required]}} in cng.errors - end - - test "it is not valid with a non-emoji content field", %{valid_emoji_react: valid_emoji_react} do - without_emoji_content = - valid_emoji_react - |> Map.put("content", "x") - - {:error, cng} = ObjectValidator.validate(without_emoji_content, []) - - refute cng.valid? - - assert {:content, {"must be a single character emoji", []}} in cng.errors - end - end -end diff --git a/test/web/activity_pub/object_validators/follow_validation_test.exs b/test/web/activity_pub/object_validators/follow_validation_test.exs deleted file mode 100644 index 6e1378be2..000000000 --- a/test/web/activity_pub/object_validators/follow_validation_test.exs +++ /dev/null @@ -1,26 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.ObjectValidators.FollowValidationTest do - use Pleroma.DataCase - - alias Pleroma.Web.ActivityPub.Builder - alias Pleroma.Web.ActivityPub.ObjectValidator - - import Pleroma.Factory - - describe "Follows" do - setup do - follower = insert(:user) - followed = insert(:user) - - {:ok, valid_follow, []} = Builder.follow(follower, followed) - %{follower: follower, followed: followed, valid_follow: valid_follow} - end - - test "validates a basic follow object", %{valid_follow: valid_follow} do - assert {:ok, _follow, []} = ObjectValidator.validate(valid_follow, []) - end - end -end diff --git a/test/web/activity_pub/object_validators/like_validation_test.exs b/test/web/activity_pub/object_validators/like_validation_test.exs deleted file mode 100644 index 2c033b7e2..000000000 --- a/test/web/activity_pub/object_validators/like_validation_test.exs +++ /dev/null @@ -1,113 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.ObjectValidators.LikeValidationTest do - use Pleroma.DataCase - - alias Pleroma.Web.ActivityPub.ObjectValidator - alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator - alias Pleroma.Web.ActivityPub.Utils - alias Pleroma.Web.CommonAPI - - import Pleroma.Factory - - describe "likes" do - setup do - user = insert(:user) - {:ok, post_activity} = CommonAPI.post(user, %{status: "uguu"}) - - valid_like = %{ - "to" => [user.ap_id], - "cc" => [], - "type" => "Like", - "id" => Utils.generate_activity_id(), - "object" => post_activity.data["object"], - "actor" => user.ap_id, - "context" => "a context" - } - - %{valid_like: valid_like, user: user, post_activity: post_activity} - end - - test "returns ok when called in the ObjectValidator", %{valid_like: valid_like} do - {:ok, object, _meta} = ObjectValidator.validate(valid_like, []) - - assert "id" in Map.keys(object) - end - - test "is valid for a valid object", %{valid_like: valid_like} do - assert LikeValidator.cast_and_validate(valid_like).valid? - end - - test "sets the 'to' field to the object actor if no recipients are given", %{ - valid_like: valid_like, - user: user - } do - without_recipients = - valid_like - |> Map.delete("to") - - {:ok, object, _meta} = ObjectValidator.validate(without_recipients, []) - - assert object["to"] == [user.ap_id] - end - - test "sets the context field to the context of the object if no context is given", %{ - valid_like: valid_like, - post_activity: post_activity - } do - without_context = - valid_like - |> Map.delete("context") - - {:ok, object, _meta} = ObjectValidator.validate(without_context, []) - - assert object["context"] == post_activity.data["context"] - end - - test "it errors when the actor is missing or not known", %{valid_like: valid_like} do - without_actor = Map.delete(valid_like, "actor") - - refute LikeValidator.cast_and_validate(without_actor).valid? - - with_invalid_actor = Map.put(valid_like, "actor", "invalidactor") - - refute LikeValidator.cast_and_validate(with_invalid_actor).valid? - end - - test "it errors when the object is missing or not known", %{valid_like: valid_like} do - without_object = Map.delete(valid_like, "object") - - refute LikeValidator.cast_and_validate(without_object).valid? - - with_invalid_object = Map.put(valid_like, "object", "invalidobject") - - refute LikeValidator.cast_and_validate(with_invalid_object).valid? - end - - test "it errors when the actor has already like the object", %{ - valid_like: valid_like, - user: user, - post_activity: post_activity - } do - _like = CommonAPI.favorite(user, post_activity.id) - - refute LikeValidator.cast_and_validate(valid_like).valid? - end - - test "it works when actor or object are wrapped in maps", %{valid_like: valid_like} do - wrapped_like = - valid_like - |> Map.put("actor", %{"id" => valid_like["actor"]}) - |> Map.put("object", %{"id" => valid_like["object"]}) - - validated = LikeValidator.cast_and_validate(wrapped_like) - - assert validated.valid? - - assert {:actor, valid_like["actor"]} in validated.changes - assert {:object, valid_like["object"]} in validated.changes - end - end -end diff --git a/test/web/activity_pub/object_validators/note_validator_test.exs b/test/web/activity_pub/object_validators/note_validator_test.exs deleted file mode 100644 index 30c481ffb..000000000 --- a/test/web/activity_pub/object_validators/note_validator_test.exs +++ /dev/null @@ -1,35 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.ObjectValidators.NoteValidatorTest do - use Pleroma.DataCase - - alias Pleroma.Web.ActivityPub.ObjectValidators.NoteValidator - alias Pleroma.Web.ActivityPub.Utils - - import Pleroma.Factory - - describe "Notes" do - setup do - user = insert(:user) - - note = %{ - "id" => Utils.generate_activity_id(), - "type" => "Note", - "actor" => user.ap_id, - "to" => [user.follower_address], - "cc" => [], - "content" => "Hellow this is content.", - "context" => "xxx", - "summary" => "a post" - } - - %{user: user, note: note} - end - - test "a basic note validates", %{note: note} do - %{valid?: true} = NoteValidator.cast_and_validate(note) - end - end -end diff --git a/test/web/activity_pub/object_validators/reject_validation_test.exs b/test/web/activity_pub/object_validators/reject_validation_test.exs deleted file mode 100644 index 370bb6e5c..000000000 --- a/test/web/activity_pub/object_validators/reject_validation_test.exs +++ /dev/null @@ -1,56 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.ObjectValidators.RejectValidationTest do - use Pleroma.DataCase - - alias Pleroma.Web.ActivityPub.Builder - alias Pleroma.Web.ActivityPub.ObjectValidator - alias Pleroma.Web.ActivityPub.Pipeline - - import Pleroma.Factory - - setup do - follower = insert(:user) - followed = insert(:user, local: false) - - {:ok, follow_data, _} = Builder.follow(follower, followed) - {:ok, follow_activity, _} = Pipeline.common_pipeline(follow_data, local: true) - - {:ok, reject_data, _} = Builder.reject(followed, follow_activity) - - %{reject_data: reject_data, followed: followed} - end - - test "it validates a basic 'reject'", %{reject_data: reject_data} do - assert {:ok, _, _} = ObjectValidator.validate(reject_data, []) - end - - test "it fails when the actor doesn't exist", %{reject_data: reject_data} do - reject_data = - reject_data - |> Map.put("actor", "https://gensokyo.2hu/users/raymoo") - - assert {:error, _} = ObjectValidator.validate(reject_data, []) - end - - test "it fails when the rejected activity doesn't exist", %{reject_data: reject_data} do - reject_data = - reject_data - |> Map.put("object", "https://gensokyo.2hu/users/raymoo/follows/1") - - assert {:error, _} = ObjectValidator.validate(reject_data, []) - end - - test "for an rejected follow, it only validates if the actor of the reject is the followed actor", - %{reject_data: reject_data} do - stranger = insert(:user) - - reject_data = - reject_data - |> Map.put("actor", stranger.ap_id) - - assert {:error, _} = ObjectValidator.validate(reject_data, []) - end -end diff --git a/test/web/activity_pub/object_validators/types/date_time_test.exs b/test/web/activity_pub/object_validators/types/date_time_test.exs deleted file mode 100644 index 43be8e936..000000000 --- a/test/web/activity_pub/object_validators/types/date_time_test.exs +++ /dev/null @@ -1,32 +0,0 @@ -defmodule Pleroma.Web.ActivityPub.ObjectValidators.Types.DateTimeTest do - alias Pleroma.EctoType.ActivityPub.ObjectValidators.DateTime - use Pleroma.DataCase - - test "it validates an xsd:Datetime" do - valid_strings = [ - "2004-04-12T13:20:00", - "2004-04-12T13:20:15.5", - "2004-04-12T13:20:00-05:00", - "2004-04-12T13:20:00Z" - ] - - invalid_strings = [ - "2004-04-12T13:00", - "2004-04-1213:20:00", - "99-04-12T13:00", - "2004-04-12" - ] - - assert {:ok, "2004-04-01T12:00:00Z"} == DateTime.cast("2004-04-01T12:00:00Z") - - Enum.each(valid_strings, fn date_time -> - result = DateTime.cast(date_time) - assert {:ok, _} = result - end) - - Enum.each(invalid_strings, fn date_time -> - result = DateTime.cast(date_time) - assert :error == result - end) - end -end 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 deleted file mode 100644 index e0ab76379..000000000 --- a/test/web/activity_pub/object_validators/types/object_id_test.exs +++ /dev/null @@ -1,41 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ObjectValidators.Types.ObjectIDTest do - alias Pleroma.EctoType.ActivityPub.ObjectValidators.ObjectID - use Pleroma.DataCase - - @uris [ - "http://lain.com/users/lain", - "http://lain.com", - "https://lain.com/object/1" - ] - - @non_uris [ - "https://", - "rin", - 1, - :x, - %{"1" => 2} - ] - - test "it accepts http uris" do - Enum.each(@uris, fn uri -> - assert {:ok, uri} == ObjectID.cast(uri) - end) - end - - test "it accepts an object with a nested uri id" do - Enum.each(@uris, fn uri -> - assert {:ok, uri} == ObjectID.cast(%{"id" => uri}) - end) - end - - test "it rejects non-uri strings" do - Enum.each(@non_uris, fn non_uri -> - assert :error == ObjectID.cast(non_uri) - assert :error == ObjectID.cast(%{"id" => non_uri}) - end) - end -end diff --git a/test/web/activity_pub/object_validators/types/recipients_test.exs b/test/web/activity_pub/object_validators/types/recipients_test.exs deleted file mode 100644 index 053916bdd..000000000 --- a/test/web/activity_pub/object_validators/types/recipients_test.exs +++ /dev/null @@ -1,27 +0,0 @@ -defmodule Pleroma.Web.ObjectValidators.Types.RecipientsTest do - alias Pleroma.EctoType.ActivityPub.ObjectValidators.Recipients - use Pleroma.DataCase - - test "it asserts that all elements of the list are object ids" do - list = ["https://lain.com/users/lain", "invalid"] - - assert :error == Recipients.cast(list) - end - - test "it works with a list" do - list = ["https://lain.com/users/lain"] - assert {:ok, list} == Recipients.cast(list) - end - - test "it works with a list with whole objects" do - list = ["https://lain.com/users/lain", %{"id" => "https://gensokyo.2hu/users/raymoo"}] - resulting_list = ["https://gensokyo.2hu/users/raymoo", "https://lain.com/users/lain"] - assert {:ok, resulting_list} == Recipients.cast(list) - end - - test "it turns a single string into a list" do - recipient = "https://lain.com/users/lain" - - assert {:ok, [recipient]} == Recipients.cast(recipient) - end -end 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 deleted file mode 100644 index 9c08606f6..000000000 --- a/test/web/activity_pub/object_validators/types/safe_text_test.exs +++ /dev/null @@ -1,30 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.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/object_validators/undo_validation_test.exs b/test/web/activity_pub/object_validators/undo_validation_test.exs deleted file mode 100644 index 75bbcc4b6..000000000 --- a/test/web/activity_pub/object_validators/undo_validation_test.exs +++ /dev/null @@ -1,53 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.ObjectValidators.UndoHandlingTest do - use Pleroma.DataCase - - alias Pleroma.Web.ActivityPub.Builder - alias Pleroma.Web.ActivityPub.ObjectValidator - alias Pleroma.Web.CommonAPI - - import Pleroma.Factory - - describe "Undos" do - setup do - user = insert(:user) - {:ok, post_activity} = CommonAPI.post(user, %{status: "uguu"}) - {:ok, like} = CommonAPI.favorite(user, post_activity.id) - {:ok, valid_like_undo, []} = Builder.undo(user, like) - - %{user: user, like: like, valid_like_undo: valid_like_undo} - end - - test "it validates a basic like undo", %{valid_like_undo: valid_like_undo} do - assert {:ok, _, _} = ObjectValidator.validate(valid_like_undo, []) - end - - test "it does not validate if the actor of the undo is not the actor of the object", %{ - valid_like_undo: valid_like_undo - } do - other_user = insert(:user, ap_id: "https://gensokyo.2hu/users/raymoo") - - bad_actor = - valid_like_undo - |> Map.put("actor", other_user.ap_id) - - {:error, cng} = ObjectValidator.validate(bad_actor, []) - - assert {:actor, {"not the same as object actor", []}} in cng.errors - end - - test "it does not validate if the object is missing", %{valid_like_undo: valid_like_undo} do - missing_object = - valid_like_undo - |> Map.put("object", "https://gensokyo.2hu/objects/1") - - {:error, cng} = ObjectValidator.validate(missing_object, []) - - assert {:object, {"can't find object", []}} in cng.errors - assert length(cng.errors) == 1 - end - end -end diff --git a/test/web/activity_pub/object_validators/update_validation_test.exs b/test/web/activity_pub/object_validators/update_validation_test.exs deleted file mode 100644 index 5e80cf731..000000000 --- a/test/web/activity_pub/object_validators/update_validation_test.exs +++ /dev/null @@ -1,44 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.ObjectValidators.UpdateHandlingTest do - use Pleroma.DataCase - - alias Pleroma.Web.ActivityPub.Builder - alias Pleroma.Web.ActivityPub.ObjectValidator - - import Pleroma.Factory - - describe "updates" do - setup do - user = insert(:user) - - object = %{ - "id" => user.ap_id, - "name" => "A new name", - "summary" => "A new bio" - } - - {:ok, valid_update, []} = Builder.update(user, object) - - %{user: user, valid_update: valid_update} - end - - test "validates a basic object", %{valid_update: valid_update} do - assert {:ok, _update, []} = ObjectValidator.validate(valid_update, []) - end - - test "returns an error if the object can't be updated by the actor", %{ - valid_update: valid_update - } do - other_user = insert(:user) - - update = - valid_update - |> Map.put("actor", other_user.ap_id) - - assert {:error, _cng} = ObjectValidator.validate(update, []) - end - end -end diff --git a/test/web/activity_pub/pipeline_test.exs b/test/web/activity_pub/pipeline_test.exs deleted file mode 100644 index f2a231eaf..000000000 --- a/test/web/activity_pub/pipeline_test.exs +++ /dev/null @@ -1,179 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.PipelineTest do - use Pleroma.DataCase - - import Mock - import Pleroma.Factory - - describe "common_pipeline/2" do - setup do - clear_config([:instance, :federating], true) - :ok - end - - test "when given an `object_data` in meta, Federation will receive a the original activity with the `object` field set to this embedded object" do - activity = insert(:note_activity) - object = %{"id" => "1", "type" => "Love"} - meta = [local: true, object_data: object] - - activity_with_object = %{activity | data: Map.put(activity.data, "object", object)} - - with_mocks([ - {Pleroma.Web.ActivityPub.ObjectValidator, [], [validate: fn o, m -> {:ok, o, m} end]}, - { - Pleroma.Web.ActivityPub.MRF, - [], - [filter: fn o -> {:ok, o} end] - }, - { - Pleroma.Web.ActivityPub.ActivityPub, - [], - [persist: fn o, m -> {:ok, o, m} end] - }, - { - Pleroma.Web.ActivityPub.SideEffects, - [], - [ - handle: fn o, m -> {:ok, o, m} end, - handle_after_transaction: fn m -> m end - ] - }, - { - Pleroma.Web.Federator, - [], - [publish: fn _o -> :ok end] - } - ]) do - assert {:ok, ^activity, ^meta} = - Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity, meta) - - assert_called(Pleroma.Web.ActivityPub.ObjectValidator.validate(activity, meta)) - assert_called(Pleroma.Web.ActivityPub.MRF.filter(activity)) - assert_called(Pleroma.Web.ActivityPub.ActivityPub.persist(activity, meta)) - assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta)) - refute called(Pleroma.Web.Federator.publish(activity)) - assert_called(Pleroma.Web.Federator.publish(activity_with_object)) - end - end - - test "it goes through validation, filtering, persisting, side effects and federation for local activities" do - activity = insert(:note_activity) - meta = [local: true] - - with_mocks([ - {Pleroma.Web.ActivityPub.ObjectValidator, [], [validate: fn o, m -> {:ok, o, m} end]}, - { - Pleroma.Web.ActivityPub.MRF, - [], - [filter: fn o -> {:ok, o} end] - }, - { - Pleroma.Web.ActivityPub.ActivityPub, - [], - [persist: fn o, m -> {:ok, o, m} end] - }, - { - Pleroma.Web.ActivityPub.SideEffects, - [], - [ - handle: fn o, m -> {:ok, o, m} end, - handle_after_transaction: fn m -> m end - ] - }, - { - Pleroma.Web.Federator, - [], - [publish: fn _o -> :ok end] - } - ]) do - assert {:ok, ^activity, ^meta} = - Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity, meta) - - assert_called(Pleroma.Web.ActivityPub.ObjectValidator.validate(activity, meta)) - assert_called(Pleroma.Web.ActivityPub.MRF.filter(activity)) - assert_called(Pleroma.Web.ActivityPub.ActivityPub.persist(activity, meta)) - assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta)) - assert_called(Pleroma.Web.Federator.publish(activity)) - end - end - - test "it goes through validation, filtering, persisting, side effects without federation for remote activities" do - activity = insert(:note_activity) - meta = [local: false] - - with_mocks([ - {Pleroma.Web.ActivityPub.ObjectValidator, [], [validate: fn o, m -> {:ok, o, m} end]}, - { - Pleroma.Web.ActivityPub.MRF, - [], - [filter: fn o -> {:ok, o} end] - }, - { - Pleroma.Web.ActivityPub.ActivityPub, - [], - [persist: fn o, m -> {:ok, o, m} end] - }, - { - Pleroma.Web.ActivityPub.SideEffects, - [], - [handle: fn o, m -> {:ok, o, m} end, handle_after_transaction: fn m -> m end] - }, - { - Pleroma.Web.Federator, - [], - [] - } - ]) do - assert {:ok, ^activity, ^meta} = - Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity, meta) - - assert_called(Pleroma.Web.ActivityPub.ObjectValidator.validate(activity, meta)) - assert_called(Pleroma.Web.ActivityPub.MRF.filter(activity)) - assert_called(Pleroma.Web.ActivityPub.ActivityPub.persist(activity, meta)) - assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta)) - end - end - - test "it goes through validation, filtering, persisting, side effects without federation for local activities if federation is deactivated" do - clear_config([:instance, :federating], false) - - activity = insert(:note_activity) - meta = [local: true] - - with_mocks([ - {Pleroma.Web.ActivityPub.ObjectValidator, [], [validate: fn o, m -> {:ok, o, m} end]}, - { - Pleroma.Web.ActivityPub.MRF, - [], - [filter: fn o -> {:ok, o} end] - }, - { - Pleroma.Web.ActivityPub.ActivityPub, - [], - [persist: fn o, m -> {:ok, o, m} end] - }, - { - Pleroma.Web.ActivityPub.SideEffects, - [], - [handle: fn o, m -> {:ok, o, m} end, handle_after_transaction: fn m -> m end] - }, - { - Pleroma.Web.Federator, - [], - [] - } - ]) do - assert {:ok, ^activity, ^meta} = - Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity, meta) - - assert_called(Pleroma.Web.ActivityPub.ObjectValidator.validate(activity, meta)) - assert_called(Pleroma.Web.ActivityPub.MRF.filter(activity)) - assert_called(Pleroma.Web.ActivityPub.ActivityPub.persist(activity, meta)) - assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta)) - end - end - end -end diff --git a/test/web/activity_pub/publisher_test.exs b/test/web/activity_pub/publisher_test.exs deleted file mode 100644 index b9388b966..000000000 --- a/test/web/activity_pub/publisher_test.exs +++ /dev/null @@ -1,365 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.PublisherTest do - use Pleroma.Web.ConnCase - - import ExUnit.CaptureLog - import Pleroma.Factory - import Tesla.Mock - import Mock - - alias Pleroma.Activity - alias Pleroma.Instances - alias Pleroma.Object - alias Pleroma.Web.ActivityPub.Publisher - alias Pleroma.Web.CommonAPI - - @as_public "https://www.w3.org/ns/activitystreams#Public" - - setup do - mock(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - - setup_all do: clear_config([:instance, :federating], true) - - describe "gather_webfinger_links/1" do - test "it returns links" do - user = insert(:user) - - expected_links = [ - %{"href" => user.ap_id, "rel" => "self", "type" => "application/activity+json"}, - %{ - "href" => user.ap_id, - "rel" => "self", - "type" => "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"" - }, - %{ - "rel" => "http://ostatus.org/schema/1.0/subscribe", - "template" => "#{Pleroma.Web.base_url()}/ostatus_subscribe?acct={uri}" - } - ] - - assert expected_links == Publisher.gather_webfinger_links(user) - end - end - - describe "determine_inbox/2" do - test "it returns sharedInbox for messages involving as:Public in to" do - user = insert(:user, %{shared_inbox: "http://example.com/inbox"}) - - activity = %Activity{ - data: %{"to" => [@as_public], "cc" => [user.follower_address]} - } - - assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox" - end - - test "it returns sharedInbox for messages involving as:Public in cc" do - user = insert(:user, %{shared_inbox: "http://example.com/inbox"}) - - activity = %Activity{ - data: %{"cc" => [@as_public], "to" => [user.follower_address]} - } - - assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox" - end - - test "it returns sharedInbox for messages involving multiple recipients in to" do - user = insert(:user, %{shared_inbox: "http://example.com/inbox"}) - user_two = insert(:user) - user_three = insert(:user) - - activity = %Activity{ - data: %{"cc" => [], "to" => [user.ap_id, user_two.ap_id, user_three.ap_id]} - } - - assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox" - end - - test "it returns sharedInbox for messages involving multiple recipients in cc" do - user = insert(:user, %{shared_inbox: "http://example.com/inbox"}) - user_two = insert(:user) - user_three = insert(:user) - - activity = %Activity{ - data: %{"to" => [], "cc" => [user.ap_id, user_two.ap_id, user_three.ap_id]} - } - - assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox" - end - - test "it returns sharedInbox for messages involving multiple recipients in total" do - user = - insert(:user, %{ - shared_inbox: "http://example.com/inbox", - inbox: "http://example.com/personal-inbox" - }) - - user_two = insert(:user) - - activity = %Activity{ - data: %{"to" => [user_two.ap_id], "cc" => [user.ap_id]} - } - - assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox" - end - - test "it returns inbox for messages involving single recipients in total" do - user = - insert(:user, %{ - shared_inbox: "http://example.com/inbox", - inbox: "http://example.com/personal-inbox" - }) - - activity = %Activity{ - data: %{"to" => [user.ap_id], "cc" => []} - } - - assert Publisher.determine_inbox(activity, user) == "http://example.com/personal-inbox" - end - end - - describe "publish_one/1" do - test "publish to url with with different ports" do - inbox80 = "http://42.site/users/nick1/inbox" - inbox42 = "http://42.site:42/users/nick1/inbox" - - mock(fn - %{method: :post, url: "http://42.site:42/users/nick1/inbox"} -> - {:ok, %Tesla.Env{status: 200, body: "port 42"}} - - %{method: :post, url: "http://42.site/users/nick1/inbox"} -> - {:ok, %Tesla.Env{status: 200, body: "port 80"}} - end) - - actor = insert(:user) - - assert {:ok, %{body: "port 42"}} = - Publisher.publish_one(%{ - inbox: inbox42, - json: "{}", - actor: actor, - id: 1, - unreachable_since: true - }) - - assert {:ok, %{body: "port 80"}} = - Publisher.publish_one(%{ - inbox: inbox80, - json: "{}", - actor: actor, - id: 1, - unreachable_since: true - }) - end - - test_with_mock "calls `Instances.set_reachable` on successful federation if `unreachable_since` is not specified", - Instances, - [:passthrough], - [] do - actor = insert(:user) - inbox = "http://200.site/users/nick1/inbox" - - assert {:ok, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1}) - assert called(Instances.set_reachable(inbox)) - end - - test_with_mock "calls `Instances.set_reachable` on successful federation if `unreachable_since` is set", - Instances, - [:passthrough], - [] do - actor = insert(:user) - inbox = "http://200.site/users/nick1/inbox" - - assert {:ok, _} = - Publisher.publish_one(%{ - inbox: inbox, - json: "{}", - actor: actor, - id: 1, - unreachable_since: NaiveDateTime.utc_now() - }) - - assert called(Instances.set_reachable(inbox)) - end - - test_with_mock "does NOT call `Instances.set_reachable` on successful federation if `unreachable_since` is nil", - Instances, - [:passthrough], - [] do - actor = insert(:user) - inbox = "http://200.site/users/nick1/inbox" - - assert {:ok, _} = - Publisher.publish_one(%{ - inbox: inbox, - json: "{}", - actor: actor, - id: 1, - unreachable_since: nil - }) - - refute called(Instances.set_reachable(inbox)) - end - - test_with_mock "calls `Instances.set_unreachable` on target inbox on non-2xx HTTP response code", - Instances, - [:passthrough], - [] do - actor = insert(:user) - inbox = "http://404.site/users/nick1/inbox" - - assert {:error, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1}) - - assert called(Instances.set_unreachable(inbox)) - end - - test_with_mock "it calls `Instances.set_unreachable` on target inbox on request error of any kind", - Instances, - [:passthrough], - [] do - actor = insert(:user) - inbox = "http://connrefused.site/users/nick1/inbox" - - assert capture_log(fn -> - assert {:error, _} = - Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1}) - end) =~ "connrefused" - - assert called(Instances.set_unreachable(inbox)) - end - - test_with_mock "does NOT call `Instances.set_unreachable` if target is reachable", - Instances, - [:passthrough], - [] do - actor = insert(:user) - inbox = "http://200.site/users/nick1/inbox" - - assert {:ok, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1}) - - refute called(Instances.set_unreachable(inbox)) - end - - test_with_mock "does NOT call `Instances.set_unreachable` if target instance has non-nil `unreachable_since`", - Instances, - [:passthrough], - [] do - actor = insert(:user) - inbox = "http://connrefused.site/users/nick1/inbox" - - assert capture_log(fn -> - assert {:error, _} = - Publisher.publish_one(%{ - inbox: inbox, - json: "{}", - actor: actor, - id: 1, - unreachable_since: NaiveDateTime.utc_now() - }) - end) =~ "connrefused" - - refute called(Instances.set_unreachable(inbox)) - end - end - - describe "publish/2" do - test_with_mock "publishes an activity with BCC to all relevant peers.", - Pleroma.Web.Federator.Publisher, - [:passthrough], - [] do - follower = - insert(:user, %{ - local: false, - inbox: "https://domain.com/users/nick1/inbox", - ap_enabled: true - }) - - actor = insert(:user, follower_address: follower.ap_id) - user = insert(:user) - - {:ok, _follower_one} = Pleroma.User.follow(follower, actor) - actor = refresh_record(actor) - - note_activity = - insert(:note_activity, - recipients: [follower.ap_id], - data_attrs: %{"bcc" => [user.ap_id]} - ) - - res = Publisher.publish(actor, note_activity) - assert res == :ok - - assert called( - Pleroma.Web.Federator.Publisher.enqueue_one(Publisher, %{ - inbox: "https://domain.com/users/nick1/inbox", - actor_id: actor.id, - id: note_activity.data["id"] - }) - ) - end - - test_with_mock "publishes a delete activity to peers who signed fetch requests to the create acitvity/object.", - Pleroma.Web.Federator.Publisher, - [:passthrough], - [] do - fetcher = - insert(:user, - local: false, - inbox: "https://domain.com/users/nick1/inbox", - ap_enabled: true - ) - - another_fetcher = - insert(:user, - local: false, - inbox: "https://domain2.com/users/nick1/inbox", - ap_enabled: true - ) - - actor = insert(:user) - - note_activity = insert(:note_activity, user: actor) - object = Object.normalize(note_activity) - - activity_path = String.trim_leading(note_activity.data["id"], Pleroma.Web.Endpoint.url()) - object_path = String.trim_leading(object.data["id"], Pleroma.Web.Endpoint.url()) - - build_conn() - |> put_req_header("accept", "application/activity+json") - |> assign(:user, fetcher) - |> get(object_path) - |> json_response(200) - - build_conn() - |> put_req_header("accept", "application/activity+json") - |> assign(:user, another_fetcher) - |> get(activity_path) - |> json_response(200) - - {:ok, delete} = CommonAPI.delete(note_activity.id, actor) - - res = Publisher.publish(actor, delete) - assert res == :ok - - assert called( - Pleroma.Web.Federator.Publisher.enqueue_one(Publisher, %{ - inbox: "https://domain.com/users/nick1/inbox", - actor_id: actor.id, - id: delete.data["id"] - }) - ) - - assert called( - Pleroma.Web.Federator.Publisher.enqueue_one(Publisher, %{ - inbox: "https://domain2.com/users/nick1/inbox", - actor_id: actor.id, - id: delete.data["id"] - }) - ) - end - end -end diff --git a/test/web/activity_pub/relay_test.exs b/test/web/activity_pub/relay_test.exs deleted file mode 100644 index 9d657ac4f..000000000 --- a/test/web/activity_pub/relay_test.exs +++ /dev/null @@ -1,128 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.RelayTest do - use Pleroma.DataCase - - alias Pleroma.Activity - alias Pleroma.User - alias Pleroma.Web.ActivityPub.Relay - alias Pleroma.Web.CommonAPI - - import ExUnit.CaptureLog - import Pleroma.Factory - import Mock - - test "gets an actor for the relay" do - user = Relay.get_actor() - assert user.ap_id == "#{Pleroma.Web.Endpoint.url()}/relay" - end - - test "relay actor is invisible" do - user = Relay.get_actor() - assert User.invisible?(user) - end - - describe "follow/1" do - test "returns errors when user not found" do - assert capture_log(fn -> - {:error, _} = Relay.follow("test-ap-id") - end) =~ "Could not decode user at fetch" - end - - test "returns activity" do - user = insert(:user) - service_actor = Relay.get_actor() - assert {:ok, %Activity{} = activity} = Relay.follow(user.ap_id) - assert activity.actor == "#{Pleroma.Web.Endpoint.url()}/relay" - assert user.ap_id in activity.recipients - assert activity.data["type"] == "Follow" - assert activity.data["actor"] == service_actor.ap_id - assert activity.data["object"] == user.ap_id - end - end - - describe "unfollow/1" do - test "returns errors when user not found" do - assert capture_log(fn -> - {:error, _} = Relay.unfollow("test-ap-id") - end) =~ "Could not decode user at fetch" - end - - test "returns activity" do - user = insert(:user) - service_actor = Relay.get_actor() - CommonAPI.follow(service_actor, user) - assert "#{user.ap_id}/followers" in User.following(service_actor) - assert {:ok, %Activity{} = activity} = Relay.unfollow(user.ap_id) - assert activity.actor == "#{Pleroma.Web.Endpoint.url()}/relay" - assert user.ap_id in activity.recipients - assert activity.data["type"] == "Undo" - assert activity.data["actor"] == service_actor.ap_id - assert activity.data["to"] == [user.ap_id] - refute "#{user.ap_id}/followers" in User.following(service_actor) - end - end - - describe "publish/1" do - setup do: clear_config([:instance, :federating]) - - test "returns error when activity not `Create` type" do - activity = insert(:like_activity) - assert Relay.publish(activity) == {:error, "Not implemented"} - end - - @tag capture_log: true - test "returns error when activity not public" do - activity = insert(:direct_note_activity) - assert Relay.publish(activity) == {:error, false} - end - - test "returns error when object is unknown" do - activity = - insert(:note_activity, - data: %{ - "type" => "Create", - "object" => "http://mastodon.example.org/eee/99541947525187367" - } - ) - - Tesla.Mock.mock(fn - %{method: :get, url: "http://mastodon.example.org/eee/99541947525187367"} -> - %Tesla.Env{status: 500, body: ""} - end) - - assert capture_log(fn -> - assert Relay.publish(activity) == {:error, false} - end) =~ "[error] error: false" - end - - test_with_mock "returns announce activity and publish to federate", - Pleroma.Web.Federator, - [:passthrough], - [] do - clear_config([:instance, :federating], true) - service_actor = Relay.get_actor() - note = insert(:note_activity) - assert {:ok, %Activity{} = activity} = Relay.publish(note) - assert activity.data["type"] == "Announce" - assert activity.data["actor"] == service_actor.ap_id - assert activity.data["to"] == [service_actor.follower_address] - assert called(Pleroma.Web.Federator.publish(activity)) - end - - test_with_mock "returns announce activity and not publish to federate", - Pleroma.Web.Federator, - [:passthrough], - [] do - clear_config([:instance, :federating], false) - service_actor = Relay.get_actor() - note = insert(:note_activity) - assert {:ok, %Activity{} = activity} = Relay.publish(note) - assert activity.data["type"] == "Announce" - assert activity.data["actor"] == service_actor.ap_id - refute called(Pleroma.Web.Federator.publish(activity)) - end - end -end diff --git a/test/web/activity_pub/side_effects_test.exs b/test/web/activity_pub/side_effects_test.exs deleted file mode 100644 index 9efbaad04..000000000 --- a/test/web/activity_pub/side_effects_test.exs +++ /dev/null @@ -1,639 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.SideEffectsTest do - use Oban.Testing, repo: Pleroma.Repo - use Pleroma.DataCase - - alias Pleroma.Activity - alias Pleroma.Chat - alias Pleroma.Chat.MessageReference - alias Pleroma.Notification - alias Pleroma.Object - alias Pleroma.Repo - alias Pleroma.Tests.ObanHelpers - alias Pleroma.User - alias Pleroma.Web.ActivityPub.ActivityPub - alias Pleroma.Web.ActivityPub.Builder - alias Pleroma.Web.ActivityPub.SideEffects - alias Pleroma.Web.CommonAPI - - import ExUnit.CaptureLog - import Mock - import Pleroma.Factory - - 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) - other_user = insert(:user) - - {:ok, op} = CommonAPI.post(other_user, %{status: "big oof"}) - {:ok, post} = CommonAPI.post(user, %{status: "hey", in_reply_to_id: op}) - {:ok, favorite} = CommonAPI.favorite(user, post.id) - object = Object.normalize(post) - {:ok, delete_data, _meta} = Builder.delete(user, object.data["id"]) - {:ok, delete_user_data, _meta} = Builder.delete(user, user.ap_id) - {:ok, delete, _meta} = ActivityPub.persist(delete_data, local: true) - {:ok, delete_user, _meta} = ActivityPub.persist(delete_user_data, local: true) - - %{ - user: user, - delete: delete, - post: post, - object: object, - delete_user: delete_user, - op: op, - favorite: favorite - } - end - - test "it handles object deletions", %{ - delete: delete, - post: post, - object: object, - user: user, - op: op, - favorite: favorite - } do - with_mock Pleroma.Web.ActivityPub.ActivityPub, [:passthrough], - stream_out: fn _ -> nil end, - stream_out_participations: fn _, _ -> nil end do - {:ok, delete, _} = SideEffects.handle(delete) - user = User.get_cached_by_ap_id(object.data["actor"]) - - assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out(delete)) - assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out_participations(object, user)) - end - - object = Object.get_by_id(object.id) - assert object.data["type"] == "Tombstone" - refute Activity.get_by_id(post.id) - refute Activity.get_by_id(favorite.id) - - user = User.get_by_id(user.id) - assert user.note_count == 0 - - object = Object.normalize(op.data["object"], false) - - assert object.data["repliesCount"] == 0 - end - - test "it handles object deletions when the object itself has been pruned", %{ - delete: delete, - post: post, - object: object, - user: user, - op: op - } do - with_mock Pleroma.Web.ActivityPub.ActivityPub, [:passthrough], - stream_out: fn _ -> nil end, - stream_out_participations: fn _, _ -> nil end do - {:ok, delete, _} = SideEffects.handle(delete) - user = User.get_cached_by_ap_id(object.data["actor"]) - - assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out(delete)) - assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out_participations(object, user)) - end - - object = Object.get_by_id(object.id) - assert object.data["type"] == "Tombstone" - refute Activity.get_by_id(post.id) - - user = User.get_by_id(user.id) - assert user.note_count == 0 - - object = Object.normalize(op.data["object"], false) - - assert object.data["repliesCount"] == 0 - end - - test "it handles user deletions", %{delete_user: delete, user: user} do - {:ok, _delete, _} = SideEffects.handle(delete) - ObanHelpers.perform_all() - - assert User.get_cached_by_ap_id(user.ap_id).deactivated - end - - test "it logs issues with objects deletion", %{ - delete: delete, - object: object - } do - {:ok, object} = - object - |> Object.change(%{data: Map.delete(object.data, "actor")}) - |> Repo.update() - - Object.invalid_object_cache(object) - - assert capture_log(fn -> - {:error, :no_object_actor} = SideEffects.handle(delete) - end) =~ "object doesn't have an actor" - end - end - - describe "EmojiReact objects" do - setup do - poster = insert(:user) - user = insert(:user) - - {:ok, post} = CommonAPI.post(poster, %{status: "hey"}) - - {:ok, emoji_react_data, []} = Builder.emoji_react(user, post.object, "👌") - {:ok, emoji_react, _meta} = ActivityPub.persist(emoji_react_data, local: true) - - %{emoji_react: emoji_react, user: user, poster: poster} - end - - test "adds the reaction to the object", %{emoji_react: emoji_react, user: user} do - {:ok, emoji_react, _} = SideEffects.handle(emoji_react) - object = Object.get_by_ap_id(emoji_react.data["object"]) - - assert object.data["reaction_count"] == 1 - assert ["👌", [user.ap_id]] in object.data["reactions"] - end - - test "creates a notification", %{emoji_react: emoji_react, poster: poster} do - {:ok, emoji_react, _} = SideEffects.handle(emoji_react) - assert Repo.get_by(Notification, user_id: poster.id, activity_id: emoji_react.id) - end - end - - describe "delete users with confirmation pending" do - setup do - user = insert(:user, confirmation_pending: true) - {:ok, delete_user_data, _meta} = Builder.delete(user, user.ap_id) - {:ok, delete_user, _meta} = ActivityPub.persist(delete_user_data, local: true) - {:ok, delete: delete_user, user: user} - end - - test "when activation is not required", %{delete: delete, user: user} do - clear_config([:instance, :account_activation_required], false) - {:ok, _, _} = SideEffects.handle(delete) - ObanHelpers.perform_all() - - assert User.get_cached_by_id(user.id).deactivated - end - - test "when activation is required", %{delete: delete, user: user} do - clear_config([:instance, :account_activation_required], true) - {:ok, _, _} = SideEffects.handle(delete) - ObanHelpers.perform_all() - - refute User.get_cached_by_id(user.id) - end - end - - describe "Undo objects" do - setup do - poster = insert(:user) - user = insert(:user) - {:ok, post} = CommonAPI.post(poster, %{status: "hey"}) - {: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} = CommonAPI.block(user, poster) - - {:ok, undo_data, _meta} = Builder.undo(user, like) - {:ok, like_undo, _meta} = ActivityPub.persist(undo_data, local: true) - - {:ok, undo_data, _meta} = Builder.undo(user, reaction) - {:ok, reaction_undo, _meta} = ActivityPub.persist(undo_data, local: true) - - {:ok, undo_data, _meta} = Builder.undo(user, announce) - {:ok, announce_undo, _meta} = ActivityPub.persist(undo_data, local: true) - - {:ok, undo_data, _meta} = Builder.undo(user, block) - {:ok, block_undo, _meta} = ActivityPub.persist(undo_data, local: true) - - %{ - like_undo: like_undo, - post: post, - like: like, - reaction_undo: reaction_undo, - reaction: reaction, - announce_undo: announce_undo, - announce: announce, - block_undo: block_undo, - block: block, - poster: poster, - user: user - } - end - - test "deletes the original block", %{ - block_undo: block_undo, - block: block - } do - {:ok, _block_undo, _meta} = SideEffects.handle(block_undo) - - refute Activity.get_by_id(block.id) - end - - test "unblocks the blocked user", %{block_undo: block_undo, block: block} do - blocker = User.get_by_ap_id(block.data["actor"]) - blocked = User.get_by_ap_id(block.data["object"]) - - {:ok, _block_undo, _} = SideEffects.handle(block_undo) - refute User.blocks?(blocker, blocked) - end - - test "an announce undo removes the announce from the object", %{ - announce_undo: announce_undo, - post: post - } do - {:ok, _announce_undo, _} = SideEffects.handle(announce_undo) - - object = Object.get_by_ap_id(post.data["object"]) - - assert object.data["announcement_count"] == 0 - assert object.data["announcements"] == [] - end - - test "deletes the original announce", %{announce_undo: announce_undo, announce: announce} do - {:ok, _announce_undo, _} = SideEffects.handle(announce_undo) - refute Activity.get_by_id(announce.id) - end - - test "a reaction undo removes the reaction from the object", %{ - reaction_undo: reaction_undo, - post: post - } do - {:ok, _reaction_undo, _} = SideEffects.handle(reaction_undo) - - object = Object.get_by_ap_id(post.data["object"]) - - assert object.data["reaction_count"] == 0 - assert object.data["reactions"] == [] - end - - test "deletes the original reaction", %{reaction_undo: reaction_undo, reaction: reaction} do - {:ok, _reaction_undo, _} = SideEffects.handle(reaction_undo) - refute Activity.get_by_id(reaction.id) - end - - test "a like undo removes the like from the object", %{like_undo: like_undo, post: post} do - {:ok, _like_undo, _} = SideEffects.handle(like_undo) - - object = Object.get_by_ap_id(post.data["object"]) - - assert object.data["like_count"] == 0 - assert object.data["likes"] == [] - end - - test "deletes the original like", %{like_undo: like_undo, like: like} do - {:ok, _like_undo, _} = SideEffects.handle(like_undo) - refute Activity.get_by_id(like.id) - end - end - - describe "like objects" do - setup do - poster = insert(:user) - user = insert(:user) - {:ok, post} = CommonAPI.post(poster, %{status: "hey"}) - - {:ok, like_data, _meta} = Builder.like(user, post.object) - {:ok, like, _meta} = ActivityPub.persist(like_data, local: true) - - %{like: like, user: user, poster: poster} - end - - test "add the like to the original object", %{like: like, user: user} do - {:ok, like, _} = SideEffects.handle(like) - object = Object.get_by_ap_id(like.data["object"]) - assert object.data["like_count"] == 1 - assert user.ap_id in object.data["likes"] - end - - test "creates a notification", %{like: like, poster: poster} do - {:ok, like, _} = SideEffects.handle(like) - assert Repo.get_by(Notification, user_id: poster.id, activity_id: like.id) - 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) - user = insert(:user) - {:ok, post} = CommonAPI.post(poster, %{status: "hey"}) - {:ok, private_post} = CommonAPI.post(poster, %{status: "hey", visibility: "private"}) - - {:ok, announce_data, _meta} = Builder.announce(user, post.object, public: true) - - {:ok, private_announce_data, _meta} = - Builder.announce(user, private_post.object, public: false) - - {:ok, relay_announce_data, _meta} = - Builder.announce(Pleroma.Web.ActivityPub.Relay.get_actor(), post.object, public: true) - - {:ok, announce, _meta} = ActivityPub.persist(announce_data, local: true) - {:ok, private_announce, _meta} = ActivityPub.persist(private_announce_data, local: true) - {:ok, relay_announce, _meta} = ActivityPub.persist(relay_announce_data, local: true) - - %{ - announce: announce, - user: user, - poster: poster, - private_announce: private_announce, - relay_announce: relay_announce - } - end - - test "adds the announce to the original object", %{announce: announce, user: user} do - {:ok, announce, _} = SideEffects.handle(announce) - object = Object.get_by_ap_id(announce.data["object"]) - assert object.data["announcement_count"] == 1 - assert user.ap_id in object.data["announcements"] - end - - test "does not add the announce to the original object if the actor is a service actor", %{ - relay_announce: announce - } do - {:ok, announce, _} = SideEffects.handle(announce) - object = Object.get_by_ap_id(announce.data["object"]) - assert object.data["announcement_count"] == nil - end - - test "creates a notification", %{announce: announce, poster: poster} do - {:ok, announce, _} = SideEffects.handle(announce) - assert Repo.get_by(Notification, user_id: poster.id, activity_id: announce.id) - end - - test "it streams out the announce", %{announce: announce} do - with_mocks([ - { - Pleroma.Web.Streamer, - [], - [ - stream: fn _, _ -> nil end - ] - }, - { - Pleroma.Web.Push, - [], - [ - send: fn _ -> nil end - ] - } - ]) do - {:ok, announce, _} = SideEffects.handle(announce) - - assert called( - Pleroma.Web.Streamer.stream(["user", "list", "public", "public:local"], announce) - ) - - assert called(Pleroma.Web.Push.send(:_)) - end - end - end -end diff --git a/test/web/activity_pub/transmogrifier/accept_handling_test.exs b/test/web/activity_pub/transmogrifier/accept_handling_test.exs deleted file mode 100644 index 77d468f5c..000000000 --- a/test/web/activity_pub/transmogrifier/accept_handling_test.exs +++ /dev/null @@ -1,91 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.Transmogrifier.AcceptHandlingTest do - use Pleroma.DataCase - - alias Pleroma.User - alias Pleroma.Web.ActivityPub.Transmogrifier - alias Pleroma.Web.CommonAPI - - import Pleroma.Factory - - test "it works for incoming accepts which were pre-accepted" do - follower = insert(:user) - followed = insert(:user) - - {:ok, follower} = User.follow(follower, followed) - assert User.following?(follower, followed) == true - - {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed) - - accept_data = - File.read!("test/fixtures/mastodon-accept-activity.json") - |> Poison.decode!() - |> Map.put("actor", followed.ap_id) - - object = - accept_data["object"] - |> Map.put("actor", follower.ap_id) - |> Map.put("id", follow_activity.data["id"]) - - accept_data = Map.put(accept_data, "object", object) - - {:ok, activity} = Transmogrifier.handle_incoming(accept_data) - refute activity.local - - assert activity.data["object"] == follow_activity.data["id"] - - assert activity.data["id"] == accept_data["id"] - - follower = User.get_cached_by_id(follower.id) - - assert User.following?(follower, followed) == true - end - - test "it works for incoming accepts which are referenced by IRI only" do - follower = insert(:user) - followed = insert(:user, locked: true) - - {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed) - - accept_data = - File.read!("test/fixtures/mastodon-accept-activity.json") - |> Poison.decode!() - |> Map.put("actor", followed.ap_id) - |> Map.put("object", follow_activity.data["id"]) - - {:ok, activity} = Transmogrifier.handle_incoming(accept_data) - assert activity.data["object"] == follow_activity.data["id"] - - follower = User.get_cached_by_id(follower.id) - - assert User.following?(follower, followed) == true - - follower = User.get_by_id(follower.id) - assert follower.following_count == 1 - - followed = User.get_by_id(followed.id) - assert followed.follower_count == 1 - end - - test "it fails for incoming accepts which cannot be correlated" do - follower = insert(:user) - followed = insert(:user, locked: true) - - accept_data = - File.read!("test/fixtures/mastodon-accept-activity.json") - |> Poison.decode!() - |> Map.put("actor", followed.ap_id) - - accept_data = - Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id)) - - {:error, _} = Transmogrifier.handle_incoming(accept_data) - - follower = User.get_cached_by_id(follower.id) - - refute User.following?(follower, followed) == true - end -end diff --git a/test/web/activity_pub/transmogrifier/announce_handling_test.exs b/test/web/activity_pub/transmogrifier/announce_handling_test.exs deleted file mode 100644 index e895636b5..000000000 --- a/test/web/activity_pub/transmogrifier/announce_handling_test.exs +++ /dev/null @@ -1,172 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.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/answer_handling_test.exs b/test/web/activity_pub/transmogrifier/answer_handling_test.exs deleted file mode 100644 index 0f6605c3f..000000000 --- a/test/web/activity_pub/transmogrifier/answer_handling_test.exs +++ /dev/null @@ -1,78 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.Transmogrifier.AnswerHandlingTest do - use Pleroma.DataCase - - alias Pleroma.Activity - alias Pleroma.Object - alias Pleroma.Web.ActivityPub.Transmogrifier - alias Pleroma.Web.CommonAPI - - import Pleroma.Factory - - setup_all do - Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - - test "incoming, rewrites Note to Answer and increments vote counters" do - user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{ - status: "suya...", - poll: %{options: ["suya", "suya.", "suya.."], expires_in: 10} - }) - - object = Object.normalize(activity) - - data = - File.read!("test/fixtures/mastodon-vote.json") - |> Poison.decode!() - |> Kernel.put_in(["to"], user.ap_id) - |> Kernel.put_in(["object", "inReplyTo"], object.data["id"]) - |> Kernel.put_in(["object", "to"], user.ap_id) - - {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) - answer_object = Object.normalize(activity) - assert answer_object.data["type"] == "Answer" - assert answer_object.data["inReplyTo"] == object.data["id"] - - new_object = Object.get_by_ap_id(object.data["id"]) - assert new_object.data["replies_count"] == object.data["replies_count"] - - assert Enum.any?( - new_object.data["oneOf"], - fn - %{"name" => "suya..", "replies" => %{"totalItems" => 1}} -> true - _ -> false - end - ) - end - - test "outgoing, rewrites Answer to Note" do - user = insert(:user) - - {:ok, poll_activity} = - CommonAPI.post(user, %{ - status: "suya...", - poll: %{options: ["suya", "suya.", "suya.."], expires_in: 10} - }) - - poll_object = Object.normalize(poll_activity) - # TODO: Replace with CommonAPI vote creation when implemented - data = - File.read!("test/fixtures/mastodon-vote.json") - |> Poison.decode!() - |> Kernel.put_in(["to"], user.ap_id) - |> Kernel.put_in(["object", "inReplyTo"], poll_object.data["id"]) - |> Kernel.put_in(["object", "to"], user.ap_id) - - {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) - {:ok, data} = Transmogrifier.prepare_outgoing(activity.data) - - assert data["object"]["type"] == "Note" - end -end diff --git a/test/web/activity_pub/transmogrifier/audio_handling_test.exs b/test/web/activity_pub/transmogrifier/audio_handling_test.exs deleted file mode 100644 index 0636d00c5..000000000 --- a/test/web/activity_pub/transmogrifier/audio_handling_test.exs +++ /dev/null @@ -1,83 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.Transmogrifier.AudioHandlingTest do - use Oban.Testing, repo: Pleroma.Repo - use Pleroma.DataCase - - alias Pleroma.Activity - alias Pleroma.Object - alias Pleroma.Web.ActivityPub.Transmogrifier - - import Pleroma.Factory - - test "it works for incoming listens" do - _user = insert(:user, ap_id: "http://mastodon.example.org/users/admin") - - data = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "type" => "Listen", - "id" => "http://mastodon.example.org/users/admin/listens/1234/activity", - "actor" => "http://mastodon.example.org/users/admin", - "object" => %{ - "type" => "Audio", - "id" => "http://mastodon.example.org/users/admin/listens/1234", - "attributedTo" => "http://mastodon.example.org/users/admin", - "title" => "lain radio episode 1", - "artist" => "lain", - "album" => "lain radio", - "length" => 180_000 - } - } - - {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) - - object = Object.normalize(activity) - - assert object.data["title"] == "lain radio episode 1" - assert object.data["artist"] == "lain" - assert object.data["album"] == "lain radio" - assert object.data["length"] == 180_000 - end - - test "Funkwhale Audio object" do - Tesla.Mock.mock(fn - %{url: "https://channels.tests.funkwhale.audio/federation/actors/compositions"} -> - %Tesla.Env{ - status: 200, - body: File.read!("test/fixtures/tesla_mock/funkwhale_channel.json") - } - end) - - data = File.read!("test/fixtures/tesla_mock/funkwhale_create_audio.json") |> Poison.decode!() - - {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) - - assert object = Object.normalize(activity, false) - - assert object.data["to"] == ["https://www.w3.org/ns/activitystreams#Public"] - - assert object.data["cc"] == [] - - assert object.data["url"] == "https://channels.tests.funkwhale.audio/library/tracks/74" - - assert object.data["attachment"] == [ - %{ - "mediaType" => "audio/ogg", - "type" => "Link", - "name" => nil, - "url" => [ - %{ - "href" => - "https://channels.tests.funkwhale.audio/api/v1/listen/3901e5d8-0445-49d5-9711-e096cf32e515/?upload=42342395-0208-4fee-a38d-259a6dae0871&download=false", - "mediaType" => "audio/ogg", - "type" => "Link" - } - ] - } - ] - end -end diff --git a/test/web/activity_pub/transmogrifier/block_handling_test.exs b/test/web/activity_pub/transmogrifier/block_handling_test.exs deleted file mode 100644 index 71f1a0ed5..000000000 --- a/test/web/activity_pub/transmogrifier/block_handling_test.exs +++ /dev/null @@ -1,63 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.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 deleted file mode 100644 index 31274c067..000000000 --- a/test/web/activity_pub/transmogrifier/chat_message_test.exs +++ /dev/null @@ -1,171 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.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 doesn't work for deactivated users" do - data = - File.read!("test/fixtures/create-chat-message.json") - |> Poison.decode!() - - _author = - insert(:user, - ap_id: data["actor"], - local: false, - last_refreshed_at: DateTime.utc_now(), - deactivated: true - ) - - _recipient = insert(:user, ap_id: List.first(data["to"]), local: true) - - assert {:error, _} = Transmogrifier.handle_incoming(data) - end - - test "it inserts it and creates a chat" do - data = - File.read!("test/fixtures/create-chat-message.json") - |> 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/delete_handling_test.exs b/test/web/activity_pub/transmogrifier/delete_handling_test.exs deleted file mode 100644 index c9a53918c..000000000 --- a/test/web/activity_pub/transmogrifier/delete_handling_test.exs +++ /dev/null @@ -1,114 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.Transmogrifier.DeleteHandlingTest do - use Oban.Testing, repo: Pleroma.Repo - use Pleroma.DataCase - - alias Pleroma.Activity - alias Pleroma.Object - alias Pleroma.Tests.ObanHelpers - alias Pleroma.User - alias Pleroma.Web.ActivityPub.Transmogrifier - - import Pleroma.Factory - - setup_all do - Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - - test "it works for incoming deletes" do - activity = insert(:note_activity) - deleting_user = insert(:user) - - data = - File.read!("test/fixtures/mastodon-delete.json") - |> Poison.decode!() - |> Map.put("actor", deleting_user.ap_id) - |> put_in(["object", "id"], activity.data["object"]) - - {:ok, %Activity{actor: actor, local: false, data: %{"id" => id}}} = - Transmogrifier.handle_incoming(data) - - assert id == data["id"] - - # We delete the Create activity because we base our timelines on it. - # This should be changed after we unify objects and activities - refute Activity.get_by_id(activity.id) - assert actor == deleting_user.ap_id - - # Objects are replaced by a tombstone object. - object = Object.normalize(activity.data["object"]) - assert object.data["type"] == "Tombstone" - end - - test "it works for incoming when the object has been pruned" do - activity = insert(:note_activity) - - {:ok, object} = - Object.normalize(activity.data["object"]) - |> Repo.delete() - - Cachex.del(:object_cache, "object:#{object.data["id"]}") - - deleting_user = insert(:user) - - data = - File.read!("test/fixtures/mastodon-delete.json") - |> Poison.decode!() - |> Map.put("actor", deleting_user.ap_id) - |> put_in(["object", "id"], activity.data["object"]) - - {:ok, %Activity{actor: actor, local: false, data: %{"id" => id}}} = - Transmogrifier.handle_incoming(data) - - assert id == data["id"] - - # We delete the Create activity because we base our timelines on it. - # This should be changed after we unify objects and activities - refute Activity.get_by_id(activity.id) - assert actor == deleting_user.ap_id - end - - test "it fails for incoming deletes with spoofed origin" do - activity = insert(:note_activity) - %{ap_id: ap_id} = insert(:user, ap_id: "https://gensokyo.2hu/users/raymoo") - - data = - File.read!("test/fixtures/mastodon-delete.json") - |> Poison.decode!() - |> Map.put("actor", ap_id) - |> put_in(["object", "id"], activity.data["object"]) - - assert match?({:error, _}, Transmogrifier.handle_incoming(data)) - end - - @tag capture_log: true - test "it works for incoming user deletes" do - %{ap_id: ap_id} = insert(:user, ap_id: "http://mastodon.example.org/users/admin") - - data = - File.read!("test/fixtures/mastodon-delete-user.json") - |> Poison.decode!() - - {:ok, _} = Transmogrifier.handle_incoming(data) - ObanHelpers.perform_all() - - assert User.get_cached_by_ap_id(ap_id).deactivated - end - - test "it fails for incoming user deletes with spoofed origin" do - %{ap_id: ap_id} = insert(:user) - - data = - File.read!("test/fixtures/mastodon-delete-user.json") - |> Poison.decode!() - |> Map.put("actor", ap_id) - - assert match?({:error, _}, Transmogrifier.handle_incoming(data)) - - assert User.get_cached_by_ap_id(ap_id) - end -end diff --git a/test/web/activity_pub/transmogrifier/emoji_react_handling_test.exs b/test/web/activity_pub/transmogrifier/emoji_react_handling_test.exs deleted file mode 100644 index 0fb056b50..000000000 --- a/test/web/activity_pub/transmogrifier/emoji_react_handling_test.exs +++ /dev/null @@ -1,61 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.Transmogrifier.EmojiReactHandlingTest 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 emoji reactions" do - user = insert(:user) - other_user = insert(:user, local: false) - {:ok, activity} = CommonAPI.post(user, %{status: "hello"}) - - data = - File.read!("test/fixtures/emoji-reaction.json") - |> Poison.decode!() - |> Map.put("object", activity.data["object"]) - |> Map.put("actor", other_user.ap_id) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert data["actor"] == other_user.ap_id - assert data["type"] == "EmojiReact" - assert data["id"] == "http://mastodon.example.org/users/admin#reactions/2" - assert data["object"] == activity.data["object"] - assert data["content"] == "👌" - - object = Object.get_by_ap_id(data["object"]) - - assert object.data["reaction_count"] == 1 - assert match?([["👌", _]], object.data["reactions"]) - end - - test "it reject invalid emoji reactions" do - user = insert(:user) - other_user = insert(:user, local: false) - {:ok, activity} = CommonAPI.post(user, %{status: "hello"}) - - data = - File.read!("test/fixtures/emoji-reaction-too-long.json") - |> Poison.decode!() - |> Map.put("object", activity.data["object"]) - |> Map.put("actor", other_user.ap_id) - - assert {:error, _} = Transmogrifier.handle_incoming(data) - - data = - File.read!("test/fixtures/emoji-reaction-no-emoji.json") - |> Poison.decode!() - |> Map.put("object", activity.data["object"]) - |> Map.put("actor", other_user.ap_id) - - assert {:error, _} = Transmogrifier.handle_incoming(data) - end -end diff --git a/test/web/activity_pub/transmogrifier/event_handling_test.exs b/test/web/activity_pub/transmogrifier/event_handling_test.exs deleted file mode 100644 index 7f1ef2cbd..000000000 --- a/test/web/activity_pub/transmogrifier/event_handling_test.exs +++ /dev/null @@ -1,40 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.Transmogrifier.EventHandlingTest do - use Oban.Testing, repo: Pleroma.Repo - use Pleroma.DataCase - - alias Pleroma.Object.Fetcher - - test "Mobilizon Event object" do - Tesla.Mock.mock(fn - %{url: "https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39"} -> - %Tesla.Env{ - status: 200, - body: File.read!("test/fixtures/tesla_mock/mobilizon.org-event.json") - } - - %{url: "https://mobilizon.org/@tcit"} -> - %Tesla.Env{ - status: 200, - body: File.read!("test/fixtures/tesla_mock/mobilizon.org-user.json") - } - end) - - assert {:ok, object} = - Fetcher.fetch_object_from_id( - "https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39" - ) - - assert object.data["to"] == ["https://www.w3.org/ns/activitystreams#Public"] - assert object.data["cc"] == [] - - assert object.data["url"] == - "https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39" - - assert object.data["published"] == "2019-12-17T11:33:56Z" - assert object.data["name"] == "Mobilizon Launching Party" - end -end diff --git a/test/web/activity_pub/transmogrifier/follow_handling_test.exs b/test/web/activity_pub/transmogrifier/follow_handling_test.exs deleted file mode 100644 index 757d90941..000000000 --- a/test/web/activity_pub/transmogrifier/follow_handling_test.exs +++ /dev/null @@ -1,208 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.Transmogrifier.FollowHandlingTest do - use Pleroma.DataCase - alias Pleroma.Activity - alias Pleroma.Notification - alias Pleroma.Repo - alias Pleroma.User - alias Pleroma.Web.ActivityPub.Transmogrifier - alias Pleroma.Web.ActivityPub.Utils - - import Pleroma.Factory - import Ecto.Query - import Mock - - setup_all do - Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - - describe "handle_incoming" do - setup do: clear_config([:user, :deny_follow_blocked]) - - test "it works for osada follow request" do - user = insert(:user) - - data = - File.read!("test/fixtures/osada-follow-activity.json") - |> Poison.decode!() - |> Map.put("object", user.ap_id) - - {:ok, %Activity{data: data, local: false} = activity} = Transmogrifier.handle_incoming(data) - - assert data["actor"] == "https://apfed.club/channel/indio" - assert data["type"] == "Follow" - assert data["id"] == "https://apfed.club/follow/9" - - activity = Repo.get(Activity, activity.id) - assert activity.data["state"] == "accept" - assert User.following?(User.get_cached_by_ap_id(data["actor"]), user) - end - - test "it works for incoming follow requests" do - user = insert(:user) - - data = - File.read!("test/fixtures/mastodon-follow-activity.json") - |> Poison.decode!() - |> Map.put("object", user.ap_id) - - {:ok, %Activity{data: data, local: false} = activity} = Transmogrifier.handle_incoming(data) - - assert data["actor"] == "http://mastodon.example.org/users/admin" - assert data["type"] == "Follow" - assert data["id"] == "http://mastodon.example.org/users/admin#follows/2" - - 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 create a Follow, but not an Accept" do - user = insert(:user, locked: true) - - data = - File.read!("test/fixtures/mastodon-follow-activity.json") - |> Poison.decode!() - |> Map.put("object", user.ap_id) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert data["state"] == "pending" - - refute User.following?(User.get_cached_by_ap_id(data["actor"]), user) - - accepts = - from( - a in Activity, - where: fragment("?->>'type' = ?", a.data, "Accept") - ) - |> 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 - # This is important because the remote might have the wrong idea about the - # current follow status. This can lead to instance A thinking that x@A is - # followed by y@B, but B thinks they are not. In this case, the follow can - # never go through again because it will never get an Accept. - user = insert(:user) - - data = - File.read!("test/fixtures/mastodon-follow-activity.json") - |> Poison.decode!() - |> Map.put("object", user.ap_id) - - {:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(data) - - accepts = - from( - a in Activity, - where: fragment("?->>'type' = ?", a.data, "Accept") - ) - |> Repo.all() - - assert length(accepts) == 1 - - data = - File.read!("test/fixtures/mastodon-follow-activity.json") - |> Poison.decode!() - |> Map.put("id", String.replace(data["id"], "2", "3")) - |> Map.put("object", user.ap_id) - - {:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(data) - - accepts = - from( - a in Activity, - where: fragment("?->>'type' = ?", a.data, "Accept") - ) - |> Repo.all() - - assert length(accepts) == 2 - end - - test "it rejects incoming follow requests from blocked users when deny_follow_blocked is enabled" do - Pleroma.Config.put([:user, :deny_follow_blocked], true) - - user = insert(:user) - {:ok, target} = User.get_or_fetch("http://mastodon.example.org/users/admin") - - {:ok, _user_relationship} = User.block(user, target) - - data = - File.read!("test/fixtures/mastodon-follow-activity.json") - |> Poison.decode!() - |> Map.put("object", user.ap_id) - - {:ok, %Activity{data: %{"id" => id}}} = Transmogrifier.handle_incoming(data) - - %Activity{} = activity = Activity.get_by_ap_id(id) - - 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) - - data = - File.read!("test/fixtures/hubzilla-follow-activity.json") - |> Poison.decode!() - |> Map.put("object", user.ap_id) - |> Utils.normalize_params() - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert data["actor"] == "https://hubzilla.example.org/channel/kaniini" - assert data["type"] == "Follow" - assert data["id"] == "https://hubzilla.example.org/channel/kaniini#follows/2" - assert User.following?(User.get_cached_by_ap_id(data["actor"]), user) - end - - test "it works for incoming follows to locked account" do - pending_follower = insert(:user, ap_id: "http://mastodon.example.org/users/admin") - user = insert(:user, locked: true) - - data = - File.read!("test/fixtures/mastodon-follow-activity.json") - |> Poison.decode!() - |> Map.put("object", user.ap_id) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert data["type"] == "Follow" - assert data["object"] == user.ap_id - assert data["state"] == "pending" - assert data["actor"] == "http://mastodon.example.org/users/admin" - - assert [^pending_follower] = User.get_follow_requests(user) - end - end -end diff --git a/test/web/activity_pub/transmogrifier/like_handling_test.exs b/test/web/activity_pub/transmogrifier/like_handling_test.exs deleted file mode 100644 index 53fe1d550..000000000 --- a/test/web/activity_pub/transmogrifier/like_handling_test.exs +++ /dev/null @@ -1,78 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.Transmogrifier.LikeHandlingTest do - use Pleroma.DataCase - - alias Pleroma.Activity - alias Pleroma.Web.ActivityPub.Transmogrifier - alias Pleroma.Web.CommonAPI - - import Pleroma.Factory - - test "it works for incoming likes" do - user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "hello"}) - - data = - File.read!("test/fixtures/mastodon-like.json") - |> Poison.decode!() - |> Map.put("object", activity.data["object"]) - - _actor = insert(:user, ap_id: data["actor"], local: false) - - {:ok, %Activity{data: data, local: false} = activity} = Transmogrifier.handle_incoming(data) - - refute Enum.empty?(activity.recipients) - - assert data["actor"] == "http://mastodon.example.org/users/admin" - assert data["type"] == "Like" - assert data["id"] == "http://mastodon.example.org/users/admin#likes/2" - assert data["object"] == activity.data["object"] - end - - test "it works for incoming misskey likes, turning them into EmojiReacts" do - user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "hello"}) - - data = - File.read!("test/fixtures/misskey-like.json") - |> Poison.decode!() - |> Map.put("object", activity.data["object"]) - - _actor = insert(:user, ap_id: data["actor"], local: false) - - {:ok, %Activity{data: activity_data, local: false}} = Transmogrifier.handle_incoming(data) - - assert activity_data["actor"] == data["actor"] - assert activity_data["type"] == "EmojiReact" - assert activity_data["id"] == data["id"] - assert activity_data["object"] == activity.data["object"] - assert activity_data["content"] == "🍮" - end - - test "it works for incoming misskey likes that contain unicode emojis, turning them into EmojiReacts" do - user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "hello"}) - - data = - File.read!("test/fixtures/misskey-like.json") - |> Poison.decode!() - |> Map.put("object", activity.data["object"]) - |> Map.put("_misskey_reaction", "⭐") - - _actor = insert(:user, ap_id: data["actor"], local: false) - - {:ok, %Activity{data: activity_data, local: false}} = Transmogrifier.handle_incoming(data) - - assert activity_data["actor"] == data["actor"] - assert activity_data["type"] == "EmojiReact" - assert activity_data["id"] == data["id"] - assert activity_data["object"] == activity.data["object"] - assert activity_data["content"] == "⭐" - end -end diff --git a/test/web/activity_pub/transmogrifier/question_handling_test.exs b/test/web/activity_pub/transmogrifier/question_handling_test.exs deleted file mode 100644 index 74ee79543..000000000 --- a/test/web/activity_pub/transmogrifier/question_handling_test.exs +++ /dev/null @@ -1,176 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.Transmogrifier.QuestionHandlingTest do - use Pleroma.DataCase - - alias Pleroma.Activity - alias Pleroma.Object - alias Pleroma.Web.ActivityPub.Transmogrifier - alias Pleroma.Web.CommonAPI - - import Pleroma.Factory - - setup_all do - Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - - test "Mastodon Question activity" do - data = File.read!("test/fixtures/mastodon-question-activity.json") |> Poison.decode!() - - {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) - - object = Object.normalize(activity, false) - - assert object.data["url"] == "https://mastodon.sdf.org/@rinpatch/102070944809637304" - - assert object.data["closed"] == "2019-05-11T09:03:36Z" - - assert object.data["context"] == activity.data["context"] - - assert object.data["context"] == - "tag:mastodon.sdf.org,2019-05-10:objectId=15095122:objectType=Conversation" - - assert object.data["context_id"] - - assert object.data["anyOf"] == [] - - assert Enum.sort(object.data["oneOf"]) == - Enum.sort([ - %{ - "name" => "25 char limit is dumb", - "replies" => %{"totalItems" => 0, "type" => "Collection"}, - "type" => "Note" - }, - %{ - "name" => "Dunno", - "replies" => %{"totalItems" => 0, "type" => "Collection"}, - "type" => "Note" - }, - %{ - "name" => "Everyone knows that!", - "replies" => %{"totalItems" => 1, "type" => "Collection"}, - "type" => "Note" - }, - %{ - "name" => "I can't even fit a funny", - "replies" => %{"totalItems" => 1, "type" => "Collection"}, - "type" => "Note" - } - ]) - - user = insert(:user) - - {:ok, reply_activity} = CommonAPI.post(user, %{status: "hewwo", in_reply_to_id: activity.id}) - - reply_object = Object.normalize(reply_activity, false) - - assert reply_object.data["context"] == object.data["context"] - assert reply_object.data["context_id"] == object.data["context_id"] - end - - test "Mastodon Question activity with HTML tags in plaintext" do - options = [ - %{ - "type" => "Note", - "name" => "<input type=\"date\">", - "replies" => %{"totalItems" => 0, "type" => "Collection"} - }, - %{ - "type" => "Note", - "name" => "<input type=\"date\"/>", - "replies" => %{"totalItems" => 0, "type" => "Collection"} - }, - %{ - "type" => "Note", - "name" => "<input type=\"date\" />", - "replies" => %{"totalItems" => 1, "type" => "Collection"} - }, - %{ - "type" => "Note", - "name" => "<input type=\"date\"></input>", - "replies" => %{"totalItems" => 1, "type" => "Collection"} - } - ] - - data = - File.read!("test/fixtures/mastodon-question-activity.json") - |> Poison.decode!() - |> Kernel.put_in(["object", "oneOf"], options) - - {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) - object = Object.normalize(activity, false) - - assert Enum.sort(object.data["oneOf"]) == Enum.sort(options) - end - - test "Mastodon Question activity with custom emojis" do - options = [ - %{ - "type" => "Note", - "name" => ":blobcat:", - "replies" => %{"totalItems" => 0, "type" => "Collection"} - }, - %{ - "type" => "Note", - "name" => ":blobfox:", - "replies" => %{"totalItems" => 0, "type" => "Collection"} - } - ] - - tag = [ - %{ - "icon" => %{ - "type" => "Image", - "url" => "https://blob.cat/emoji/custom/blobcats/blobcat.png" - }, - "id" => "https://blob.cat/emoji/custom/blobcats/blobcat.png", - "name" => ":blobcat:", - "type" => "Emoji", - "updated" => "1970-01-01T00:00:00Z" - }, - %{ - "icon" => %{"type" => "Image", "url" => "https://blob.cat/emoji/blobfox/blobfox.png"}, - "id" => "https://blob.cat/emoji/blobfox/blobfox.png", - "name" => ":blobfox:", - "type" => "Emoji", - "updated" => "1970-01-01T00:00:00Z" - } - ] - - data = - File.read!("test/fixtures/mastodon-question-activity.json") - |> Poison.decode!() - |> Kernel.put_in(["object", "oneOf"], options) - |> Kernel.put_in(["object", "tag"], tag) - - {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) - object = Object.normalize(activity, false) - - assert object.data["oneOf"] == options - - assert object.data["emoji"] == %{ - "blobcat" => "https://blob.cat/emoji/custom/blobcats/blobcat.png", - "blobfox" => "https://blob.cat/emoji/blobfox/blobfox.png" - } - end - - test "returns an error if received a second time" do - data = File.read!("test/fixtures/mastodon-question-activity.json") |> Poison.decode!() - - assert {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) - - assert {:error, {:validate_object, {:error, _}}} = Transmogrifier.handle_incoming(data) - end - - test "accepts a Question with no content" do - data = - File.read!("test/fixtures/mastodon-question-activity.json") - |> Poison.decode!() - |> Kernel.put_in(["object", "content"], "") - - assert {:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(data) - end -end diff --git a/test/web/activity_pub/transmogrifier/reject_handling_test.exs b/test/web/activity_pub/transmogrifier/reject_handling_test.exs deleted file mode 100644 index 7592fbe1c..000000000 --- a/test/web/activity_pub/transmogrifier/reject_handling_test.exs +++ /dev/null @@ -1,67 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.Transmogrifier.RejectHandlingTest do - use Pleroma.DataCase - - alias Pleroma.Activity - alias Pleroma.User - alias Pleroma.Web.ActivityPub.Transmogrifier - alias Pleroma.Web.CommonAPI - - import Pleroma.Factory - - test "it fails for incoming rejects which cannot be correlated" do - follower = insert(:user) - followed = insert(:user, locked: true) - - accept_data = - File.read!("test/fixtures/mastodon-reject-activity.json") - |> Poison.decode!() - |> Map.put("actor", followed.ap_id) - - accept_data = - Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id)) - - {:error, _} = Transmogrifier.handle_incoming(accept_data) - - follower = User.get_cached_by_id(follower.id) - - refute User.following?(follower, followed) == true - end - - test "it works for incoming rejects which are referenced by IRI only" do - follower = insert(:user) - followed = insert(:user, locked: true) - - {:ok, follower} = User.follow(follower, followed) - {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed) - - assert User.following?(follower, followed) == true - - reject_data = - File.read!("test/fixtures/mastodon-reject-activity.json") - |> Poison.decode!() - |> Map.put("actor", followed.ap_id) - |> Map.put("object", follow_activity.data["id"]) - - {:ok, %Activity{data: _}} = Transmogrifier.handle_incoming(reject_data) - - follower = User.get_cached_by_id(follower.id) - - assert User.following?(follower, followed) == false - end - - test "it rejects activities without a valid ID" do - user = insert(:user) - - data = - File.read!("test/fixtures/mastodon-follow-activity.json") - |> Poison.decode!() - |> Map.put("object", user.ap_id) - |> Map.put("id", "") - - :error = Transmogrifier.handle_incoming(data) - end -end diff --git a/test/web/activity_pub/transmogrifier/undo_handling_test.exs b/test/web/activity_pub/transmogrifier/undo_handling_test.exs deleted file mode 100644 index 8683f7135..000000000 --- a/test/web/activity_pub/transmogrifier/undo_handling_test.exs +++ /dev/null @@ -1,185 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.Transmogrifier.UndoHandlingTest do - use Pleroma.DataCase - - alias Pleroma.Activity - alias Pleroma.Object - alias Pleroma.User - alias Pleroma.Web.ActivityPub.Transmogrifier - alias Pleroma.Web.CommonAPI - - import Pleroma.Factory - - test "it works for incoming emoji reaction undos" do - user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "hello"}) - {:ok, reaction_activity} = CommonAPI.react_with_emoji(activity.id, user, "👌") - - data = - File.read!("test/fixtures/mastodon-undo-like.json") - |> Poison.decode!() - |> Map.put("object", reaction_activity.data["id"]) - |> Map.put("actor", user.ap_id) - - {:ok, activity} = Transmogrifier.handle_incoming(data) - - assert activity.actor == user.ap_id - assert activity.data["id"] == data["id"] - assert activity.data["type"] == "Undo" - end - - test "it returns an error for incoming unlikes wihout a like activity" do - user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{status: "leave a like pls"}) - - data = - File.read!("test/fixtures/mastodon-undo-like.json") - |> Poison.decode!() - |> Map.put("object", activity.data["object"]) - - assert Transmogrifier.handle_incoming(data) == :error - end - - test "it works for incoming unlikes with an existing like activity" do - user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{status: "leave a like pls"}) - - like_data = - File.read!("test/fixtures/mastodon-like.json") - |> Poison.decode!() - |> Map.put("object", activity.data["object"]) - - _liker = insert(:user, ap_id: like_data["actor"], local: false) - - {:ok, %Activity{data: like_data, local: false}} = Transmogrifier.handle_incoming(like_data) - - data = - File.read!("test/fixtures/mastodon-undo-like.json") - |> Poison.decode!() - |> Map.put("object", like_data) - |> Map.put("actor", like_data["actor"]) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert data["actor"] == "http://mastodon.example.org/users/admin" - assert data["type"] == "Undo" - assert data["id"] == "http://mastodon.example.org/users/admin#likes/2/undo" - assert data["object"] == "http://mastodon.example.org/users/admin#likes/2" - - note = Object.get_by_ap_id(like_data["object"]) - assert note.data["like_count"] == 0 - assert note.data["likes"] == [] - end - - test "it works for incoming unlikes with an existing like activity and a compact object" do - user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{status: "leave a like pls"}) - - like_data = - File.read!("test/fixtures/mastodon-like.json") - |> Poison.decode!() - |> Map.put("object", activity.data["object"]) - - _liker = insert(:user, ap_id: like_data["actor"], local: false) - - {:ok, %Activity{data: like_data, local: false}} = Transmogrifier.handle_incoming(like_data) - - data = - File.read!("test/fixtures/mastodon-undo-like.json") - |> Poison.decode!() - |> Map.put("object", like_data["id"]) - |> Map.put("actor", like_data["actor"]) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert data["actor"] == "http://mastodon.example.org/users/admin" - assert data["type"] == "Undo" - assert data["id"] == "http://mastodon.example.org/users/admin#likes/2/undo" - assert data["object"] == "http://mastodon.example.org/users/admin#likes/2" - end - - test "it works for incoming unannounces with an existing notice" do - user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{status: "hey"}) - - announce_data = - File.read!("test/fixtures/mastodon-announce.json") - |> Poison.decode!() - |> Map.put("object", activity.data["object"]) - - _announcer = insert(:user, ap_id: announce_data["actor"], local: false) - - {:ok, %Activity{data: announce_data, local: false}} = - Transmogrifier.handle_incoming(announce_data) - - data = - File.read!("test/fixtures/mastodon-undo-announce.json") - |> Poison.decode!() - |> Map.put("object", announce_data) - |> Map.put("actor", announce_data["actor"]) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert data["type"] == "Undo" - - assert data["object"] == - "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity" - end - - test "it works for incoming unfollows with an existing follow" do - user = insert(:user) - - follow_data = - File.read!("test/fixtures/mastodon-follow-activity.json") - |> Poison.decode!() - |> Map.put("object", user.ap_id) - - _follower = insert(:user, ap_id: follow_data["actor"], local: false) - - {:ok, %Activity{data: _, local: false}} = Transmogrifier.handle_incoming(follow_data) - - data = - File.read!("test/fixtures/mastodon-unfollow-activity.json") - |> Poison.decode!() - |> Map.put("object", follow_data) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert data["type"] == "Undo" - assert data["object"]["type"] == "Follow" - assert data["object"]["object"] == user.ap_id - assert data["actor"] == "http://mastodon.example.org/users/admin" - - refute User.following?(User.get_cached_by_ap_id(data["actor"]), user) - end - - test "it works for incoming unblocks with an existing block" do - user = insert(:user) - - block_data = - File.read!("test/fixtures/mastodon-block-activity.json") - |> Poison.decode!() - |> Map.put("object", user.ap_id) - - _blocker = insert(:user, ap_id: block_data["actor"], local: false) - - {:ok, %Activity{data: _, local: false}} = Transmogrifier.handle_incoming(block_data) - - data = - File.read!("test/fixtures/mastodon-unblock-activity.json") - |> Poison.decode!() - |> Map.put("object", block_data) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - assert data["type"] == "Undo" - assert data["object"] == block_data["id"] - - blocker = User.get_cached_by_ap_id(data["actor"]) - - refute User.blocks?(blocker, user) - end -end diff --git a/test/web/activity_pub/transmogrifier/user_update_handling_test.exs b/test/web/activity_pub/transmogrifier/user_update_handling_test.exs deleted file mode 100644 index 64636656c..000000000 --- a/test/web/activity_pub/transmogrifier/user_update_handling_test.exs +++ /dev/null @@ -1,159 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.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 deleted file mode 100644 index 3fa41b0c7..000000000 --- a/test/web/activity_pub/transmogrifier_test.exs +++ /dev/null @@ -1,1379 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do - use Oban.Testing, repo: Pleroma.Repo - use Pleroma.DataCase - - alias Pleroma.Activity - alias Pleroma.Object - alias Pleroma.Object.Fetcher - alias Pleroma.Tests.ObanHelpers - alias Pleroma.User - alias Pleroma.Web.ActivityPub.Transmogrifier - alias Pleroma.Web.AdminAPI.AccountView - alias Pleroma.Web.CommonAPI - - import Mock - import Pleroma.Factory - import ExUnit.CaptureLog - - setup_all do - Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - - setup do: clear_config([:instance, :max_remote_account_fields]) - - describe "handle_incoming" do - test "it works for incoming notices with tag not being an array (kroeg)" do - data = File.read!("test/fixtures/kroeg-array-less-emoji.json") |> Poison.decode!() - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - object = Object.normalize(data["object"]) - - assert object.data["emoji"] == %{ - "icon_e_smile" => "https://puckipedia.com/forum/images/smilies/icon_e_smile.png" - } - - data = File.read!("test/fixtures/kroeg-array-less-hashtag.json") |> Poison.decode!() - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - object = Object.normalize(data["object"]) - - assert "test" in object.data["tag"] - end - - test "it works for incoming notices with url not being a string (prismo)" do - data = File.read!("test/fixtures/prismo-url-map.json") |> Poison.decode!() - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - object = Object.normalize(data["object"]) - - assert object.data["url"] == "https://prismo.news/posts/83" - end - - test "it cleans up incoming notices which are not really DMs" do - user = insert(:user) - other_user = insert(:user) - - to = [user.ap_id, other_user.ap_id] - - data = - File.read!("test/fixtures/mastodon-post-activity.json") - |> Poison.decode!() - |> Map.put("to", to) - |> Map.put("cc", []) - - object = - data["object"] - |> Map.put("to", to) - |> Map.put("cc", []) - - data = Map.put(data, "object", object) - - {:ok, %Activity{data: data, local: false} = activity} = Transmogrifier.handle_incoming(data) - - assert data["to"] == [] - assert data["cc"] == to - - object_data = Object.normalize(activity).data - - assert object_data["to"] == [] - assert object_data["cc"] == to - end - - test "it ignores an incoming notice if we already have it" do - activity = insert(:note_activity) - - data = - File.read!("test/fixtures/mastodon-post-activity.json") - |> Poison.decode!() - |> Map.put("object", Object.normalize(activity).data) - - {:ok, returned_activity} = Transmogrifier.handle_incoming(data) - - assert activity == returned_activity - end - - @tag capture_log: true - test "it fetches reply-to activities if we don't have them" do - data = - File.read!("test/fixtures/mastodon-post-activity.json") - |> Poison.decode!() - - object = - data["object"] - |> Map.put("inReplyTo", "https://shitposter.club/notice/2827873") - - data = Map.put(data, "object", object) - {:ok, returned_activity} = Transmogrifier.handle_incoming(data) - returned_object = Object.normalize(returned_activity, false) - - assert activity = - Activity.get_create_by_object_ap_id( - "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment" - ) - - assert returned_object.data["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873" - end - - test "it does not fetch reply-to activities beyond max replies depth limit" do - data = - File.read!("test/fixtures/mastodon-post-activity.json") - |> Poison.decode!() - - object = - data["object"] - |> Map.put("inReplyTo", "https://shitposter.club/notice/2827873") - - data = Map.put(data, "object", object) - - with_mock Pleroma.Web.Federator, - allowed_thread_distance?: fn _ -> false end do - {:ok, returned_activity} = Transmogrifier.handle_incoming(data) - - returned_object = Object.normalize(returned_activity, false) - - refute Activity.get_create_by_object_ap_id( - "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment" - ) - - assert returned_object.data["inReplyToAtomUri"] == - "https://shitposter.club/notice/2827873" - end - end - - test "it does not crash if the object in inReplyTo can't be fetched" do - data = - File.read!("test/fixtures/mastodon-post-activity.json") - |> Poison.decode!() - - object = - data["object"] - |> Map.put("inReplyTo", "https://404.site/whatever") - - data = - data - |> Map.put("object", object) - - assert capture_log(fn -> - {:ok, _returned_activity} = Transmogrifier.handle_incoming(data) - end) =~ "[warn] Couldn't fetch \"https://404.site/whatever\", error: nil" - end - - test "it does not work for deactivated users" do - data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!() - - insert(:user, ap_id: data["actor"], deactivated: true) - - assert {:error, _} = Transmogrifier.handle_incoming(data) - end - - test "it works for incoming notices" do - data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!() - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert data["id"] == - "http://mastodon.example.org/users/admin/statuses/99512778738411822/activity" - - assert data["context"] == - "tag:mastodon.example.org,2018-02-12:objectId=20:objectType=Conversation" - - assert data["to"] == ["https://www.w3.org/ns/activitystreams#Public"] - - assert data["cc"] == [ - "http://mastodon.example.org/users/admin/followers", - "http://localtesting.pleroma.lol/users/lain" - ] - - assert data["actor"] == "http://mastodon.example.org/users/admin" - - object_data = Object.normalize(data["object"]).data - - assert object_data["id"] == - "http://mastodon.example.org/users/admin/statuses/99512778738411822" - - assert object_data["to"] == ["https://www.w3.org/ns/activitystreams#Public"] - - assert object_data["cc"] == [ - "http://mastodon.example.org/users/admin/followers", - "http://localtesting.pleroma.lol/users/lain" - ] - - assert object_data["actor"] == "http://mastodon.example.org/users/admin" - assert object_data["attributedTo"] == "http://mastodon.example.org/users/admin" - - assert object_data["context"] == - "tag:mastodon.example.org,2018-02-12:objectId=20:objectType=Conversation" - - assert object_data["sensitive"] == true - - user = User.get_cached_by_ap_id(object_data["actor"]) - - assert user.note_count == 1 - end - - test "it works for incoming notices with hashtags" do - data = File.read!("test/fixtures/mastodon-post-activity-hashtag.json") |> Poison.decode!() - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - object = Object.normalize(data["object"]) - - assert Enum.at(object.data["tag"], 2) == "moo" - end - - test "it works for incoming notices with contentMap" do - data = - File.read!("test/fixtures/mastodon-post-activity-contentmap.json") |> Poison.decode!() - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - object = Object.normalize(data["object"]) - - assert object.data["content"] == - "<p><span class=\"h-card\"><a href=\"http://localtesting.pleroma.lol/users/lain\" class=\"u-url mention\">@<span>lain</span></a></span></p>" - end - - test "it works for incoming notices with to/cc not being an array (kroeg)" do - data = File.read!("test/fixtures/kroeg-post-activity.json") |> Poison.decode!() - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - object = Object.normalize(data["object"]) - - assert object.data["content"] == - "<p>henlo from my Psion netBook</p><p>message sent from my Psion netBook</p>" - end - - test "it ensures that as:Public activities make it to their followers collection" do - user = insert(:user) - - data = - File.read!("test/fixtures/mastodon-post-activity.json") - |> Poison.decode!() - |> Map.put("actor", user.ap_id) - |> Map.put("to", ["https://www.w3.org/ns/activitystreams#Public"]) - |> Map.put("cc", []) - - object = - data["object"] - |> Map.put("attributedTo", user.ap_id) - |> Map.put("to", ["https://www.w3.org/ns/activitystreams#Public"]) - |> Map.put("cc", []) - |> Map.put("id", user.ap_id <> "/activities/12345678") - - data = Map.put(data, "object", object) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert data["cc"] == [User.ap_followers(user)] - end - - test "it ensures that address fields become lists" do - user = insert(:user) - - data = - File.read!("test/fixtures/mastodon-post-activity.json") - |> Poison.decode!() - |> Map.put("actor", user.ap_id) - |> Map.put("to", nil) - |> Map.put("cc", nil) - - object = - data["object"] - |> Map.put("attributedTo", user.ap_id) - |> Map.put("to", nil) - |> Map.put("cc", nil) - |> Map.put("id", user.ap_id <> "/activities/12345678") - - data = Map.put(data, "object", object) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert !is_nil(data["to"]) - assert !is_nil(data["cc"]) - end - - test "it strips internal likes" do - data = - File.read!("test/fixtures/mastodon-post-activity.json") - |> Poison.decode!() - - likes = %{ - "first" => - "http://mastodon.example.org/objects/dbdbc507-52c8-490d-9b7c-1e1d52e5c132/likes?page=1", - "id" => "http://mastodon.example.org/objects/dbdbc507-52c8-490d-9b7c-1e1d52e5c132/likes", - "totalItems" => 3, - "type" => "OrderedCollection" - } - - object = Map.put(data["object"], "likes", likes) - data = Map.put(data, "object", object) - - {:ok, %Activity{object: object}} = Transmogrifier.handle_incoming(data) - - refute Map.has_key?(object.data, "likes") - end - - test "it strips internal reactions" do - user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{status: "#cofe"}) - {:ok, _} = CommonAPI.react_with_emoji(activity.id, user, "📢") - - %{object: object} = Activity.get_by_id_with_object(activity.id) - assert Map.has_key?(object.data, "reactions") - assert Map.has_key?(object.data, "reaction_count") - - object_data = Transmogrifier.strip_internal_fields(object.data) - refute Map.has_key?(object_data, "reactions") - refute Map.has_key?(object_data, "reaction_count") - end - - test "it works for incoming unfollows with an existing follow" do - user = insert(:user) - - follow_data = - File.read!("test/fixtures/mastodon-follow-activity.json") - |> Poison.decode!() - |> Map.put("object", user.ap_id) - - {:ok, %Activity{data: _, local: false}} = Transmogrifier.handle_incoming(follow_data) - - data = - File.read!("test/fixtures/mastodon-unfollow-activity.json") - |> Poison.decode!() - |> Map.put("object", follow_data) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert data["type"] == "Undo" - assert data["object"]["type"] == "Follow" - assert data["object"]["object"] == user.ap_id - assert data["actor"] == "http://mastodon.example.org/users/admin" - - refute User.following?(User.get_cached_by_ap_id(data["actor"]), user) - end - - test "skip converting the content when it is nil" do - object_id = "https://peertube.social/videos/watch/278d2b7c-0f38-4aaa-afe6-9ecc0c4a34fe" - - {:ok, object} = Fetcher.fetch_and_contain_remote_object_from_id(object_id) - - result = - Pleroma.Web.ActivityPub.Transmogrifier.fix_object(Map.merge(object, %{"content" => nil})) - - assert result["content"] == nil - end - - test "it converts content of object to html" do - object_id = "https://peertube.social/videos/watch/278d2b7c-0f38-4aaa-afe6-9ecc0c4a34fe" - - {:ok, %{"content" => content_markdown}} = - Fetcher.fetch_and_contain_remote_object_from_id(object_id) - - {:ok, %Pleroma.Object{data: %{"content" => content}} = object} = - Fetcher.fetch_object_from_id(object_id) - - assert content_markdown == - "Support this and our other Michigan!/usr/group videos and meetings. Learn more at http://mug.org/membership\n\nTwenty Years in Jail: FreeBSD's Jails, Then and Now\n\nJails started as a limited virtualization system, but over the last two years they've..." - - assert content == - "<p>Support this and our other Michigan!/usr/group videos and meetings. Learn more at <a href=\"http://mug.org/membership\">http://mug.org/membership</a></p><p>Twenty Years in Jail: FreeBSD’s Jails, Then and Now</p><p>Jails started as a limited virtualization system, but over the last two years they’ve…</p>" - - assert object.data["mediaType"] == "text/html" - end - - test "it remaps video URLs as attachments if necessary" do - {:ok, object} = - Fetcher.fetch_object_from_id( - "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3" - ) - - assert object.data["url"] == - "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3" - - assert object.data["attachment"] == [ - %{ - "type" => "Link", - "mediaType" => "video/mp4", - "url" => [ - %{ - "href" => - "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4", - "mediaType" => "video/mp4", - "type" => "Link" - } - ] - } - ] - - {:ok, object} = - Fetcher.fetch_object_from_id( - "https://framatube.org/videos/watch/6050732a-8a7a-43d4-a6cd-809525a1d206" - ) - - assert object.data["attachment"] == [ - %{ - "type" => "Link", - "mediaType" => "video/mp4", - "url" => [ - %{ - "href" => - "https://framatube.org/static/webseed/6050732a-8a7a-43d4-a6cd-809525a1d206-1080.mp4", - "mediaType" => "video/mp4", - "type" => "Link" - } - ] - } - ] - - assert object.data["url"] == - "https://framatube.org/videos/watch/6050732a-8a7a-43d4-a6cd-809525a1d206" - end - - test "it accepts Flag activities" do - user = insert(:user) - other_user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "test post"}) - object = Object.normalize(activity) - - note_obj = %{ - "type" => "Note", - "id" => activity.data["id"], - "content" => "test post", - "published" => object.data["published"], - "actor" => AccountView.render("show.json", %{user: user, skip_visibility_check: true}) - } - - message = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "cc" => [user.ap_id], - "object" => [user.ap_id, activity.data["id"]], - "type" => "Flag", - "content" => "blocked AND reported!!!", - "actor" => other_user.ap_id - } - - assert {:ok, activity} = Transmogrifier.handle_incoming(message) - - assert activity.data["object"] == [user.ap_id, note_obj] - assert activity.data["content"] == "blocked AND reported!!!" - assert activity.data["actor"] == other_user.ap_id - assert activity.data["cc"] == [user.ap_id] - end - - test "it correctly processes messages with non-array to field" do - user = insert(:user) - - message = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "to" => "https://www.w3.org/ns/activitystreams#Public", - "type" => "Create", - "object" => %{ - "content" => "blah blah blah", - "type" => "Note", - "attributedTo" => user.ap_id, - "inReplyTo" => nil - }, - "actor" => user.ap_id - } - - assert {:ok, activity} = Transmogrifier.handle_incoming(message) - - assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["to"] - end - - test "it correctly processes messages with non-array cc field" do - user = insert(:user) - - message = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "to" => user.follower_address, - "cc" => "https://www.w3.org/ns/activitystreams#Public", - "type" => "Create", - "object" => %{ - "content" => "blah blah blah", - "type" => "Note", - "attributedTo" => user.ap_id, - "inReplyTo" => nil - }, - "actor" => user.ap_id - } - - assert {:ok, activity} = Transmogrifier.handle_incoming(message) - - assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["cc"] - assert [user.follower_address] == activity.data["to"] - end - - test "it correctly processes messages with weirdness in address fields" do - user = insert(:user) - - message = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "to" => [nil, user.follower_address], - "cc" => ["https://www.w3.org/ns/activitystreams#Public", ["¿"]], - "type" => "Create", - "object" => %{ - "content" => "…", - "type" => "Note", - "attributedTo" => user.ap_id, - "inReplyTo" => nil - }, - "actor" => user.ap_id - } - - assert {:ok, activity} = Transmogrifier.handle_incoming(message) - - assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["cc"] - assert [user.follower_address] == activity.data["to"] - end - - test "it accepts Move activities" do - old_user = insert(:user) - new_user = insert(:user) - - message = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "type" => "Move", - "actor" => old_user.ap_id, - "object" => old_user.ap_id, - "target" => new_user.ap_id - } - - assert :error = Transmogrifier.handle_incoming(message) - - {:ok, _new_user} = User.update_and_set_cache(new_user, %{also_known_as: [old_user.ap_id]}) - - assert {:ok, %Activity{} = activity} = Transmogrifier.handle_incoming(message) - assert activity.actor == old_user.ap_id - assert activity.data["actor"] == old_user.ap_id - assert activity.data["object"] == old_user.ap_id - assert activity.data["target"] == new_user.ap_id - assert activity.data["type"] == "Move" - end - end - - describe "`handle_incoming/2`, Mastodon format `replies` handling" do - setup do: clear_config([:activitypub, :note_replies_output_limit], 5) - setup do: clear_config([:instance, :federation_incoming_replies_max_depth]) - - setup do - data = - "test/fixtures/mastodon-post-activity.json" - |> File.read!() - |> Poison.decode!() - - items = get_in(data, ["object", "replies", "first", "items"]) - assert length(items) > 0 - - %{data: data, items: items} - end - - test "schedules background fetching of `replies` items if max thread depth limit allows", %{ - data: data, - items: items - } do - Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 10) - - {:ok, _activity} = Transmogrifier.handle_incoming(data) - - for id <- items do - job_args = %{"op" => "fetch_remote", "id" => id, "depth" => 1} - assert_enqueued(worker: Pleroma.Workers.RemoteFetcherWorker, args: job_args) - end - end - - test "does NOT schedule background fetching of `replies` beyond max thread depth limit allows", - %{data: data} do - Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 0) - - {:ok, _activity} = Transmogrifier.handle_incoming(data) - - assert all_enqueued(worker: Pleroma.Workers.RemoteFetcherWorker) == [] - end - end - - describe "`handle_incoming/2`, Pleroma format `replies` handling" do - setup do: clear_config([:activitypub, :note_replies_output_limit], 5) - setup do: clear_config([:instance, :federation_incoming_replies_max_depth]) - - setup do - user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "post1"}) - - {:ok, reply1} = - CommonAPI.post(user, %{status: "reply1", in_reply_to_status_id: activity.id}) - - {:ok, reply2} = - CommonAPI.post(user, %{status: "reply2", in_reply_to_status_id: activity.id}) - - replies_uris = Enum.map([reply1, reply2], fn a -> a.object.data["id"] end) - - {:ok, federation_output} = Transmogrifier.prepare_outgoing(activity.data) - - Repo.delete(activity.object) - Repo.delete(activity) - - %{federation_output: federation_output, replies_uris: replies_uris} - end - - test "schedules background fetching of `replies` items if max thread depth limit allows", %{ - federation_output: federation_output, - replies_uris: replies_uris - } do - Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 1) - - {:ok, _activity} = Transmogrifier.handle_incoming(federation_output) - - for id <- replies_uris do - job_args = %{"op" => "fetch_remote", "id" => id, "depth" => 1} - assert_enqueued(worker: Pleroma.Workers.RemoteFetcherWorker, args: job_args) - end - end - - test "does NOT schedule background fetching of `replies` beyond max thread depth limit allows", - %{federation_output: federation_output} do - Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 0) - - {:ok, _activity} = Transmogrifier.handle_incoming(federation_output) - - assert all_enqueued(worker: Pleroma.Workers.RemoteFetcherWorker) == [] - end - end - - describe "prepare outgoing" do - test "it inlines private announced objects" do - user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "hey", visibility: "private"}) - - {:ok, announce_activity} = CommonAPI.repeat(activity.id, user) - - {:ok, modified} = Transmogrifier.prepare_outgoing(announce_activity.data) - - assert modified["object"]["content"] == "hey" - assert modified["object"]["actor"] == modified["object"]["attributedTo"] - end - - test "it turns mentions into tags" do - user = insert(:user) - other_user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{status: "hey, @#{other_user.nickname}, how are ya? #2hu"}) - - with_mock Pleroma.Notification, - get_notified_from_activity: fn _, _ -> [] end do - {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) - - object = modified["object"] - - expected_mention = %{ - "href" => other_user.ap_id, - "name" => "@#{other_user.nickname}", - "type" => "Mention" - } - - expected_tag = %{ - "href" => Pleroma.Web.Endpoint.url() <> "/tags/2hu", - "type" => "Hashtag", - "name" => "#2hu" - } - - refute called(Pleroma.Notification.get_notified_from_activity(:_, :_)) - assert Enum.member?(object["tag"], expected_tag) - assert Enum.member?(object["tag"], expected_mention) - end - end - - test "it adds the sensitive property" do - user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "#nsfw hey"}) - {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) - - assert modified["object"]["sensitive"] - end - - test "it adds the json-ld context and the conversation property" do - user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "hey"}) - {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) - - assert modified["@context"] == - Pleroma.Web.ActivityPub.Utils.make_json_ld_header()["@context"] - - assert modified["object"]["conversation"] == modified["context"] - end - - test "it sets the 'attributedTo' property to the actor of the object if it doesn't have one" do - user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "hey"}) - {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) - - assert modified["object"]["actor"] == modified["object"]["attributedTo"] - end - - test "it strips internal hashtag data" do - user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "#2hu"}) - - expected_tag = %{ - "href" => Pleroma.Web.Endpoint.url() <> "/tags/2hu", - "type" => "Hashtag", - "name" => "#2hu" - } - - {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) - - assert modified["object"]["tag"] == [expected_tag] - end - - test "it strips internal fields" do - user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "#2hu :firefox:"}) - - {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) - - assert length(modified["object"]["tag"]) == 2 - - assert is_nil(modified["object"]["emoji"]) - assert is_nil(modified["object"]["like_count"]) - assert is_nil(modified["object"]["announcements"]) - assert is_nil(modified["object"]["announcement_count"]) - assert is_nil(modified["object"]["context_id"]) - end - - test "it strips internal fields of article" do - activity = insert(:article_activity) - - {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) - - assert length(modified["object"]["tag"]) == 2 - - assert is_nil(modified["object"]["emoji"]) - assert is_nil(modified["object"]["like_count"]) - assert is_nil(modified["object"]["announcements"]) - assert is_nil(modified["object"]["announcement_count"]) - assert is_nil(modified["object"]["context_id"]) - assert is_nil(modified["object"]["likes"]) - end - - test "the directMessage flag is present" do - user = insert(:user) - other_user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "2hu :moominmamma:"}) - - {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) - - assert modified["directMessage"] == false - - {:ok, activity} = CommonAPI.post(user, %{status: "@#{other_user.nickname} :moominmamma:"}) - - {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) - - assert modified["directMessage"] == false - - {:ok, activity} = - CommonAPI.post(user, %{ - status: "@#{other_user.nickname} :moominmamma:", - visibility: "direct" - }) - - {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) - - assert modified["directMessage"] == true - end - - test "it strips BCC field" do - user = insert(:user) - {:ok, list} = Pleroma.List.create("foo", user) - - {:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"}) - - {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) - - assert is_nil(modified["bcc"]) - end - - test "it can handle Listen activities" do - listen_activity = insert(:listen) - - {:ok, modified} = Transmogrifier.prepare_outgoing(listen_activity.data) - - assert modified["type"] == "Listen" - - user = insert(:user) - - {:ok, activity} = CommonAPI.listen(user, %{"title" => "lain radio episode 1"}) - - {:ok, _modified} = Transmogrifier.prepare_outgoing(activity.data) - end - end - - describe "user upgrade" do - test "it upgrades a user to activitypub" do - user = - insert(:user, %{ - nickname: "rye@niu.moe", - local: false, - ap_id: "https://niu.moe/users/rye", - follower_address: User.ap_followers(%User{nickname: "rye@niu.moe"}) - }) - - user_two = insert(:user) - Pleroma.FollowingRelationship.follow(user_two, user, :follow_accept) - - {:ok, activity} = CommonAPI.post(user, %{status: "test"}) - {:ok, unrelated_activity} = CommonAPI.post(user_two, %{status: "test"}) - assert "http://localhost:4001/users/rye@niu.moe/followers" in activity.recipients - - user = User.get_cached_by_id(user.id) - assert user.note_count == 1 - - {:ok, user} = Transmogrifier.upgrade_user_from_ap_id("https://niu.moe/users/rye") - ObanHelpers.perform_all() - - assert user.ap_enabled - assert user.note_count == 1 - assert user.follower_address == "https://niu.moe/users/rye/followers" - assert user.following_address == "https://niu.moe/users/rye/following" - - user = User.get_cached_by_id(user.id) - assert user.note_count == 1 - - activity = Activity.get_by_id(activity.id) - assert user.follower_address in activity.recipients - - assert %{ - "url" => [ - %{ - "href" => - "https://cdn.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg" - } - ] - } = user.avatar - - assert %{ - "url" => [ - %{ - "href" => - "https://cdn.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png" - } - ] - } = user.banner - - refute "..." in activity.recipients - - unrelated_activity = Activity.get_by_id(unrelated_activity.id) - refute user.follower_address in unrelated_activity.recipients - - user_two = User.get_cached_by_id(user_two.id) - assert User.following?(user_two, user) - refute "..." in User.following(user_two) - end - end - - describe "actor rewriting" do - test "it fixes the actor URL property to be a proper URI" do - data = %{ - "url" => %{"href" => "http://example.com"} - } - - rewritten = Transmogrifier.maybe_fix_user_object(data) - assert rewritten["url"] == "http://example.com" - end - end - - describe "actor origin containment" do - test "it rejects activities which reference objects with bogus origins" do - data = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "id" => "http://mastodon.example.org/users/admin/activities/1234", - "actor" => "http://mastodon.example.org/users/admin", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "object" => "https://info.pleroma.site/activity.json", - "type" => "Announce" - } - - assert capture_log(fn -> - {:error, _} = Transmogrifier.handle_incoming(data) - end) =~ "Object containment failed" - end - - test "it rejects activities which reference objects that have an incorrect attribution (variant 1)" do - data = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "id" => "http://mastodon.example.org/users/admin/activities/1234", - "actor" => "http://mastodon.example.org/users/admin", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "object" => "https://info.pleroma.site/activity2.json", - "type" => "Announce" - } - - assert capture_log(fn -> - {:error, _} = Transmogrifier.handle_incoming(data) - end) =~ "Object containment failed" - end - - test "it rejects activities which reference objects that have an incorrect attribution (variant 2)" do - data = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "id" => "http://mastodon.example.org/users/admin/activities/1234", - "actor" => "http://mastodon.example.org/users/admin", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "object" => "https://info.pleroma.site/activity3.json", - "type" => "Announce" - } - - assert capture_log(fn -> - {:error, _} = Transmogrifier.handle_incoming(data) - end) =~ "Object containment failed" - end - end - - describe "reserialization" do - test "successfully reserializes a message with inReplyTo == nil" do - user = insert(:user) - - message = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "type" => "Create", - "object" => %{ - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "type" => "Note", - "content" => "Hi", - "inReplyTo" => nil, - "attributedTo" => user.ap_id - }, - "actor" => user.ap_id - } - - {:ok, activity} = Transmogrifier.handle_incoming(message) - - {:ok, _} = Transmogrifier.prepare_outgoing(activity.data) - end - - test "successfully reserializes a message with AS2 objects in IR" do - user = insert(:user) - - message = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "type" => "Create", - "object" => %{ - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "type" => "Note", - "content" => "Hi", - "inReplyTo" => nil, - "attributedTo" => user.ap_id, - "tag" => [ - %{"name" => "#2hu", "href" => "http://example.com/2hu", "type" => "Hashtag"}, - %{"name" => "Bob", "href" => "http://example.com/bob", "type" => "Mention"} - ] - }, - "actor" => user.ap_id - } - - {:ok, activity} = Transmogrifier.handle_incoming(message) - - {:ok, _} = Transmogrifier.prepare_outgoing(activity.data) - end - end - - describe "fix_explicit_addressing" do - setup do - user = insert(:user) - [user: user] - end - - test "moves non-explicitly mentioned actors to cc", %{user: user} do - explicitly_mentioned_actors = [ - "https://pleroma.gold/users/user1", - "https://pleroma.gold/user2" - ] - - object = %{ - "actor" => user.ap_id, - "to" => explicitly_mentioned_actors ++ ["https://social.beepboop.ga/users/dirb"], - "cc" => [], - "tag" => - Enum.map(explicitly_mentioned_actors, fn href -> - %{"type" => "Mention", "href" => href} - end) - } - - fixed_object = Transmogrifier.fix_explicit_addressing(object) - assert Enum.all?(explicitly_mentioned_actors, &(&1 in fixed_object["to"])) - refute "https://social.beepboop.ga/users/dirb" in fixed_object["to"] - assert "https://social.beepboop.ga/users/dirb" in fixed_object["cc"] - end - - test "does not move actor's follower collection to cc", %{user: user} do - object = %{ - "actor" => user.ap_id, - "to" => [user.follower_address], - "cc" => [] - } - - fixed_object = Transmogrifier.fix_explicit_addressing(object) - assert user.follower_address in fixed_object["to"] - refute user.follower_address in fixed_object["cc"] - end - - test "removes recipient's follower collection from cc", %{user: user} do - recipient = insert(:user) - - object = %{ - "actor" => user.ap_id, - "to" => [recipient.ap_id, "https://www.w3.org/ns/activitystreams#Public"], - "cc" => [user.follower_address, recipient.follower_address] - } - - fixed_object = Transmogrifier.fix_explicit_addressing(object) - - assert user.follower_address in fixed_object["cc"] - refute recipient.follower_address in fixed_object["cc"] - refute recipient.follower_address in fixed_object["to"] - end - end - - describe "fix_summary/1" do - test "returns fixed object" do - assert Transmogrifier.fix_summary(%{"summary" => nil}) == %{"summary" => ""} - assert Transmogrifier.fix_summary(%{"summary" => "ok"}) == %{"summary" => "ok"} - assert Transmogrifier.fix_summary(%{}) == %{"summary" => ""} - end - end - - describe "fix_in_reply_to/2" do - setup do: clear_config([:instance, :federation_incoming_replies_max_depth]) - - setup do - data = Poison.decode!(File.read!("test/fixtures/mastodon-post-activity.json")) - [data: data] - end - - test "returns not modified object when hasn't containts inReplyTo field", %{data: data} do - assert Transmogrifier.fix_in_reply_to(data) == data - end - - test "returns object with inReplyToAtomUri when denied incoming reply", %{data: data} do - Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 0) - - object_with_reply = - Map.put(data["object"], "inReplyTo", "https://shitposter.club/notice/2827873") - - modified_object = Transmogrifier.fix_in_reply_to(object_with_reply) - assert modified_object["inReplyTo"] == "https://shitposter.club/notice/2827873" - assert modified_object["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873" - - object_with_reply = - Map.put(data["object"], "inReplyTo", %{"id" => "https://shitposter.club/notice/2827873"}) - - modified_object = Transmogrifier.fix_in_reply_to(object_with_reply) - assert modified_object["inReplyTo"] == %{"id" => "https://shitposter.club/notice/2827873"} - assert modified_object["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873" - - object_with_reply = - Map.put(data["object"], "inReplyTo", ["https://shitposter.club/notice/2827873"]) - - modified_object = Transmogrifier.fix_in_reply_to(object_with_reply) - assert modified_object["inReplyTo"] == ["https://shitposter.club/notice/2827873"] - assert modified_object["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873" - - object_with_reply = Map.put(data["object"], "inReplyTo", []) - modified_object = Transmogrifier.fix_in_reply_to(object_with_reply) - assert modified_object["inReplyTo"] == [] - assert modified_object["inReplyToAtomUri"] == "" - end - - @tag capture_log: true - test "returns modified object when allowed incoming reply", %{data: data} do - object_with_reply = - Map.put( - data["object"], - "inReplyTo", - "https://shitposter.club/notice/2827873" - ) - - Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 5) - modified_object = Transmogrifier.fix_in_reply_to(object_with_reply) - - assert modified_object["inReplyTo"] == - "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment" - - assert modified_object["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873" - - assert modified_object["context"] == - "tag:shitposter.club,2017-05-05:objectType=thread:nonce=3c16e9c2681f6d26" - end - end - - describe "fix_url/1" do - test "fixes data for object when url is map" do - object = %{ - "url" => %{ - "type" => "Link", - "mimeType" => "video/mp4", - "href" => "https://peede8d-46fb-ad81-2d4c2d1630e3-480.mp4" - } - } - - assert Transmogrifier.fix_url(object) == %{ - "url" => "https://peede8d-46fb-ad81-2d4c2d1630e3-480.mp4" - } - end - - test "fixes data for video object" do - object = %{ - "type" => "Video", - "url" => [ - %{ - "type" => "Link", - "mimeType" => "video/mp4", - "href" => "https://peede8d-46fb-ad81-2d4c2d1630e3-480.mp4" - }, - %{ - "type" => "Link", - "mimeType" => "video/mp4", - "href" => "https://peertube46fb-ad81-2d4c2d1630e3-240.mp4" - }, - %{ - "type" => "Link", - "mimeType" => "text/html", - "href" => "https://peertube.-2d4c2d1630e3" - }, - %{ - "type" => "Link", - "mimeType" => "text/html", - "href" => "https://peertube.-2d4c2d16377-42" - } - ] - } - - assert Transmogrifier.fix_url(object) == %{ - "attachment" => [ - %{ - "href" => "https://peede8d-46fb-ad81-2d4c2d1630e3-480.mp4", - "mimeType" => "video/mp4", - "type" => "Link" - } - ], - "type" => "Video", - "url" => "https://peertube.-2d4c2d1630e3" - } - end - - test "fixes url for not Video object" do - object = %{ - "type" => "Text", - "url" => [ - %{ - "type" => "Link", - "mimeType" => "text/html", - "href" => "https://peertube.-2d4c2d1630e3" - }, - %{ - "type" => "Link", - "mimeType" => "text/html", - "href" => "https://peertube.-2d4c2d16377-42" - } - ] - } - - assert Transmogrifier.fix_url(object) == %{ - "type" => "Text", - "url" => "https://peertube.-2d4c2d1630e3" - } - - assert Transmogrifier.fix_url(%{"type" => "Text", "url" => []}) == %{ - "type" => "Text", - "url" => "" - } - end - - test "retunrs not modified object" do - assert Transmogrifier.fix_url(%{"type" => "Text"}) == %{"type" => "Text"} - end - end - - describe "get_obj_helper/2" do - test "returns nil when cannot normalize object" do - assert capture_log(fn -> - refute Transmogrifier.get_obj_helper("test-obj-id") - end) =~ "Unsupported URI scheme" - end - - @tag capture_log: true - test "returns {:ok, %Object{}} for success case" do - assert {:ok, %Object{}} = - Transmogrifier.get_obj_helper("https://shitposter.club/notice/2827873") - end - end - - describe "fix_attachments/1" do - test "returns not modified object" do - data = Poison.decode!(File.read!("test/fixtures/mastodon-post-activity.json")) - assert Transmogrifier.fix_attachments(data) == data - end - - test "returns modified object when attachment is map" do - assert Transmogrifier.fix_attachments(%{ - "attachment" => %{ - "mediaType" => "video/mp4", - "url" => "https://peertube.moe/stat-480.mp4" - } - }) == %{ - "attachment" => [ - %{ - "mediaType" => "video/mp4", - "type" => "Document", - "url" => [ - %{ - "href" => "https://peertube.moe/stat-480.mp4", - "mediaType" => "video/mp4", - "type" => "Link" - } - ] - } - ] - } - end - - test "returns modified object when attachment is list" do - assert Transmogrifier.fix_attachments(%{ - "attachment" => [ - %{"mediaType" => "video/mp4", "url" => "https://pe.er/stat-480.mp4"}, - %{"mimeType" => "video/mp4", "href" => "https://pe.er/stat-480.mp4"} - ] - }) == %{ - "attachment" => [ - %{ - "mediaType" => "video/mp4", - "type" => "Document", - "url" => [ - %{ - "href" => "https://pe.er/stat-480.mp4", - "mediaType" => "video/mp4", - "type" => "Link" - } - ] - }, - %{ - "mediaType" => "video/mp4", - "type" => "Document", - "url" => [ - %{ - "href" => "https://pe.er/stat-480.mp4", - "mediaType" => "video/mp4", - "type" => "Link" - } - ] - } - ] - } - end - end - - describe "fix_emoji/1" do - test "returns not modified object when object not contains tags" do - data = Poison.decode!(File.read!("test/fixtures/mastodon-post-activity.json")) - assert Transmogrifier.fix_emoji(data) == data - end - - test "returns object with emoji when object contains list tags" do - assert Transmogrifier.fix_emoji(%{ - "tag" => [ - %{"type" => "Emoji", "name" => ":bib:", "icon" => %{"url" => "/test"}}, - %{"type" => "Hashtag"} - ] - }) == %{ - "emoji" => %{"bib" => "/test"}, - "tag" => [ - %{"icon" => %{"url" => "/test"}, "name" => ":bib:", "type" => "Emoji"}, - %{"type" => "Hashtag"} - ] - } - end - - test "returns object with emoji when object contains map tag" do - assert Transmogrifier.fix_emoji(%{ - "tag" => %{"type" => "Emoji", "name" => ":bib:", "icon" => %{"url" => "/test"}} - }) == %{ - "emoji" => %{"bib" => "/test"}, - "tag" => %{"icon" => %{"url" => "/test"}, "name" => ":bib:", "type" => "Emoji"} - } - end - end - - describe "set_replies/1" do - setup do: clear_config([:activitypub, :note_replies_output_limit], 2) - - test "returns unmodified object if activity doesn't have self-replies" do - data = Poison.decode!(File.read!("test/fixtures/mastodon-post-activity.json")) - assert Transmogrifier.set_replies(data) == data - end - - test "sets `replies` collection with a limited number of self-replies" do - [user, another_user] = insert_list(2, :user) - - {:ok, %{id: id1} = activity} = CommonAPI.post(user, %{status: "1"}) - - {:ok, %{id: id2} = self_reply1} = - CommonAPI.post(user, %{status: "self-reply 1", in_reply_to_status_id: id1}) - - {:ok, self_reply2} = - CommonAPI.post(user, %{status: "self-reply 2", in_reply_to_status_id: id1}) - - # Assuming to _not_ be present in `replies` due to :note_replies_output_limit is set to 2 - {:ok, _} = CommonAPI.post(user, %{status: "self-reply 3", in_reply_to_status_id: id1}) - - {:ok, _} = - CommonAPI.post(user, %{ - status: "self-reply to self-reply", - in_reply_to_status_id: id2 - }) - - {:ok, _} = - CommonAPI.post(another_user, %{ - status: "another user's reply", - in_reply_to_status_id: id1 - }) - - object = Object.normalize(activity) - replies_uris = Enum.map([self_reply1, self_reply2], fn a -> a.object.data["id"] end) - - assert %{"type" => "Collection", "items" => ^replies_uris} = - Transmogrifier.set_replies(object.data)["replies"] - end - end - - test "take_emoji_tags/1" do - user = insert(:user, %{emoji: %{"firefox" => "https://example.org/firefox.png"}}) - - assert Transmogrifier.take_emoji_tags(user) == [ - %{ - "icon" => %{"type" => "Image", "url" => "https://example.org/firefox.png"}, - "id" => "https://example.org/firefox.png", - "name" => ":firefox:", - "type" => "Emoji", - "updated" => "1970-01-01T00:00:00Z" - } - ] - end -end diff --git a/test/web/activity_pub/utils_test.exs b/test/web/activity_pub/utils_test.exs deleted file mode 100644 index d50213545..000000000 --- a/test/web/activity_pub/utils_test.exs +++ /dev/null @@ -1,548 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.UtilsTest do - use Pleroma.DataCase - alias Pleroma.Activity - alias Pleroma.Object - alias Pleroma.Repo - alias Pleroma.User - alias Pleroma.Web.ActivityPub.Utils - alias Pleroma.Web.AdminAPI.AccountView - alias Pleroma.Web.CommonAPI - - import Pleroma.Factory - - require Pleroma.Constants - - describe "fetch the latest Follow" do - test "fetches the latest Follow activity" do - %Activity{data: %{"type" => "Follow"}} = activity = insert(:follow_activity) - follower = User.get_cached_by_ap_id(activity.data["actor"]) - followed = User.get_cached_by_ap_id(activity.data["object"]) - - assert activity == Utils.fetch_latest_follow(follower, followed) - end - end - - describe "determine_explicit_mentions()" do - test "works with an object that has mentions" do - object = %{ - "tag" => [ - %{ - "type" => "Mention", - "href" => "https://example.com/~alyssa", - "name" => "Alyssa P. Hacker" - } - ] - } - - assert Utils.determine_explicit_mentions(object) == ["https://example.com/~alyssa"] - end - - test "works with an object that does not have mentions" do - object = %{ - "tag" => [ - %{"type" => "Hashtag", "href" => "https://example.com/tag/2hu", "name" => "2hu"} - ] - } - - assert Utils.determine_explicit_mentions(object) == [] - end - - test "works with an object that has mentions and other tags" do - object = %{ - "tag" => [ - %{ - "type" => "Mention", - "href" => "https://example.com/~alyssa", - "name" => "Alyssa P. Hacker" - }, - %{"type" => "Hashtag", "href" => "https://example.com/tag/2hu", "name" => "2hu"} - ] - } - - assert Utils.determine_explicit_mentions(object) == ["https://example.com/~alyssa"] - end - - test "works with an object that has no tags" do - object = %{} - - assert Utils.determine_explicit_mentions(object) == [] - end - - test "works with an object that has only IR tags" do - object = %{"tag" => ["2hu"]} - - assert Utils.determine_explicit_mentions(object) == [] - end - - test "works with an object has tags as map" do - object = %{ - "tag" => %{ - "type" => "Mention", - "href" => "https://example.com/~alyssa", - "name" => "Alyssa P. Hacker" - } - } - - assert Utils.determine_explicit_mentions(object) == ["https://example.com/~alyssa"] - end - end - - describe "make_like_data" do - setup do - user = insert(:user) - other_user = insert(:user) - third_user = insert(:user) - [user: user, other_user: other_user, third_user: third_user] - end - - test "addresses actor's follower address if the activity is public", %{ - user: user, - other_user: other_user, - third_user: third_user - } do - expected_to = Enum.sort([user.ap_id, other_user.follower_address]) - expected_cc = Enum.sort(["https://www.w3.org/ns/activitystreams#Public", third_user.ap_id]) - - {:ok, activity} = - CommonAPI.post(user, %{ - status: - "hey @#{other_user.nickname}, @#{third_user.nickname} how about beering together this weekend?" - }) - - %{"to" => to, "cc" => cc} = Utils.make_like_data(other_user, activity, nil) - assert Enum.sort(to) == expected_to - assert Enum.sort(cc) == expected_cc - end - - test "does not adress actor's follower address if the activity is not public", %{ - user: user, - other_user: other_user, - third_user: third_user - } do - expected_to = Enum.sort([user.ap_id]) - expected_cc = [third_user.ap_id] - - {:ok, activity} = - CommonAPI.post(user, %{ - status: "@#{other_user.nickname} @#{third_user.nickname} bought a new swimsuit!", - visibility: "private" - }) - - %{"to" => to, "cc" => cc} = Utils.make_like_data(other_user, activity, nil) - assert Enum.sort(to) == expected_to - assert Enum.sort(cc) == expected_cc - end - end - - test "make_json_ld_header/0" do - assert Utils.make_json_ld_header() == %{ - "@context" => [ - "https://www.w3.org/ns/activitystreams", - "http://localhost:4001/schemas/litepub-0.1.jsonld", - %{ - "@language" => "und" - } - ] - } - end - - describe "get_existing_votes" do - test "fetches existing votes" do - user = insert(:user) - other_user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{ - status: "How do I pronounce LaTeX?", - poll: %{ - options: ["laytekh", "lahtekh", "latex"], - expires_in: 20, - multiple: true - } - }) - - object = Object.normalize(activity) - {:ok, votes, object} = CommonAPI.vote(other_user, object, [0, 1]) - assert Enum.sort(Utils.get_existing_votes(other_user.ap_id, object)) == Enum.sort(votes) - end - - test "fetches only Create activities" do - user = insert(:user) - other_user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{ - status: "Are we living in a society?", - poll: %{ - options: ["yes", "no"], - expires_in: 20 - } - }) - - object = Object.normalize(activity) - {:ok, [vote], object} = CommonAPI.vote(other_user, object, [0]) - {:ok, _activity} = CommonAPI.favorite(user, activity.id) - [fetched_vote] = Utils.get_existing_votes(other_user.ap_id, object) - assert fetched_vote.id == vote.id - end - end - - describe "update_follow_state_for_all/2" do - test "updates the state of all Follow activities with the same actor and object" do - user = insert(:user, locked: true) - follower = insert(:user) - - {:ok, _, _, follow_activity} = CommonAPI.follow(follower, user) - {:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user) - - data = - follow_activity_two.data - |> Map.put("state", "accept") - - cng = Ecto.Changeset.change(follow_activity_two, data: data) - - {:ok, follow_activity_two} = Repo.update(cng) - - {:ok, follow_activity_two} = - Utils.update_follow_state_for_all(follow_activity_two, "accept") - - assert refresh_record(follow_activity).data["state"] == "accept" - assert refresh_record(follow_activity_two).data["state"] == "accept" - end - end - - describe "update_follow_state/2" do - test "updates the state of the given follow activity" do - user = insert(:user, locked: true) - follower = insert(:user) - - {:ok, _, _, follow_activity} = CommonAPI.follow(follower, user) - {:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user) - - data = - follow_activity_two.data - |> Map.put("state", "accept") - - cng = Ecto.Changeset.change(follow_activity_two, data: data) - - {:ok, follow_activity_two} = Repo.update(cng) - - {:ok, follow_activity_two} = Utils.update_follow_state(follow_activity_two, "reject") - - assert refresh_record(follow_activity).data["state"] == "pending" - assert refresh_record(follow_activity_two).data["state"] == "reject" - end - end - - describe "update_element_in_object/3" do - test "updates likes" do - user = insert(:user) - activity = insert(:note_activity) - object = Object.normalize(activity) - - assert {:ok, updated_object} = - Utils.update_element_in_object( - "like", - [user.ap_id], - object - ) - - assert updated_object.data["likes"] == [user.ap_id] - assert updated_object.data["like_count"] == 1 - end - end - - describe "add_like_to_object/2" do - test "add actor to likes" do - user = insert(:user) - user2 = insert(:user) - object = insert(:note) - - assert {:ok, updated_object} = - Utils.add_like_to_object( - %Activity{data: %{"actor" => user.ap_id}}, - object - ) - - assert updated_object.data["likes"] == [user.ap_id] - assert updated_object.data["like_count"] == 1 - - assert {:ok, updated_object2} = - Utils.add_like_to_object( - %Activity{data: %{"actor" => user2.ap_id}}, - updated_object - ) - - assert updated_object2.data["likes"] == [user2.ap_id, user.ap_id] - assert updated_object2.data["like_count"] == 2 - end - end - - describe "remove_like_from_object/2" do - test "removes ap_id from likes" do - user = insert(:user) - user2 = insert(:user) - object = insert(:note, data: %{"likes" => [user.ap_id, user2.ap_id], "like_count" => 2}) - - assert {:ok, updated_object} = - Utils.remove_like_from_object( - %Activity{data: %{"actor" => user.ap_id}}, - object - ) - - assert updated_object.data["likes"] == [user2.ap_id] - assert updated_object.data["like_count"] == 1 - end - end - - describe "get_existing_like/2" do - test "fetches existing like" do - note_activity = insert(:note_activity) - assert object = Object.normalize(note_activity) - - user = insert(:user) - refute Utils.get_existing_like(user.ap_id, object) - {:ok, like_activity} = CommonAPI.favorite(user, note_activity.id) - - assert ^like_activity = Utils.get_existing_like(user.ap_id, object) - end - end - - describe "get_get_existing_announce/2" do - test "returns nil if announce not found" do - actor = insert(:user) - refute Utils.get_existing_announce(actor.ap_id, %{data: %{"id" => "test"}}) - end - - test "fetches existing announce" do - note_activity = insert(:note_activity) - assert object = Object.normalize(note_activity) - actor = insert(:user) - - {:ok, announce} = CommonAPI.repeat(note_activity.id, actor) - assert Utils.get_existing_announce(actor.ap_id, object) == announce - end - end - - describe "fetch_latest_block/2" do - test "fetches last block activities" do - user1 = insert(:user) - user2 = insert(:user) - - 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 - end - - describe "recipient_in_message/3" do - test "returns true when recipient in `to`" do - recipient = insert(:user) - actor = insert(:user) - assert Utils.recipient_in_message(recipient, actor, %{"to" => recipient.ap_id}) - - assert Utils.recipient_in_message( - recipient, - actor, - %{"to" => [recipient.ap_id], "cc" => ""} - ) - end - - test "returns true when recipient in `cc`" do - recipient = insert(:user) - actor = insert(:user) - assert Utils.recipient_in_message(recipient, actor, %{"cc" => recipient.ap_id}) - - assert Utils.recipient_in_message( - recipient, - actor, - %{"cc" => [recipient.ap_id], "to" => ""} - ) - end - - test "returns true when recipient in `bto`" do - recipient = insert(:user) - actor = insert(:user) - assert Utils.recipient_in_message(recipient, actor, %{"bto" => recipient.ap_id}) - - assert Utils.recipient_in_message( - recipient, - actor, - %{"bcc" => "", "bto" => [recipient.ap_id]} - ) - end - - test "returns true when recipient in `bcc`" do - recipient = insert(:user) - actor = insert(:user) - assert Utils.recipient_in_message(recipient, actor, %{"bcc" => recipient.ap_id}) - - assert Utils.recipient_in_message( - recipient, - actor, - %{"bto" => "", "bcc" => [recipient.ap_id]} - ) - end - - test "returns true when message without addresses fields" do - recipient = insert(:user) - actor = insert(:user) - assert Utils.recipient_in_message(recipient, actor, %{"bccc" => recipient.ap_id}) - - assert Utils.recipient_in_message( - recipient, - actor, - %{"btod" => "", "bccc" => [recipient.ap_id]} - ) - end - - test "returns false" do - recipient = insert(:user) - actor = insert(:user) - refute Utils.recipient_in_message(recipient, actor, %{"to" => "ap_id"}) - end - end - - describe "lazy_put_activity_defaults/2" do - test "returns map with id and published data" do - note_activity = insert(:note_activity) - object = Object.normalize(note_activity) - res = Utils.lazy_put_activity_defaults(%{"context" => object.data["id"]}) - assert res["context"] == object.data["id"] - assert res["context_id"] == object.id - assert res["id"] - assert res["published"] - end - - test "returns map with fake id and published data" do - assert %{ - "context" => "pleroma:fakecontext", - "context_id" => -1, - "id" => "pleroma:fakeid", - "published" => _ - } = Utils.lazy_put_activity_defaults(%{}, true) - end - - test "returns activity data with object" do - note_activity = insert(:note_activity) - object = Object.normalize(note_activity) - - res = - Utils.lazy_put_activity_defaults(%{ - "context" => object.data["id"], - "object" => %{} - }) - - assert res["context"] == object.data["id"] - assert res["context_id"] == object.id - assert res["id"] - assert res["published"] - assert res["object"]["id"] - assert res["object"]["published"] - assert res["object"]["context"] == object.data["id"] - assert res["object"]["context_id"] == object.id - end - end - - describe "make_flag_data" do - test "returns empty map when params is invalid" do - assert Utils.make_flag_data(%{}, %{}) == %{} - end - - test "returns map with Flag object" do - reporter = insert(:user) - target_account = insert(:user) - {:ok, activity} = CommonAPI.post(target_account, %{status: "foobar"}) - context = Utils.generate_context_id() - content = "foobar" - - target_ap_id = target_account.ap_id - activity_ap_id = activity.data["id"] - - res = - Utils.make_flag_data( - %{ - actor: reporter, - context: context, - account: target_account, - statuses: [%{"id" => activity.data["id"]}], - content: content - }, - %{} - ) - - note_obj = %{ - "type" => "Note", - "id" => activity_ap_id, - "content" => content, - "published" => activity.object.data["published"], - "actor" => - AccountView.render("show.json", %{user: target_account, skip_visibility_check: true}) - } - - assert %{ - "type" => "Flag", - "content" => ^content, - "context" => ^context, - "object" => [^target_ap_id, ^note_obj], - "state" => "open" - } = res - end - end - - describe "add_announce_to_object/2" do - test "adds actor to announcement" do - user = insert(:user) - object = insert(:note) - - activity = - insert(:note_activity, - data: %{ - "actor" => user.ap_id, - "cc" => [Pleroma.Constants.as_public()] - } - ) - - assert {:ok, updated_object} = Utils.add_announce_to_object(activity, object) - assert updated_object.data["announcements"] == [user.ap_id] - assert updated_object.data["announcement_count"] == 1 - end - end - - describe "remove_announce_from_object/2" do - test "removes actor from announcements" do - user = insert(:user) - user2 = insert(:user) - - object = - insert(:note, - data: %{"announcements" => [user.ap_id, user2.ap_id], "announcement_count" => 2} - ) - - activity = insert(:note_activity, data: %{"actor" => user.ap_id}) - - assert {:ok, updated_object} = Utils.remove_announce_from_object(activity, object) - assert updated_object.data["announcements"] == [user2.ap_id] - assert updated_object.data["announcement_count"] == 1 - end - end - - describe "get_cached_emoji_reactions/1" do - test "returns the data or an emtpy list" do - object = insert(:note) - assert Utils.get_cached_emoji_reactions(object) == [] - - object = insert(:note, data: %{"reactions" => [["x", ["lain"]]]}) - assert Utils.get_cached_emoji_reactions(object) == [["x", ["lain"]]] - - object = insert(:note, data: %{"reactions" => %{}}) - assert Utils.get_cached_emoji_reactions(object) == [] - end - end -end diff --git a/test/web/activity_pub/views/object_view_test.exs b/test/web/activity_pub/views/object_view_test.exs deleted file mode 100644 index f0389845d..000000000 --- a/test/web/activity_pub/views/object_view_test.exs +++ /dev/null @@ -1,84 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.ObjectViewTest do - use Pleroma.DataCase - import Pleroma.Factory - - alias Pleroma.Object - alias Pleroma.Web.ActivityPub.ObjectView - alias Pleroma.Web.CommonAPI - - test "renders a note object" do - note = insert(:note) - - result = ObjectView.render("object.json", %{object: note}) - - assert result["id"] == note.data["id"] - assert result["to"] == note.data["to"] - assert result["content"] == note.data["content"] - assert result["type"] == "Note" - assert result["@context"] - end - - test "renders a note activity" do - note = insert(:note_activity) - object = Object.normalize(note) - - result = ObjectView.render("object.json", %{object: note}) - - assert result["id"] == note.data["id"] - assert result["to"] == note.data["to"] - assert result["object"]["type"] == "Note" - assert result["object"]["content"] == object.data["content"] - assert result["type"] == "Create" - assert result["@context"] - end - - describe "note activity's `replies` collection rendering" do - setup do: clear_config([:activitypub, :note_replies_output_limit], 5) - - test "renders `replies` collection for a note activity" do - user = insert(:user) - activity = insert(:note_activity, user: user) - - {:ok, self_reply1} = - CommonAPI.post(user, %{status: "self-reply 1", in_reply_to_status_id: activity.id}) - - replies_uris = [self_reply1.object.data["id"]] - result = ObjectView.render("object.json", %{object: refresh_record(activity)}) - - assert %{"type" => "Collection", "items" => ^replies_uris} = - get_in(result, ["object", "replies"]) - end - end - - test "renders a like activity" do - note = insert(:note_activity) - object = Object.normalize(note) - user = insert(:user) - - {:ok, like_activity} = CommonAPI.favorite(user, note.id) - - result = ObjectView.render("object.json", %{object: like_activity}) - - assert result["id"] == like_activity.data["id"] - assert result["object"] == object.data["id"] - assert result["type"] == "Like" - end - - test "renders an announce activity" do - note = insert(:note_activity) - object = Object.normalize(note) - user = insert(:user) - - {:ok, announce_activity} = CommonAPI.repeat(note.id, user) - - result = ObjectView.render("object.json", %{object: announce_activity}) - - assert result["id"] == announce_activity.data["id"] - assert result["object"] == object.data["id"] - assert result["type"] == "Announce" - end -end diff --git a/test/web/activity_pub/views/user_view_test.exs b/test/web/activity_pub/views/user_view_test.exs deleted file mode 100644 index 98c7c9d09..000000000 --- a/test/web/activity_pub/views/user_view_test.exs +++ /dev/null @@ -1,180 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.UserViewTest do - use Pleroma.DataCase - import Pleroma.Factory - - alias Pleroma.User - alias Pleroma.Web.ActivityPub.UserView - alias Pleroma.Web.CommonAPI - - test "Renders a user, including the public key" do - user = insert(:user) - {:ok, user} = User.ensure_keys_present(user) - - result = UserView.render("user.json", %{user: user}) - - assert result["id"] == user.ap_id - assert result["preferredUsername"] == user.nickname - - assert String.contains?(result["publicKey"]["publicKeyPem"], "BEGIN PUBLIC KEY") - end - - test "Renders profile fields" do - fields = [ - %{"name" => "foo", "value" => "bar"} - ] - - {:ok, user} = - insert(:user) - |> User.update_changeset(%{fields: fields}) - |> User.update_and_set_cache() - - assert %{ - "attachment" => [%{"name" => "foo", "type" => "PropertyValue", "value" => "bar"}] - } = UserView.render("user.json", %{user: user}) - end - - test "Renders with emoji tags" do - user = insert(:user, emoji: %{"bib" => "/test"}) - - assert %{ - "tag" => [ - %{ - "icon" => %{"type" => "Image", "url" => "/test"}, - "id" => "/test", - "name" => ":bib:", - "type" => "Emoji", - "updated" => "1970-01-01T00:00:00Z" - } - ] - } = UserView.render("user.json", %{user: user}) - end - - test "Does not add an avatar image if the user hasn't set one" do - user = insert(:user) - {:ok, user} = User.ensure_keys_present(user) - - result = UserView.render("user.json", %{user: user}) - refute result["icon"] - refute result["image"] - - user = - insert(:user, - avatar: %{"url" => [%{"href" => "https://someurl"}]}, - banner: %{"url" => [%{"href" => "https://somebanner"}]} - ) - - {:ok, user} = User.ensure_keys_present(user) - - result = UserView.render("user.json", %{user: user}) - assert result["icon"]["url"] == "https://someurl" - assert result["image"]["url"] == "https://somebanner" - end - - test "renders an invisible user with the invisible property set to true" do - user = insert(:user, invisible: true) - - assert %{"invisible" => true} = UserView.render("service.json", %{user: user}) - end - - describe "endpoints" do - test "local users have a usable endpoints structure" do - user = insert(:user) - {:ok, user} = User.ensure_keys_present(user) - - result = UserView.render("user.json", %{user: user}) - - assert result["id"] == user.ap_id - - %{ - "sharedInbox" => _, - "oauthAuthorizationEndpoint" => _, - "oauthRegistrationEndpoint" => _, - "oauthTokenEndpoint" => _ - } = result["endpoints"] - end - - test "remote users have an empty endpoints structure" do - user = insert(:user, local: false) - {:ok, user} = User.ensure_keys_present(user) - - result = UserView.render("user.json", %{user: user}) - - assert result["id"] == user.ap_id - assert result["endpoints"] == %{} - end - - test "instance users do not expose oAuth endpoints" do - user = insert(:user, nickname: nil, local: true) - {:ok, user} = User.ensure_keys_present(user) - - result = UserView.render("user.json", %{user: user}) - - refute result["endpoints"]["oauthAuthorizationEndpoint"] - refute result["endpoints"]["oauthRegistrationEndpoint"] - refute result["endpoints"]["oauthTokenEndpoint"] - end - end - - describe "followers" do - test "sets totalItems to zero when followers are hidden" do - user = insert(:user) - other_user = insert(:user) - {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user) - assert %{"totalItems" => 1} = UserView.render("followers.json", %{user: user}) - user = Map.merge(user, %{hide_followers_count: true, hide_followers: true}) - refute UserView.render("followers.json", %{user: user}) |> Map.has_key?("totalItems") - end - - test "sets correct totalItems when followers are hidden but the follower counter is not" do - user = insert(:user) - other_user = insert(:user) - {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user) - assert %{"totalItems" => 1} = UserView.render("followers.json", %{user: user}) - user = Map.merge(user, %{hide_followers_count: false, hide_followers: true}) - assert %{"totalItems" => 1} = UserView.render("followers.json", %{user: user}) - end - end - - describe "following" do - test "sets totalItems to zero when follows are hidden" do - user = insert(:user) - other_user = insert(:user) - {:ok, user, _other_user, _activity} = CommonAPI.follow(user, other_user) - assert %{"totalItems" => 1} = UserView.render("following.json", %{user: user}) - user = Map.merge(user, %{hide_follows_count: true, hide_follows: true}) - assert %{"totalItems" => 0} = UserView.render("following.json", %{user: user}) - end - - test "sets correct totalItems when follows are hidden but the follow counter is not" do - user = insert(:user) - other_user = insert(:user) - {:ok, user, _other_user, _activity} = CommonAPI.follow(user, other_user) - assert %{"totalItems" => 1} = UserView.render("following.json", %{user: user}) - user = Map.merge(user, %{hide_follows_count: false, hide_follows: true}) - assert %{"totalItems" => 1} = UserView.render("following.json", %{user: user}) - end - end - - describe "acceptsChatMessages" do - test "it returns this value if it is set" do - true_user = insert(:user, accepts_chat_messages: true) - false_user = insert(:user, accepts_chat_messages: false) - nil_user = insert(:user, accepts_chat_messages: nil) - - assert %{"capabilities" => %{"acceptsChatMessages" => true}} = - UserView.render("user.json", user: true_user) - - assert %{"capabilities" => %{"acceptsChatMessages" => false}} = - UserView.render("user.json", user: false_user) - - refute Map.has_key?( - UserView.render("user.json", user: nil_user)["capabilities"], - "acceptsChatMessages" - ) - end - end -end diff --git a/test/web/activity_pub/visibilty_test.exs b/test/web/activity_pub/visibilty_test.exs deleted file mode 100644 index 8e9354c65..000000000 --- a/test/web/activity_pub/visibilty_test.exs +++ /dev/null @@ -1,230 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.VisibilityTest do - use Pleroma.DataCase - - alias Pleroma.Activity - alias Pleroma.Web.ActivityPub.Visibility - alias Pleroma.Web.CommonAPI - import Pleroma.Factory - - setup do - user = insert(:user) - mentioned = insert(:user) - following = insert(:user) - unrelated = insert(:user) - {:ok, following} = Pleroma.User.follow(following, user) - {:ok, list} = Pleroma.List.create("foo", user) - - Pleroma.List.follow(list, unrelated) - - {:ok, public} = - CommonAPI.post(user, %{status: "@#{mentioned.nickname}", visibility: "public"}) - - {:ok, private} = - CommonAPI.post(user, %{status: "@#{mentioned.nickname}", visibility: "private"}) - - {:ok, direct} = - CommonAPI.post(user, %{status: "@#{mentioned.nickname}", visibility: "direct"}) - - {:ok, unlisted} = - CommonAPI.post(user, %{status: "@#{mentioned.nickname}", visibility: "unlisted"}) - - {:ok, list} = - CommonAPI.post(user, %{ - status: "@#{mentioned.nickname}", - visibility: "list:#{list.id}" - }) - - %{ - public: public, - private: private, - direct: direct, - unlisted: unlisted, - user: user, - mentioned: mentioned, - following: following, - unrelated: unrelated, - list: list - } - end - - test "is_direct?", %{ - public: public, - private: private, - direct: direct, - unlisted: unlisted, - list: list - } do - assert Visibility.is_direct?(direct) - refute Visibility.is_direct?(public) - refute Visibility.is_direct?(private) - refute Visibility.is_direct?(unlisted) - assert Visibility.is_direct?(list) - end - - test "is_public?", %{ - public: public, - private: private, - direct: direct, - unlisted: unlisted, - list: list - } do - refute Visibility.is_public?(direct) - assert Visibility.is_public?(public) - refute Visibility.is_public?(private) - assert Visibility.is_public?(unlisted) - refute Visibility.is_public?(list) - end - - test "is_private?", %{ - public: public, - private: private, - direct: direct, - unlisted: unlisted, - list: list - } do - refute Visibility.is_private?(direct) - refute Visibility.is_private?(public) - assert Visibility.is_private?(private) - refute Visibility.is_private?(unlisted) - refute Visibility.is_private?(list) - end - - test "is_list?", %{ - public: public, - private: private, - direct: direct, - unlisted: unlisted, - list: list - } do - refute Visibility.is_list?(direct) - refute Visibility.is_list?(public) - refute Visibility.is_list?(private) - refute Visibility.is_list?(unlisted) - assert Visibility.is_list?(list) - end - - test "visible_for_user?", %{ - public: public, - private: private, - direct: direct, - unlisted: unlisted, - user: user, - mentioned: mentioned, - following: following, - unrelated: unrelated, - list: list - } do - # All visible to author - - assert Visibility.visible_for_user?(public, user) - assert Visibility.visible_for_user?(private, user) - assert Visibility.visible_for_user?(unlisted, user) - assert Visibility.visible_for_user?(direct, user) - assert Visibility.visible_for_user?(list, user) - - # All visible to a mentioned user - - assert Visibility.visible_for_user?(public, mentioned) - assert Visibility.visible_for_user?(private, mentioned) - assert Visibility.visible_for_user?(unlisted, mentioned) - assert Visibility.visible_for_user?(direct, mentioned) - assert Visibility.visible_for_user?(list, mentioned) - - # DM not visible for just follower - - assert Visibility.visible_for_user?(public, following) - assert Visibility.visible_for_user?(private, following) - assert Visibility.visible_for_user?(unlisted, following) - refute Visibility.visible_for_user?(direct, following) - refute Visibility.visible_for_user?(list, following) - - # Public and unlisted visible for unrelated user - - assert Visibility.visible_for_user?(public, unrelated) - assert Visibility.visible_for_user?(unlisted, unrelated) - refute Visibility.visible_for_user?(private, unrelated) - refute Visibility.visible_for_user?(direct, unrelated) - - # Visible for a list member - assert Visibility.visible_for_user?(list, unrelated) - end - - test "doesn't die when the user doesn't exist", - %{ - direct: direct, - user: user - } do - Repo.delete(user) - Cachex.clear(:user_cache) - refute Visibility.is_private?(direct) - end - - test "get_visibility", %{ - public: public, - private: private, - direct: direct, - unlisted: unlisted, - list: list - } do - assert Visibility.get_visibility(public) == "public" - assert Visibility.get_visibility(private) == "private" - assert Visibility.get_visibility(direct) == "direct" - assert Visibility.get_visibility(unlisted) == "unlisted" - assert Visibility.get_visibility(list) == "list" - end - - test "get_visibility with directMessage flag" do - assert Visibility.get_visibility(%{data: %{"directMessage" => true}}) == "direct" - end - - test "get_visibility with listMessage flag" do - assert Visibility.get_visibility(%{data: %{"listMessage" => ""}}) == "list" - end - - describe "entire_thread_visible_for_user?/2" do - test "returns false if not found activity", %{user: user} do - refute Visibility.entire_thread_visible_for_user?(%Activity{}, user) - end - - test "returns true if activity hasn't 'Create' type", %{user: user} do - activity = insert(:like_activity) - assert Visibility.entire_thread_visible_for_user?(activity, user) - end - - test "returns false when invalid recipients", %{user: user} do - author = insert(:user) - - activity = - insert(:note_activity, - note: - insert(:note, - user: author, - data: %{"to" => ["test-user"]} - ) - ) - - refute Visibility.entire_thread_visible_for_user?(activity, user) - end - - test "returns true if user following to author" do - author = insert(:user) - user = insert(:user) - Pleroma.User.follow(user, author) - - activity = - insert(:note_activity, - note: - insert(:note, - user: author, - data: %{"to" => [user.ap_id]} - ) - ) - - assert Visibility.entire_thread_visible_for_user?(activity, user) - end - end -end |