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