logo

pleroma

My custom branche(s) on git.pleroma.social/pleroma/pleroma git clone https://hacktivis.me/git/pleroma.git

publisher_test.exs (14487B)


  1. # Pleroma: A lightweight social networking server
  2. # Copyright © 2017-2022 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 {:discard, _} =
  172. Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
  173. assert called(Instances.set_unreachable(inbox))
  174. end
  175. test_with_mock "it calls `Instances.set_unreachable` on target inbox on request error of any kind",
  176. Instances,
  177. [:passthrough],
  178. [] do
  179. actor = insert(:user)
  180. inbox = "http://connrefused.site/users/nick1/inbox"
  181. assert capture_log(fn ->
  182. assert {:error, _} =
  183. Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
  184. end) =~ "connrefused"
  185. assert called(Instances.set_unreachable(inbox))
  186. end
  187. test_with_mock "does NOT call `Instances.set_unreachable` if target is reachable",
  188. Instances,
  189. [:passthrough],
  190. [] do
  191. actor = insert(:user)
  192. inbox = "http://200.site/users/nick1/inbox"
  193. assert {:ok, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
  194. refute called(Instances.set_unreachable(inbox))
  195. end
  196. test_with_mock "does NOT call `Instances.set_unreachable` if target instance has non-nil `unreachable_since`",
  197. Instances,
  198. [:passthrough],
  199. [] do
  200. actor = insert(:user)
  201. inbox = "http://connrefused.site/users/nick1/inbox"
  202. assert capture_log(fn ->
  203. assert {:error, _} =
  204. Publisher.publish_one(%{
  205. inbox: inbox,
  206. json: "{}",
  207. actor: actor,
  208. id: 1,
  209. unreachable_since: NaiveDateTime.utc_now()
  210. })
  211. end) =~ "connrefused"
  212. refute called(Instances.set_unreachable(inbox))
  213. end
  214. end
  215. describe "publish/2" do
  216. test_with_mock "doesn't publish a non-public activity to quarantined instances.",
  217. Pleroma.Web.ActivityPub.Publisher,
  218. [:passthrough],
  219. [] do
  220. Config.put([:instance, :quarantined_instances], [{"domain.com", "some reason"}])
  221. follower =
  222. insert(:user, %{
  223. local: false,
  224. inbox: "https://domain.com/users/nick1/inbox"
  225. })
  226. actor = insert(:user, follower_address: follower.ap_id)
  227. {:ok, follower, actor} = Pleroma.User.follow(follower, actor)
  228. actor = refresh_record(actor)
  229. note_activity =
  230. insert(:followers_only_note_activity,
  231. user: actor,
  232. recipients: [follower.ap_id]
  233. )
  234. res = Publisher.publish(actor, note_activity)
  235. assert res == :ok
  236. assert not called(
  237. Publisher.enqueue_one(%{
  238. inbox: "https://domain.com/users/nick1/inbox",
  239. actor_id: actor.id,
  240. id: note_activity.data["id"]
  241. })
  242. )
  243. end
  244. test_with_mock "Publishes a non-public activity to non-quarantined instances.",
  245. Pleroma.Web.ActivityPub.Publisher,
  246. [:passthrough],
  247. [] do
  248. Config.put([:instance, :quarantined_instances], [{"somedomain.com", "some reason"}])
  249. follower =
  250. insert(:user, %{
  251. local: false,
  252. inbox: "https://domain.com/users/nick1/inbox"
  253. })
  254. actor = insert(:user, follower_address: follower.ap_id)
  255. {:ok, follower, actor} = Pleroma.User.follow(follower, actor)
  256. actor = refresh_record(actor)
  257. note_activity =
  258. insert(:followers_only_note_activity,
  259. user: actor,
  260. recipients: [follower.ap_id]
  261. )
  262. res = Publisher.publish(actor, note_activity)
  263. assert res == :ok
  264. assert called(
  265. Publisher.enqueue_one(
  266. %{
  267. inbox: "https://domain.com/users/nick1/inbox",
  268. actor_id: actor.id,
  269. id: note_activity.data["id"]
  270. },
  271. priority: 1
  272. )
  273. )
  274. end
  275. test_with_mock "Publishes to directly addressed actors with higher priority.",
  276. Pleroma.Web.ActivityPub.Publisher,
  277. [:passthrough],
  278. [] do
  279. note_activity = insert(:direct_note_activity)
  280. actor = Pleroma.User.get_by_ap_id(note_activity.data["actor"])
  281. res = Publisher.publish(actor, note_activity)
  282. assert res == :ok
  283. assert called(
  284. Publisher.enqueue_one(
  285. %{
  286. inbox: :_,
  287. actor_id: actor.id,
  288. id: note_activity.data["id"]
  289. },
  290. priority: 0
  291. )
  292. )
  293. end
  294. test_with_mock "publishes an activity with BCC to all relevant peers.",
  295. Pleroma.Web.ActivityPub.Publisher,
  296. [:passthrough],
  297. [] do
  298. follower =
  299. insert(:user, %{
  300. local: false,
  301. inbox: "https://domain.com/users/nick1/inbox"
  302. })
  303. actor = insert(:user, follower_address: follower.ap_id)
  304. user = insert(:user)
  305. {:ok, follower, actor} = Pleroma.User.follow(follower, actor)
  306. note_activity =
  307. insert(:note_activity,
  308. recipients: [follower.ap_id],
  309. data_attrs: %{"bcc" => [user.ap_id]}
  310. )
  311. res = Publisher.publish(actor, note_activity)
  312. assert res == :ok
  313. assert called(
  314. Publisher.enqueue_one(%{
  315. inbox: "https://domain.com/users/nick1/inbox",
  316. actor_id: actor.id,
  317. id: note_activity.data["id"]
  318. })
  319. )
  320. end
  321. test_with_mock "publishes a delete activity to peers who signed fetch requests to the create acitvity/object.",
  322. Pleroma.Web.ActivityPub.Publisher,
  323. [:passthrough],
  324. [] do
  325. fetcher =
  326. insert(:user,
  327. local: false,
  328. inbox: "https://domain.com/users/nick1/inbox"
  329. )
  330. another_fetcher =
  331. insert(:user,
  332. local: false,
  333. inbox: "https://domain2.com/users/nick1/inbox"
  334. )
  335. actor = insert(:user)
  336. note_activity = insert(:note_activity, user: actor)
  337. object = Object.normalize(note_activity, fetch: false)
  338. activity_path = String.trim_leading(note_activity.data["id"], Pleroma.Web.Endpoint.url())
  339. object_path = String.trim_leading(object.data["id"], Pleroma.Web.Endpoint.url())
  340. build_conn()
  341. |> put_req_header("accept", "application/activity+json")
  342. |> assign(:user, fetcher)
  343. |> get(object_path)
  344. |> json_response(200)
  345. build_conn()
  346. |> put_req_header("accept", "application/activity+json")
  347. |> assign(:user, another_fetcher)
  348. |> get(activity_path)
  349. |> json_response(200)
  350. {:ok, delete} = CommonAPI.delete(note_activity.id, actor)
  351. res = Publisher.publish(actor, delete)
  352. assert res == :ok
  353. assert called(
  354. Publisher.enqueue_one(
  355. %{
  356. inbox: "https://domain.com/users/nick1/inbox",
  357. actor_id: actor.id,
  358. id: delete.data["id"]
  359. },
  360. priority: 1
  361. )
  362. )
  363. assert called(
  364. Publisher.enqueue_one(
  365. %{
  366. inbox: "https://domain2.com/users/nick1/inbox",
  367. actor_id: actor.id,
  368. id: delete.data["id"]
  369. },
  370. priority: 1
  371. )
  372. )
  373. end
  374. end
  375. end