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