purge chat and shout endpoints
[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.Notification
11 alias Pleroma.Object
12 alias Pleroma.Repo
13 alias Pleroma.Tests.ObanHelpers
14 alias Pleroma.User
15 alias Pleroma.Web.ActivityPub.ActivityPub
16 alias Pleroma.Web.ActivityPub.Builder
17 alias Pleroma.Web.ActivityPub.SideEffects
18 alias Pleroma.Web.ActivityPub.Utils
19 alias Pleroma.Web.CommonAPI
20
21 import Mock
22 import Pleroma.Factory
23
24 describe "handle_after_transaction" do
25 test "it streams out notifications and streams" do
26 author = insert(:user, local: true)
27 recipient = insert(:user, local: true)
28
29 {:ok, note_data, _meta} =
30 Builder.note(%Pleroma.Web.CommonAPI.ActivityDraft{
31 user: author,
32 to: [recipient.ap_id],
33 mentions: [recipient],
34 content_html: "hey",
35 extra: %{"id" => Utils.generate_object_id()}
36 })
37
38 {:ok, create_activity_data, _meta} =
39 Builder.create(author, note_data["id"], [recipient.ap_id])
40
41 {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false)
42
43 {:ok, _create_activity, meta} =
44 SideEffects.handle(create_activity, local: false, object_data: note_data)
45
46 assert [notification] = meta[:notifications]
47
48 with_mocks([
49 {
50 Pleroma.Web.Streamer,
51 [],
52 [
53 stream: fn _, _ -> nil end
54 ]
55 },
56 {
57 Pleroma.Web.Push,
58 [],
59 [
60 send: fn _ -> nil end
61 ]
62 }
63 ]) do
64 SideEffects.handle_after_transaction(meta)
65
66 assert called(Pleroma.Web.Streamer.stream(["user", "user:notification"], notification))
67 assert called(Pleroma.Web.Push.send(notification))
68 end
69 end
70 end
71
72 describe "blocking users" do
73 setup do
74 user = insert(:user)
75 blocked = insert(:user)
76 User.follow(blocked, user)
77 User.follow(user, blocked)
78
79 {:ok, block_data, []} = Builder.block(user, blocked)
80 {:ok, block, _meta} = ActivityPub.persist(block_data, local: true)
81
82 %{user: user, blocked: blocked, block: block}
83 end
84
85 test "it unfollows and blocks", %{user: user, blocked: blocked, block: block} do
86 assert User.following?(user, blocked)
87 assert User.following?(blocked, user)
88
89 {:ok, _, _} = SideEffects.handle(block)
90
91 refute User.following?(user, blocked)
92 refute User.following?(blocked, user)
93 assert User.blocks?(user, blocked)
94 end
95
96 test "it updates following relationship", %{user: user, blocked: blocked, block: block} do
97 {:ok, _, _} = SideEffects.handle(block)
98
99 refute Pleroma.FollowingRelationship.get(user, blocked)
100 assert User.get_follow_state(user, blocked) == nil
101 assert User.get_follow_state(blocked, user) == nil
102 assert User.get_follow_state(user, blocked, nil) == nil
103 assert User.get_follow_state(blocked, user, nil) == nil
104 end
105
106 test "it blocks but does not unfollow if the relevant setting is set", %{
107 user: user,
108 blocked: blocked,
109 block: block
110 } do
111 clear_config([:activitypub, :unfollow_blocked], false)
112 assert User.following?(user, blocked)
113 assert User.following?(blocked, user)
114
115 {:ok, _, _} = SideEffects.handle(block)
116
117 refute User.following?(user, blocked)
118 assert User.following?(blocked, user)
119 assert User.blocks?(user, blocked)
120 end
121 end
122
123 describe "update users" do
124 setup do
125 user = insert(:user, local: false)
126 {:ok, update_data, []} = Builder.update(user, %{"id" => user.ap_id, "name" => "new name!"})
127 {:ok, update, _meta} = ActivityPub.persist(update_data, local: true)
128
129 %{user: user, update_data: update_data, update: update}
130 end
131
132 test "it updates the user", %{user: user, update: update} do
133 {:ok, _, _} = SideEffects.handle(update)
134 user = User.get_by_id(user.id)
135 assert user.name == "new name!"
136 end
137
138 test "it uses a given changeset to update", %{user: user, update: update} do
139 changeset = Ecto.Changeset.change(user, %{default_scope: "direct"})
140
141 assert user.default_scope == "public"
142 {:ok, _, _} = SideEffects.handle(update, user_update_changeset: changeset)
143 user = User.get_by_id(user.id)
144 assert user.default_scope == "direct"
145 end
146 end
147
148 describe "EmojiReact objects" do
149 setup do
150 poster = insert(:user)
151 user = insert(:user)
152
153 {:ok, post} = CommonAPI.post(poster, %{status: "hey"})
154
155 {:ok, emoji_react_data, []} = Builder.emoji_react(user, post.object, "👌")
156 {:ok, emoji_react, _meta} = ActivityPub.persist(emoji_react_data, local: true)
157
158 %{emoji_react: emoji_react, user: user, poster: poster}
159 end
160
161 test "adds the reaction to the object", %{emoji_react: emoji_react, user: user} do
162 {:ok, emoji_react, _} = SideEffects.handle(emoji_react)
163 object = Object.get_by_ap_id(emoji_react.data["object"])
164
165 assert object.data["reaction_count"] == 1
166 assert ["👌", [user.ap_id], nil] in object.data["reactions"]
167 end
168
169 test "creates a notification", %{emoji_react: emoji_react, poster: poster} do
170 {:ok, emoji_react, _} = SideEffects.handle(emoji_react)
171 assert Repo.get_by(Notification, user_id: poster.id, activity_id: emoji_react.id)
172 end
173 end
174
175 describe "Question objects" do
176 setup do
177 user = insert(:user)
178 question = build(:question, user: user)
179 question_activity = build(:question_activity, question: question)
180 activity_data = Map.put(question_activity.data, "object", question.data["id"])
181 meta = [object_data: question.data, local: false]
182
183 {:ok, activity, meta} = ActivityPub.persist(activity_data, meta)
184
185 %{activity: activity, meta: meta}
186 end
187
188 test "enqueues the poll end", %{activity: activity, meta: meta} do
189 {:ok, activity, meta} = SideEffects.handle(activity, meta)
190
191 assert_enqueued(
192 worker: Pleroma.Workers.PollWorker,
193 args: %{op: "poll_end", activity_id: activity.id},
194 scheduled_at: NaiveDateTime.from_iso8601!(meta[:object_data]["closed"])
195 )
196 end
197 end
198
199 describe "delete users with confirmation pending" do
200 setup do
201 user = insert(:user, is_confirmed: false)
202 {:ok, delete_user_data, _meta} = Builder.delete(user, user.ap_id)
203 {:ok, delete_user, _meta} = ActivityPub.persist(delete_user_data, local: true)
204 {:ok, delete: delete_user, user: user}
205 end
206
207 test "when activation is required", %{delete: delete, user: user} do
208 clear_config([:instance, :account_activation_required], true)
209 {:ok, _, _} = SideEffects.handle(delete)
210 ObanHelpers.perform_all()
211
212 refute User.get_cached_by_id(user.id)
213 end
214 end
215
216 describe "Undo objects" do
217 setup do
218 poster = insert(:user)
219 user = insert(:user)
220 {:ok, post} = CommonAPI.post(poster, %{status: "hey"})
221 {:ok, like} = CommonAPI.favorite(user, post.id)
222 {:ok, reaction} = CommonAPI.react_with_emoji(post.id, user, "👍")
223 {:ok, announce} = CommonAPI.repeat(post.id, user)
224 {:ok, block} = CommonAPI.block(user, poster)
225
226 {:ok, undo_data, _meta} = Builder.undo(user, like)
227 {:ok, like_undo, _meta} = ActivityPub.persist(undo_data, local: true)
228
229 {:ok, undo_data, _meta} = Builder.undo(user, reaction)
230 {:ok, reaction_undo, _meta} = ActivityPub.persist(undo_data, local: true)
231
232 {:ok, undo_data, _meta} = Builder.undo(user, announce)
233 {:ok, announce_undo, _meta} = ActivityPub.persist(undo_data, local: true)
234
235 {:ok, undo_data, _meta} = Builder.undo(user, block)
236 {:ok, block_undo, _meta} = ActivityPub.persist(undo_data, local: true)
237
238 %{
239 like_undo: like_undo,
240 post: post,
241 like: like,
242 reaction_undo: reaction_undo,
243 reaction: reaction,
244 announce_undo: announce_undo,
245 announce: announce,
246 block_undo: block_undo,
247 block: block,
248 poster: poster,
249 user: user
250 }
251 end
252
253 test "deletes the original block", %{
254 block_undo: block_undo,
255 block: block
256 } do
257 {:ok, _block_undo, _meta} = SideEffects.handle(block_undo)
258
259 refute Activity.get_by_id(block.id)
260 end
261
262 test "unblocks the blocked user", %{block_undo: block_undo, block: block} do
263 blocker = User.get_by_ap_id(block.data["actor"])
264 blocked = User.get_by_ap_id(block.data["object"])
265
266 {:ok, _block_undo, _} = SideEffects.handle(block_undo)
267 refute User.blocks?(blocker, blocked)
268 end
269
270 test "an announce undo removes the announce from the object", %{
271 announce_undo: announce_undo,
272 post: post
273 } do
274 {:ok, _announce_undo, _} = SideEffects.handle(announce_undo)
275
276 object = Object.get_by_ap_id(post.data["object"])
277
278 assert object.data["announcement_count"] == 0
279 assert object.data["announcements"] == []
280 end
281
282 test "deletes the original announce", %{announce_undo: announce_undo, announce: announce} do
283 {:ok, _announce_undo, _} = SideEffects.handle(announce_undo)
284 refute Activity.get_by_id(announce.id)
285 end
286
287 test "a reaction undo removes the reaction from the object", %{
288 reaction_undo: reaction_undo,
289 post: post
290 } do
291 {:ok, _reaction_undo, _} = SideEffects.handle(reaction_undo)
292
293 object = Object.get_by_ap_id(post.data["object"])
294
295 assert object.data["reaction_count"] == 0
296 assert object.data["reactions"] == []
297 end
298
299 test "deletes the original reaction", %{reaction_undo: reaction_undo, reaction: reaction} do
300 {:ok, _reaction_undo, _} = SideEffects.handle(reaction_undo)
301 refute Activity.get_by_id(reaction.id)
302 end
303
304 test "a like undo removes the like from the object", %{like_undo: like_undo, post: post} do
305 {:ok, _like_undo, _} = SideEffects.handle(like_undo)
306
307 object = Object.get_by_ap_id(post.data["object"])
308
309 assert object.data["like_count"] == 0
310 assert object.data["likes"] == []
311 end
312
313 test "deletes the original like", %{like_undo: like_undo, like: like} do
314 {:ok, _like_undo, _} = SideEffects.handle(like_undo)
315 refute Activity.get_by_id(like.id)
316 end
317 end
318
319 describe "like objects" do
320 setup do
321 poster = insert(:user)
322 user = insert(:user)
323 {:ok, post} = CommonAPI.post(poster, %{status: "hey"})
324
325 {:ok, like_data, _meta} = Builder.like(user, post.object)
326 {:ok, like, _meta} = ActivityPub.persist(like_data, local: true)
327
328 %{like: like, user: user, poster: poster}
329 end
330
331 test "add the like to the original object", %{like: like, user: user} do
332 {:ok, like, _} = SideEffects.handle(like)
333 object = Object.get_by_ap_id(like.data["object"])
334 assert object.data["like_count"] == 1
335 assert user.ap_id in object.data["likes"]
336 end
337
338 test "creates a notification", %{like: like, poster: poster} do
339 {:ok, like, _} = SideEffects.handle(like)
340 assert Repo.get_by(Notification, user_id: poster.id, activity_id: like.id)
341 end
342 end
343
344 describe "announce objects" do
345 setup do
346 poster = insert(:user)
347 user = insert(:user)
348 {:ok, post} = CommonAPI.post(poster, %{status: "hey"})
349 {:ok, private_post} = CommonAPI.post(poster, %{status: "hey", visibility: "private"})
350
351 {:ok, announce_data, _meta} = Builder.announce(user, post.object, public: true)
352
353 {:ok, private_announce_data, _meta} =
354 Builder.announce(user, private_post.object, public: false)
355
356 {:ok, relay_announce_data, _meta} =
357 Builder.announce(Pleroma.Web.ActivityPub.Relay.get_actor(), post.object, public: true)
358
359 {:ok, announce, _meta} = ActivityPub.persist(announce_data, local: true)
360 {:ok, private_announce, _meta} = ActivityPub.persist(private_announce_data, local: true)
361 {:ok, relay_announce, _meta} = ActivityPub.persist(relay_announce_data, local: true)
362
363 %{
364 announce: announce,
365 user: user,
366 poster: poster,
367 private_announce: private_announce,
368 relay_announce: relay_announce
369 }
370 end
371
372 test "adds the announce to the original object", %{announce: announce, user: user} do
373 {:ok, announce, _} = SideEffects.handle(announce)
374 object = Object.get_by_ap_id(announce.data["object"])
375 assert object.data["announcement_count"] == 1
376 assert user.ap_id in object.data["announcements"]
377 end
378
379 test "does not add the announce to the original object if the actor is a service actor", %{
380 relay_announce: announce
381 } do
382 {:ok, announce, _} = SideEffects.handle(announce)
383 object = Object.get_by_ap_id(announce.data["object"])
384 assert object.data["announcement_count"] == nil
385 end
386
387 test "creates a notification", %{announce: announce, poster: poster} do
388 {:ok, announce, _} = SideEffects.handle(announce)
389 assert Repo.get_by(Notification, user_id: poster.id, activity_id: announce.id)
390 end
391
392 test "it streams out the announce", %{announce: announce} do
393 with_mocks([
394 {
395 Pleroma.Web.Streamer,
396 [],
397 [
398 stream: fn _, _ -> nil end
399 ]
400 },
401 {
402 Pleroma.Web.Push,
403 [],
404 [
405 send: fn _ -> nil end
406 ]
407 }
408 ]) do
409 {:ok, announce, _} = SideEffects.handle(announce)
410
411 assert called(
412 Pleroma.Web.Streamer.stream(["user", "list", "public", "public:local"], announce)
413 )
414
415 assert called(Pleroma.Web.Push.send(:_))
416 end
417 end
418 end
419
420 describe "removing a follower" do
421 setup do
422 user = insert(:user)
423 followed = insert(:user)
424
425 {:ok, _, _, follow_activity} = CommonAPI.follow(user, followed)
426
427 {:ok, reject_data, []} = Builder.reject(followed, follow_activity)
428 {:ok, reject, _meta} = ActivityPub.persist(reject_data, local: true)
429
430 %{user: user, followed: followed, reject: reject}
431 end
432
433 test "", %{user: user, followed: followed, reject: reject} do
434 assert User.following?(user, followed)
435 assert Pleroma.FollowingRelationship.get(user, followed)
436
437 {:ok, _, _} = SideEffects.handle(reject)
438
439 refute User.following?(user, followed)
440 refute Pleroma.FollowingRelationship.get(user, followed)
441 assert User.get_follow_state(user, followed) == nil
442 assert User.get_follow_state(user, followed, nil) == nil
443 end
444 end
445
446 describe "removing a follower from remote" do
447 setup do
448 user = insert(:user)
449 followed = insert(:user, local: false)
450
451 # Mock a local-to-remote follow
452 {:ok, follow_data, []} = Builder.follow(user, followed)
453
454 follow_data =
455 follow_data
456 |> Map.put("state", "accept")
457
458 {:ok, follow, _meta} = ActivityPub.persist(follow_data, local: true)
459 {:ok, _, _} = SideEffects.handle(follow)
460
461 # Mock a remote-to-local accept
462 {:ok, accept_data, _} = Builder.accept(followed, follow)
463 {:ok, accept, _} = ActivityPub.persist(accept_data, local: false)
464 {:ok, _, _} = SideEffects.handle(accept)
465
466 # Mock a remote-to-local reject
467 {:ok, reject_data, []} = Builder.reject(followed, follow)
468 {:ok, reject, _meta} = ActivityPub.persist(reject_data, local: false)
469
470 %{user: user, followed: followed, reject: reject}
471 end
472
473 test "", %{user: user, followed: followed, reject: reject} do
474 assert User.following?(user, followed)
475 assert Pleroma.FollowingRelationship.get(user, followed)
476
477 {:ok, _, _} = SideEffects.handle(reject)
478
479 refute User.following?(user, followed)
480 refute Pleroma.FollowingRelationship.get(user, followed)
481
482 assert Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(user, followed).data["state"] ==
483 "reject"
484
485 assert User.get_follow_state(user, followed) == nil
486 assert User.get_follow_state(user, followed, nil) == nil
487 end
488 end
489 end