logo

pleroma

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

publisher_test.exs (14832B)


  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 "should_federate?/1" do
  22. test "it returns false when the inbox is nil" do
  23. refute Publisher.should_federate?(nil, false)
  24. refute Publisher.should_federate?(nil, true)
  25. end
  26. test "it returns true when public is true" do
  27. assert Publisher.should_federate?(false, true)
  28. end
  29. end
  30. describe "gather_webfinger_links/1" do
  31. test "it returns links" do
  32. user = insert(:user)
  33. expected_links = [
  34. %{"href" => user.ap_id, "rel" => "self", "type" => "application/activity+json"},
  35. %{
  36. "href" => user.ap_id,
  37. "rel" => "self",
  38. "type" => "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""
  39. },
  40. %{
  41. "rel" => "http://ostatus.org/schema/1.0/subscribe",
  42. "template" => "#{Pleroma.Web.Endpoint.url()}/ostatus_subscribe?acct={uri}"
  43. }
  44. ]
  45. assert expected_links == Publisher.gather_webfinger_links(user)
  46. end
  47. end
  48. describe "determine_inbox/2" do
  49. test "it returns sharedInbox for messages involving as:Public in to" do
  50. user = insert(:user, %{shared_inbox: "http://example.com/inbox"})
  51. activity = %Activity{
  52. data: %{"to" => [@as_public], "cc" => [user.follower_address]}
  53. }
  54. assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox"
  55. end
  56. test "it returns sharedInbox for messages involving as:Public in cc" do
  57. user = insert(:user, %{shared_inbox: "http://example.com/inbox"})
  58. activity = %Activity{
  59. data: %{"cc" => [@as_public], "to" => [user.follower_address]}
  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 to" 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: %{"cc" => [], "to" => [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 cc" do
  73. user = insert(:user, %{shared_inbox: "http://example.com/inbox"})
  74. user_two = insert(:user)
  75. user_three = insert(:user)
  76. activity = %Activity{
  77. data: %{"to" => [], "cc" => [user.ap_id, user_two.ap_id, user_three.ap_id]}
  78. }
  79. assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox"
  80. end
  81. test "it returns sharedInbox for messages involving multiple recipients in total" do
  82. user =
  83. insert(:user, %{
  84. shared_inbox: "http://example.com/inbox",
  85. inbox: "http://example.com/personal-inbox"
  86. })
  87. user_two = insert(:user)
  88. activity = %Activity{
  89. data: %{"to" => [user_two.ap_id], "cc" => [user.ap_id]}
  90. }
  91. assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox"
  92. end
  93. test "it returns inbox for messages involving single recipients in total" do
  94. user =
  95. insert(:user, %{
  96. shared_inbox: "http://example.com/inbox",
  97. inbox: "http://example.com/personal-inbox"
  98. })
  99. activity = %Activity{
  100. data: %{"to" => [user.ap_id], "cc" => []}
  101. }
  102. assert Publisher.determine_inbox(activity, user) == "http://example.com/personal-inbox"
  103. end
  104. end
  105. describe "publish_one/1" do
  106. test "publish to url with with different ports" do
  107. inbox80 = "http://42.site/users/nick1/inbox"
  108. inbox42 = "http://42.site:42/users/nick1/inbox"
  109. mock(fn
  110. %{method: :post, url: "http://42.site:42/users/nick1/inbox"} ->
  111. {:ok, %Tesla.Env{status: 200, body: "port 42"}}
  112. %{method: :post, url: "http://42.site/users/nick1/inbox"} ->
  113. {:ok, %Tesla.Env{status: 200, body: "port 80"}}
  114. end)
  115. actor = insert(:user)
  116. assert {:ok, %{body: "port 42"}} =
  117. Publisher.publish_one(%{
  118. inbox: inbox42,
  119. json: "{}",
  120. actor: actor,
  121. id: 1,
  122. unreachable_since: true
  123. })
  124. assert {:ok, %{body: "port 80"}} =
  125. Publisher.publish_one(%{
  126. inbox: inbox80,
  127. json: "{}",
  128. actor: actor,
  129. id: 1,
  130. unreachable_since: true
  131. })
  132. end
  133. test_with_mock "calls `Instances.set_reachable` on successful federation if `unreachable_since` is not specified",
  134. Instances,
  135. [:passthrough],
  136. [] do
  137. actor = insert(:user)
  138. inbox = "http://200.site/users/nick1/inbox"
  139. assert {:ok, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
  140. assert called(Instances.set_reachable(inbox))
  141. end
  142. test_with_mock "calls `Instances.set_reachable` on successful federation if `unreachable_since` is set",
  143. Instances,
  144. [:passthrough],
  145. [] do
  146. actor = insert(:user)
  147. inbox = "http://200.site/users/nick1/inbox"
  148. assert {:ok, _} =
  149. Publisher.publish_one(%{
  150. inbox: inbox,
  151. json: "{}",
  152. actor: actor,
  153. id: 1,
  154. unreachable_since: NaiveDateTime.utc_now()
  155. })
  156. assert called(Instances.set_reachable(inbox))
  157. end
  158. test_with_mock "does NOT call `Instances.set_reachable` on successful federation if `unreachable_since` is nil",
  159. Instances,
  160. [:passthrough],
  161. [] do
  162. actor = insert(:user)
  163. inbox = "http://200.site/users/nick1/inbox"
  164. assert {:ok, _} =
  165. Publisher.publish_one(%{
  166. inbox: inbox,
  167. json: "{}",
  168. actor: actor,
  169. id: 1,
  170. unreachable_since: nil
  171. })
  172. refute called(Instances.set_reachable(inbox))
  173. end
  174. @tag capture_log: true
  175. test_with_mock "calls `Instances.set_unreachable` on target inbox on non-2xx HTTP response code",
  176. Instances,
  177. [:passthrough],
  178. [] do
  179. actor = insert(:user)
  180. inbox = "http://404.site/users/nick1/inbox"
  181. assert {:discard, _} =
  182. Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
  183. assert called(Instances.set_unreachable(inbox))
  184. end
  185. test_with_mock "it calls `Instances.set_unreachable` on target inbox on request error of any kind",
  186. Instances,
  187. [:passthrough],
  188. [] do
  189. actor = insert(:user)
  190. inbox = "http://connrefused.site/users/nick1/inbox"
  191. assert capture_log(fn ->
  192. assert {:error, _} =
  193. Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
  194. end) =~ "connrefused"
  195. assert called(Instances.set_unreachable(inbox))
  196. end
  197. test_with_mock "does NOT call `Instances.set_unreachable` if target is reachable",
  198. Instances,
  199. [:passthrough],
  200. [] do
  201. actor = insert(:user)
  202. inbox = "http://200.site/users/nick1/inbox"
  203. assert {:ok, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
  204. refute called(Instances.set_unreachable(inbox))
  205. end
  206. test_with_mock "does NOT call `Instances.set_unreachable` if target instance has non-nil `unreachable_since`",
  207. Instances,
  208. [:passthrough],
  209. [] do
  210. actor = insert(:user)
  211. inbox = "http://connrefused.site/users/nick1/inbox"
  212. assert capture_log(fn ->
  213. assert {:error, _} =
  214. Publisher.publish_one(%{
  215. inbox: inbox,
  216. json: "{}",
  217. actor: actor,
  218. id: 1,
  219. unreachable_since: NaiveDateTime.utc_now()
  220. })
  221. end) =~ "connrefused"
  222. refute called(Instances.set_unreachable(inbox))
  223. end
  224. end
  225. describe "publish/2" do
  226. test_with_mock "doesn't publish a non-public activity to quarantined instances.",
  227. Pleroma.Web.ActivityPub.Publisher,
  228. [:passthrough],
  229. [] do
  230. Config.put([:instance, :quarantined_instances], [{"domain.com", "some reason"}])
  231. follower =
  232. insert(:user, %{
  233. local: false,
  234. inbox: "https://domain.com/users/nick1/inbox"
  235. })
  236. actor = insert(:user, follower_address: follower.ap_id)
  237. {:ok, follower, actor} = Pleroma.User.follow(follower, actor)
  238. actor = refresh_record(actor)
  239. note_activity =
  240. insert(:followers_only_note_activity,
  241. user: actor,
  242. recipients: [follower.ap_id]
  243. )
  244. res = Publisher.publish(actor, note_activity)
  245. assert res == :ok
  246. assert not called(
  247. Publisher.enqueue_one(%{
  248. inbox: "https://domain.com/users/nick1/inbox",
  249. actor_id: actor.id,
  250. id: note_activity.data["id"]
  251. })
  252. )
  253. end
  254. test_with_mock "Publishes a non-public activity to non-quarantined instances.",
  255. Pleroma.Web.ActivityPub.Publisher,
  256. [:passthrough],
  257. [] do
  258. Config.put([:instance, :quarantined_instances], [{"somedomain.com", "some reason"}])
  259. follower =
  260. insert(:user, %{
  261. local: false,
  262. inbox: "https://domain.com/users/nick1/inbox"
  263. })
  264. actor = insert(:user, follower_address: follower.ap_id)
  265. {:ok, follower, actor} = Pleroma.User.follow(follower, actor)
  266. actor = refresh_record(actor)
  267. note_activity =
  268. insert(:followers_only_note_activity,
  269. user: actor,
  270. recipients: [follower.ap_id]
  271. )
  272. res = Publisher.publish(actor, note_activity)
  273. assert res == :ok
  274. assert called(
  275. Publisher.enqueue_one(
  276. %{
  277. inbox: "https://domain.com/users/nick1/inbox",
  278. actor_id: actor.id,
  279. id: note_activity.data["id"]
  280. },
  281. priority: 1
  282. )
  283. )
  284. end
  285. test_with_mock "Publishes to directly addressed actors with higher priority.",
  286. Pleroma.Web.ActivityPub.Publisher,
  287. [:passthrough],
  288. [] do
  289. note_activity = insert(:direct_note_activity)
  290. actor = Pleroma.User.get_by_ap_id(note_activity.data["actor"])
  291. res = Publisher.publish(actor, note_activity)
  292. assert res == :ok
  293. assert called(
  294. Publisher.enqueue_one(
  295. %{
  296. inbox: :_,
  297. actor_id: actor.id,
  298. id: note_activity.data["id"]
  299. },
  300. priority: 0
  301. )
  302. )
  303. end
  304. test_with_mock "publishes an activity with BCC to all relevant peers.",
  305. Pleroma.Web.ActivityPub.Publisher,
  306. [:passthrough],
  307. [] do
  308. follower =
  309. insert(:user, %{
  310. local: false,
  311. inbox: "https://domain.com/users/nick1/inbox"
  312. })
  313. actor = insert(:user, follower_address: follower.ap_id)
  314. user = insert(:user)
  315. {:ok, follower, actor} = Pleroma.User.follow(follower, actor)
  316. note_activity =
  317. insert(:note_activity,
  318. recipients: [follower.ap_id],
  319. data_attrs: %{"bcc" => [user.ap_id]}
  320. )
  321. res = Publisher.publish(actor, note_activity)
  322. assert res == :ok
  323. assert called(
  324. Publisher.enqueue_one(%{
  325. inbox: "https://domain.com/users/nick1/inbox",
  326. actor_id: actor.id,
  327. id: note_activity.data["id"]
  328. })
  329. )
  330. end
  331. test_with_mock "publishes a delete activity to peers who signed fetch requests to the create acitvity/object.",
  332. Pleroma.Web.ActivityPub.Publisher,
  333. [:passthrough],
  334. [] do
  335. fetcher =
  336. insert(:user,
  337. local: false,
  338. inbox: "https://domain.com/users/nick1/inbox"
  339. )
  340. another_fetcher =
  341. insert(:user,
  342. local: false,
  343. inbox: "https://domain2.com/users/nick1/inbox"
  344. )
  345. actor = insert(:user)
  346. note_activity = insert(:note_activity, user: actor)
  347. object = Object.normalize(note_activity, fetch: false)
  348. activity_path = String.trim_leading(note_activity.data["id"], Pleroma.Web.Endpoint.url())
  349. object_path = String.trim_leading(object.data["id"], Pleroma.Web.Endpoint.url())
  350. build_conn()
  351. |> put_req_header("accept", "application/activity+json")
  352. |> assign(:user, fetcher)
  353. |> get(object_path)
  354. |> json_response(200)
  355. build_conn()
  356. |> put_req_header("accept", "application/activity+json")
  357. |> assign(:user, another_fetcher)
  358. |> get(activity_path)
  359. |> json_response(200)
  360. {:ok, delete} = CommonAPI.delete(note_activity.id, actor)
  361. res = Publisher.publish(actor, delete)
  362. assert res == :ok
  363. assert called(
  364. Publisher.enqueue_one(
  365. %{
  366. inbox: "https://domain.com/users/nick1/inbox",
  367. actor_id: actor.id,
  368. id: delete.data["id"]
  369. },
  370. priority: 1
  371. )
  372. )
  373. assert called(
  374. Publisher.enqueue_one(
  375. %{
  376. inbox: "https://domain2.com/users/nick1/inbox",
  377. actor_id: actor.id,
  378. id: delete.data["id"]
  379. },
  380. priority: 1
  381. )
  382. )
  383. end
  384. end
  385. end