ce35c96051f7cae6dfead2910f62c408628a9fa3
[akkoma] / test / web / activity_pub / activity_pub_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.ActivityPubTest do
6 use Pleroma.DataCase
7 use Oban.Testing, repo: Pleroma.Repo
8
9 alias Pleroma.Activity
10 alias Pleroma.Builders.ActivityBuilder
11 alias Pleroma.Config
12 alias Pleroma.Notification
13 alias Pleroma.Object
14 alias Pleroma.User
15 alias Pleroma.Web.ActivityPub.ActivityPub
16 alias Pleroma.Web.ActivityPub.Utils
17 alias Pleroma.Web.AdminAPI.AccountView
18 alias Pleroma.Web.CommonAPI
19
20 import ExUnit.CaptureLog
21 import Mock
22 import Pleroma.Factory
23 import Tesla.Mock
24
25 setup do
26 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
27 :ok
28 end
29
30 setup do: clear_config([:instance, :federating])
31
32 describe "streaming out participations" do
33 test "it streams them out" do
34 user = insert(:user)
35 {:ok, activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"})
36
37 {:ok, conversation} = Pleroma.Conversation.create_or_bump_for(activity)
38
39 participations =
40 conversation.participations
41 |> Repo.preload(:user)
42
43 with_mock Pleroma.Web.Streamer,
44 stream: fn _, _ -> nil end do
45 ActivityPub.stream_out_participations(conversation.participations)
46
47 assert called(Pleroma.Web.Streamer.stream("participation", participations))
48 end
49 end
50
51 test "streams them out on activity creation" do
52 user_one = insert(:user)
53 user_two = insert(:user)
54
55 with_mock Pleroma.Web.Streamer,
56 stream: fn _, _ -> nil end do
57 {:ok, activity} =
58 CommonAPI.post(user_one, %{
59 status: "@#{user_two.nickname}",
60 visibility: "direct"
61 })
62
63 conversation =
64 activity.data["context"]
65 |> Pleroma.Conversation.get_for_ap_id()
66 |> Repo.preload(participations: :user)
67
68 assert called(Pleroma.Web.Streamer.stream("participation", conversation.participations))
69 end
70 end
71 end
72
73 describe "fetching restricted by visibility" do
74 test "it restricts by the appropriate visibility" do
75 user = insert(:user)
76
77 {:ok, public_activity} = CommonAPI.post(user, %{status: ".", visibility: "public"})
78
79 {:ok, direct_activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"})
80
81 {:ok, unlisted_activity} = CommonAPI.post(user, %{status: ".", visibility: "unlisted"})
82
83 {:ok, private_activity} = CommonAPI.post(user, %{status: ".", visibility: "private"})
84
85 activities = ActivityPub.fetch_activities([], %{visibility: "direct", actor_id: user.ap_id})
86
87 assert activities == [direct_activity]
88
89 activities =
90 ActivityPub.fetch_activities([], %{visibility: "unlisted", actor_id: user.ap_id})
91
92 assert activities == [unlisted_activity]
93
94 activities =
95 ActivityPub.fetch_activities([], %{visibility: "private", actor_id: user.ap_id})
96
97 assert activities == [private_activity]
98
99 activities = ActivityPub.fetch_activities([], %{visibility: "public", actor_id: user.ap_id})
100
101 assert activities == [public_activity]
102
103 activities =
104 ActivityPub.fetch_activities([], %{
105 visibility: ~w[private public],
106 actor_id: user.ap_id
107 })
108
109 assert activities == [public_activity, private_activity]
110 end
111 end
112
113 describe "fetching excluded by visibility" do
114 test "it excludes by the appropriate visibility" do
115 user = insert(:user)
116
117 {:ok, public_activity} = CommonAPI.post(user, %{status: ".", visibility: "public"})
118
119 {:ok, direct_activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"})
120
121 {:ok, unlisted_activity} = CommonAPI.post(user, %{status: ".", visibility: "unlisted"})
122
123 {:ok, private_activity} = CommonAPI.post(user, %{status: ".", visibility: "private"})
124
125 activities =
126 ActivityPub.fetch_activities([], %{
127 exclude_visibilities: "direct",
128 actor_id: user.ap_id
129 })
130
131 assert public_activity in activities
132 assert unlisted_activity in activities
133 assert private_activity in activities
134 refute direct_activity in activities
135
136 activities =
137 ActivityPub.fetch_activities([], %{
138 exclude_visibilities: "unlisted",
139 actor_id: user.ap_id
140 })
141
142 assert public_activity in activities
143 refute unlisted_activity in activities
144 assert private_activity in activities
145 assert direct_activity in activities
146
147 activities =
148 ActivityPub.fetch_activities([], %{
149 exclude_visibilities: "private",
150 actor_id: user.ap_id
151 })
152
153 assert public_activity in activities
154 assert unlisted_activity in activities
155 refute private_activity in activities
156 assert direct_activity in activities
157
158 activities =
159 ActivityPub.fetch_activities([], %{
160 exclude_visibilities: "public",
161 actor_id: user.ap_id
162 })
163
164 refute public_activity in activities
165 assert unlisted_activity in activities
166 assert private_activity in activities
167 assert direct_activity in activities
168 end
169 end
170
171 describe "building a user from his ap id" do
172 test "it returns a user" do
173 user_id = "http://mastodon.example.org/users/admin"
174 {:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
175 assert user.ap_id == user_id
176 assert user.nickname == "admin@mastodon.example.org"
177 assert user.ap_enabled
178 assert user.follower_address == "http://mastodon.example.org/users/admin/followers"
179 end
180
181 test "it returns a user that is invisible" do
182 user_id = "http://mastodon.example.org/users/relay"
183 {:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
184 assert User.invisible?(user)
185 end
186
187 test "it fetches the appropriate tag-restricted posts" do
188 user = insert(:user)
189
190 {:ok, status_one} = CommonAPI.post(user, %{status: ". #test"})
191 {:ok, status_two} = CommonAPI.post(user, %{status: ". #essais"})
192 {:ok, status_three} = CommonAPI.post(user, %{status: ". #test #reject"})
193
194 fetch_one = ActivityPub.fetch_activities([], %{type: "Create", tag: "test"})
195
196 fetch_two = ActivityPub.fetch_activities([], %{type: "Create", tag: ["test", "essais"]})
197
198 fetch_three =
199 ActivityPub.fetch_activities([], %{
200 type: "Create",
201 tag: ["test", "essais"],
202 tag_reject: ["reject"]
203 })
204
205 fetch_four =
206 ActivityPub.fetch_activities([], %{
207 type: "Create",
208 tag: ["test"],
209 tag_all: ["test", "reject"]
210 })
211
212 assert fetch_one == [status_one, status_three]
213 assert fetch_two == [status_one, status_two, status_three]
214 assert fetch_three == [status_one, status_two]
215 assert fetch_four == [status_three]
216 end
217 end
218
219 describe "insertion" do
220 test "drops activities beyond a certain limit" do
221 limit = Config.get([:instance, :remote_limit])
222
223 random_text =
224 :crypto.strong_rand_bytes(limit + 1)
225 |> Base.encode64()
226 |> binary_part(0, limit + 1)
227
228 data = %{
229 "ok" => true,
230 "object" => %{
231 "content" => random_text
232 }
233 }
234
235 assert {:error, {:remote_limit_error, _}} = ActivityPub.insert(data)
236 end
237
238 test "doesn't drop activities with content being null" do
239 user = insert(:user)
240
241 data = %{
242 "actor" => user.ap_id,
243 "to" => [],
244 "object" => %{
245 "actor" => user.ap_id,
246 "to" => [],
247 "type" => "Note",
248 "content" => nil
249 }
250 }
251
252 assert {:ok, _} = ActivityPub.insert(data)
253 end
254
255 test "returns the activity if one with the same id is already in" do
256 activity = insert(:note_activity)
257 {:ok, new_activity} = ActivityPub.insert(activity.data)
258
259 assert activity.id == new_activity.id
260 end
261
262 test "inserts a given map into the activity database, giving it an id if it has none." do
263 user = insert(:user)
264
265 data = %{
266 "actor" => user.ap_id,
267 "to" => [],
268 "object" => %{
269 "actor" => user.ap_id,
270 "to" => [],
271 "type" => "Note",
272 "content" => "hey"
273 }
274 }
275
276 {:ok, %Activity{} = activity} = ActivityPub.insert(data)
277 assert activity.data["ok"] == data["ok"]
278 assert is_binary(activity.data["id"])
279
280 given_id = "bla"
281
282 data = %{
283 "id" => given_id,
284 "actor" => user.ap_id,
285 "to" => [],
286 "context" => "blabla",
287 "object" => %{
288 "actor" => user.ap_id,
289 "to" => [],
290 "type" => "Note",
291 "content" => "hey"
292 }
293 }
294
295 {:ok, %Activity{} = activity} = ActivityPub.insert(data)
296 assert activity.data["ok"] == data["ok"]
297 assert activity.data["id"] == given_id
298 assert activity.data["context"] == "blabla"
299 assert activity.data["context_id"]
300 end
301
302 test "adds a context when none is there" do
303 user = insert(:user)
304
305 data = %{
306 "actor" => user.ap_id,
307 "to" => [],
308 "object" => %{
309 "actor" => user.ap_id,
310 "to" => [],
311 "type" => "Note",
312 "content" => "hey"
313 }
314 }
315
316 {:ok, %Activity{} = activity} = ActivityPub.insert(data)
317 object = Pleroma.Object.normalize(activity)
318
319 assert is_binary(activity.data["context"])
320 assert is_binary(object.data["context"])
321 assert activity.data["context_id"]
322 assert object.data["context_id"]
323 end
324
325 test "adds an id to a given object if it lacks one and is a note and inserts it to the object database" do
326 user = insert(:user)
327
328 data = %{
329 "actor" => user.ap_id,
330 "to" => [],
331 "object" => %{
332 "actor" => user.ap_id,
333 "to" => [],
334 "type" => "Note",
335 "content" => "hey"
336 }
337 }
338
339 {:ok, %Activity{} = activity} = ActivityPub.insert(data)
340 assert object = Object.normalize(activity)
341 assert is_binary(object.data["id"])
342 end
343 end
344
345 describe "listen activities" do
346 test "does not increase user note count" do
347 user = insert(:user)
348
349 {:ok, activity} =
350 ActivityPub.listen(%{
351 to: ["https://www.w3.org/ns/activitystreams#Public"],
352 actor: user,
353 context: "",
354 object: %{
355 "actor" => user.ap_id,
356 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
357 "artist" => "lain",
358 "title" => "lain radio episode 1",
359 "length" => 180_000,
360 "type" => "Audio"
361 }
362 })
363
364 assert activity.actor == user.ap_id
365
366 user = User.get_cached_by_id(user.id)
367 assert user.note_count == 0
368 end
369
370 test "can be fetched into a timeline" do
371 _listen_activity_1 = insert(:listen)
372 _listen_activity_2 = insert(:listen)
373 _listen_activity_3 = insert(:listen)
374
375 timeline = ActivityPub.fetch_activities([], %{type: ["Listen"]})
376
377 assert length(timeline) == 3
378 end
379 end
380
381 describe "create activities" do
382 test "it reverts create" do
383 user = insert(:user)
384
385 with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
386 assert {:error, :reverted} =
387 ActivityPub.create(%{
388 to: ["user1", "user2"],
389 actor: user,
390 context: "",
391 object: %{
392 "to" => ["user1", "user2"],
393 "type" => "Note",
394 "content" => "testing"
395 }
396 })
397 end
398
399 assert Repo.aggregate(Activity, :count, :id) == 0
400 assert Repo.aggregate(Object, :count, :id) == 0
401 end
402
403 test "removes doubled 'to' recipients" do
404 user = insert(:user)
405
406 {:ok, activity} =
407 ActivityPub.create(%{
408 to: ["user1", "user1", "user2"],
409 actor: user,
410 context: "",
411 object: %{
412 "to" => ["user1", "user1", "user2"],
413 "type" => "Note",
414 "content" => "testing"
415 }
416 })
417
418 assert activity.data["to"] == ["user1", "user2"]
419 assert activity.actor == user.ap_id
420 assert activity.recipients == ["user1", "user2", user.ap_id]
421 end
422
423 test "increases user note count only for public activities" do
424 user = insert(:user)
425
426 {:ok, _} =
427 CommonAPI.post(User.get_cached_by_id(user.id), %{
428 status: "1",
429 visibility: "public"
430 })
431
432 {:ok, _} =
433 CommonAPI.post(User.get_cached_by_id(user.id), %{
434 status: "2",
435 visibility: "unlisted"
436 })
437
438 {:ok, _} =
439 CommonAPI.post(User.get_cached_by_id(user.id), %{
440 status: "2",
441 visibility: "private"
442 })
443
444 {:ok, _} =
445 CommonAPI.post(User.get_cached_by_id(user.id), %{
446 status: "3",
447 visibility: "direct"
448 })
449
450 user = User.get_cached_by_id(user.id)
451 assert user.note_count == 2
452 end
453
454 test "increases replies count" do
455 user = insert(:user)
456 user2 = insert(:user)
457
458 {:ok, activity} = CommonAPI.post(user, %{status: "1", visibility: "public"})
459 ap_id = activity.data["id"]
460 reply_data = %{status: "1", in_reply_to_status_id: activity.id}
461
462 # public
463 {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, :visibility, "public"))
464 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
465 assert object.data["repliesCount"] == 1
466
467 # unlisted
468 {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, :visibility, "unlisted"))
469 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
470 assert object.data["repliesCount"] == 2
471
472 # private
473 {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, :visibility, "private"))
474 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
475 assert object.data["repliesCount"] == 2
476
477 # direct
478 {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, :visibility, "direct"))
479 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
480 assert object.data["repliesCount"] == 2
481 end
482 end
483
484 describe "fetch activities for recipients" do
485 test "retrieve the activities for certain recipients" do
486 {:ok, activity_one} = ActivityBuilder.insert(%{"to" => ["someone"]})
487 {:ok, activity_two} = ActivityBuilder.insert(%{"to" => ["someone_else"]})
488 {:ok, _activity_three} = ActivityBuilder.insert(%{"to" => ["noone"]})
489
490 activities = ActivityPub.fetch_activities(["someone", "someone_else"])
491 assert length(activities) == 2
492 assert activities == [activity_one, activity_two]
493 end
494 end
495
496 describe "fetch activities in context" do
497 test "retrieves activities that have a given context" do
498 {:ok, activity} = ActivityBuilder.insert(%{"type" => "Create", "context" => "2hu"})
499 {:ok, activity_two} = ActivityBuilder.insert(%{"type" => "Create", "context" => "2hu"})
500 {:ok, _activity_three} = ActivityBuilder.insert(%{"type" => "Create", "context" => "3hu"})
501 {:ok, _activity_four} = ActivityBuilder.insert(%{"type" => "Announce", "context" => "2hu"})
502 activity_five = insert(:note_activity)
503 user = insert(:user)
504
505 {:ok, _user_relationship} = User.block(user, %{ap_id: activity_five.data["actor"]})
506
507 activities = ActivityPub.fetch_activities_for_context("2hu", %{blocking_user: user})
508 assert activities == [activity_two, activity]
509 end
510 end
511
512 test "doesn't return blocked activities" do
513 activity_one = insert(:note_activity)
514 activity_two = insert(:note_activity)
515 activity_three = insert(:note_activity)
516 user = insert(:user)
517 booster = insert(:user)
518 {:ok, _user_relationship} = User.block(user, %{ap_id: activity_one.data["actor"]})
519
520 activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
521
522 assert Enum.member?(activities, activity_two)
523 assert Enum.member?(activities, activity_three)
524 refute Enum.member?(activities, activity_one)
525
526 {:ok, _user_block} = User.unblock(user, %{ap_id: activity_one.data["actor"]})
527
528 activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
529
530 assert Enum.member?(activities, activity_two)
531 assert Enum.member?(activities, activity_three)
532 assert Enum.member?(activities, activity_one)
533
534 {:ok, _user_relationship} = User.block(user, %{ap_id: activity_three.data["actor"]})
535 {:ok, %{data: %{"object" => id}}} = CommonAPI.repeat(activity_three.id, booster)
536 %Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
537 activity_three = Activity.get_by_id(activity_three.id)
538
539 activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
540
541 assert Enum.member?(activities, activity_two)
542 refute Enum.member?(activities, activity_three)
543 refute Enum.member?(activities, boost_activity)
544 assert Enum.member?(activities, activity_one)
545
546 activities = ActivityPub.fetch_activities([], %{blocking_user: nil, skip_preload: true})
547
548 assert Enum.member?(activities, activity_two)
549 assert Enum.member?(activities, activity_three)
550 assert Enum.member?(activities, boost_activity)
551 assert Enum.member?(activities, activity_one)
552 end
553
554 test "doesn't return transitive interactions concerning blocked users" do
555 blocker = insert(:user)
556 blockee = insert(:user)
557 friend = insert(:user)
558
559 {:ok, _user_relationship} = User.block(blocker, blockee)
560
561 {:ok, activity_one} = CommonAPI.post(friend, %{status: "hey!"})
562
563 {:ok, activity_two} = CommonAPI.post(friend, %{status: "hey! @#{blockee.nickname}"})
564
565 {:ok, activity_three} = CommonAPI.post(blockee, %{status: "hey! @#{friend.nickname}"})
566
567 {:ok, activity_four} = CommonAPI.post(blockee, %{status: "hey! @#{blocker.nickname}"})
568
569 activities = ActivityPub.fetch_activities([], %{blocking_user: blocker})
570
571 assert Enum.member?(activities, activity_one)
572 refute Enum.member?(activities, activity_two)
573 refute Enum.member?(activities, activity_three)
574 refute Enum.member?(activities, activity_four)
575 end
576
577 test "doesn't return announce activities with blocked users in 'to'" do
578 blocker = insert(:user)
579 blockee = insert(:user)
580 friend = insert(:user)
581
582 {:ok, _user_relationship} = User.block(blocker, blockee)
583
584 {:ok, activity_one} = CommonAPI.post(friend, %{status: "hey!"})
585
586 {:ok, activity_two} = CommonAPI.post(blockee, %{status: "hey! @#{friend.nickname}"})
587
588 {:ok, activity_three} = CommonAPI.repeat(activity_two.id, friend)
589
590 activities =
591 ActivityPub.fetch_activities([], %{blocking_user: blocker})
592 |> Enum.map(fn act -> act.id end)
593
594 assert Enum.member?(activities, activity_one.id)
595 refute Enum.member?(activities, activity_two.id)
596 refute Enum.member?(activities, activity_three.id)
597 end
598
599 test "doesn't return announce activities with blocked users in 'cc'" do
600 blocker = insert(:user)
601 blockee = insert(:user)
602 friend = insert(:user)
603
604 {:ok, _user_relationship} = User.block(blocker, blockee)
605
606 {:ok, activity_one} = CommonAPI.post(friend, %{status: "hey!"})
607
608 {:ok, activity_two} = CommonAPI.post(blockee, %{status: "hey! @#{friend.nickname}"})
609
610 assert object = Pleroma.Object.normalize(activity_two)
611
612 data = %{
613 "actor" => friend.ap_id,
614 "object" => object.data["id"],
615 "context" => object.data["context"],
616 "type" => "Announce",
617 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
618 "cc" => [blockee.ap_id]
619 }
620
621 assert {:ok, activity_three} = ActivityPub.insert(data)
622
623 activities =
624 ActivityPub.fetch_activities([], %{blocking_user: blocker})
625 |> Enum.map(fn act -> act.id end)
626
627 assert Enum.member?(activities, activity_one.id)
628 refute Enum.member?(activities, activity_two.id)
629 refute Enum.member?(activities, activity_three.id)
630 end
631
632 test "doesn't return activities from blocked domains" do
633 domain = "dogwhistle.zone"
634 domain_user = insert(:user, %{ap_id: "https://#{domain}/@pundit"})
635 note = insert(:note, %{data: %{"actor" => domain_user.ap_id}})
636 activity = insert(:note_activity, %{note: note})
637 user = insert(:user)
638 {:ok, user} = User.block_domain(user, domain)
639
640 activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
641
642 refute activity in activities
643
644 followed_user = insert(:user)
645 ActivityPub.follow(user, followed_user)
646 {:ok, repeat_activity} = CommonAPI.repeat(activity.id, followed_user)
647
648 activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
649
650 refute repeat_activity in activities
651 end
652
653 test "does return activities from followed users on blocked domains" do
654 domain = "meanies.social"
655 domain_user = insert(:user, %{ap_id: "https://#{domain}/@pundit"})
656 blocker = insert(:user)
657
658 {:ok, blocker} = User.follow(blocker, domain_user)
659 {:ok, blocker} = User.block_domain(blocker, domain)
660
661 assert User.following?(blocker, domain_user)
662 assert User.blocks_domain?(blocker, domain_user)
663 refute User.blocks?(blocker, domain_user)
664
665 note = insert(:note, %{data: %{"actor" => domain_user.ap_id}})
666 activity = insert(:note_activity, %{note: note})
667
668 activities = ActivityPub.fetch_activities([], %{blocking_user: blocker, skip_preload: true})
669
670 assert activity in activities
671
672 # And check that if the guy we DO follow boosts someone else from their domain,
673 # that should be hidden
674 another_user = insert(:user, %{ap_id: "https://#{domain}/@meanie2"})
675 bad_note = insert(:note, %{data: %{"actor" => another_user.ap_id}})
676 bad_activity = insert(:note_activity, %{note: bad_note})
677 {:ok, repeat_activity} = CommonAPI.repeat(bad_activity.id, domain_user)
678
679 activities = ActivityPub.fetch_activities([], %{blocking_user: blocker, skip_preload: true})
680
681 refute repeat_activity in activities
682 end
683
684 test "doesn't return muted activities" do
685 activity_one = insert(:note_activity)
686 activity_two = insert(:note_activity)
687 activity_three = insert(:note_activity)
688 user = insert(:user)
689 booster = insert(:user)
690
691 activity_one_actor = User.get_by_ap_id(activity_one.data["actor"])
692 {:ok, _user_relationships} = User.mute(user, activity_one_actor)
693
694 activities = ActivityPub.fetch_activities([], %{muting_user: user, skip_preload: true})
695
696 assert Enum.member?(activities, activity_two)
697 assert Enum.member?(activities, activity_three)
698 refute Enum.member?(activities, activity_one)
699
700 # Calling with 'with_muted' will deliver muted activities, too.
701 activities =
702 ActivityPub.fetch_activities([], %{
703 muting_user: user,
704 with_muted: true,
705 skip_preload: true
706 })
707
708 assert Enum.member?(activities, activity_two)
709 assert Enum.member?(activities, activity_three)
710 assert Enum.member?(activities, activity_one)
711
712 {:ok, _user_mute} = User.unmute(user, activity_one_actor)
713
714 activities = ActivityPub.fetch_activities([], %{muting_user: user, skip_preload: true})
715
716 assert Enum.member?(activities, activity_two)
717 assert Enum.member?(activities, activity_three)
718 assert Enum.member?(activities, activity_one)
719
720 activity_three_actor = User.get_by_ap_id(activity_three.data["actor"])
721 {:ok, _user_relationships} = User.mute(user, activity_three_actor)
722 {:ok, %{data: %{"object" => id}}} = CommonAPI.repeat(activity_three.id, booster)
723 %Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
724 activity_three = Activity.get_by_id(activity_three.id)
725
726 activities = ActivityPub.fetch_activities([], %{muting_user: user, skip_preload: true})
727
728 assert Enum.member?(activities, activity_two)
729 refute Enum.member?(activities, activity_three)
730 refute Enum.member?(activities, boost_activity)
731 assert Enum.member?(activities, activity_one)
732
733 activities = ActivityPub.fetch_activities([], %{muting_user: nil, skip_preload: true})
734
735 assert Enum.member?(activities, activity_two)
736 assert Enum.member?(activities, activity_three)
737 assert Enum.member?(activities, boost_activity)
738 assert Enum.member?(activities, activity_one)
739 end
740
741 test "doesn't return thread muted activities" do
742 user = insert(:user)
743 _activity_one = insert(:note_activity)
744 note_two = insert(:note, data: %{"context" => "suya.."})
745 activity_two = insert(:note_activity, note: note_two)
746
747 {:ok, _activity_two} = CommonAPI.add_mute(user, activity_two)
748
749 assert [_activity_one] = ActivityPub.fetch_activities([], %{muting_user: user})
750 end
751
752 test "returns thread muted activities when with_muted is set" do
753 user = insert(:user)
754 _activity_one = insert(:note_activity)
755 note_two = insert(:note, data: %{"context" => "suya.."})
756 activity_two = insert(:note_activity, note: note_two)
757
758 {:ok, _activity_two} = CommonAPI.add_mute(user, activity_two)
759
760 assert [_activity_two, _activity_one] =
761 ActivityPub.fetch_activities([], %{muting_user: user, with_muted: true})
762 end
763
764 test "does include announces on request" do
765 activity_three = insert(:note_activity)
766 user = insert(:user)
767 booster = insert(:user)
768
769 {:ok, user} = User.follow(user, booster)
770
771 {:ok, announce} = CommonAPI.repeat(activity_three.id, booster)
772
773 [announce_activity] = ActivityPub.fetch_activities([user.ap_id | User.following(user)])
774
775 assert announce_activity.id == announce.id
776 end
777
778 test "excludes reblogs on request" do
779 user = insert(:user)
780 {:ok, expected_activity} = ActivityBuilder.insert(%{"type" => "Create"}, %{:user => user})
781 {:ok, _} = ActivityBuilder.insert(%{"type" => "Announce"}, %{:user => user})
782
783 [activity] = ActivityPub.fetch_user_activities(user, nil, %{exclude_reblogs: true})
784
785 assert activity == expected_activity
786 end
787
788 describe "public fetch activities" do
789 test "doesn't retrieve unlisted activities" do
790 user = insert(:user)
791
792 {:ok, _unlisted_activity} = CommonAPI.post(user, %{status: "yeah", visibility: "unlisted"})
793
794 {:ok, listed_activity} = CommonAPI.post(user, %{status: "yeah"})
795
796 [activity] = ActivityPub.fetch_public_activities()
797
798 assert activity == listed_activity
799 end
800
801 test "retrieves public activities" do
802 _activities = ActivityPub.fetch_public_activities()
803
804 %{public: public} = ActivityBuilder.public_and_non_public()
805
806 activities = ActivityPub.fetch_public_activities()
807 assert length(activities) == 1
808 assert Enum.at(activities, 0) == public
809 end
810
811 test "retrieves a maximum of 20 activities" do
812 ActivityBuilder.insert_list(10)
813 expected_activities = ActivityBuilder.insert_list(20)
814
815 activities = ActivityPub.fetch_public_activities()
816
817 assert collect_ids(activities) == collect_ids(expected_activities)
818 assert length(activities) == 20
819 end
820
821 test "retrieves ids starting from a since_id" do
822 activities = ActivityBuilder.insert_list(30)
823 expected_activities = ActivityBuilder.insert_list(10)
824 since_id = List.last(activities).id
825
826 activities = ActivityPub.fetch_public_activities(%{since_id: since_id})
827
828 assert collect_ids(activities) == collect_ids(expected_activities)
829 assert length(activities) == 10
830 end
831
832 test "retrieves ids up to max_id" do
833 ActivityBuilder.insert_list(10)
834 expected_activities = ActivityBuilder.insert_list(20)
835
836 %{id: max_id} =
837 10
838 |> ActivityBuilder.insert_list()
839 |> List.first()
840
841 activities = ActivityPub.fetch_public_activities(%{max_id: max_id})
842
843 assert length(activities) == 20
844 assert collect_ids(activities) == collect_ids(expected_activities)
845 end
846
847 test "paginates via offset/limit" do
848 _first_part_activities = ActivityBuilder.insert_list(10)
849 second_part_activities = ActivityBuilder.insert_list(10)
850
851 later_activities = ActivityBuilder.insert_list(10)
852
853 activities = ActivityPub.fetch_public_activities(%{page: "2", page_size: "20"}, :offset)
854
855 assert length(activities) == 20
856
857 assert collect_ids(activities) ==
858 collect_ids(second_part_activities) ++ collect_ids(later_activities)
859 end
860
861 test "doesn't return reblogs for users for whom reblogs have been muted" do
862 activity = insert(:note_activity)
863 user = insert(:user)
864 booster = insert(:user)
865 {:ok, _reblog_mute} = CommonAPI.hide_reblogs(user, booster)
866
867 {:ok, activity} = CommonAPI.repeat(activity.id, booster)
868
869 activities = ActivityPub.fetch_activities([], %{muting_user: user})
870
871 refute Enum.any?(activities, fn %{id: id} -> id == activity.id end)
872 end
873
874 test "returns reblogs for users for whom reblogs have not been muted" do
875 activity = insert(:note_activity)
876 user = insert(:user)
877 booster = insert(:user)
878 {:ok, _reblog_mute} = CommonAPI.hide_reblogs(user, booster)
879 {:ok, _reblog_mute} = CommonAPI.show_reblogs(user, booster)
880
881 {:ok, activity} = CommonAPI.repeat(activity.id, booster)
882
883 activities = ActivityPub.fetch_activities([], %{muting_user: user})
884
885 assert Enum.any?(activities, fn %{id: id} -> id == activity.id end)
886 end
887 end
888
889 describe "uploading files" do
890 test "copies the file to the configured folder" do
891 file = %Plug.Upload{
892 content_type: "image/jpg",
893 path: Path.absname("test/fixtures/image.jpg"),
894 filename: "an_image.jpg"
895 }
896
897 {:ok, %Object{} = object} = ActivityPub.upload(file)
898 assert object.data["name"] == "an_image.jpg"
899 end
900
901 test "works with base64 encoded images" do
902 file = %{
903 img: data_uri()
904 }
905
906 {:ok, %Object{}} = ActivityPub.upload(file)
907 end
908 end
909
910 describe "fetch the latest Follow" do
911 test "fetches the latest Follow activity" do
912 %Activity{data: %{"type" => "Follow"}} = activity = insert(:follow_activity)
913 follower = Repo.get_by(User, ap_id: activity.data["actor"])
914 followed = Repo.get_by(User, ap_id: activity.data["object"])
915
916 assert activity == Utils.fetch_latest_follow(follower, followed)
917 end
918 end
919
920 describe "following / unfollowing" do
921 test "it reverts follow activity" do
922 follower = insert(:user)
923 followed = insert(:user)
924
925 with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
926 assert {:error, :reverted} = ActivityPub.follow(follower, followed)
927 end
928
929 assert Repo.aggregate(Activity, :count, :id) == 0
930 assert Repo.aggregate(Object, :count, :id) == 0
931 end
932
933 test "it reverts unfollow activity" do
934 follower = insert(:user)
935 followed = insert(:user)
936
937 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
938
939 with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
940 assert {:error, :reverted} = ActivityPub.unfollow(follower, followed)
941 end
942
943 activity = Activity.get_by_id(follow_activity.id)
944 assert activity.data["type"] == "Follow"
945 assert activity.data["actor"] == follower.ap_id
946
947 assert activity.data["object"] == followed.ap_id
948 end
949
950 test "creates a follow activity" do
951 follower = insert(:user)
952 followed = insert(:user)
953
954 {:ok, activity} = ActivityPub.follow(follower, followed)
955 assert activity.data["type"] == "Follow"
956 assert activity.data["actor"] == follower.ap_id
957 assert activity.data["object"] == followed.ap_id
958 end
959
960 test "creates an undo activity for the last follow" do
961 follower = insert(:user)
962 followed = insert(:user)
963
964 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
965 {:ok, activity} = ActivityPub.unfollow(follower, followed)
966
967 assert activity.data["type"] == "Undo"
968 assert activity.data["actor"] == follower.ap_id
969
970 embedded_object = activity.data["object"]
971 assert is_map(embedded_object)
972 assert embedded_object["type"] == "Follow"
973 assert embedded_object["object"] == followed.ap_id
974 assert embedded_object["id"] == follow_activity.data["id"]
975 end
976
977 test "creates an undo activity for a pending follow request" do
978 follower = insert(:user)
979 followed = insert(:user, %{locked: true})
980
981 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
982 {:ok, activity} = ActivityPub.unfollow(follower, followed)
983
984 assert activity.data["type"] == "Undo"
985 assert activity.data["actor"] == follower.ap_id
986
987 embedded_object = activity.data["object"]
988 assert is_map(embedded_object)
989 assert embedded_object["type"] == "Follow"
990 assert embedded_object["object"] == followed.ap_id
991 assert embedded_object["id"] == follow_activity.data["id"]
992 end
993 end
994
995 describe "blocking" do
996 test "reverts block activity on error" do
997 [blocker, blocked] = insert_list(2, :user)
998
999 with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
1000 assert {:error, :reverted} = ActivityPub.block(blocker, blocked)
1001 end
1002
1003 assert Repo.aggregate(Activity, :count, :id) == 0
1004 assert Repo.aggregate(Object, :count, :id) == 0
1005 end
1006
1007 test "creates a block activity" do
1008 clear_config([:instance, :federating], true)
1009 blocker = insert(:user)
1010 blocked = insert(:user)
1011
1012 with_mock Pleroma.Web.Federator,
1013 publish: fn _ -> nil end do
1014 {:ok, activity} = ActivityPub.block(blocker, blocked)
1015
1016 assert activity.data["type"] == "Block"
1017 assert activity.data["actor"] == blocker.ap_id
1018 assert activity.data["object"] == blocked.ap_id
1019
1020 assert called(Pleroma.Web.Federator.publish(activity))
1021 end
1022 end
1023
1024 test "works with outgoing blocks disabled, but doesn't federate" do
1025 clear_config([:instance, :federating], true)
1026 clear_config([:activitypub, :outgoing_blocks], false)
1027 blocker = insert(:user)
1028 blocked = insert(:user)
1029
1030 with_mock Pleroma.Web.Federator,
1031 publish: fn _ -> nil end do
1032 {:ok, activity} = ActivityPub.block(blocker, blocked)
1033
1034 assert activity.data["type"] == "Block"
1035 assert activity.data["actor"] == blocker.ap_id
1036 assert activity.data["object"] == blocked.ap_id
1037
1038 refute called(Pleroma.Web.Federator.publish(:_))
1039 end
1040 end
1041 end
1042
1043 describe "timeline post-processing" do
1044 test "it filters broken threads" do
1045 user1 = insert(:user)
1046 user2 = insert(:user)
1047 user3 = insert(:user)
1048
1049 {:ok, user1} = User.follow(user1, user3)
1050 assert User.following?(user1, user3)
1051
1052 {:ok, user2} = User.follow(user2, user3)
1053 assert User.following?(user2, user3)
1054
1055 {:ok, user3} = User.follow(user3, user2)
1056 assert User.following?(user3, user2)
1057
1058 {:ok, public_activity} = CommonAPI.post(user3, %{status: "hi 1"})
1059
1060 {:ok, private_activity_1} = CommonAPI.post(user3, %{status: "hi 2", visibility: "private"})
1061
1062 {:ok, private_activity_2} =
1063 CommonAPI.post(user2, %{
1064 status: "hi 3",
1065 visibility: "private",
1066 in_reply_to_status_id: private_activity_1.id
1067 })
1068
1069 {:ok, private_activity_3} =
1070 CommonAPI.post(user3, %{
1071 status: "hi 4",
1072 visibility: "private",
1073 in_reply_to_status_id: private_activity_2.id
1074 })
1075
1076 activities =
1077 ActivityPub.fetch_activities([user1.ap_id | User.following(user1)])
1078 |> Enum.map(fn a -> a.id end)
1079
1080 private_activity_1 = Activity.get_by_ap_id_with_object(private_activity_1.data["id"])
1081
1082 assert [public_activity.id, private_activity_1.id, private_activity_3.id] == activities
1083
1084 assert length(activities) == 3
1085
1086 activities =
1087 ActivityPub.fetch_activities([user1.ap_id | User.following(user1)], %{user: user1})
1088 |> Enum.map(fn a -> a.id end)
1089
1090 assert [public_activity.id, private_activity_1.id] == activities
1091 assert length(activities) == 2
1092 end
1093 end
1094
1095 describe "flag/1" do
1096 setup do
1097 reporter = insert(:user)
1098 target_account = insert(:user)
1099 content = "foobar"
1100 {:ok, activity} = CommonAPI.post(target_account, %{status: content})
1101 context = Utils.generate_context_id()
1102
1103 reporter_ap_id = reporter.ap_id
1104 target_ap_id = target_account.ap_id
1105 activity_ap_id = activity.data["id"]
1106
1107 activity_with_object = Activity.get_by_ap_id_with_object(activity_ap_id)
1108
1109 {:ok,
1110 %{
1111 reporter: reporter,
1112 context: context,
1113 target_account: target_account,
1114 reported_activity: activity,
1115 content: content,
1116 activity_ap_id: activity_ap_id,
1117 activity_with_object: activity_with_object,
1118 reporter_ap_id: reporter_ap_id,
1119 target_ap_id: target_ap_id
1120 }}
1121 end
1122
1123 test "it can create a Flag activity",
1124 %{
1125 reporter: reporter,
1126 context: context,
1127 target_account: target_account,
1128 reported_activity: reported_activity,
1129 content: content,
1130 activity_ap_id: activity_ap_id,
1131 activity_with_object: activity_with_object,
1132 reporter_ap_id: reporter_ap_id,
1133 target_ap_id: target_ap_id
1134 } do
1135 assert {:ok, activity} =
1136 ActivityPub.flag(%{
1137 actor: reporter,
1138 context: context,
1139 account: target_account,
1140 statuses: [reported_activity],
1141 content: content
1142 })
1143
1144 note_obj = %{
1145 "type" => "Note",
1146 "id" => activity_ap_id,
1147 "content" => content,
1148 "published" => activity_with_object.object.data["published"],
1149 "actor" => AccountView.render("show.json", %{user: target_account})
1150 }
1151
1152 assert %Activity{
1153 actor: ^reporter_ap_id,
1154 data: %{
1155 "type" => "Flag",
1156 "content" => ^content,
1157 "context" => ^context,
1158 "object" => [^target_ap_id, ^note_obj]
1159 }
1160 } = activity
1161 end
1162
1163 test_with_mock "strips status data from Flag, before federating it",
1164 %{
1165 reporter: reporter,
1166 context: context,
1167 target_account: target_account,
1168 reported_activity: reported_activity,
1169 content: content
1170 },
1171 Utils,
1172 [:passthrough],
1173 [] do
1174 {:ok, activity} =
1175 ActivityPub.flag(%{
1176 actor: reporter,
1177 context: context,
1178 account: target_account,
1179 statuses: [reported_activity],
1180 content: content
1181 })
1182
1183 new_data =
1184 put_in(activity.data, ["object"], [target_account.ap_id, reported_activity.data["id"]])
1185
1186 assert_called(Utils.maybe_federate(%{activity | data: new_data}))
1187 end
1188 end
1189
1190 test "fetch_activities/2 returns activities addressed to a list " do
1191 user = insert(:user)
1192 member = insert(:user)
1193 {:ok, list} = Pleroma.List.create("foo", user)
1194 {:ok, list} = Pleroma.List.follow(list, member)
1195
1196 {:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})
1197
1198 activity = Repo.preload(activity, :bookmark)
1199 activity = %Activity{activity | thread_muted?: !!activity.thread_muted?}
1200
1201 assert ActivityPub.fetch_activities([], %{user: user}) == [activity]
1202 end
1203
1204 def data_uri do
1205 File.read!("test/fixtures/avatar_data_uri")
1206 end
1207
1208 describe "fetch_activities_bounded" do
1209 test "fetches private posts for followed users" do
1210 user = insert(:user)
1211
1212 {:ok, activity} =
1213 CommonAPI.post(user, %{
1214 status: "thought I looked cute might delete later :3",
1215 visibility: "private"
1216 })
1217
1218 [result] = ActivityPub.fetch_activities_bounded([user.follower_address], [])
1219 assert result.id == activity.id
1220 end
1221
1222 test "fetches only public posts for other users" do
1223 user = insert(:user)
1224 {:ok, activity} = CommonAPI.post(user, %{status: "#cofe", visibility: "public"})
1225
1226 {:ok, _private_activity} =
1227 CommonAPI.post(user, %{
1228 status: "why is tenshi eating a corndog so cute?",
1229 visibility: "private"
1230 })
1231
1232 [result] = ActivityPub.fetch_activities_bounded([], [user.follower_address])
1233 assert result.id == activity.id
1234 end
1235 end
1236
1237 describe "fetch_follow_information_for_user" do
1238 test "syncronizes following/followers counters" do
1239 user =
1240 insert(:user,
1241 local: false,
1242 follower_address: "http://localhost:4001/users/fuser2/followers",
1243 following_address: "http://localhost:4001/users/fuser2/following"
1244 )
1245
1246 {:ok, info} = ActivityPub.fetch_follow_information_for_user(user)
1247 assert info.follower_count == 527
1248 assert info.following_count == 267
1249 end
1250
1251 test "detects hidden followers" do
1252 mock(fn env ->
1253 case env.url do
1254 "http://localhost:4001/users/masto_closed/followers?page=1" ->
1255 %Tesla.Env{status: 403, body: ""}
1256
1257 _ ->
1258 apply(HttpRequestMock, :request, [env])
1259 end
1260 end)
1261
1262 user =
1263 insert(:user,
1264 local: false,
1265 follower_address: "http://localhost:4001/users/masto_closed/followers",
1266 following_address: "http://localhost:4001/users/masto_closed/following"
1267 )
1268
1269 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1270 assert follow_info.hide_followers == true
1271 assert follow_info.hide_follows == false
1272 end
1273
1274 test "detects hidden follows" do
1275 mock(fn env ->
1276 case env.url do
1277 "http://localhost:4001/users/masto_closed/following?page=1" ->
1278 %Tesla.Env{status: 403, body: ""}
1279
1280 _ ->
1281 apply(HttpRequestMock, :request, [env])
1282 end
1283 end)
1284
1285 user =
1286 insert(:user,
1287 local: false,
1288 follower_address: "http://localhost:4001/users/masto_closed/followers",
1289 following_address: "http://localhost:4001/users/masto_closed/following"
1290 )
1291
1292 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1293 assert follow_info.hide_followers == false
1294 assert follow_info.hide_follows == true
1295 end
1296
1297 test "detects hidden follows/followers for friendica" do
1298 user =
1299 insert(:user,
1300 local: false,
1301 follower_address: "http://localhost:8080/followers/fuser3",
1302 following_address: "http://localhost:8080/following/fuser3"
1303 )
1304
1305 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1306 assert follow_info.hide_followers == true
1307 assert follow_info.follower_count == 296
1308 assert follow_info.following_count == 32
1309 assert follow_info.hide_follows == true
1310 end
1311
1312 test "doesn't crash when follower and following counters are hidden" do
1313 mock(fn env ->
1314 case env.url do
1315 "http://localhost:4001/users/masto_hidden_counters/following" ->
1316 json(%{
1317 "@context" => "https://www.w3.org/ns/activitystreams",
1318 "id" => "http://localhost:4001/users/masto_hidden_counters/followers"
1319 })
1320
1321 "http://localhost:4001/users/masto_hidden_counters/following?page=1" ->
1322 %Tesla.Env{status: 403, body: ""}
1323
1324 "http://localhost:4001/users/masto_hidden_counters/followers" ->
1325 json(%{
1326 "@context" => "https://www.w3.org/ns/activitystreams",
1327 "id" => "http://localhost:4001/users/masto_hidden_counters/following"
1328 })
1329
1330 "http://localhost:4001/users/masto_hidden_counters/followers?page=1" ->
1331 %Tesla.Env{status: 403, body: ""}
1332 end
1333 end)
1334
1335 user =
1336 insert(:user,
1337 local: false,
1338 follower_address: "http://localhost:4001/users/masto_hidden_counters/followers",
1339 following_address: "http://localhost:4001/users/masto_hidden_counters/following"
1340 )
1341
1342 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1343
1344 assert follow_info.hide_followers == true
1345 assert follow_info.follower_count == 0
1346 assert follow_info.hide_follows == true
1347 assert follow_info.following_count == 0
1348 end
1349 end
1350
1351 describe "fetch_favourites/3" do
1352 test "returns a favourite activities sorted by adds to favorite" do
1353 user = insert(:user)
1354 other_user = insert(:user)
1355 user1 = insert(:user)
1356 user2 = insert(:user)
1357 {:ok, a1} = CommonAPI.post(user1, %{status: "bla"})
1358 {:ok, _a2} = CommonAPI.post(user2, %{status: "traps are happy"})
1359 {:ok, a3} = CommonAPI.post(user2, %{status: "Trees Are "})
1360 {:ok, a4} = CommonAPI.post(user2, %{status: "Agent Smith "})
1361 {:ok, a5} = CommonAPI.post(user1, %{status: "Red or Blue "})
1362
1363 {:ok, _} = CommonAPI.favorite(user, a4.id)
1364 {:ok, _} = CommonAPI.favorite(other_user, a3.id)
1365 {:ok, _} = CommonAPI.favorite(user, a3.id)
1366 {:ok, _} = CommonAPI.favorite(other_user, a5.id)
1367 {:ok, _} = CommonAPI.favorite(user, a5.id)
1368 {:ok, _} = CommonAPI.favorite(other_user, a4.id)
1369 {:ok, _} = CommonAPI.favorite(user, a1.id)
1370 {:ok, _} = CommonAPI.favorite(other_user, a1.id)
1371 result = ActivityPub.fetch_favourites(user)
1372
1373 assert Enum.map(result, & &1.id) == [a1.id, a5.id, a3.id, a4.id]
1374
1375 result = ActivityPub.fetch_favourites(user, %{limit: 2})
1376 assert Enum.map(result, & &1.id) == [a1.id, a5.id]
1377 end
1378 end
1379
1380 describe "Move activity" do
1381 test "create" do
1382 %{ap_id: old_ap_id} = old_user = insert(:user)
1383 %{ap_id: new_ap_id} = new_user = insert(:user, also_known_as: [old_ap_id])
1384 follower = insert(:user)
1385 follower_move_opted_out = insert(:user, allow_following_move: false)
1386
1387 User.follow(follower, old_user)
1388 User.follow(follower_move_opted_out, old_user)
1389
1390 assert User.following?(follower, old_user)
1391 assert User.following?(follower_move_opted_out, old_user)
1392
1393 assert {:ok, activity} = ActivityPub.move(old_user, new_user)
1394
1395 assert %Activity{
1396 actor: ^old_ap_id,
1397 data: %{
1398 "actor" => ^old_ap_id,
1399 "object" => ^old_ap_id,
1400 "target" => ^new_ap_id,
1401 "type" => "Move"
1402 },
1403 local: true
1404 } = activity
1405
1406 params = %{
1407 "op" => "move_following",
1408 "origin_id" => old_user.id,
1409 "target_id" => new_user.id
1410 }
1411
1412 assert_enqueued(worker: Pleroma.Workers.BackgroundWorker, args: params)
1413
1414 Pleroma.Workers.BackgroundWorker.perform(params, nil)
1415
1416 refute User.following?(follower, old_user)
1417 assert User.following?(follower, new_user)
1418
1419 assert User.following?(follower_move_opted_out, old_user)
1420 refute User.following?(follower_move_opted_out, new_user)
1421
1422 activity = %Activity{activity | object: nil}
1423
1424 assert [%Notification{activity: ^activity}] = Notification.for_user(follower)
1425
1426 assert [%Notification{activity: ^activity}] = Notification.for_user(follower_move_opted_out)
1427 end
1428
1429 test "old user must be in the new user's `also_known_as` list" do
1430 old_user = insert(:user)
1431 new_user = insert(:user)
1432
1433 assert {:error, "Target account must have the origin in `alsoKnownAs`"} =
1434 ActivityPub.move(old_user, new_user)
1435 end
1436 end
1437
1438 test "doesn't retrieve replies activities with exclude_replies" do
1439 user = insert(:user)
1440
1441 {:ok, activity} = CommonAPI.post(user, %{status: "yeah"})
1442
1443 {:ok, _reply} = CommonAPI.post(user, %{status: "yeah", in_reply_to_status_id: activity.id})
1444
1445 [result] = ActivityPub.fetch_public_activities(%{exclude_replies: true})
1446
1447 assert result.id == activity.id
1448
1449 assert length(ActivityPub.fetch_public_activities()) == 2
1450 end
1451
1452 describe "replies filtering with public messages" do
1453 setup :public_messages
1454
1455 test "public timeline", %{users: %{u1: user}} do
1456 activities_ids =
1457 %{}
1458 |> Map.put(:type, ["Create", "Announce"])
1459 |> Map.put(:local_only, false)
1460 |> Map.put(:blocking_user, user)
1461 |> Map.put(:muting_user, user)
1462 |> Map.put(:reply_filtering_user, user)
1463 |> ActivityPub.fetch_public_activities()
1464 |> Enum.map(& &1.id)
1465
1466 assert length(activities_ids) == 16
1467 end
1468
1469 test "public timeline with reply_visibility `following`", %{
1470 users: %{u1: user},
1471 u1: u1,
1472 u2: u2,
1473 u3: u3,
1474 u4: u4,
1475 activities: activities
1476 } do
1477 activities_ids =
1478 %{}
1479 |> Map.put(:type, ["Create", "Announce"])
1480 |> Map.put(:local_only, false)
1481 |> Map.put(:blocking_user, user)
1482 |> Map.put(:muting_user, user)
1483 |> Map.put(:reply_visibility, "following")
1484 |> Map.put(:reply_filtering_user, user)
1485 |> ActivityPub.fetch_public_activities()
1486 |> Enum.map(& &1.id)
1487
1488 assert length(activities_ids) == 14
1489
1490 visible_ids =
1491 Map.values(u1) ++ Map.values(u2) ++ Map.values(u4) ++ Map.values(activities) ++ [u3[:r1]]
1492
1493 assert Enum.all?(visible_ids, &(&1 in activities_ids))
1494 end
1495
1496 test "public timeline with reply_visibility `self`", %{
1497 users: %{u1: user},
1498 u1: u1,
1499 u2: u2,
1500 u3: u3,
1501 u4: u4,
1502 activities: activities
1503 } do
1504 activities_ids =
1505 %{}
1506 |> Map.put(:type, ["Create", "Announce"])
1507 |> Map.put(:local_only, false)
1508 |> Map.put(:blocking_user, user)
1509 |> Map.put(:muting_user, user)
1510 |> Map.put(:reply_visibility, "self")
1511 |> Map.put(:reply_filtering_user, user)
1512 |> ActivityPub.fetch_public_activities()
1513 |> Enum.map(& &1.id)
1514
1515 assert length(activities_ids) == 10
1516 visible_ids = Map.values(u1) ++ [u2[:r1], u3[:r1], u4[:r1]] ++ Map.values(activities)
1517 assert Enum.all?(visible_ids, &(&1 in activities_ids))
1518 end
1519
1520 test "home timeline", %{
1521 users: %{u1: user},
1522 activities: activities,
1523 u1: u1,
1524 u2: u2,
1525 u3: u3,
1526 u4: u4
1527 } do
1528 params =
1529 %{}
1530 |> Map.put(:type, ["Create", "Announce"])
1531 |> Map.put(:blocking_user, user)
1532 |> Map.put(:muting_user, user)
1533 |> Map.put(:user, user)
1534 |> Map.put(:reply_filtering_user, user)
1535
1536 activities_ids =
1537 ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
1538 |> Enum.map(& &1.id)
1539
1540 assert length(activities_ids) == 13
1541
1542 visible_ids =
1543 Map.values(u1) ++
1544 Map.values(u3) ++
1545 [
1546 activities[:a1],
1547 activities[:a2],
1548 activities[:a4],
1549 u2[:r1],
1550 u2[:r3],
1551 u4[:r1],
1552 u4[:r2]
1553 ]
1554
1555 assert Enum.all?(visible_ids, &(&1 in activities_ids))
1556 end
1557
1558 test "home timeline with reply_visibility `following`", %{
1559 users: %{u1: user},
1560 activities: activities,
1561 u1: u1,
1562 u2: u2,
1563 u3: u3,
1564 u4: u4
1565 } do
1566 params =
1567 %{}
1568 |> Map.put(:type, ["Create", "Announce"])
1569 |> Map.put(:blocking_user, user)
1570 |> Map.put(:muting_user, user)
1571 |> Map.put(:user, user)
1572 |> Map.put(:reply_visibility, "following")
1573 |> Map.put(:reply_filtering_user, user)
1574
1575 activities_ids =
1576 ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
1577 |> Enum.map(& &1.id)
1578
1579 assert length(activities_ids) == 11
1580
1581 visible_ids =
1582 Map.values(u1) ++
1583 [
1584 activities[:a1],
1585 activities[:a2],
1586 activities[:a4],
1587 u2[:r1],
1588 u2[:r3],
1589 u3[:r1],
1590 u4[:r1],
1591 u4[:r2]
1592 ]
1593
1594 assert Enum.all?(visible_ids, &(&1 in activities_ids))
1595 end
1596
1597 test "home timeline with reply_visibility `self`", %{
1598 users: %{u1: user},
1599 activities: activities,
1600 u1: u1,
1601 u2: u2,
1602 u3: u3,
1603 u4: u4
1604 } do
1605 params =
1606 %{}
1607 |> Map.put(:type, ["Create", "Announce"])
1608 |> Map.put(:blocking_user, user)
1609 |> Map.put(:muting_user, user)
1610 |> Map.put(:user, user)
1611 |> Map.put(:reply_visibility, "self")
1612 |> Map.put(:reply_filtering_user, user)
1613
1614 activities_ids =
1615 ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
1616 |> Enum.map(& &1.id)
1617
1618 assert length(activities_ids) == 9
1619
1620 visible_ids =
1621 Map.values(u1) ++
1622 [
1623 activities[:a1],
1624 activities[:a2],
1625 activities[:a4],
1626 u2[:r1],
1627 u3[:r1],
1628 u4[:r1]
1629 ]
1630
1631 assert Enum.all?(visible_ids, &(&1 in activities_ids))
1632 end
1633
1634 test "filtering out announces where the user is the actor of the announced message" do
1635 user = insert(:user)
1636 other_user = insert(:user)
1637 third_user = insert(:user)
1638 User.follow(user, other_user)
1639
1640 {:ok, post} = CommonAPI.post(user, %{status: "yo"})
1641 {:ok, other_post} = CommonAPI.post(third_user, %{status: "yo"})
1642 {:ok, _announce} = CommonAPI.repeat(post.id, other_user)
1643 {:ok, _announce} = CommonAPI.repeat(post.id, third_user)
1644 {:ok, announce} = CommonAPI.repeat(other_post.id, other_user)
1645
1646 params = %{
1647 type: ["Announce"]
1648 }
1649
1650 results =
1651 [user.ap_id | User.following(user)]
1652 |> ActivityPub.fetch_activities(params)
1653
1654 assert length(results) == 3
1655
1656 params = %{
1657 type: ["Announce"],
1658 announce_filtering_user: user
1659 }
1660
1661 [result] =
1662 [user.ap_id | User.following(user)]
1663 |> ActivityPub.fetch_activities(params)
1664
1665 assert result.id == announce.id
1666 end
1667 end
1668
1669 describe "replies filtering with private messages" do
1670 setup :private_messages
1671
1672 test "public timeline", %{users: %{u1: user}} do
1673 activities_ids =
1674 %{}
1675 |> Map.put(:type, ["Create", "Announce"])
1676 |> Map.put(:local_only, false)
1677 |> Map.put(:blocking_user, user)
1678 |> Map.put(:muting_user, user)
1679 |> Map.put(:user, user)
1680 |> ActivityPub.fetch_public_activities()
1681 |> Enum.map(& &1.id)
1682
1683 assert activities_ids == []
1684 end
1685
1686 test "public timeline with default reply_visibility `following`", %{users: %{u1: user}} do
1687 activities_ids =
1688 %{}
1689 |> Map.put(:type, ["Create", "Announce"])
1690 |> Map.put(:local_only, false)
1691 |> Map.put(:blocking_user, user)
1692 |> Map.put(:muting_user, user)
1693 |> Map.put(:reply_visibility, "following")
1694 |> Map.put(:reply_filtering_user, user)
1695 |> Map.put(:user, user)
1696 |> ActivityPub.fetch_public_activities()
1697 |> Enum.map(& &1.id)
1698
1699 assert activities_ids == []
1700 end
1701
1702 test "public timeline with default reply_visibility `self`", %{users: %{u1: user}} do
1703 activities_ids =
1704 %{}
1705 |> Map.put(:type, ["Create", "Announce"])
1706 |> Map.put(:local_only, false)
1707 |> Map.put(:blocking_user, user)
1708 |> Map.put(:muting_user, user)
1709 |> Map.put(:reply_visibility, "self")
1710 |> Map.put(:reply_filtering_user, user)
1711 |> Map.put(:user, user)
1712 |> ActivityPub.fetch_public_activities()
1713 |> Enum.map(& &1.id)
1714
1715 assert activities_ids == []
1716 end
1717
1718 test "home timeline", %{users: %{u1: user}} do
1719 params =
1720 %{}
1721 |> Map.put(:type, ["Create", "Announce"])
1722 |> Map.put(:blocking_user, user)
1723 |> Map.put(:muting_user, user)
1724 |> Map.put(:user, user)
1725
1726 activities_ids =
1727 ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
1728 |> Enum.map(& &1.id)
1729
1730 assert length(activities_ids) == 12
1731 end
1732
1733 test "home timeline with default reply_visibility `following`", %{users: %{u1: user}} do
1734 params =
1735 %{}
1736 |> Map.put(:type, ["Create", "Announce"])
1737 |> Map.put(:blocking_user, user)
1738 |> Map.put(:muting_user, user)
1739 |> Map.put(:user, user)
1740 |> Map.put(:reply_visibility, "following")
1741 |> Map.put(:reply_filtering_user, user)
1742
1743 activities_ids =
1744 ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
1745 |> Enum.map(& &1.id)
1746
1747 assert length(activities_ids) == 12
1748 end
1749
1750 test "home timeline with default reply_visibility `self`", %{
1751 users: %{u1: user},
1752 activities: activities,
1753 u1: u1,
1754 u2: u2,
1755 u3: u3,
1756 u4: u4
1757 } do
1758 params =
1759 %{}
1760 |> Map.put(:type, ["Create", "Announce"])
1761 |> Map.put(:blocking_user, user)
1762 |> Map.put(:muting_user, user)
1763 |> Map.put(:user, user)
1764 |> Map.put(:reply_visibility, "self")
1765 |> Map.put(:reply_filtering_user, user)
1766
1767 activities_ids =
1768 ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
1769 |> Enum.map(& &1.id)
1770
1771 assert length(activities_ids) == 10
1772
1773 visible_ids =
1774 Map.values(u1) ++ Map.values(u4) ++ [u2[:r1], u3[:r1]] ++ Map.values(activities)
1775
1776 assert Enum.all?(visible_ids, &(&1 in activities_ids))
1777 end
1778 end
1779
1780 defp public_messages(_) do
1781 [u1, u2, u3, u4] = insert_list(4, :user)
1782 {:ok, u1} = User.follow(u1, u2)
1783 {:ok, u2} = User.follow(u2, u1)
1784 {:ok, u1} = User.follow(u1, u4)
1785 {:ok, u4} = User.follow(u4, u1)
1786
1787 {:ok, u2} = User.follow(u2, u3)
1788 {:ok, u3} = User.follow(u3, u2)
1789
1790 {:ok, a1} = CommonAPI.post(u1, %{status: "Status"})
1791
1792 {:ok, r1_1} =
1793 CommonAPI.post(u2, %{
1794 status: "@#{u1.nickname} reply from u2 to u1",
1795 in_reply_to_status_id: a1.id
1796 })
1797
1798 {:ok, r1_2} =
1799 CommonAPI.post(u3, %{
1800 status: "@#{u1.nickname} reply from u3 to u1",
1801 in_reply_to_status_id: a1.id
1802 })
1803
1804 {:ok, r1_3} =
1805 CommonAPI.post(u4, %{
1806 status: "@#{u1.nickname} reply from u4 to u1",
1807 in_reply_to_status_id: a1.id
1808 })
1809
1810 {:ok, a2} = CommonAPI.post(u2, %{status: "Status"})
1811
1812 {:ok, r2_1} =
1813 CommonAPI.post(u1, %{
1814 status: "@#{u2.nickname} reply from u1 to u2",
1815 in_reply_to_status_id: a2.id
1816 })
1817
1818 {:ok, r2_2} =
1819 CommonAPI.post(u3, %{
1820 status: "@#{u2.nickname} reply from u3 to u2",
1821 in_reply_to_status_id: a2.id
1822 })
1823
1824 {:ok, r2_3} =
1825 CommonAPI.post(u4, %{
1826 status: "@#{u2.nickname} reply from u4 to u2",
1827 in_reply_to_status_id: a2.id
1828 })
1829
1830 {:ok, a3} = CommonAPI.post(u3, %{status: "Status"})
1831
1832 {:ok, r3_1} =
1833 CommonAPI.post(u1, %{
1834 status: "@#{u3.nickname} reply from u1 to u3",
1835 in_reply_to_status_id: a3.id
1836 })
1837
1838 {:ok, r3_2} =
1839 CommonAPI.post(u2, %{
1840 status: "@#{u3.nickname} reply from u2 to u3",
1841 in_reply_to_status_id: a3.id
1842 })
1843
1844 {:ok, r3_3} =
1845 CommonAPI.post(u4, %{
1846 status: "@#{u3.nickname} reply from u4 to u3",
1847 in_reply_to_status_id: a3.id
1848 })
1849
1850 {:ok, a4} = CommonAPI.post(u4, %{status: "Status"})
1851
1852 {:ok, r4_1} =
1853 CommonAPI.post(u1, %{
1854 status: "@#{u4.nickname} reply from u1 to u4",
1855 in_reply_to_status_id: a4.id
1856 })
1857
1858 {:ok, r4_2} =
1859 CommonAPI.post(u2, %{
1860 status: "@#{u4.nickname} reply from u2 to u4",
1861 in_reply_to_status_id: a4.id
1862 })
1863
1864 {:ok, r4_3} =
1865 CommonAPI.post(u3, %{
1866 status: "@#{u4.nickname} reply from u3 to u4",
1867 in_reply_to_status_id: a4.id
1868 })
1869
1870 {:ok,
1871 users: %{u1: u1, u2: u2, u3: u3, u4: u4},
1872 activities: %{a1: a1.id, a2: a2.id, a3: a3.id, a4: a4.id},
1873 u1: %{r1: r1_1.id, r2: r1_2.id, r3: r1_3.id},
1874 u2: %{r1: r2_1.id, r2: r2_2.id, r3: r2_3.id},
1875 u3: %{r1: r3_1.id, r2: r3_2.id, r3: r3_3.id},
1876 u4: %{r1: r4_1.id, r2: r4_2.id, r3: r4_3.id}}
1877 end
1878
1879 defp private_messages(_) do
1880 [u1, u2, u3, u4] = insert_list(4, :user)
1881 {:ok, u1} = User.follow(u1, u2)
1882 {:ok, u2} = User.follow(u2, u1)
1883 {:ok, u1} = User.follow(u1, u3)
1884 {:ok, u3} = User.follow(u3, u1)
1885 {:ok, u1} = User.follow(u1, u4)
1886 {:ok, u4} = User.follow(u4, u1)
1887
1888 {:ok, u2} = User.follow(u2, u3)
1889 {:ok, u3} = User.follow(u3, u2)
1890
1891 {:ok, a1} = CommonAPI.post(u1, %{status: "Status", visibility: "private"})
1892
1893 {:ok, r1_1} =
1894 CommonAPI.post(u2, %{
1895 status: "@#{u1.nickname} reply from u2 to u1",
1896 in_reply_to_status_id: a1.id,
1897 visibility: "private"
1898 })
1899
1900 {:ok, r1_2} =
1901 CommonAPI.post(u3, %{
1902 status: "@#{u1.nickname} reply from u3 to u1",
1903 in_reply_to_status_id: a1.id,
1904 visibility: "private"
1905 })
1906
1907 {:ok, r1_3} =
1908 CommonAPI.post(u4, %{
1909 status: "@#{u1.nickname} reply from u4 to u1",
1910 in_reply_to_status_id: a1.id,
1911 visibility: "private"
1912 })
1913
1914 {:ok, a2} = CommonAPI.post(u2, %{status: "Status", visibility: "private"})
1915
1916 {:ok, r2_1} =
1917 CommonAPI.post(u1, %{
1918 status: "@#{u2.nickname} reply from u1 to u2",
1919 in_reply_to_status_id: a2.id,
1920 visibility: "private"
1921 })
1922
1923 {:ok, r2_2} =
1924 CommonAPI.post(u3, %{
1925 status: "@#{u2.nickname} reply from u3 to u2",
1926 in_reply_to_status_id: a2.id,
1927 visibility: "private"
1928 })
1929
1930 {:ok, a3} = CommonAPI.post(u3, %{status: "Status", visibility: "private"})
1931
1932 {:ok, r3_1} =
1933 CommonAPI.post(u1, %{
1934 status: "@#{u3.nickname} reply from u1 to u3",
1935 in_reply_to_status_id: a3.id,
1936 visibility: "private"
1937 })
1938
1939 {:ok, r3_2} =
1940 CommonAPI.post(u2, %{
1941 status: "@#{u3.nickname} reply from u2 to u3",
1942 in_reply_to_status_id: a3.id,
1943 visibility: "private"
1944 })
1945
1946 {:ok, a4} = CommonAPI.post(u4, %{status: "Status", visibility: "private"})
1947
1948 {:ok, r4_1} =
1949 CommonAPI.post(u1, %{
1950 status: "@#{u4.nickname} reply from u1 to u4",
1951 in_reply_to_status_id: a4.id,
1952 visibility: "private"
1953 })
1954
1955 {:ok,
1956 users: %{u1: u1, u2: u2, u3: u3, u4: u4},
1957 activities: %{a1: a1.id, a2: a2.id, a3: a3.id, a4: a4.id},
1958 u1: %{r1: r1_1.id, r2: r1_2.id, r3: r1_3.id},
1959 u2: %{r1: r2_1.id, r2: r2_2.id},
1960 u3: %{r1: r3_1.id, r2: r3_2.id},
1961 u4: %{r1: r4_1.id}}
1962 end
1963
1964 describe "maybe_update_follow_information/1" do
1965 setup do
1966 clear_config([:instance, :external_user_synchronization], true)
1967
1968 user = %{
1969 local: false,
1970 ap_id: "https://gensokyo.2hu/users/raymoo",
1971 following_address: "https://gensokyo.2hu/users/following",
1972 follower_address: "https://gensokyo.2hu/users/followers",
1973 type: "Person"
1974 }
1975
1976 %{user: user}
1977 end
1978
1979 test "logs an error when it can't fetch the info", %{user: user} do
1980 assert capture_log(fn ->
1981 ActivityPub.maybe_update_follow_information(user)
1982 end) =~ "Follower/Following counter update for #{user.ap_id} failed"
1983 end
1984
1985 test "just returns the input if the user type is Application", %{
1986 user: user
1987 } do
1988 user =
1989 user
1990 |> Map.put(:type, "Application")
1991
1992 refute capture_log(fn ->
1993 assert ^user = ActivityPub.maybe_update_follow_information(user)
1994 end) =~ "Follower/Following counter update for #{user.ap_id} failed"
1995 end
1996
1997 test "it just returns the input if the user has no following/follower addresses", %{
1998 user: user
1999 } do
2000 user =
2001 user
2002 |> Map.put(:following_address, nil)
2003 |> Map.put(:follower_address, nil)
2004
2005 refute capture_log(fn ->
2006 assert ^user = ActivityPub.maybe_update_follow_information(user)
2007 end) =~ "Follower/Following counter update for #{user.ap_id} failed"
2008 end
2009 end
2010
2011 describe "global activity expiration" do
2012 setup do: clear_config([:instance, :rewrite_policy])
2013
2014 test "creates an activity expiration for local Create activities" do
2015 Pleroma.Config.put(
2016 [:instance, :rewrite_policy],
2017 Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy
2018 )
2019
2020 {:ok, %{id: id_create}} = ActivityBuilder.insert(%{"type" => "Create", "context" => "3hu"})
2021 {:ok, _follow} = ActivityBuilder.insert(%{"type" => "Follow", "context" => "3hu"})
2022
2023 assert [%{activity_id: ^id_create}] = Pleroma.ActivityExpiration |> Repo.all()
2024 end
2025 end
2026 end