Merge branch 'shortening-modules-name' 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 blocker = insert(:user)
1065 blocked = insert(:user)
1066
1067 {:ok, activity} = ActivityPub.block(blocker, blocked)
1068
1069 assert activity.data["type"] == "Block"
1070 assert activity.data["actor"] == blocker.ap_id
1071 assert activity.data["object"] == blocked.ap_id
1072 end
1073 end
1074
1075 describe "timeline post-processing" do
1076 test "it filters broken threads" do
1077 user1 = insert(:user)
1078 user2 = insert(:user)
1079 user3 = insert(:user)
1080
1081 {:ok, user1} = User.follow(user1, user3)
1082 assert User.following?(user1, user3)
1083
1084 {:ok, user2} = User.follow(user2, user3)
1085 assert User.following?(user2, user3)
1086
1087 {:ok, user3} = User.follow(user3, user2)
1088 assert User.following?(user3, user2)
1089
1090 {:ok, public_activity} = CommonAPI.post(user3, %{"status" => "hi 1"})
1091
1092 {:ok, private_activity_1} =
1093 CommonAPI.post(user3, %{"status" => "hi 2", "visibility" => "private"})
1094
1095 {:ok, private_activity_2} =
1096 CommonAPI.post(user2, %{
1097 "status" => "hi 3",
1098 "visibility" => "private",
1099 "in_reply_to_status_id" => private_activity_1.id
1100 })
1101
1102 {:ok, private_activity_3} =
1103 CommonAPI.post(user3, %{
1104 "status" => "hi 4",
1105 "visibility" => "private",
1106 "in_reply_to_status_id" => private_activity_2.id
1107 })
1108
1109 activities =
1110 ActivityPub.fetch_activities([user1.ap_id | User.following(user1)])
1111 |> Enum.map(fn a -> a.id end)
1112
1113 private_activity_1 = Activity.get_by_ap_id_with_object(private_activity_1.data["id"])
1114
1115 assert [public_activity.id, private_activity_1.id, private_activity_3.id] == activities
1116
1117 assert length(activities) == 3
1118
1119 activities =
1120 ActivityPub.fetch_activities([user1.ap_id | User.following(user1)], %{"user" => user1})
1121 |> Enum.map(fn a -> a.id end)
1122
1123 assert [public_activity.id, private_activity_1.id] == activities
1124 assert length(activities) == 2
1125 end
1126 end
1127
1128 describe "update" do
1129 setup do: clear_config([:instance, :max_pinned_statuses])
1130
1131 test "it creates an update activity with the new user data" do
1132 user = insert(:user)
1133 {:ok, user} = User.ensure_keys_present(user)
1134 user_data = Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: user})
1135
1136 {:ok, update} =
1137 ActivityPub.update(%{
1138 actor: user_data["id"],
1139 to: [user.follower_address],
1140 cc: [],
1141 object: user_data
1142 })
1143
1144 assert update.data["actor"] == user.ap_id
1145 assert update.data["to"] == [user.follower_address]
1146 assert embedded_object = update.data["object"]
1147 assert embedded_object["id"] == user_data["id"]
1148 assert embedded_object["type"] == user_data["type"]
1149 end
1150 end
1151
1152 test "returned pinned statuses" do
1153 Config.put([:instance, :max_pinned_statuses], 3)
1154 user = insert(:user)
1155
1156 {:ok, activity_one} = CommonAPI.post(user, %{"status" => "HI!!!"})
1157 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
1158 {:ok, activity_three} = CommonAPI.post(user, %{"status" => "HI!!!"})
1159
1160 CommonAPI.pin(activity_one.id, user)
1161 user = refresh_record(user)
1162
1163 CommonAPI.pin(activity_two.id, user)
1164 user = refresh_record(user)
1165
1166 CommonAPI.pin(activity_three.id, user)
1167 user = refresh_record(user)
1168
1169 activities = ActivityPub.fetch_user_activities(user, nil, %{"pinned" => "true"})
1170
1171 assert 3 = length(activities)
1172 end
1173
1174 describe "flag/1" do
1175 setup do
1176 reporter = insert(:user)
1177 target_account = insert(:user)
1178 content = "foobar"
1179 {:ok, activity} = CommonAPI.post(target_account, %{"status" => content})
1180 context = Utils.generate_context_id()
1181
1182 reporter_ap_id = reporter.ap_id
1183 target_ap_id = target_account.ap_id
1184 activity_ap_id = activity.data["id"]
1185
1186 activity_with_object = Activity.get_by_ap_id_with_object(activity_ap_id)
1187
1188 {:ok,
1189 %{
1190 reporter: reporter,
1191 context: context,
1192 target_account: target_account,
1193 reported_activity: activity,
1194 content: content,
1195 activity_ap_id: activity_ap_id,
1196 activity_with_object: activity_with_object,
1197 reporter_ap_id: reporter_ap_id,
1198 target_ap_id: target_ap_id
1199 }}
1200 end
1201
1202 test "it can create a Flag activity",
1203 %{
1204 reporter: reporter,
1205 context: context,
1206 target_account: target_account,
1207 reported_activity: reported_activity,
1208 content: content,
1209 activity_ap_id: activity_ap_id,
1210 activity_with_object: activity_with_object,
1211 reporter_ap_id: reporter_ap_id,
1212 target_ap_id: target_ap_id
1213 } do
1214 assert {:ok, activity} =
1215 ActivityPub.flag(%{
1216 actor: reporter,
1217 context: context,
1218 account: target_account,
1219 statuses: [reported_activity],
1220 content: content
1221 })
1222
1223 note_obj = %{
1224 "type" => "Note",
1225 "id" => activity_ap_id,
1226 "content" => content,
1227 "published" => activity_with_object.object.data["published"],
1228 "actor" => AccountView.render("show.json", %{user: target_account})
1229 }
1230
1231 assert %Activity{
1232 actor: ^reporter_ap_id,
1233 data: %{
1234 "type" => "Flag",
1235 "content" => ^content,
1236 "context" => ^context,
1237 "object" => [^target_ap_id, ^note_obj]
1238 }
1239 } = activity
1240 end
1241
1242 test_with_mock "strips status data from Flag, before federating it",
1243 %{
1244 reporter: reporter,
1245 context: context,
1246 target_account: target_account,
1247 reported_activity: reported_activity,
1248 content: content
1249 },
1250 Utils,
1251 [:passthrough],
1252 [] do
1253 {:ok, activity} =
1254 ActivityPub.flag(%{
1255 actor: reporter,
1256 context: context,
1257 account: target_account,
1258 statuses: [reported_activity],
1259 content: content
1260 })
1261
1262 new_data =
1263 put_in(activity.data, ["object"], [target_account.ap_id, reported_activity.data["id"]])
1264
1265 assert_called(Utils.maybe_federate(%{activity | data: new_data}))
1266 end
1267 end
1268
1269 test "fetch_activities/2 returns activities addressed to a list " do
1270 user = insert(:user)
1271 member = insert(:user)
1272 {:ok, list} = Pleroma.List.create("foo", user)
1273 {:ok, list} = Pleroma.List.follow(list, member)
1274
1275 {:ok, activity} =
1276 CommonAPI.post(user, %{"status" => "foobar", "visibility" => "list:#{list.id}"})
1277
1278 activity = Repo.preload(activity, :bookmark)
1279 activity = %Activity{activity | thread_muted?: !!activity.thread_muted?}
1280
1281 assert ActivityPub.fetch_activities([], %{"user" => user}) == [activity]
1282 end
1283
1284 def data_uri do
1285 File.read!("test/fixtures/avatar_data_uri")
1286 end
1287
1288 describe "fetch_activities_bounded" do
1289 test "fetches private posts for followed users" do
1290 user = insert(:user)
1291
1292 {:ok, activity} =
1293 CommonAPI.post(user, %{
1294 "status" => "thought I looked cute might delete later :3",
1295 "visibility" => "private"
1296 })
1297
1298 [result] = ActivityPub.fetch_activities_bounded([user.follower_address], [])
1299 assert result.id == activity.id
1300 end
1301
1302 test "fetches only public posts for other users" do
1303 user = insert(:user)
1304 {:ok, activity} = CommonAPI.post(user, %{"status" => "#cofe", "visibility" => "public"})
1305
1306 {:ok, _private_activity} =
1307 CommonAPI.post(user, %{
1308 "status" => "why is tenshi eating a corndog so cute?",
1309 "visibility" => "private"
1310 })
1311
1312 [result] = ActivityPub.fetch_activities_bounded([], [user.follower_address])
1313 assert result.id == activity.id
1314 end
1315 end
1316
1317 describe "fetch_follow_information_for_user" do
1318 test "syncronizes following/followers counters" do
1319 user =
1320 insert(:user,
1321 local: false,
1322 follower_address: "http://localhost:4001/users/fuser2/followers",
1323 following_address: "http://localhost:4001/users/fuser2/following"
1324 )
1325
1326 {:ok, info} = ActivityPub.fetch_follow_information_for_user(user)
1327 assert info.follower_count == 527
1328 assert info.following_count == 267
1329 end
1330
1331 test "detects hidden followers" do
1332 mock(fn env ->
1333 case env.url do
1334 "http://localhost:4001/users/masto_closed/followers?page=1" ->
1335 %Tesla.Env{status: 403, body: ""}
1336
1337 _ ->
1338 apply(HttpRequestMock, :request, [env])
1339 end
1340 end)
1341
1342 user =
1343 insert(:user,
1344 local: false,
1345 follower_address: "http://localhost:4001/users/masto_closed/followers",
1346 following_address: "http://localhost:4001/users/masto_closed/following"
1347 )
1348
1349 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1350 assert follow_info.hide_followers == true
1351 assert follow_info.hide_follows == false
1352 end
1353
1354 test "detects hidden follows" do
1355 mock(fn env ->
1356 case env.url do
1357 "http://localhost:4001/users/masto_closed/following?page=1" ->
1358 %Tesla.Env{status: 403, body: ""}
1359
1360 _ ->
1361 apply(HttpRequestMock, :request, [env])
1362 end
1363 end)
1364
1365 user =
1366 insert(:user,
1367 local: false,
1368 follower_address: "http://localhost:4001/users/masto_closed/followers",
1369 following_address: "http://localhost:4001/users/masto_closed/following"
1370 )
1371
1372 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1373 assert follow_info.hide_followers == false
1374 assert follow_info.hide_follows == true
1375 end
1376
1377 test "detects hidden follows/followers for friendica" do
1378 user =
1379 insert(:user,
1380 local: false,
1381 follower_address: "http://localhost:8080/followers/fuser3",
1382 following_address: "http://localhost:8080/following/fuser3"
1383 )
1384
1385 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1386 assert follow_info.hide_followers == true
1387 assert follow_info.follower_count == 296
1388 assert follow_info.following_count == 32
1389 assert follow_info.hide_follows == true
1390 end
1391
1392 test "doesn't crash when follower and following counters are hidden" do
1393 mock(fn env ->
1394 case env.url do
1395 "http://localhost:4001/users/masto_hidden_counters/following" ->
1396 json(%{
1397 "@context" => "https://www.w3.org/ns/activitystreams",
1398 "id" => "http://localhost:4001/users/masto_hidden_counters/followers"
1399 })
1400
1401 "http://localhost:4001/users/masto_hidden_counters/following?page=1" ->
1402 %Tesla.Env{status: 403, body: ""}
1403
1404 "http://localhost:4001/users/masto_hidden_counters/followers" ->
1405 json(%{
1406 "@context" => "https://www.w3.org/ns/activitystreams",
1407 "id" => "http://localhost:4001/users/masto_hidden_counters/following"
1408 })
1409
1410 "http://localhost:4001/users/masto_hidden_counters/followers?page=1" ->
1411 %Tesla.Env{status: 403, body: ""}
1412 end
1413 end)
1414
1415 user =
1416 insert(:user,
1417 local: false,
1418 follower_address: "http://localhost:4001/users/masto_hidden_counters/followers",
1419 following_address: "http://localhost:4001/users/masto_hidden_counters/following"
1420 )
1421
1422 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1423
1424 assert follow_info.hide_followers == true
1425 assert follow_info.follower_count == 0
1426 assert follow_info.hide_follows == true
1427 assert follow_info.following_count == 0
1428 end
1429 end
1430
1431 describe "fetch_favourites/3" do
1432 test "returns a favourite activities sorted by adds to favorite" do
1433 user = insert(:user)
1434 other_user = insert(:user)
1435 user1 = insert(:user)
1436 user2 = insert(:user)
1437 {:ok, a1} = CommonAPI.post(user1, %{"status" => "bla"})
1438 {:ok, _a2} = CommonAPI.post(user2, %{"status" => "traps are happy"})
1439 {:ok, a3} = CommonAPI.post(user2, %{"status" => "Trees Are "})
1440 {:ok, a4} = CommonAPI.post(user2, %{"status" => "Agent Smith "})
1441 {:ok, a5} = CommonAPI.post(user1, %{"status" => "Red or Blue "})
1442
1443 {:ok, _} = CommonAPI.favorite(user, a4.id)
1444 {:ok, _} = CommonAPI.favorite(other_user, a3.id)
1445 {:ok, _} = CommonAPI.favorite(user, a3.id)
1446 {:ok, _} = CommonAPI.favorite(other_user, a5.id)
1447 {:ok, _} = CommonAPI.favorite(user, a5.id)
1448 {:ok, _} = CommonAPI.favorite(other_user, a4.id)
1449 {:ok, _} = CommonAPI.favorite(user, a1.id)
1450 {:ok, _} = CommonAPI.favorite(other_user, a1.id)
1451 result = ActivityPub.fetch_favourites(user)
1452
1453 assert Enum.map(result, & &1.id) == [a1.id, a5.id, a3.id, a4.id]
1454
1455 result = ActivityPub.fetch_favourites(user, %{"limit" => 2})
1456 assert Enum.map(result, & &1.id) == [a1.id, a5.id]
1457 end
1458 end
1459
1460 describe "Move activity" do
1461 test "create" do
1462 %{ap_id: old_ap_id} = old_user = insert(:user)
1463 %{ap_id: new_ap_id} = new_user = insert(:user, also_known_as: [old_ap_id])
1464 follower = insert(:user)
1465 follower_move_opted_out = insert(:user, allow_following_move: false)
1466
1467 User.follow(follower, old_user)
1468 User.follow(follower_move_opted_out, old_user)
1469
1470 assert User.following?(follower, old_user)
1471 assert User.following?(follower_move_opted_out, old_user)
1472
1473 assert {:ok, activity} = ActivityPub.move(old_user, new_user)
1474
1475 assert %Activity{
1476 actor: ^old_ap_id,
1477 data: %{
1478 "actor" => ^old_ap_id,
1479 "object" => ^old_ap_id,
1480 "target" => ^new_ap_id,
1481 "type" => "Move"
1482 },
1483 local: true
1484 } = activity
1485
1486 params = %{
1487 "op" => "move_following",
1488 "origin_id" => old_user.id,
1489 "target_id" => new_user.id
1490 }
1491
1492 assert_enqueued(worker: Pleroma.Workers.BackgroundWorker, args: params)
1493
1494 Pleroma.Workers.BackgroundWorker.perform(params, nil)
1495
1496 refute User.following?(follower, old_user)
1497 assert User.following?(follower, new_user)
1498
1499 assert User.following?(follower_move_opted_out, old_user)
1500 refute User.following?(follower_move_opted_out, new_user)
1501
1502 activity = %Activity{activity | object: nil}
1503
1504 assert [%Notification{activity: ^activity}] = Notification.for_user(follower)
1505
1506 assert [%Notification{activity: ^activity}] = Notification.for_user(follower_move_opted_out)
1507 end
1508
1509 test "old user must be in the new user's `also_known_as` list" do
1510 old_user = insert(:user)
1511 new_user = insert(:user)
1512
1513 assert {:error, "Target account must have the origin in `alsoKnownAs`"} =
1514 ActivityPub.move(old_user, new_user)
1515 end
1516 end
1517
1518 test "doesn't retrieve replies activities with exclude_replies" do
1519 user = insert(:user)
1520
1521 {:ok, activity} = CommonAPI.post(user, %{"status" => "yeah"})
1522
1523 {:ok, _reply} =
1524 CommonAPI.post(user, %{"status" => "yeah", "in_reply_to_status_id" => activity.id})
1525
1526 [result] = ActivityPub.fetch_public_activities(%{"exclude_replies" => "true"})
1527
1528 assert result.id == activity.id
1529
1530 assert length(ActivityPub.fetch_public_activities()) == 2
1531 end
1532
1533 describe "replies filtering with public messages" do
1534 setup :public_messages
1535
1536 test "public timeline", %{users: %{u1: user}} do
1537 activities_ids =
1538 %{}
1539 |> Map.put("type", ["Create", "Announce"])
1540 |> Map.put("local_only", false)
1541 |> Map.put("blocking_user", user)
1542 |> Map.put("muting_user", user)
1543 |> Map.put("reply_filtering_user", user)
1544 |> ActivityPub.fetch_public_activities()
1545 |> Enum.map(& &1.id)
1546
1547 assert length(activities_ids) == 16
1548 end
1549
1550 test "public timeline with reply_visibility `following`", %{
1551 users: %{u1: user},
1552 u1: u1,
1553 u2: u2,
1554 u3: u3,
1555 u4: u4,
1556 activities: activities
1557 } do
1558 activities_ids =
1559 %{}
1560 |> Map.put("type", ["Create", "Announce"])
1561 |> Map.put("local_only", false)
1562 |> Map.put("blocking_user", user)
1563 |> Map.put("muting_user", user)
1564 |> Map.put("reply_visibility", "following")
1565 |> Map.put("reply_filtering_user", user)
1566 |> ActivityPub.fetch_public_activities()
1567 |> Enum.map(& &1.id)
1568
1569 assert length(activities_ids) == 14
1570
1571 visible_ids =
1572 Map.values(u1) ++ Map.values(u2) ++ Map.values(u4) ++ Map.values(activities) ++ [u3[:r1]]
1573
1574 assert Enum.all?(visible_ids, &(&1 in activities_ids))
1575 end
1576
1577 test "public timeline with reply_visibility `self`", %{
1578 users: %{u1: user},
1579 u1: u1,
1580 u2: u2,
1581 u3: u3,
1582 u4: u4,
1583 activities: activities
1584 } do
1585 activities_ids =
1586 %{}
1587 |> Map.put("type", ["Create", "Announce"])
1588 |> Map.put("local_only", false)
1589 |> Map.put("blocking_user", user)
1590 |> Map.put("muting_user", user)
1591 |> Map.put("reply_visibility", "self")
1592 |> Map.put("reply_filtering_user", user)
1593 |> ActivityPub.fetch_public_activities()
1594 |> Enum.map(& &1.id)
1595
1596 assert length(activities_ids) == 10
1597 visible_ids = Map.values(u1) ++ [u2[:r1], u3[:r1], u4[:r1]] ++ Map.values(activities)
1598 assert Enum.all?(visible_ids, &(&1 in activities_ids))
1599 end
1600
1601 test "home timeline", %{
1602 users: %{u1: user},
1603 activities: activities,
1604 u1: u1,
1605 u2: u2,
1606 u3: u3,
1607 u4: u4
1608 } do
1609 params =
1610 %{}
1611 |> Map.put("type", ["Create", "Announce"])
1612 |> Map.put("blocking_user", user)
1613 |> Map.put("muting_user", user)
1614 |> Map.put("user", user)
1615 |> Map.put("reply_filtering_user", user)
1616
1617 activities_ids =
1618 ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
1619 |> Enum.map(& &1.id)
1620
1621 assert length(activities_ids) == 13
1622
1623 visible_ids =
1624 Map.values(u1) ++
1625 Map.values(u3) ++
1626 [
1627 activities[:a1],
1628 activities[:a2],
1629 activities[:a4],
1630 u2[:r1],
1631 u2[:r3],
1632 u4[:r1],
1633 u4[:r2]
1634 ]
1635
1636 assert Enum.all?(visible_ids, &(&1 in activities_ids))
1637 end
1638
1639 test "home timeline with reply_visibility `following`", %{
1640 users: %{u1: user},
1641 activities: activities,
1642 u1: u1,
1643 u2: u2,
1644 u3: u3,
1645 u4: u4
1646 } do
1647 params =
1648 %{}
1649 |> Map.put("type", ["Create", "Announce"])
1650 |> Map.put("blocking_user", user)
1651 |> Map.put("muting_user", user)
1652 |> Map.put("user", user)
1653 |> Map.put("reply_visibility", "following")
1654 |> Map.put("reply_filtering_user", user)
1655
1656 activities_ids =
1657 ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
1658 |> Enum.map(& &1.id)
1659
1660 assert length(activities_ids) == 11
1661
1662 visible_ids =
1663 Map.values(u1) ++
1664 [
1665 activities[:a1],
1666 activities[:a2],
1667 activities[:a4],
1668 u2[:r1],
1669 u2[:r3],
1670 u3[:r1],
1671 u4[:r1],
1672 u4[:r2]
1673 ]
1674
1675 assert Enum.all?(visible_ids, &(&1 in activities_ids))
1676 end
1677
1678 test "home timeline with reply_visibility `self`", %{
1679 users: %{u1: user},
1680 activities: activities,
1681 u1: u1,
1682 u2: u2,
1683 u3: u3,
1684 u4: u4
1685 } do
1686 params =
1687 %{}
1688 |> Map.put("type", ["Create", "Announce"])
1689 |> Map.put("blocking_user", user)
1690 |> Map.put("muting_user", user)
1691 |> Map.put("user", user)
1692 |> Map.put("reply_visibility", "self")
1693 |> Map.put("reply_filtering_user", user)
1694
1695 activities_ids =
1696 ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
1697 |> Enum.map(& &1.id)
1698
1699 assert length(activities_ids) == 9
1700
1701 visible_ids =
1702 Map.values(u1) ++
1703 [
1704 activities[:a1],
1705 activities[:a2],
1706 activities[:a4],
1707 u2[:r1],
1708 u3[:r1],
1709 u4[:r1]
1710 ]
1711
1712 assert Enum.all?(visible_ids, &(&1 in activities_ids))
1713 end
1714 end
1715
1716 describe "replies filtering with private messages" do
1717 setup :private_messages
1718
1719 test "public timeline", %{users: %{u1: user}} do
1720 activities_ids =
1721 %{}
1722 |> Map.put("type", ["Create", "Announce"])
1723 |> Map.put("local_only", false)
1724 |> Map.put("blocking_user", user)
1725 |> Map.put("muting_user", user)
1726 |> Map.put("user", user)
1727 |> ActivityPub.fetch_public_activities()
1728 |> Enum.map(& &1.id)
1729
1730 assert activities_ids == []
1731 end
1732
1733 test "public timeline with default reply_visibility `following`", %{users: %{u1: user}} do
1734 activities_ids =
1735 %{}
1736 |> Map.put("type", ["Create", "Announce"])
1737 |> Map.put("local_only", false)
1738 |> Map.put("blocking_user", user)
1739 |> Map.put("muting_user", user)
1740 |> Map.put("reply_visibility", "following")
1741 |> Map.put("reply_filtering_user", user)
1742 |> Map.put("user", user)
1743 |> ActivityPub.fetch_public_activities()
1744 |> Enum.map(& &1.id)
1745
1746 assert activities_ids == []
1747 end
1748
1749 test "public timeline with default reply_visibility `self`", %{users: %{u1: user}} do
1750 activities_ids =
1751 %{}
1752 |> Map.put("type", ["Create", "Announce"])
1753 |> Map.put("local_only", false)
1754 |> Map.put("blocking_user", user)
1755 |> Map.put("muting_user", user)
1756 |> Map.put("reply_visibility", "self")
1757 |> Map.put("reply_filtering_user", user)
1758 |> Map.put("user", user)
1759 |> ActivityPub.fetch_public_activities()
1760 |> Enum.map(& &1.id)
1761
1762 assert activities_ids == []
1763 end
1764
1765 test "home timeline", %{users: %{u1: user}} do
1766 params =
1767 %{}
1768 |> Map.put("type", ["Create", "Announce"])
1769 |> Map.put("blocking_user", user)
1770 |> Map.put("muting_user", user)
1771 |> Map.put("user", user)
1772
1773 activities_ids =
1774 ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
1775 |> Enum.map(& &1.id)
1776
1777 assert length(activities_ids) == 12
1778 end
1779
1780 test "home timeline with default reply_visibility `following`", %{users: %{u1: user}} do
1781 params =
1782 %{}
1783 |> Map.put("type", ["Create", "Announce"])
1784 |> Map.put("blocking_user", user)
1785 |> Map.put("muting_user", user)
1786 |> Map.put("user", user)
1787 |> Map.put("reply_visibility", "following")
1788 |> Map.put("reply_filtering_user", user)
1789
1790 activities_ids =
1791 ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
1792 |> Enum.map(& &1.id)
1793
1794 assert length(activities_ids) == 12
1795 end
1796
1797 test "home timeline with default reply_visibility `self`", %{
1798 users: %{u1: user},
1799 activities: activities,
1800 u1: u1,
1801 u2: u2,
1802 u3: u3,
1803 u4: u4
1804 } 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", "self")
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) == 10
1819
1820 visible_ids =
1821 Map.values(u1) ++ Map.values(u4) ++ [u2[:r1], u3[:r1]] ++ Map.values(activities)
1822
1823 assert Enum.all?(visible_ids, &(&1 in activities_ids))
1824 end
1825 end
1826
1827 defp public_messages(_) do
1828 [u1, u2, u3, u4] = insert_list(4, :user)
1829 {:ok, u1} = User.follow(u1, u2)
1830 {:ok, u2} = User.follow(u2, u1)
1831 {:ok, u1} = User.follow(u1, u4)
1832 {:ok, u4} = User.follow(u4, u1)
1833
1834 {:ok, u2} = User.follow(u2, u3)
1835 {:ok, u3} = User.follow(u3, u2)
1836
1837 {:ok, a1} = CommonAPI.post(u1, %{"status" => "Status"})
1838
1839 {:ok, r1_1} =
1840 CommonAPI.post(u2, %{
1841 "status" => "@#{u1.nickname} reply from u2 to u1",
1842 "in_reply_to_status_id" => a1.id
1843 })
1844
1845 {:ok, r1_2} =
1846 CommonAPI.post(u3, %{
1847 "status" => "@#{u1.nickname} reply from u3 to u1",
1848 "in_reply_to_status_id" => a1.id
1849 })
1850
1851 {:ok, r1_3} =
1852 CommonAPI.post(u4, %{
1853 "status" => "@#{u1.nickname} reply from u4 to u1",
1854 "in_reply_to_status_id" => a1.id
1855 })
1856
1857 {:ok, a2} = CommonAPI.post(u2, %{"status" => "Status"})
1858
1859 {:ok, r2_1} =
1860 CommonAPI.post(u1, %{
1861 "status" => "@#{u2.nickname} reply from u1 to u2",
1862 "in_reply_to_status_id" => a2.id
1863 })
1864
1865 {:ok, r2_2} =
1866 CommonAPI.post(u3, %{
1867 "status" => "@#{u2.nickname} reply from u3 to u2",
1868 "in_reply_to_status_id" => a2.id
1869 })
1870
1871 {:ok, r2_3} =
1872 CommonAPI.post(u4, %{
1873 "status" => "@#{u2.nickname} reply from u4 to u2",
1874 "in_reply_to_status_id" => a2.id
1875 })
1876
1877 {:ok, a3} = CommonAPI.post(u3, %{"status" => "Status"})
1878
1879 {:ok, r3_1} =
1880 CommonAPI.post(u1, %{
1881 "status" => "@#{u3.nickname} reply from u1 to u3",
1882 "in_reply_to_status_id" => a3.id
1883 })
1884
1885 {:ok, r3_2} =
1886 CommonAPI.post(u2, %{
1887 "status" => "@#{u3.nickname} reply from u2 to u3",
1888 "in_reply_to_status_id" => a3.id
1889 })
1890
1891 {:ok, r3_3} =
1892 CommonAPI.post(u4, %{
1893 "status" => "@#{u3.nickname} reply from u4 to u3",
1894 "in_reply_to_status_id" => a3.id
1895 })
1896
1897 {:ok, a4} = CommonAPI.post(u4, %{"status" => "Status"})
1898
1899 {:ok, r4_1} =
1900 CommonAPI.post(u1, %{
1901 "status" => "@#{u4.nickname} reply from u1 to u4",
1902 "in_reply_to_status_id" => a4.id
1903 })
1904
1905 {:ok, r4_2} =
1906 CommonAPI.post(u2, %{
1907 "status" => "@#{u4.nickname} reply from u2 to u4",
1908 "in_reply_to_status_id" => a4.id
1909 })
1910
1911 {:ok, r4_3} =
1912 CommonAPI.post(u3, %{
1913 "status" => "@#{u4.nickname} reply from u3 to u4",
1914 "in_reply_to_status_id" => a4.id
1915 })
1916
1917 {:ok,
1918 users: %{u1: u1, u2: u2, u3: u3, u4: u4},
1919 activities: %{a1: a1.id, a2: a2.id, a3: a3.id, a4: a4.id},
1920 u1: %{r1: r1_1.id, r2: r1_2.id, r3: r1_3.id},
1921 u2: %{r1: r2_1.id, r2: r2_2.id, r3: r2_3.id},
1922 u3: %{r1: r3_1.id, r2: r3_2.id, r3: r3_3.id},
1923 u4: %{r1: r4_1.id, r2: r4_2.id, r3: r4_3.id}}
1924 end
1925
1926 defp private_messages(_) do
1927 [u1, u2, u3, u4] = insert_list(4, :user)
1928 {:ok, u1} = User.follow(u1, u2)
1929 {:ok, u2} = User.follow(u2, u1)
1930 {:ok, u1} = User.follow(u1, u3)
1931 {:ok, u3} = User.follow(u3, u1)
1932 {:ok, u1} = User.follow(u1, u4)
1933 {:ok, u4} = User.follow(u4, u1)
1934
1935 {:ok, u2} = User.follow(u2, u3)
1936 {:ok, u3} = User.follow(u3, u2)
1937
1938 {:ok, a1} = CommonAPI.post(u1, %{"status" => "Status", "visibility" => "private"})
1939
1940 {:ok, r1_1} =
1941 CommonAPI.post(u2, %{
1942 "status" => "@#{u1.nickname} reply from u2 to u1",
1943 "in_reply_to_status_id" => a1.id,
1944 "visibility" => "private"
1945 })
1946
1947 {:ok, r1_2} =
1948 CommonAPI.post(u3, %{
1949 "status" => "@#{u1.nickname} reply from u3 to u1",
1950 "in_reply_to_status_id" => a1.id,
1951 "visibility" => "private"
1952 })
1953
1954 {:ok, r1_3} =
1955 CommonAPI.post(u4, %{
1956 "status" => "@#{u1.nickname} reply from u4 to u1",
1957 "in_reply_to_status_id" => a1.id,
1958 "visibility" => "private"
1959 })
1960
1961 {:ok, a2} = CommonAPI.post(u2, %{"status" => "Status", "visibility" => "private"})
1962
1963 {:ok, r2_1} =
1964 CommonAPI.post(u1, %{
1965 "status" => "@#{u2.nickname} reply from u1 to u2",
1966 "in_reply_to_status_id" => a2.id,
1967 "visibility" => "private"
1968 })
1969
1970 {:ok, r2_2} =
1971 CommonAPI.post(u3, %{
1972 "status" => "@#{u2.nickname} reply from u3 to u2",
1973 "in_reply_to_status_id" => a2.id,
1974 "visibility" => "private"
1975 })
1976
1977 {:ok, a3} = CommonAPI.post(u3, %{"status" => "Status", "visibility" => "private"})
1978
1979 {:ok, r3_1} =
1980 CommonAPI.post(u1, %{
1981 "status" => "@#{u3.nickname} reply from u1 to u3",
1982 "in_reply_to_status_id" => a3.id,
1983 "visibility" => "private"
1984 })
1985
1986 {:ok, r3_2} =
1987 CommonAPI.post(u2, %{
1988 "status" => "@#{u3.nickname} reply from u2 to u3",
1989 "in_reply_to_status_id" => a3.id,
1990 "visibility" => "private"
1991 })
1992
1993 {:ok, a4} = CommonAPI.post(u4, %{"status" => "Status", "visibility" => "private"})
1994
1995 {:ok, r4_1} =
1996 CommonAPI.post(u1, %{
1997 "status" => "@#{u4.nickname} reply from u1 to u4",
1998 "in_reply_to_status_id" => a4.id,
1999 "visibility" => "private"
2000 })
2001
2002 {:ok,
2003 users: %{u1: u1, u2: u2, u3: u3, u4: u4},
2004 activities: %{a1: a1.id, a2: a2.id, a3: a3.id, a4: a4.id},
2005 u1: %{r1: r1_1.id, r2: r1_2.id, r3: r1_3.id},
2006 u2: %{r1: r2_1.id, r2: r2_2.id},
2007 u3: %{r1: r3_1.id, r2: r3_2.id},
2008 u4: %{r1: r4_1.id}}
2009 end
2010
2011 describe "maybe_update_follow_information/1" do
2012 setup do
2013 clear_config([:instance, :external_user_synchronization], true)
2014
2015 user = %{
2016 local: false,
2017 ap_id: "https://gensokyo.2hu/users/raymoo",
2018 following_address: "https://gensokyo.2hu/users/following",
2019 follower_address: "https://gensokyo.2hu/users/followers",
2020 type: "Person"
2021 }
2022
2023 %{user: user}
2024 end
2025
2026 test "logs an error when it can't fetch the info", %{user: user} do
2027 assert capture_log(fn ->
2028 ActivityPub.maybe_update_follow_information(user)
2029 end) =~ "Follower/Following counter update for #{user.ap_id} failed"
2030 end
2031
2032 test "just returns the input if the user type is Application", %{
2033 user: user
2034 } do
2035 user =
2036 user
2037 |> Map.put(:type, "Application")
2038
2039 refute capture_log(fn ->
2040 assert ^user = ActivityPub.maybe_update_follow_information(user)
2041 end) =~ "Follower/Following counter update for #{user.ap_id} failed"
2042 end
2043
2044 test "it just returns the input if the user has no following/follower addresses", %{
2045 user: user
2046 } do
2047 user =
2048 user
2049 |> Map.put(:following_address, nil)
2050 |> Map.put(:follower_address, nil)
2051
2052 refute capture_log(fn ->
2053 assert ^user = ActivityPub.maybe_update_follow_information(user)
2054 end) =~ "Follower/Following counter update for #{user.ap_id} failed"
2055 end
2056 end
2057 end