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