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, [],
325 stream: fn _, payload ->
328 assert cm_ref.seen == true
330 {^recipient, cm_ref} ->
331 assert cm_ref.seen == false
334 Pleroma.Web.PleromaAPI.ChatView.render("show.json",
335 last_message: cm_ref,
339 assert view.unread == 1
345 {:ok, _create_activity, _meta} =
346 SideEffects.handle(create_activity, local: false, object_data: chat_message_data)
348 assert called(Pleroma.Web.Streamer.stream(["user", "user:pleroma_chat"], {author, :_}))
349 assert called(Pleroma.Web.Streamer.stream(["user", "user:pleroma_chat"], {recipient, :_}))
353 test "it creates a Chat and ChatMessageReferences for the local users and bumps the unread count, except for the author" do
354 author = insert(:user, local: true)
355 recipient = insert(:user, local: true)
357 {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey")
359 {:ok, create_activity_data, _meta} =
360 Builder.create(author, chat_message_data["id"], [recipient.ap_id])
362 {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false)
364 {:ok, _create_activity, _meta} =
365 SideEffects.handle(create_activity, local: false, object_data: chat_message_data)
367 chat = Chat.get(author.id, recipient.ap_id)
369 [cm_ref] = ChatMessageReference.for_chat_query(chat) |> Repo.all()
371 assert cm_ref.object.data["content"] == "hey"
372 assert cm_ref.seen == true
374 chat = Chat.get(recipient.id, author.ap_id)
376 [cm_ref] = ChatMessageReference.for_chat_query(chat) |> Repo.all()
378 assert cm_ref.object.data["content"] == "hey"
379 assert cm_ref.seen == false
382 test "it creates a Chat for the local users and bumps the unread count" do
383 author = insert(:user, local: false)
384 recipient = insert(:user, local: true)
386 {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey")
388 {:ok, create_activity_data, _meta} =
389 Builder.create(author, chat_message_data["id"], [recipient.ap_id])
391 {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false)
393 {:ok, _create_activity, _meta} =
394 SideEffects.handle(create_activity, local: false, object_data: chat_message_data)
396 # An object is created
397 assert Object.get_by_ap_id(chat_message_data["id"])
399 # The remote user won't get a chat
400 chat = Chat.get(author.id, recipient.ap_id)
403 # The local user will get a chat
404 chat = Chat.get(recipient.id, author.ap_id)
407 author = insert(:user, local: true)
408 recipient = insert(:user, local: true)
410 {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey")
412 {:ok, create_activity_data, _meta} =
413 Builder.create(author, chat_message_data["id"], [recipient.ap_id])
415 {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false)
417 {:ok, _create_activity, _meta} =
418 SideEffects.handle(create_activity, local: false, object_data: chat_message_data)
420 # Both users are local and get the chat
421 chat = Chat.get(author.id, recipient.ap_id)
424 chat = Chat.get(recipient.id, author.ap_id)
429 describe "announce objects" do
431 poster = insert(:user)
433 {:ok, post} = CommonAPI.post(poster, %{status: "hey"})
434 {:ok, private_post} = CommonAPI.post(poster, %{status: "hey", visibility: "private"})
436 {:ok, announce_data, _meta} = Builder.announce(user, post.object, public: true)
438 {:ok, private_announce_data, _meta} =
439 Builder.announce(user, private_post.object, public: false)
441 {:ok, relay_announce_data, _meta} =
442 Builder.announce(Pleroma.Web.ActivityPub.Relay.get_actor(), post.object, public: true)
444 {:ok, announce, _meta} = ActivityPub.persist(announce_data, local: true)
445 {:ok, private_announce, _meta} = ActivityPub.persist(private_announce_data, local: true)
446 {:ok, relay_announce, _meta} = ActivityPub.persist(relay_announce_data, local: true)
452 private_announce: private_announce,
453 relay_announce: relay_announce
457 test "adds the announce to the original object", %{announce: announce, user: user} do
458 {:ok, announce, _} = SideEffects.handle(announce)
459 object = Object.get_by_ap_id(announce.data["object"])
460 assert object.data["announcement_count"] == 1
461 assert user.ap_id in object.data["announcements"]
464 test "does not add the announce to the original object if the actor is a service actor", %{
465 relay_announce: announce
467 {:ok, announce, _} = SideEffects.handle(announce)
468 object = Object.get_by_ap_id(announce.data["object"])
469 assert object.data["announcement_count"] == nil
472 test "creates a notification", %{announce: announce, poster: poster} do
473 {:ok, announce, _} = SideEffects.handle(announce)
474 assert Repo.get_by(Notification, user_id: poster.id, activity_id: announce.id)
477 test "it streams out the announce", %{announce: announce} do
478 with_mock Pleroma.Web.ActivityPub.ActivityPub, [:passthrough], stream_out: fn _ -> nil end do
479 {:ok, announce, _} = SideEffects.handle(announce)
481 assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out(announce))