SideEffectsTest: More tests.
[akkoma] / test / web / activity_pub / side_effects_test.exs
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
5 defmodule Pleroma.Web.ActivityPub.SideEffectsTest do
6 use Oban.Testing, repo: Pleroma.Repo
7 use Pleroma.DataCase
8
9 alias Pleroma.Activity
10 alias Pleroma.Chat
11 alias Pleroma.ChatMessageReference
12 alias Pleroma.Notification
13 alias Pleroma.Object
14 alias Pleroma.Repo
15 alias Pleroma.Tests.ObanHelpers
16 alias Pleroma.User
17 alias Pleroma.Web.ActivityPub.ActivityPub
18 alias Pleroma.Web.ActivityPub.Builder
19 alias Pleroma.Web.ActivityPub.SideEffects
20 alias Pleroma.Web.CommonAPI
21
22 import Pleroma.Factory
23 import Mock
24
25 describe "delete objects" do
26 setup do
27 user = insert(:user)
28 other_user = insert(:user)
29
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)
38
39 %{
40 user: user,
41 delete: delete,
42 post: post,
43 object: object,
44 delete_user: delete_user,
45 op: op,
46 favorite: favorite
47 }
48 end
49
50 test "it handles object deletions", %{
51 delete: delete,
52 post: post,
53 object: object,
54 user: user,
55 op: op,
56 favorite: favorite
57 } do
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"])
63
64 assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out(delete))
65 assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out_participations(object, user))
66 end
67
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)
72
73 user = User.get_by_id(user.id)
74 assert user.note_count == 0
75
76 object = Object.normalize(op.data["object"], false)
77
78 assert object.data["repliesCount"] == 0
79 end
80
81 test "it handles object deletions when the object itself has been pruned", %{
82 delete: delete,
83 post: post,
84 object: object,
85 user: user,
86 op: op
87 } do
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"])
93
94 assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out(delete))
95 assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out_participations(object, user))
96 end
97
98 object = Object.get_by_id(object.id)
99 assert object.data["type"] == "Tombstone"
100 refute Activity.get_by_id(post.id)
101
102 user = User.get_by_id(user.id)
103 assert user.note_count == 0
104
105 object = Object.normalize(op.data["object"], false)
106
107 assert object.data["repliesCount"] == 0
108 end
109
110 test "it handles user deletions", %{delete_user: delete, user: user} do
111 {:ok, _delete, _} = SideEffects.handle(delete)
112 ObanHelpers.perform_all()
113
114 assert User.get_cached_by_ap_id(user.ap_id).deactivated
115 end
116 end
117
118 describe "EmojiReact objects" do
119 setup do
120 poster = insert(:user)
121 user = insert(:user)
122
123 {:ok, post} = CommonAPI.post(poster, %{status: "hey"})
124
125 {:ok, emoji_react_data, []} = Builder.emoji_react(user, post.object, "👌")
126 {:ok, emoji_react, _meta} = ActivityPub.persist(emoji_react_data, local: true)
127
128 %{emoji_react: emoji_react, user: user, poster: poster}
129 end
130
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"])
134
135 assert object.data["reaction_count"] == 1
136 assert ["👌", [user.ap_id]] in object.data["reactions"]
137 end
138
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)
142 end
143 end
144
145 describe "delete users with confirmation pending" do
146 setup 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}
151 end
152
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()
157
158 assert User.get_cached_by_id(user.id).deactivated
159 end
160
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()
165
166 refute User.get_cached_by_id(user.id)
167 end
168 end
169
170 describe "Undo objects" do
171 setup do
172 poster = insert(:user)
173 user = 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)
180
181 {:ok, undo_data, _meta} = Builder.undo(user, like)
182 {:ok, like_undo, _meta} = ActivityPub.persist(undo_data, local: true)
183
184 {:ok, undo_data, _meta} = Builder.undo(user, reaction)
185 {:ok, reaction_undo, _meta} = ActivityPub.persist(undo_data, local: true)
186
187 {:ok, undo_data, _meta} = Builder.undo(user, announce)
188 {:ok, announce_undo, _meta} = ActivityPub.persist(undo_data, local: true)
189
190 {:ok, undo_data, _meta} = Builder.undo(user, block)
191 {:ok, block_undo, _meta} = ActivityPub.persist(undo_data, local: true)
192
193 %{
194 like_undo: like_undo,
195 post: post,
196 like: like,
197 reaction_undo: reaction_undo,
198 reaction: reaction,
199 announce_undo: announce_undo,
200 announce: announce,
201 block_undo: block_undo,
202 block: block,
203 poster: poster,
204 user: user
205 }
206 end
207
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)
211 end
212
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"])
216
217 {:ok, _block_undo, _} = SideEffects.handle(block_undo)
218 refute User.blocks?(blocker, blocked)
219 end
220
221 test "an announce undo removes the announce from the object", %{
222 announce_undo: announce_undo,
223 post: post
224 } do
225 {:ok, _announce_undo, _} = SideEffects.handle(announce_undo)
226
227 object = Object.get_by_ap_id(post.data["object"])
228
229 assert object.data["announcement_count"] == 0
230 assert object.data["announcements"] == []
231 end
232
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)
236 end
237
238 test "a reaction undo removes the reaction from the object", %{
239 reaction_undo: reaction_undo,
240 post: post
241 } do
242 {:ok, _reaction_undo, _} = SideEffects.handle(reaction_undo)
243
244 object = Object.get_by_ap_id(post.data["object"])
245
246 assert object.data["reaction_count"] == 0
247 assert object.data["reactions"] == []
248 end
249
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)
253 end
254
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)
257
258 object = Object.get_by_ap_id(post.data["object"])
259
260 assert object.data["like_count"] == 0
261 assert object.data["likes"] == []
262 end
263
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)
267 end
268 end
269
270 describe "like objects" do
271 setup do
272 poster = insert(:user)
273 user = insert(:user)
274 {:ok, post} = CommonAPI.post(poster, %{status: "hey"})
275
276 {:ok, like_data, _meta} = Builder.like(user, post.object)
277 {:ok, like, _meta} = ActivityPub.persist(like_data, local: true)
278
279 %{like: like, user: user, poster: poster}
280 end
281
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"]
287 end
288
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)
292 end
293 end
294
295 describe "creation of ChatMessages" do
296 test "notifies the recipient" do
297 author = insert(:user, local: false)
298 recipient = insert(:user, local: true)
299
300 {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey")
301
302 {:ok, create_activity_data, _meta} =
303 Builder.create(author, chat_message_data["id"], [recipient.ap_id])
304
305 {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false)
306
307 {:ok, _create_activity, _meta} =
308 SideEffects.handle(create_activity, local: false, object_data: chat_message_data)
309
310 assert Repo.get_by(Notification, user_id: recipient.id, activity_id: create_activity.id)
311 end
312
313 test "it streams the created ChatMessage" do
314 author = insert(:user, local: true)
315 recipient = insert(:user, local: true)
316
317 {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey")
318
319 {:ok, create_activity_data, _meta} =
320 Builder.create(author, chat_message_data["id"], [recipient.ap_id])
321
322 {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false)
323
324 with_mock Pleroma.Web.Streamer, [],
325 stream: fn _, payload ->
326 case payload do
327 {^author, cm_ref} ->
328 assert cm_ref.seen == true
329
330 {^recipient, cm_ref} ->
331 assert cm_ref.seen == false
332
333 view =
334 Pleroma.Web.PleromaAPI.ChatView.render("show.json",
335 last_message: cm_ref,
336 chat: cm_ref.chat
337 )
338
339 assert view.unread == 1
340
341 _ ->
342 nil
343 end
344 end do
345 {:ok, _create_activity, _meta} =
346 SideEffects.handle(create_activity, local: false, object_data: chat_message_data)
347
348 assert called(Pleroma.Web.Streamer.stream(["user", "user:pleroma_chat"], {author, :_}))
349 assert called(Pleroma.Web.Streamer.stream(["user", "user:pleroma_chat"], {recipient, :_}))
350 end
351 end
352
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)
356
357 {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey")
358
359 {:ok, create_activity_data, _meta} =
360 Builder.create(author, chat_message_data["id"], [recipient.ap_id])
361
362 {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false)
363
364 {:ok, _create_activity, _meta} =
365 SideEffects.handle(create_activity, local: false, object_data: chat_message_data)
366
367 chat = Chat.get(author.id, recipient.ap_id)
368
369 [cm_ref] = ChatMessageReference.for_chat_query(chat) |> Repo.all()
370
371 assert cm_ref.object.data["content"] == "hey"
372 assert cm_ref.seen == true
373
374 chat = Chat.get(recipient.id, author.ap_id)
375
376 [cm_ref] = ChatMessageReference.for_chat_query(chat) |> Repo.all()
377
378 assert cm_ref.object.data["content"] == "hey"
379 assert cm_ref.seen == false
380 end
381
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)
385
386 {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey")
387
388 {:ok, create_activity_data, _meta} =
389 Builder.create(author, chat_message_data["id"], [recipient.ap_id])
390
391 {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false)
392
393 {:ok, _create_activity, _meta} =
394 SideEffects.handle(create_activity, local: false, object_data: chat_message_data)
395
396 # An object is created
397 assert Object.get_by_ap_id(chat_message_data["id"])
398
399 # The remote user won't get a chat
400 chat = Chat.get(author.id, recipient.ap_id)
401 refute chat
402
403 # The local user will get a chat
404 chat = Chat.get(recipient.id, author.ap_id)
405 assert chat
406
407 author = insert(:user, local: true)
408 recipient = insert(:user, local: true)
409
410 {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey")
411
412 {:ok, create_activity_data, _meta} =
413 Builder.create(author, chat_message_data["id"], [recipient.ap_id])
414
415 {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false)
416
417 {:ok, _create_activity, _meta} =
418 SideEffects.handle(create_activity, local: false, object_data: chat_message_data)
419
420 # Both users are local and get the chat
421 chat = Chat.get(author.id, recipient.ap_id)
422 assert chat
423
424 chat = Chat.get(recipient.id, author.ap_id)
425 assert chat
426 end
427 end
428
429 describe "announce objects" do
430 setup do
431 poster = insert(:user)
432 user = insert(:user)
433 {:ok, post} = CommonAPI.post(poster, %{status: "hey"})
434 {:ok, private_post} = CommonAPI.post(poster, %{status: "hey", visibility: "private"})
435
436 {:ok, announce_data, _meta} = Builder.announce(user, post.object, public: true)
437
438 {:ok, private_announce_data, _meta} =
439 Builder.announce(user, private_post.object, public: false)
440
441 {:ok, relay_announce_data, _meta} =
442 Builder.announce(Pleroma.Web.ActivityPub.Relay.get_actor(), post.object, public: true)
443
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)
447
448 %{
449 announce: announce,
450 user: user,
451 poster: poster,
452 private_announce: private_announce,
453 relay_announce: relay_announce
454 }
455 end
456
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"]
462 end
463
464 test "does not add the announce to the original object if the actor is a service actor", %{
465 relay_announce: announce
466 } do
467 {:ok, announce, _} = SideEffects.handle(announce)
468 object = Object.get_by_ap_id(announce.data["object"])
469 assert object.data["announcement_count"] == nil
470 end
471
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)
475 end
476
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)
480
481 assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out(announce))
482 end
483 end
484 end
485 end