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