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