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