logo

pleroma

My custom branche(s) on git.pleroma.social/pleroma/pleroma

side_effects_test.exs (21236B)


  1. # Pleroma: A lightweight social networking server
  2. # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
  3. # SPDX-License-Identifier: AGPL-3.0-only
  4. defmodule Pleroma.Web.ActivityPub.SideEffectsTest do
  5. use Oban.Testing, repo: Pleroma.Repo
  6. use Pleroma.DataCase
  7. alias Pleroma.Activity
  8. alias Pleroma.Chat
  9. alias Pleroma.Chat.MessageReference
  10. alias Pleroma.Notification
  11. alias Pleroma.Object
  12. alias Pleroma.Repo
  13. alias Pleroma.Tests.ObanHelpers
  14. alias Pleroma.User
  15. alias Pleroma.Web.ActivityPub.ActivityPub
  16. alias Pleroma.Web.ActivityPub.Builder
  17. alias Pleroma.Web.ActivityPub.SideEffects
  18. alias Pleroma.Web.CommonAPI
  19. import ExUnit.CaptureLog
  20. import Mock
  21. import Pleroma.Factory
  22. describe "handle_after_transaction" do
  23. test "it streams out notifications and streams" do
  24. author = insert(:user, local: true)
  25. recipient = insert(:user, local: true)
  26. {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey")
  27. {:ok, create_activity_data, _meta} =
  28. Builder.create(author, chat_message_data["id"], [recipient.ap_id])
  29. {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false)
  30. {:ok, _create_activity, meta} =
  31. SideEffects.handle(create_activity, local: false, object_data: chat_message_data)
  32. assert [notification] = meta[:notifications]
  33. with_mocks([
  34. {
  35. Pleroma.Web.Streamer,
  36. [],
  37. [
  38. stream: fn _, _ -> nil end
  39. ]
  40. },
  41. {
  42. Pleroma.Web.Push,
  43. [],
  44. [
  45. send: fn _ -> nil end
  46. ]
  47. }
  48. ]) do
  49. SideEffects.handle_after_transaction(meta)
  50. assert called(Pleroma.Web.Streamer.stream(["user", "user:notification"], notification))
  51. assert called(Pleroma.Web.Streamer.stream(["user", "user:pleroma_chat"], :_))
  52. assert called(Pleroma.Web.Push.send(notification))
  53. end
  54. end
  55. end
  56. describe "blocking users" do
  57. setup do
  58. user = insert(:user)
  59. blocked = insert(:user)
  60. User.follow(blocked, user)
  61. User.follow(user, blocked)
  62. {:ok, block_data, []} = Builder.block(user, blocked)
  63. {:ok, block, _meta} = ActivityPub.persist(block_data, local: true)
  64. %{user: user, blocked: blocked, block: block}
  65. end
  66. test "it unfollows and blocks", %{user: user, blocked: blocked, block: block} do
  67. assert User.following?(user, blocked)
  68. assert User.following?(blocked, user)
  69. {:ok, _, _} = SideEffects.handle(block)
  70. refute User.following?(user, blocked)
  71. refute User.following?(blocked, user)
  72. assert User.blocks?(user, blocked)
  73. end
  74. test "it blocks but does not unfollow if the relevant setting is set", %{
  75. user: user,
  76. blocked: blocked,
  77. block: block
  78. } do
  79. clear_config([:activitypub, :unfollow_blocked], false)
  80. assert User.following?(user, blocked)
  81. assert User.following?(blocked, user)
  82. {:ok, _, _} = SideEffects.handle(block)
  83. refute User.following?(user, blocked)
  84. assert User.following?(blocked, user)
  85. assert User.blocks?(user, blocked)
  86. end
  87. end
  88. describe "update users" do
  89. setup do
  90. user = insert(:user)
  91. {:ok, update_data, []} = Builder.update(user, %{"id" => user.ap_id, "name" => "new name!"})
  92. {:ok, update, _meta} = ActivityPub.persist(update_data, local: true)
  93. %{user: user, update_data: update_data, update: update}
  94. end
  95. test "it updates the user", %{user: user, update: update} do
  96. {:ok, _, _} = SideEffects.handle(update)
  97. user = User.get_by_id(user.id)
  98. assert user.name == "new name!"
  99. end
  100. test "it uses a given changeset to update", %{user: user, update: update} do
  101. changeset = Ecto.Changeset.change(user, %{default_scope: "direct"})
  102. assert user.default_scope == "public"
  103. {:ok, _, _} = SideEffects.handle(update, user_update_changeset: changeset)
  104. user = User.get_by_id(user.id)
  105. assert user.default_scope == "direct"
  106. end
  107. end
  108. describe "delete objects" do
  109. setup do
  110. user = insert(:user)
  111. other_user = insert(:user)
  112. {:ok, op} = CommonAPI.post(other_user, %{status: "big oof"})
  113. {:ok, post} = CommonAPI.post(user, %{status: "hey", in_reply_to_id: op})
  114. {:ok, favorite} = CommonAPI.favorite(user, post.id)
  115. object = Object.normalize(post)
  116. {:ok, delete_data, _meta} = Builder.delete(user, object.data["id"])
  117. {:ok, delete_user_data, _meta} = Builder.delete(user, user.ap_id)
  118. {:ok, delete, _meta} = ActivityPub.persist(delete_data, local: true)
  119. {:ok, delete_user, _meta} = ActivityPub.persist(delete_user_data, local: true)
  120. %{
  121. user: user,
  122. delete: delete,
  123. post: post,
  124. object: object,
  125. delete_user: delete_user,
  126. op: op,
  127. favorite: favorite
  128. }
  129. end
  130. test "it handles object deletions", %{
  131. delete: delete,
  132. post: post,
  133. object: object,
  134. user: user,
  135. op: op,
  136. favorite: favorite
  137. } do
  138. with_mock Pleroma.Web.ActivityPub.ActivityPub, [:passthrough],
  139. stream_out: fn _ -> nil end,
  140. stream_out_participations: fn _, _ -> nil end do
  141. {:ok, delete, _} = SideEffects.handle(delete)
  142. user = User.get_cached_by_ap_id(object.data["actor"])
  143. assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out(delete))
  144. assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out_participations(object, user))
  145. end
  146. object = Object.get_by_id(object.id)
  147. assert object.data["type"] == "Tombstone"
  148. refute Activity.get_by_id(post.id)
  149. refute Activity.get_by_id(favorite.id)
  150. user = User.get_by_id(user.id)
  151. assert user.note_count == 0
  152. object = Object.normalize(op.data["object"], false)
  153. assert object.data["repliesCount"] == 0
  154. end
  155. test "it handles object deletions when the object itself has been pruned", %{
  156. delete: delete,
  157. post: post,
  158. object: object,
  159. user: user,
  160. op: op
  161. } do
  162. with_mock Pleroma.Web.ActivityPub.ActivityPub, [:passthrough],
  163. stream_out: fn _ -> nil end,
  164. stream_out_participations: fn _, _ -> nil end do
  165. {:ok, delete, _} = SideEffects.handle(delete)
  166. user = User.get_cached_by_ap_id(object.data["actor"])
  167. assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out(delete))
  168. assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out_participations(object, user))
  169. end
  170. object = Object.get_by_id(object.id)
  171. assert object.data["type"] == "Tombstone"
  172. refute Activity.get_by_id(post.id)
  173. user = User.get_by_id(user.id)
  174. assert user.note_count == 0
  175. object = Object.normalize(op.data["object"], false)
  176. assert object.data["repliesCount"] == 0
  177. end
  178. test "it handles user deletions", %{delete_user: delete, user: user} do
  179. {:ok, _delete, _} = SideEffects.handle(delete)
  180. ObanHelpers.perform_all()
  181. assert User.get_cached_by_ap_id(user.ap_id).deactivated
  182. end
  183. test "it logs issues with objects deletion", %{
  184. delete: delete,
  185. object: object
  186. } do
  187. {:ok, object} =
  188. object
  189. |> Object.change(%{data: Map.delete(object.data, "actor")})
  190. |> Repo.update()
  191. Object.invalid_object_cache(object)
  192. assert capture_log(fn ->
  193. {:error, :no_object_actor} = SideEffects.handle(delete)
  194. end) =~ "object doesn't have an actor"
  195. end
  196. end
  197. describe "EmojiReact objects" do
  198. setup do
  199. poster = insert(:user)
  200. user = insert(:user)
  201. {:ok, post} = CommonAPI.post(poster, %{status: "hey"})
  202. {:ok, emoji_react_data, []} = Builder.emoji_react(user, post.object, "👌")
  203. {:ok, emoji_react, _meta} = ActivityPub.persist(emoji_react_data, local: true)
  204. %{emoji_react: emoji_react, user: user, poster: poster}
  205. end
  206. test "adds the reaction to the object", %{emoji_react: emoji_react, user: user} do
  207. {:ok, emoji_react, _} = SideEffects.handle(emoji_react)
  208. object = Object.get_by_ap_id(emoji_react.data["object"])
  209. assert object.data["reaction_count"] == 1
  210. assert ["👌", [user.ap_id]] in object.data["reactions"]
  211. end
  212. test "creates a notification", %{emoji_react: emoji_react, poster: poster} do
  213. {:ok, emoji_react, _} = SideEffects.handle(emoji_react)
  214. assert Repo.get_by(Notification, user_id: poster.id, activity_id: emoji_react.id)
  215. end
  216. end
  217. describe "delete users with confirmation pending" do
  218. setup do
  219. user = insert(:user, confirmation_pending: true)
  220. {:ok, delete_user_data, _meta} = Builder.delete(user, user.ap_id)
  221. {:ok, delete_user, _meta} = ActivityPub.persist(delete_user_data, local: true)
  222. {:ok, delete: delete_user, user: user}
  223. end
  224. test "when activation is not required", %{delete: delete, user: user} do
  225. clear_config([:instance, :account_activation_required], false)
  226. {:ok, _, _} = SideEffects.handle(delete)
  227. ObanHelpers.perform_all()
  228. assert User.get_cached_by_id(user.id).deactivated
  229. end
  230. test "when activation is required", %{delete: delete, user: user} do
  231. clear_config([:instance, :account_activation_required], true)
  232. {:ok, _, _} = SideEffects.handle(delete)
  233. ObanHelpers.perform_all()
  234. refute User.get_cached_by_id(user.id)
  235. end
  236. end
  237. describe "Undo objects" do
  238. setup do
  239. poster = insert(:user)
  240. user = insert(:user)
  241. {:ok, post} = CommonAPI.post(poster, %{status: "hey"})
  242. {:ok, like} = CommonAPI.favorite(user, post.id)
  243. {:ok, reaction} = CommonAPI.react_with_emoji(post.id, user, "👍")
  244. {:ok, announce} = CommonAPI.repeat(post.id, user)
  245. {:ok, block} = CommonAPI.block(user, poster)
  246. {:ok, undo_data, _meta} = Builder.undo(user, like)
  247. {:ok, like_undo, _meta} = ActivityPub.persist(undo_data, local: true)
  248. {:ok, undo_data, _meta} = Builder.undo(user, reaction)
  249. {:ok, reaction_undo, _meta} = ActivityPub.persist(undo_data, local: true)
  250. {:ok, undo_data, _meta} = Builder.undo(user, announce)
  251. {:ok, announce_undo, _meta} = ActivityPub.persist(undo_data, local: true)
  252. {:ok, undo_data, _meta} = Builder.undo(user, block)
  253. {:ok, block_undo, _meta} = ActivityPub.persist(undo_data, local: true)
  254. %{
  255. like_undo: like_undo,
  256. post: post,
  257. like: like,
  258. reaction_undo: reaction_undo,
  259. reaction: reaction,
  260. announce_undo: announce_undo,
  261. announce: announce,
  262. block_undo: block_undo,
  263. block: block,
  264. poster: poster,
  265. user: user
  266. }
  267. end
  268. test "deletes the original block", %{
  269. block_undo: block_undo,
  270. block: block
  271. } do
  272. {:ok, _block_undo, _meta} = SideEffects.handle(block_undo)
  273. refute Activity.get_by_id(block.id)
  274. end
  275. test "unblocks the blocked user", %{block_undo: block_undo, block: block} do
  276. blocker = User.get_by_ap_id(block.data["actor"])
  277. blocked = User.get_by_ap_id(block.data["object"])
  278. {:ok, _block_undo, _} = SideEffects.handle(block_undo)
  279. refute User.blocks?(blocker, blocked)
  280. end
  281. test "an announce undo removes the announce from the object", %{
  282. announce_undo: announce_undo,
  283. post: post
  284. } do
  285. {:ok, _announce_undo, _} = SideEffects.handle(announce_undo)
  286. object = Object.get_by_ap_id(post.data["object"])
  287. assert object.data["announcement_count"] == 0
  288. assert object.data["announcements"] == []
  289. end
  290. test "deletes the original announce", %{announce_undo: announce_undo, announce: announce} do
  291. {:ok, _announce_undo, _} = SideEffects.handle(announce_undo)
  292. refute Activity.get_by_id(announce.id)
  293. end
  294. test "a reaction undo removes the reaction from the object", %{
  295. reaction_undo: reaction_undo,
  296. post: post
  297. } do
  298. {:ok, _reaction_undo, _} = SideEffects.handle(reaction_undo)
  299. object = Object.get_by_ap_id(post.data["object"])
  300. assert object.data["reaction_count"] == 0
  301. assert object.data["reactions"] == []
  302. end
  303. test "deletes the original reaction", %{reaction_undo: reaction_undo, reaction: reaction} do
  304. {:ok, _reaction_undo, _} = SideEffects.handle(reaction_undo)
  305. refute Activity.get_by_id(reaction.id)
  306. end
  307. test "a like undo removes the like from the object", %{like_undo: like_undo, post: post} do
  308. {:ok, _like_undo, _} = SideEffects.handle(like_undo)
  309. object = Object.get_by_ap_id(post.data["object"])
  310. assert object.data["like_count"] == 0
  311. assert object.data["likes"] == []
  312. end
  313. test "deletes the original like", %{like_undo: like_undo, like: like} do
  314. {:ok, _like_undo, _} = SideEffects.handle(like_undo)
  315. refute Activity.get_by_id(like.id)
  316. end
  317. end
  318. describe "like objects" do
  319. setup do
  320. poster = insert(:user)
  321. user = insert(:user)
  322. {:ok, post} = CommonAPI.post(poster, %{status: "hey"})
  323. {:ok, like_data, _meta} = Builder.like(user, post.object)
  324. {:ok, like, _meta} = ActivityPub.persist(like_data, local: true)
  325. %{like: like, user: user, poster: poster}
  326. end
  327. test "add the like to the original object", %{like: like, user: user} do
  328. {:ok, like, _} = SideEffects.handle(like)
  329. object = Object.get_by_ap_id(like.data["object"])
  330. assert object.data["like_count"] == 1
  331. assert user.ap_id in object.data["likes"]
  332. end
  333. test "creates a notification", %{like: like, poster: poster} do
  334. {:ok, like, _} = SideEffects.handle(like)
  335. assert Repo.get_by(Notification, user_id: poster.id, activity_id: like.id)
  336. end
  337. end
  338. describe "creation of ChatMessages" do
  339. test "notifies the recipient" do
  340. author = insert(:user, local: false)
  341. recipient = insert(:user, local: true)
  342. {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey")
  343. {:ok, create_activity_data, _meta} =
  344. Builder.create(author, chat_message_data["id"], [recipient.ap_id])
  345. {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false)
  346. {:ok, _create_activity, _meta} =
  347. SideEffects.handle(create_activity, local: false, object_data: chat_message_data)
  348. assert Repo.get_by(Notification, user_id: recipient.id, activity_id: create_activity.id)
  349. end
  350. test "it streams the created ChatMessage" do
  351. author = insert(:user, local: true)
  352. recipient = insert(:user, local: true)
  353. {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey")
  354. {:ok, create_activity_data, _meta} =
  355. Builder.create(author, chat_message_data["id"], [recipient.ap_id])
  356. {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false)
  357. {:ok, _create_activity, meta} =
  358. SideEffects.handle(create_activity, local: false, object_data: chat_message_data)
  359. assert [_, _] = meta[:streamables]
  360. end
  361. test "it creates a Chat and MessageReferences for the local users and bumps the unread count, except for the author" do
  362. author = insert(:user, local: true)
  363. recipient = insert(:user, local: true)
  364. {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey")
  365. {:ok, create_activity_data, _meta} =
  366. Builder.create(author, chat_message_data["id"], [recipient.ap_id])
  367. {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false)
  368. with_mocks([
  369. {
  370. Pleroma.Web.Streamer,
  371. [],
  372. [
  373. stream: fn _, _ -> nil end
  374. ]
  375. },
  376. {
  377. Pleroma.Web.Push,
  378. [],
  379. [
  380. send: fn _ -> nil end
  381. ]
  382. }
  383. ]) do
  384. {:ok, _create_activity, meta} =
  385. SideEffects.handle(create_activity, local: false, object_data: chat_message_data)
  386. # The notification gets created
  387. assert [notification] = meta[:notifications]
  388. assert notification.activity_id == create_activity.id
  389. # But it is not sent out
  390. refute called(Pleroma.Web.Streamer.stream(["user", "user:notification"], notification))
  391. refute called(Pleroma.Web.Push.send(notification))
  392. # Same for the user chat stream
  393. assert [{topics, _}, _] = meta[:streamables]
  394. assert topics == ["user", "user:pleroma_chat"]
  395. refute called(Pleroma.Web.Streamer.stream(["user", "user:pleroma_chat"], :_))
  396. chat = Chat.get(author.id, recipient.ap_id)
  397. [cm_ref] = MessageReference.for_chat_query(chat) |> Repo.all()
  398. assert cm_ref.object.data["content"] == "hey"
  399. assert cm_ref.unread == false
  400. chat = Chat.get(recipient.id, author.ap_id)
  401. [cm_ref] = MessageReference.for_chat_query(chat) |> Repo.all()
  402. assert cm_ref.object.data["content"] == "hey"
  403. assert cm_ref.unread == true
  404. end
  405. end
  406. test "it creates a Chat for the local users and bumps the unread count" do
  407. author = insert(:user, local: false)
  408. recipient = insert(:user, local: true)
  409. {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey")
  410. {:ok, create_activity_data, _meta} =
  411. Builder.create(author, chat_message_data["id"], [recipient.ap_id])
  412. {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false)
  413. {:ok, _create_activity, _meta} =
  414. SideEffects.handle(create_activity, local: false, object_data: chat_message_data)
  415. # An object is created
  416. assert Object.get_by_ap_id(chat_message_data["id"])
  417. # The remote user won't get a chat
  418. chat = Chat.get(author.id, recipient.ap_id)
  419. refute chat
  420. # The local user will get a chat
  421. chat = Chat.get(recipient.id, author.ap_id)
  422. assert chat
  423. author = insert(:user, local: true)
  424. recipient = insert(:user, local: true)
  425. {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey")
  426. {:ok, create_activity_data, _meta} =
  427. Builder.create(author, chat_message_data["id"], [recipient.ap_id])
  428. {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false)
  429. {:ok, _create_activity, _meta} =
  430. SideEffects.handle(create_activity, local: false, object_data: chat_message_data)
  431. # Both users are local and get the chat
  432. chat = Chat.get(author.id, recipient.ap_id)
  433. assert chat
  434. chat = Chat.get(recipient.id, author.ap_id)
  435. assert chat
  436. end
  437. end
  438. describe "announce objects" do
  439. setup do
  440. poster = insert(:user)
  441. user = insert(:user)
  442. {:ok, post} = CommonAPI.post(poster, %{status: "hey"})
  443. {:ok, private_post} = CommonAPI.post(poster, %{status: "hey", visibility: "private"})
  444. {:ok, announce_data, _meta} = Builder.announce(user, post.object, public: true)
  445. {:ok, private_announce_data, _meta} =
  446. Builder.announce(user, private_post.object, public: false)
  447. {:ok, relay_announce_data, _meta} =
  448. Builder.announce(Pleroma.Web.ActivityPub.Relay.get_actor(), post.object, public: true)
  449. {:ok, announce, _meta} = ActivityPub.persist(announce_data, local: true)
  450. {:ok, private_announce, _meta} = ActivityPub.persist(private_announce_data, local: true)
  451. {:ok, relay_announce, _meta} = ActivityPub.persist(relay_announce_data, local: true)
  452. %{
  453. announce: announce,
  454. user: user,
  455. poster: poster,
  456. private_announce: private_announce,
  457. relay_announce: relay_announce
  458. }
  459. end
  460. test "adds the announce to the original object", %{announce: announce, user: user} do
  461. {:ok, announce, _} = SideEffects.handle(announce)
  462. object = Object.get_by_ap_id(announce.data["object"])
  463. assert object.data["announcement_count"] == 1
  464. assert user.ap_id in object.data["announcements"]
  465. end
  466. test "does not add the announce to the original object if the actor is a service actor", %{
  467. relay_announce: announce
  468. } do
  469. {:ok, announce, _} = SideEffects.handle(announce)
  470. object = Object.get_by_ap_id(announce.data["object"])
  471. assert object.data["announcement_count"] == nil
  472. end
  473. test "creates a notification", %{announce: announce, poster: poster} do
  474. {:ok, announce, _} = SideEffects.handle(announce)
  475. assert Repo.get_by(Notification, user_id: poster.id, activity_id: announce.id)
  476. end
  477. test "it streams out the announce", %{announce: announce} do
  478. with_mocks([
  479. {
  480. Pleroma.Web.Streamer,
  481. [],
  482. [
  483. stream: fn _, _ -> nil end
  484. ]
  485. },
  486. {
  487. Pleroma.Web.Push,
  488. [],
  489. [
  490. send: fn _ -> nil end
  491. ]
  492. }
  493. ]) do
  494. {:ok, announce, _} = SideEffects.handle(announce)
  495. assert called(
  496. Pleroma.Web.Streamer.stream(["user", "list", "public", "public:local"], announce)
  497. )
  498. assert called(Pleroma.Web.Push.send(:_))
  499. end
  500. end
  501. end
  502. end