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