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