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