logo

pleroma

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

publisher_test.exs (15860B)


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