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