Fork of Pleroma with site-specific changes and feature branches https://git.pleroma.social/pleroma/pleroma
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

365 lines
11KB

  1. # Pleroma: A lightweight social networking server
  2. # Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
  3. # SPDX-License-Identifier: AGPL-3.0-only
  4. defmodule Pleroma.Web.ActivityPub.PublisherTest do
  5. use Pleroma.Web.ConnCase
  6. import ExUnit.CaptureLog
  7. import Pleroma.Factory
  8. import Tesla.Mock
  9. import Mock
  10. alias Pleroma.Activity
  11. alias Pleroma.Instances
  12. alias Pleroma.Object
  13. alias Pleroma.Web.ActivityPub.Publisher
  14. alias Pleroma.Web.CommonAPI
  15. @as_public "https://www.w3.org/ns/activitystreams#Public"
  16. setup do
  17. mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
  18. :ok
  19. end
  20. setup_all do: clear_config([:instance, :federating], true)
  21. describe "gather_webfinger_links/1" do
  22. test "it returns links" do
  23. user = insert(:user)
  24. expected_links = [
  25. %{"href" => user.ap_id, "rel" => "self", "type" => "application/activity+json"},
  26. %{
  27. "href" => user.ap_id,
  28. "rel" => "self",
  29. "type" => "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""
  30. },
  31. %{
  32. "rel" => "http://ostatus.org/schema/1.0/subscribe",
  33. "template" => "#{Pleroma.Web.Endpoint.url()}/ostatus_subscribe?acct={uri}"
  34. }
  35. ]
  36. assert expected_links == Publisher.gather_webfinger_links(user)
  37. end
  38. end
  39. describe "determine_inbox/2" do
  40. test "it returns sharedInbox for messages involving as:Public in to" do
  41. user = insert(:user, %{shared_inbox: "http://example.com/inbox"})
  42. activity = %Activity{
  43. data: %{"to" => [@as_public], "cc" => [user.follower_address]}
  44. }
  45. assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox"
  46. end
  47. test "it returns sharedInbox for messages involving as:Public in cc" do
  48. user = insert(:user, %{shared_inbox: "http://example.com/inbox"})
  49. activity = %Activity{
  50. data: %{"cc" => [@as_public], "to" => [user.follower_address]}
  51. }
  52. assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox"
  53. end
  54. test "it returns sharedInbox for messages involving multiple recipients in to" do
  55. user = insert(:user, %{shared_inbox: "http://example.com/inbox"})
  56. user_two = insert(:user)
  57. user_three = insert(:user)
  58. activity = %Activity{
  59. data: %{"cc" => [], "to" => [user.ap_id, user_two.ap_id, user_three.ap_id]}
  60. }
  61. assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox"
  62. end
  63. test "it returns sharedInbox for messages involving multiple recipients in cc" do
  64. user = insert(:user, %{shared_inbox: "http://example.com/inbox"})
  65. user_two = insert(:user)
  66. user_three = insert(:user)
  67. activity = %Activity{
  68. data: %{"to" => [], "cc" => [user.ap_id, user_two.ap_id, user_three.ap_id]}
  69. }
  70. assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox"
  71. end
  72. test "it returns sharedInbox for messages involving multiple recipients in total" do
  73. user =
  74. insert(:user, %{
  75. shared_inbox: "http://example.com/inbox",
  76. inbox: "http://example.com/personal-inbox"
  77. })
  78. user_two = insert(:user)
  79. activity = %Activity{
  80. data: %{"to" => [user_two.ap_id], "cc" => [user.ap_id]}
  81. }
  82. assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox"
  83. end
  84. test "it returns inbox for messages involving single recipients in total" do
  85. user =
  86. insert(:user, %{
  87. shared_inbox: "http://example.com/inbox",
  88. inbox: "http://example.com/personal-inbox"
  89. })
  90. activity = %Activity{
  91. data: %{"to" => [user.ap_id], "cc" => []}
  92. }
  93. assert Publisher.determine_inbox(activity, user) == "http://example.com/personal-inbox"
  94. end
  95. end
  96. describe "publish_one/1" do
  97. test "publish to url with with different ports" do
  98. inbox80 = "http://42.site/users/nick1/inbox"
  99. inbox42 = "http://42.site:42/users/nick1/inbox"
  100. mock(fn
  101. %{method: :post, url: "http://42.site:42/users/nick1/inbox"} ->
  102. {:ok, %Tesla.Env{status: 200, body: "port 42"}}
  103. %{method: :post, url: "http://42.site/users/nick1/inbox"} ->
  104. {:ok, %Tesla.Env{status: 200, body: "port 80"}}
  105. end)
  106. actor = insert(:user)
  107. assert {:ok, %{body: "port 42"}} =
  108. Publisher.publish_one(%{
  109. inbox: inbox42,
  110. json: "{}",
  111. actor: actor,
  112. id: 1,
  113. unreachable_since: true
  114. })
  115. assert {:ok, %{body: "port 80"}} =
  116. Publisher.publish_one(%{
  117. inbox: inbox80,
  118. json: "{}",
  119. actor: actor,
  120. id: 1,
  121. unreachable_since: true
  122. })
  123. end
  124. test_with_mock "calls `Instances.set_reachable` on successful federation if `unreachable_since` is not specified",
  125. Instances,
  126. [:passthrough],
  127. [] do
  128. actor = insert(:user)
  129. inbox = "http://200.site/users/nick1/inbox"
  130. assert {:ok, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
  131. assert called(Instances.set_reachable(inbox))
  132. end
  133. test_with_mock "calls `Instances.set_reachable` on successful federation if `unreachable_since` is set",
  134. Instances,
  135. [:passthrough],
  136. [] do
  137. actor = insert(:user)
  138. inbox = "http://200.site/users/nick1/inbox"
  139. assert {:ok, _} =
  140. Publisher.publish_one(%{
  141. inbox: inbox,
  142. json: "{}",
  143. actor: actor,
  144. id: 1,
  145. unreachable_since: NaiveDateTime.utc_now()
  146. })
  147. assert called(Instances.set_reachable(inbox))
  148. end
  149. test_with_mock "does NOT call `Instances.set_reachable` on successful federation if `unreachable_since` is nil",
  150. Instances,
  151. [:passthrough],
  152. [] do
  153. actor = insert(:user)
  154. inbox = "http://200.site/users/nick1/inbox"
  155. assert {:ok, _} =
  156. Publisher.publish_one(%{
  157. inbox: inbox,
  158. json: "{}",
  159. actor: actor,
  160. id: 1,
  161. unreachable_since: nil
  162. })
  163. refute called(Instances.set_reachable(inbox))
  164. end
  165. test_with_mock "calls `Instances.set_unreachable` on target inbox on non-2xx HTTP response code",
  166. Instances,
  167. [:passthrough],
  168. [] do
  169. actor = insert(:user)
  170. inbox = "http://404.site/users/nick1/inbox"
  171. assert {:error, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
  172. assert called(Instances.set_unreachable(inbox))
  173. end
  174. test_with_mock "it calls `Instances.set_unreachable` on target inbox on request error of any kind",
  175. Instances,
  176. [:passthrough],
  177. [] do
  178. actor = insert(:user)
  179. inbox = "http://connrefused.site/users/nick1/inbox"
  180. assert capture_log(fn ->
  181. assert {:error, _} =
  182. Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
  183. end) =~ "connrefused"
  184. assert called(Instances.set_unreachable(inbox))
  185. end
  186. test_with_mock "does NOT call `Instances.set_unreachable` if target is reachable",
  187. Instances,
  188. [:passthrough],
  189. [] do
  190. actor = insert(:user)
  191. inbox = "http://200.site/users/nick1/inbox"
  192. assert {:ok, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
  193. refute called(Instances.set_unreachable(inbox))
  194. end
  195. test_with_mock "does NOT call `Instances.set_unreachable` if target instance has non-nil `unreachable_since`",
  196. Instances,
  197. [:passthrough],
  198. [] do
  199. actor = insert(:user)
  200. inbox = "http://connrefused.site/users/nick1/inbox"
  201. assert capture_log(fn ->
  202. assert {:error, _} =
  203. Publisher.publish_one(%{
  204. inbox: inbox,
  205. json: "{}",
  206. actor: actor,
  207. id: 1,
  208. unreachable_since: NaiveDateTime.utc_now()
  209. })
  210. end) =~ "connrefused"
  211. refute called(Instances.set_unreachable(inbox))
  212. end
  213. end
  214. describe "publish/2" do
  215. test_with_mock "publishes an activity with BCC to all relevant peers.",
  216. Pleroma.Web.Federator.Publisher,
  217. [:passthrough],
  218. [] do
  219. follower =
  220. insert(:user, %{
  221. local: false,
  222. inbox: "https://domain.com/users/nick1/inbox",
  223. ap_enabled: true
  224. })
  225. actor = insert(:user, follower_address: follower.ap_id)
  226. user = insert(:user)
  227. {:ok, follower, actor} = Pleroma.User.follow(follower, actor)
  228. note_activity =
  229. insert(:note_activity,
  230. recipients: [follower.ap_id],
  231. data_attrs: %{"bcc" => [user.ap_id]}
  232. )
  233. res = Publisher.publish(actor, note_activity)
  234. assert res == :ok
  235. assert called(
  236. Pleroma.Web.Federator.Publisher.enqueue_one(Publisher, %{
  237. inbox: "https://domain.com/users/nick1/inbox",
  238. actor_id: actor.id,
  239. id: note_activity.data["id"]
  240. })
  241. )
  242. end
  243. test_with_mock "publishes a delete activity to peers who signed fetch requests to the create acitvity/object.",
  244. Pleroma.Web.Federator.Publisher,
  245. [:passthrough],
  246. [] do
  247. fetcher =
  248. insert(:user,
  249. local: false,
  250. inbox: "https://domain.com/users/nick1/inbox",
  251. ap_enabled: true
  252. )
  253. another_fetcher =
  254. insert(:user,
  255. local: false,
  256. inbox: "https://domain2.com/users/nick1/inbox",
  257. ap_enabled: true
  258. )
  259. actor = insert(:user)
  260. note_activity = insert(:note_activity, user: actor)
  261. object = Object.normalize(note_activity, fetch: false)
  262. activity_path = String.trim_leading(note_activity.data["id"], Pleroma.Web.Endpoint.url())
  263. object_path = String.trim_leading(object.data["id"], Pleroma.Web.Endpoint.url())
  264. build_conn()
  265. |> put_req_header("accept", "application/activity+json")
  266. |> assign(:user, fetcher)
  267. |> get(object_path)
  268. |> json_response(200)
  269. build_conn()
  270. |> put_req_header("accept", "application/activity+json")
  271. |> assign(:user, another_fetcher)
  272. |> get(activity_path)
  273. |> json_response(200)
  274. {:ok, delete} = CommonAPI.delete(note_activity.id, actor)
  275. res = Publisher.publish(actor, delete)
  276. assert res == :ok
  277. assert called(
  278. Pleroma.Web.Federator.Publisher.enqueue_one(Publisher, %{
  279. inbox: "https://domain.com/users/nick1/inbox",
  280. actor_id: actor.id,
  281. id: delete.data["id"]
  282. })
  283. )
  284. assert called(
  285. Pleroma.Web.Federator.Publisher.enqueue_one(Publisher, %{
  286. inbox: "https://domain2.com/users/nick1/inbox",
  287. actor_id: actor.id,
  288. id: delete.data["id"]
  289. })
  290. )
  291. end
  292. end
  293. end