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