cb2d41f0b835b3417201d1b98d77323743d06570
[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 "unannouncing an object" do
1012 test "unannouncing a previously announced object" do
1013 note_activity = insert(:note_activity)
1014 object = Object.normalize(note_activity)
1015 user = insert(:user)
1016
1017 # Unannouncing an object that is not announced does nothing
1018 {:ok, object} = ActivityPub.unannounce(user, object)
1019 refute object.data["announcement_count"]
1020
1021 {:ok, announce_activity, object} = ActivityPub.announce(user, object)
1022 assert object.data["announcement_count"] == 1
1023
1024 {:ok, unannounce_activity, object} = ActivityPub.unannounce(user, object)
1025 assert object.data["announcement_count"] == 0
1026
1027 assert unannounce_activity.data["to"] == [
1028 User.ap_followers(user),
1029 object.data["actor"]
1030 ]
1031
1032 assert unannounce_activity.data["type"] == "Undo"
1033 assert unannounce_activity.data["object"] == announce_activity.data
1034 assert unannounce_activity.data["actor"] == user.ap_id
1035 assert unannounce_activity.data["context"] == announce_activity.data["context"]
1036
1037 assert Activity.get_by_id(announce_activity.id) == nil
1038 end
1039
1040 test "reverts unannouncing on error" do
1041 note_activity = insert(:note_activity)
1042 object = Object.normalize(note_activity)
1043 user = insert(:user)
1044
1045 {:ok, _announce_activity, object} = ActivityPub.announce(user, object)
1046 assert object.data["announcement_count"] == 1
1047
1048 with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
1049 assert {:error, :reverted} = ActivityPub.unannounce(user, object)
1050 end
1051
1052 object = Object.get_by_ap_id(object.data["id"])
1053 assert object.data["announcement_count"] == 1
1054 end
1055 end
1056
1057 describe "uploading files" do
1058 test "copies the file to the configured folder" do
1059 file = %Plug.Upload{
1060 content_type: "image/jpg",
1061 path: Path.absname("test/fixtures/image.jpg"),
1062 filename: "an_image.jpg"
1063 }
1064
1065 {:ok, %Object{} = object} = ActivityPub.upload(file)
1066 assert object.data["name"] == "an_image.jpg"
1067 end
1068
1069 test "works with base64 encoded images" do
1070 file = %{
1071 "img" => data_uri()
1072 }
1073
1074 {:ok, %Object{}} = ActivityPub.upload(file)
1075 end
1076 end
1077
1078 describe "fetch the latest Follow" do
1079 test "fetches the latest Follow activity" do
1080 %Activity{data: %{"type" => "Follow"}} = activity = insert(:follow_activity)
1081 follower = Repo.get_by(User, ap_id: activity.data["actor"])
1082 followed = Repo.get_by(User, ap_id: activity.data["object"])
1083
1084 assert activity == Utils.fetch_latest_follow(follower, followed)
1085 end
1086 end
1087
1088 describe "following / unfollowing" do
1089 test "it reverts follow activity" do
1090 follower = insert(:user)
1091 followed = insert(:user)
1092
1093 with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
1094 assert {:error, :reverted} = ActivityPub.follow(follower, followed)
1095 end
1096
1097 assert Repo.aggregate(Activity, :count, :id) == 0
1098 assert Repo.aggregate(Object, :count, :id) == 0
1099 end
1100
1101 test "it reverts unfollow activity" do
1102 follower = insert(:user)
1103 followed = insert(:user)
1104
1105 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
1106
1107 with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
1108 assert {:error, :reverted} = ActivityPub.unfollow(follower, followed)
1109 end
1110
1111 activity = Activity.get_by_id(follow_activity.id)
1112 assert activity.data["type"] == "Follow"
1113 assert activity.data["actor"] == follower.ap_id
1114
1115 assert activity.data["object"] == followed.ap_id
1116 end
1117
1118 test "creates a follow activity" do
1119 follower = insert(:user)
1120 followed = insert(:user)
1121
1122 {:ok, activity} = ActivityPub.follow(follower, followed)
1123 assert activity.data["type"] == "Follow"
1124 assert activity.data["actor"] == follower.ap_id
1125 assert activity.data["object"] == followed.ap_id
1126 end
1127
1128 test "creates an undo activity for the last follow" do
1129 follower = insert(:user)
1130 followed = insert(:user)
1131
1132 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
1133 {:ok, activity} = ActivityPub.unfollow(follower, followed)
1134
1135 assert activity.data["type"] == "Undo"
1136 assert activity.data["actor"] == follower.ap_id
1137
1138 embedded_object = activity.data["object"]
1139 assert is_map(embedded_object)
1140 assert embedded_object["type"] == "Follow"
1141 assert embedded_object["object"] == followed.ap_id
1142 assert embedded_object["id"] == follow_activity.data["id"]
1143 end
1144
1145 test "creates an undo activity for a pending follow request" do
1146 follower = insert(:user)
1147 followed = insert(:user, %{locked: true})
1148
1149 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
1150 {:ok, activity} = ActivityPub.unfollow(follower, followed)
1151
1152 assert activity.data["type"] == "Undo"
1153 assert activity.data["actor"] == follower.ap_id
1154
1155 embedded_object = activity.data["object"]
1156 assert is_map(embedded_object)
1157 assert embedded_object["type"] == "Follow"
1158 assert embedded_object["object"] == followed.ap_id
1159 assert embedded_object["id"] == follow_activity.data["id"]
1160 end
1161 end
1162
1163 describe "blocking / unblocking" do
1164 test "reverts block activity on error" do
1165 [blocker, blocked] = insert_list(2, :user)
1166
1167 with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
1168 assert {:error, :reverted} = ActivityPub.block(blocker, blocked)
1169 end
1170
1171 assert Repo.aggregate(Activity, :count, :id) == 0
1172 assert Repo.aggregate(Object, :count, :id) == 0
1173 end
1174
1175 test "creates a block activity" do
1176 blocker = insert(:user)
1177 blocked = insert(:user)
1178
1179 {:ok, activity} = ActivityPub.block(blocker, blocked)
1180
1181 assert activity.data["type"] == "Block"
1182 assert activity.data["actor"] == blocker.ap_id
1183 assert activity.data["object"] == blocked.ap_id
1184 end
1185
1186 test "reverts unblock activity on error" do
1187 [blocker, blocked] = insert_list(2, :user)
1188 {:ok, block_activity} = ActivityPub.block(blocker, blocked)
1189
1190 with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
1191 assert {:error, :reverted} = ActivityPub.unblock(blocker, blocked)
1192 end
1193
1194 assert block_activity.data["type"] == "Block"
1195 assert block_activity.data["actor"] == blocker.ap_id
1196
1197 assert Repo.aggregate(Activity, :count, :id) == 1
1198 assert Repo.aggregate(Object, :count, :id) == 1
1199 end
1200
1201 test "creates an undo activity for the last block" do
1202 blocker = insert(:user)
1203 blocked = insert(:user)
1204
1205 {:ok, block_activity} = ActivityPub.block(blocker, blocked)
1206 {:ok, activity} = ActivityPub.unblock(blocker, blocked)
1207
1208 assert activity.data["type"] == "Undo"
1209 assert activity.data["actor"] == blocker.ap_id
1210
1211 embedded_object = activity.data["object"]
1212 assert is_map(embedded_object)
1213 assert embedded_object["type"] == "Block"
1214 assert embedded_object["object"] == blocked.ap_id
1215 assert embedded_object["id"] == block_activity.data["id"]
1216 end
1217 end
1218
1219 describe "deletion" do
1220 setup do: clear_config([:instance, :rewrite_policy])
1221
1222 test "it reverts deletion on error" do
1223 note = insert(:note_activity)
1224 object = Object.normalize(note)
1225
1226 with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
1227 assert {:error, :reverted} = ActivityPub.delete(object)
1228 end
1229
1230 assert Repo.aggregate(Activity, :count, :id) == 1
1231 assert Repo.get(Object, object.id) == object
1232 assert Activity.get_by_id(note.id) == note
1233 end
1234
1235 test "it creates a delete activity and deletes the original object" do
1236 note = insert(:note_activity)
1237 object = Object.normalize(note)
1238 {:ok, delete} = ActivityPub.delete(object)
1239
1240 assert delete.data["type"] == "Delete"
1241 assert delete.data["actor"] == note.data["actor"]
1242 assert delete.data["object"] == object.data["id"]
1243
1244 assert Activity.get_by_id(delete.id) != nil
1245
1246 assert Repo.get(Object, object.id).data["type"] == "Tombstone"
1247 end
1248
1249 test "it doesn't fail when an activity was already deleted" do
1250 {:ok, delete} = insert(:note_activity) |> Object.normalize() |> ActivityPub.delete()
1251
1252 assert {:ok, ^delete} = delete |> Object.normalize() |> ActivityPub.delete()
1253 end
1254
1255 test "decrements user note count only for public activities" do
1256 user = insert(:user, note_count: 10)
1257
1258 {:ok, a1} =
1259 CommonAPI.post(User.get_cached_by_id(user.id), %{
1260 "status" => "yeah",
1261 "visibility" => "public"
1262 })
1263
1264 {:ok, a2} =
1265 CommonAPI.post(User.get_cached_by_id(user.id), %{
1266 "status" => "yeah",
1267 "visibility" => "unlisted"
1268 })
1269
1270 {:ok, a3} =
1271 CommonAPI.post(User.get_cached_by_id(user.id), %{
1272 "status" => "yeah",
1273 "visibility" => "private"
1274 })
1275
1276 {:ok, a4} =
1277 CommonAPI.post(User.get_cached_by_id(user.id), %{
1278 "status" => "yeah",
1279 "visibility" => "direct"
1280 })
1281
1282 {:ok, _} = Object.normalize(a1) |> ActivityPub.delete()
1283 {:ok, _} = Object.normalize(a2) |> ActivityPub.delete()
1284 {:ok, _} = Object.normalize(a3) |> ActivityPub.delete()
1285 {:ok, _} = Object.normalize(a4) |> ActivityPub.delete()
1286
1287 user = User.get_cached_by_id(user.id)
1288 assert user.note_count == 10
1289 end
1290
1291 test "it creates a delete activity and checks that it is also sent to users mentioned by the deleted object" do
1292 user = insert(:user)
1293 note = insert(:note_activity)
1294 object = Object.normalize(note)
1295
1296 {:ok, object} =
1297 object
1298 |> Object.change(%{
1299 data: %{
1300 "actor" => object.data["actor"],
1301 "id" => object.data["id"],
1302 "to" => [user.ap_id],
1303 "type" => "Note"
1304 }
1305 })
1306 |> Object.update_and_set_cache()
1307
1308 {:ok, delete} = ActivityPub.delete(object)
1309
1310 assert user.ap_id in delete.data["to"]
1311 end
1312
1313 test "decreases reply count" do
1314 user = insert(:user)
1315 user2 = insert(:user)
1316
1317 {:ok, activity} = CommonAPI.post(user, %{"status" => "1", "visibility" => "public"})
1318 reply_data = %{"status" => "1", "in_reply_to_status_id" => activity.id}
1319 ap_id = activity.data["id"]
1320
1321 {:ok, public_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "public"))
1322 {:ok, unlisted_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "unlisted"))
1323 {:ok, private_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "private"))
1324 {:ok, direct_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "direct"))
1325
1326 _ = CommonAPI.delete(direct_reply.id, user2)
1327 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
1328 assert object.data["repliesCount"] == 2
1329
1330 _ = CommonAPI.delete(private_reply.id, user2)
1331 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
1332 assert object.data["repliesCount"] == 2
1333
1334 _ = CommonAPI.delete(public_reply.id, user2)
1335 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
1336 assert object.data["repliesCount"] == 1
1337
1338 _ = CommonAPI.delete(unlisted_reply.id, user2)
1339 assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
1340 assert object.data["repliesCount"] == 0
1341 end
1342
1343 test "it passes delete activity through MRF before deleting the object" do
1344 Pleroma.Config.put([:instance, :rewrite_policy], Pleroma.Web.ActivityPub.MRF.DropPolicy)
1345
1346 note = insert(:note_activity)
1347 object = Object.normalize(note)
1348
1349 {:error, {:reject, _}} = ActivityPub.delete(object)
1350
1351 assert Activity.get_by_id(note.id)
1352 assert Repo.get(Object, object.id).data["type"] == object.data["type"]
1353 end
1354 end
1355
1356 describe "timeline post-processing" do
1357 test "it filters broken threads" do
1358 user1 = insert(:user)
1359 user2 = insert(:user)
1360 user3 = insert(:user)
1361
1362 {:ok, user1} = User.follow(user1, user3)
1363 assert User.following?(user1, user3)
1364
1365 {:ok, user2} = User.follow(user2, user3)
1366 assert User.following?(user2, user3)
1367
1368 {:ok, user3} = User.follow(user3, user2)
1369 assert User.following?(user3, user2)
1370
1371 {:ok, public_activity} = CommonAPI.post(user3, %{"status" => "hi 1"})
1372
1373 {:ok, private_activity_1} =
1374 CommonAPI.post(user3, %{"status" => "hi 2", "visibility" => "private"})
1375
1376 {:ok, private_activity_2} =
1377 CommonAPI.post(user2, %{
1378 "status" => "hi 3",
1379 "visibility" => "private",
1380 "in_reply_to_status_id" => private_activity_1.id
1381 })
1382
1383 {:ok, private_activity_3} =
1384 CommonAPI.post(user3, %{
1385 "status" => "hi 4",
1386 "visibility" => "private",
1387 "in_reply_to_status_id" => private_activity_2.id
1388 })
1389
1390 activities =
1391 ActivityPub.fetch_activities([user1.ap_id | User.following(user1)])
1392 |> Enum.map(fn a -> a.id end)
1393
1394 private_activity_1 = Activity.get_by_ap_id_with_object(private_activity_1.data["id"])
1395
1396 assert [public_activity.id, private_activity_1.id, private_activity_3.id] == activities
1397
1398 assert length(activities) == 3
1399
1400 activities =
1401 ActivityPub.fetch_activities([user1.ap_id | User.following(user1)], %{"user" => user1})
1402 |> Enum.map(fn a -> a.id end)
1403
1404 assert [public_activity.id, private_activity_1.id] == activities
1405 assert length(activities) == 2
1406 end
1407 end
1408
1409 describe "update" do
1410 setup do: clear_config([:instance, :max_pinned_statuses])
1411
1412 test "it creates an update activity with the new user data" do
1413 user = insert(:user)
1414 {:ok, user} = User.ensure_keys_present(user)
1415 user_data = Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: user})
1416
1417 {:ok, update} =
1418 ActivityPub.update(%{
1419 actor: user_data["id"],
1420 to: [user.follower_address],
1421 cc: [],
1422 object: user_data
1423 })
1424
1425 assert update.data["actor"] == user.ap_id
1426 assert update.data["to"] == [user.follower_address]
1427 assert embedded_object = update.data["object"]
1428 assert embedded_object["id"] == user_data["id"]
1429 assert embedded_object["type"] == user_data["type"]
1430 end
1431 end
1432
1433 test "returned pinned statuses" do
1434 Config.put([:instance, :max_pinned_statuses], 3)
1435 user = insert(:user)
1436
1437 {:ok, activity_one} = CommonAPI.post(user, %{"status" => "HI!!!"})
1438 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
1439 {:ok, activity_three} = CommonAPI.post(user, %{"status" => "HI!!!"})
1440
1441 CommonAPI.pin(activity_one.id, user)
1442 user = refresh_record(user)
1443
1444 CommonAPI.pin(activity_two.id, user)
1445 user = refresh_record(user)
1446
1447 CommonAPI.pin(activity_three.id, user)
1448 user = refresh_record(user)
1449
1450 activities = ActivityPub.fetch_user_activities(user, nil, %{"pinned" => "true"})
1451
1452 assert 3 = length(activities)
1453 end
1454
1455 describe "flag/1" do
1456 setup do
1457 reporter = insert(:user)
1458 target_account = insert(:user)
1459 content = "foobar"
1460 {:ok, activity} = CommonAPI.post(target_account, %{"status" => content})
1461 context = Utils.generate_context_id()
1462
1463 reporter_ap_id = reporter.ap_id
1464 target_ap_id = target_account.ap_id
1465 activity_ap_id = activity.data["id"]
1466
1467 activity_with_object = Activity.get_by_ap_id_with_object(activity_ap_id)
1468
1469 {:ok,
1470 %{
1471 reporter: reporter,
1472 context: context,
1473 target_account: target_account,
1474 reported_activity: activity,
1475 content: content,
1476 activity_ap_id: activity_ap_id,
1477 activity_with_object: activity_with_object,
1478 reporter_ap_id: reporter_ap_id,
1479 target_ap_id: target_ap_id
1480 }}
1481 end
1482
1483 test "it can create a Flag activity",
1484 %{
1485 reporter: reporter,
1486 context: context,
1487 target_account: target_account,
1488 reported_activity: reported_activity,
1489 content: content,
1490 activity_ap_id: activity_ap_id,
1491 activity_with_object: activity_with_object,
1492 reporter_ap_id: reporter_ap_id,
1493 target_ap_id: target_ap_id
1494 } do
1495 assert {:ok, activity} =
1496 ActivityPub.flag(%{
1497 actor: reporter,
1498 context: context,
1499 account: target_account,
1500 statuses: [reported_activity],
1501 content: content
1502 })
1503
1504 note_obj = %{
1505 "type" => "Note",
1506 "id" => activity_ap_id,
1507 "content" => content,
1508 "published" => activity_with_object.object.data["published"],
1509 "actor" => AccountView.render("show.json", %{user: target_account})
1510 }
1511
1512 assert %Activity{
1513 actor: ^reporter_ap_id,
1514 data: %{
1515 "type" => "Flag",
1516 "content" => ^content,
1517 "context" => ^context,
1518 "object" => [^target_ap_id, ^note_obj]
1519 }
1520 } = activity
1521 end
1522
1523 test_with_mock "strips status data from Flag, before federating it",
1524 %{
1525 reporter: reporter,
1526 context: context,
1527 target_account: target_account,
1528 reported_activity: reported_activity,
1529 content: content
1530 },
1531 Utils,
1532 [:passthrough],
1533 [] do
1534 {:ok, activity} =
1535 ActivityPub.flag(%{
1536 actor: reporter,
1537 context: context,
1538 account: target_account,
1539 statuses: [reported_activity],
1540 content: content
1541 })
1542
1543 new_data =
1544 put_in(activity.data, ["object"], [target_account.ap_id, reported_activity.data["id"]])
1545
1546 assert_called(Utils.maybe_federate(%{activity | data: new_data}))
1547 end
1548 end
1549
1550 test "fetch_activities/2 returns activities addressed to a list " do
1551 user = insert(:user)
1552 member = insert(:user)
1553 {:ok, list} = Pleroma.List.create("foo", user)
1554 {:ok, list} = Pleroma.List.follow(list, member)
1555
1556 {:ok, activity} =
1557 CommonAPI.post(user, %{"status" => "foobar", "visibility" => "list:#{list.id}"})
1558
1559 activity = Repo.preload(activity, :bookmark)
1560 activity = %Activity{activity | thread_muted?: !!activity.thread_muted?}
1561
1562 assert ActivityPub.fetch_activities([], %{"user" => user}) == [activity]
1563 end
1564
1565 def data_uri do
1566 File.read!("test/fixtures/avatar_data_uri")
1567 end
1568
1569 describe "fetch_activities_bounded" do
1570 test "fetches private posts for followed users" do
1571 user = insert(:user)
1572
1573 {:ok, activity} =
1574 CommonAPI.post(user, %{
1575 "status" => "thought I looked cute might delete later :3",
1576 "visibility" => "private"
1577 })
1578
1579 [result] = ActivityPub.fetch_activities_bounded([user.follower_address], [])
1580 assert result.id == activity.id
1581 end
1582
1583 test "fetches only public posts for other users" do
1584 user = insert(:user)
1585 {:ok, activity} = CommonAPI.post(user, %{"status" => "#cofe", "visibility" => "public"})
1586
1587 {:ok, _private_activity} =
1588 CommonAPI.post(user, %{
1589 "status" => "why is tenshi eating a corndog so cute?",
1590 "visibility" => "private"
1591 })
1592
1593 [result] = ActivityPub.fetch_activities_bounded([], [user.follower_address])
1594 assert result.id == activity.id
1595 end
1596 end
1597
1598 describe "fetch_follow_information_for_user" do
1599 test "syncronizes following/followers counters" do
1600 user =
1601 insert(:user,
1602 local: false,
1603 follower_address: "http://localhost:4001/users/fuser2/followers",
1604 following_address: "http://localhost:4001/users/fuser2/following"
1605 )
1606
1607 {:ok, info} = ActivityPub.fetch_follow_information_for_user(user)
1608 assert info.follower_count == 527
1609 assert info.following_count == 267
1610 end
1611
1612 test "detects hidden followers" do
1613 mock(fn env ->
1614 case env.url do
1615 "http://localhost:4001/users/masto_closed/followers?page=1" ->
1616 %Tesla.Env{status: 403, body: ""}
1617
1618 _ ->
1619 apply(HttpRequestMock, :request, [env])
1620 end
1621 end)
1622
1623 user =
1624 insert(:user,
1625 local: false,
1626 follower_address: "http://localhost:4001/users/masto_closed/followers",
1627 following_address: "http://localhost:4001/users/masto_closed/following"
1628 )
1629
1630 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1631 assert follow_info.hide_followers == true
1632 assert follow_info.hide_follows == false
1633 end
1634
1635 test "detects hidden follows" do
1636 mock(fn env ->
1637 case env.url do
1638 "http://localhost:4001/users/masto_closed/following?page=1" ->
1639 %Tesla.Env{status: 403, body: ""}
1640
1641 _ ->
1642 apply(HttpRequestMock, :request, [env])
1643 end
1644 end)
1645
1646 user =
1647 insert(:user,
1648 local: false,
1649 follower_address: "http://localhost:4001/users/masto_closed/followers",
1650 following_address: "http://localhost:4001/users/masto_closed/following"
1651 )
1652
1653 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1654 assert follow_info.hide_followers == false
1655 assert follow_info.hide_follows == true
1656 end
1657
1658 test "detects hidden follows/followers for friendica" do
1659 user =
1660 insert(:user,
1661 local: false,
1662 follower_address: "http://localhost:8080/followers/fuser3",
1663 following_address: "http://localhost:8080/following/fuser3"
1664 )
1665
1666 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1667 assert follow_info.hide_followers == true
1668 assert follow_info.follower_count == 296
1669 assert follow_info.following_count == 32
1670 assert follow_info.hide_follows == true
1671 end
1672
1673 test "doesn't crash when follower and following counters are hidden" do
1674 mock(fn env ->
1675 case env.url do
1676 "http://localhost:4001/users/masto_hidden_counters/following" ->
1677 json(%{
1678 "@context" => "https://www.w3.org/ns/activitystreams",
1679 "id" => "http://localhost:4001/users/masto_hidden_counters/followers"
1680 })
1681
1682 "http://localhost:4001/users/masto_hidden_counters/following?page=1" ->
1683 %Tesla.Env{status: 403, body: ""}
1684
1685 "http://localhost:4001/users/masto_hidden_counters/followers" ->
1686 json(%{
1687 "@context" => "https://www.w3.org/ns/activitystreams",
1688 "id" => "http://localhost:4001/users/masto_hidden_counters/following"
1689 })
1690
1691 "http://localhost:4001/users/masto_hidden_counters/followers?page=1" ->
1692 %Tesla.Env{status: 403, body: ""}
1693 end
1694 end)
1695
1696 user =
1697 insert(:user,
1698 local: false,
1699 follower_address: "http://localhost:4001/users/masto_hidden_counters/followers",
1700 following_address: "http://localhost:4001/users/masto_hidden_counters/following"
1701 )
1702
1703 {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
1704
1705 assert follow_info.hide_followers == true
1706 assert follow_info.follower_count == 0
1707 assert follow_info.hide_follows == true
1708 assert follow_info.following_count == 0
1709 end
1710 end
1711
1712 describe "fetch_favourites/3" do
1713 test "returns a favourite activities sorted by adds to favorite" do
1714 user = insert(:user)
1715 other_user = insert(:user)
1716 user1 = insert(:user)
1717 user2 = insert(:user)
1718 {:ok, a1} = CommonAPI.post(user1, %{"status" => "bla"})
1719 {:ok, _a2} = CommonAPI.post(user2, %{"status" => "traps are happy"})
1720 {:ok, a3} = CommonAPI.post(user2, %{"status" => "Trees Are "})
1721 {:ok, a4} = CommonAPI.post(user2, %{"status" => "Agent Smith "})
1722 {:ok, a5} = CommonAPI.post(user1, %{"status" => "Red or Blue "})
1723
1724 {:ok, _} = CommonAPI.favorite(user, a4.id)
1725 {:ok, _} = CommonAPI.favorite(other_user, a3.id)
1726 {:ok, _} = CommonAPI.favorite(user, a3.id)
1727 {:ok, _} = CommonAPI.favorite(other_user, a5.id)
1728 {:ok, _} = CommonAPI.favorite(user, a5.id)
1729 {:ok, _} = CommonAPI.favorite(other_user, a4.id)
1730 {:ok, _} = CommonAPI.favorite(user, a1.id)
1731 {:ok, _} = CommonAPI.favorite(other_user, a1.id)
1732 result = ActivityPub.fetch_favourites(user)
1733
1734 assert Enum.map(result, & &1.id) == [a1.id, a5.id, a3.id, a4.id]
1735
1736 result = ActivityPub.fetch_favourites(user, %{"limit" => 2})
1737 assert Enum.map(result, & &1.id) == [a1.id, a5.id]
1738 end
1739 end
1740
1741 describe "Move activity" do
1742 test "create" do
1743 %{ap_id: old_ap_id} = old_user = insert(:user)
1744 %{ap_id: new_ap_id} = new_user = insert(:user, also_known_as: [old_ap_id])
1745 follower = insert(:user)
1746 follower_move_opted_out = insert(:user, allow_following_move: false)
1747
1748 User.follow(follower, old_user)
1749 User.follow(follower_move_opted_out, old_user)
1750
1751 assert User.following?(follower, old_user)
1752 assert User.following?(follower_move_opted_out, old_user)
1753
1754 assert {:ok, activity} = ActivityPub.move(old_user, new_user)
1755
1756 assert %Activity{
1757 actor: ^old_ap_id,
1758 data: %{
1759 "actor" => ^old_ap_id,
1760 "object" => ^old_ap_id,
1761 "target" => ^new_ap_id,
1762 "type" => "Move"
1763 },
1764 local: true
1765 } = activity
1766
1767 params = %{
1768 "op" => "move_following",
1769 "origin_id" => old_user.id,
1770 "target_id" => new_user.id
1771 }
1772
1773 assert_enqueued(worker: Pleroma.Workers.BackgroundWorker, args: params)
1774
1775 Pleroma.Workers.BackgroundWorker.perform(params, nil)
1776
1777 refute User.following?(follower, old_user)
1778 assert User.following?(follower, new_user)
1779
1780 assert User.following?(follower_move_opted_out, old_user)
1781 refute User.following?(follower_move_opted_out, new_user)
1782
1783 activity = %Activity{activity | object: nil}
1784
1785 assert [%Notification{activity: ^activity}] = Notification.for_user(follower)
1786
1787 assert [%Notification{activity: ^activity}] = Notification.for_user(follower_move_opted_out)
1788 end
1789
1790 test "old user must be in the new user's `also_known_as` list" do
1791 old_user = insert(:user)
1792 new_user = insert(:user)
1793
1794 assert {:error, "Target account must have the origin in `alsoKnownAs`"} =
1795 ActivityPub.move(old_user, new_user)
1796 end
1797 end
1798
1799 test "doesn't retrieve replies activities with exclude_replies" do
1800 user = insert(:user)
1801
1802 {:ok, activity} = CommonAPI.post(user, %{"status" => "yeah"})
1803
1804 {:ok, _reply} =
1805 CommonAPI.post(user, %{"status" => "yeah", "in_reply_to_status_id" => activity.id})
1806
1807 [result] = ActivityPub.fetch_public_activities(%{"exclude_replies" => "true"})
1808
1809 assert result.id == activity.id
1810
1811 assert length(ActivityPub.fetch_public_activities()) == 2
1812 end
1813
1814 describe "replies filtering with public messages" do
1815 setup :public_messages
1816
1817 test "public timeline", %{users: %{u1: user}} do
1818 activities_ids =
1819 %{}
1820 |> Map.put("type", ["Create", "Announce"])
1821 |> Map.put("local_only", false)
1822 |> Map.put("blocking_user", user)
1823 |> Map.put("muting_user", user)
1824 |> Map.put("reply_filtering_user", user)
1825 |> ActivityPub.fetch_public_activities()
1826 |> Enum.map(& &1.id)
1827
1828 assert length(activities_ids) == 16
1829 end
1830
1831 test "public timeline with reply_visibility `following`", %{
1832 users: %{u1: user},
1833 u1: u1,
1834 u2: u2,
1835 u3: u3,
1836 u4: u4,
1837 activities: activities
1838 } do
1839 activities_ids =
1840 %{}
1841 |> Map.put("type", ["Create", "Announce"])
1842 |> Map.put("local_only", false)
1843 |> Map.put("blocking_user", user)
1844 |> Map.put("muting_user", user)
1845 |> Map.put("reply_visibility", "following")
1846 |> Map.put("reply_filtering_user", user)
1847 |> ActivityPub.fetch_public_activities()
1848 |> Enum.map(& &1.id)
1849
1850 assert length(activities_ids) == 14
1851
1852 visible_ids =
1853 Map.values(u1) ++ Map.values(u2) ++ Map.values(u4) ++ Map.values(activities) ++ [u3[:r1]]
1854
1855 assert Enum.all?(visible_ids, &(&1 in activities_ids))
1856 end
1857
1858 test "public timeline with reply_visibility `self`", %{
1859 users: %{u1: user},
1860 u1: u1,
1861 u2: u2,
1862 u3: u3,
1863 u4: u4,
1864 activities: activities
1865 } do
1866 activities_ids =
1867 %{}
1868 |> Map.put("type", ["Create", "Announce"])
1869 |> Map.put("local_only", false)
1870 |> Map.put("blocking_user", user)
1871 |> Map.put("muting_user", user)
1872 |> Map.put("reply_visibility", "self")
1873 |> Map.put("reply_filtering_user", user)
1874 |> ActivityPub.fetch_public_activities()
1875 |> Enum.map(& &1.id)
1876
1877 assert length(activities_ids) == 10
1878 visible_ids = Map.values(u1) ++ [u2[:r1], u3[:r1], u4[:r1]] ++ Map.values(activities)
1879 assert Enum.all?(visible_ids, &(&1 in activities_ids))
1880 end
1881
1882 test "home timeline", %{
1883 users: %{u1: user},
1884 activities: activities,
1885 u1: u1,
1886 u2: u2,
1887 u3: u3,
1888 u4: u4
1889 } do
1890 params =
1891 %{}
1892 |> Map.put("type", ["Create", "Announce"])
1893 |> Map.put("blocking_user", user)
1894 |> Map.put("muting_user", user)
1895 |> Map.put("user", user)
1896 |> Map.put("reply_filtering_user", user)
1897
1898 activities_ids =
1899 ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
1900 |> Enum.map(& &1.id)
1901
1902 assert length(activities_ids) == 13
1903
1904 visible_ids =
1905 Map.values(u1) ++
1906 Map.values(u3) ++
1907 [
1908 activities[:a1],
1909 activities[:a2],
1910 activities[:a4],
1911 u2[:r1],
1912 u2[:r3],
1913 u4[:r1],
1914 u4[:r2]
1915 ]
1916
1917 assert Enum.all?(visible_ids, &(&1 in activities_ids))
1918 end
1919
1920 test "home timeline with reply_visibility `following`", %{
1921 users: %{u1: user},
1922 activities: activities,
1923 u1: u1,
1924 u2: u2,
1925 u3: u3,
1926 u4: u4
1927 } do
1928 params =
1929 %{}
1930 |> Map.put("type", ["Create", "Announce"])
1931 |> Map.put("blocking_user", user)
1932 |> Map.put("muting_user", user)
1933 |> Map.put("user", user)
1934 |> Map.put("reply_visibility", "following")
1935 |> Map.put("reply_filtering_user", user)
1936
1937 activities_ids =
1938 ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
1939 |> Enum.map(& &1.id)
1940
1941 assert length(activities_ids) == 11
1942
1943 visible_ids =
1944 Map.values(u1) ++
1945 [
1946 activities[:a1],
1947 activities[:a2],
1948 activities[:a4],
1949 u2[:r1],
1950 u2[:r3],
1951 u3[:r1],
1952 u4[:r1],
1953 u4[:r2]
1954 ]
1955
1956 assert Enum.all?(visible_ids, &(&1 in activities_ids))
1957 end
1958
1959 test "home timeline with reply_visibility `self`", %{
1960 users: %{u1: user},
1961 activities: activities,
1962 u1: u1,
1963 u2: u2,
1964 u3: u3,
1965 u4: u4
1966 } do
1967 params =
1968 %{}
1969 |> Map.put("type", ["Create", "Announce"])
1970 |> Map.put("blocking_user", user)
1971 |> Map.put("muting_user", user)
1972 |> Map.put("user", user)
1973 |> Map.put("reply_visibility", "self")
1974 |> Map.put("reply_filtering_user", user)
1975
1976 activities_ids =
1977 ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
1978 |> Enum.map(& &1.id)
1979
1980 assert length(activities_ids) == 9
1981
1982 visible_ids =
1983 Map.values(u1) ++
1984 [
1985 activities[:a1],
1986 activities[:a2],
1987 activities[:a4],
1988 u2[:r1],
1989 u3[:r1],
1990 u4[:r1]
1991 ]
1992
1993 assert Enum.all?(visible_ids, &(&1 in activities_ids))
1994 end
1995 end
1996
1997 describe "replies filtering with private messages" do
1998 setup :private_messages
1999
2000 test "public timeline", %{users: %{u1: user}} do
2001 activities_ids =
2002 %{}
2003 |> Map.put("type", ["Create", "Announce"])
2004 |> Map.put("local_only", false)
2005 |> Map.put("blocking_user", user)
2006 |> Map.put("muting_user", user)
2007 |> Map.put("user", user)
2008 |> ActivityPub.fetch_public_activities()
2009 |> Enum.map(& &1.id)
2010
2011 assert activities_ids == []
2012 end
2013
2014 test "public timeline with default reply_visibility `following`", %{users: %{u1: user}} do
2015 activities_ids =
2016 %{}
2017 |> Map.put("type", ["Create", "Announce"])
2018 |> Map.put("local_only", false)
2019 |> Map.put("blocking_user", user)
2020 |> Map.put("muting_user", user)
2021 |> Map.put("reply_visibility", "following")
2022 |> Map.put("reply_filtering_user", user)
2023 |> Map.put("user", user)
2024 |> ActivityPub.fetch_public_activities()
2025 |> Enum.map(& &1.id)
2026
2027 assert activities_ids == []
2028 end
2029
2030 test "public timeline with default reply_visibility `self`", %{users: %{u1: user}} do
2031 activities_ids =
2032 %{}
2033 |> Map.put("type", ["Create", "Announce"])
2034 |> Map.put("local_only", false)
2035 |> Map.put("blocking_user", user)
2036 |> Map.put("muting_user", user)
2037 |> Map.put("reply_visibility", "self")
2038 |> Map.put("reply_filtering_user", user)
2039 |> Map.put("user", user)
2040 |> ActivityPub.fetch_public_activities()
2041 |> Enum.map(& &1.id)
2042
2043 assert activities_ids == []
2044 end
2045
2046 test "home timeline", %{users: %{u1: user}} do
2047 params =
2048 %{}
2049 |> Map.put("type", ["Create", "Announce"])
2050 |> Map.put("blocking_user", user)
2051 |> Map.put("muting_user", user)
2052 |> Map.put("user", user)
2053
2054 activities_ids =
2055 ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
2056 |> Enum.map(& &1.id)
2057
2058 assert length(activities_ids) == 12
2059 end
2060
2061 test "home timeline with default reply_visibility `following`", %{users: %{u1: user}} do
2062 params =
2063 %{}
2064 |> Map.put("type", ["Create", "Announce"])
2065 |> Map.put("blocking_user", user)
2066 |> Map.put("muting_user", user)
2067 |> Map.put("user", user)
2068 |> Map.put("reply_visibility", "following")
2069 |> Map.put("reply_filtering_user", user)
2070
2071 activities_ids =
2072 ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
2073 |> Enum.map(& &1.id)
2074
2075 assert length(activities_ids) == 12
2076 end
2077
2078 test "home timeline with default reply_visibility `self`", %{
2079 users: %{u1: user},
2080 activities: activities,
2081 u1: u1,
2082 u2: u2,
2083 u3: u3,
2084 u4: u4
2085 } do
2086 params =
2087 %{}
2088 |> Map.put("type", ["Create", "Announce"])
2089 |> Map.put("blocking_user", user)
2090 |> Map.put("muting_user", user)
2091 |> Map.put("user", user)
2092 |> Map.put("reply_visibility", "self")
2093 |> Map.put("reply_filtering_user", user)
2094
2095 activities_ids =
2096 ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
2097 |> Enum.map(& &1.id)
2098
2099 assert length(activities_ids) == 10
2100
2101 visible_ids =
2102 Map.values(u1) ++ Map.values(u4) ++ [u2[:r1], u3[:r1]] ++ Map.values(activities)
2103
2104 assert Enum.all?(visible_ids, &(&1 in activities_ids))
2105 end
2106 end
2107
2108 defp public_messages(_) do
2109 [u1, u2, u3, u4] = insert_list(4, :user)
2110 {:ok, u1} = User.follow(u1, u2)
2111 {:ok, u2} = User.follow(u2, u1)
2112 {:ok, u1} = User.follow(u1, u4)
2113 {:ok, u4} = User.follow(u4, u1)
2114
2115 {:ok, u2} = User.follow(u2, u3)
2116 {:ok, u3} = User.follow(u3, u2)
2117
2118 {:ok, a1} = CommonAPI.post(u1, %{"status" => "Status"})
2119
2120 {:ok, r1_1} =
2121 CommonAPI.post(u2, %{
2122 "status" => "@#{u1.nickname} reply from u2 to u1",
2123 "in_reply_to_status_id" => a1.id
2124 })
2125
2126 {:ok, r1_2} =
2127 CommonAPI.post(u3, %{
2128 "status" => "@#{u1.nickname} reply from u3 to u1",
2129 "in_reply_to_status_id" => a1.id
2130 })
2131
2132 {:ok, r1_3} =
2133 CommonAPI.post(u4, %{
2134 "status" => "@#{u1.nickname} reply from u4 to u1",
2135 "in_reply_to_status_id" => a1.id
2136 })
2137
2138 {:ok, a2} = CommonAPI.post(u2, %{"status" => "Status"})
2139
2140 {:ok, r2_1} =
2141 CommonAPI.post(u1, %{
2142 "status" => "@#{u2.nickname} reply from u1 to u2",
2143 "in_reply_to_status_id" => a2.id
2144 })
2145
2146 {:ok, r2_2} =
2147 CommonAPI.post(u3, %{
2148 "status" => "@#{u2.nickname} reply from u3 to u2",
2149 "in_reply_to_status_id" => a2.id
2150 })
2151
2152 {:ok, r2_3} =
2153 CommonAPI.post(u4, %{
2154 "status" => "@#{u2.nickname} reply from u4 to u2",
2155 "in_reply_to_status_id" => a2.id
2156 })
2157
2158 {:ok, a3} = CommonAPI.post(u3, %{"status" => "Status"})
2159
2160 {:ok, r3_1} =
2161 CommonAPI.post(u1, %{
2162 "status" => "@#{u3.nickname} reply from u1 to u3",
2163 "in_reply_to_status_id" => a3.id
2164 })
2165
2166 {:ok, r3_2} =
2167 CommonAPI.post(u2, %{
2168 "status" => "@#{u3.nickname} reply from u2 to u3",
2169 "in_reply_to_status_id" => a3.id
2170 })
2171
2172 {:ok, r3_3} =
2173 CommonAPI.post(u4, %{
2174 "status" => "@#{u3.nickname} reply from u4 to u3",
2175 "in_reply_to_status_id" => a3.id
2176 })
2177
2178 {:ok, a4} = CommonAPI.post(u4, %{"status" => "Status"})
2179
2180 {:ok, r4_1} =
2181 CommonAPI.post(u1, %{
2182 "status" => "@#{u4.nickname} reply from u1 to u4",
2183 "in_reply_to_status_id" => a4.id
2184 })
2185
2186 {:ok, r4_2} =
2187 CommonAPI.post(u2, %{
2188 "status" => "@#{u4.nickname} reply from u2 to u4",
2189 "in_reply_to_status_id" => a4.id
2190 })
2191
2192 {:ok, r4_3} =
2193 CommonAPI.post(u3, %{
2194 "status" => "@#{u4.nickname} reply from u3 to u4",
2195 "in_reply_to_status_id" => a4.id
2196 })
2197
2198 {:ok,
2199 users: %{u1: u1, u2: u2, u3: u3, u4: u4},
2200 activities: %{a1: a1.id, a2: a2.id, a3: a3.id, a4: a4.id},
2201 u1: %{r1: r1_1.id, r2: r1_2.id, r3: r1_3.id},
2202 u2: %{r1: r2_1.id, r2: r2_2.id, r3: r2_3.id},
2203 u3: %{r1: r3_1.id, r2: r3_2.id, r3: r3_3.id},
2204 u4: %{r1: r4_1.id, r2: r4_2.id, r3: r4_3.id}}
2205 end
2206
2207 defp private_messages(_) do
2208 [u1, u2, u3, u4] = insert_list(4, :user)
2209 {:ok, u1} = User.follow(u1, u2)
2210 {:ok, u2} = User.follow(u2, u1)
2211 {:ok, u1} = User.follow(u1, u3)
2212 {:ok, u3} = User.follow(u3, u1)
2213 {:ok, u1} = User.follow(u1, u4)
2214 {:ok, u4} = User.follow(u4, u1)
2215
2216 {:ok, u2} = User.follow(u2, u3)
2217 {:ok, u3} = User.follow(u3, u2)
2218
2219 {:ok, a1} = CommonAPI.post(u1, %{"status" => "Status", "visibility" => "private"})
2220
2221 {:ok, r1_1} =
2222 CommonAPI.post(u2, %{
2223 "status" => "@#{u1.nickname} reply from u2 to u1",
2224 "in_reply_to_status_id" => a1.id,
2225 "visibility" => "private"
2226 })
2227
2228 {:ok, r1_2} =
2229 CommonAPI.post(u3, %{
2230 "status" => "@#{u1.nickname} reply from u3 to u1",
2231 "in_reply_to_status_id" => a1.id,
2232 "visibility" => "private"
2233 })
2234
2235 {:ok, r1_3} =
2236 CommonAPI.post(u4, %{
2237 "status" => "@#{u1.nickname} reply from u4 to u1",
2238 "in_reply_to_status_id" => a1.id,
2239 "visibility" => "private"
2240 })
2241
2242 {:ok, a2} = CommonAPI.post(u2, %{"status" => "Status", "visibility" => "private"})
2243
2244 {:ok, r2_1} =
2245 CommonAPI.post(u1, %{
2246 "status" => "@#{u2.nickname} reply from u1 to u2",
2247 "in_reply_to_status_id" => a2.id,
2248 "visibility" => "private"
2249 })
2250
2251 {:ok, r2_2} =
2252 CommonAPI.post(u3, %{
2253 "status" => "@#{u2.nickname} reply from u3 to u2",
2254 "in_reply_to_status_id" => a2.id,
2255 "visibility" => "private"
2256 })
2257
2258 {:ok, a3} = CommonAPI.post(u3, %{"status" => "Status", "visibility" => "private"})
2259
2260 {:ok, r3_1} =
2261 CommonAPI.post(u1, %{
2262 "status" => "@#{u3.nickname} reply from u1 to u3",
2263 "in_reply_to_status_id" => a3.id,
2264 "visibility" => "private"
2265 })
2266
2267 {:ok, r3_2} =
2268 CommonAPI.post(u2, %{
2269 "status" => "@#{u3.nickname} reply from u2 to u3",
2270 "in_reply_to_status_id" => a3.id,
2271 "visibility" => "private"
2272 })
2273
2274 {:ok, a4} = CommonAPI.post(u4, %{"status" => "Status", "visibility" => "private"})
2275
2276 {:ok, r4_1} =
2277 CommonAPI.post(u1, %{
2278 "status" => "@#{u4.nickname} reply from u1 to u4",
2279 "in_reply_to_status_id" => a4.id,
2280 "visibility" => "private"
2281 })
2282
2283 {:ok,
2284 users: %{u1: u1, u2: u2, u3: u3, u4: u4},
2285 activities: %{a1: a1.id, a2: a2.id, a3: a3.id, a4: a4.id},
2286 u1: %{r1: r1_1.id, r2: r1_2.id, r3: r1_3.id},
2287 u2: %{r1: r2_1.id, r2: r2_2.id},
2288 u3: %{r1: r3_1.id, r2: r3_2.id},
2289 u4: %{r1: r4_1.id}}
2290 end
2291
2292 describe "maybe_update_follow_information/1" do
2293 setup do
2294 clear_config([:instance, :external_user_synchronization], true)
2295
2296 user = %{
2297 local: false,
2298 ap_id: "https://gensokyo.2hu/users/raymoo",
2299 following_address: "https://gensokyo.2hu/users/following",
2300 follower_address: "https://gensokyo.2hu/users/followers",
2301 type: "Person"
2302 }
2303
2304 %{user: user}
2305 end
2306
2307 test "logs an error when it can't fetch the info", %{user: user} do
2308 assert capture_log(fn ->
2309 ActivityPub.maybe_update_follow_information(user)
2310 end) =~ "Follower/Following counter update for #{user.ap_id} failed"
2311 end
2312
2313 test "just returns the input if the user type is Application", %{
2314 user: user
2315 } do
2316 user =
2317 user
2318 |> Map.put(:type, "Application")
2319
2320 refute capture_log(fn ->
2321 assert ^user = ActivityPub.maybe_update_follow_information(user)
2322 end) =~ "Follower/Following counter update for #{user.ap_id} failed"
2323 end
2324
2325 test "it just returns the input if the user has no following/follower addresses", %{
2326 user: user
2327 } do
2328 user =
2329 user
2330 |> Map.put(:following_address, nil)
2331 |> Map.put(:follower_address, nil)
2332
2333 refute capture_log(fn ->
2334 assert ^user = ActivityPub.maybe_update_follow_information(user)
2335 end) =~ "Follower/Following counter update for #{user.ap_id} failed"
2336 end
2337 end
2338 end