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