Merge branch 'develop' of git.pleroma.social:pleroma/pleroma into remake-remodel-dms
[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.Notification
12 alias Pleroma.Object
13 alias Pleroma.Repo
14 alias Pleroma.Tests.ObanHelpers
15 alias Pleroma.User
16 alias Pleroma.Web.ActivityPub.ActivityPub
17 alias Pleroma.Web.ActivityPub.Builder
18 alias Pleroma.Web.ActivityPub.SideEffects
19 alias Pleroma.Web.CommonAPI
20
21 import Pleroma.Factory
22 import Mock
23
24 describe "delete objects" do
25 setup do
26 user = insert(:user)
27 other_user = insert(:user)
28
29 {:ok, op} = CommonAPI.post(other_user, %{status: "big oof"})
30 {:ok, post} = CommonAPI.post(user, %{status: "hey", in_reply_to_id: op})
31 {:ok, favorite} = CommonAPI.favorite(user, post.id)
32 object = Object.normalize(post)
33 {:ok, delete_data, _meta} = Builder.delete(user, object.data["id"])
34 {:ok, delete_user_data, _meta} = Builder.delete(user, user.ap_id)
35 {:ok, delete, _meta} = ActivityPub.persist(delete_data, local: true)
36 {:ok, delete_user, _meta} = ActivityPub.persist(delete_user_data, local: true)
37
38 %{
39 user: user,
40 delete: delete,
41 post: post,
42 object: object,
43 delete_user: delete_user,
44 op: op,
45 favorite: favorite
46 }
47 end
48
49 test "it handles object deletions", %{
50 delete: delete,
51 post: post,
52 object: object,
53 user: user,
54 op: op,
55 favorite: favorite
56 } do
57 with_mock Pleroma.Web.ActivityPub.ActivityPub, [:passthrough],
58 stream_out: fn _ -> nil end,
59 stream_out_participations: fn _, _ -> nil end do
60 {:ok, delete, _} = SideEffects.handle(delete)
61 user = User.get_cached_by_ap_id(object.data["actor"])
62
63 assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out(delete))
64 assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out_participations(object, user))
65 end
66
67 object = Object.get_by_id(object.id)
68 assert object.data["type"] == "Tombstone"
69 refute Activity.get_by_id(post.id)
70 refute Activity.get_by_id(favorite.id)
71
72 user = User.get_by_id(user.id)
73 assert user.note_count == 0
74
75 object = Object.normalize(op.data["object"], false)
76
77 assert object.data["repliesCount"] == 0
78 end
79
80 test "it handles object deletions when the object itself has been pruned", %{
81 delete: delete,
82 post: post,
83 object: object,
84 user: user,
85 op: op
86 } do
87 with_mock Pleroma.Web.ActivityPub.ActivityPub, [:passthrough],
88 stream_out: fn _ -> nil end,
89 stream_out_participations: fn _, _ -> nil end do
90 {:ok, delete, _} = SideEffects.handle(delete)
91 user = User.get_cached_by_ap_id(object.data["actor"])
92
93 assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out(delete))
94 assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out_participations(object, user))
95 end
96
97 object = Object.get_by_id(object.id)
98 assert object.data["type"] == "Tombstone"
99 refute Activity.get_by_id(post.id)
100
101 user = User.get_by_id(user.id)
102 assert user.note_count == 0
103
104 object = Object.normalize(op.data["object"], false)
105
106 assert object.data["repliesCount"] == 0
107 end
108
109 test "it handles user deletions", %{delete_user: delete, user: user} do
110 {:ok, _delete, _} = SideEffects.handle(delete)
111 ObanHelpers.perform_all()
112
113 assert User.get_cached_by_ap_id(user.ap_id).deactivated
114 end
115 end
116
117 describe "EmojiReact objects" do
118 setup do
119 poster = insert(:user)
120 user = insert(:user)
121
122 {:ok, post} = CommonAPI.post(poster, %{status: "hey"})
123
124 {:ok, emoji_react_data, []} = Builder.emoji_react(user, post.object, "👌")
125 {:ok, emoji_react, _meta} = ActivityPub.persist(emoji_react_data, local: true)
126
127 %{emoji_react: emoji_react, user: user, poster: poster}
128 end
129
130 test "adds the reaction to the object", %{emoji_react: emoji_react, user: user} do
131 {:ok, emoji_react, _} = SideEffects.handle(emoji_react)
132 object = Object.get_by_ap_id(emoji_react.data["object"])
133
134 assert object.data["reaction_count"] == 1
135 assert ["👌", [user.ap_id]] in object.data["reactions"]
136 end
137
138 test "creates a notification", %{emoji_react: emoji_react, poster: poster} do
139 {:ok, emoji_react, _} = SideEffects.handle(emoji_react)
140 assert Repo.get_by(Notification, user_id: poster.id, activity_id: emoji_react.id)
141 end
142 end
143
144 describe "delete users with confirmation pending" do
145 setup do
146 user = insert(:user, confirmation_pending: true)
147 {:ok, delete_user_data, _meta} = Builder.delete(user, user.ap_id)
148 {:ok, delete_user, _meta} = ActivityPub.persist(delete_user_data, local: true)
149 {:ok, delete: delete_user, user: user}
150 end
151
152 test "when activation is not required", %{delete: delete, user: user} do
153 clear_config([:instance, :account_activation_required], false)
154 {:ok, _, _} = SideEffects.handle(delete)
155 ObanHelpers.perform_all()
156
157 assert User.get_cached_by_id(user.id).deactivated
158 end
159
160 test "when activation is required", %{delete: delete, user: user} do
161 clear_config([:instance, :account_activation_required], true)
162 {:ok, _, _} = SideEffects.handle(delete)
163 ObanHelpers.perform_all()
164
165 refute User.get_cached_by_id(user.id)
166 end
167 end
168
169 describe "Undo objects" do
170 setup do
171 poster = insert(:user)
172 user = insert(:user)
173 {:ok, post} = CommonAPI.post(poster, %{status: "hey"})
174 {:ok, like} = CommonAPI.favorite(user, post.id)
175 {:ok, reaction} = CommonAPI.react_with_emoji(post.id, user, "👍")
176 {:ok, announce} = CommonAPI.repeat(post.id, user)
177 {:ok, block} = ActivityPub.block(user, poster)
178 User.block(user, poster)
179
180 {:ok, undo_data, _meta} = Builder.undo(user, like)
181 {:ok, like_undo, _meta} = ActivityPub.persist(undo_data, local: true)
182
183 {:ok, undo_data, _meta} = Builder.undo(user, reaction)
184 {:ok, reaction_undo, _meta} = ActivityPub.persist(undo_data, local: true)
185
186 {:ok, undo_data, _meta} = Builder.undo(user, announce)
187 {:ok, announce_undo, _meta} = ActivityPub.persist(undo_data, local: true)
188
189 {:ok, undo_data, _meta} = Builder.undo(user, block)
190 {:ok, block_undo, _meta} = ActivityPub.persist(undo_data, local: true)
191
192 %{
193 like_undo: like_undo,
194 post: post,
195 like: like,
196 reaction_undo: reaction_undo,
197 reaction: reaction,
198 announce_undo: announce_undo,
199 announce: announce,
200 block_undo: block_undo,
201 block: block,
202 poster: poster,
203 user: user
204 }
205 end
206
207 test "deletes the original block", %{block_undo: block_undo, block: block} do
208 {:ok, _block_undo, _} = SideEffects.handle(block_undo)
209 refute Activity.get_by_id(block.id)
210 end
211
212 test "unblocks the blocked user", %{block_undo: block_undo, block: block} do
213 blocker = User.get_by_ap_id(block.data["actor"])
214 blocked = User.get_by_ap_id(block.data["object"])
215
216 {:ok, _block_undo, _} = SideEffects.handle(block_undo)
217 refute User.blocks?(blocker, blocked)
218 end
219
220 test "an announce undo removes the announce from the object", %{
221 announce_undo: announce_undo,
222 post: post
223 } do
224 {:ok, _announce_undo, _} = SideEffects.handle(announce_undo)
225
226 object = Object.get_by_ap_id(post.data["object"])
227
228 assert object.data["announcement_count"] == 0
229 assert object.data["announcements"] == []
230 end
231
232 test "deletes the original announce", %{announce_undo: announce_undo, announce: announce} do
233 {:ok, _announce_undo, _} = SideEffects.handle(announce_undo)
234 refute Activity.get_by_id(announce.id)
235 end
236
237 test "a reaction undo removes the reaction from the object", %{
238 reaction_undo: reaction_undo,
239 post: post
240 } do
241 {:ok, _reaction_undo, _} = SideEffects.handle(reaction_undo)
242
243 object = Object.get_by_ap_id(post.data["object"])
244
245 assert object.data["reaction_count"] == 0
246 assert object.data["reactions"] == []
247 end
248
249 test "deletes the original reaction", %{reaction_undo: reaction_undo, reaction: reaction} do
250 {:ok, _reaction_undo, _} = SideEffects.handle(reaction_undo)
251 refute Activity.get_by_id(reaction.id)
252 end
253
254 test "a like undo removes the like from the object", %{like_undo: like_undo, post: post} do
255 {:ok, _like_undo, _} = SideEffects.handle(like_undo)
256
257 object = Object.get_by_ap_id(post.data["object"])
258
259 assert object.data["like_count"] == 0
260 assert object.data["likes"] == []
261 end
262
263 test "deletes the original like", %{like_undo: like_undo, like: like} do
264 {:ok, _like_undo, _} = SideEffects.handle(like_undo)
265 refute Activity.get_by_id(like.id)
266 end
267 end
268
269 describe "like objects" do
270 setup do
271 poster = insert(:user)
272 user = insert(:user)
273 {:ok, post} = CommonAPI.post(poster, %{status: "hey"})
274
275 {:ok, like_data, _meta} = Builder.like(user, post.object)
276 {:ok, like, _meta} = ActivityPub.persist(like_data, local: true)
277
278 %{like: like, user: user, poster: poster}
279 end
280
281 test "add the like to the original object", %{like: like, user: user} do
282 {:ok, like, _} = SideEffects.handle(like)
283 object = Object.get_by_ap_id(like.data["object"])
284 assert object.data["like_count"] == 1
285 assert user.ap_id in object.data["likes"]
286 end
287
288 test "creates a notification", %{like: like, poster: poster} do
289 {:ok, like, _} = SideEffects.handle(like)
290 assert Repo.get_by(Notification, user_id: poster.id, activity_id: like.id)
291 end
292 end
293
294 describe "creation of ChatMessages" do
295 test "notifies the recipient" do
296 author = insert(:user, local: false)
297 recipient = insert(:user, local: true)
298
299 {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey")
300
301 {:ok, create_activity_data, _meta} =
302 Builder.create(author, chat_message_data["id"], [recipient.ap_id])
303
304 {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false)
305
306 {:ok, _create_activity, _meta} =
307 SideEffects.handle(create_activity, local: false, object_data: chat_message_data)
308
309 assert Repo.get_by(Notification, user_id: recipient.id, activity_id: create_activity.id)
310 end
311
312 test "it streams the created ChatMessage" do
313 author = insert(:user, local: true)
314 recipient = insert(:user, local: true)
315
316 {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey")
317
318 {:ok, create_activity_data, _meta} =
319 Builder.create(author, chat_message_data["id"], [recipient.ap_id])
320
321 {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false)
322
323 with_mock Pleroma.Web.Streamer, [], stream: fn _, _ -> nil end do
324 {:ok, _create_activity, _meta} =
325 SideEffects.handle(create_activity, local: false, object_data: chat_message_data)
326
327 object = Object.normalize(create_activity, false)
328
329 assert called(Pleroma.Web.Streamer.stream(["user", "user:pleroma_chat"], object))
330 end
331 end
332
333 test "it creates a Chat for the local users and bumps the unread count, except for the author" do
334 author = insert(:user, local: true)
335 recipient = insert(:user, local: true)
336
337 {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey")
338
339 {:ok, create_activity_data, _meta} =
340 Builder.create(author, chat_message_data["id"], [recipient.ap_id])
341
342 {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false)
343
344 {:ok, _create_activity, _meta} =
345 SideEffects.handle(create_activity, local: false, object_data: chat_message_data)
346
347 chat = Chat.get(author.id, recipient.ap_id)
348 assert chat.unread == 0
349
350 chat = Chat.get(recipient.id, author.ap_id)
351 assert chat.unread == 1
352 end
353
354 test "it creates a Chat for the local users and bumps the unread count" 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 # An object is created
369 assert Object.get_by_ap_id(chat_message_data["id"])
370
371 # The remote user won't get a chat
372 chat = Chat.get(author.id, recipient.ap_id)
373 refute chat
374
375 # The local user will get a chat
376 chat = Chat.get(recipient.id, author.ap_id)
377 assert chat
378
379 author = insert(:user, local: true)
380 recipient = insert(:user, local: true)
381
382 {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey")
383
384 {:ok, create_activity_data, _meta} =
385 Builder.create(author, chat_message_data["id"], [recipient.ap_id])
386
387 {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false)
388
389 {:ok, _create_activity, _meta} =
390 SideEffects.handle(create_activity, local: false, object_data: chat_message_data)
391
392 # Both users are local and get the chat
393 chat = Chat.get(author.id, recipient.ap_id)
394 assert chat
395
396 chat = Chat.get(recipient.id, author.ap_id)
397 assert chat
398 end
399 end
400
401 describe "announce objects" do
402 setup do
403 poster = insert(:user)
404 user = insert(:user)
405 {:ok, post} = CommonAPI.post(poster, %{status: "hey"})
406 {:ok, private_post} = CommonAPI.post(poster, %{status: "hey", visibility: "private"})
407
408 {:ok, announce_data, _meta} = Builder.announce(user, post.object, public: true)
409
410 {:ok, private_announce_data, _meta} =
411 Builder.announce(user, private_post.object, public: false)
412
413 {:ok, relay_announce_data, _meta} =
414 Builder.announce(Pleroma.Web.ActivityPub.Relay.get_actor(), post.object, public: true)
415
416 {:ok, announce, _meta} = ActivityPub.persist(announce_data, local: true)
417 {:ok, private_announce, _meta} = ActivityPub.persist(private_announce_data, local: true)
418 {:ok, relay_announce, _meta} = ActivityPub.persist(relay_announce_data, local: true)
419
420 %{
421 announce: announce,
422 user: user,
423 poster: poster,
424 private_announce: private_announce,
425 relay_announce: relay_announce
426 }
427 end
428
429 test "adds the announce to the original object", %{announce: announce, user: user} do
430 {:ok, announce, _} = SideEffects.handle(announce)
431 object = Object.get_by_ap_id(announce.data["object"])
432 assert object.data["announcement_count"] == 1
433 assert user.ap_id in object.data["announcements"]
434 end
435
436 test "does not add the announce to the original object if the actor is a service actor", %{
437 relay_announce: announce
438 } do
439 {:ok, announce, _} = SideEffects.handle(announce)
440 object = Object.get_by_ap_id(announce.data["object"])
441 assert object.data["announcement_count"] == nil
442 end
443
444 test "creates a notification", %{announce: announce, poster: poster} do
445 {:ok, announce, _} = SideEffects.handle(announce)
446 assert Repo.get_by(Notification, user_id: poster.id, activity_id: announce.id)
447 end
448
449 test "it streams out the announce", %{announce: announce} do
450 with_mock Pleroma.Web.ActivityPub.ActivityPub, [:passthrough], stream_out: fn _ -> nil end do
451 {:ok, announce, _} = SideEffects.handle(announce)
452
453 assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out(announce))
454 end
455 end
456 end
457 end