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