1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Web.ActivityPub.SideEffectsTest do
6 use Oban.Testing, repo: Pleroma.Repo
11 alias Pleroma.ChatMessageReference
12 alias Pleroma.Notification
15 alias Pleroma.Tests.ObanHelpers
17 alias Pleroma.Web.ActivityPub.ActivityPub
18 alias Pleroma.Web.ActivityPub.Builder
19 alias Pleroma.Web.ActivityPub.SideEffects
20 alias Pleroma.Web.CommonAPI
22 import Pleroma.Factory
25 describe "delete objects" do
28 other_user = insert(:user)
30 {:ok, op} = CommonAPI.post(other_user, %{status: "big oof"})
31 {:ok, post} = CommonAPI.post(user, %{status: "hey", in_reply_to_id: op})
32 {:ok, favorite} = CommonAPI.favorite(user, post.id)
33 object = Object.normalize(post)
34 {:ok, delete_data, _meta} = Builder.delete(user, object.data["id"])
35 {:ok, delete_user_data, _meta} = Builder.delete(user, user.ap_id)
36 {:ok, delete, _meta} = ActivityPub.persist(delete_data, local: true)
37 {:ok, delete_user, _meta} = ActivityPub.persist(delete_user_data, local: true)
44 delete_user: delete_user,
50 test "it handles object deletions", %{
58 with_mock Pleroma.Web.ActivityPub.ActivityPub, [:passthrough],
59 stream_out: fn _ -> nil end,
60 stream_out_participations: fn _, _ -> nil end do
61 {:ok, delete, _} = SideEffects.handle(delete)
62 user = User.get_cached_by_ap_id(object.data["actor"])
64 assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out(delete))
65 assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out_participations(object, user))
68 object = Object.get_by_id(object.id)
69 assert object.data["type"] == "Tombstone"
70 refute Activity.get_by_id(post.id)
71 refute Activity.get_by_id(favorite.id)
73 user = User.get_by_id(user.id)
74 assert user.note_count == 0
76 object = Object.normalize(op.data["object"], false)
78 assert object.data["repliesCount"] == 0
81 test "it handles object deletions when the object itself has been pruned", %{
88 with_mock Pleroma.Web.ActivityPub.ActivityPub, [:passthrough],
89 stream_out: fn _ -> nil end,
90 stream_out_participations: fn _, _ -> nil end do
91 {:ok, delete, _} = SideEffects.handle(delete)
92 user = User.get_cached_by_ap_id(object.data["actor"])
94 assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out(delete))
95 assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out_participations(object, user))
98 object = Object.get_by_id(object.id)
99 assert object.data["type"] == "Tombstone"
100 refute Activity.get_by_id(post.id)
102 user = User.get_by_id(user.id)
103 assert user.note_count == 0
105 object = Object.normalize(op.data["object"], false)
107 assert object.data["repliesCount"] == 0
110 test "it handles user deletions", %{delete_user: delete, user: user} do
111 {:ok, _delete, _} = SideEffects.handle(delete)
112 ObanHelpers.perform_all()
114 assert User.get_cached_by_ap_id(user.ap_id).deactivated
118 describe "EmojiReact objects" do
120 poster = insert(:user)
123 {:ok, post} = CommonAPI.post(poster, %{status: "hey"})
125 {:ok, emoji_react_data, []} = Builder.emoji_react(user, post.object, "👌")
126 {:ok, emoji_react, _meta} = ActivityPub.persist(emoji_react_data, local: true)
128 %{emoji_react: emoji_react, user: user, poster: poster}
131 test "adds the reaction to the object", %{emoji_react: emoji_react, user: user} do
132 {:ok, emoji_react, _} = SideEffects.handle(emoji_react)
133 object = Object.get_by_ap_id(emoji_react.data["object"])
135 assert object.data["reaction_count"] == 1
136 assert ["👌", [user.ap_id]] in object.data["reactions"]
139 test "creates a notification", %{emoji_react: emoji_react, poster: poster} do
140 {:ok, emoji_react, _} = SideEffects.handle(emoji_react)
141 assert Repo.get_by(Notification, user_id: poster.id, activity_id: emoji_react.id)
145 describe "delete users with confirmation pending" do
147 user = insert(:user, confirmation_pending: true)
148 {:ok, delete_user_data, _meta} = Builder.delete(user, user.ap_id)
149 {:ok, delete_user, _meta} = ActivityPub.persist(delete_user_data, local: true)
150 {:ok, delete: delete_user, user: user}
153 test "when activation is not required", %{delete: delete, user: user} do
154 clear_config([:instance, :account_activation_required], false)
155 {:ok, _, _} = SideEffects.handle(delete)
156 ObanHelpers.perform_all()
158 assert User.get_cached_by_id(user.id).deactivated
161 test "when activation is required", %{delete: delete, user: user} do
162 clear_config([:instance, :account_activation_required], true)
163 {:ok, _, _} = SideEffects.handle(delete)
164 ObanHelpers.perform_all()
166 refute User.get_cached_by_id(user.id)
170 describe "Undo objects" do
172 poster = insert(:user)
174 {:ok, post} = CommonAPI.post(poster, %{status: "hey"})
175 {:ok, like} = CommonAPI.favorite(user, post.id)
176 {:ok, reaction} = CommonAPI.react_with_emoji(post.id, user, "👍")
177 {:ok, announce} = CommonAPI.repeat(post.id, user)
178 {:ok, block} = ActivityPub.block(user, poster)
179 User.block(user, poster)
181 {:ok, undo_data, _meta} = Builder.undo(user, like)
182 {:ok, like_undo, _meta} = ActivityPub.persist(undo_data, local: true)
184 {:ok, undo_data, _meta} = Builder.undo(user, reaction)
185 {:ok, reaction_undo, _meta} = ActivityPub.persist(undo_data, local: true)
187 {:ok, undo_data, _meta} = Builder.undo(user, announce)
188 {:ok, announce_undo, _meta} = ActivityPub.persist(undo_data, local: true)
190 {:ok, undo_data, _meta} = Builder.undo(user, block)
191 {:ok, block_undo, _meta} = ActivityPub.persist(undo_data, local: true)
194 like_undo: like_undo,
197 reaction_undo: reaction_undo,
199 announce_undo: announce_undo,
201 block_undo: block_undo,
208 test "deletes the original block", %{block_undo: block_undo, block: block} do
209 {:ok, _block_undo, _} = SideEffects.handle(block_undo)
210 refute Activity.get_by_id(block.id)
213 test "unblocks the blocked user", %{block_undo: block_undo, block: block} do
214 blocker = User.get_by_ap_id(block.data["actor"])
215 blocked = User.get_by_ap_id(block.data["object"])
217 {:ok, _block_undo, _} = SideEffects.handle(block_undo)
218 refute User.blocks?(blocker, blocked)
221 test "an announce undo removes the announce from the object", %{
222 announce_undo: announce_undo,
225 {:ok, _announce_undo, _} = SideEffects.handle(announce_undo)
227 object = Object.get_by_ap_id(post.data["object"])
229 assert object.data["announcement_count"] == 0
230 assert object.data["announcements"] == []
233 test "deletes the original announce", %{announce_undo: announce_undo, announce: announce} do
234 {:ok, _announce_undo, _} = SideEffects.handle(announce_undo)
235 refute Activity.get_by_id(announce.id)
238 test "a reaction undo removes the reaction from the object", %{
239 reaction_undo: reaction_undo,
242 {:ok, _reaction_undo, _} = SideEffects.handle(reaction_undo)
244 object = Object.get_by_ap_id(post.data["object"])
246 assert object.data["reaction_count"] == 0
247 assert object.data["reactions"] == []
250 test "deletes the original reaction", %{reaction_undo: reaction_undo, reaction: reaction} do
251 {:ok, _reaction_undo, _} = SideEffects.handle(reaction_undo)
252 refute Activity.get_by_id(reaction.id)
255 test "a like undo removes the like from the object", %{like_undo: like_undo, post: post} do
256 {:ok, _like_undo, _} = SideEffects.handle(like_undo)
258 object = Object.get_by_ap_id(post.data["object"])
260 assert object.data["like_count"] == 0
261 assert object.data["likes"] == []
264 test "deletes the original like", %{like_undo: like_undo, like: like} do
265 {:ok, _like_undo, _} = SideEffects.handle(like_undo)
266 refute Activity.get_by_id(like.id)
270 describe "like objects" do
272 poster = insert(:user)
274 {:ok, post} = CommonAPI.post(poster, %{status: "hey"})
276 {:ok, like_data, _meta} = Builder.like(user, post.object)
277 {:ok, like, _meta} = ActivityPub.persist(like_data, local: true)
279 %{like: like, user: user, poster: poster}
282 test "add the like to the original object", %{like: like, user: user} do
283 {:ok, like, _} = SideEffects.handle(like)
284 object = Object.get_by_ap_id(like.data["object"])
285 assert object.data["like_count"] == 1
286 assert user.ap_id in object.data["likes"]
289 test "creates a notification", %{like: like, poster: poster} do
290 {:ok, like, _} = SideEffects.handle(like)
291 assert Repo.get_by(Notification, user_id: poster.id, activity_id: like.id)
295 describe "creation of ChatMessages" do
296 test "notifies the recipient" do
297 author = insert(:user, local: false)
298 recipient = insert(:user, local: true)
300 {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey")
302 {:ok, create_activity_data, _meta} =
303 Builder.create(author, chat_message_data["id"], [recipient.ap_id])
305 {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false)
307 {:ok, _create_activity, _meta} =
308 SideEffects.handle(create_activity, local: false, object_data: chat_message_data)
310 assert Repo.get_by(Notification, user_id: recipient.id, activity_id: create_activity.id)
313 test "it streams the created ChatMessage" do
314 author = insert(:user, local: true)
315 recipient = insert(:user, local: true)
317 {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey")
319 {:ok, create_activity_data, _meta} =
320 Builder.create(author, chat_message_data["id"], [recipient.ap_id])
322 {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false)
324 with_mock Pleroma.Web.Streamer, [], stream: fn _, _ -> nil end do
325 {:ok, _create_activity, _meta} =
326 SideEffects.handle(create_activity, local: false, object_data: chat_message_data)
328 assert called(Pleroma.Web.Streamer.stream(["user", "user:pleroma_chat"], {author, :_}))
329 assert called(Pleroma.Web.Streamer.stream(["user", "user:pleroma_chat"], {recipient, :_}))
333 test "it creates a Chat and ChatMessageReferences for the local users and bumps the unread count, except for the author" do
334 author = insert(:user, local: true)
335 recipient = insert(:user, local: true)
337 {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey")
339 {:ok, create_activity_data, _meta} =
340 Builder.create(author, chat_message_data["id"], [recipient.ap_id])
342 {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false)
344 {:ok, _create_activity, _meta} =
345 SideEffects.handle(create_activity, local: false, object_data: chat_message_data)
347 chat = Chat.get(author.id, recipient.ap_id)
349 [cm_ref] = ChatMessageReference.for_chat_query(chat) |> Repo.all()
351 assert cm_ref.object.data["content"] == "hey"
352 assert cm_ref.seen == true
354 chat = Chat.get(recipient.id, author.ap_id)
356 [cm_ref] = ChatMessageReference.for_chat_query(chat) |> Repo.all()
358 assert cm_ref.object.data["content"] == "hey"
359 assert cm_ref.seen == false
362 test "it creates a Chat for the local users and bumps the unread count" do
363 author = insert(:user, local: false)
364 recipient = insert(:user, local: true)
366 {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey")
368 {:ok, create_activity_data, _meta} =
369 Builder.create(author, chat_message_data["id"], [recipient.ap_id])
371 {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false)
373 {:ok, _create_activity, _meta} =
374 SideEffects.handle(create_activity, local: false, object_data: chat_message_data)
376 # An object is created
377 assert Object.get_by_ap_id(chat_message_data["id"])
379 # The remote user won't get a chat
380 chat = Chat.get(author.id, recipient.ap_id)
383 # The local user will get a chat
384 chat = Chat.get(recipient.id, author.ap_id)
387 author = insert(:user, local: true)
388 recipient = insert(:user, local: true)
390 {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey")
392 {:ok, create_activity_data, _meta} =
393 Builder.create(author, chat_message_data["id"], [recipient.ap_id])
395 {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false)
397 {:ok, _create_activity, _meta} =
398 SideEffects.handle(create_activity, local: false, object_data: chat_message_data)
400 # Both users are local and get the chat
401 chat = Chat.get(author.id, recipient.ap_id)
404 chat = Chat.get(recipient.id, author.ap_id)
409 describe "announce objects" do
411 poster = insert(:user)
413 {:ok, post} = CommonAPI.post(poster, %{status: "hey"})
414 {:ok, private_post} = CommonAPI.post(poster, %{status: "hey", visibility: "private"})
416 {:ok, announce_data, _meta} = Builder.announce(user, post.object, public: true)
418 {:ok, private_announce_data, _meta} =
419 Builder.announce(user, private_post.object, public: false)
421 {:ok, relay_announce_data, _meta} =
422 Builder.announce(Pleroma.Web.ActivityPub.Relay.get_actor(), post.object, public: true)
424 {:ok, announce, _meta} = ActivityPub.persist(announce_data, local: true)
425 {:ok, private_announce, _meta} = ActivityPub.persist(private_announce_data, local: true)
426 {:ok, relay_announce, _meta} = ActivityPub.persist(relay_announce_data, local: true)
432 private_announce: private_announce,
433 relay_announce: relay_announce
437 test "adds the announce to the original object", %{announce: announce, user: user} do
438 {:ok, announce, _} = SideEffects.handle(announce)
439 object = Object.get_by_ap_id(announce.data["object"])
440 assert object.data["announcement_count"] == 1
441 assert user.ap_id in object.data["announcements"]
444 test "does not add the announce to the original object if the actor is a service actor", %{
445 relay_announce: announce
447 {:ok, announce, _} = SideEffects.handle(announce)
448 object = Object.get_by_ap_id(announce.data["object"])
449 assert object.data["announcement_count"] == nil
452 test "creates a notification", %{announce: announce, poster: poster} do
453 {:ok, announce, _} = SideEffects.handle(announce)
454 assert Repo.get_by(Notification, user_id: poster.id, activity_id: announce.id)
457 test "it streams out the announce", %{announce: announce} do
458 with_mock Pleroma.Web.ActivityPub.ActivityPub, [:passthrough], stream_out: fn _ -> nil end do
459 {:ok, announce, _} = SideEffects.handle(announce)
461 assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out(announce))