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