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