Merge branch 'develop' into refactor/following-relationships
[akkoma] / test / web / activity_pub / activity_pub_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 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 alias Pleroma.Activity
8 alias Pleroma.Builders.ActivityBuilder
9 alias Pleroma.Object
10 alias Pleroma.User
11 alias Pleroma.Web.ActivityPub.ActivityPub
12 alias Pleroma.Web.ActivityPub.Utils
13 alias Pleroma.Web.CommonAPI
14
15 import Pleroma.Factory
16 import Tesla.Mock
17 import Mock
18
19 setup do
20 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
21 :ok
22 end
23
24 clear_config([:instance, :federating])
25
26 describe "streaming out participations" do
27 test "it streams them out" do
28 user = insert(:user)
29 {:ok, activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"})
30
31 {:ok, conversation} = Pleroma.Conversation.create_or_bump_for(activity)
32
33 participations =
34 conversation.participations
35 |> Repo.preload(:user)
36
37 with_mock Pleroma.Web.Streamer,
38 stream: fn _, _ -> nil end do
39 ActivityPub.stream_out_participations(conversation.participations)
40
41 assert called(Pleroma.Web.Streamer.stream("participation", participations))
42 end
43 end
44
45 test "streams them out on activity creation" do
46 user_one = insert(:user)
47 user_two = insert(:user)
48
49 with_mock Pleroma.Web.Streamer,
50 stream: fn _, _ -> nil end do
51 {:ok, activity} =
52 CommonAPI.post(user_one, %{
53 "status" => "@#{user_two.nickname}",
54 "visibility" => "direct"
55 })
56
57 conversation =
58 activity.data["context"]
59 |> Pleroma.Conversation.get_for_ap_id()
60 |> Repo.preload(participations: :user)
61
62 assert called(Pleroma.Web.Streamer.stream("participation", conversation.participations))
63 end
64 end
65 end
66
67 describe "fetching restricted by visibility" do
68 test "it restricts by the appropriate visibility" do
69 user = insert(:user)
70
71 {:ok, public_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"})
72
73 {:ok, direct_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"})
74
75 {:ok, unlisted_activity} =
76 CommonAPI.post(user, %{"status" => ".", "visibility" => "unlisted"})
77
78 {:ok, private_activity} =
79 CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
80
81 activities =
82 ActivityPub.fetch_activities([], %{:visibility => "direct", "actor_id" => user.ap_id})
83
84 assert activities == [direct_activity]
85
86 activities =
87 ActivityPub.fetch_activities([], %{:visibility => "unlisted", "actor_id" => user.ap_id})
88
89 assert activities == [unlisted_activity]
90
91 activities =
92 ActivityPub.fetch_activities([], %{:visibility => "private", "actor_id" => user.ap_id})
93
94 assert activities == [private_activity]
95
96 activities =
97 ActivityPub.fetch_activities([], %{:visibility => "public", "actor_id" => user.ap_id})
98
99 assert activities == [public_activity]
100
101 activities =
102 ActivityPub.fetch_activities([], %{
103 :visibility => ~w[private public],
104 "actor_id" => user.ap_id
105 })
106
107 assert activities == [public_activity, private_activity]
108 end
109 end
110
111 describe "fetching excluded by visibility" do
112 test "it excludes by the appropriate visibility" do
113 user = insert(:user)
114
115 {:ok, public_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"})
116
117 {:ok, direct_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"})
118
119 {:ok, unlisted_activity} =
120 CommonAPI.post(user, %{"status" => ".", "visibility" => "unlisted"})
121
122 {:ok, private_activity} =
123 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.info.source_data
178 assert user.info.ap_enabled
179 assert user.follower_address == "http://mastodon.example.org/users/admin/followers"
180 end
181
182 test "it fetches the appropriate tag-restricted posts" do
183 user = insert(:user)
184
185 {:ok, status_one} = CommonAPI.post(user, %{"status" => ". #test"})
186 {:ok, status_two} = CommonAPI.post(user, %{"status" => ". #essais"})
187 {:ok, status_three} = CommonAPI.post(user, %{"status" => ". #test #reject"})
188
189 fetch_one = ActivityPub.fetch_activities([], %{"type" => "Create", "tag" => "test"})
190
191 fetch_two =
192 ActivityPub.fetch_activities([], %{"type" => "Create", "tag" => ["test", "essais"]})
193
194 fetch_three =
195 ActivityPub.fetch_activities([], %{
196 "type" => "Create",
197 "tag" => ["test", "essais"],
198 "tag_reject" => ["reject"]
199 })
200
201 fetch_four =
202 ActivityPub.fetch_activities([], %{
203 "type" => "Create",
204 "tag" => ["test"],
205 "tag_all" => ["test", "reject"]
206 })
207
208 assert fetch_one == [status_one, status_three]
209 assert fetch_two == [status_one, status_two, status_three]
210 assert fetch_three == [status_one, status_two]
211 assert fetch_four == [status_three]
212 end
213 end
214
215 describe "insertion" do
216 test "drops activities beyond a certain limit" do
217 limit = Pleroma.Config.get([:instance, :remote_limit])
218
219 random_text =
220 :crypto.strong_rand_bytes(limit + 1)
221 |> Base.encode64()
222 |> binary_part(0, limit + 1)
223
224 data = %{
225 "ok" => true,
226 "object" => %{
227 "content" => random_text
228 }
229 }
230
231 assert {:error, {:remote_limit_error, _}} = ActivityPub.insert(data)
232 end
233
234 test "doesn't drop activities with content being null" do
235 user = insert(:user)
236
237 data = %{
238 "actor" => user.ap_id,
239 "to" => [],
240 "object" => %{
241 "actor" => user.ap_id,
242 "to" => [],
243 "type" => "Note",
244 "content" => nil
245 }
246 }
247
248 assert {:ok, _} = ActivityPub.insert(data)
249 end
250
251 test "returns the activity if one with the same id is already in" do
252 activity = insert(:note_activity)
253 {:ok, new_activity} = ActivityPub.insert(activity.data)
254
255 assert activity.id == new_activity.id
256 end
257
258 test "inserts a given map into the activity database, giving it an id if it has none." do
259 user = insert(:user)
260
261 data = %{
262 "actor" => user.ap_id,
263 "to" => [],
264 "object" => %{
265 "actor" => user.ap_id,
266 "to" => [],
267 "type" => "Note",
268 "content" => "hey"
269 }
270 }
271
272 {:ok, %Activity{} = activity} = ActivityPub.insert(data)
273 assert activity.data["ok"] == data["ok"]
274 assert is_binary(activity.data["id"])
275
276 given_id = "bla"
277
278 data = %{
279 "id" => given_id,
280 "actor" => user.ap_id,
281 "to" => [],
282 "context" => "blabla",
283 "object" => %{
284 "actor" => user.ap_id,
285 "to" => [],
286 "type" => "Note",
287 "content" => "hey"
288 }
289 }
290
291 {:ok, %Activity{} = activity} = ActivityPub.insert(data)
292 assert activity.data["ok"] == data["ok"]
293 assert activity.data["id"] == given_id
294 assert activity.data["context"] == "blabla"
295 assert activity.data["context_id"]
296 end
297
298 test "adds a context when none is there" do
299 user = insert(:user)
300
301 data = %{
302 "actor" => user.ap_id,
303 "to" => [],
304 "object" => %{
305 "actor" => user.ap_id,
306 "to" => [],
307 "type" => "Note",
308 "content" => "hey"
309 }
310 }
311
312 {:ok, %Activity{} = activity} = ActivityPub.insert(data)
313 object = Pleroma.Object.normalize(activity)
314
315 assert is_binary(activity.data["context"])
316 assert is_binary(object.data["context"])
317 assert activity.data["context_id"]
318 assert object.data["context_id"]
319 end
320
321 test "adds an id to a given object if it lacks one and is a note and inserts it to the object database" do
322 user = insert(:user)
323
324 data = %{
325 "actor" => user.ap_id,
326 "to" => [],
327 "object" => %{
328 "actor" => user.ap_id,
329 "to" => [],
330 "type" => "Note",
331 "content" => "hey"
332 }
333 }
334
335 {:ok, %Activity{} = activity} = ActivityPub.insert(data)
336 assert object = Object.normalize(activity)
337 assert is_binary(object.data["id"])
338 end
339 end
340
341 describe "listen activities" do
342 test "does not increase user note count" do
343 user = insert(:user)
344
345 {:ok, activity} =
346 ActivityPub.listen(%{
347 to: ["https://www.w3.org/ns/activitystreams#Public"],
348 actor: user,
349 context: "",
350 object: %{
351 "actor" => user.ap_id,
352 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
353 "artist" => "lain",
354 "title" => "lain radio episode 1",
355 "length" => 180_000,
356 "type" => "Audio"
357 }
358 })
359
360 assert activity.actor == user.ap_id
361
362 user = User.get_cached_by_id(user.id)
363 assert user.info.note_count == 0
364 end
365
366 test "can be fetched into a timeline" do
367 _listen_activity_1 = insert(:listen)
368 _listen_activity_2 = insert(:listen)
369 _listen_activity_3 = insert(:listen)
370
371 timeline = ActivityPub.fetch_activities([], %{"type" => ["Listen"]})
372
373 assert length(timeline) == 3
374 end
375 end
376
377 describe "create activities" do
378 test "removes doubled 'to' recipients" do
379 user = insert(:user)
380
381 {:ok, activity} =
382 ActivityPub.create(%{
383 to: ["user1", "user1", "user2"],
384 actor: user,
385 context: "",
386 object: %{
387 "to" => ["user1", "user1", "user2"],
388 "type" => "Note",
389 "content" => "testing"
390 }
391 })
392
393 assert activity.data["to"] == ["user1", "user2"]
394 assert activity.actor == user.ap_id
395 assert activity.recipients == ["user1", "user2", user.ap_id]
396 end
397
398 test "increases user note count only for public activities" do
399 user = insert(:user)
400
401 {:ok, _} =
402 CommonAPI.post(User.get_cached_by_id(user.id), %{
403 "status" => "1",
404 "visibility" => "public"
405 })
406
407 {:ok, _} =
408 CommonAPI.post(User.get_cached_by_id(user.id), %{
409 "status" => "2",
410 "visibility" => "unlisted"
411 })
412
413 {:ok, _} =
414 CommonAPI.post(User.get_cached_by_id(user.id), %{
415 "status" => "2",
416 "visibility" => "private"
417 })
418
419 {:ok, _} =
420 CommonAPI.post(User.get_cached_by_id(user.id), %{
421 "status" => "3",
422 "visibility" => "direct"
423 })
424
425 user = User.get_cached_by_id(user.id)
426 assert user.info.note_count == 2
427 end
428
429 test "increases replies count" do
430 user = insert(:user)
431 user2 = insert(:user)
432
433 {:ok, activity} = CommonAPI.post(user, %{"status" => "1", "visibility" => "public"})
434 ap_id = activity.data["id"]
435 reply_data = %{"status" => "1", "in_reply_to_status_id" => activity.id}
436
437 # public
438 {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "public"))
439 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
440 assert object.data["repliesCount"] == 1
441
442 # unlisted
443 {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "unlisted"))
444 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
445 assert object.data["repliesCount"] == 2
446
447 # private
448 {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "private"))
449 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
450 assert object.data["repliesCount"] == 2
451
452 # direct
453 {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "direct"))
454 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
455 assert object.data["repliesCount"] == 2
456 end
457 end
458
459 describe "fetch activities for recipients" do
460 test "retrieve the activities for certain recipients" do
461 {:ok, activity_one} = ActivityBuilder.insert(%{"to" => ["someone"]})
462 {:ok, activity_two} = ActivityBuilder.insert(%{"to" => ["someone_else"]})
463 {:ok, _activity_three} = ActivityBuilder.insert(%{"to" => ["noone"]})
464
465 activities = ActivityPub.fetch_activities(["someone", "someone_else"])
466 assert length(activities) == 2
467 assert activities == [activity_one, activity_two]
468 end
469 end
470
471 describe "fetch activities in context" do
472 test "retrieves activities that have a given context" do
473 {:ok, activity} = ActivityBuilder.insert(%{"type" => "Create", "context" => "2hu"})
474 {:ok, activity_two} = ActivityBuilder.insert(%{"type" => "Create", "context" => "2hu"})
475 {:ok, _activity_three} = ActivityBuilder.insert(%{"type" => "Create", "context" => "3hu"})
476 {:ok, _activity_four} = ActivityBuilder.insert(%{"type" => "Announce", "context" => "2hu"})
477 activity_five = insert(:note_activity)
478 user = insert(:user)
479
480 {:ok, user} = User.block(user, %{ap_id: activity_five.data["actor"]})
481
482 activities = ActivityPub.fetch_activities_for_context("2hu", %{"blocking_user" => user})
483 assert activities == [activity_two, activity]
484 end
485 end
486
487 test "doesn't return blocked activities" do
488 activity_one = insert(:note_activity)
489 activity_two = insert(:note_activity)
490 activity_three = insert(:note_activity)
491 user = insert(:user)
492 booster = insert(:user)
493 {:ok, user} = User.block(user, %{ap_id: activity_one.data["actor"]})
494
495 activities =
496 ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
497
498 assert Enum.member?(activities, activity_two)
499 assert Enum.member?(activities, activity_three)
500 refute Enum.member?(activities, activity_one)
501
502 {:ok, user} = User.unblock(user, %{ap_id: activity_one.data["actor"]})
503
504 activities =
505 ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
506
507 assert Enum.member?(activities, activity_two)
508 assert Enum.member?(activities, activity_three)
509 assert Enum.member?(activities, activity_one)
510
511 {:ok, user} = User.block(user, %{ap_id: activity_three.data["actor"]})
512 {:ok, _announce, %{data: %{"id" => id}}} = CommonAPI.repeat(activity_three.id, booster)
513 %Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
514 activity_three = Activity.get_by_id(activity_three.id)
515
516 activities =
517 ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
518
519 assert Enum.member?(activities, activity_two)
520 refute Enum.member?(activities, activity_three)
521 refute Enum.member?(activities, boost_activity)
522 assert Enum.member?(activities, activity_one)
523
524 activities =
525 ActivityPub.fetch_activities([], %{"blocking_user" => nil, "skip_preload" => true})
526
527 assert Enum.member?(activities, activity_two)
528 assert Enum.member?(activities, activity_three)
529 assert Enum.member?(activities, boost_activity)
530 assert Enum.member?(activities, activity_one)
531 end
532
533 test "doesn't return transitive interactions concerning blocked users" do
534 blocker = insert(:user)
535 blockee = insert(:user)
536 friend = insert(:user)
537
538 {:ok, blocker} = User.block(blocker, blockee)
539
540 {:ok, activity_one} = CommonAPI.post(friend, %{"status" => "hey!"})
541
542 {:ok, activity_two} = CommonAPI.post(friend, %{"status" => "hey! @#{blockee.nickname}"})
543
544 {:ok, activity_three} = CommonAPI.post(blockee, %{"status" => "hey! @#{friend.nickname}"})
545
546 {:ok, activity_four} = CommonAPI.post(blockee, %{"status" => "hey! @#{blocker.nickname}"})
547
548 activities = ActivityPub.fetch_activities([], %{"blocking_user" => blocker})
549
550 assert Enum.member?(activities, activity_one)
551 refute Enum.member?(activities, activity_two)
552 refute Enum.member?(activities, activity_three)
553 refute Enum.member?(activities, activity_four)
554 end
555
556 test "doesn't return announce activities concerning blocked users" do
557 blocker = insert(:user)
558 blockee = insert(:user)
559 friend = insert(:user)
560
561 {:ok, blocker} = User.block(blocker, blockee)
562
563 {:ok, activity_one} = CommonAPI.post(friend, %{"status" => "hey!"})
564
565 {:ok, activity_two} = CommonAPI.post(blockee, %{"status" => "hey! @#{friend.nickname}"})
566
567 {:ok, activity_three, _} = CommonAPI.repeat(activity_two.id, friend)
568
569 activities =
570 ActivityPub.fetch_activities([], %{"blocking_user" => blocker})
571 |> Enum.map(fn act -> act.id end)
572
573 assert Enum.member?(activities, activity_one.id)
574 refute Enum.member?(activities, activity_two.id)
575 refute Enum.member?(activities, activity_three.id)
576 end
577
578 test "doesn't return activities from blocked domains" do
579 domain = "dogwhistle.zone"
580 domain_user = insert(:user, %{ap_id: "https://#{domain}/@pundit"})
581 note = insert(:note, %{data: %{"actor" => domain_user.ap_id}})
582 activity = insert(:note_activity, %{note: note})
583 user = insert(:user)
584 {:ok, user} = User.block_domain(user, domain)
585
586 activities =
587 ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
588
589 refute activity in activities
590
591 followed_user = insert(:user)
592 ActivityPub.follow(user, followed_user)
593 {:ok, repeat_activity, _} = CommonAPI.repeat(activity.id, followed_user)
594
595 activities =
596 ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
597
598 refute repeat_activity in activities
599 end
600
601 test "doesn't return muted activities" do
602 activity_one = insert(:note_activity)
603 activity_two = insert(:note_activity)
604 activity_three = insert(:note_activity)
605 user = insert(:user)
606 booster = insert(:user)
607 {:ok, user} = User.mute(user, %User{ap_id: activity_one.data["actor"]})
608
609 activities =
610 ActivityPub.fetch_activities([], %{"muting_user" => user, "skip_preload" => true})
611
612 assert Enum.member?(activities, activity_two)
613 assert Enum.member?(activities, activity_three)
614 refute Enum.member?(activities, activity_one)
615
616 # Calling with 'with_muted' will deliver muted activities, too.
617 activities =
618 ActivityPub.fetch_activities([], %{
619 "muting_user" => user,
620 "with_muted" => true,
621 "skip_preload" => true
622 })
623
624 assert Enum.member?(activities, activity_two)
625 assert Enum.member?(activities, activity_three)
626 assert Enum.member?(activities, activity_one)
627
628 {:ok, user} = User.unmute(user, %User{ap_id: activity_one.data["actor"]})
629
630 activities =
631 ActivityPub.fetch_activities([], %{"muting_user" => user, "skip_preload" => true})
632
633 assert Enum.member?(activities, activity_two)
634 assert Enum.member?(activities, activity_three)
635 assert Enum.member?(activities, activity_one)
636
637 {:ok, user} = User.mute(user, %User{ap_id: activity_three.data["actor"]})
638 {:ok, _announce, %{data: %{"id" => id}}} = CommonAPI.repeat(activity_three.id, booster)
639 %Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
640 activity_three = Activity.get_by_id(activity_three.id)
641
642 activities =
643 ActivityPub.fetch_activities([], %{"muting_user" => user, "skip_preload" => true})
644
645 assert Enum.member?(activities, activity_two)
646 refute Enum.member?(activities, activity_three)
647 refute Enum.member?(activities, boost_activity)
648 assert Enum.member?(activities, activity_one)
649
650 activities = ActivityPub.fetch_activities([], %{"muting_user" => nil, "skip_preload" => true})
651
652 assert Enum.member?(activities, activity_two)
653 assert Enum.member?(activities, activity_three)
654 assert Enum.member?(activities, boost_activity)
655 assert Enum.member?(activities, activity_one)
656 end
657
658 test "doesn't return thread muted activities" do
659 user = insert(:user)
660 _activity_one = insert(:note_activity)
661 note_two = insert(:note, data: %{"context" => "suya.."})
662 activity_two = insert(:note_activity, note: note_two)
663
664 {:ok, _activity_two} = CommonAPI.add_mute(user, activity_two)
665
666 assert [_activity_one] = ActivityPub.fetch_activities([], %{"muting_user" => user})
667 end
668
669 test "returns thread muted activities when with_muted is set" do
670 user = insert(:user)
671 _activity_one = insert(:note_activity)
672 note_two = insert(:note, data: %{"context" => "suya.."})
673 activity_two = insert(:note_activity, note: note_two)
674
675 {:ok, _activity_two} = CommonAPI.add_mute(user, activity_two)
676
677 assert [_activity_two, _activity_one] =
678 ActivityPub.fetch_activities([], %{"muting_user" => user, "with_muted" => true})
679 end
680
681 test "does include announces on request" do
682 activity_three = insert(:note_activity)
683 user = insert(:user)
684 booster = insert(:user)
685
686 {:ok, user} = User.follow(user, booster)
687
688 {:ok, announce, _object} = CommonAPI.repeat(activity_three.id, booster)
689
690 [announce_activity] = ActivityPub.fetch_activities([user.ap_id | User.following(user)])
691
692 assert announce_activity.id == announce.id
693 end
694
695 test "excludes reblogs on request" do
696 user = insert(:user)
697 {:ok, expected_activity} = ActivityBuilder.insert(%{"type" => "Create"}, %{:user => user})
698 {:ok, _} = ActivityBuilder.insert(%{"type" => "Announce"}, %{:user => user})
699
700 [activity] = ActivityPub.fetch_user_activities(user, nil, %{"exclude_reblogs" => "true"})
701
702 assert activity == expected_activity
703 end
704
705 describe "public fetch activities" do
706 test "doesn't retrieve unlisted activities" do
707 user = insert(:user)
708
709 {:ok, _unlisted_activity} =
710 CommonAPI.post(user, %{"status" => "yeah", "visibility" => "unlisted"})
711
712 {:ok, listed_activity} = CommonAPI.post(user, %{"status" => "yeah"})
713
714 [activity] = ActivityPub.fetch_public_activities()
715
716 assert activity == listed_activity
717 end
718
719 test "retrieves public activities" do
720 _activities = ActivityPub.fetch_public_activities()
721
722 %{public: public} = ActivityBuilder.public_and_non_public()
723
724 activities = ActivityPub.fetch_public_activities()
725 assert length(activities) == 1
726 assert Enum.at(activities, 0) == public
727 end
728
729 test "retrieves a maximum of 20 activities" do
730 activities = ActivityBuilder.insert_list(30)
731 last_expected = List.last(activities)
732
733 activities = ActivityPub.fetch_public_activities()
734 last = List.last(activities)
735
736 assert length(activities) == 20
737 assert last == last_expected
738 end
739
740 test "retrieves ids starting from a since_id" do
741 activities = ActivityBuilder.insert_list(30)
742 later_activities = ActivityBuilder.insert_list(10)
743 since_id = List.last(activities).id
744 last_expected = List.last(later_activities)
745
746 activities = ActivityPub.fetch_public_activities(%{"since_id" => since_id})
747 last = List.last(activities)
748
749 assert length(activities) == 10
750 assert last == last_expected
751 end
752
753 test "retrieves ids up to max_id" do
754 _first_activities = ActivityBuilder.insert_list(10)
755 activities = ActivityBuilder.insert_list(20)
756 later_activities = ActivityBuilder.insert_list(10)
757 max_id = List.first(later_activities).id
758 last_expected = List.last(activities)
759
760 activities = ActivityPub.fetch_public_activities(%{"max_id" => max_id})
761 last = List.last(activities)
762
763 assert length(activities) == 20
764 assert last == last_expected
765 end
766
767 test "paginates via offset/limit" do
768 _first_activities = ActivityBuilder.insert_list(10)
769 activities = ActivityBuilder.insert_list(10)
770 _later_activities = ActivityBuilder.insert_list(10)
771 first_expected = List.first(activities)
772
773 activities =
774 ActivityPub.fetch_public_activities(%{"page" => "2", "page_size" => "20"}, :offset)
775
776 first = List.first(activities)
777
778 assert length(activities) == 20
779 assert first == first_expected
780 end
781
782 test "doesn't return reblogs for users for whom reblogs have been muted" do
783 activity = insert(:note_activity)
784 user = insert(:user)
785 booster = insert(:user)
786 {:ok, user} = CommonAPI.hide_reblogs(user, booster)
787
788 {:ok, activity, _} = CommonAPI.repeat(activity.id, booster)
789
790 activities = ActivityPub.fetch_activities([], %{"muting_user" => user})
791
792 refute Enum.any?(activities, fn %{id: id} -> id == activity.id end)
793 end
794
795 test "returns reblogs for users for whom reblogs have not been muted" do
796 activity = insert(:note_activity)
797 user = insert(:user)
798 booster = insert(:user)
799 {:ok, user} = CommonAPI.hide_reblogs(user, booster)
800 {:ok, user} = CommonAPI.show_reblogs(user, booster)
801
802 {:ok, activity, _} = CommonAPI.repeat(activity.id, booster)
803
804 activities = ActivityPub.fetch_activities([], %{"muting_user" => user})
805
806 assert Enum.any?(activities, fn %{id: id} -> id == activity.id end)
807 end
808 end
809
810 describe "like an object" do
811 test_with_mock "sends an activity to federation", Pleroma.Web.Federator, [:passthrough], [] do
812 Pleroma.Config.put([:instance, :federating], true)
813 note_activity = insert(:note_activity)
814 assert object_activity = Object.normalize(note_activity)
815
816 user = insert(:user)
817
818 {:ok, like_activity, _object} = ActivityPub.like(user, object_activity)
819 assert called(Pleroma.Web.Federator.publish(like_activity))
820 end
821
822 test "returns exist activity if object already liked" do
823 note_activity = insert(:note_activity)
824 assert object_activity = Object.normalize(note_activity)
825
826 user = insert(:user)
827
828 {:ok, like_activity, _object} = ActivityPub.like(user, object_activity)
829
830 {:ok, like_activity_exist, _object} = ActivityPub.like(user, object_activity)
831 assert like_activity == like_activity_exist
832 end
833
834 test "adds a like activity to the db" do
835 note_activity = insert(:note_activity)
836 assert object = Object.normalize(note_activity)
837
838 user = insert(:user)
839 user_two = insert(:user)
840
841 {:ok, like_activity, object} = ActivityPub.like(user, object)
842
843 assert like_activity.data["actor"] == user.ap_id
844 assert like_activity.data["type"] == "Like"
845 assert like_activity.data["object"] == object.data["id"]
846 assert like_activity.data["to"] == [User.ap_followers(user), note_activity.data["actor"]]
847 assert like_activity.data["context"] == object.data["context"]
848 assert object.data["like_count"] == 1
849 assert object.data["likes"] == [user.ap_id]
850
851 # Just return the original activity if the user already liked it.
852 {:ok, same_like_activity, object} = ActivityPub.like(user, object)
853
854 assert like_activity == same_like_activity
855 assert object.data["likes"] == [user.ap_id]
856 assert object.data["like_count"] == 1
857
858 {:ok, _like_activity, object} = ActivityPub.like(user_two, object)
859 assert object.data["like_count"] == 2
860 end
861 end
862
863 describe "unliking" do
864 test_with_mock "sends an activity to federation", Pleroma.Web.Federator, [:passthrough], [] do
865 Pleroma.Config.put([:instance, :federating], true)
866
867 note_activity = insert(:note_activity)
868 object = Object.normalize(note_activity)
869 user = insert(:user)
870
871 {:ok, object} = ActivityPub.unlike(user, object)
872 refute called(Pleroma.Web.Federator.publish())
873
874 {:ok, _like_activity, object} = ActivityPub.like(user, object)
875 assert object.data["like_count"] == 1
876
877 {:ok, unlike_activity, _, object} = ActivityPub.unlike(user, object)
878 assert object.data["like_count"] == 0
879
880 assert called(Pleroma.Web.Federator.publish(unlike_activity))
881 end
882
883 test "unliking a previously liked object" do
884 note_activity = insert(:note_activity)
885 object = Object.normalize(note_activity)
886 user = insert(:user)
887
888 # Unliking something that hasn't been liked does nothing
889 {:ok, object} = ActivityPub.unlike(user, object)
890 assert object.data["like_count"] == 0
891
892 {:ok, like_activity, object} = ActivityPub.like(user, object)
893 assert object.data["like_count"] == 1
894
895 {:ok, unlike_activity, _, object} = ActivityPub.unlike(user, object)
896 assert object.data["like_count"] == 0
897
898 assert Activity.get_by_id(like_activity.id) == nil
899 assert note_activity.actor in unlike_activity.recipients
900 end
901 end
902
903 describe "announcing an object" do
904 test "adds an announce activity to the db" do
905 note_activity = insert(:note_activity)
906 object = Object.normalize(note_activity)
907 user = insert(:user)
908
909 {:ok, announce_activity, object} = ActivityPub.announce(user, object)
910 assert object.data["announcement_count"] == 1
911 assert object.data["announcements"] == [user.ap_id]
912
913 assert announce_activity.data["to"] == [
914 User.ap_followers(user),
915 note_activity.data["actor"]
916 ]
917
918 assert announce_activity.data["object"] == object.data["id"]
919 assert announce_activity.data["actor"] == user.ap_id
920 assert announce_activity.data["context"] == object.data["context"]
921 end
922 end
923
924 describe "announcing a private object" do
925 test "adds an announce activity to the db if the audience is not widened" do
926 user = insert(:user)
927 {:ok, note_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
928 object = Object.normalize(note_activity)
929
930 {:ok, announce_activity, object} = ActivityPub.announce(user, object, nil, true, false)
931
932 assert announce_activity.data["to"] == [User.ap_followers(user)]
933
934 assert announce_activity.data["object"] == object.data["id"]
935 assert announce_activity.data["actor"] == user.ap_id
936 assert announce_activity.data["context"] == object.data["context"]
937 end
938
939 test "does not add an announce activity to the db if the audience is widened" do
940 user = insert(:user)
941 {:ok, note_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
942 object = Object.normalize(note_activity)
943
944 assert {:error, _} = ActivityPub.announce(user, object, nil, true, true)
945 end
946
947 test "does not add an announce activity to the db if the announcer is not the author" do
948 user = insert(:user)
949 announcer = insert(:user)
950 {:ok, note_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
951 object = Object.normalize(note_activity)
952
953 assert {:error, _} = ActivityPub.announce(announcer, object, nil, true, false)
954 end
955 end
956
957 describe "unannouncing an object" do
958 test "unannouncing a previously announced object" do
959 note_activity = insert(:note_activity)
960 object = Object.normalize(note_activity)
961 user = insert(:user)
962
963 # Unannouncing an object that is not announced does nothing
964 # {:ok, object} = ActivityPub.unannounce(user, object)
965 # assert object.data["announcement_count"] == 0
966
967 {:ok, announce_activity, object} = ActivityPub.announce(user, object)
968 assert object.data["announcement_count"] == 1
969
970 {:ok, unannounce_activity, object} = ActivityPub.unannounce(user, object)
971 assert object.data["announcement_count"] == 0
972
973 assert unannounce_activity.data["to"] == [
974 User.ap_followers(user),
975 object.data["actor"]
976 ]
977
978 assert unannounce_activity.data["type"] == "Undo"
979 assert unannounce_activity.data["object"] == announce_activity.data
980 assert unannounce_activity.data["actor"] == user.ap_id
981 assert unannounce_activity.data["context"] == announce_activity.data["context"]
982
983 assert Activity.get_by_id(announce_activity.id) == nil
984 end
985 end
986
987 describe "uploading files" do
988 test "copies the file to the configured folder" do
989 file = %Plug.Upload{
990 content_type: "image/jpg",
991 path: Path.absname("test/fixtures/image.jpg"),
992 filename: "an_image.jpg"
993 }
994
995 {:ok, %Object{} = object} = ActivityPub.upload(file)
996 assert object.data["name"] == "an_image.jpg"
997 end
998
999 test "works with base64 encoded images" do
1000 file = %{
1001 "img" => data_uri()
1002 }
1003
1004 {:ok, %Object{}} = ActivityPub.upload(file)
1005 end
1006 end
1007
1008 describe "fetch the latest Follow" do
1009 test "fetches the latest Follow activity" do
1010 %Activity{data: %{"type" => "Follow"}} = activity = insert(:follow_activity)
1011 follower = Repo.get_by(User, ap_id: activity.data["actor"])
1012 followed = Repo.get_by(User, ap_id: activity.data["object"])
1013
1014 assert activity == Utils.fetch_latest_follow(follower, followed)
1015 end
1016 end
1017
1018 describe "following / unfollowing" do
1019 test "creates a follow activity" do
1020 follower = insert(:user)
1021 followed = insert(:user)
1022
1023 {:ok, activity} = ActivityPub.follow(follower, followed)
1024 assert activity.data["type"] == "Follow"
1025 assert activity.data["actor"] == follower.ap_id
1026 assert activity.data["object"] == followed.ap_id
1027 end
1028
1029 test "creates an undo activity for the last follow" do
1030 follower = insert(:user)
1031 followed = insert(:user)
1032
1033 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
1034 {:ok, activity} = ActivityPub.unfollow(follower, followed)
1035
1036 assert activity.data["type"] == "Undo"
1037 assert activity.data["actor"] == follower.ap_id
1038
1039 embedded_object = activity.data["object"]
1040 assert is_map(embedded_object)
1041 assert embedded_object["type"] == "Follow"
1042 assert embedded_object["object"] == followed.ap_id
1043 assert embedded_object["id"] == follow_activity.data["id"]
1044 end
1045 end
1046
1047 describe "blocking / unblocking" do
1048 test "creates a block activity" do
1049 blocker = insert(:user)
1050 blocked = insert(:user)
1051
1052 {:ok, activity} = ActivityPub.block(blocker, blocked)
1053
1054 assert activity.data["type"] == "Block"
1055 assert activity.data["actor"] == blocker.ap_id
1056 assert activity.data["object"] == blocked.ap_id
1057 end
1058
1059 test "creates an undo activity for the last block" do
1060 blocker = insert(:user)
1061 blocked = insert(:user)
1062
1063 {:ok, block_activity} = ActivityPub.block(blocker, blocked)
1064 {:ok, activity} = ActivityPub.unblock(blocker, blocked)
1065
1066 assert activity.data["type"] == "Undo"
1067 assert activity.data["actor"] == blocker.ap_id
1068
1069 embedded_object = activity.data["object"]
1070 assert is_map(embedded_object)
1071 assert embedded_object["type"] == "Block"
1072 assert embedded_object["object"] == blocked.ap_id
1073 assert embedded_object["id"] == block_activity.data["id"]
1074 end
1075 end
1076
1077 describe "deletion" do
1078 test "it creates a delete activity and deletes the original object" do
1079 note = insert(:note_activity)
1080 object = Object.normalize(note)
1081 {:ok, delete} = ActivityPub.delete(object)
1082
1083 assert delete.data["type"] == "Delete"
1084 assert delete.data["actor"] == note.data["actor"]
1085 assert delete.data["object"] == object.data["id"]
1086
1087 assert Activity.get_by_id(delete.id) != nil
1088
1089 assert Repo.get(Object, object.id).data["type"] == "Tombstone"
1090 end
1091
1092 test "decrements user note count only for public activities" do
1093 user = insert(:user, info: %{note_count: 10})
1094
1095 {:ok, a1} =
1096 CommonAPI.post(User.get_cached_by_id(user.id), %{
1097 "status" => "yeah",
1098 "visibility" => "public"
1099 })
1100
1101 {:ok, a2} =
1102 CommonAPI.post(User.get_cached_by_id(user.id), %{
1103 "status" => "yeah",
1104 "visibility" => "unlisted"
1105 })
1106
1107 {:ok, a3} =
1108 CommonAPI.post(User.get_cached_by_id(user.id), %{
1109 "status" => "yeah",
1110 "visibility" => "private"
1111 })
1112
1113 {:ok, a4} =
1114 CommonAPI.post(User.get_cached_by_id(user.id), %{
1115 "status" => "yeah",
1116 "visibility" => "direct"
1117 })
1118
1119 {:ok, _} = Object.normalize(a1) |> ActivityPub.delete()
1120 {:ok, _} = Object.normalize(a2) |> ActivityPub.delete()
1121 {:ok, _} = Object.normalize(a3) |> ActivityPub.delete()
1122 {:ok, _} = Object.normalize(a4) |> ActivityPub.delete()
1123
1124 user = User.get_cached_by_id(user.id)
1125 assert user.info.note_count == 10
1126 end
1127
1128 test "it creates a delete activity and checks that it is also sent to users mentioned by the deleted object" do
1129 user = insert(:user)
1130 note = insert(:note_activity)
1131 object = Object.normalize(note)
1132
1133 {:ok, object} =
1134 object
1135 |> Object.change(%{
1136 data: %{
1137 "actor" => object.data["actor"],
1138 "id" => object.data["id"],
1139 "to" => [user.ap_id],
1140 "type" => "Note"
1141 }
1142 })
1143 |> Object.update_and_set_cache()
1144
1145 {:ok, delete} = ActivityPub.delete(object)
1146
1147 assert user.ap_id in delete.data["to"]
1148 end
1149
1150 test "decreases reply count" do
1151 user = insert(:user)
1152 user2 = insert(:user)
1153
1154 {:ok, activity} = CommonAPI.post(user, %{"status" => "1", "visibility" => "public"})
1155 reply_data = %{"status" => "1", "in_reply_to_status_id" => activity.id}
1156 ap_id = activity.data["id"]
1157
1158 {:ok, public_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "public"))
1159 {:ok, unlisted_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "unlisted"))
1160 {:ok, private_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "private"))
1161 {:ok, direct_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "direct"))
1162
1163 _ = CommonAPI.delete(direct_reply.id, user2)
1164 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
1165 assert object.data["repliesCount"] == 2
1166
1167 _ = CommonAPI.delete(private_reply.id, user2)
1168 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
1169 assert object.data["repliesCount"] == 2
1170
1171 _ = CommonAPI.delete(public_reply.id, user2)
1172 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
1173 assert object.data["repliesCount"] == 1
1174
1175 _ = CommonAPI.delete(unlisted_reply.id, user2)
1176 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
1177 assert object.data["repliesCount"] == 0
1178 end
1179 end
1180
1181 describe "timeline post-processing" do
1182 test "it filters broken threads" do
1183 user1 = insert(:user)
1184 user2 = insert(:user)
1185 user3 = insert(:user)
1186
1187 {:ok, user1} = User.follow(user1, user3)
1188 assert User.following?(user1, user3)
1189
1190 {:ok, user2} = User.follow(user2, user3)
1191 assert User.following?(user2, user3)
1192
1193 {:ok, user3} = User.follow(user3, user2)
1194 assert User.following?(user3, user2)
1195
1196 {:ok, public_activity} = CommonAPI.post(user3, %{"status" => "hi 1"})
1197
1198 {:ok, private_activity_1} =
1199 CommonAPI.post(user3, %{"status" => "hi 2", "visibility" => "private"})
1200
1201 {:ok, private_activity_2} =
1202 CommonAPI.post(user2, %{
1203 "status" => "hi 3",
1204 "visibility" => "private",
1205 "in_reply_to_status_id" => private_activity_1.id
1206 })
1207
1208 {:ok, private_activity_3} =
1209 CommonAPI.post(user3, %{
1210 "status" => "hi 4",
1211 "visibility" => "private",
1212 "in_reply_to_status_id" => private_activity_2.id
1213 })
1214
1215 activities =
1216 ActivityPub.fetch_activities([user1.ap_id | User.following(user1)])
1217 |> Enum.map(fn a -> a.id end)
1218
1219 private_activity_1 = Activity.get_by_ap_id_with_object(private_activity_1.data["id"])
1220
1221 assert [public_activity.id, private_activity_1.id, private_activity_3.id] == activities
1222
1223 assert length(activities) == 3
1224
1225 activities =
1226 ActivityPub.fetch_activities([user1.ap_id | User.following(user1)], %{"user" => user1})
1227 |> Enum.map(fn a -> a.id end)
1228
1229 assert [public_activity.id, private_activity_1.id] == activities
1230 assert length(activities) == 2
1231 end
1232 end
1233
1234 describe "update" do
1235 test "it creates an update activity with the new user data" do
1236 user = insert(:user)
1237 {:ok, user} = User.ensure_keys_present(user)
1238 user_data = Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: user})
1239
1240 {:ok, update} =
1241 ActivityPub.update(%{
1242 actor: user_data["id"],
1243 to: [user.follower_address],
1244 cc: [],
1245 object: user_data
1246 })
1247
1248 assert update.data["actor"] == user.ap_id
1249 assert update.data["to"] == [user.follower_address]
1250 assert embedded_object = update.data["object"]
1251 assert embedded_object["id"] == user_data["id"]
1252 assert embedded_object["type"] == user_data["type"]
1253 end
1254 end
1255
1256 test "returned pinned statuses" do
1257 Pleroma.Config.put([:instance, :max_pinned_statuses], 3)
1258 user = insert(:user)
1259
1260 {:ok, activity_one} = CommonAPI.post(user, %{"status" => "HI!!!"})
1261 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
1262 {:ok, activity_three} = CommonAPI.post(user, %{"status" => "HI!!!"})
1263
1264 CommonAPI.pin(activity_one.id, user)
1265 user = refresh_record(user)
1266
1267 CommonAPI.pin(activity_two.id, user)
1268 user = refresh_record(user)
1269
1270 CommonAPI.pin(activity_three.id, user)
1271 user = refresh_record(user)
1272
1273 activities = ActivityPub.fetch_user_activities(user, nil, %{"pinned" => "true"})
1274
1275 assert 3 = length(activities)
1276 end
1277
1278 test "it can create a Flag activity" do
1279 reporter = insert(:user)
1280 target_account = insert(:user)
1281 {:ok, activity} = CommonAPI.post(target_account, %{"status" => "foobar"})
1282 context = Utils.generate_context_id()
1283 content = "foobar"
1284
1285 reporter_ap_id = reporter.ap_id
1286 target_ap_id = target_account.ap_id
1287 activity_ap_id = activity.data["id"]
1288
1289 assert {:ok, activity} =
1290 ActivityPub.flag(%{
1291 actor: reporter,
1292 context: context,
1293 account: target_account,
1294 statuses: [activity],
1295 content: content
1296 })
1297
1298 assert %Activity{
1299 actor: ^reporter_ap_id,
1300 data: %{
1301 "type" => "Flag",
1302 "content" => ^content,
1303 "context" => ^context,
1304 "object" => [^target_ap_id, ^activity_ap_id]
1305 }
1306 } = activity
1307 end
1308
1309 test "fetch_activities/2 returns activities addressed to a list " do
1310 user = insert(:user)
1311 member = insert(:user)
1312 {:ok, list} = Pleroma.List.create("foo", user)
1313 {:ok, list} = Pleroma.List.follow(list, member)
1314
1315 {:ok, activity} =
1316 CommonAPI.post(user, %{"status" => "foobar", "visibility" => "list:#{list.id}"})
1317
1318 activity = Repo.preload(activity, :bookmark)
1319 activity = %Activity{activity | thread_muted?: !!activity.thread_muted?}
1320
1321 assert ActivityPub.fetch_activities([], %{"user" => user}) == [activity]
1322 end
1323
1324 def data_uri do
1325 File.read!("test/fixtures/avatar_data_uri")
1326 end
1327
1328 describe "fetch_activities_bounded" do
1329 test "fetches private posts for followed users" do
1330 user = insert(:user)
1331
1332 {:ok, activity} =
1333 CommonAPI.post(user, %{
1334 "status" => "thought I looked cute might delete later :3",
1335 "visibility" => "private"
1336 })
1337
1338 [result] = ActivityPub.fetch_activities_bounded([user.follower_address], [])
1339 assert result.id == activity.id
1340 end
1341
1342 test "fetches only public posts for other users" do
1343 user = insert(:user)
1344 {:ok, activity} = CommonAPI.post(user, %{"status" => "#cofe", "visibility" => "public"})
1345
1346 {:ok, _private_activity} =
1347 CommonAPI.post(user, %{
1348 "status" => "why is tenshi eating a corndog so cute?",
1349 "visibility" => "private"
1350 })
1351
1352 [result] = ActivityPub.fetch_activities_bounded([], [user.follower_address])
1353 assert result.id == activity.id
1354 end
1355 end
1356
1357 describe "fetch_follow_information_for_user" do
1358 test "syncronizes following/followers counters" do
1359 user =
1360 insert(:user,
1361 local: false,
1362 follower_address: "http://localhost:4001/users/fuser2/followers",
1363 following_address: "http://localhost:4001/users/fuser2/following"
1364 )
1365
1366 {:ok, info} = ActivityPub.fetch_follow_information_for_user(user)
1367 assert info.follower_count == 527
1368 assert info.following_count == 267
1369 end
1370
1371 test "detects hidden followers" do
1372 mock(fn env ->
1373 case env.url do
1374 "http://localhost:4001/users/masto_closed/followers?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, info} = ActivityPub.fetch_follow_information_for_user(user)
1390 assert info.hide_followers == true
1391 assert info.hide_follows == false
1392 end
1393
1394 test "detects hidden follows" do
1395 mock(fn env ->
1396 case env.url do
1397 "http://localhost:4001/users/masto_closed/following?page=1" ->
1398 %Tesla.Env{status: 403, body: ""}
1399
1400 _ ->
1401 apply(HttpRequestMock, :request, [env])
1402 end
1403 end)
1404
1405 user =
1406 insert(:user,
1407 local: false,
1408 follower_address: "http://localhost:4001/users/masto_closed/followers",
1409 following_address: "http://localhost:4001/users/masto_closed/following"
1410 )
1411
1412 {:ok, info} = ActivityPub.fetch_follow_information_for_user(user)
1413 assert info.hide_followers == false
1414 assert info.hide_follows == true
1415 end
1416 end
1417 end