# Pleroma: A lightweight social networking server
-# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2019 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
+
import Pleroma.Factory
alias Pleroma.Activity
+ alias Pleroma.Delivery
alias Pleroma.Instances
alias Pleroma.Object
+ alias Pleroma.Tests.ObanHelpers
alias Pleroma.User
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.Workers.ReceiverWorker
setup_all do
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
:ok
end
+ clear_config_all([:instance, :federating],
+ do: Pleroma.Config.put([:instance, :federating], true)
+ )
+
describe "/relay" 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(404)
|> assert
+ end
+ end
- Pleroma.Config.put([:instance, :allow_relay], true)
+ 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
end
assert json_response(conn, 404)
end
- end
- describe "/object/:uuid/likes" do
- test "it returns the like activities in a collection", %{conn: conn} do
- like = insert(:like_activity)
- like_object_ap_id = Object.normalize(like).data["id"]
- uuid = String.split(like_object_ap_id, "/") |> List.last()
+ test "it caches a response", %{conn: conn} do
+ note = insert(:note)
+ uuid = String.split(note.data["id"], "/") |> List.last()
- result =
+ conn1 =
conn
|> put_req_header("accept", "application/activity+json")
- |> get("/objects/#{uuid}/likes")
- |> json_response(200)
+ |> 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 List.first(result["first"]["orderedItems"])["id"] == like.data["id"]
+ 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
end
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_by_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
end
describe "/inbox" do
|> post("/inbox", data)
assert "ok" == json_response(conn, 200)
- :timer.sleep(500)
+
+ ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
assert Activity.get_by_ap_id(data["id"])
end
|> post("/users/#{user.nickname}/inbox", data)
assert "ok" == json_response(conn, 200)
- :timer.sleep(500)
+ ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
assert Activity.get_by_ap_id(data["id"])
end
|> post("/users/#{recipient.nickname}/inbox", data)
assert "ok" == json_response(conn, 200)
- :timer.sleep(500)
+ ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
assert Activity.get_by_ap_id(data["id"])
end
assert json_response(conn, 403)
end
+ test "it doesn't crash without an authenticated user", %{conn: conn} do
+ user = insert(:user)
+
+ conn =
+ conn
+ |> 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)
conn
|> assign(:user, user)
|> put_req_header("accept", "application/activity+json")
- |> get("/users/#{user.nickname}/inbox")
+ |> get("/users/#{user.nickname}/inbox?page=true")
assert response(conn, 200) =~ note_object.data["content"]
end
|> 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
conn =
conn
|> put_req_header("accept", "application/activity+json")
- |> get("/users/#{user.nickname}/outbox")
+ |> get("/users/#{user.nickname}/outbox?page=true")
assert response(conn, 200) =~ note_object.data["content"]
end
conn =
conn
|> put_req_header("accept", "application/activity+json")
- |> get("/users/#{user.nickname}/outbox")
+ |> get("/users/#{user.nickname}/outbox?page=true")
assert response(conn, 200) =~ announce_activity.data["object"]
end
|> post("/users/#{user.nickname}/outbox", data)
result = json_response(conn, 201)
+
assert Activity.get_by_ap_id(result["id"])
end
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
+ |> assign(:relay, true)
+ |> get("/relay/followers")
+ |> json_response(200)
+
+ assert result["first"]["orderedItems"] == [user.ap_id]
+ end
+ end
+
+ describe "/relay/following" do
+ test "it returns relay following", %{conn: conn} do
+ result =
+ conn
+ |> assign(:relay, true)
+ |> get("/relay/following")
+ |> json_response(200)
+
+ assert result["first"]["orderedItems"] == []
+ end
+ end
+
describe "/users/:nickname/followers" do
test "it returns the followers in a collection", %{conn: conn} do
user = insert(:user)
assert result["first"]["orderedItems"] == [user.ap_id]
end
- test "it returns returns empty if the user has 'hide_followers' set", %{conn: conn} do
+ test "it returns returns a uri if the user has 'hide_followers' set", %{conn: conn} do
user = insert(:user)
user_two = insert(:user, %{info: %{hide_followers: true}})
User.follow(user, user_two)
|> get("/users/#{user_two.nickname}/followers")
|> json_response(200)
- assert result["first"]["orderedItems"] == []
- assert result["totalItems"] == 0
+ 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 not authenticated",
+ %{conn: conn} do
+ user = insert(:user, %{info: %{hide_followers: true}})
+
+ result =
+ conn
+ |> get("/users/#{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, %{info: %{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
assert result["first"]["orderedItems"] == [user_two.ap_id]
end
- test "it returns returns empty if the user has 'hide_follows' set", %{conn: conn} do
+ test "it returns a uri if the user has 'hide_follows' set", %{conn: conn} do
user = insert(:user, %{info: %{hide_follows: true}})
user_two = insert(:user)
User.follow(user, user_two)
|> get("/users/#{user.nickname}/following")
|> json_response(200)
- assert result["first"]["orderedItems"] == []
- assert result["totalItems"] == 0
+ 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 not authenticated",
+ %{conn: conn} do
+ user = insert(:user, %{info: %{hide_follows: true}})
+
+ result =
+ conn
+ |> get("/users/#{user.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, %{info: %{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
assert result["totalItems"] == 15
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 "Additionnal ActivityPub C2S endpoints" do
+ test "/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)
+ end
+
+ clear_config([:media_proxy])
+ clear_config([Pleroma.Upload])
+
+ test "uploadMedia", %{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"
+ }
+
+ conn =
+ conn
+ |> assign(:user, user)
+ |> post("/api/ap/upload_media", %{"file" => image, "description" => desc})
+
+ assert object = json_response(conn, :created)
+ assert object["name"] == desc
+ assert object["type"] == "Document"
+ assert object["actor"] == user.ap_id
+ end
+ end
end