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