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