1 # Pleroma: A lightweight social networking server
2 # Copyright © 2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Web.ActivityPub.PublisherTest do
6 use Pleroma.Web.ConnCase
8 import ExUnit.CaptureLog
13 alias Pleroma.Activity
14 alias Pleroma.Instances
16 alias Pleroma.Web.ActivityPub.Publisher
17 alias Pleroma.Web.CommonAPI
19 @as_public "https://www.w3.org/ns/activitystreams#Public"
22 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
26 describe "determine_inbox/2" do
27 test "it returns sharedInbox for messages involving as:Public in to" do
30 info: %{source_data: %{"endpoints" => %{"sharedInbox" => "http://example.com/inbox"}}}
34 data: %{"to" => [@as_public], "cc" => [user.follower_address]}
37 assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox"
40 test "it returns sharedInbox for messages involving as:Public in cc" do
43 info: %{source_data: %{"endpoints" => %{"sharedInbox" => "http://example.com/inbox"}}}
47 data: %{"cc" => [@as_public], "to" => [user.follower_address]}
50 assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox"
53 test "it returns sharedInbox for messages involving multiple recipients in to" do
56 info: %{source_data: %{"endpoints" => %{"sharedInbox" => "http://example.com/inbox"}}}
59 user_two = insert(:user)
60 user_three = insert(:user)
63 data: %{"cc" => [], "to" => [user.ap_id, user_two.ap_id, user_three.ap_id]}
66 assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox"
69 test "it returns sharedInbox for messages involving multiple recipients in cc" do
72 info: %{source_data: %{"endpoints" => %{"sharedInbox" => "http://example.com/inbox"}}}
75 user_two = insert(:user)
76 user_three = insert(:user)
79 data: %{"to" => [], "cc" => [user.ap_id, user_two.ap_id, user_three.ap_id]}
82 assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox"
85 test "it returns sharedInbox for messages involving multiple recipients in total" do
90 "inbox" => "http://example.com/personal-inbox",
91 "endpoints" => %{"sharedInbox" => "http://example.com/inbox"}
96 user_two = insert(:user)
99 data: %{"to" => [user_two.ap_id], "cc" => [user.ap_id]}
102 assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox"
105 test "it returns inbox for messages involving single recipients in total" do
110 "inbox" => "http://example.com/personal-inbox",
111 "endpoints" => %{"sharedInbox" => "http://example.com/inbox"}
116 activity = %Activity{
117 data: %{"to" => [user.ap_id], "cc" => []}
120 assert Publisher.determine_inbox(activity, user) == "http://example.com/personal-inbox"
124 describe "publish_one/1" do
125 test_with_mock "calls `Instances.set_reachable` on successful federation if `unreachable_since` is not specified",
129 actor = insert(:user)
130 inbox = "http://200.site/users/nick1/inbox"
132 assert {:ok, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
134 assert called(Instances.set_reachable(inbox))
137 test_with_mock "calls `Instances.set_reachable` on successful federation if `unreachable_since` is set",
141 actor = insert(:user)
142 inbox = "http://200.site/users/nick1/inbox"
145 Publisher.publish_one(%{
150 unreachable_since: NaiveDateTime.utc_now()
153 assert called(Instances.set_reachable(inbox))
156 test_with_mock "does NOT call `Instances.set_reachable` on successful federation if `unreachable_since` is nil",
160 actor = insert(:user)
161 inbox = "http://200.site/users/nick1/inbox"
164 Publisher.publish_one(%{
169 unreachable_since: nil
172 refute called(Instances.set_reachable(inbox))
175 test_with_mock "calls `Instances.set_unreachable` on target inbox on non-2xx HTTP response code",
179 actor = insert(:user)
180 inbox = "http://404.site/users/nick1/inbox"
182 assert {:error, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
184 assert called(Instances.set_unreachable(inbox))
187 test_with_mock "it calls `Instances.set_unreachable` on target inbox on request error of any kind",
191 actor = insert(:user)
192 inbox = "http://connrefused.site/users/nick1/inbox"
194 assert capture_log(fn ->
196 Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
197 end) =~ "connrefused"
199 assert called(Instances.set_unreachable(inbox))
202 test_with_mock "does NOT call `Instances.set_unreachable` if target is reachable",
206 actor = insert(:user)
207 inbox = "http://200.site/users/nick1/inbox"
209 assert {:ok, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
211 refute called(Instances.set_unreachable(inbox))
214 test_with_mock "does NOT call `Instances.set_unreachable` if target instance has non-nil `unreachable_since`",
218 actor = insert(:user)
219 inbox = "http://connrefused.site/users/nick1/inbox"
221 assert capture_log(fn ->
223 Publisher.publish_one(%{
228 unreachable_since: NaiveDateTime.utc_now()
230 end) =~ "connrefused"
232 refute called(Instances.set_unreachable(inbox))
236 describe "publish/2" do
237 test_with_mock "publishes an activity with BCC to all relevant peers.",
238 Pleroma.Web.Federator.Publisher,
246 source_data: %{"inbox" => "https://domain.com/users/nick1/inbox"}
250 actor = insert(:user, follower_address: follower.ap_id)
253 {:ok, _follower_one} = Pleroma.User.follow(follower, actor)
254 actor = refresh_record(actor)
257 insert(:note_activity,
258 recipients: [follower.ap_id],
259 data_attrs: %{"bcc" => [user.ap_id]}
262 res = Publisher.publish(actor, note_activity)
266 Pleroma.Web.Federator.Publisher.enqueue_one(Publisher, %{
267 inbox: "https://domain.com/users/nick1/inbox",
269 id: note_activity.data["id"]
274 test_with_mock "publishes a delete activity to peers who signed fetch requests to the create acitvity/object.",
275 Pleroma.Web.Federator.Publisher,
283 source_data: %{"inbox" => "https://domain.com/users/nick1/inbox"}
292 source_data: %{"inbox" => "https://domain2.com/users/nick1/inbox"}
296 actor = insert(:user)
298 note_activity = insert(:note_activity, user: actor)
299 object = Object.normalize(note_activity)
301 activity_path = String.trim_leading(note_activity.data["id"], Pleroma.Web.Endpoint.url())
302 object_path = String.trim_leading(object.data["id"], Pleroma.Web.Endpoint.url())
305 |> put_req_header("accept", "application/activity+json")
306 |> assign(:user, fetcher)
308 |> json_response(200)
311 |> put_req_header("accept", "application/activity+json")
312 |> assign(:user, another_fetcher)
313 |> get(activity_path)
314 |> json_response(200)
316 {:ok, delete} = CommonAPI.delete(note_activity.id, actor)
318 res = Publisher.publish(actor, delete)
322 Pleroma.Web.Federator.Publisher.enqueue_one(Publisher, %{
323 inbox: "https://domain.com/users/nick1/inbox",
325 id: delete.data["id"]
330 Pleroma.Web.Federator.Publisher.enqueue_one(Publisher, %{
331 inbox: "https://domain2.com/users/nick1/inbox",
333 id: delete.data["id"]